Mercurial > cgi-bin > hgwebdir.cgi > VMS > VMS_Implementations > VMS_impls > VMS__MC_shared_impl
view vmalloc.c @ 137:99343ffe1918
The shutdown now uses inter master requests
| author | Merten Sach <msach@mailbox.tu-berlin.de> |
|---|---|
| date | Mon, 19 Sep 2011 14:15:37 +0200 |
| parents | 0b49fd35afc1 |
| children | 99798e4438a6 |
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 <inttypes.h>
12 #include <stdlib.h>
13 #include <stdio.h>
15 #include "VMS.h"
16 #include "Histogram/Histogram.h"
18 inline void
19 sendFreeReqst_lib(int receiverID, void *ptrToFree, VirtProcr *animPr);
21 inline void
22 sendFreeReqst_master(int receiverID, void *ptrToFree);
24 /*Helper function
25 *Insert a newly generated free chunk into the first spot on the free list.
26 * The chunk is cast as a MallocProlog, so the various pointers in it are
27 * accessed with C's help -- and the size of the prolog is easily added to
28 * the pointer when a chunk is returned to the app -- so C handles changes
29 * in pointer sizes among machines.
30 *
31 *The list head is a normal MallocProlog struct -- identified by its
32 * prevChunkInFreeList being NULL -- the only one.
33 *
34 *The end of the list is identified by next chunk being NULL, as usual.
35 */
36 void inline
37 add_chunk_to_free_list( MallocProlog *chunk, MallocProlog *listHead )
38 {
39 chunk->nextChunkInFreeList = listHead->nextChunkInFreeList;
40 if( chunk->nextChunkInFreeList != NULL ) //if not last in free list
41 chunk->nextChunkInFreeList->prevChunkInFreeList = chunk;
42 chunk->prevChunkInFreeList = listHead;
43 listHead->nextChunkInFreeList = chunk;
44 }
46 /*
47 * This function is called by code which is part of the master loop.
48 * This reads the animating coreID from the MasterEnv and calls the normal malloc
49 * in VMS__malloc_on_core
50 */
51 void *
52 VMS__malloc( size_t sizeRequested)
53 {
54 return VMS__malloc_on_core(sizeRequested, _VMSMasterEnv->currentMasterProcrID);
55 }
57 /*
58 * This is called by the plugin. This call to VMS_malloc_on_core is run on the
59 * slave VPs stack so there is no switch to the VMS runtime.
60 */
61 void *
62 VMS__malloc_in_lib(size_t sizeRequested, VirtProcr *VProcr)
63 {
64 return VMS__malloc_on_core(sizeRequested, VProcr->coreAnimatedBy);
65 }
67 /*
68 *Search down list, checking size by the nextHigherInMem pointer, to find
69 * first chunk bigger than size needed.
70 *Shave off the extra and make it into a new free-list element, hook it in
71 * then return the address of the found element plus size of prolog.
72 */
73 void *
74 VMS__malloc_on_core( size_t sizeRequested, int procrID)
75 { MallocProlog *foundElem = NULL, *currElem, *newElem;
76 MallocPrologAllocated *returnElem;
77 ssize_t amountExtra, sizeConsumed,sizeOfFound;
78 uint32 foundElemIsTopOfHeap;
80 //============================= MEASUREMENT STUFF ========================
81 #ifdef MEAS__TIME_MALLOC
82 int32 startStamp, endStamp;
83 saveLowTimeStampCountInto( startStamp );
84 #endif
85 //========================================================================
87 //step up the size to be aligned at 16-byte boundary, prob better ways
88 sizeRequested = (sizeRequested + 16) & ~15;
89 currElem = (_VMSMasterEnv->freeListHead[procrID])
90 ->nextChunkInFreeList;
92 while( currElem != NULL )
93 { //check if size of currElem is big enough
94 sizeOfFound=(size_t)((uintptr_t)currElem->nextHigherInMem -(uintptr_t)currElem);
95 amountExtra = sizeOfFound - sizeRequested - sizeof(MallocProlog);
96 if( amountExtra > 0 )
97 { //found it, get out of loop
98 foundElem = currElem;
99 currElem = NULL;
100 }
101 else
102 currElem = currElem->nextChunkInFreeList;
103 }
105 if( foundElem == NULL )
106 { ERROR1("\nMalloc failed, requested size: %d\n", sizeRequested);
107 return (void *)NULL; //indicates malloc failed
108 }
109 //Using a kludge to identify the element that is the top chunk in the
110 // heap -- saving top-of-heap addr in head's nextHigherInMem -- and
111 // save addr of start of heap in head's nextLowerInMem
112 //Will handle top of Heap specially
113 foundElemIsTopOfHeap = foundElem->nextHigherInMem ==
114 _VMSMasterEnv->freeListHead[procrID]->nextHigherInMem;
116 //before shave off and try to insert new elem, remove found elem
117 //note, foundElem will never be the head, so always has valid prevChunk
118 foundElem->prevChunkInFreeList->nextChunkInFreeList =
119 foundElem->nextChunkInFreeList;
120 if( foundElem->nextChunkInFreeList != NULL )
121 { foundElem->nextChunkInFreeList->prevChunkInFreeList =
122 foundElem->prevChunkInFreeList;
123 }
124 returnElem = (MallocPrologAllocated*)foundElem;
125 returnElem->prevChunkInFreeList = NULL;//indicates elem currently allocated
126 returnElem->procrID = procrID;
128 //if enough, turn extra into new elem & insert it
129 if( amountExtra > 64 )
130 { //make new elem by adding to addr of curr elem then casting
131 sizeConsumed = sizeof(MallocProlog) + sizeRequested;
132 newElem = (MallocProlog *)( (uintptr_t)returnElem + sizeConsumed );
133 newElem->nextLowerInMem = (MallocProlog*)returnElem;
134 newElem->nextHigherInMem = returnElem->nextHigherInMem;
135 returnElem->nextHigherInMem = newElem;
136 if( ! foundElemIsTopOfHeap )
137 { //there is no next higher for top of heap, so can't write to it
138 newElem->nextHigherInMem->nextLowerInMem = newElem;
139 }
140 add_chunk_to_free_list( newElem, _VMSMasterEnv->freeListHead[procrID] );
141 }
142 else
143 {
144 sizeConsumed = sizeOfFound;
145 }
146 _VMSMasterEnv->amtOfOutstandingMem += sizeConsumed;
148 //============================= MEASUREMENT STUFF ========================
149 #ifdef MEAS__TIME_MALLOC
150 saveLowTimeStampCountInto( endStamp );
151 addIntervalToHist( startStamp, endStamp, _VMSMasterEnv->mallocTimeHist );
152 #endif
153 //========================================================================
155 //skip over the prolog by adding its size to the pointer return
156 return (void*)((uintptr_t)returnElem + sizeof(MallocProlog));
157 }
159 /*
160 * This free is called for a master loop. It decides whether the allocation of
161 * chunk was done on the same core. If it was it calls VMS__free_on_core
162 * otherwise it sends a message to the responsible core.
163 */
164 void
165 VMS__free(void *ptrToFree)
166 {
167 MallocPrologAllocated *chunk = (MallocPrologAllocated*)ptrToFree - 1;
168 if(chunk->procrID == _VMSMasterEnv->currentMasterProcrID)
169 {
170 VMS__free_on_core(ptrToFree, _VMSMasterEnv->currentMasterProcrID);
171 }
172 else
173 {
174 sendFreeReqst_master(chunk->procrID, ptrToFree);
176 }
177 }
179 /*
180 * This free is called for the plugins. It decides whether the allocation of
181 * chunk was done on the same core. If it was it calls VMS__free_on_core
182 * otherwise it sends a message to the responsible core.
183 */
184 void
185 VMS__free_in_lib(void *ptrToFree, VirtProcr *VProc)
186 {
187 MallocPrologAllocated *chunk = (MallocPrologAllocated*)ptrToFree - 1;
188 if(chunk->procrID == VProc->coreAnimatedBy)
189 {
190 VMS__free_on_core(ptrToFree, VProc->coreAnimatedBy);
191 }
192 else
193 {
194 sendFreeReqst_lib(chunk->procrID, ptrToFree, VProc);
195 }
196 }
198 /*
199 * This is called form a masterVP and request an free from a different masterVP.
200 * The free of the request structure is done after the request is handled.
201 */
202 inline void
203 sendFreeReqst_master(int receiverID, void *ptrToFree)
204 {
205 InterVMSCoreReqst *freeReqst = VMS__malloc(sizeof(InterVMSCoreReqst));
206 freeReqst->freePtr = ptrToFree;
207 freeReqst->secondReqType = transfer_free_ptr;
209 sendInterMasterReqst(receiverID, (InterMasterReqst*)freeReqst);
210 }
212 /*
213 * This is called if the free is called from the plugin. This requests an inter
214 * master request from his master.
215 */
216 inline void
217 sendFreeReqst_lib(int receiverID, void *ptrToFree, VirtProcr *animPr )
218 {
219 VMSSemReq reqData;
220 InterVMSCoreReqst *freeReqst = VMS__malloc(sizeof(InterVMSCoreReqst));
221 freeReqst->freePtr = ptrToFree;
222 freeReqst->secondReqType = transfer_free_ptr;
224 reqData.reqType = interMasterReqst;
225 reqData.receiverID = receiverID;
226 reqData.data = (void*)freeReqst;
228 VMS__send_VMSSem_request( (void*)&reqData, animPr );
229 }
231 /*This is sequential code -- only to be called from the Master
232 * When free, subtract the size of prolog from pointer, then cast it to a
233 * MallocProlog. Then check the nextLower and nextHigher chunks to see if
234 * one or both are also free, and coalesce if so, and if neither free, then
235 * add this one to free-list.
236 */
237 void
238 VMS__free_on_core( void *ptrToFree, int procrID)
239 { MallocProlog *elemToFree, *nextLowerElem, *nextHigherElem;
240 size_t sizeOfElem;
241 uint32 lowerExistsAndIsFree, higherExistsAndIsFree;
243 //============================= MEASUREMENT STUFF ========================
244 #ifdef MEAS__TIME_MALLOC
245 int32 startStamp, endStamp;
246 saveLowTimeStampCountInto( startStamp );
247 #endif
248 //========================================================================
250 MallocProlog* masterListHead = _VMSMasterEnv->freeListHead[procrID];
252 if( ptrToFree < (void*)masterListHead->nextLowerInMem ||
253 ptrToFree > (void*)masterListHead->nextHigherInMem )
254 { //outside the range of data owned by VMS's malloc, so do nothing
255 return;
256 }
257 //subtract size of prolog to get pointer to prolog, then cast
258 elemToFree = (MallocProlog *)((uintptr_t)ptrToFree - sizeof(MallocProlog));
259 sizeOfElem =(size_t)((uintptr_t)elemToFree->nextHigherInMem-(uintptr_t)elemToFree);
261 if( elemToFree->prevChunkInFreeList != NULL )
262 { printf( "error: freeing same element twice!" ); exit(1);
263 }
265 _VMSMasterEnv->amtOfOutstandingMem -= sizeOfElem;
267 nextLowerElem = elemToFree->nextLowerInMem;
268 nextHigherElem = elemToFree->nextHigherInMem;
270 if( nextHigherElem == NULL )
271 higherExistsAndIsFree = FALSE;
272 else //okay exists, now check if in the free-list by checking back ptr
273 higherExistsAndIsFree = (nextHigherElem->prevChunkInFreeList != NULL);
275 if( nextLowerElem == NULL )
276 lowerExistsAndIsFree = FALSE;
277 else //okay, it exists, now check if it's free
278 lowerExistsAndIsFree = (nextLowerElem->prevChunkInFreeList != NULL);
281 //now, know what exists and what's free
282 if( lowerExistsAndIsFree )
283 { if( higherExistsAndIsFree )
284 { //both exist and are free, so coalesce all three
285 //First, remove higher from free-list
286 nextHigherElem->prevChunkInFreeList->nextChunkInFreeList =
287 nextHigherElem->nextChunkInFreeList;
288 if( nextHigherElem->nextChunkInFreeList != NULL ) //end-of-list?
289 nextHigherElem->nextChunkInFreeList->prevChunkInFreeList =
290 nextHigherElem->prevChunkInFreeList;
291 //Now, fix-up sequence-in-mem list -- by side-effect, this also
292 // changes size of the lower elem, which is still in free-list
293 nextLowerElem->nextHigherInMem = nextHigherElem->nextHigherInMem;
294 if( nextHigherElem->nextHigherInMem !=
295 masterListHead->nextHigherInMem )
296 nextHigherElem->nextHigherInMem->nextLowerInMem = nextLowerElem;
297 //notice didn't do anything to elemToFree -- it simply is no
298 // longer reachable from any of the lists. Wonder if could be a
299 // security leak because left valid addresses in it,
300 // but don't care for now.
301 }
302 else
303 { //lower is the only of the two that exists and is free,
304 //In this case, no adjustment to free-list, just change mem-list.
305 // By side-effect, changes size of the lower elem
306 nextLowerElem->nextHigherInMem = elemToFree->nextHigherInMem;
307 if( elemToFree->nextHigherInMem !=
308 masterListHead->nextHigherInMem )
309 elemToFree->nextHigherInMem->nextLowerInMem = nextLowerElem;
310 }
311 }
312 else
313 { //lower either doesn't exist or isn't free, so check higher
314 if( higherExistsAndIsFree )
315 { //higher exists and is the only of the two free
316 //First, in free-list, replace higher elem with the one to free
317 elemToFree->nextChunkInFreeList=nextHigherElem->nextChunkInFreeList;
318 elemToFree->prevChunkInFreeList=nextHigherElem->prevChunkInFreeList;
319 elemToFree->prevChunkInFreeList->nextChunkInFreeList = elemToFree;
320 if( elemToFree->nextChunkInFreeList != NULL ) // end-of-list?
321 elemToFree->nextChunkInFreeList->prevChunkInFreeList =elemToFree;
322 //Now chg mem-list. By side-effect, changes size of elemToFree
323 elemToFree->nextHigherInMem = nextHigherElem->nextHigherInMem;
324 if( elemToFree->nextHigherInMem !=
325 masterListHead->nextHigherInMem )
326 elemToFree->nextHigherInMem->nextLowerInMem = elemToFree;
327 }
328 else
329 { //neither lower nor higher is availabe to coalesce so add to list
330 // this makes prev chunk ptr non-null, which indicates it's free
331 elemToFree->nextChunkInFreeList =
332 masterListHead->nextChunkInFreeList;
333 masterListHead->nextChunkInFreeList = elemToFree;
334 if( elemToFree->nextChunkInFreeList != NULL ) // end-of-list?
335 elemToFree->nextChunkInFreeList->prevChunkInFreeList =elemToFree;
336 elemToFree->prevChunkInFreeList = masterListHead;
337 }
338 }
339 //============================= MEASUREMENT STUFF ========================
340 #ifdef MEAS__TIME_MALLOC
341 saveLowTimeStampCountInto( endStamp );
342 addIntervalToHist( startStamp, endStamp, _VMSMasterEnv->freeTimeHist );
343 #endif
344 //========================================================================
346 }
349 /*Allocates memory from the external system -- higher overhead
350 *
351 *Because of Linux's malloc throwing bizarre random faults when malloc is
352 * used inside a VMS virtual processor, have to pass this as a request and
353 * have the core loop do it when it gets around to it -- will look for these
354 * chores leftover from the previous animation of masterVP the next time it
355 * goes to animate the masterVP -- so it takes two separate masterVP
356 * animations, separated by work, to complete an external malloc or
357 * external free request.
358 *
359 *Thinking core loop accepts signals -- just looks if signal-location is
360 * empty or not --
361 */
362 void *
363 VMS__malloc_in_ext( size_t sizeRequested )
364 {
365 /*
366 //This is running in the master, so no chance for multiple cores to be
367 // competing for the core's flag.
368 if( *(_VMSMasterEnv->coreLoopSignalAddr[ 0 ]) != 0 )
369 { //something has already signalled to core loop, so save the signal
370 // and look, next time master animated, to see if can send it.
371 //Note, the addr to put a signal is in the coreloop's frame, so just
372 // checks it each time through -- make it volatile to avoid GCC
373 // optimizations -- it's a coreloop local var that only changes
374 // after jumping away. The signal includes the addr to send the
375 //return to -- even if just empty return completion-signal
376 //
377 //save the signal in some queue that the master looks at each time
378 // it starts up -- one loc says if empty for fast common case --
379 //something like that -- want to hide this inside this call -- but
380 // think this has to come as a request -- req handler gives procr
381 // back to master loop, which gives it back to req handler at point
382 // it sees that core loop has sent return signal. Something like
383 // that.
384 saveTheSignal
386 }
387 coreSigData->type = malloc;
388 coreSigData->sizeToMalloc = sizeRequested;
389 coreSigData->locToSignalCompletion = &figureOut;
390 _VMSMasterEnv->coreLoopSignals[ 0 ] = coreSigData;
391 */
392 //just risk system-stack faults until get this figured out
393 return malloc( sizeRequested );
394 }
397 /*Frees memory that was allocated in the external system -- higher overhead
398 *
399 *As noted in external malloc comment, this is clunky 'cause the free has
400 * to be called in the core loop.
401 */
402 void
403 VMS__free_in_ext( void *ptrToFree )
404 {
405 //just risk system-stack faults until get this figured out
406 free( ptrToFree );
407 }
410 /*Designed to be called from the main thread outside of VMS, during init
411 */
412 MallocProlog *
413 VMS_ext__create_free_list()
414 { MallocProlog *freeListHead, *firstChunk;
416 //Note, this is running in the main thread -- all increases in malloc
417 // mem and all frees of it must be done in this thread, with the
418 // thread's original stack available
420 freeListHead = malloc( sizeof(MallocProlog) );
421 firstChunk = malloc( MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE );
422 if( firstChunk == NULL ) {printf("malloc error\n"); exit(1);}
424 //Touch memory to avoid page faults
425 void *ptr,*endPtr;
426 endPtr = (void*)firstChunk+MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE;
427 for(ptr = firstChunk; ptr < endPtr; ptr+=PAGE_SIZE)
428 {
429 *(char*)ptr = 0;
430 }
432 freeListHead->prevChunkInFreeList = NULL;
433 //Use this addr to free the heap when cleanup
434 freeListHead->nextLowerInMem = firstChunk;
435 //to identify top-of-heap elem, compare this addr to elem's next higher
436 freeListHead->nextHigherInMem = (void*)( (uintptr_t)firstChunk +
437 MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE);
438 freeListHead->nextChunkInFreeList = firstChunk;
440 firstChunk->nextChunkInFreeList = NULL;
441 firstChunk->prevChunkInFreeList = freeListHead;
442 //next Higher has to be set to top of chunk, so can calc size in malloc
443 firstChunk->nextHigherInMem = (void*)( (uintptr_t)firstChunk +
444 MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE);
445 firstChunk->nextLowerInMem = NULL; //identifies as bott of heap
447 _VMSMasterEnv->amtOfOutstandingMem = 0; //none allocated yet
449 return freeListHead;
450 }
453 /*Designed to be called from the main thread outside of VMS, during cleanup
454 */
455 void
456 VMS_ext__free_free_list( MallocProlog* freeListHead)
457 {
458 //stashed a ptr to the one and only bug chunk malloc'd from OS in the
459 // free list head's next lower in mem pointer
460 free( freeListHead->nextLowerInMem );
461 //don't free the head -- it'll be in an array eventually -- free whole
462 // array when all the free lists linked from it have already been freed
463 }
