xref: /petsc/src/sys/objects/garbage.c (revision 15229ffc342989b2bf0590a733d92c152a3348fc)
162e5d2d2SJDBetteridge #include <petsc/private/garbagecollector.h>
262e5d2d2SJDBetteridge 
362e5d2d2SJDBetteridge /* Fetches garbage hashmap from communicator */
462e5d2d2SJDBetteridge static PetscErrorCode GarbageGetHMap_Private(MPI_Comm comm, PetscGarbage *garbage)
562e5d2d2SJDBetteridge {
662e5d2d2SJDBetteridge   PetscMPIInt  flag;
762e5d2d2SJDBetteridge   PetscHMapObj garbage_map;
862e5d2d2SJDBetteridge 
962e5d2d2SJDBetteridge   PetscFunctionBegin;
1062e5d2d2SJDBetteridge   PetscCallMPI(MPI_Comm_get_attr(comm, Petsc_Garbage_HMap_keyval, garbage, &flag));
1162e5d2d2SJDBetteridge   if (!flag) {
1262e5d2d2SJDBetteridge     /* No garbage,create one */
1362e5d2d2SJDBetteridge     PetscCall(PetscHMapObjCreate(&garbage_map));
1462e5d2d2SJDBetteridge     garbage->map = garbage_map;
1562e5d2d2SJDBetteridge     PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_Garbage_HMap_keyval, garbage->ptr));
1662e5d2d2SJDBetteridge   }
173ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1862e5d2d2SJDBetteridge }
1962e5d2d2SJDBetteridge 
2062e5d2d2SJDBetteridge /*@C
2162e5d2d2SJDBetteridge   PetscObjectDelayedDestroy - Adds an object to a data structure for
2262e5d2d2SJDBetteridge   later destruction.
2362e5d2d2SJDBetteridge 
2462e5d2d2SJDBetteridge   Not Collective
2562e5d2d2SJDBetteridge 
262fe279fdSBarry Smith   Input Parameter:
2762e5d2d2SJDBetteridge . obj - object to be destroyed
2862e5d2d2SJDBetteridge 
2921532e8aSBarry Smith   Level: developer
3021532e8aSBarry Smith 
3162e5d2d2SJDBetteridge   Notes:
3262e5d2d2SJDBetteridge   Analogue to `PetscObjectDestroy()` for use in managed languages.
3362e5d2d2SJDBetteridge 
3462e5d2d2SJDBetteridge   A PETSc object is given a creation index at initialisation based on
3562e5d2d2SJDBetteridge   the communicator it was created on and the order in which it is
3662e5d2d2SJDBetteridge   created. When this function is passed a PETSc object, a pointer to
3721532e8aSBarry Smith   the object is stashed on a garbage dictionary (`PetscHMapObj`) which is
3862e5d2d2SJDBetteridge   keyed by its creation index.
3962e5d2d2SJDBetteridge 
4062e5d2d2SJDBetteridge   Objects stashed on this garbage dictionary can later be destroyed
4162e5d2d2SJDBetteridge   with a call to `PetscGarbageCleanup()`.
4262e5d2d2SJDBetteridge 
4362e5d2d2SJDBetteridge   This function is intended for use with managed languages such as
4462e5d2d2SJDBetteridge   Python or Julia, which may not destroy objects in a deterministic
4562e5d2d2SJDBetteridge   order.
4662e5d2d2SJDBetteridge 
47b0ed7cdaSConnor Ward   Serial objects (that have a communicator with size 1) are destroyed
48b0ed7cdaSConnor Ward   eagerly since deadlocks cannot occur.
49b0ed7cdaSConnor Ward 
5021532e8aSBarry Smith .seealso: `PetscGarbageCleanup()`, `PetscObjectDestroy()`
5162e5d2d2SJDBetteridge @*/
5262e5d2d2SJDBetteridge PetscErrorCode PetscObjectDelayedDestroy(PetscObject *obj)
5362e5d2d2SJDBetteridge {
54b0ed7cdaSConnor Ward   MPI_Comm     comm;
55b0ed7cdaSConnor Ward   PetscMPIInt  size;
5662e5d2d2SJDBetteridge   PetscInt     count;
5762e5d2d2SJDBetteridge   PetscGarbage garbage;
5862e5d2d2SJDBetteridge 
5962e5d2d2SJDBetteridge   PetscFunctionBegin;
604f572ea9SToby Isaac   PetscAssertPointer(obj, 1);
6162e5d2d2SJDBetteridge   /* Don't stash NULL pointers */
6262e5d2d2SJDBetteridge   if (*obj != NULL) {
6362e5d2d2SJDBetteridge     /* Elaborate check for getting non-cyclic reference counts */
6462e5d2d2SJDBetteridge     if (!(*obj)->non_cyclic_references) {
6562e5d2d2SJDBetteridge       count = --(*obj)->refct;
6662e5d2d2SJDBetteridge     } else {
6762e5d2d2SJDBetteridge       PetscCall((*obj)->non_cyclic_references(*obj, &count));
6862e5d2d2SJDBetteridge       --count;
6962e5d2d2SJDBetteridge       --(*obj)->refct;
7062e5d2d2SJDBetteridge     }
7162e5d2d2SJDBetteridge     /* Only stash if the (non-cyclic) reference count hits 0 */
7262e5d2d2SJDBetteridge     if (count == 0) {
7362e5d2d2SJDBetteridge       (*obj)->refct = 1;
74b0ed7cdaSConnor Ward       PetscCall(PetscObjectGetComm(*obj, &comm));
75b0ed7cdaSConnor Ward       PetscCallMPI(MPI_Comm_size(comm, &size));
76b0ed7cdaSConnor Ward       /* Eagerly destroy serial objects */
77b0ed7cdaSConnor Ward       if (size == 1) {
78b0ed7cdaSConnor Ward         PetscCall(PetscObjectDestroy(obj));
79b0ed7cdaSConnor Ward       } else {
80b0ed7cdaSConnor Ward         PetscCall(GarbageGetHMap_Private(comm, &garbage));
8162e5d2d2SJDBetteridge         PetscCall(PetscHMapObjSet(garbage.map, (*obj)->cidx, *obj));
8262e5d2d2SJDBetteridge       }
8362e5d2d2SJDBetteridge     }
84b0ed7cdaSConnor Ward   }
8562e5d2d2SJDBetteridge   *obj = NULL;
863ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
8762e5d2d2SJDBetteridge }
8862e5d2d2SJDBetteridge 
8962e5d2d2SJDBetteridge /* Performs the intersection of 2 sorted arrays seta and setb of lengths
9062e5d2d2SJDBetteridge    lena and lenb respectively,returning the result in seta and lena
9162e5d2d2SJDBetteridge    This is an O(n) operation */
9262e5d2d2SJDBetteridge static PetscErrorCode GarbageKeySortedIntersect_Private(PetscInt64 seta[], PetscInt *lena, PetscInt64 setb[], PetscInt lenb)
9362e5d2d2SJDBetteridge {
9462e5d2d2SJDBetteridge   /* The arrays seta and setb MUST be sorted! */
95b0271995SJDBetteridge   PetscInt ii, jj = 0, counter = 0;
9662e5d2d2SJDBetteridge 
9762e5d2d2SJDBetteridge   PetscFunctionBegin;
9862e5d2d2SJDBetteridge   if (PetscDefined(USE_DEBUG)) {
9962e5d2d2SJDBetteridge     PetscBool sorted = PETSC_FALSE;
10062e5d2d2SJDBetteridge     /* In debug mode check whether the array are sorted */
10162e5d2d2SJDBetteridge     PetscCall(PetscSortedInt64(*lena, seta, &sorted));
10279528a3fSJDBetteridge     PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Provided array in argument 1 is not sorted");
10362e5d2d2SJDBetteridge     PetscCall(PetscSortedInt64(lenb, setb, &sorted));
10479528a3fSJDBetteridge     PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Provided array in argument 3 is not sorted");
10562e5d2d2SJDBetteridge   }
10662e5d2d2SJDBetteridge   for (ii = 0; ii < *lena; ii++) {
107aa624791SPierre Jolivet     while (jj < lenb && seta[ii] > setb[jj]) jj++;
108b0271995SJDBetteridge     if (jj >= lenb) break;
109b0271995SJDBetteridge     if (seta[ii] == setb[jj]) {
11062e5d2d2SJDBetteridge       seta[counter] = seta[ii];
11162e5d2d2SJDBetteridge       counter++;
11262e5d2d2SJDBetteridge     }
11362e5d2d2SJDBetteridge   }
114b0271995SJDBetteridge 
11562e5d2d2SJDBetteridge   *lena = counter;
1163ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
11762e5d2d2SJDBetteridge }
11862e5d2d2SJDBetteridge 
11962e5d2d2SJDBetteridge /* Wrapper to create MPI reduce operator for set intersection */
12062e5d2d2SJDBetteridge void PetscGarbageKeySortedIntersect(void *inset, void *inoutset, PetscMPIInt *length, MPI_Datatype *dtype)
12162e5d2d2SJDBetteridge {
12262e5d2d2SJDBetteridge   PetscInt64 *seta, *setb;
12362e5d2d2SJDBetteridge 
12462e5d2d2SJDBetteridge   seta = (PetscInt64 *)inoutset;
12562e5d2d2SJDBetteridge   setb = (PetscInt64 *)inset;
12662e5d2d2SJDBetteridge 
1273ba16761SJacob Faibussowitsch   PetscCallAbort(PETSC_COMM_SELF, GarbageKeySortedIntersect_Private(&seta[1], (PetscInt *)&seta[0], &setb[1], (PetscInt)setb[0]));
12862e5d2d2SJDBetteridge }
12962e5d2d2SJDBetteridge 
13062e5d2d2SJDBetteridge /* Performs a collective allreduce intersection of one array per rank */
1318e89d99cSJDBetteridge PetscErrorCode GarbageKeyAllReduceIntersect_Private(MPI_Comm comm, PetscInt64 *set, PetscInt *entries)
13262e5d2d2SJDBetteridge {
13362e5d2d2SJDBetteridge   PetscInt     ii, max_entries;
13462e5d2d2SJDBetteridge   PetscInt64  *sendset, *recvset;
13562e5d2d2SJDBetteridge   MPI_Datatype keyset_type;
13662e5d2d2SJDBetteridge 
13762e5d2d2SJDBetteridge   PetscFunctionBegin;
13862e5d2d2SJDBetteridge   /* Sort keys first for use with `GarbageKeySortedIntersect_Private()`*/
13962e5d2d2SJDBetteridge   PetscCall(PetscSortInt64(*entries, set));
14062e5d2d2SJDBetteridge 
14162e5d2d2SJDBetteridge   /* Get the maximum size of all key sets */
142712fec58SPierre Jolivet   PetscCall(MPIU_Allreduce(entries, &max_entries, 1, MPIU_INT, MPI_MAX, comm));
14362e5d2d2SJDBetteridge   PetscCall(PetscMalloc1(max_entries + 1, &sendset));
14462e5d2d2SJDBetteridge   PetscCall(PetscMalloc1(max_entries + 1, &recvset));
14562e5d2d2SJDBetteridge   sendset[0] = (PetscInt64)*entries;
14662e5d2d2SJDBetteridge   for (ii = 1; ii < *entries + 1; ii++) sendset[ii] = set[ii - 1];
14762e5d2d2SJDBetteridge 
14862e5d2d2SJDBetteridge   /* Create a custom data type to hold the set */
14962e5d2d2SJDBetteridge   PetscCallMPI(MPI_Type_contiguous(max_entries + 1, MPIU_INT64, &keyset_type));
15062e5d2d2SJDBetteridge   /* PetscCallMPI(MPI_Type_set_name(keyset_type,"PETSc garbage key set type")); */
15162e5d2d2SJDBetteridge   PetscCallMPI(MPI_Type_commit(&keyset_type));
15262e5d2d2SJDBetteridge 
15362e5d2d2SJDBetteridge   /* Perform custom intersect reduce operation over sets */
15462e5d2d2SJDBetteridge   PetscCallMPI(MPI_Allreduce(sendset, recvset, 1, keyset_type, Petsc_Garbage_SetIntersectOp, comm));
15562e5d2d2SJDBetteridge 
15662e5d2d2SJDBetteridge   PetscCallMPI(MPI_Type_free(&keyset_type));
15762e5d2d2SJDBetteridge 
15862e5d2d2SJDBetteridge   *entries = (PetscInt)recvset[0];
15962e5d2d2SJDBetteridge   for (ii = 0; ii < *entries; ii++) set[ii] = recvset[ii + 1];
16062e5d2d2SJDBetteridge 
16162e5d2d2SJDBetteridge   PetscCall(PetscFree(sendset));
16262e5d2d2SJDBetteridge   PetscCall(PetscFree(recvset));
1633ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
16462e5d2d2SJDBetteridge }
16562e5d2d2SJDBetteridge 
16662e5d2d2SJDBetteridge /*@C
16762e5d2d2SJDBetteridge   PetscGarbageCleanup - Destroys objects placed in the garbage by
16821532e8aSBarry Smith   `PetscObjectDelayedDestroy()`.
16962e5d2d2SJDBetteridge 
17062e5d2d2SJDBetteridge   Collective
17162e5d2d2SJDBetteridge 
1722fe279fdSBarry Smith   Input Parameter:
17321532e8aSBarry Smith . comm - MPI communicator over which to perform collective cleanup
17421532e8aSBarry Smith 
17521532e8aSBarry Smith   Level: developer
17662e5d2d2SJDBetteridge 
17762e5d2d2SJDBetteridge   Notes:
17862e5d2d2SJDBetteridge   Implements a collective garbage collection.
17962e5d2d2SJDBetteridge   A per- MPI communicator garbage dictionary is created to store
18021532e8aSBarry Smith   references to objects destroyed using `PetscObjectDelayedDestroy()`.
18121532e8aSBarry Smith   Objects that appear in this dictionary on all MPI processes can be destroyed
18221532e8aSBarry Smith   by calling `PetscGarbageCleanup()`.
18362e5d2d2SJDBetteridge 
18410450e9eSJacob Faibussowitsch   This is done as follows\:
18562e5d2d2SJDBetteridge   1.  Keys of the garbage dictionary, which correspond to the creation
18662e5d2d2SJDBetteridge   indices of the objects stashed, are sorted.
18762e5d2d2SJDBetteridge   2.  A collective intersection of dictionary keys is performed by all
18862e5d2d2SJDBetteridge   ranks in the communicator.
18962e5d2d2SJDBetteridge   3.  The intersection is broadcast back to all ranks in the
19062e5d2d2SJDBetteridge   communicator.
19162e5d2d2SJDBetteridge   4.  The objects on the dictionary are collectively destroyed in
19262e5d2d2SJDBetteridge   creation index order using a call to PetscObjectDestroy().
19362e5d2d2SJDBetteridge 
19462e5d2d2SJDBetteridge   This function is intended for use with managed languages such as
19562e5d2d2SJDBetteridge   Python or Julia, which may not destroy objects in a deterministic
19662e5d2d2SJDBetteridge   order.
19762e5d2d2SJDBetteridge 
198aec76313SJacob Faibussowitsch .seealso: `PetscObjectDelayedDestroy()`
19962e5d2d2SJDBetteridge @*/
20062e5d2d2SJDBetteridge PetscErrorCode PetscGarbageCleanup(MPI_Comm comm)
20162e5d2d2SJDBetteridge {
20262e5d2d2SJDBetteridge   PetscInt     ii, entries, offset;
20362e5d2d2SJDBetteridge   PetscInt64  *keys;
20462e5d2d2SJDBetteridge   PetscObject  obj;
20562e5d2d2SJDBetteridge   PetscGarbage garbage;
20662e5d2d2SJDBetteridge 
20762e5d2d2SJDBetteridge   PetscFunctionBegin;
20862e5d2d2SJDBetteridge   /* Duplicate comm to prevent it being cleaned up by PetscObjectDestroy() */
20962e5d2d2SJDBetteridge   PetscCall(PetscCommDuplicate(comm, &comm, NULL));
21062e5d2d2SJDBetteridge 
21162e5d2d2SJDBetteridge   /* Grab garbage from comm and remove it
21262e5d2d2SJDBetteridge    this avoids calling PetscCommDestroy() and endlessly recursing */
21362e5d2d2SJDBetteridge   PetscCall(GarbageGetHMap_Private(comm, &garbage));
21462e5d2d2SJDBetteridge   PetscCallMPI(MPI_Comm_delete_attr(comm, Petsc_Garbage_HMap_keyval));
21562e5d2d2SJDBetteridge 
21662e5d2d2SJDBetteridge   /* Get keys from garbage hash map */
21762e5d2d2SJDBetteridge   PetscCall(PetscHMapObjGetSize(garbage.map, &entries));
21862e5d2d2SJDBetteridge   PetscCall(PetscMalloc1(entries, &keys));
21962e5d2d2SJDBetteridge   offset = 0;
22062e5d2d2SJDBetteridge   PetscCall(PetscHMapObjGetKeys(garbage.map, &offset, keys));
22162e5d2d2SJDBetteridge 
22262e5d2d2SJDBetteridge   /* Gather and intersect */
22362e5d2d2SJDBetteridge   PetscCall(GarbageKeyAllReduceIntersect_Private(comm, keys, &entries));
22462e5d2d2SJDBetteridge 
225*15229ffcSPierre Jolivet   /* Collectively destroy objects that appear in garbage in
22662e5d2d2SJDBetteridge      creation index order */
22762e5d2d2SJDBetteridge   for (ii = 0; ii < entries; ii++) {
22862e5d2d2SJDBetteridge     PetscCall(PetscHMapObjGet(garbage.map, keys[ii], &obj));
22962e5d2d2SJDBetteridge     PetscCall(PetscObjectDestroy(&obj));
23062e5d2d2SJDBetteridge     PetscCall(PetscFree(obj));
23162e5d2d2SJDBetteridge     PetscCall(PetscHMapObjDel(garbage.map, keys[ii]));
23262e5d2d2SJDBetteridge   }
23362e5d2d2SJDBetteridge   PetscCall(PetscFree(keys));
23462e5d2d2SJDBetteridge 
23562e5d2d2SJDBetteridge   /* Put garbage back */
23662e5d2d2SJDBetteridge   PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_Garbage_HMap_keyval, garbage.ptr));
23762e5d2d2SJDBetteridge   PetscCall(PetscCommDestroy(&comm));
2383ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
23962e5d2d2SJDBetteridge }
24062e5d2d2SJDBetteridge 
24162e5d2d2SJDBetteridge /* Utility function for printing the contents of the garbage on a given comm */
24262e5d2d2SJDBetteridge PetscErrorCode PetscGarbageView(MPI_Comm comm, PetscViewer viewer)
24362e5d2d2SJDBetteridge {
24462e5d2d2SJDBetteridge   char         text[64];
24562e5d2d2SJDBetteridge   PetscInt     ii, entries, offset;
24662e5d2d2SJDBetteridge   PetscInt64  *keys;
24762e5d2d2SJDBetteridge   PetscObject  obj;
24862e5d2d2SJDBetteridge   PetscGarbage garbage;
24962e5d2d2SJDBetteridge   PetscMPIInt  rank;
25062e5d2d2SJDBetteridge 
25162e5d2d2SJDBetteridge   PetscFunctionBegin;
25262e5d2d2SJDBetteridge   PetscCall(PetscPrintf(comm, "PETSc garbage on "));
25362e5d2d2SJDBetteridge   if (comm == PETSC_COMM_WORLD) {
25462e5d2d2SJDBetteridge     PetscCall(PetscPrintf(comm, "PETSC_COMM_WORLD\n"));
25562e5d2d2SJDBetteridge   } else if (comm == PETSC_COMM_SELF) {
25662e5d2d2SJDBetteridge     PetscCall(PetscPrintf(comm, "PETSC_COMM_SELF\n"));
25762e5d2d2SJDBetteridge   } else {
25862e5d2d2SJDBetteridge     PetscCall(PetscPrintf(comm, "UNKNOWN_COMM\n"));
25962e5d2d2SJDBetteridge   }
26062e5d2d2SJDBetteridge   PetscCall(PetscCommDuplicate(comm, &comm, NULL));
26162e5d2d2SJDBetteridge   PetscCall(GarbageGetHMap_Private(comm, &garbage));
26262e5d2d2SJDBetteridge 
26362e5d2d2SJDBetteridge   /* Get keys from garbage hash map and sort */
26462e5d2d2SJDBetteridge   PetscCall(PetscHMapObjGetSize(garbage.map, &entries));
26562e5d2d2SJDBetteridge   PetscCall(PetscMalloc1(entries, &keys));
26662e5d2d2SJDBetteridge   offset = 0;
26762e5d2d2SJDBetteridge   PetscCall(PetscHMapObjGetKeys(garbage.map, &offset, keys));
26862e5d2d2SJDBetteridge 
26962e5d2d2SJDBetteridge   /* Pretty print entries in a table */
27062e5d2d2SJDBetteridge   PetscCallMPI(MPI_Comm_rank(comm, &rank));
27162e5d2d2SJDBetteridge   PetscCall(PetscSynchronizedPrintf(comm, "Rank %i:: ", rank));
27262e5d2d2SJDBetteridge   PetscCall(PetscFormatConvert("Total entries: %D\n", text));
27362e5d2d2SJDBetteridge   PetscCall(PetscSynchronizedPrintf(comm, text, entries));
27462e5d2d2SJDBetteridge   if (entries) {
27562e5d2d2SJDBetteridge     PetscCall(PetscSynchronizedPrintf(comm, "| Key   | Type                   | Name                             | Object ID |\n"));
27662e5d2d2SJDBetteridge     PetscCall(PetscSynchronizedPrintf(comm, "|-------|------------------------|----------------------------------|-----------|\n"));
27762e5d2d2SJDBetteridge   }
27862e5d2d2SJDBetteridge   for (ii = 0; ii < entries; ii++) {
27962e5d2d2SJDBetteridge     PetscCall(PetscHMapObjGet(garbage.map, keys[ii], &obj));
28062e5d2d2SJDBetteridge     PetscCall(PetscFormatConvert("| %5" PetscInt64_FMT " | %-22s | %-32s | %6D    |\n", text));
28162e5d2d2SJDBetteridge     PetscCall(PetscSynchronizedPrintf(comm, text, keys[ii], obj->class_name, obj->description, obj->id));
28262e5d2d2SJDBetteridge   }
28362e5d2d2SJDBetteridge   PetscCall(PetscSynchronizedFlush(comm, PETSC_STDOUT));
28462e5d2d2SJDBetteridge 
28562e5d2d2SJDBetteridge   PetscCall(PetscFree(keys));
28662e5d2d2SJDBetteridge   PetscCall(PetscCommDestroy(&comm));
2873ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
28862e5d2d2SJDBetteridge }
289