annotate vmalloc.c @ 66:bf08108405cc

Added recycle pool -- will merge later -- need to get PLDI results for now
author Me
date Mon, 15 Nov 2010 12:11:24 -0800
parents 13b22ffb8a2f
children
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>
Me@53 11 #include <stdlib.h>
Me@50 12
Me@50 13 #include "VMS.h"
Me@66 14 #include "DynArray/DynArray.h"
Me@66 15 #include "Queue_impl/PrivateQueue.h"
Me@50 16
Me@50 17 /*Helper function
Me@50 18 *Insert a newly generated free chunk into the first spot on the free list.
Me@50 19 * The chunk is cast as a MallocProlog, so the various pointers in it are
Me@50 20 * accessed with C's help -- and the size of the prolog is easily added to
Me@50 21 * the pointer when a chunk is returned to the app -- so C handles changes
Me@50 22 * in pointer sizes among machines.
Me@50 23 *
Me@50 24 *The list head is a normal MallocProlog struct -- identified by its
Me@50 25 * prevChunkInFreeList being NULL -- the only one.
Me@50 26 *
Me@50 27 *The end of the list is identified by next chunk being NULL, as usual.
Me@50 28 */
Me@50 29 void inline
Me@50 30 add_chunk_to_free_list( MallocProlog *chunk, MallocProlog *listHead )
Me@50 31 {
Me@50 32 chunk->nextChunkInFreeList = listHead->nextChunkInFreeList;
Me@50 33 if( chunk->nextChunkInFreeList != NULL ) //if not last in free list
Me@50 34 chunk->nextChunkInFreeList->prevChunkInFreeList = chunk;
Me@50 35 chunk->prevChunkInFreeList = listHead;
Me@50 36 listHead->nextChunkInFreeList = chunk;
Me@50 37 }
Me@50 38
Me@50 39
Me@50 40 /*This is sequential code, meant to only be called from the Master, not from
Me@50 41 * any slave VPs.
Me@50 42 *Search down list, checking size by the nextHigherInMem pointer, to find
Me@50 43 * first chunk bigger than size needed.
Me@50 44 *Shave off the extra and make it into a new free-list element, hook it in
Me@50 45 * then return the address of the found element plus size of prolog.
Me@50 46 *
Me@50 47 *Will find a
Me@50 48 */
Me@50 49 void *
Me@50 50 VMS__malloc( int32 sizeRequested )
Me@50 51 { MallocProlog *foundElem = NULL, *currElem, *newElem;
Me@50 52 int32 amountExtra, foundElemIsTopOfHeap, sizeConsumed,sizeOfFound;
Me@50 53
Me@65 54 //============================= MEASUREMENT STUFF ========================
Me@65 55 #ifdef MEAS__TIME_MALLOC
Me@65 56 int32 startStamp, endStamp;
Me@65 57 saveLowTimeStampCountInto( startStamp );
Me@65 58 #endif
Me@65 59 //========================================================================
Me@65 60
Me@50 61 //step up the size to be aligned at 16-byte boundary, prob better ways
Me@50 62 sizeRequested = ((sizeRequested + 16) >> 4) << 4;
Me@50 63 currElem = (_VMSMasterEnv->freeListHead)->nextChunkInFreeList;
Me@50 64
Me@50 65 while( currElem != NULL )
Me@50 66 { //check if size of currElem is big enough
Me@50 67 sizeOfFound=(int32)((char*)currElem->nextHigherInMem -(char*)currElem);
Me@50 68 amountExtra = sizeOfFound - sizeRequested - sizeof(MallocProlog);
Me@50 69 if( amountExtra > 0 )
Me@50 70 { //found it, get out of loop
Me@50 71 foundElem = currElem;
Me@50 72 currElem = NULL;
Me@50 73 }
Me@50 74 else
Me@50 75 currElem = currElem->nextChunkInFreeList;
Me@50 76 }
Me@50 77
Me@50 78 if( foundElem == NULL )
Me@55 79 { ERROR("\nmalloc failed\n")
Me@60 80 return (void *)NULL; //indicates malloc failed
Me@50 81 }
Me@50 82 //Using a kludge to identify the element that is the top chunk in the
Me@50 83 // heap -- saving top-of-heap addr in head's nextHigherInMem -- and
Me@50 84 // save addr of start of heap in head's nextLowerInMem
Me@50 85 //Will handle top of Heap specially
Me@50 86 foundElemIsTopOfHeap = foundElem->nextHigherInMem ==
Me@50 87 _VMSMasterEnv->freeListHead->nextHigherInMem;
Me@50 88
Me@50 89 //before shave off and try to insert new elem, remove found elem
Me@50 90 //note, foundElem will never be the head, so always has valid prevChunk
Me@50 91 foundElem->prevChunkInFreeList->nextChunkInFreeList =
Me@50 92 foundElem->nextChunkInFreeList;
Me@50 93 if( foundElem->nextChunkInFreeList != NULL )
Me@50 94 { foundElem->nextChunkInFreeList->prevChunkInFreeList =
Me@50 95 foundElem->prevChunkInFreeList;
Me@50 96 }
Me@50 97 foundElem->prevChunkInFreeList = NULL;//indicates elem currently allocated
Me@50 98
Me@50 99 //if enough, turn extra into new elem & insert it
Me@50 100 if( amountExtra > 64 )
Me@50 101 { //make new elem by adding to addr of curr elem then casting
Me@50 102 sizeConsumed = sizeof(MallocProlog) + sizeRequested;
Me@50 103 newElem = (MallocProlog *)( (char *)foundElem + sizeConsumed );
Me@50 104 newElem->nextHigherInMem = foundElem->nextHigherInMem;
Me@50 105 newElem->nextLowerInMem = foundElem;
Me@50 106 foundElem->nextHigherInMem = newElem;
Me@50 107
Me@50 108 if( ! foundElemIsTopOfHeap )
Me@50 109 { //there is no next higher for top of heap, so can't write to it
Me@50 110 newElem->nextHigherInMem->nextLowerInMem = newElem;
Me@50 111 }
Me@50 112 add_chunk_to_free_list( newElem, _VMSMasterEnv->freeListHead );
Me@50 113 }
Me@50 114 else
Me@50 115 {
Me@50 116 sizeConsumed = sizeOfFound;
Me@50 117 }
Me@50 118 _VMSMasterEnv->amtOfOutstandingMem += sizeConsumed;
Me@50 119
Me@65 120 //============================= MEASUREMENT STUFF ========================
Me@65 121 #ifdef MEAS__TIME_MALLOC
Me@65 122 saveLowTimeStampCountInto( endStamp );
Me@65 123 addIntervalToHist( startStamp, endStamp, _VMSMasterEnv->mallocTimeHist );
Me@65 124 #endif
Me@65 125 //========================================================================
Me@65 126
Me@50 127 //skip over the prolog by adding its size to the pointer return
Me@50 128 return (void *)((char *)foundElem + sizeof(MallocProlog));
Me@50 129 }
Me@50 130
Me@50 131
Me@50 132 /*This is sequential code -- only to be called from the Master
Me@50 133 * When free, subtract the size of prolog from pointer, then cast it to a
Me@50 134 * MallocProlog. Then check the nextLower and nextHigher chunks to see if
Me@50 135 * one or both are also free, and coalesce if so, and if neither free, then
Me@50 136 * add this one to free-list.
Me@50 137 */
Me@50 138 void
Me@50 139 VMS__free( void *ptrToFree )
Me@50 140 { MallocProlog *elemToFree, *nextLowerElem, *nextHigherElem;
Me@50 141 int32 lowerExistsAndIsFree, higherExistsAndIsFree, sizeOfElem;
Me@50 142
Me@65 143 //============================= MEASUREMENT STUFF ========================
Me@65 144 #ifdef MEAS__TIME_MALLOC
Me@65 145 int32 startStamp, endStamp;
Me@65 146 saveLowTimeStampCountInto( startStamp );
Me@65 147 #endif
Me@65 148 //========================================================================
Me@65 149
Me@53 150 if( ptrToFree < (void*)_VMSMasterEnv->freeListHead->nextLowerInMem ||
Me@53 151 ptrToFree > (void*)_VMSMasterEnv->freeListHead->nextHigherInMem )
Me@50 152 { //outside the range of data owned by VMS's malloc, so do nothing
Me@50 153 return;
Me@50 154 }
Me@50 155 //subtract size of prolog to get pointer to prolog, then cast
Me@50 156 elemToFree = (MallocProlog *)((char *)ptrToFree - sizeof(MallocProlog));
Me@50 157 sizeOfElem =(int32)((char*)elemToFree->nextHigherInMem-(char*)elemToFree);
Me@53 158
Me@53 159 if( elemToFree->prevChunkInFreeList != NULL )
Me@53 160 { printf( "error: freeing same element twice!" ); exit(1);
Me@53 161 }
Me@53 162
Me@50 163 _VMSMasterEnv->amtOfOutstandingMem -= sizeOfElem;
Me@50 164
Me@50 165 nextLowerElem = elemToFree->nextLowerInMem;
Me@50 166 nextHigherElem = elemToFree->nextHigherInMem;
Me@50 167
Me@50 168 if( nextHigherElem == NULL )
Me@50 169 higherExistsAndIsFree = FALSE;
Me@50 170 else //okay exists, now check if in the free-list by checking back ptr
Me@50 171 higherExistsAndIsFree = (nextHigherElem->prevChunkInFreeList != NULL);
Me@50 172
Me@50 173 if( nextLowerElem == NULL )
Me@50 174 lowerExistsAndIsFree = FALSE;
Me@50 175 else //okay, it exists, now check if it's free
Me@50 176 lowerExistsAndIsFree = (nextLowerElem->prevChunkInFreeList != NULL);
Me@50 177
Me@50 178
Me@50 179 //now, know what exists and what's free
Me@50 180 if( lowerExistsAndIsFree )
Me@50 181 { if( higherExistsAndIsFree )
Me@50 182 { //both exist and are free, so coalesce all three
Me@50 183 //First, remove higher from free-list
Me@50 184 nextHigherElem->prevChunkInFreeList->nextChunkInFreeList =
Me@50 185 nextHigherElem->nextChunkInFreeList;
Me@50 186 if( nextHigherElem->nextChunkInFreeList != NULL ) //end-of-list?
Me@50 187 nextHigherElem->nextChunkInFreeList->prevChunkInFreeList =
Me@50 188 nextHigherElem->prevChunkInFreeList;
Me@50 189 //Now, fix-up sequence-in-mem list -- by side-effect, this also
Me@50 190 // changes size of the lower elem, which is still in free-list
Me@50 191 nextLowerElem->nextHigherInMem = nextHigherElem->nextHigherInMem;
Me@50 192 if( nextHigherElem->nextHigherInMem !=
Me@50 193 _VMSMasterEnv->freeListHead->nextHigherInMem )
Me@50 194 nextHigherElem->nextHigherInMem->nextLowerInMem = nextLowerElem;
Me@50 195 //notice didn't do anything to elemToFree -- it simply is no
Me@50 196 // longer reachable from any of the lists. Wonder if could be a
Me@50 197 // security leak because left valid addresses in it,
Me@50 198 // but don't care for now.
Me@50 199 }
Me@50 200 else
Me@50 201 { //lower is the only of the two that exists and is free,
Me@50 202 //In this case, no adjustment to free-list, just change mem-list.
Me@50 203 // By side-effect, changes size of the lower elem
Me@50 204 nextLowerElem->nextHigherInMem = elemToFree->nextHigherInMem;
Me@50 205 if( elemToFree->nextHigherInMem !=
Me@50 206 _VMSMasterEnv->freeListHead->nextHigherInMem )
Me@50 207 elemToFree->nextHigherInMem->nextLowerInMem = nextLowerElem;
Me@50 208 }
Me@50 209 }
Me@50 210 else
Me@50 211 { //lower either doesn't exist or isn't free, so check higher
Me@50 212 if( higherExistsAndIsFree )
Me@50 213 { //higher exists and is the only of the two free
Me@50 214 //First, in free-list, replace higher elem with the one to free
Me@50 215 elemToFree->nextChunkInFreeList=nextHigherElem->nextChunkInFreeList;
Me@50 216 elemToFree->prevChunkInFreeList=nextHigherElem->prevChunkInFreeList;
Me@50 217 elemToFree->prevChunkInFreeList->nextChunkInFreeList = elemToFree;
Me@50 218 if( elemToFree->nextChunkInFreeList != NULL ) // end-of-list?
Me@50 219 elemToFree->nextChunkInFreeList->prevChunkInFreeList =elemToFree;
Me@50 220 //Now chg mem-list. By side-effect, changes size of elemToFree
Me@50 221 elemToFree->nextHigherInMem = nextHigherElem->nextHigherInMem;
Me@50 222 if( elemToFree->nextHigherInMem !=
Me@50 223 _VMSMasterEnv->freeListHead->nextHigherInMem )
Me@50 224 elemToFree->nextHigherInMem->nextLowerInMem = elemToFree;
Me@50 225 }
Me@50 226 else
Me@50 227 { //neither lower nor higher is availabe to coalesce so add to list
Me@50 228 // this makes prev chunk ptr non-null, which indicates it's free
Me@50 229 elemToFree->nextChunkInFreeList =
Me@50 230 _VMSMasterEnv->freeListHead->nextChunkInFreeList;
Me@50 231 _VMSMasterEnv->freeListHead->nextChunkInFreeList = elemToFree;
Me@50 232 if( elemToFree->nextChunkInFreeList != NULL ) // end-of-list?
Me@50 233 elemToFree->nextChunkInFreeList->prevChunkInFreeList =elemToFree;
Me@50 234 elemToFree->prevChunkInFreeList = _VMSMasterEnv->freeListHead;
Me@50 235 }
Me@50 236 }
Me@65 237 //============================= MEASUREMENT STUFF ========================
Me@65 238 #ifdef MEAS__TIME_MALLOC
Me@65 239 saveLowTimeStampCountInto( endStamp );
Me@65 240 addIntervalToHist( startStamp, endStamp, _VMSMasterEnv->freeTimeHist );
Me@65 241 #endif
Me@65 242 //========================================================================
Me@50 243
Me@50 244 }
Me@50 245
Me@50 246
Me@53 247 /*Allocates memory from the external system -- higher overhead
Me@53 248 *
Me@53 249 *Because of Linux's malloc throwing bizarre random faults when malloc is
Me@53 250 * used inside a VMS virtual processor, have to pass this as a request and
Me@53 251 * have the core loop do it when it gets around to it -- will look for these
Me@53 252 * chores leftover from the previous animation of masterVP the next time it
Me@53 253 * goes to animate the masterVP -- so it takes two separate masterVP
Me@53 254 * animations, separated by work, to complete an external malloc or
Me@53 255 * external free request.
Me@53 256 *
Me@53 257 *Thinking core loop accepts signals -- just looks if signal-location is
Me@53 258 * empty or not --
Me@53 259 */
Me@53 260 void *
Me@53 261 VMS__malloc_in_ext( int32 sizeRequested )
Me@53 262 {
Me@53 263 /*
Me@53 264 //This is running in the master, so no chance for multiple cores to be
Me@53 265 // competing for the core's flag.
Me@53 266 if( *(_VMSMasterEnv->coreLoopSignalAddr[ 0 ]) != 0 )
Me@53 267 { //something has already signalled to core loop, so save the signal
Me@53 268 // and look, next time master animated, to see if can send it.
Me@53 269 //Note, the addr to put a signal is in the coreloop's frame, so just
Me@53 270 // checks it each time through -- make it volatile to avoid GCC
Me@53 271 // optimizations -- it's a coreloop local var that only changes
Me@53 272 // after jumping away. The signal includes the addr to send the
Me@53 273 //return to -- even if just empty return completion-signal
Me@53 274 //
Me@53 275 //save the signal in some queue that the master looks at each time
Me@53 276 // it starts up -- one loc says if empty for fast common case --
Me@53 277 //something like that -- want to hide this inside this call -- but
Me@53 278 // think this has to come as a request -- req handler gives procr
Me@53 279 // back to master loop, which gives it back to req handler at point
Me@53 280 // it sees that core loop has sent return signal. Something like
Me@53 281 // that.
Me@53 282 saveTheSignal
Me@53 283
Me@53 284 }
Me@53 285 coreSigData->type = malloc;
Me@53 286 coreSigData->sizeToMalloc = sizeRequested;
Me@53 287 coreSigData->locToSignalCompletion = &figureOut;
Me@53 288 _VMSMasterEnv->coreLoopSignals[ 0 ] = coreSigData;
Me@53 289 */
Me@53 290 //just risk system-stack faults until get this figured out
Me@53 291 return malloc( sizeRequested );
Me@53 292 }
Me@53 293
Me@53 294
Me@53 295 /*Frees memory that was allocated in the external system -- higher overhead
Me@53 296 *
Me@53 297 *As noted in external malloc comment, this is clunky 'cause the free has
Me@53 298 * to be called in the core loop.
Me@53 299 */
Me@53 300 void
Me@53 301 VMS__free_in_ext( void *ptrToFree )
Me@53 302 {
Me@53 303 //just risk system-stack faults until get this figured out
Me@53 304 free( ptrToFree );
Me@53 305
Me@53 306 //TODO: fix this -- so
Me@53 307 }
Me@53 308
Me@53 309
Me@50 310 /*Designed to be called from the main thread outside of VMS, during init
Me@50 311 */
Me@50 312 MallocProlog *
Me@53 313 VMS_ext__create_free_list()
Me@50 314 { MallocProlog *freeListHead, *firstChunk;
Me@50 315
Me@50 316 //Note, this is running in the main thread -- all increases in malloc
Me@50 317 // mem and all frees of it must be done in this thread, with the
Me@50 318 // thread's original stack available
Me@50 319 freeListHead = malloc( sizeof(MallocProlog) );
Me@50 320 firstChunk = malloc( MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE );
Me@50 321 if( firstChunk == NULL ) {printf("malloc error\n"); exit(1);}
Me@50 322
Me@50 323 freeListHead->prevChunkInFreeList = NULL;
Me@50 324 //Use this addr to free the heap when cleanup
Me@50 325 freeListHead->nextLowerInMem = firstChunk;
Me@50 326 //to identify top-of-heap elem, compare this addr to elem's next higher
Me@53 327 freeListHead->nextHigherInMem = (void*)( (char*)firstChunk +
Me@53 328 MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE);
Me@50 329 freeListHead->nextChunkInFreeList = firstChunk;
Me@50 330
Me@50 331 firstChunk->nextChunkInFreeList = NULL;
Me@50 332 firstChunk->prevChunkInFreeList = freeListHead;
Me@50 333 //next Higher has to be set to top of chunk, so can calc size in malloc
Me@53 334 firstChunk->nextHigherInMem = (void*)( (char*)firstChunk +
Me@53 335 MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE);
Me@50 336 firstChunk->nextLowerInMem = NULL; //identifies as bott of heap
Me@53 337
Me@53 338 _VMSMasterEnv->amtOfOutstandingMem = 0; //none allocated yet
Me@50 339
Me@50 340 return freeListHead;
Me@50 341 }
Me@50 342
Me@50 343
Me@50 344 /*Designed to be called from the main thread outside of VMS, during cleanup
Me@50 345 */
Me@50 346 void
Me@50 347 VMS_ext__free_free_list( MallocProlog *freeListHead )
Me@50 348 {
Me@50 349 //stashed a ptr to the one and only bug chunk malloc'd from OS in the
Me@50 350 // free list head's next lower in mem pointer
Me@50 351 free( freeListHead->nextLowerInMem );
Me@50 352
Me@50 353 //don't free the head -- it'll be in an array eventually -- free whole
Me@50 354 // array when all the free lists linked from it have already been freed
Me@50 355 }
Me@50 356
Me@66 357
Me@66 358
Me@66 359 void
Me@66 360 add_array_to_recycle_pool( VMSRecyclePool *pool )
Me@66 361 { void *newArray;
Me@66 362 int32 idxToNewArray;
Me@66 363
Me@66 364 newArray = VMS__malloc( pool->currArraySize );
Me@66 365 idxToNewArray = addToDynArray( newArray, pool->arraysInfo );
Me@66 366
Me@66 367 pool->ptrToNextStruct = pool->arrays[ idxToNewArray ];
Me@66 368 pool->ptrToTopOfCurrArray = pool->ptrToNextStruct +
Me@66 369 pool->currArraySize - 1;
Me@66 370
Me@66 371 pool->currArraySize += pool->currArraySize >> 2; //mult by 1.25
Me@66 372 pool->hasNewStructs = TRUE;
Me@66 373 }
Me@66 374
Me@66 375 VMSRecyclePool *
Me@66 376 VMS__make_recycle_pool( int32 sizeOfStructsToHold )
Me@66 377 { VMSRecyclePool *pool;
Me@66 378
Me@66 379 pool = VMS__malloc( sizeof(VMSRecyclePool) );
Me@66 380 pool->recycleQ = makeVMSPrivQ();
Me@66 381 pool->arraysInfo = makePrivDynArrayOfSize( &(pool->arrays), 64 );
Me@66 382 pool->sizeOfStructs = sizeOfStructsToHold;
Me@66 383 pool->currArraySize = 64 * sizeOfStructsToHold; //64 makes aligned as well
Me@66 384 add_array_to_recycle_pool( pool );
Me@66 385 }
Me@66 386
Me@66 387 void *
Me@66 388 VMS__get_from_recycle_pool( VMSRecyclePool *pool )
Me@66 389 { void *retStruct;
Me@66 390
Me@66 391 if( pool->hasNewStructs )
Me@66 392 {
Me@66 393 ReStart:
Me@66 394 retStruct = pool->ptrToNextStruct;
Me@66 395 pool->ptrToNextStruct += pool->sizeOfStructs;
Me@66 396 if( pool->ptrToNextStruct < pool->ptrToTopOfCurrArray )
Me@66 397 return retStruct;
Me@66 398 else
Me@66 399 { pool->hasNewStructs = FALSE;
Me@66 400 //fall-through to code after top IF
Me@66 401 }
Me@66 402 }
Me@66 403
Me@66 404 retStruct = readPrivQ( pool->recycleQ );
Me@66 405 if( retStruct != NULL ) return retStruct;
Me@66 406 else
Me@66 407 { add_array_to_recycle_pool( pool );
Me@66 408 goto ReStart;
Me@66 409 }
Me@66 410 }
Me@66 411
Me@66 412 void
Me@66 413 VMS__recycle( void *chunkToRecycle, VMSRecyclePool *pool )
Me@66 414 { //PrivQ self-expands when writing
Me@66 415 writePrivQ( chunkToRecycle, pool->recycleQ );
Me@66 416 }
Me@66 417
Me@66 418 void
Me@66 419 VMS__free_recycle_pool( VMSRecyclePool *pool )
Me@66 420 { int32 i;
Me@66 421
Me@66 422 freeDynArrayDeep( pool->arraysInfo, &VMS__free );
Me@66 423 freePrivQ( pool->recycleQ );
Me@66 424 VMS__free( pool );
Me@66 425 }