-
-
Notifications
You must be signed in to change notification settings - Fork 19
Memory management
ParMMG instances monitor their memory usage and enforce an upper limit. This limit is either set by the user or is automatically set to 50% of the detected available physical memory. Specifically, each MMG5_Mesh struct monitors its own memory usage (which is stored in the mesh.memCur field) while all other memory allocations (eg communicator buffers) are accounted for in the parmesh.memCur field.
To ensure all allocated memory is accounted for, the memCur fields should not be updated manually. Instead, all memory allocation and deallocation should be done using the PMMG_{M,C,RE,REC}ALLOC/PMMG_FREE macros. These macros correctly update the memory allocation/usage status and handle errors without leaving the memory status variables in a bad state.
The arguments for these macros are:
- a PMMG_ParMesh or a MMG5_Mesh pointer ie a ptr to the struct that accounts for that memory usage
- pointer to allocate memory to. eg parmesh->ext_face_comm
- size of member elements to allocate. eg 1
- type of elements to allocate. eg PMMG_ext_comm
- a message that can be used to make error message more informative. eg int node comm idx1
- the command(s) to execute if a failure occurs. eg printf("error");ier= PMMG_FAILURE, please note that there is NO ; in the last command statement.
The PMMG_?ALLOC routines can fail if the requested allocation exceeds the allowed memory usage or if the actual allocation (malloc/calloc/etc) actually fail. In either case, the macro's last parameter is executed (the "on_failure").
The PMMG_FREE macro does not expect any on_failure commands as free is expected to always succeed. However PMMG_FREE does check whether we have deallocated too much, which can happen for example if we try to deallocate memory that was not allocated via the PMMG_?ALLOC macros or if there is a mismatch in the allocate/deallocate sizes. If the check fails, ie when one tries to deallocate more memory than memCur has recorded, then a message is printed and the memCur field is not updated.
The main structure in MMG is the MMG5_Mesh structure. MMG also has a similar mechanism for monitoring memory and limiting its memory usage.
Allocation happens in two stages:
- first one checks whether there is enough memory available and increment the memory counter
- then the actual allocation call
For example to allocate siz bytes of memory to a pointer, one would often do sth like:
Increase_memory_count(5*sizeof(double));
ptr = (double *) malloc (5*sizeof(double));
if ( !ptr ) {
fprintf(stdout," ## Error: unable to allocate ""table of double"".");
exit(EXIT_FAILURE);
}
which in MMG is done like this:
_MMG5_ADD_MEM(mesh,5*sizeof(double),"table of double",exit(EXIT_FAILURE));
_MMG5_SAFE_MALLOC(ptr,5,double);
Deallocations in MMG are also done using a macro which decrements the memory counter, frees the memory and nullifies the pointer (assigns it to NULL
). For example, to deallocate the memory allocated in the previous example, instead of the following code:
Decrease_memory_count(5*sizeof(double));
free(ptr);
ptr = NULL;
one will simply write:
MMG5_DEL_MEM(mesh,ptr,5*sizeof(double));
Every function should check for any possible error and all errors should be properly handled. Errors may occur either on failed memory allocations or other functions that return an error. On either case a function should preserve the state of the data given and should not "surprise" the calling function, ie:
- memory allocated in this function should be properly freed.
- data passed via function arguments should either be returned as expected if the function executed successfully or should be preserved in case of an error.
The returned values that can be used are PMMG_SUCCESS / PMMG_LOWFAILURE / PMMG_STRONGFAILURE. Strong or low failure should be used to indicate whether the resulting mesh conformity and can be used or not.