1 /* -----------------------------------------------------------------------------
3 * (c) The University of Glasgow 2006-2007
5 * OS-specific memory management
7 * ---------------------------------------------------------------------------*/
17 typedef struct alloc_rec_ {
18 char* base; /* non-aligned base address, directly from VirtualAlloc */
19 int size; /* Size in bytes */
20 struct alloc_rec_* next;
23 typedef struct block_rec_ {
24 char* base; /* base address, non-MBLOCK-aligned */
25 int size; /* size in bytes */
26 struct block_rec_* next;
29 /* allocs are kept in ascending order, and are the memory regions as
30 returned by the OS as we need to have matching VirtualAlloc and
32 static alloc_rec* allocs = NULL;
34 /* free_blocks are kept in ascending order, and adjacent blocks are merged */
35 static block_rec* free_blocks = NULL;
48 rec = (alloc_rec*)stgMallocBytes(sizeof(alloc_rec),"getMBlocks: allocNew");
49 rec->size = (n+1)*MBLOCK_SIZE;
51 VirtualAlloc(NULL, rec->size, MEM_RESERVE, PAGE_READWRITE);
55 if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
57 errorBelch("out of memory");
60 "getMBlocks: VirtualAlloc MEM_RESERVE %d blocks failed", n);
64 temp.base=0; temp.size=0; temp.next=allocs;
68 for(; it->next!=0 && it->next->base<rec->base; it=it->next) ;
79 insertFree(char* alloc_base, int alloc_size) {
84 temp.base=0; temp.size=0; temp.next=free_blocks;
87 for( ; it!=0 && it->base<alloc_base; prev=it, it=it->next) {}
89 if(it!=0 && alloc_base+alloc_size == it->base) {
90 if(prev->base + prev->size == alloc_base) { /* Merge it, alloc, prev */
91 prev->size += alloc_size + it->size;
92 prev->next = it->next;
94 } else { /* Merge it, alloc */
95 it->base = alloc_base;
96 it->size += alloc_size;
98 } else if(prev->base + prev->size == alloc_base) { /* Merge alloc, prev */
99 prev->size += alloc_size;
100 } else { /* Merge none */
102 rec = (block_rec*)stgMallocBytes(sizeof(block_rec),"getMBlocks: insertFree");
103 rec->base=alloc_base;
104 rec->size=alloc_size;
108 free_blocks=temp.next;
113 findFreeBlocks(nat n) {
121 required_size = n*MBLOCK_SIZE;
122 temp.next=free_blocks; temp.base=0; temp.size=0;
124 /* TODO: Don't just take first block, find smallest sufficient block */
125 for( ; it!=0 && it->size<required_size; prev=it, it=it->next ) {}
127 if( (((unsigned long)it->base) & MBLOCK_MASK) == 0) { /* MBlock aligned */
128 ret = (void*)it->base;
129 if(it->size==required_size) {
133 it->base += required_size;
134 it->size -=required_size;
140 need_base = (char*)(((unsigned long)it->base) & ((unsigned long)~MBLOCK_MASK)) + MBLOCK_SIZE;
141 next = (block_rec*)stgMallocBytes(
143 , "getMBlocks: findFreeBlocks: splitting");
144 new_size = need_base - it->base;
145 next->base = need_base +required_size;
146 next->size = it->size - (new_size+required_size);
148 next->next = it->next;
150 ret=(void*)need_base;
153 free_blocks=temp.next;
157 /* VirtualAlloc MEM_COMMIT can't cross boundaries of VirtualAlloc MEM_RESERVE,
158 so we might need to do many VirtualAlloc MEM_COMMITs. We simply walk the
159 (ordered) allocated blocks. */
161 commitBlocks(char* base, int size) {
164 for( ; it!=0 && (it->base+it->size)<=base; it=it->next ) {}
165 for( ; it!=0 && size>0; it=it->next ) {
168 size_delta = it->size - (base-it->base);
169 if(size_delta>size) size_delta=size;
170 temp = VirtualAlloc(base, size_delta, MEM_COMMIT, PAGE_READWRITE);
172 sysErrorBelch("getMBlocks: VirtualAlloc MEM_COMMIT failed");
173 stg_exit(EXIT_FAILURE);
181 osGetMBlocks(nat n) {
183 ret = findFreeBlocks(n);
187 /* We already belch in allocNew if it fails */
189 stg_exit(EXIT_FAILURE);
191 insertFree(alloc->base, alloc->size);
192 ret = findFreeBlocks(n);
197 /* (In)sanity tests */
198 if (((W_)ret & MBLOCK_MASK) != 0) {
199 barf("getMBlocks: misaligned block returned");
202 commitBlocks(ret, MBLOCK_SIZE*n);
208 void osFreeMBlocks(char *addr, nat n)
211 lnat nBytes = (lnat)n * MBLOCK_SIZE;
213 insertFree(addr, nBytes);
216 while ((p != NULL) && (addr >= (p->base + p->size))) {
220 if ((p == NULL) || (p->base > addr)) {
221 errorBelch("Memory to be freed isn't allocated\n");
222 stg_exit(EXIT_FAILURE);
224 if (p->base + p->size >= addr + nBytes) {
225 if (!VirtualFree(addr, nBytes, MEM_DECOMMIT)) {
226 sysErrorBelch("osFreeMBlocks: VirtualFree MEM_DECOMMIT failed");
227 stg_exit(EXIT_FAILURE);
232 lnat bytesToFree = p->base + p->size - addr;
233 if (!VirtualFree(addr, bytesToFree, MEM_DECOMMIT)) {
234 sysErrorBelch("osFreeMBlocks: VirtualFree MEM_DECOMMIT failed");
235 stg_exit(EXIT_FAILURE);
238 nBytes -= bytesToFree;
244 void osReleaseFreeMemory(void)
246 alloc_rec *prev_a, *a;
248 block_rec *prev_fb, *fb;
250 char *a_end, *fb_end;
252 /* go through allocs and free_blocks in lockstep, looking for allocs
253 that are completely free, and uncommit them */
257 head_a.next = allocs;
260 head_fb.next = free_blocks;
267 a_end = a->base + a->size;
268 while (fb != NULL && fb->base + fb->size < a_end) {
273 fb_end = fb->base + fb->size;
274 if (fb->base <= a->base) {
275 /* The alloc is within the free block. Now we need to know
276 if it sticks out at either end. */
277 if (fb_end == a_end) {
278 if (fb->base == a->base) {
279 /* fb and a are identical, so just free fb */
280 prev_fb->next = fb->next;
285 /* fb begins earlier, so truncate it to not include a */
286 fb->size = a->base - fb->base;
290 /* fb ends later, so we'll make fb just be the part
291 after a. First though, if it also starts earlier,
292 we make a new free block record for the before bit. */
293 if (fb->base != a->base) {
296 new_fb = (block_rec *)stgMallocBytes(sizeof(block_rec),"osReleaseFreeMemory");
297 new_fb->base = fb->base;
298 new_fb->size = a->base - fb->base;
300 prev_fb->next = new_fb;
302 fb->size = fb_end - a_end;
305 /* Now we can free the alloc */
306 prev_a->next = a->next;
307 if(!VirtualFree((void *)a->base, 0, MEM_RELEASE)) {
308 sysErrorBelch("freeAllMBlocks: VirtualFree MEM_RELEASE failed");
309 stg_exit(EXIT_FAILURE);
315 /* Otherwise this alloc is not freeable, so go on to the
322 allocs = head_a.next;
323 free_blocks = head_fb.next;
327 osFreeAllMBlocks(void)
346 if(!VirtualFree((void*)it->base, 0, MEM_RELEASE)) {
347 sysErrorBelch("freeAllMBlocks: VirtualFree MEM_RELEASE failed");
348 stg_exit(EXIT_FAILURE);
357 lnat getPageSize (void)
359 static lnat pagesize = 0;
363 SYSTEM_INFO sSysInfo;
364 GetSystemInfo(&sSysInfo);
365 pagesize = sSysInfo.dwPageSize;
370 void setExecutable (void *p, lnat len, rtsBool exec)
372 DWORD dwOldProtect = 0;
373 if (VirtualProtect (p, len,
374 exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
377 sysErrorBelch("setExecutable: failed to protect 0x%p; old protection: %lu\n",
378 p, (unsigned long)dwOldProtect);
379 stg_exit(EXIT_FAILURE);