xref: /petsc/src/sys/objects/tagm.c (revision e8fb0fc05573aa974b3f79797ad519d0316ca217)
1 #define PETSC_DLL
2 /*
3       Some PETSc utilites
4 */
5 #include "petscsys.h"             /*I    "petscsys.h"   I*/
6 #if defined(PETSC_HAVE_STDLIB_H)
7 #include <stdlib.h>
8 #endif
9 
10 /* ---------------------------------------------------------------- */
11 /*
12    A simple way to manage tags inside a communicator.
13 
14    It uses the attributes to determine if a new communicator
15       is needed and to store the available tags.
16 
17    Notes on the implementation
18 
19    The tagvalues to use are stored in a two element array.  The first element
20    is the first free tag value.  The second is used to indicate how
21    many "copies" of the communicator there are used in destroying.
22 
23 
24 */
25 
26 static PetscMPIInt Petsc_Tag_keyval       = MPI_KEYVAL_INVALID;
27 static PetscMPIInt Petsc_InnerComm_keyval = MPI_KEYVAL_INVALID;
28 static PetscMPIInt Petsc_OuterComm_keyval = MPI_KEYVAL_INVALID;
29 EXTERN_C_BEGIN
30 #undef __FUNCT__
31 #define __FUNCT__ "Petsc_DelTag"
32 /*
33    Private routine to delete internal storage when a communicator is freed.
34   This is called by MPI, not by users.
35 
36     Note: this is declared extern "C" because it is passed to the system routine signal()
37           which is an extern "C" routine.
38 */
39 PetscMPIInt PETSC_DLLEXPORT Petsc_DelTag(MPI_Comm comm,PetscMPIInt keyval,void* attr_val,void* extra_state)
40 {
41   PetscErrorCode ierr;
42 
43   PetscFunctionBegin;
44   ierr = PetscInfo1(0,"Deleting tag data in an MPI_Comm %ld\n",(long)comm);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
45   ierr = PetscFree(attr_val);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
46   PetscFunctionReturn(MPI_SUCCESS);
47 }
48 EXTERN_C_END
49 
50 EXTERN_C_BEGIN
51 #undef __FUNCT__
52 #define __FUNCT__ "Petsc_DelComm"
53 /*
54    Private routine to delete internal storage when a communicator is freed.
55   This is called by MPI, not by users.
56 
57     Note: this is declared extern "C" because it is passed to the system routine signal()
58           which is an extern "C" routine.
59 */
60 PetscMPIInt PETSC_DLLEXPORT Petsc_DelComm(MPI_Comm comm,PetscMPIInt keyval,void* attr_val,void* extra_state)
61 {
62   PetscErrorCode ierr;
63 
64   PetscFunctionBegin;
65   ierr = PetscInfo1(0,"Deleting PETSc communicator imbedded in a user MPI_Comm %ld\n",(long)comm);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
66   /* actually don't delete anything because we cannot increase the reference count of the communicator anyways */
67   PetscFunctionReturn(MPI_SUCCESS);
68 }
69 EXTERN_C_END
70 
71 #undef __FUNCT__
72 #define __FUNCT__ "PetscObjectGetNewTag"
73 /*@C
74     PetscObjectGetNewTag - Gets a unique new tag from a PETSc object. All
75     processors that share the object MUST call this routine EXACTLY the same
76     number of times.  This tag should only be used with the current objects
77     communicator; do NOT use it with any other MPI communicator.
78 
79     Collective on PetscObject
80 
81     Input Parameter:
82 .   obj - the PETSc object; this must be cast with a (PetscObject), for example,
83          PetscObjectGetNewTag((PetscObject)mat,&tag);
84 
85     Output Parameter:
86 .   tag - the new tag
87 
88     Level: developer
89 
90     Concepts: tag^getting
91     Concepts: message tag^getting
92     Concepts: MPI message tag^getting
93 
94 .seealso: PetscCommGetNewTag()
95 @*/
96 PetscErrorCode PETSC_DLLEXPORT PetscObjectGetNewTag(PetscObject obj,PetscMPIInt *tag)
97 {
98   PetscErrorCode ierr;
99 
100   PetscFunctionBegin;
101   ierr = PetscCommGetNewTag(obj->comm,tag);CHKERRQ(ierr);
102   PetscFunctionReturn(0);
103 }
104 
105 #undef __FUNCT__
106 #define __FUNCT__ "PetscCommGetNewTag"
107 /*@
108     PetscCommGetNewTag - Gets a unique new tag from a PETSc communicator. All
109     processors that share the communicator MUST call this routine EXACTLY the same
110     number of times.  This tag should only be used with the current objects
111     communicator; do NOT use it with any other MPI communicator.
112 
113     Collective on comm
114 
115     Input Parameter:
116 .   comm - the PETSc communicator
117 
118     Output Parameter:
119 .   tag - the new tag
120 
121     Level: developer
122 
123     Concepts: tag^getting
124     Concepts: message tag^getting
125     Concepts: MPI message tag^getting
126 
127 .seealso: PetscObjectGetNewTag(), PetscCommDuplicate()
128 @*/
129 PetscErrorCode PETSC_DLLEXPORT PetscCommGetNewTag(MPI_Comm comm,PetscMPIInt *tag)
130 {
131   PetscErrorCode ierr;
132   PetscMPIInt    *tagvalp=0,*maxval;
133   PetscTruth     flg;
134 
135   PetscFunctionBegin;
136   PetscValidIntPointer(tag,2);
137 
138   if (Petsc_Tag_keyval == MPI_KEYVAL_INVALID) {
139     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelTag,&Petsc_Tag_keyval,(void*)0);CHKERRQ(ierr);
140     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_InnerComm_keyval,(void*)0);CHKERRQ(ierr);
141     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_OuterComm_keyval,(void*)0);CHKERRQ(ierr);
142   }
143 
144   ierr = MPI_Attr_get(comm,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
145   if (!flg) SETERRQ(PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
146 
147   if (tagvalp[0] < 1) {
148     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",tagvalp[1]);CHKERRQ(ierr);
149     ierr       = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,(void**)&maxval,(PetscMPIInt*)&flg);CHKERRQ(ierr);
150     if (!flg) {
151       SETERRQ(PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
152     }
153     tagvalp[0] = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
154   }
155 
156   *tag = tagvalp[0]--;
157   PetscFunctionReturn(0);
158 }
159 
160 #undef __FUNCT__
161 #define __FUNCT__ "PetscCommSynchronizeTags"
162 /*@
163     PetscCommSynchronizeTags - It is possible for the private PETSc tags to get out of
164     synch between processes. This function rectifies this disparity.
165 
166     Collective on comm
167 
168     Input Parameter:
169 .   comm - the PETSc communicator
170 
171     Level: developer
172 
173     Concepts: tag^getting
174     Concepts: message tag^getting
175     Concepts: MPI message tag^getting
176 
177 .seealso: PetscObjectCheckTags()
178 @*/
179 PetscErrorCode PETSC_DLLEXPORT PetscCommSynchronizeTags(MPI_Comm comm)
180 {
181   PetscMPIInt   *tagvalp = 0, tag;
182   PetscTruth     flg;
183   PetscErrorCode ierr;
184 
185   PetscFunctionBegin;
186   ierr = MPI_Attr_get(comm, Petsc_Tag_keyval, (void **) &tagvalp, (PetscMPIInt *) &flg);CHKERRQ(ierr);
187   if (!flg) {
188     MPI_Comm innerComm;
189     void    *ptr;
190 
191     /* check if this communicator has a PETSc communicator imbedded in it */
192     ierr = MPI_Attr_get(comm, Petsc_InnerComm_keyval, &ptr, (PetscMPIInt*) &flg);CHKERRQ(ierr);
193     if (!flg) {
194       SETERRQ(PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
195     } else {
196       /* We use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers */
197       ierr = PetscMemcpy(&innerComm, &ptr, sizeof(MPI_Comm));CHKERRQ(ierr);
198       ierr = MPI_Attr_get(innerComm, Petsc_Tag_keyval, (void **) &tagvalp, (PetscMPIInt *) &flg);CHKERRQ(ierr);
199       if (!flg) {
200         SETERRQ(PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tagvalp attribute set");
201       }
202     }
203   }
204   ierr = MPI_Allreduce(tagvalp, &tag, 1, MPI_INT, MPI_MIN, comm);CHKERRQ(ierr);
205   tagvalp[0] = tag;
206   ierr = PetscInfo2(0, "Reset tag for comm %ld to \n", (long) comm, tagvalp[0]);CHKERRQ(ierr);
207   PetscFunctionReturn(0);
208 }
209 
210 #undef __FUNCT__
211 #define __FUNCT__ "PetscCommCheckTags"
212 /*@
213     PetscCommCheckTags - It is possible for the private PETSc tags to get out of
214     synch between processes. This function returns an error if the tags are invalid.
215 
216     Collective on comm
217 
218     Input Parameter:
219 .   comm - the PETSc communicator
220 
221     Level: developer
222 
223     Concepts: tag^getting
224     Concepts: message tag^getting
225     Concepts: MPI message tag^getting
226 
227 .seealso: PetscObjectSynchronizeTags()
228 @*/
229 PetscErrorCode PETSC_DLLEXPORT PetscCommCheckTags(MPI_Comm comm)
230 {
231   PetscMPIInt   *tagvalp = 0, tag;
232   PetscTruth     flg;
233   PetscErrorCode ierr;
234 
235   PetscFunctionBegin;
236   ierr = MPI_Attr_get(comm, Petsc_Tag_keyval, (void **) &tagvalp, (PetscMPIInt *) &flg);CHKERRQ(ierr);
237   if (!flg) {
238     MPI_Comm innerComm;
239     void    *ptr;
240 
241     /* check if this communicator has a PETSc communicator imbedded in it */
242     ierr = MPI_Attr_get(comm, Petsc_InnerComm_keyval, &ptr, (PetscMPIInt*) &flg);CHKERRQ(ierr);
243     if (!flg) {
244       SETERRQ(PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
245     } else {
246       /* We use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers */
247       ierr = PetscMemcpy(&innerComm, &ptr, sizeof(MPI_Comm));CHKERRQ(ierr);
248       ierr = MPI_Attr_get(innerComm, Petsc_Tag_keyval, (void **) &tagvalp, (PetscMPIInt *) &flg);CHKERRQ(ierr);
249       if (!flg) {
250         SETERRQ(PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tagvalp attribute set");
251       }
252     }
253   }
254   tag = tagvalp[0];
255   ierr = MPI_Bcast(&tag, 1, MPI_INT, 0, comm);CHKERRQ(ierr);
256   if (tagvalp[0] != tag) {
257     SETERRQ2(PETSC_ERR_LIB, "Invalid tag %d should be %d", tagvalp[0], tag);
258   }
259   PetscFunctionReturn(0);
260 }
261 
262 #undef __FUNCT__
263 #define __FUNCT__ "PetscCommDuplicate"
264 /*@C
265   PetscCommDuplicate - Duplicates the communicator only if it is not already a PETSc
266                          communicator.
267 
268   Collective on MPI_Comm
269 
270   Input Parameters:
271 . comm_in - Input communicator
272 
273   Output Parameters:
274 + comm_out - Output communicator.  May be comm_in.
275 - first_tag - Tag available that has not already been used with this communicator (you may
276               pass in PETSC_NULL if you do not need a tag)
277 
278   PETSc communicators are just regular MPI communicators that keep track of which
279   tags have been used to prevent tag conflict. If you pass a non-PETSc communicator into
280   a PETSc creation routine it will attach a private communicator for use in the objects communications.
281 
282   Level: developer
283 
284   Concepts: communicator^duplicate
285 
286 .seealso: PetscObjectGetNewTag(), PetscCommGetNewTag()
287 @*/
288 PetscErrorCode PETSC_DLLEXPORT PetscCommDuplicate(MPI_Comm comm_in,MPI_Comm *comm_out,PetscMPIInt* first_tag)
289 {
290   PetscErrorCode ierr;
291   PetscMPIInt    *tagvalp,*maxval;
292   PetscTruth     flg;
293 #if defined(PETSC_USE_DEBUG)
294   PetscMPIInt    tag;
295 #endif
296 
297   PetscFunctionBegin;
298   if (Petsc_Tag_keyval == MPI_KEYVAL_INVALID) {
299     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelTag,&Petsc_Tag_keyval,(void*)0);CHKERRQ(ierr);
300     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_InnerComm_keyval,(void*)0);CHKERRQ(ierr);
301     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_OuterComm_keyval,(void*)0);CHKERRQ(ierr);
302   }
303   ierr = MPI_Attr_get(comm_in,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
304 
305   if (!flg) {
306     void *ptr;
307     /* check if this communicator has a PETSc communicator imbedded in it */
308     ierr = MPI_Attr_get(comm_in,Petsc_InnerComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
309     if (!flg) {
310       /* This communicator is not yet known to this system, so we duplicate it and set its value */
311       ierr       = MPI_Comm_dup(comm_in,comm_out);CHKERRQ(ierr);
312       ierr       = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,(void**)&maxval,(PetscMPIInt*)&flg);CHKERRQ(ierr);
313       if (!flg) {
314         SETERRQ(PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
315       }
316       ierr = PetscMalloc(2*sizeof(PetscMPIInt),&tagvalp);CHKERRQ(ierr);
317       tagvalp[0] = *maxval;
318       tagvalp[1] = 0;
319       ierr       = MPI_Attr_put(*comm_out,Petsc_Tag_keyval,tagvalp);CHKERRQ(ierr);
320       ierr = PetscInfo3(0,"Duplicating a communicator %ld %ld max tags = %d\n",(long)comm_in,(long)*comm_out,*maxval);CHKERRQ(ierr);
321 
322       /* save PETSc communicator inside user communicator, so we can get it next time */
323       ierr = PetscMemcpy(&ptr,comm_out,sizeof(MPI_Comm));CHKERRQ(ierr);
324       ierr = MPI_Attr_put(comm_in,Petsc_InnerComm_keyval,ptr);CHKERRQ(ierr);
325       ierr = PetscMemcpy(&ptr,&comm_in,sizeof(MPI_Comm));CHKERRQ(ierr);
326       ierr = MPI_Attr_put(*comm_out,Petsc_OuterComm_keyval,ptr);CHKERRQ(ierr);
327     } else {
328       /*
329         We use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers
330       */
331       ierr = PetscMemcpy(comm_out,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
332       ierr = MPI_Attr_get(*comm_out,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
333       if (!flg) {
334         SETERRQ(PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tagvalp attribute set");
335       }
336       ierr = PetscInfo2(0,"Using internal PETSc communicator %ld %ld\n",(long)comm_in,(long)*comm_out);CHKERRQ(ierr);
337     }
338   } else {
339     *comm_out = comm_in;
340   }
341 
342 #if defined(PETSC_USE_DEBUG)
343   /*
344      Hanging here means that some processes have called PetscCommDuplicate() and others have not.
345      This likley means that a subset of processes in a MPI_Comm have attempted to create a PetscObject!
346      ALL processes that share a communicator MUST shared objects created from that communicator.
347   */
348   ierr = MPI_Allreduce(tagvalp,&tag,1,MPI_INT,MPI_BOR,comm_in);CHKERRQ(ierr);
349   if (tag != tagvalp[0]) {
350     SETERRQ(PETSC_ERR_ARG_CORRUPT,"Communicator was used on subset of processors.");
351   }
352 #endif
353 
354   if (tagvalp[0] < 1) {
355     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",tagvalp[1]);CHKERRQ(ierr);
356     ierr       = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,(void**)&maxval,(PetscMPIInt*)&flg);CHKERRQ(ierr);
357     if (!flg) {
358       SETERRQ(PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
359     }
360     tagvalp[0] = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
361   }
362 
363   if (first_tag) {
364     *first_tag = tagvalp[0]--;
365     ierr = PetscInfo1(0,"  returning tag %ld\n",(long)*first_tag);CHKERRQ(ierr);
366   }
367   tagvalp[1]++; /* number of references to this comm */
368   PetscFunctionReturn(0);
369 }
370 
371 #undef __FUNCT__
372 #define __FUNCT__ "PetscCommDestroy"
373 /*@C
374    PetscCommDestroy - Frees communicator.  Use in conjunction with PetscCommDuplicate().
375 
376    Collective on MPI_Comm
377 
378    Input Parameter:
379 .  comm - the communicator to free
380 
381    Level: developer
382 
383    Concepts: communicator^destroy
384 
385 @*/
386 PetscErrorCode PETSC_DLLEXPORT PetscCommDestroy(MPI_Comm *comm)
387 {
388   PetscErrorCode ierr;
389   PetscMPIInt    *tagvalp;
390   PetscTruth     flg;
391   MPI_Comm       icomm = *comm,ocomm;
392   void           *ptr;
393 
394   PetscFunctionBegin;
395   if (Petsc_Tag_keyval == MPI_KEYVAL_INVALID) {
396     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelTag,&Petsc_Tag_keyval,(void*)0);CHKERRQ(ierr);
397     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_InnerComm_keyval,(void*)0);CHKERRQ(ierr);
398     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_OuterComm_keyval,(void*)0);CHKERRQ(ierr);
399   }
400   ierr = MPI_Attr_get(icomm,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
401   if (!flg) {
402     ierr  = MPI_Attr_get(icomm,Petsc_InnerComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
403     /*
404         We use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers
405     */
406     ierr = PetscMemcpy(&icomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
407     if (!flg) {
408       PetscFunctionReturn(0);
409     }
410     ierr = MPI_Attr_get(icomm,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
411     if (!flg) {
412       SETERRQ(PETSC_ERR_ARG_CORRUPT,"Error freeing MPI_Comm, problem with corrupted memory");
413     }
414   }
415   tagvalp[1]--;
416   if (!tagvalp[1]) {
417 
418     ierr  = MPI_Attr_get(icomm,Petsc_OuterComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
419     ierr = PetscMemcpy(&ocomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
420 
421     if (flg) {
422       ierr = MPI_Attr_delete(ocomm,Petsc_InnerComm_keyval);CHKERRQ(ierr);
423     }
424 
425     ierr = PetscInfo1(0,"Deleting MPI_Comm %ld\n",(long)icomm);CHKERRQ(ierr);
426     ierr = MPI_Comm_free(&icomm);CHKERRQ(ierr);
427   }
428   PetscFunctionReturn(0);
429 }
430 
431