xref: /petsc/src/sys/objects/tagm.c (revision ba8a68b80012e103e52d15749afb2a3a8a06877f)
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 references of the communicator there, when it equals zero the communicator may be freed.
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 tag storage when a communicator is freed.
34 
35    This is called by MPI, not by users.
36 
37    Note: this is declared extern "C" because it is passed to MPI_Keyval_create
38 
39 */
40 PetscMPIInt PETSC_DLLEXPORT MPIAPI Petsc_DelTag(MPI_Comm comm,PetscMPIInt keyval,void* attr_val,void* extra_state)
41 {
42   PetscErrorCode ierr;
43 
44   PetscFunctionBegin;
45   ierr = PetscInfo1(0,"Deleting tag data in an MPI_Comm %ld\n",(long)comm);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
46   ierr = PetscFree(attr_val);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
47   PetscFunctionReturn(MPI_SUCCESS);
48 }
49 EXTERN_C_END
50 
51 EXTERN_C_BEGIN
52 #undef __FUNCT__
53 #define __FUNCT__ "Petsc_DelComm"
54 /*
55   This does not actually free anything, it simply marks when a reference count to an internal MPI_Comm reaches zero and the
56   the external MPI_Comm drops its reference to the internal MPI_Comm
57 
58   This is called by MPI, not by users.
59 
60   Note: this is declared extern "C" because it is passed to MPI_Keyval_create
61 
62 */
63 PetscMPIInt PETSC_DLLEXPORT MPIAPI Petsc_DelComm(MPI_Comm comm,PetscMPIInt keyval,void* attr_val,void* extra_state)
64 {
65   PetscErrorCode ierr;
66 
67   PetscFunctionBegin;
68   ierr = PetscInfo1(0,"Deleting PETSc communicator imbedded in a user MPI_Comm %ld\n",(long)comm);if (ierr) PetscFunctionReturn((PetscMPIInt)ierr);
69   /* actually don't delete anything because we cannot increase the reference count of the communicator anyways */
70   PetscFunctionReturn(MPI_SUCCESS);
71 }
72 EXTERN_C_END
73 
74 #undef __FUNCT__
75 #define __FUNCT__ "PetscObjectGetNewTag"
76 /*@C
77     PetscObjectGetNewTag - Gets a unique new tag from a PETSc object. All
78     processors that share the object MUST call this routine EXACTLY the same
79     number of times.  This tag should only be used with the current objects
80     communicator; do NOT use it with any other MPI communicator.
81 
82     Collective on PetscObject
83 
84     Input Parameter:
85 .   obj - the PETSc object; this must be cast with a (PetscObject), for example,
86          PetscObjectGetNewTag((PetscObject)mat,&tag);
87 
88     Output Parameter:
89 .   tag - the new tag
90 
91     Level: developer
92 
93     Concepts: tag^getting
94     Concepts: message tag^getting
95     Concepts: MPI message tag^getting
96 
97 .seealso: PetscCommGetNewTag()
98 @*/
99 PetscErrorCode PETSC_DLLEXPORT PetscObjectGetNewTag(PetscObject obj,PetscMPIInt *tag)
100 {
101   PetscErrorCode ierr;
102 
103   PetscFunctionBegin;
104   ierr = PetscCommGetNewTag(obj->comm,tag);CHKERRQ(ierr);
105   PetscFunctionReturn(0);
106 }
107 
108 #undef __FUNCT__
109 #define __FUNCT__ "PetscCommGetNewTag"
110 /*@
111     PetscCommGetNewTag - Gets a unique new tag from a PETSc communicator. All
112     processors that share the communicator MUST call this routine EXACTLY the same
113     number of times.  This tag should only be used with the current objects
114     communicator; do NOT use it with any other MPI communicator.
115 
116     Collective on comm
117 
118     Input Parameter:
119 .   comm - the MPI communicator
120 
121     Output Parameter:
122 .   tag - the new tag
123 
124     Level: developer
125 
126     Concepts: tag^getting
127     Concepts: message tag^getting
128     Concepts: MPI message tag^getting
129 
130 .seealso: PetscObjectGetNewTag(), PetscCommDuplicate()
131 @*/
132 PetscErrorCode PETSC_DLLEXPORT PetscCommGetNewTag(MPI_Comm comm,PetscMPIInt *tag)
133 {
134   PetscErrorCode ierr;
135   PetscMPIInt    *tagvalp=0,*maxval;
136   PetscTruth     flg;
137 
138   PetscFunctionBegin;
139   PetscValidIntPointer(tag,2);
140 
141   if (Petsc_Tag_keyval == MPI_KEYVAL_INVALID) {
142     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelTag,&Petsc_Tag_keyval,(void*)0);CHKERRQ(ierr);
143     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_InnerComm_keyval,(void*)0);CHKERRQ(ierr);
144     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_OuterComm_keyval,(void*)0);CHKERRQ(ierr);
145   }
146 
147   ierr = MPI_Attr_get(comm,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
148   if (!flg) SETERRQ(PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
149 
150   if (tagvalp[0] < 1) {
151     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",tagvalp[1]);CHKERRQ(ierr);
152     ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,(void**)&maxval,(PetscMPIInt*)&flg);CHKERRQ(ierr);
153     if (!flg) {
154       SETERRQ(PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
155     }
156     tagvalp[0] = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
157   }
158 
159   *tag = tagvalp[0]--;
160 #if defined(PETSC_USE_DEBUG)
161   /*
162      Hanging here means that some processes have called PetscCommGetNewTag() and others have not.
163   */
164   ierr = MPI_Barrier(comm);CHKERRQ(ierr);
165 #endif
166   PetscFunctionReturn(0);
167 }
168 
169 #undef __FUNCT__
170 #define __FUNCT__ "PetscCommDuplicate"
171 /*@C
172   PetscCommDuplicate - Duplicates the communicator only if it is not already a PETSc communicator.
173 
174   Collective on MPI_Comm
175 
176   Input Parameters:
177 . comm_in - Input communicator
178 
179   Output Parameters:
180 + comm_out - Output communicator.  May be comm_in.
181 - first_tag - Tag available that has not already been used with this communicator (you may
182               pass in PETSC_NULL if you do not need a tag)
183 
184   PETSc communicators are just regular MPI communicators that keep track of which
185   tags have been used to prevent tag conflict. If you pass a non-PETSc communicator into
186   a PETSc creation routine it will attach a private communicator for use in the objects communications.
187   The internal MPI_Comm is used to perform all the MPI calls for PETSc, the outter MPI_Comm is a user
188   level MPI_Comm that may be performing communication for the user or other library and so IS NOT used by PETSc.
189 
190   Level: developer
191 
192   Concepts: communicator^duplicate
193 
194 .seealso: PetscObjectGetNewTag(), PetscCommGetNewTag(), PetscCommDestroy()
195 @*/
196 PetscErrorCode PETSC_DLLEXPORT PetscCommDuplicate(MPI_Comm comm_in,MPI_Comm *comm_out,PetscMPIInt* first_tag)
197 {
198   PetscErrorCode ierr;
199   PetscMPIInt    *tagvalp,*maxval;
200   PetscTruth     flg;
201 
202   PetscFunctionBegin;
203   if (Petsc_Tag_keyval == MPI_KEYVAL_INVALID) {
204     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelTag,&Petsc_Tag_keyval,(void*)0);CHKERRQ(ierr);
205     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_InnerComm_keyval,(void*)0);CHKERRQ(ierr);
206     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_OuterComm_keyval,(void*)0);CHKERRQ(ierr);
207   }
208   ierr = MPI_Attr_get(comm_in,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
209 
210   if (!flg) {  /* this is NOT a PETSc comm */
211     void *ptr;
212     /* check if this communicator has a PETSc communicator imbedded in it */
213     ierr = MPI_Attr_get(comm_in,Petsc_InnerComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
214     if (!flg) {
215       /* This communicator is not yet known to this system, so we duplicate it and make an internal communicator */
216       ierr       = MPI_Comm_dup(comm_in,comm_out);CHKERRQ(ierr);
217       ierr       = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,(void**)&maxval,(PetscMPIInt*)&flg);CHKERRQ(ierr);
218       if (!flg) {
219         SETERRQ(PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
220       }
221       ierr = PetscMalloc(2*sizeof(PetscMPIInt),&tagvalp);CHKERRQ(ierr);
222       tagvalp[0] = *maxval;
223       tagvalp[1] = 0;
224       ierr       = MPI_Attr_put(*comm_out,Petsc_Tag_keyval,tagvalp);CHKERRQ(ierr);
225       ierr = PetscInfo3(0,"Duplicating a communicator %ld %ld max tags = %d\n",(long)comm_in,(long)*comm_out,*maxval);CHKERRQ(ierr);
226 
227       /* save PETSc communicator inside user communicator, so we can get it next time */
228       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
229       ierr = PetscMemcpy(&ptr,comm_out,sizeof(MPI_Comm));CHKERRQ(ierr);
230       ierr = MPI_Attr_put(comm_in,Petsc_InnerComm_keyval,ptr);CHKERRQ(ierr);
231       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
232       ierr = PetscMemcpy(&ptr,&comm_in,sizeof(MPI_Comm));CHKERRQ(ierr);
233       ierr = MPI_Attr_put(*comm_out,Petsc_OuterComm_keyval,ptr);CHKERRQ(ierr);
234     } else {
235       /* pull out the inner MPI_Comm and hand it back to the caller */
236       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
237       ierr = PetscMemcpy(comm_out,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
238       ierr = MPI_Attr_get(*comm_out,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
239       if (!flg) {
240         SETERRQ(PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tagvalp attribute set");
241       }
242       ierr = PetscInfo2(0,"Using internal PETSc communicator %ld %ld\n",(long)comm_in,(long)*comm_out);CHKERRQ(ierr);
243     }
244   } else {
245     *comm_out = comm_in;
246   }
247 
248 #if defined(PETSC_USE_DEBUG)
249   /*
250      Hanging here means that some processes have called PetscCommDuplicate() and others have not.
251      This likley means that a subset of processes in a MPI_Comm have attempted to create a PetscObject!
252      ALL processes that share a communicator MUST shared objects created from that communicator.
253   */
254   ierr = MPI_Barrier(comm_in);CHKERRQ(ierr);
255 #endif
256 
257   if (tagvalp[0] < 1) {
258     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",tagvalp[1]);CHKERRQ(ierr);
259     ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,(void**)&maxval,(PetscMPIInt*)&flg);CHKERRQ(ierr);
260     if (!flg) {
261       SETERRQ(PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
262     }
263     tagvalp[0] = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
264   }
265 
266   if (first_tag) {
267     *first_tag = tagvalp[0]--;
268     ierr = PetscInfo1(0,"  returning tag %ld\n",(long)*first_tag);CHKERRQ(ierr);
269   }
270   tagvalp[1]++; /* number of references to this comm */
271   PetscFunctionReturn(0);
272 }
273 
274 #undef __FUNCT__
275 #define __FUNCT__ "PetscCommDestroy"
276 /*@C
277    PetscCommDestroy - Frees communicator.  Use in conjunction with PetscCommDuplicate().
278 
279    Collective on MPI_Comm
280 
281    Input Parameter:
282 .  comm - the communicator to free
283 
284    Level: developer
285 
286    Concepts: communicator^destroy
287 
288 .seealso:   PetscCommDuplicate()
289 @*/
290 PetscErrorCode PETSC_DLLEXPORT PetscCommDestroy(MPI_Comm *comm)
291 {
292   PetscErrorCode ierr;
293   PetscMPIInt    *tagvalp;
294   PetscTruth     flg;
295   MPI_Comm       icomm = *comm,ocomm;
296   void           *ptr;
297 
298   PetscFunctionBegin;
299   if (Petsc_Tag_keyval == MPI_KEYVAL_INVALID) {
300     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelTag,&Petsc_Tag_keyval,(void*)0);CHKERRQ(ierr);
301     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_InnerComm_keyval,(void*)0);CHKERRQ(ierr);
302     ierr = MPI_Keyval_create(MPI_NULL_COPY_FN,Petsc_DelComm,&Petsc_OuterComm_keyval,(void*)0);CHKERRQ(ierr);
303   }
304   ierr = MPI_Attr_get(icomm,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
305   if (!flg) { /* not a PETSc comm, check if it has an inner comm */
306     ierr  = MPI_Attr_get(icomm,Petsc_InnerComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
307     if (!flg) {
308       SETERRQ(PETSC_ERR_ARG_CORRUPT,"MPI_Comm does not have tagvalues nor does it have inner MPI_Comm");
309     }
310     /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
311     ierr = PetscMemcpy(&icomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
312     ierr = MPI_Attr_get(icomm,Petsc_Tag_keyval,(void**)&tagvalp,(PetscMPIInt*)&flg);CHKERRQ(ierr);
313     if (!flg) {
314       SETERRQ(PETSC_ERR_ARG_CORRUPT,"Inner MPI_Comm does not have expected tagvalues, problem with corrupted memory");
315     }
316   }
317   tagvalp[1]--;
318   if (!tagvalp[1]) {
319 
320     /* if MPI_Comm has outter comm then remove reference to inner MPI_Comm from outter MPI_Comm */
321     ierr  = MPI_Attr_get(icomm,Petsc_OuterComm_keyval,&ptr,(PetscMPIInt*)&flg);CHKERRQ(ierr);
322     /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
323     ierr = PetscMemcpy(&ocomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
324     if (flg) {
325       ierr = MPI_Attr_delete(ocomm,Petsc_InnerComm_keyval);CHKERRQ(ierr);
326     }
327 
328     ierr = PetscInfo1(0,"Deleting PETSc MPI_Comm %ld\n",(long)icomm);CHKERRQ(ierr);
329     ierr = MPI_Comm_free(&icomm);CHKERRQ(ierr);
330   }
331   PetscFunctionReturn(0);
332 }
333 
334