/* * Copyright(C) 1999-2021 National Technology & Engineering Solutions * of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with * NTESS, the U.S. Government retains certain rights in this software. * * See packages/seacas/LICENSE for details */ /* */ /* * NOTES ON MODULE EXMEMY: * * -2. The big assumption on which this is based is that a FTNINT * which is either an int or a long int, is the same size as * the void* returned by malloc. There are both compile-time * and run-time assertions that attempt to catch this. * * -1. The information below is the original implementation. At that * time, the only C routines linked with most executables were the * supes library itself and there were no other routines calling * 'sbrk' and life was good. This situation has changed with the * addition of the exodusII, netcdf, nemesis, and other libraries. * Some of these are C-based and do 'malloc' and 'free' * calls. Most man pages for 'malloc' and 'sbrk' warn of dire * consequences of mixing calls to 'malloc' and 'sbrk' and we have * had instances of memory corruption under the old model. To * eliminate this undefined and undesirable behavior, the EXMEMY * routine has been rewritten to use 'malloc' and 'free' instead * of 'sbrk' calls. The PAGESIZE manipulations have also been * removed since the underlying 'malloc' implementation is better * at dealing with this than we are (hopefully). * * The implementation of 'free' is somewhat of a kluge and * required changes to the calling 'mem_mgr' routines. Specifically, * * 1) A few routines were disabled -- mdget which is supposed to * get a large block of memory and parcel it out in smaller chunks. * This minimizes system calls, but is incompatible with 'free' since * memory passed to 'mddel' may not be on a boundary obtained * my malloc. * * 2) 'mdlong' is mapped to 'realloc'. A flag is passed into the * exmemy call which tells whether we are freeing memory or * changing size. The '*memret' argument is used to pass the flag * in to avoid changing the interface to exmemy. * * 3) 'deferred' mode is eliminated; all memory requests are * satisfied immediately. * * 4) 'mdcomp' is also eliminated. * * The interface has not been changed; the disabled calls are * just noops. * * ---Below here is historical notes--- * * 0. On a request for more memory, we ask for it in multiples of * PAGES of memory of size PAGESIZE. The reason is that on allocation, * the memory manager does keep track the voids in memory. Unfortunately, * the same cannot be said for the voids created on deallocation, so... * * 1. The SUPES memory handler, EXMEMY, normally deallocates memory * only if one asks for it a a location that is an exact page multiple. * Further, the memory manager used to try to release memory * from each memory block---this, of course, assumes that the OS * allows for gaps in a users data space. It so happens that VMS * does. I've changed the memory manager (cf. mem_mgr/mxgive) so that * it now only tries to release a void if it is at the top of the * program space. I release as much as I can on any given release * request. * * 2. Although it's not clear from the comments, or from the documentation * that I've read, on deallocation *memrtn is the amount of memory that * exmemy was *NOT* able to return to the system. Still unclear? * An example: Suppose that I tried to return 524 bytes to the system. * Ultimately, I would do this through a call to exmemy with a value * for *memreq of -524 / Numsize (remember we ask to give in numeric * storage units). Assuming that the amount actually returned * was 512 then *memrtn should be set to 12 divided by the NumSize * for most systems---see the comments below for the Cray strategy. * (memrtn is now the amount of memory released/allocated. If it is * less than zero, an error occurred) */ /* * Assumptions: * 1. All systems have stdlib.h and define the prototype for malloc * 2. All systems are ANSI C compliant (have function prototypes) * 3. sizeof(FTNINT) == sizeof(void*) */ #include #include /* * Define the Fortran/C interface for the system in question * See also itools/config/cf/fortranc.h, which defaults to float, int --pw */ #include #define CT_ASSERT(e) extern char(*ct_assert(void))[sizeof(char[1 - 2 * !(e)])] /* If the following line causes a compile-time error, then there is a problem * which will cause the supes memory manager to not work correctly on this * platform. The error will be something similar to: * * exmemy.c(141): error: the size of an array must be greater than zero * CT_ASSERT(sizeof(FTNINT) == sizeof(void*)); * * Contact Greg Sjaardema, gdsjaar@sandia.gov for asisstance. */ CT_ASSERT(sizeof(FTNINT) == sizeof(void *)); #if defined(ADDC_) void exmemy_(FTNINT *memreq, FTNINT *locblk, FTNINT *memrtn) #else void exmemy(FTNINT *memreq, FTNINT *locblk, FTNINT *memrtn) #endif /* ADDC_ */ { size_t numbytes; size_t NumSize; /* Get multiplier which allows us to */ /* convert everything in terms of Numeric Storage Units. */ NumSize = sizeof(FTNREAL) / sizeof(char); /* Normal allocation call */ if (*memreq > 0) { /* Then we need more memory. */ numbytes = (*memreq) * NumSize; size_t *block_location = (size_t *)malloc(numbytes); /* printf("%11x %11d\n", block_location, numbytes); */ /* convert back to numerical storage units */ *locblk = (FTNINT)((size_t)block_location / NumSize); /* See if we have lost any information in the conversion. For example, if * the pointer is a 64-bit quantity and a FTNINT is 32-bits, we may not be * able to recover the block_location... This should have been caught in * the ct_assert above, but if not, we check again here... */ assert(block_location == (size_t *)((size_t)(*locblk) * NumSize)); if (block_location == NULL) { /* * Then the call to 'malloc' has failed, most likely due to * asking for more memory than what's available. */ *memrtn = -1; } else { /* The last call for memory was successful! */ *memrtn = (FTNINT)(numbytes / NumSize); } } else { /* Otherwise, if *memrtn is negative, we want to give some back. */ /* * There are a few kludges used here to try to get the memory * system to work correctly with malloc/free instead of sbrk. * A flag is passed in via the memrtn variable: * -999 == free the memory block * -998 == realloc */ /* Adjust '*locblk' to be the raw address. It is passed * in in terms of Numeric Storage Units. */ size_t *block_location = (size_t *)((size_t)(*locblk) * NumSize); if (*memrtn == -999 || *memreq == 0) { /* Handle normal 'free' */ /* printf("FREE: %11x %11d\n", block_location, *memreq); */ free(block_location); *memrtn = -(*memreq); } else if (*memrtn == -998) { /* realloc call to shrink/grow memory */ numbytes = -(*memreq) * NumSize; /* printf("PRE: %11x %11d (realloc)\n", block_location, numbytes); */ size_t *new_location = realloc(block_location, numbytes); /* printf("POST: %11x %11d\n", new_location, numbytes); */ if (new_location == NULL && *memreq > 0) { /* * Then the call to 'realloc' has failed, most likely due to * asking for more memory than what's available. */ *memrtn = -1; } else { *memrtn = -(*memreq); /* convert back to numerical storage units */ *locblk = (FTNINT)((size_t)new_location / NumSize); } /* If '*memrtn' flag was not set to -999 or -998, * ignore free request */ } } } #ifdef TEST #include int main(int argc, char **argv) { FTNINT locblk; FTNINT memrtn; size_t first = 0; size_t last = 0; assert(sizeof(FTNREAL) == sizeof(FTNINT)); printf("Size of FORTRAN int/real = %lu bytes\n", sizeof(FTNINT)); FTNINT memreq = 10000000; /* 10 Million words */ for (int i = 0; i < 1000; i++) { #if defined(ADDC_) exmemy_(&memreq, &locblk, &memrtn); #else exmemy(&memreq, &locblk, &memrtn); #endif /* ADDC_ */ printf("%4d: %11x %11d\n", i, locblk, memrtn); if (memrtn < 0) { FTNINT allocated; FTNINT overhead; allocated = i * (memreq / 1000000); overhead = (last - first - (i - 1) * memreq) * sizeof(FTNINT); printf("Total Allocation = %d MegaWords, %d MB\n", allocated, allocated * sizeof(FTNINT)); /* NOTE: 'last' is pointer to beginning of last block, not end */ printf("Overhead = %d bytes, %d bytes/allocation\n", overhead, overhead / (i - 1)); break; } else { last = locblk; } if (i == 0) { first = locblk; } } } #endif /* TEST */