X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=rts%2Fwin32%2FOSMem.c;h=44286d25622a83adb1c05821f1afc833c60bd899;hb=83d563cb9ede0ba792836e529b1e2929db926355;hp=356c6dffa407557bd590c1a05f723ea968efa953;hpb=7a876582088a557b4ac978f272c91e7f4aa2eacf;p=ghc-hetmet.git diff --git a/rts/win32/OSMem.c b/rts/win32/OSMem.c index 356c6df..44286d2 100644 --- a/rts/win32/OSMem.c +++ b/rts/win32/OSMem.c @@ -1,14 +1,274 @@ /* ----------------------------------------------------------------------------- * - * (c) The University of Glasgow 2006 + * (c) The University of Glasgow 2006-2007 * * OS-specific memory management * * ---------------------------------------------------------------------------*/ -#include #include "Rts.h" -#include "OSMem.h" +#include "sm/OSMem.h" +#include "RtsUtils.h" + +#if HAVE_WINDOWS_H +#include +#endif + +/* alloc_rec keeps the info we need to have matching VirtualAlloc and + VirtualFree calls. +*/ +typedef struct alloc_rec_ { + char* base; /* non-aligned base address, directly from VirtualAlloc */ + int size; /* Size in bytes */ + struct alloc_rec_* next; +} alloc_rec; + +typedef struct block_rec_ { + char* base; /* base address, non-MBLOCK-aligned */ + int size; /* size in bytes */ + struct block_rec_* next; +} block_rec; + +static alloc_rec* allocs = NULL; +static block_rec* free_blocks = NULL; + +void +osMemInit(void) +{ + allocs = NULL; + free_blocks = NULL; +} + +static +alloc_rec* +allocNew(nat n) { + alloc_rec* rec; + rec = (alloc_rec*)stgMallocBytes(sizeof(alloc_rec),"getMBlocks: allocNew"); + rec->size = (n+1)*MBLOCK_SIZE; + rec->base = + VirtualAlloc(NULL, rec->size, MEM_RESERVE, PAGE_READWRITE); + if(rec->base==0) { + stgFree((void*)rec); + rec=0; + if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { + + errorBelch("out of memory"); + } else { + sysErrorBelch( + "getMBlocks: VirtualAlloc MEM_RESERVE %d blocks failed", n); + } + } else { + alloc_rec temp; + temp.base=0; temp.size=0; temp.next=allocs; + + alloc_rec* it; + it=&temp; + for(; it->next!=0 && it->next->basebase; it=it->next) ; + rec->next=it->next; + it->next=rec; + + allocs=temp.next; + } + return rec; +} + +static +void +insertFree(char* alloc_base, int alloc_size) { + block_rec temp; + block_rec* it; + block_rec* prev; + + temp.base=0; temp.size=0; temp.next=free_blocks; + it = free_blocks; + prev = &temp; + for( ; it!=0 && it->basenext) {} + + if(it!=0 && alloc_base+alloc_size == it->base) { + if(prev->base + prev->size == alloc_base) { /* Merge it, alloc, prev */ + prev->size += alloc_size + it->size; + prev->next = it->next; + stgFree(it); + } else { /* Merge it, alloc */ + it->base = alloc_base; + it->size += alloc_size; + } + } else if(prev->base + prev->size == alloc_base) { /* Merge alloc, prev */ + prev->size += alloc_size; + } else { /* Merge none */ + block_rec* rec; + rec = (block_rec*)stgMallocBytes(sizeof(block_rec),"getMBlocks: insertFree"); + rec->base=alloc_base; + rec->size=alloc_size; + rec->next = it; + prev->next=rec; + } + free_blocks=temp.next; +} + +static +void* +findFreeBlocks(nat n) { + void* ret=0; + block_rec* it; + block_rec temp; + block_rec* prev; + + int required_size; + it=free_blocks; + required_size = n*MBLOCK_SIZE; + temp.next=free_blocks; temp.base=0; temp.size=0; + prev=&temp; + /* TODO: Don't just take first block, find smallest sufficient block */ + for( ; it!=0 && it->sizenext ) {} + if(it!=0) { + if( (((unsigned long)it->base) & MBLOCK_MASK) == 0) { /* MBlock aligned */ + ret = (void*)it->base; + if(it->size==required_size) { + prev->next=it->next; + stgFree(it); + } else { + it->base += required_size; + it->size -=required_size; + } + } else { + char* need_base; + block_rec* next; + int new_size; + need_base = (char*)(((unsigned long)it->base) & ((unsigned long)~MBLOCK_MASK)) + MBLOCK_SIZE; + next = (block_rec*)stgMallocBytes( + sizeof(block_rec) + , "getMBlocks: findFreeBlocks: splitting"); + new_size = need_base - it->base; + next->base = need_base +required_size; + next->size = it->size - (new_size+required_size); + it->size = new_size; + next->next = it->next; + it->next = next; + ret=(void*)need_base; + } + } + free_blocks=temp.next; + return ret; +} + +/* VirtualAlloc MEM_COMMIT can't cross boundaries of VirtualAlloc MEM_RESERVE, + so we might need to do many VirtualAlloc MEM_COMMITs. We simply walk the + (ordered) allocated blocks. */ +static void +commitBlocks(char* base, int size) { + alloc_rec* it; + it=allocs; + for( ; it!=0 && (it->base+it->size)<=base; it=it->next ) {} + for( ; it!=0 && size>0; it=it->next ) { + int size_delta; + void* temp; + size_delta = it->size - (base-it->base); + if(size_delta>size) size_delta=size; + temp = VirtualAlloc(base, size_delta, MEM_COMMIT, PAGE_READWRITE); + if(temp==0) { + sysErrorBelch("getMBlocks: VirtualAlloc MEM_COMMIT failed"); + stg_exit(EXIT_FAILURE); + } + size-=size_delta; + base+=size_delta; + } +} + +void * +osGetMBlocks(nat n) { + void* ret; + ret = findFreeBlocks(n); + if(ret==0) { + alloc_rec* alloc; + alloc = allocNew(n); + /* We already belch in allocNew if it fails */ + if (alloc == 0) { + stg_exit(EXIT_FAILURE); + } else { + insertFree(alloc->base, alloc->size); + ret = findFreeBlocks(n); + } + } + + if(ret!=0) { + /* (In)sanity tests */ + if (((W_)ret & MBLOCK_MASK) != 0) { + barf("getMBlocks: misaligned block returned"); + } + + commitBlocks(ret, MBLOCK_SIZE*n); + } + + return ret; +} + +void osFreeMBlocks(char *addr, nat n) +{ + alloc_rec *p; + lnat nBytes = (lnat)n * MBLOCK_SIZE; + + insertFree(addr, nBytes); + + p = allocs; + while ((p != NULL) && (addr >= (p->base + p->size))) { + p = p->next; + } + while (nBytes > 0) { + if ((p == NULL) || (p->base > addr)) { + errorBelch("Memory to be freed isn't allocated\n"); + stg_exit(EXIT_FAILURE); + } + if (p->base + p->size >= addr + nBytes) { + if (!VirtualFree(addr, nBytes, MEM_DECOMMIT)) { + sysErrorBelch("osFreeMBlocks: VirtualFree MEM_DECOMMIT failed"); + stg_exit(EXIT_FAILURE); + } + nBytes = 0; + } + else { + lnat bytesToFree = p->base + p->size - addr; + if (!VirtualFree(addr, bytesToFree, MEM_DECOMMIT)) { + sysErrorBelch("osFreeMBlocks: VirtualFree MEM_DECOMMIT failed"); + stg_exit(EXIT_FAILURE); + } + addr += bytesToFree; + nBytes -= bytesToFree; + p = p->next; + } + } +} + +void +osFreeAllMBlocks(void) +{ + { + block_rec* next; + block_rec* it; + next=0; + it = free_blocks; + for(; it!=0; ) { + next = it->next; + stgFree(it); + it=next; + } + } + { + alloc_rec* next; + alloc_rec* it; + next=0; + it=allocs; + for(; it!=0; ) { + if(!VirtualFree((void*)it->base, 0, MEM_RELEASE)) { + sysErrorBelch("freeAllMBlocks: VirtualFree MEM_RELEASE failed"); + stg_exit(EXIT_FAILURE); + } + next = it->next; + stgFree(it); + it=next; + } + } +} lnat getPageSize (void) { @@ -30,7 +290,8 @@ void setExecutable (void *p, lnat len, rtsBool exec) exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, &dwOldProtect) == 0) { - barf("makeExecutable: failed to protect 0x%p; error=%lu; old protection: %lu\n", - p, (unsigned long)GetLastError(), (unsigned long)dwOldProtect); + sysErrorBelch("setExecutable: failed to protect 0x%p; old protection: %lu\n", + p, (unsigned long)dwOldProtect); + stg_exit(EXIT_FAILURE); } }