xref: /petsc/src/sys/objects/tagm.c (revision 8468deee71cff1b2e1654d636f4c743a8bd8dfe8)
1 
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 #include <petsc-private/threadcommimpl.h>
11 /* ---------------------------------------------------------------- */
12 /*
13    A simple way to manage tags inside a communicator.
14 
15    It uses the attributes to determine if a new communicator
16       is needed and to store the available tags.
17 
18 */
19 
20 
21 #undef __FUNCT__
22 #define __FUNCT__ "PetscObjectGetNewTag"
23 /*@C
24     PetscObjectGetNewTag - Gets a unique new tag from a PETSc object. All
25     processors that share the object MUST call this routine EXACTLY the same
26     number of times.  This tag should only be used with the current objects
27     communicator; do NOT use it with any other MPI communicator.
28 
29     Collective on PetscObject
30 
31     Input Parameter:
32 .   obj - the PETSc object; this must be cast with a (PetscObject), for example,
33          PetscObjectGetNewTag((PetscObject)mat,&tag);
34 
35     Output Parameter:
36 .   tag - the new tag
37 
38     Level: developer
39 
40     Concepts: tag^getting
41     Concepts: message tag^getting
42     Concepts: MPI message tag^getting
43 
44 .seealso: PetscCommGetNewTag()
45 @*/
46 PetscErrorCode  PetscObjectGetNewTag(PetscObject obj,PetscMPIInt *tag)
47 {
48   PetscErrorCode ierr;
49 
50   PetscFunctionBegin;
51   ierr = PetscCommGetNewTag(obj->comm,tag);CHKERRQ(ierr);
52   PetscFunctionReturn(0);
53 }
54 
55 #undef __FUNCT__
56 #define __FUNCT__ "PetscCommGetNewTag"
57 /*@
58     PetscCommGetNewTag - Gets a unique new tag from a PETSc communicator. All
59     processors that share the communicator MUST call this routine EXACTLY the same
60     number of times.  This tag should only be used with the current objects
61     communicator; do NOT use it with any other MPI communicator.
62 
63     Collective on comm
64 
65     Input Parameter:
66 .   comm - the MPI communicator
67 
68     Output Parameter:
69 .   tag - the new tag
70 
71     Level: developer
72 
73     Concepts: tag^getting
74     Concepts: message tag^getting
75     Concepts: MPI message tag^getting
76 
77 .seealso: PetscObjectGetNewTag(), PetscCommDuplicate()
78 @*/
79 PetscErrorCode  PetscCommGetNewTag(MPI_Comm comm,PetscMPIInt *tag)
80 {
81   PetscErrorCode   ierr;
82   PetscCommCounter *counter;
83   PetscMPIInt      *maxval,flg;
84 
85   PetscFunctionBegin;
86   PetscValidIntPointer(tag,2);
87 
88   ierr = MPI_Attr_get(comm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
89   if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Bad MPI communicator supplied; must be a PETSc communicator");
90 
91   if (counter->tag < 1) {
92     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",counter->refcount);CHKERRQ(ierr);
93     ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
94     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
95     counter->tag = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
96   }
97 
98   *tag = counter->tag--;
99 #if defined(PETSC_USE_DEBUG)
100   /*
101      Hanging here means that some processes have called PetscCommGetNewTag() and others have not.
102   */
103   ierr = MPI_Barrier(comm);CHKERRQ(ierr);
104 #endif
105   PetscFunctionReturn(0);
106 }
107 
108 #undef __FUNCT__
109 #define __FUNCT__ "PetscCommDuplicate"
110 /*@C
111   PetscCommDuplicate - Duplicates the communicator only if it is not already a PETSc communicator.
112 
113   Collective on MPI_Comm
114 
115   Input Parameters:
116 . comm_in - Input communicator
117 
118   Output Parameters:
119 + comm_out - Output communicator.  May be comm_in.
120 - first_tag - Tag available that has not already been used with this communicator (you may
121               pass in PETSC_NULL if you do not need a tag)
122 
123   PETSc communicators are just regular MPI communicators that keep track of which
124   tags have been used to prevent tag conflict. If you pass a non-PETSc communicator into
125   a PETSc creation routine it will attach a private communicator for use in the objects communications.
126   The internal MPI_Comm is used to perform all the MPI calls for PETSc, the outer MPI_Comm is a user
127   level MPI_Comm that may be performing communication for the user or other library and so IS NOT used by PETSc.
128 
129   Level: developer
130 
131   Concepts: communicator^duplicate
132 
133 .seealso: PetscObjectGetNewTag(), PetscCommGetNewTag(), PetscCommDestroy()
134 @*/
135 PetscErrorCode  PetscCommDuplicate(MPI_Comm comm_in,MPI_Comm *comm_out,PetscMPIInt *first_tag)
136 {
137   PetscErrorCode   ierr;
138   PetscCommCounter *counter;
139   PetscMPIInt      *maxval,flg;
140   PetscInt         trank;
141   PetscThreadComm  tcomm;
142 
143   PetscFunctionBegin;
144   ierr = MPI_Attr_get(comm_in,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
145 
146   if (!flg) {  /* this is NOT a PETSc comm */
147     void *ptr;
148     /* check if this communicator has a PETSc communicator imbedded in it */
149     ierr = MPI_Attr_get(comm_in,Petsc_InnerComm_keyval,&ptr,&flg);CHKERRQ(ierr);
150     if (!flg) {
151       /* This communicator is not yet known to this system, so we duplicate it and make an internal communicator */
152       ierr = MPI_Comm_dup(comm_in,comm_out);CHKERRQ(ierr);
153       ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
154       if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
155       ierr = PetscMalloc(sizeof(PetscCommCounter),&counter);CHKERRQ(ierr);
156 
157       counter->tag       = *maxval;
158       counter->refcount  = 0;
159       counter->namecount = 0;
160 
161       ierr = MPI_Attr_put(*comm_out,Petsc_Counter_keyval,counter);CHKERRQ(ierr);
162       ierr = PetscInfo3(0,"Duplicating a communicator %ld %ld max tags = %d\n",(long)comm_in,(long)*comm_out,*maxval);CHKERRQ(ierr);
163 
164       /* save PETSc communicator inside user communicator, so we can get it next time */
165       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
166       ierr = PetscMemcpy(&ptr,comm_out,sizeof(MPI_Comm));CHKERRQ(ierr);
167       ierr = MPI_Attr_put(comm_in,Petsc_InnerComm_keyval,ptr);CHKERRQ(ierr);
168       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
169       ierr = PetscMemcpy(&ptr,&comm_in,sizeof(MPI_Comm));CHKERRQ(ierr);
170       ierr = MPI_Attr_put(*comm_out,Petsc_OuterComm_keyval,ptr);CHKERRQ(ierr);
171     } else {
172       /* pull out the inner MPI_Comm and hand it back to the caller */
173       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
174       ierr = PetscMemcpy(comm_out,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
175       ierr = MPI_Attr_get(*comm_out,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
176       if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_PLIB,"Inner PETSc communicator does not have its tag/name counter attribute set");
177       ierr = PetscInfo2(0,"Using internal PETSc communicator %ld %ld\n",(long)comm_in,(long)*comm_out);CHKERRQ(ierr);
178     }
179   } else *comm_out = comm_in;
180 
181 #if defined(PETSC_USE_DEBUG)
182   /*
183      Hanging here means that some processes have called PetscCommDuplicate() and others have not.
184      This likley means that a subset of processes in a MPI_Comm have attempted to create a PetscObject!
185      ALL processes that share a communicator MUST shared objects created from that communicator.
186   */
187   ierr = MPI_Barrier(comm_in);CHKERRQ(ierr);
188 #endif
189 
190   if (counter->tag < 1) {
191     ierr = PetscInfo1(0,"Out of tags for object, starting to recycle. Comm reference count %d\n",counter->refcount);CHKERRQ(ierr);
192     ierr = MPI_Attr_get(MPI_COMM_WORLD,MPI_TAG_UB,&maxval,&flg);CHKERRQ(ierr);
193     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_LIB,"MPI error: MPI_Attr_get() is not returning a MPI_TAG_UB");
194     counter->tag = *maxval - 128; /* hope that any still active tags were issued right at the beginning of the run */
195   }
196 
197   if (first_tag) *first_tag = counter->tag--;
198 
199   ierr = MPI_Attr_get(*comm_out,Petsc_ThreadComm_keyval,(PetscThreadComm*)&tcomm,&flg);CHKERRQ(ierr);
200   if (!flg) {
201     /* Threadcomm does not exist on this communicator, get the global threadcomm and attach it to this communicator */
202     ierr = PetscCommGetThreadComm(PETSC_COMM_WORLD,&tcomm);CHKERRQ(ierr);
203     ierr = PetscThreadCommAttach(*comm_out,tcomm);CHKERRQ(ierr);
204   }
205   /* Only the main thread updates counter->refcount */
206   ierr = PetscThreadCommGetRank(tcomm,&trank);CHKERRQ(ierr);
207   if (!trank) counter->refcount++; /* number of references to this comm */
208   PetscFunctionReturn(0);
209 }
210 
211 #undef __FUNCT__
212 #define __FUNCT__ "PetscCommDestroy"
213 /*@C
214    PetscCommDestroy - Frees communicator.  Use in conjunction with PetscCommDuplicate().
215 
216    Collective on MPI_Comm
217 
218    Input Parameter:
219 .  comm - the communicator to free
220 
221    Level: developer
222 
223    Concepts: communicator^destroy
224 
225 .seealso:   PetscCommDuplicate()
226 @*/
227 PetscErrorCode  PetscCommDestroy(MPI_Comm *comm)
228 {
229   PetscErrorCode   ierr;
230   PetscCommCounter *counter;
231   PetscMPIInt      flg;
232   MPI_Comm         icomm = *comm,ocomm;
233   void             *ptr;
234   PetscThreadComm  tcomm;
235 
236   PetscFunctionBegin;
237   if (*comm == MPI_COMM_NULL) PetscFunctionReturn(0);
238   ierr = MPI_Attr_get(icomm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
239   if (!flg) { /* not a PETSc comm, check if it has an inner comm */
240     ierr = MPI_Attr_get(icomm,Petsc_InnerComm_keyval,&ptr,&flg);CHKERRQ(ierr);
241     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"MPI_Comm does not have tag/name counter nor does it have inner MPI_Comm");
242     /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
243     ierr = PetscMemcpy(&icomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
244     ierr = MPI_Attr_get(icomm,Petsc_Counter_keyval,&counter,&flg);CHKERRQ(ierr);
245     if (!flg) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Inner MPI_Comm does not have expected tag/name counter, problem with corrupted memory");
246   }
247 
248   /* Only the main thread updates counter->refcount */
249   ierr = MPI_Attr_get(icomm,Petsc_ThreadComm_keyval,(PetscThreadComm*)&tcomm,&flg);CHKERRQ(ierr);
250   if (flg) {
251     PetscInt trank;
252     ierr = PetscThreadCommGetRank(tcomm,&trank);CHKERRQ(ierr);
253     /* Only thread rank 0 updates the counter */
254     if (!trank) counter->refcount--;
255   } else counter->refcount--;
256 
257   if (!counter->refcount) {
258     /* if MPI_Comm has outer comm then remove reference to inner MPI_Comm from outer MPI_Comm */
259     ierr = MPI_Attr_get(icomm,Petsc_OuterComm_keyval,&ptr,&flg);CHKERRQ(ierr);
260     if (flg) {
261       /*  Use PetscMemcpy() because casting from pointer to integer of different size is not allowed with some compilers  */
262       ierr = PetscMemcpy(&ocomm,&ptr,sizeof(MPI_Comm));CHKERRQ(ierr);
263       ierr = MPI_Attr_get(ocomm,Petsc_InnerComm_keyval,&ptr,&flg);CHKERRQ(ierr);
264       if (flg) {
265         ierr = MPI_Attr_delete(ocomm,Petsc_InnerComm_keyval);CHKERRQ(ierr);
266       } else SETERRQ2(PETSC_COMM_SELF,PETSC_ERR_ARG_CORRUPT,"Outer MPI_Comm %ld does not have expected reference to inner comm %d, problem with corrupted memory",(long int)ocomm,(long int)icomm);
267     }
268 
269     ierr = PetscInfo1(0,"Deleting PETSc MPI_Comm %ld\n",(long)icomm);CHKERRQ(ierr);
270     ierr = MPI_Comm_free(&icomm);CHKERRQ(ierr);
271   }
272   *comm = MPI_COMM_NULL;
273   PetscFunctionReturn(0);
274 }
275 
276 #undef  __FUNCT__
277 #define __FUNCT__ "PetscObjectsGetGlobalNumbering"
278 /*@C
279     PetscObjectsGetGlobalNumbering - computes a global numbering
280     of PetscObjects living on subcommunicators of a given communicator.
281     This results in a deadlock-free ordering of the subcommunicators
282     and, hence, the objects.
283 
284 
285     Collective on comm.
286 
287     Input Parameters:
288 +   comm    - MPI_Comm
289 .   len     - length of objlist
290 -   objlist - a list of PETSc objects living on subcommunicators of comm
291                 (subcommunicator ordering is assumed to be deadlock-free)
292 
293     Output Parameters:
294 +   count      - number of globally-distinct subcommunicators on objlist
295 .   numbering  - global numbers of objlist entries (allocated by user)
296 
297 
298     Level: developer
299 
300     Concepts: MPI subcomm^numbering
301 
302 @*/
303 PetscErrorCode  PetscObjectsGetGlobalNumbering(MPI_Comm comm, PetscInt len, PetscObject *objlist, PetscInt *count, PetscInt *numbering)
304 {
305   PetscErrorCode ierr;
306   PetscInt       i, roots, offset;
307   PetscMPIInt    size, rank;
308 
309   PetscFunctionBegin;
310   PetscValidPointer(objlist,3);
311   PetscValidPointer(count,4);
312   PetscValidPointer(numbering,5);
313   ierr  = MPI_Comm_size(comm, &size);CHKERRQ(ierr);
314   ierr  = MPI_Comm_rank(comm, &rank);CHKERRQ(ierr);
315   roots = 0;
316   for (i = 0; i < len; ++i) {
317     PetscMPIInt srank;
318     ierr = MPI_Comm_rank(objlist[i]->comm, &srank);CHKERRQ(ierr);
319     /* Am I the root of the i-th subcomm? */
320     if (!srank) ++roots;
321   }
322   /* Obtain the sum of all roots -- the global number of distinct subcomms. */
323   ierr = MPI_Allreduce(&roots,count,1,MPIU_INT,MPI_SUM,comm);CHKERRQ(ierr);
324   /* Now introduce a global numbering for subcomms, initially known only by subcomm roots. */
325   /*
326    At the subcomm roots number the subcomms in the subcomm-root local manner,
327    and make it global by calculating the shift.
328    */
329   ierr    = MPI_Scan(&roots,&offset,1,MPIU_INT,MPI_SUM,comm);CHKERRQ(ierr);
330   offset -= roots;
331   /* Now we are ready to broadcast global subcomm numbers within each subcomm.*/
332   /*
333      This is where the assumption of a deadlock-free ordering of the subcomms is assumed:
334      broadcast is collective on the subcomm.
335    */
336   roots = 0;
337   for (i = 0; i < len; ++i) {
338     PetscMPIInt srank;
339     numbering[i] = offset + roots; /* only meaningful if !srank. */
340 
341     ierr = MPI_Comm_rank(objlist[i]->comm, &srank);CHKERRQ(ierr);
342     ierr = MPI_Bcast(numbering+i,1,MPIU_INT,0,objlist[i]->comm);CHKERRQ(ierr);
343     if (!srank) ++roots;
344   }
345   PetscFunctionReturn(0);
346 }
347