view vmalloc.c @ 135:0b49fd35afc1

distributed free working -app sends a VMSSemReqst to his Master which send a request to a different Master -Master send the request directly -The request structure is freed by the sender, when the request was handled There are still problems on shutdown. The shutdownVPs are all allocated by one Master which is likly to be terminated
author Merten Sach <msach@mailbox.tu-berlin.de>
date Fri, 16 Sep 2011 20:08:28 +0200
parents a9b72021f053
children 99343ffe1918
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 { ERROR("\nmalloc failed\n")
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 }