view vmalloc.c @ 76:9ddbb071142d

make hardware independent and port to 64bit
author Merten Sach <msach@mailbox.tu-berlin.de>
date Thu, 16 Jun 2011 14:41:15 +0200
parents 9c3107044f86
children 521c75d64cef
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 /*Helper function
19 *Insert a newly generated free chunk into the first spot on the free list.
20 * The chunk is cast as a MallocProlog, so the various pointers in it are
21 * accessed with C's help -- and the size of the prolog is easily added to
22 * the pointer when a chunk is returned to the app -- so C handles changes
23 * in pointer sizes among machines.
24 *
25 *The list head is a normal MallocProlog struct -- identified by its
26 * prevChunkInFreeList being NULL -- the only one.
27 *
28 *The end of the list is identified by next chunk being NULL, as usual.
29 */
30 void inline
31 add_chunk_to_free_list( MallocProlog *chunk, MallocProlog *listHead )
32 {
33 chunk->nextChunkInFreeList = listHead->nextChunkInFreeList;
34 if( chunk->nextChunkInFreeList != NULL ) //if not last in free list
35 chunk->nextChunkInFreeList->prevChunkInFreeList = chunk;
36 chunk->prevChunkInFreeList = listHead;
37 listHead->nextChunkInFreeList = chunk;
38 }
41 /*This is sequential code, meant to only be called from the Master, not from
42 * any slave VPs.
43 *Search down list, checking size by the nextHigherInMem pointer, to find
44 * first chunk bigger than size needed.
45 *Shave off the extra and make it into a new free-list element, hook it in
46 * then return the address of the found element plus size of prolog.
47 *
48 *Will find a
49 */
50 void *VMS__malloc( size_t sizeRequested )
51 { MallocProlog *foundElem = NULL, *currElem, *newElem;
52 ssize_t amountExtra, sizeConsumed,sizeOfFound;
53 uint32 foundElemIsTopOfHeap;
55 //============================= MEASUREMENT STUFF ========================
56 #ifdef MEAS__TIME_MALLOC
57 int32 startStamp, endStamp;
58 saveLowTimeStampCountInto( startStamp );
59 #endif
60 //========================================================================
62 //step up the size to be aligned at 16-byte boundary, prob better ways
63 sizeRequested = ((sizeRequested + 16) >> 4) << 4;
64 currElem = (_VMSMasterEnv->freeListHead)->nextChunkInFreeList;
66 while( currElem != NULL )
67 { //check if size of currElem is big enough
68 sizeOfFound=(size_t)((uintptr_t)currElem->nextHigherInMem -(uintptr_t)currElem);
69 amountExtra = sizeOfFound - sizeRequested - sizeof(MallocProlog);
70 if( amountExtra > 0 )
71 { //found it, get out of loop
72 foundElem = currElem;
73 currElem = NULL;
74 }
75 else
76 currElem = currElem->nextChunkInFreeList;
77 }
79 if( foundElem == NULL )
80 { ERROR("\nmalloc failed\n")
81 return (void *)NULL; //indicates malloc failed
82 }
83 //Using a kludge to identify the element that is the top chunk in the
84 // heap -- saving top-of-heap addr in head's nextHigherInMem -- and
85 // save addr of start of heap in head's nextLowerInMem
86 //Will handle top of Heap specially
87 foundElemIsTopOfHeap = foundElem->nextHigherInMem ==
88 _VMSMasterEnv->freeListHead->nextHigherInMem;
90 //before shave off and try to insert new elem, remove found elem
91 //note, foundElem will never be the head, so always has valid prevChunk
92 foundElem->prevChunkInFreeList->nextChunkInFreeList =
93 foundElem->nextChunkInFreeList;
94 if( foundElem->nextChunkInFreeList != NULL )
95 { foundElem->nextChunkInFreeList->prevChunkInFreeList =
96 foundElem->prevChunkInFreeList;
97 }
98 foundElem->prevChunkInFreeList = NULL;//indicates elem currently allocated
100 //if enough, turn extra into new elem & insert it
101 if( amountExtra > 64 )
102 { //make new elem by adding to addr of curr elem then casting
103 sizeConsumed = sizeof(MallocProlog) + sizeRequested;
104 newElem = (MallocProlog *)( (uintptr_t)foundElem + sizeConsumed );
105 newElem->nextHigherInMem = foundElem->nextHigherInMem;
106 newElem->nextLowerInMem = foundElem;
107 foundElem->nextHigherInMem = newElem;
109 if( ! foundElemIsTopOfHeap )
110 { //there is no next higher for top of heap, so can't write to it
111 newElem->nextHigherInMem->nextLowerInMem = newElem;
112 }
113 add_chunk_to_free_list( newElem, _VMSMasterEnv->freeListHead );
114 }
115 else
116 {
117 sizeConsumed = sizeOfFound;
118 }
119 _VMSMasterEnv->amtOfOutstandingMem += sizeConsumed;
121 //============================= MEASUREMENT STUFF ========================
122 #ifdef MEAS__TIME_MALLOC
123 saveLowTimeStampCountInto( endStamp );
124 addIntervalToHist( startStamp, endStamp, _VMSMasterEnv->mallocTimeHist );
125 #endif
126 //========================================================================
128 //skip over the prolog by adding its size to the pointer return
129 return (void*)((uintptr_t)foundElem + sizeof(MallocProlog));
130 }
133 /*This is sequential code -- only to be called from the Master
134 * When free, subtract the size of prolog from pointer, then cast it to a
135 * MallocProlog. Then check the nextLower and nextHigher chunks to see if
136 * one or both are also free, and coalesce if so, and if neither free, then
137 * add this one to free-list.
138 */
139 void
140 VMS__free( void *ptrToFree )
141 { MallocProlog *elemToFree, *nextLowerElem, *nextHigherElem;
142 size_t sizeOfElem;
143 uint32 lowerExistsAndIsFree, higherExistsAndIsFree;
145 //============================= MEASUREMENT STUFF ========================
146 #ifdef MEAS__TIME_MALLOC
147 int32 startStamp, endStamp;
148 saveLowTimeStampCountInto( startStamp );
149 #endif
150 //========================================================================
152 if( ptrToFree < (void*)_VMSMasterEnv->freeListHead->nextLowerInMem ||
153 ptrToFree > (void*)_VMSMasterEnv->freeListHead->nextHigherInMem )
154 { //outside the range of data owned by VMS's malloc, so do nothing
155 return;
156 }
157 //subtract size of prolog to get pointer to prolog, then cast
158 elemToFree = (MallocProlog *)((uintptr_t)ptrToFree - sizeof(MallocProlog));
159 sizeOfElem =(size_t)((uintptr_t)elemToFree->nextHigherInMem-(uintptr_t)elemToFree);
161 if( elemToFree->prevChunkInFreeList != NULL )
162 { printf( "error: freeing same element twice!" ); exit(1);
163 }
165 _VMSMasterEnv->amtOfOutstandingMem -= sizeOfElem;
167 nextLowerElem = elemToFree->nextLowerInMem;
168 nextHigherElem = elemToFree->nextHigherInMem;
170 if( nextHigherElem == NULL )
171 higherExistsAndIsFree = FALSE;
172 else //okay exists, now check if in the free-list by checking back ptr
173 higherExistsAndIsFree = (nextHigherElem->prevChunkInFreeList != NULL);
175 if( nextLowerElem == NULL )
176 lowerExistsAndIsFree = FALSE;
177 else //okay, it exists, now check if it's free
178 lowerExistsAndIsFree = (nextLowerElem->prevChunkInFreeList != NULL);
181 //now, know what exists and what's free
182 if( lowerExistsAndIsFree )
183 { if( higherExistsAndIsFree )
184 { //both exist and are free, so coalesce all three
185 //First, remove higher from free-list
186 nextHigherElem->prevChunkInFreeList->nextChunkInFreeList =
187 nextHigherElem->nextChunkInFreeList;
188 if( nextHigherElem->nextChunkInFreeList != NULL ) //end-of-list?
189 nextHigherElem->nextChunkInFreeList->prevChunkInFreeList =
190 nextHigherElem->prevChunkInFreeList;
191 //Now, fix-up sequence-in-mem list -- by side-effect, this also
192 // changes size of the lower elem, which is still in free-list
193 nextLowerElem->nextHigherInMem = nextHigherElem->nextHigherInMem;
194 if( nextHigherElem->nextHigherInMem !=
195 _VMSMasterEnv->freeListHead->nextHigherInMem )
196 nextHigherElem->nextHigherInMem->nextLowerInMem = nextLowerElem;
197 //notice didn't do anything to elemToFree -- it simply is no
198 // longer reachable from any of the lists. Wonder if could be a
199 // security leak because left valid addresses in it,
200 // but don't care for now.
201 }
202 else
203 { //lower is the only of the two that exists and is free,
204 //In this case, no adjustment to free-list, just change mem-list.
205 // By side-effect, changes size of the lower elem
206 nextLowerElem->nextHigherInMem = elemToFree->nextHigherInMem;
207 if( elemToFree->nextHigherInMem !=
208 _VMSMasterEnv->freeListHead->nextHigherInMem )
209 elemToFree->nextHigherInMem->nextLowerInMem = nextLowerElem;
210 }
211 }
212 else
213 { //lower either doesn't exist or isn't free, so check higher
214 if( higherExistsAndIsFree )
215 { //higher exists and is the only of the two free
216 //First, in free-list, replace higher elem with the one to free
217 elemToFree->nextChunkInFreeList=nextHigherElem->nextChunkInFreeList;
218 elemToFree->prevChunkInFreeList=nextHigherElem->prevChunkInFreeList;
219 elemToFree->prevChunkInFreeList->nextChunkInFreeList = elemToFree;
220 if( elemToFree->nextChunkInFreeList != NULL ) // end-of-list?
221 elemToFree->nextChunkInFreeList->prevChunkInFreeList =elemToFree;
222 //Now chg mem-list. By side-effect, changes size of elemToFree
223 elemToFree->nextHigherInMem = nextHigherElem->nextHigherInMem;
224 if( elemToFree->nextHigherInMem !=
225 _VMSMasterEnv->freeListHead->nextHigherInMem )
226 elemToFree->nextHigherInMem->nextLowerInMem = elemToFree;
227 }
228 else
229 { //neither lower nor higher is availabe to coalesce so add to list
230 // this makes prev chunk ptr non-null, which indicates it's free
231 elemToFree->nextChunkInFreeList =
232 _VMSMasterEnv->freeListHead->nextChunkInFreeList;
233 _VMSMasterEnv->freeListHead->nextChunkInFreeList = elemToFree;
234 if( elemToFree->nextChunkInFreeList != NULL ) // end-of-list?
235 elemToFree->nextChunkInFreeList->prevChunkInFreeList =elemToFree;
236 elemToFree->prevChunkInFreeList = _VMSMasterEnv->freeListHead;
237 }
238 }
239 //============================= MEASUREMENT STUFF ========================
240 #ifdef MEAS__TIME_MALLOC
241 saveLowTimeStampCountInto( endStamp );
242 addIntervalToHist( startStamp, endStamp, _VMSMasterEnv->freeTimeHist );
243 #endif
244 //========================================================================
246 }
249 /*Allocates memory from the external system -- higher overhead
250 *
251 *Because of Linux's malloc throwing bizarre random faults when malloc is
252 * used inside a VMS virtual processor, have to pass this as a request and
253 * have the core loop do it when it gets around to it -- will look for these
254 * chores leftover from the previous animation of masterVP the next time it
255 * goes to animate the masterVP -- so it takes two separate masterVP
256 * animations, separated by work, to complete an external malloc or
257 * external free request.
258 *
259 *Thinking core loop accepts signals -- just looks if signal-location is
260 * empty or not --
261 */
262 void *
263 VMS__malloc_in_ext( size_t sizeRequested )
264 {
265 /*
266 //This is running in the master, so no chance for multiple cores to be
267 // competing for the core's flag.
268 if( *(_VMSMasterEnv->coreLoopSignalAddr[ 0 ]) != 0 )
269 { //something has already signalled to core loop, so save the signal
270 // and look, next time master animated, to see if can send it.
271 //Note, the addr to put a signal is in the coreloop's frame, so just
272 // checks it each time through -- make it volatile to avoid GCC
273 // optimizations -- it's a coreloop local var that only changes
274 // after jumping away. The signal includes the addr to send the
275 //return to -- even if just empty return completion-signal
276 //
277 //save the signal in some queue that the master looks at each time
278 // it starts up -- one loc says if empty for fast common case --
279 //something like that -- want to hide this inside this call -- but
280 // think this has to come as a request -- req handler gives procr
281 // back to master loop, which gives it back to req handler at point
282 // it sees that core loop has sent return signal. Something like
283 // that.
284 saveTheSignal
286 }
287 coreSigData->type = malloc;
288 coreSigData->sizeToMalloc = sizeRequested;
289 coreSigData->locToSignalCompletion = &figureOut;
290 _VMSMasterEnv->coreLoopSignals[ 0 ] = coreSigData;
291 */
292 //just risk system-stack faults until get this figured out
293 return malloc( sizeRequested );
294 }
297 /*Frees memory that was allocated in the external system -- higher overhead
298 *
299 *As noted in external malloc comment, this is clunky 'cause the free has
300 * to be called in the core loop.
301 */
302 void
303 VMS__free_in_ext( void *ptrToFree )
304 {
305 //just risk system-stack faults until get this figured out
306 free( ptrToFree );
308 //TODO: fix this -- so
309 }
312 /*Designed to be called from the main thread outside of VMS, during init
313 */
314 MallocProlog *
315 VMS_ext__create_free_list()
316 { MallocProlog *freeListHead, *firstChunk;
318 //Note, this is running in the main thread -- all increases in malloc
319 // mem and all frees of it must be done in this thread, with the
320 // thread's original stack available
321 freeListHead = malloc( sizeof(MallocProlog) );
322 firstChunk = malloc( MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE );
323 if( firstChunk == NULL ) {printf("malloc error\n"); exit(1);}
325 freeListHead->prevChunkInFreeList = NULL;
326 //Use this addr to free the heap when cleanup
327 freeListHead->nextLowerInMem = firstChunk;
328 //to identify top-of-heap elem, compare this addr to elem's next higher
329 freeListHead->nextHigherInMem = (void*)( (uintptr_t)firstChunk +
330 MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE);
331 freeListHead->nextChunkInFreeList = firstChunk;
333 firstChunk->nextChunkInFreeList = NULL;
334 firstChunk->prevChunkInFreeList = freeListHead;
335 //next Higher has to be set to top of chunk, so can calc size in malloc
336 firstChunk->nextHigherInMem = (void*)( (uintptr_t)firstChunk +
337 MALLOC_ADDITIONAL_MEM_FROM_OS_SIZE);
338 firstChunk->nextLowerInMem = NULL; //identifies as bott of heap
340 _VMSMasterEnv->amtOfOutstandingMem = 0; //none allocated yet
342 return freeListHead;
343 }
346 /*Designed to be called from the main thread outside of VMS, during cleanup
347 */
348 void
349 VMS_ext__free_free_list( MallocProlog *freeListHead )
350 {
351 //stashed a ptr to the one and only bug chunk malloc'd from OS in the
352 // free list head's next lower in mem pointer
353 free( freeListHead->nextLowerInMem );
355 //don't free the head -- it'll be in an array eventually -- free whole
356 // array when all the free lists linked from it have already been freed
357 }