xref: /petsc/src/sys/objects/tagm.c (revision 64075487abb64f282fbbbeb1cd75f6936ec16d51)
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 PetscErrorCode PETSC_DLLEXPORT PetscCommSynchronizeTags(MPI_Comm comm)
163 {
164   PetscMPIInt   *tagvalp = 0, tag;
165   PetscTruth     flg;
166   PetscErrorCode ierr;
167 
168   PetscFunctionBegin;
169   ierr = MPI_Attr_get(comm, Petsc_Tag_keyval, (void **) &tagvalp, (PetscMPIInt *) &flg);CHKERRQ(ierr);
170   if (!flg) {
171     MPI_Comm innerComm;
172     void    *ptr;
173 
174     /* check if this communicator has a PETSc communicator imbedded in it */
175     ierr = MPI_Attr_get(comm, Petsc_InnerComm_keyval, &ptr, (PetscMPIInt*) &flg);CHKERRQ(ierr);
176     if (!flg) {
177       SETERRQ(PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
178     } else {
179       /* We use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers */
180       ierr = PetscMemcpy(&innerComm, &ptr, sizeof(MPI_Comm));CHKERRQ(ierr);
181       ierr = MPI_Attr_get(innerComm, Petsc_Tag_keyval, (void **) &tagvalp, (PetscMPIInt *) &flg);CHKERRQ(ierr);
182       if (!flg) {
183         SETERRQ(PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tagvalp attribute set");
184       }
185     }
186   }
187   ierr = MPI_Allreduce(tagvalp, &tag, 1, MPI_INT, MPI_MIN, comm);CHKERRQ(ierr);
188   tagvalp[0] = tag;
189   ierr = PetscInfo2(0, "Reset tag for comm %ld to \n", (long) comm, tagvalp[0]);CHKERRQ(ierr);
190   PetscFunctionReturn(0);
191 }
192 
193 #undef __FUNCT__
194 #define __FUNCT__ "PetscCommCheckTags"
195 PetscErrorCode PETSC_DLLEXPORT PetscCommCheckTags(MPI_Comm comm)
196 {
197   PetscMPIInt   *tagvalp = 0, tag;
198   PetscTruth     flg;
199   PetscErrorCode ierr;
200 
201   PetscFunctionBegin;
202   ierr = MPI_Attr_get(comm, Petsc_Tag_keyval, (void **) &tagvalp, (PetscMPIInt *) &flg);CHKERRQ(ierr);
203   if (!flg) {
204     MPI_Comm innerComm;
205     void    *ptr;
206 
207     /* check if this communicator has a PETSc communicator imbedded in it */
208     ierr = MPI_Attr_get(comm, Petsc_InnerComm_keyval, &ptr, (PetscMPIInt*) &flg);CHKERRQ(ierr);
209     if (!flg) {
210       SETERRQ(PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
211     } else {
212       /* We use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers */
213       ierr = PetscMemcpy(&innerComm, &ptr, sizeof(MPI_Comm));CHKERRQ(ierr);
214       ierr = MPI_Attr_get(innerComm, Petsc_Tag_keyval, (void **) &tagvalp, (PetscMPIInt *) &flg);CHKERRQ(ierr);
215       if (!flg) {
216         SETERRQ(PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tagvalp attribute set");
217       }
218     }
219   }
220   tag = tagvalp[0];
221   ierr = MPI_Bcast(&tag, 1, MPI_INT, 0, comm);CHKERRQ(ierr);
222   if (tagvalp[0] != tag) {
223     SETERRQ2(PETSC_ERR_LIB, "Invalid tag %d should be %d", tagvalp[0], tag);
224   }
225   PetscFunctionReturn(0);
226 }
227 
228 #undef __FUNCT__
229 #define __FUNCT__ "PetscCommDuplicate"
230 /*@C
231   PetscCommDuplicate - Duplicates the communicator only if it is not already a PETSc
232                          communicator.
233 
234   Collective on MPI_Comm
235 
236   Input Parameters:
237 . comm_in - Input communicator
238 
239   Output Parameters:
240 + comm_out - Output communicator.  May be comm_in.
241 - first_tag - Tag available that has not already been used with this communicator (you may
242               pass in PETSC_NULL if you do not need a tag)
243 
244   PETSc communicators are just regular MPI communicators that keep track of which
245   tags have been used to prevent tag conflict. If you pass a non-PETSc communicator into
246   a PETSc creation routine it will attach a private communicator for use in the objects communications.
247 
248   Level: developer
249 
250   Concepts: communicator^duplicate
251 
252 .seealso: PetscObjectGetNewTag(), PetscCommGetNewTag()
253 @*/
254 PetscErrorCode PETSC_DLLEXPORT PetscCommDuplicate(MPI_Comm comm_in,MPI_Comm *comm_out,PetscMPIInt* first_tag)
255 {
256   PetscErrorCode ierr;
257   PetscMPIInt    *tagvalp,*maxval;
258   PetscTruth     flg;
259 
260   PetscFunctionBegin;
261   if (Petsc_Tag_keyval == MPI_KEYVAL_INVALID) {
262     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelTag,&Petsc_Tag_keyval,(void*)0);CHKERRQ(ierr);
263     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_InnerComm_keyval,(void*)0);CHKERRQ(ierr);
264     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_OuterComm_keyval,(void*)0);CHKERRQ(ierr);
265   }
266   ierr = MPI_Attr_get(comm_in,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
267 
268   if (!flg) {
269     void *ptr;
270     /* check if this communicator has a PETSc communicator imbedded in it */
271     ierr = MPI_Attr_get(comm_in,Petsc_InnerComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
272     if (!flg) {
273       /* This communicator is not yet known to this system, so we duplicate it and set its value */
274       ierr       = MPI_Comm_dup(comm_in,comm_out);CHKERRQ(ierr);
275       ierr       = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,(void**)&maxval,(PetscMPIInt*)&flg);CHKERRQ(ierr);
276       if (!flg) {
277         SETERRQ(PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
278       }
279       ierr = PetscMalloc(2*sizeof(PetscMPIInt),&tagvalp);CHKERRQ(ierr);
280       tagvalp[0] = *maxval;
281       tagvalp[1] = 0;
282       ierr       = MPI_Attr_put(*comm_out,Petsc_Tag_keyval,tagvalp);CHKERRQ(ierr);
283       ierr = PetscInfo3(0,"Duplicating a communicator %ld %ld max tags = %d\n",(long)comm_in,(long)*comm_out,*maxval);CHKERRQ(ierr);
284 
285       /* save PETSc communicator inside user communicator, so we can get it next time */
286       ierr = PetscMemcpy(&ptr,comm_out,sizeof(MPI_Comm));CHKERRQ(ierr);
287       ierr = MPI_Attr_put(comm_in,Petsc_InnerComm_keyval,ptr);CHKERRQ(ierr);
288       ierr = PetscMemcpy(&ptr,&comm_in,sizeof(MPI_Comm));CHKERRQ(ierr);
289       ierr = MPI_Attr_put(*comm_out,Petsc_OuterComm_keyval,ptr);CHKERRQ(ierr);
290     } else {
291       /*
292         We use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers
293       */
294       ierr = PetscMemcpy(comm_out,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
295       ierr = MPI_Attr_get(*comm_out,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
296       if (!flg) {
297         SETERRQ(PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tagvalp attribute set");
298       }
299       ierr = PetscInfo2(0,"Using internal PETSc communicator %ld %ld\n",(long)comm_in,(long)*comm_out);CHKERRQ(ierr);
300     }
301   } else {
302 #if defined(PETSC_USE_DEBUG)
303     PetscMPIInt tag;
304     ierr = MPI_Allreduce(tagvalp,&tag,1,MPI_INT,MPI_BOR,comm_in);CHKERRQ(ierr);
305     if (tag != tagvalp[0]) {
306       SETERRQ(PETSC_ERR_ARG_CORRUPT,"Communicator was used on subset of processors.");
307     }
308 #endif
309     *comm_out = comm_in;
310   }
311 
312   if (tagvalp[0] < 1) {
313     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",tagvalp[1]);CHKERRQ(ierr);
314     ierr       = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,(void**)&maxval,(PetscMPIInt*)&flg);CHKERRQ(ierr);
315     if (!flg) {
316       SETERRQ(PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
317     }
318     tagvalp[0] = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
319   }
320 
321   if (first_tag) {
322     *first_tag = tagvalp[0]--;
323   }
324   tagvalp[1]++; /* number of references to this comm */
325   PetscFunctionReturn(0);
326 }
327 
328 #undef __FUNCT__
329 #define __FUNCT__ "PetscCommDestroy"
330 /*@C
331    PetscCommDestroy - Frees communicator.  Use in conjunction with PetscCommDuplicate().
332 
333    Collective on MPI_Comm
334 
335    Input Parameter:
336 .  comm - the communicator to free
337 
338    Level: developer
339 
340    Concepts: communicator^destroy
341 
342 @*/
343 PetscErrorCode PETSC_DLLEXPORT PetscCommDestroy(MPI_Comm *comm)
344 {
345   PetscErrorCode ierr;
346   PetscMPIInt    *tagvalp;
347   PetscTruth     flg;
348   MPI_Comm       icomm = *comm,ocomm;
349   void           *ptr;
350 
351   PetscFunctionBegin;
352   if (Petsc_Tag_keyval == MPI_KEYVAL_INVALID) {
353     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelTag,&Petsc_Tag_keyval,(void*)0);CHKERRQ(ierr);
354     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_InnerComm_keyval,(void*)0);CHKERRQ(ierr);
355     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_OuterComm_keyval,(void*)0);CHKERRQ(ierr);
356   }
357   ierr = MPI_Attr_get(icomm,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
358   if (!flg) {
359     ierr  = MPI_Attr_get(icomm,Petsc_InnerComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
360     /*
361         We use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers
362     */
363     ierr = PetscMemcpy(&icomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
364     if (!flg) {
365       PetscFunctionReturn(0);
366     }
367     ierr = MPI_Attr_get(icomm,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
368     if (!flg) {
369       SETERRQ(PETSC_ERR_ARG_CORRUPT,"Error freeing MPI_Comm, problem with corrupted memory");
370     }
371   }
372   tagvalp[1]--;
373   if (!tagvalp[1]) {
374 
375     ierr  = MPI_Attr_get(icomm,Petsc_OuterComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
376     ierr = PetscMemcpy(&ocomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
377 
378     if (flg) {
379       ierr = MPI_Attr_delete(ocomm,Petsc_InnerComm_keyval);CHKERRQ(ierr);
380     }
381 
382     ierr = PetscInfo1(0,"Deleting MPI_Comm %ld\n",(long)icomm);CHKERRQ(ierr);
383     ierr = MPI_Comm_free(&icomm);CHKERRQ(ierr);
384   }
385   PetscFunctionReturn(0);
386 }
387 
388