xref: /petsc/src/sys/objects/garbage.c (revision 8e89d99caa829ca6bbb3284e0947305730d96d1e)
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   }
1762e5d2d2SJDBetteridge   PetscFunctionReturn(0);
1862e5d2d2SJDBetteridge }
1962e5d2d2SJDBetteridge 
2062e5d2d2SJDBetteridge /*@C
2162e5d2d2SJDBetteridge     PetscObjectDelayedDestroy - Adds an object to a data structure for
2262e5d2d2SJDBetteridge     later destruction.
2362e5d2d2SJDBetteridge 
2462e5d2d2SJDBetteridge     Not Collective
2562e5d2d2SJDBetteridge 
2662e5d2d2SJDBetteridge     Input Parameters:
2762e5d2d2SJDBetteridge .   obj - object to be destroyed
2862e5d2d2SJDBetteridge 
2962e5d2d2SJDBetteridge     Notes:
3062e5d2d2SJDBetteridge     Analogue to `PetscObjectDestroy()` for use in managed languages.
3162e5d2d2SJDBetteridge 
3262e5d2d2SJDBetteridge     A PETSc object is given a creation index at initialisation based on
3362e5d2d2SJDBetteridge     the communicator it was created on and the order in which it is
3462e5d2d2SJDBetteridge     created. When this function is passed a PETSc object, a pointer to
3562e5d2d2SJDBetteridge     the object is stashed on a garbage dictionary (PetscHMapObj) which is
3662e5d2d2SJDBetteridge     keyed by its creation index.
3762e5d2d2SJDBetteridge 
3862e5d2d2SJDBetteridge     Objects stashed on this garbage dictionary can later be destroyed
3962e5d2d2SJDBetteridge     with a call to `PetscGarbageCleanup()`.
4062e5d2d2SJDBetteridge 
4162e5d2d2SJDBetteridge     This function is intended for use with managed languages such as
4262e5d2d2SJDBetteridge     Python or Julia, which may not destroy objects in a deterministic
4362e5d2d2SJDBetteridge     order.
4462e5d2d2SJDBetteridge 
4562e5d2d2SJDBetteridge     Level: developer
4662e5d2d2SJDBetteridge 
4762e5d2d2SJDBetteridge .seealso: `PetscGarbageCleanup()`
4862e5d2d2SJDBetteridge @*/
4962e5d2d2SJDBetteridge PetscErrorCode PetscObjectDelayedDestroy(PetscObject *obj)
5062e5d2d2SJDBetteridge {
5162e5d2d2SJDBetteridge   MPI_Comm     petsc_comm;
5262e5d2d2SJDBetteridge   PetscInt     count;
5362e5d2d2SJDBetteridge   PetscGarbage garbage;
5462e5d2d2SJDBetteridge 
5562e5d2d2SJDBetteridge   PetscFunctionBegin;
5662e5d2d2SJDBetteridge   PetscValidPointer(obj, 1);
5762e5d2d2SJDBetteridge   /* Don't stash NULL pointers */
5862e5d2d2SJDBetteridge   if (*obj != NULL) {
5962e5d2d2SJDBetteridge     /* Elaborate check for getting non-cyclic reference counts */
6062e5d2d2SJDBetteridge     if (!(*obj)->non_cyclic_references) {
6162e5d2d2SJDBetteridge       count = --(*obj)->refct;
6262e5d2d2SJDBetteridge     } else {
6362e5d2d2SJDBetteridge       PetscCall((*obj)->non_cyclic_references(*obj, &count));
6462e5d2d2SJDBetteridge       --count;
6562e5d2d2SJDBetteridge       --(*obj)->refct;
6662e5d2d2SJDBetteridge     }
6762e5d2d2SJDBetteridge     /* Only stash if the (non-cyclic) reference count hits 0 */
6862e5d2d2SJDBetteridge     if (count == 0) {
6962e5d2d2SJDBetteridge       (*obj)->refct = 1;
7062e5d2d2SJDBetteridge       PetscCall(PetscObjectGetComm(*obj, &petsc_comm));
7162e5d2d2SJDBetteridge       PetscCall(GarbageGetHMap_Private(petsc_comm, &garbage));
7262e5d2d2SJDBetteridge       PetscCall(PetscHMapObjSet(garbage.map, (*obj)->cidx, *obj));
7362e5d2d2SJDBetteridge     }
7462e5d2d2SJDBetteridge   }
7562e5d2d2SJDBetteridge   *obj = NULL;
7662e5d2d2SJDBetteridge   PetscFunctionReturn(0);
7762e5d2d2SJDBetteridge }
7862e5d2d2SJDBetteridge 
7962e5d2d2SJDBetteridge /* Performs the intersection of 2 sorted arrays seta and setb of lengths
8062e5d2d2SJDBetteridge    lena and lenb respectively,returning the result in seta and lena
8162e5d2d2SJDBetteridge    This is an O(n) operation */
8262e5d2d2SJDBetteridge static PetscErrorCode GarbageKeySortedIntersect_Private(PetscInt64 seta[], PetscInt *lena, PetscInt64 setb[], PetscInt lenb)
8362e5d2d2SJDBetteridge {
8462e5d2d2SJDBetteridge   /* The arrays seta and setb MUST be sorted! */
85b0271995SJDBetteridge   PetscInt ii, jj = 0, counter = 0;
8662e5d2d2SJDBetteridge 
8762e5d2d2SJDBetteridge   PetscFunctionBegin;
8862e5d2d2SJDBetteridge   if (PetscDefined(USE_DEBUG)) {
8962e5d2d2SJDBetteridge     PetscBool sorted = PETSC_FALSE;
9062e5d2d2SJDBetteridge     /* In debug mode check whether the array are sorted */
9162e5d2d2SJDBetteridge     PetscCall(PetscSortedInt64(*lena, seta, &sorted));
9279528a3fSJDBetteridge     PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Provided array in argument 1 is not sorted");
9362e5d2d2SJDBetteridge     PetscCall(PetscSortedInt64(lenb, setb, &sorted));
9479528a3fSJDBetteridge     PetscCheck(sorted, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Provided array in argument 3 is not sorted");
9562e5d2d2SJDBetteridge   }
9662e5d2d2SJDBetteridge   for (ii = 0; ii < *lena; ii++) {
97b0271995SJDBetteridge     while (jj < lenb && seta[ii] > setb[jj]) { jj++; }
98b0271995SJDBetteridge     if (jj >= lenb) break;
99b0271995SJDBetteridge     if (seta[ii] == setb[jj]) {
10062e5d2d2SJDBetteridge       seta[counter] = seta[ii];
10162e5d2d2SJDBetteridge       counter++;
10262e5d2d2SJDBetteridge     }
10362e5d2d2SJDBetteridge   }
104b0271995SJDBetteridge 
10562e5d2d2SJDBetteridge   *lena = counter;
10662e5d2d2SJDBetteridge   PetscFunctionReturn(0);
10762e5d2d2SJDBetteridge }
10862e5d2d2SJDBetteridge 
10962e5d2d2SJDBetteridge /* Wrapper to create MPI reduce operator for set intersection */
11062e5d2d2SJDBetteridge void PetscGarbageKeySortedIntersect(void *inset, void *inoutset, PetscMPIInt *length, MPI_Datatype *dtype)
11162e5d2d2SJDBetteridge {
11262e5d2d2SJDBetteridge   PetscInt64 *seta, *setb;
11362e5d2d2SJDBetteridge 
11462e5d2d2SJDBetteridge   seta = (PetscInt64 *)inoutset;
11562e5d2d2SJDBetteridge   setb = (PetscInt64 *)inset;
11662e5d2d2SJDBetteridge 
11762e5d2d2SJDBetteridge   GarbageKeySortedIntersect_Private(&seta[1], (PetscInt *)&seta[0], &setb[1], (PetscInt)setb[0]);
11862e5d2d2SJDBetteridge }
11962e5d2d2SJDBetteridge 
12062e5d2d2SJDBetteridge /* Performs a collective allreduce intersection of one array per rank */
121*8e89d99cSJDBetteridge PetscErrorCode GarbageKeyAllReduceIntersect_Private(MPI_Comm comm, PetscInt64 *set, PetscInt *entries)
12262e5d2d2SJDBetteridge {
12362e5d2d2SJDBetteridge   PetscInt     ii, max_entries;
12462e5d2d2SJDBetteridge   PetscInt64  *sendset, *recvset;
12562e5d2d2SJDBetteridge   MPI_Datatype keyset_type;
12662e5d2d2SJDBetteridge 
12762e5d2d2SJDBetteridge   PetscFunctionBegin;
12862e5d2d2SJDBetteridge   /* Sort keys first for use with `GarbageKeySortedIntersect_Private()`*/
12962e5d2d2SJDBetteridge   PetscCall(PetscSortInt64(*entries, set));
13062e5d2d2SJDBetteridge 
13162e5d2d2SJDBetteridge   /* Get the maximum size of all key sets */
13262e5d2d2SJDBetteridge   PetscCallMPI(MPI_Allreduce(entries, &max_entries, 1, MPIU_INT, MPI_MAX, comm));
13362e5d2d2SJDBetteridge   PetscCall(PetscMalloc1(max_entries + 1, &sendset));
13462e5d2d2SJDBetteridge   PetscCall(PetscMalloc1(max_entries + 1, &recvset));
13562e5d2d2SJDBetteridge   sendset[0] = (PetscInt64)*entries;
13662e5d2d2SJDBetteridge   for (ii = 1; ii < *entries + 1; ii++) sendset[ii] = set[ii - 1];
13762e5d2d2SJDBetteridge 
13862e5d2d2SJDBetteridge   /* Create a custom data type to hold the set */
13962e5d2d2SJDBetteridge   PetscCallMPI(MPI_Type_contiguous(max_entries + 1, MPIU_INT64, &keyset_type));
14062e5d2d2SJDBetteridge   /* PetscCallMPI(MPI_Type_set_name(keyset_type,"PETSc garbage key set type")); */
14162e5d2d2SJDBetteridge   PetscCallMPI(MPI_Type_commit(&keyset_type));
14262e5d2d2SJDBetteridge 
14362e5d2d2SJDBetteridge   /* Perform custom intersect reduce operation over sets */
14462e5d2d2SJDBetteridge   PetscCallMPI(MPI_Allreduce(sendset, recvset, 1, keyset_type, Petsc_Garbage_SetIntersectOp, comm));
14562e5d2d2SJDBetteridge 
14662e5d2d2SJDBetteridge   PetscCallMPI(MPI_Type_free(&keyset_type));
14762e5d2d2SJDBetteridge 
14862e5d2d2SJDBetteridge   *entries = (PetscInt)recvset[0];
14962e5d2d2SJDBetteridge   for (ii = 0; ii < *entries; ii++) set[ii] = recvset[ii + 1];
15062e5d2d2SJDBetteridge 
15162e5d2d2SJDBetteridge   PetscCall(PetscFree(sendset));
15262e5d2d2SJDBetteridge   PetscCall(PetscFree(recvset));
15362e5d2d2SJDBetteridge   PetscFunctionReturn(0);
15462e5d2d2SJDBetteridge }
15562e5d2d2SJDBetteridge 
15662e5d2d2SJDBetteridge /*@C
15762e5d2d2SJDBetteridge     PetscGarbageCleanup - Destroys objects placed in the garbage by
15862e5d2d2SJDBetteridge     PetscObjectDelayedDestroy().
15962e5d2d2SJDBetteridge 
16062e5d2d2SJDBetteridge     Collective
16162e5d2d2SJDBetteridge 
16262e5d2d2SJDBetteridge     Input Parameters:
16362e5d2d2SJDBetteridge .   comm      - communicator over which to perform collective cleanup
16462e5d2d2SJDBetteridge 
16562e5d2d2SJDBetteridge     Notes:
16662e5d2d2SJDBetteridge     Implements a collective garbage collection.
16762e5d2d2SJDBetteridge     A per- MPI communicator garbage dictionary is created to store
16862e5d2d2SJDBetteridge     references to objects destroyed using PetscObjectDelayedDestroy().
16962e5d2d2SJDBetteridge     Objects that appear in this dictionary on all ranks can be destroyed
17062e5d2d2SJDBetteridge     by calling PetscGarbageCleanup().
17162e5d2d2SJDBetteridge 
17262e5d2d2SJDBetteridge     This is done as follows:
17362e5d2d2SJDBetteridge     1.  Keys of the garbage dictionary, which correspond to the creation
17462e5d2d2SJDBetteridge         indices of the objects stashed, are sorted.
17562e5d2d2SJDBetteridge     2.  A collective intersection of dictionary keys is performed by all
17662e5d2d2SJDBetteridge         ranks in the communicator.
17762e5d2d2SJDBetteridge     3.  The intersection is broadcast back to all ranks in the
17862e5d2d2SJDBetteridge         communicator.
17962e5d2d2SJDBetteridge     4.  The objects on the dictionary are collectively destroyed in
18062e5d2d2SJDBetteridge         creation index order using a call to PetscObjectDestroy().
18162e5d2d2SJDBetteridge 
18262e5d2d2SJDBetteridge     This function is intended for use with managed languages such as
18362e5d2d2SJDBetteridge     Python or Julia, which may not destroy objects in a deterministic
18462e5d2d2SJDBetteridge     order.
18562e5d2d2SJDBetteridge 
18662e5d2d2SJDBetteridge     Level: developer
18762e5d2d2SJDBetteridge 
18862e5d2d2SJDBetteridge .seealso: PetscObjectDelayedDestroy()
18962e5d2d2SJDBetteridge @*/
19062e5d2d2SJDBetteridge PetscErrorCode PetscGarbageCleanup(MPI_Comm comm)
19162e5d2d2SJDBetteridge {
19262e5d2d2SJDBetteridge   PetscInt     ii, entries, offset;
19362e5d2d2SJDBetteridge   PetscInt64  *keys;
19462e5d2d2SJDBetteridge   PetscObject  obj;
19562e5d2d2SJDBetteridge   PetscGarbage garbage;
19662e5d2d2SJDBetteridge 
19762e5d2d2SJDBetteridge   PetscFunctionBegin;
19862e5d2d2SJDBetteridge   /* Duplicate comm to prevent it being cleaned up by PetscObjectDestroy() */
19962e5d2d2SJDBetteridge   PetscCall(PetscCommDuplicate(comm, &comm, NULL));
20062e5d2d2SJDBetteridge 
20162e5d2d2SJDBetteridge   /* Grab garbage from comm and remove it
20262e5d2d2SJDBetteridge    this avoids calling PetscCommDestroy() and endlessly recursing */
20362e5d2d2SJDBetteridge   PetscCall(GarbageGetHMap_Private(comm, &garbage));
20462e5d2d2SJDBetteridge   PetscCallMPI(MPI_Comm_delete_attr(comm, Petsc_Garbage_HMap_keyval));
20562e5d2d2SJDBetteridge 
20662e5d2d2SJDBetteridge   /* Get keys from garbage hash map */
20762e5d2d2SJDBetteridge   PetscCall(PetscHMapObjGetSize(garbage.map, &entries));
20862e5d2d2SJDBetteridge   PetscCall(PetscMalloc1(entries, &keys));
20962e5d2d2SJDBetteridge   offset = 0;
21062e5d2d2SJDBetteridge   PetscCall(PetscHMapObjGetKeys(garbage.map, &offset, keys));
21162e5d2d2SJDBetteridge 
21262e5d2d2SJDBetteridge   /* Gather and intersect */
21362e5d2d2SJDBetteridge   PetscCall(GarbageKeyAllReduceIntersect_Private(comm, keys, &entries));
21462e5d2d2SJDBetteridge 
21562e5d2d2SJDBetteridge   /* Collectively destroy objects objects that appear in garbage in
21662e5d2d2SJDBetteridge      creation index order */
21762e5d2d2SJDBetteridge   for (ii = 0; ii < entries; ii++) {
21862e5d2d2SJDBetteridge     PetscCall(PetscHMapObjGet(garbage.map, keys[ii], &obj));
21962e5d2d2SJDBetteridge     PetscCall(PetscObjectDestroy(&obj));
22062e5d2d2SJDBetteridge     PetscCall(PetscFree(obj));
22162e5d2d2SJDBetteridge     PetscCall(PetscHMapObjDel(garbage.map, keys[ii]));
22262e5d2d2SJDBetteridge   }
22362e5d2d2SJDBetteridge   PetscCall(PetscFree(keys));
22462e5d2d2SJDBetteridge 
22562e5d2d2SJDBetteridge   /* Put garbage back */
22662e5d2d2SJDBetteridge   PetscCallMPI(MPI_Comm_set_attr(comm, Petsc_Garbage_HMap_keyval, garbage.ptr));
22762e5d2d2SJDBetteridge   PetscCall(PetscCommDestroy(&comm));
22862e5d2d2SJDBetteridge   PetscFunctionReturn(0);
22962e5d2d2SJDBetteridge }
23062e5d2d2SJDBetteridge 
23162e5d2d2SJDBetteridge /* Utility function for printing the contents of the garbage on a given comm */
23262e5d2d2SJDBetteridge PetscErrorCode PetscGarbageView(MPI_Comm comm, PetscViewer viewer)
23362e5d2d2SJDBetteridge {
23462e5d2d2SJDBetteridge   char         text[64];
23562e5d2d2SJDBetteridge   PetscInt     ii, entries, offset;
23662e5d2d2SJDBetteridge   PetscInt64  *keys;
23762e5d2d2SJDBetteridge   PetscObject  obj;
23862e5d2d2SJDBetteridge   PetscGarbage garbage;
23962e5d2d2SJDBetteridge   PetscMPIInt  rank;
24062e5d2d2SJDBetteridge 
24162e5d2d2SJDBetteridge   PetscFunctionBegin;
24262e5d2d2SJDBetteridge   PetscCall(PetscPrintf(comm, "PETSc garbage on "));
24362e5d2d2SJDBetteridge   if (comm == PETSC_COMM_WORLD) {
24462e5d2d2SJDBetteridge     PetscCall(PetscPrintf(comm, "PETSC_COMM_WORLD\n"));
24562e5d2d2SJDBetteridge   } else if (comm == PETSC_COMM_SELF) {
24662e5d2d2SJDBetteridge     PetscCall(PetscPrintf(comm, "PETSC_COMM_SELF\n"));
24762e5d2d2SJDBetteridge   } else {
24862e5d2d2SJDBetteridge     PetscCall(PetscPrintf(comm, "UNKNOWN_COMM\n"));
24962e5d2d2SJDBetteridge   }
25062e5d2d2SJDBetteridge   PetscCall(PetscCommDuplicate(comm, &comm, NULL));
25162e5d2d2SJDBetteridge   PetscCall(GarbageGetHMap_Private(comm, &garbage));
25262e5d2d2SJDBetteridge 
25362e5d2d2SJDBetteridge   /* Get keys from garbage hash map and sort */
25462e5d2d2SJDBetteridge   PetscCall(PetscHMapObjGetSize(garbage.map, &entries));
25562e5d2d2SJDBetteridge   PetscCall(PetscMalloc1(entries, &keys));
25662e5d2d2SJDBetteridge   offset = 0;
25762e5d2d2SJDBetteridge   PetscCall(PetscHMapObjGetKeys(garbage.map, &offset, keys));
25862e5d2d2SJDBetteridge 
25962e5d2d2SJDBetteridge   /* Pretty print entries in a table */
26062e5d2d2SJDBetteridge   PetscCallMPI(MPI_Comm_rank(comm, &rank));
26162e5d2d2SJDBetteridge   PetscCall(PetscSynchronizedPrintf(comm, "Rank %i:: ", rank));
26262e5d2d2SJDBetteridge   PetscCall(PetscFormatConvert("Total entries: %D\n", text));
26362e5d2d2SJDBetteridge   PetscCall(PetscSynchronizedPrintf(comm, text, entries));
26462e5d2d2SJDBetteridge   if (entries) {
26562e5d2d2SJDBetteridge     PetscCall(PetscSynchronizedPrintf(comm, "| Key   | Type                   | Name                             | Object ID |\n"));
26662e5d2d2SJDBetteridge     PetscCall(PetscSynchronizedPrintf(comm, "|-------|------------------------|----------------------------------|-----------|\n"));
26762e5d2d2SJDBetteridge   }
26862e5d2d2SJDBetteridge   for (ii = 0; ii < entries; ii++) {
26962e5d2d2SJDBetteridge     PetscCall(PetscHMapObjGet(garbage.map, keys[ii], &obj));
27062e5d2d2SJDBetteridge     PetscCall(PetscFormatConvert("| %5" PetscInt64_FMT " | %-22s | %-32s | %6D    |\n", text));
27162e5d2d2SJDBetteridge     PetscCall(PetscSynchronizedPrintf(comm, text, keys[ii], obj->class_name, obj->description, obj->id));
27262e5d2d2SJDBetteridge   }
27362e5d2d2SJDBetteridge   PetscCall(PetscSynchronizedFlush(comm, PETSC_STDOUT));
27462e5d2d2SJDBetteridge 
27562e5d2d2SJDBetteridge   PetscCall(PetscFree(keys));
27662e5d2d2SJDBetteridge   PetscCall(PetscCommDestroy(&comm));
27762e5d2d2SJDBetteridge   PetscFunctionReturn(0);
27862e5d2d2SJDBetteridge }
279