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