annotate vmalloc.c @ 139:99798e4438a6

Merge of Malloc2 and inter master requests
author Merten Sach <msach@mailbox.tu-berlin.de>
date Mon, 19 Sep 2011 16:12:01 +0200
parents d0aa5a796fc5 99343ffe1918
children 2c8f3cf6c058
rev   line source
Me@50 1 /*
Me@50 2 * Copyright 2009 OpenSourceCodeStewardshipFoundation.org
Me@50 3 * Licensed under GNU General Public License version 2
Me@50 4 *
Me@50 5 * Author: seanhalle@yahoo.com
Me@50 6 *
Me@50 7 * Created on November 14, 2009, 9:07 PM
Me@50 8 */
Me@50 9
Me@50 10 #include <malloc.h>
msach@76 11 #include <inttypes.h>
Me@53 12 #include <stdlib.h>
msach@76 13 #include <stdio.h>
msach@101 14 #include <string.h>
msach@101 15 #include <math.h>
Me@50 16
msach@134 17 #include "VMS.h"
Me@68 18 #include "Histogram/Histogram.h"
Me@50 19
msach@115 20 #define MAX_UINT64 0xFFFFFFFFFFFFFFFF
msach@115 21
msach@135 22 inline void
msach@135 23 sendFreeReqst_lib(int receiverID, void *ptrToFree, VirtProcr *animPr);
msach@135 24
msach@135 25 inline void
msach@135 26 sendFreeReqst_master(int receiverID, void *ptrToFree);
msach@135 27
msach@102 28 //A MallocProlog is a head element if the HigherInMem variable is NULL
msach@102 29 //A Chunk is free if the prevChunkInFreeList variable is NULL
msach@102 30
msach@101 31 /*
msach@101 32 * This calculates the container which fits the given size.
Me@50 33 */
msach@101 34 inline
msach@101 35 uint32 getContainer(size_t size)
msach@101 36 {
msach@118 37 return (log2(size)-LOG128)/LOG54;
msach@101 38 }
msach@101 39
msach@101 40 /*
msach@101 41 * Removes the first chunk of a freeList
msach@101 42 * The chunk is removed but not set as free. There is no check if
msach@101 43 * the free list is empty, so make sure this is not the case.
msach@101 44 */
msach@101 45 inline
msach@116 46 MallocProlog *removeChunk(MallocArrays* freeLists, uint32 containerIdx)
msach@101 47 {
msach@116 48 MallocProlog** container = &freeLists->bigChunks[containerIdx];
msach@116 49 MallocProlog* removedChunk = *container;
msach@116 50 *container = removedChunk->nextChunkInFreeList;
msach@116 51
msach@116 52 if(removedChunk->nextChunkInFreeList)
msach@116 53 removedChunk->nextChunkInFreeList->prevChunkInFreeList =
msach@116 54 (MallocProlog*)container;
msach@116 55
msach@116 56 if(*container == NULL)
msach@116 57 {
msach@116 58 if(containerIdx < 64)
msach@116 59 freeLists->bigChunksSearchVector[0] &= ~((uint64)1 << containerIdx);
msach@116 60 else
msach@116 61 freeLists->bigChunksSearchVector[1] &= ~((uint64)1 << (containerIdx-64));
msach@116 62 }
msach@116 63
msach@116 64 return removedChunk;
msach@116 65 }
msach@116 66
msach@116 67 /*
msach@116 68 * Removes the first chunk of a freeList
msach@116 69 * The chunk is removed but not set as free. There is no check if
msach@116 70 * the free list is empty, so make sure this is not the case.
msach@116 71 */
msach@116 72 inline
msach@116 73 MallocProlog *removeSmallChunk(MallocArrays* freeLists, uint32 containerIdx)
msach@116 74 {
msach@116 75 MallocProlog** container = &freeLists->smallChunks[containerIdx];
msach@116 76 MallocProlog* removedChunk = *container;
msach@101 77 *container = removedChunk->nextChunkInFreeList;
msach@101 78
msach@101 79 if(removedChunk->nextChunkInFreeList)
msach@101 80 removedChunk->nextChunkInFreeList->prevChunkInFreeList =
msach@101 81 (MallocProlog*)container;
msach@101 82
msach@101 83 return removedChunk;
msach@101 84 }
msach@101 85
msach@102 86 inline
msach@117 87 size_t getChunkSize(MallocProlog* chunk)
msach@102 88 {
msach@117 89 return (uintptr_t)chunk->nextHigherInMem -
msach@117 90 (uintptr_t)chunk - sizeof(MallocProlog);
msach@102 91 }
msach@102 92
msach@101 93 /*
msach@101 94 * Removes a chunk from a free list.
msach@101 95 */
msach@101 96 inline
msach@115 97 void extractChunk(MallocProlog* chunk, MallocArrays *freeLists)
msach@101 98 {
msach@102 99 chunk->prevChunkInFreeList->nextChunkInFreeList = chunk->nextChunkInFreeList;
msach@102 100 if(chunk->nextChunkInFreeList)
msach@102 101 chunk->nextChunkInFreeList->prevChunkInFreeList = chunk->prevChunkInFreeList;
msach@116 102
msach@116 103 //The last element in the list points to the container. If the container points
msach@116 104 //to NULL the container is empty
msach@125 105 if(*((void**)(chunk->prevChunkInFreeList)) == NULL && getChunkSize(chunk) >= BIG_LOWER_BOUND);
msach@115 106 {
msach@117 107 //Find the approppiate container because we do not know it
msach@117 108 uint64 containerIdx = ((uintptr_t)chunk->prevChunkInFreeList - (uintptr_t)freeLists->bigChunks) >> 3;
msach@116 109 if(containerIdx < (uint32)64)
msach@116 110 freeLists->bigChunksSearchVector[0] &= ~((uint64)1 << containerIdx);
msach@116 111 if(containerIdx < 128 && containerIdx >=64)
msach@116 112 freeLists->bigChunksSearchVector[1] &= ~((uint64)1 << (containerIdx-64));
msach@117 113
msach@115 114 }
msach@101 115 }
msach@101 116
msach@101 117 /*
msach@101 118 * Merges two chunks.
msach@102 119 * Chunk A has to be before chunk B in memory. Both have to be removed from
msach@102 120 * a free list
msach@101 121 */
msach@101 122 inline
msach@101 123 MallocProlog *mergeChunks(MallocProlog* chunkA, MallocProlog* chunkB)
msach@101 124 {
msach@102 125 chunkA->nextHigherInMem = chunkB->nextHigherInMem;
msach@117 126 chunkB->nextHigherInMem->nextLowerInMem = chunkA;
msach@102 127 return chunkA;
msach@101 128 }
msach@101 129 /*
msach@101 130 * Inserts a chunk into a free list.
msach@101 131 */
msach@101 132 inline
msach@101 133 void insertChunk(MallocProlog* chunk, MallocProlog** container)
msach@101 134 {
msach@101 135 chunk->nextChunkInFreeList = *container;
msach@101 136 chunk->prevChunkInFreeList = (MallocProlog*)container;
msach@101 137 if(*container)
msach@101 138 (*container)->prevChunkInFreeList = chunk;
msach@101 139 *container = chunk;
msach@101 140 }
msach@101 141
msach@101 142 /*
msach@101 143 * Divides the chunk that a new chunk of newSize is created.
msach@101 144 * There is no size check, so make sure the size value is valid.
msach@101 145 */
msach@101 146 inline
msach@101 147 MallocProlog *divideChunk(MallocProlog* chunk, size_t newSize)
msach@101 148 {
msach@101 149 MallocProlog* newChunk = (MallocProlog*)((uintptr_t)chunk->nextHigherInMem -
msach@101 150 newSize - sizeof(MallocProlog));
msach@101 151
msach@101 152 newChunk->nextLowerInMem = chunk;
msach@101 153 newChunk->nextHigherInMem = chunk->nextHigherInMem;
msach@101 154
msach@117 155 chunk->nextHigherInMem->nextLowerInMem = newChunk;
msach@101 156 chunk->nextHigherInMem = newChunk;
msach@101 157
msach@101 158 return newChunk;
msach@101 159 }
msach@101 160
msach@117 161 /*
msach@117 162 * Search for chunk in the list of big chunks. Split the block if it's too big
msach@117 163 */
msach@101 164 inline
msach@117 165 MallocProlog *searchChunk(MallocArrays *freeLists, size_t sizeRequested, uint32 containerIdx)
msach@101 166 {
msach@117 167 MallocProlog* foundChunk;
msach@117 168
msach@117 169 uint64 searchVector = freeLists->bigChunksSearchVector[0];
msach@117 170 //set small chunk bits to zero
msach@117 171 searchVector &= MAX_UINT64 << containerIdx;
msach@117 172 containerIdx = __builtin_ffsl(searchVector);
msach@117 173
msach@117 174 if(containerIdx == 0)
msach@117 175 {
msach@117 176 searchVector = freeLists->bigChunksSearchVector[1];
msach@117 177 containerIdx = __builtin_ffsl(searchVector);
msach@117 178 if(containerIdx == 0)
msach@117 179 {
msach@117 180 printf("VMS malloc failed: low memory");
msach@117 181 exit(1);
msach@117 182 }
msach@117 183 containerIdx += 64;
msach@117 184 }
msach@117 185 containerIdx--;
msach@117 186
msach@117 187
msach@117 188 foundChunk = removeChunk(freeLists, containerIdx);
msach@117 189 size_t chunkSize = getChunkSize(foundChunk);
msach@117 190
msach@117 191 //If the new chunk is larger than the requested size: split
msach@125 192 if(chunkSize > sizeRequested + 2 * sizeof(MallocProlog) + BIG_LOWER_BOUND)
msach@117 193 {
msach@117 194 MallocProlog *newChunk = divideChunk(foundChunk,sizeRequested);
msach@117 195 containerIdx = getContainer(getChunkSize(foundChunk)) - 1;
msach@117 196 insertChunk(foundChunk,&freeLists->bigChunks[containerIdx]);
msach@117 197 if(containerIdx < 64)
msach@117 198 freeLists->bigChunksSearchVector[0] |= ((uint64)1 << containerIdx);
msach@117 199 else
msach@117 200 freeLists->bigChunksSearchVector[1] |= ((uint64)1 << (containerIdx-64));
msach@117 201 foundChunk = newChunk;
msach@117 202 }
msach@117 203
msach@117 204 return foundChunk;
msach@101 205 }
Me@50 206
msach@132 207 /*
msach@132 208 * This function is called by code which is part of the master loop.
msach@132 209 * This reads the animating coreID from the MasterEnv and calls the normal malloc
msach@132 210 * in VMS__malloc_on_core
msach@132 211 */
msach@132 212 void *
msach@132 213 VMS__malloc( size_t sizeRequested)
msach@132 214 {
msach@132 215 return VMS__malloc_on_core(sizeRequested, _VMSMasterEnv->currentMasterProcrID);
msach@132 216 }
Me@50 217
msach@132 218 /*
msach@132 219 * This is called by the plugin. This call to VMS_malloc_on_core is run on the
msach@132 220 * slave VPs stack so there is no switch to the VMS runtime.
msach@132 221 */
msach@132 222 void *
msach@132 223 VMS__malloc_in_lib(size_t sizeRequested, VirtProcr *VProcr)
msach@132 224 {
msach@132 225 return VMS__malloc_on_core(sizeRequested, VProcr->coreAnimatedBy);
msach@132 226 }
msach@132 227
msach@132 228 /*
msach@101 229 * This is sequential code, meant to only be called from the Master, not from
msach@101 230 * any slave VPs.
Me@50 231 */
msach@139 232 void *VMS__malloc_on_core( size_t sizeRequested, int procrID )
msach@101 233 {
Me@65 234 //============================= MEASUREMENT STUFF ========================
Me@65 235 #ifdef MEAS__TIME_MALLOC
Me@65 236 int32 startStamp, endStamp;
Me@65 237 saveLowTimeStampCountInto( startStamp );
Me@65 238 #endif
Me@65 239 //========================================================================
Me@65 240
msach@139 241 MallocArrays* freeLists = _VMSMasterEnv->freeLists[procrID];
msach@117 242 MallocProlog* foundChunk;
msach@139 243 MallocPrologAllocated* returnChunk;
msach@78 244
msach@101 245 //Return a small chunk if the requested size is smaller than 128B
msach@102 246 if(sizeRequested <= LOWER_BOUND)
msach@102 247 {
msach@117 248 uint32 freeListIdx = (sizeRequested-1)/SMALL_CHUNK_SIZE;
msach@117 249 if(freeLists->smallChunks[freeListIdx] == NULL)
msach@117 250 foundChunk = searchChunk(freeLists, SMALL_CHUNK_SIZE*(freeListIdx+1), 0);
msach@117 251 else
msach@117 252 foundChunk = removeSmallChunk(freeLists, freeListIdx);
msach@102 253
msach@139 254 returnChunk = (MallocPrologAllocated*)foundChunk;
msach@139 255 returnChunk->prevChunkInFreeList = NULL;//indicates elem currently allocated
msach@139 256 returnChunk->procrID = procrID;
msach@139 257 return returnChunk + 1;
msach@102 258 }
msach@78 259
msach@101 260 //Calculate the expected container. Start one higher to have a Chunk that's
msach@101 261 //always big enough.
msach@117 262 uint32 containerIdx = getContainer(sizeRequested);
msach@78 263
msach@115 264 if(freeLists->bigChunks[containerIdx] == NULL)
msach@117 265 foundChunk = searchChunk(freeLists, sizeRequested, containerIdx);
msach@78 266 else
msach@117 267 foundChunk = removeChunk(freeLists, containerIdx);
msach@101 268
msach@101 269 //Mark as allocated
msach@139 270 returnChunk = (MallocPrologAllocated*)foundChunk;
msach@139 271 returnChunk->prevChunkInFreeList = NULL;//indicates elem currently allocated
msach@139 272 returnChunk->procrID = procrID;
msach@101 273
msach@78 274 //============================= MEASUREMENT STUFF ========================
msach@78 275 #ifdef MEAS__TIME_MALLOC
msach@78 276 saveLowTimeStampCountInto( endStamp );
msach@78 277 addIntervalToHist( startStamp, endStamp, _VMSMasterEnv->mallocTimeHist );
msach@78 278 #endif
msach@78 279 //========================================================================
msach@102 280
msach@101 281 //skip over the prolog by adding its size to the pointer return
msach@139 282 return returnChunk + 1;
msach@78 283 }
msach@139 284
msach@139 285 /*
msach@132 286 * This free is called for a master loop. It decides whether the allocation of
msach@132 287 * chunk was done on the same core. If it was it calls VMS__free_on_core
msach@132 288 * otherwise it sends a message to the responsible core.
msach@78 289 */
msach@132 290 void
msach@132 291 VMS__free(void *ptrToFree)
msach@132 292 {
msach@134 293 MallocPrologAllocated *chunk = (MallocPrologAllocated*)ptrToFree - 1;
msach@132 294 if(chunk->procrID == _VMSMasterEnv->currentMasterProcrID)
msach@132 295 {
msach@132 296 VMS__free_on_core(ptrToFree, _VMSMasterEnv->currentMasterProcrID);
msach@132 297 }
msach@132 298 else
msach@132 299 {
msach@135 300 sendFreeReqst_master(chunk->procrID, ptrToFree);
msach@135 301
msach@132 302 }
msach@132 303 }
msach@78 304
msach@132 305 /*
msach@132 306 * This free is called for the plugins. It decides whether the allocation of
msach@132 307 * chunk was done on the same core. If it was it calls VMS__free_on_core
msach@132 308 * otherwise it sends a message to the responsible core.
msach@132 309 */
msach@132 310 void
msach@132 311 VMS__free_in_lib(void *ptrToFree, VirtProcr *VProc)
msach@132 312 {
msach@134 313 MallocPrologAllocated *chunk = (MallocPrologAllocated*)ptrToFree - 1;
msach@132 314 if(chunk->procrID == VProc->coreAnimatedBy)
msach@132 315 {
msach@132 316 VMS__free_on_core(ptrToFree, VProc->coreAnimatedBy);
msach@78 317 }
msach@132 318 else
msach@132 319 {
msach@135 320 sendFreeReqst_lib(chunk->procrID, ptrToFree, VProc);
Me@50 321 }
msach@132 322 }
Me@50 323
msach@135 324 /*
msach@135 325 * This is called form a masterVP and request an free from a different masterVP.
msach@135 326 * The free of the request structure is done after the request is handled.
msach@135 327 */
msach@135 328 inline void
msach@135 329 sendFreeReqst_master(int receiverID, void *ptrToFree)
msach@135 330 {
msach@135 331 InterVMSCoreReqst *freeReqst = VMS__malloc(sizeof(InterVMSCoreReqst));
msach@135 332 freeReqst->freePtr = ptrToFree;
msach@135 333 freeReqst->secondReqType = transfer_free_ptr;
msach@135 334
msach@135 335 sendInterMasterReqst(receiverID, (InterMasterReqst*)freeReqst);
msach@135 336 }
msach@135 337
msach@135 338 /*
msach@135 339 * This is called if the free is called from the plugin. This requests an inter
msach@135 340 * master request from his master.
msach@135 341 */
msach@135 342 inline void
msach@135 343 sendFreeReqst_lib(int receiverID, void *ptrToFree, VirtProcr *animPr )
msach@135 344 {
msach@135 345 VMSSemReq reqData;
msach@135 346 InterVMSCoreReqst *freeReqst = VMS__malloc(sizeof(InterVMSCoreReqst));
msach@135 347 freeReqst->freePtr = ptrToFree;
msach@135 348 freeReqst->secondReqType = transfer_free_ptr;
msach@135 349
msach@135 350 reqData.reqType = interMasterReqst;
msach@135 351 reqData.receiverID = receiverID;
msach@135 352 reqData.data = (void*)freeReqst;
msach@135 353
msach@135 354 VMS__send_VMSSem_request( (void*)&reqData, animPr );
msach@135 355 }
msach@135 356
msach@101 357 /*
msach@101 358 * This is sequential code, meant to only be called from the Master, not from
msach@82 359 * any slave VPs.
Me@50 360 */
Me@50 361 void
msach@139 362 VMS__free_on_core( void *ptrToFree, int procrID )
msach@101 363 {
msach@117 364
Me@65 365 //============================= MEASUREMENT STUFF ========================
Me@65 366 #ifdef MEAS__TIME_MALLOC
Me@65 367 int32 startStamp, endStamp;
Me@65 368 saveLowTimeStampCountInto( startStamp );
Me@65 369 #endif
Me@65 370 //========================================================================
msach@134 371
msach@139 372 MallocArrays* freeLists = _VMSMasterEnv->freeLists[procrID];
msach@101 373 MallocProlog *chunkToFree = (MallocProlog*)ptrToFree - 1;
msach@102 374 uint32 containerIdx;
msach@101 375
msach@101 376 //Check for free neighbors
msach@101 377 if(chunkToFree->nextLowerInMem)
msach@101 378 {
msach@101 379 if(chunkToFree->nextLowerInMem->prevChunkInFreeList != NULL)
msach@101 380 {//Chunk is not allocated
msach@117 381 extractChunk(chunkToFree->nextLowerInMem, freeLists);
msach@117 382 chunkToFree = mergeChunks(chunkToFree->nextLowerInMem, chunkToFree);
Me@50 383 }
msach@101 384 }
msach@102 385 if(chunkToFree->nextHigherInMem)
msach@102 386 {
msach@102 387 if(chunkToFree->nextHigherInMem->prevChunkInFreeList != NULL)
msach@102 388 {//Chunk is not allocated
msach@117 389 extractChunk(chunkToFree->nextHigherInMem, freeLists);
msach@117 390 chunkToFree = mergeChunks(chunkToFree, chunkToFree->nextHigherInMem);
Me@50 391 }
msach@102 392 }
msach@117 393
msach@117 394 size_t chunkSize = getChunkSize(chunkToFree);
msach@125 395 if(chunkSize < BIG_LOWER_BOUND)
msach@117 396 {
msach@117 397 containerIdx = (chunkSize/SMALL_CHUNK_SIZE)-1;
msach@125 398 if(containerIdx > SMALL_CHUNK_COUNT-1)
msach@125 399 containerIdx = SMALL_CHUNK_COUNT-1;
msach@117 400 insertChunk(chunkToFree, &freeLists->smallChunks[containerIdx]);
msach@117 401 }
Me@50 402 else
msach@117 403 {
msach@117 404 containerIdx = getContainer(getChunkSize(chunkToFree)) - 1;
msach@117 405 insertChunk(chunkToFree, &freeLists->bigChunks[containerIdx]);
msach@117 406 if(containerIdx < 64)
msach@117 407 freeLists->bigChunksSearchVector[0] |= (uint64)1 << containerIdx;
msach@117 408 else
msach@117 409 freeLists->bigChunksSearchVector[1] |= (uint64)1 << (containerIdx-64);
msach@117 410 }
msach@102 411
Me@65 412 //============================= MEASUREMENT STUFF ========================
Me@65 413 #ifdef MEAS__TIME_MALLOC
Me@65 414 saveLowTimeStampCountInto( endStamp );
Me@65 415 addIntervalToHist( startStamp, endStamp, _VMSMasterEnv->freeTimeHist );
Me@65 416 #endif
Me@65 417 //========================================================================
Me@50 418
Me@50 419 }
Me@50 420
msach@117 421 /*
msach@117 422 * Designed to be called from the main thread outside of VMS, during init
Me@53 423 */
msach@101 424 MallocArrays *
Me@53 425 VMS_ext__create_free_list()
msach@101 426 {
msach@101 427 //Initialize containers for small chunks and fill with zeros
msach@139 428 MallocArrays *freeLists = (MallocArrays*)malloc( sizeof(MallocArrays) );
msach@101 429
msach@101 430 freeLists->smallChunks =
msach@101 431 (MallocProlog**)malloc(SMALL_CHUNK_COUNT*sizeof(MallocProlog*));
msach@101 432 memset((void*)freeLists->smallChunks,
msach@101 433 0,SMALL_CHUNK_COUNT*sizeof(MallocProlog*));
msach@101 434
msach@101 435 //Calculate number of containers for big chunks
msach@101 436 uint32 container = getContainer(MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE)+1;
msach@101 437 freeLists->bigChunks = (MallocProlog**)malloc(container*sizeof(MallocProlog*));
msach@101 438 memset((void*)freeLists->bigChunks,0,container*sizeof(MallocProlog*));
msach@101 439 freeLists->containerCount = container;
msach@101 440
msach@101 441 //Create first element in lastContainer
msach@101 442 MallocProlog *firstChunk = malloc( MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE );
msach@117 443 if( firstChunk == NULL ) {printf("Can't allocate initial memory\n"); exit(1);}
msach@102 444 freeLists->memSpace = firstChunk;
msach@79 445
msach@82 446 //Touch memory to avoid page faults
msach@81 447 void *ptr,*endPtr;
msach@81 448 endPtr = (void*)firstChunk+MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE;
msach@81 449 for(ptr = firstChunk; ptr < endPtr; ptr+=PAGE_SIZE)
msach@81 450 {
msach@81 451 *(char*)ptr = 0;
msach@81 452 }
Me@53 453
msach@101 454 firstChunk->nextLowerInMem = NULL;
msach@101 455 firstChunk->nextHigherInMem = (MallocProlog*)((uintptr_t)firstChunk +
msach@101 456 MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE - sizeof(MallocProlog*));
msach@101 457 firstChunk->nextChunkInFreeList = NULL;
msach@101 458 //previous element in the queue is the container
msach@139 459 firstChunk->prevChunkInFreeList = (MallocProlog*)&freeLists->bigChunks[container-2];
msach@82 460
msach@117 461 freeLists->bigChunks[container-2] = firstChunk;
msach@115 462 //Insert into bit search list
msach@117 463 if(container <= 65)
msach@139 464 freeLists->bigChunksSearchVector[0] = ((uint64)1 << (container-2));
msach@116 465 else
msach@139 466 freeLists->bigChunksSearchVector[1] = ((uint64)1 << (container-66));
msach@101 467
msach@101 468 //Create dummy chunk to mark the top of stack this is of course
msach@101 469 //never freed
msach@101 470 MallocProlog *dummyChunk = firstChunk->nextHigherInMem;
msach@101 471 dummyChunk->nextHigherInMem = dummyChunk+1;
msach@101 472 dummyChunk->nextLowerInMem = NULL;
msach@101 473 dummyChunk->nextChunkInFreeList = NULL;
msach@101 474 dummyChunk->prevChunkInFreeList = NULL;
msach@101 475
msach@101 476 return freeLists;
Me@50 477 }
Me@50 478
Me@50 479
Me@50 480 /*Designed to be called from the main thread outside of VMS, during cleanup
Me@50 481 */
Me@50 482 void
msach@102 483 VMS_ext__free_free_list( MallocArrays *freeLists )
Me@50 484 {
msach@102 485 free(freeLists->memSpace);
msach@102 486 free(freeLists->bigChunks);
msach@102 487 free(freeLists->smallChunks);
msach@139 488 free(freeLists);
Me@50 489 }
Me@50 490