Whitespace only, in rts/win32/OSMem.c
[ghc-hetmet.git] / rts / win32 / OSMem.c
1 /* -----------------------------------------------------------------------------
2  *
3  * (c) The University of Glasgow 2006-2007
4  *
5  * OS-specific memory management
6  *
7  * ---------------------------------------------------------------------------*/
8
9 #include "Rts.h"
10 #include "sm/OSMem.h"
11 #include "RtsUtils.h"
12
13 #if HAVE_WINDOWS_H
14 #include <windows.h>
15 #endif
16
17 /* alloc_rec keeps the info we need to have matching VirtualAlloc and
18    VirtualFree calls.
19 */
20 typedef struct alloc_rec_ {
21     char* base;     /* non-aligned base address, directly from VirtualAlloc */
22     int size;       /* Size in bytes */
23     struct alloc_rec_* next;
24 } alloc_rec;
25
26 typedef struct block_rec_ {
27     char* base;         /* base address, non-MBLOCK-aligned */
28     int size;           /* size in bytes */
29     struct block_rec_* next;
30 } block_rec;
31
32 static alloc_rec* allocs = NULL;
33 static block_rec* free_blocks = NULL;
34
35 void
36 osMemInit(void)
37 {
38     allocs = NULL;
39     free_blocks = NULL;
40 }
41
42 static
43 alloc_rec*
44 allocNew(nat n) {
45     alloc_rec* rec;
46     rec = (alloc_rec*)stgMallocBytes(sizeof(alloc_rec),"getMBlocks: allocNew");
47     rec->size = (n+1)*MBLOCK_SIZE;
48     rec->base =
49         VirtualAlloc(NULL, rec->size, MEM_RESERVE, PAGE_READWRITE);
50     if(rec->base==0) {
51         stgFree((void*)rec);
52         rec=0;
53         if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {
54
55             errorBelch("out of memory");
56         } else {
57             sysErrorBelch(
58                 "getMBlocks: VirtualAlloc MEM_RESERVE %d blocks failed", n);
59         }
60     } else {
61         alloc_rec temp;
62         temp.base=0; temp.size=0; temp.next=allocs;
63
64         alloc_rec* it;
65         it=&temp;
66         for(; it->next!=0 && it->next->base<rec->base; it=it->next) ;
67         rec->next=it->next;
68         it->next=rec;
69
70         allocs=temp.next;
71     }
72     return rec;
73 }
74
75 static
76 void
77 insertFree(char* alloc_base, int alloc_size) {
78     block_rec temp;
79     block_rec* it;
80     block_rec* prev;
81
82     temp.base=0; temp.size=0; temp.next=free_blocks;
83     it = free_blocks;
84     prev = &temp;
85     for( ; it!=0 && it->base<alloc_base; prev=it, it=it->next) {}
86
87     if(it!=0 && alloc_base+alloc_size == it->base) {
88         if(prev->base + prev->size == alloc_base) {        /* Merge it, alloc, prev */
89             prev->size += alloc_size + it->size;
90             prev->next = it->next;
91             stgFree(it);
92         } else {                                            /* Merge it, alloc */
93             it->base = alloc_base;
94             it->size += alloc_size;
95         }
96     } else if(prev->base + prev->size == alloc_base) {     /* Merge alloc, prev */
97         prev->size += alloc_size;
98     } else {                                                /* Merge none */
99         block_rec* rec;
100         rec = (block_rec*)stgMallocBytes(sizeof(block_rec),"getMBlocks: insertFree");
101         rec->base=alloc_base;
102         rec->size=alloc_size;
103         rec->next = it;
104         prev->next=rec;
105     }
106     free_blocks=temp.next;
107 }
108
109 static
110 void*
111 findFreeBlocks(nat n) {
112     void* ret=0;
113     block_rec* it;
114     block_rec temp;
115     block_rec* prev;
116
117     int required_size;
118     it=free_blocks;
119     required_size = n*MBLOCK_SIZE;
120     temp.next=free_blocks; temp.base=0; temp.size=0;
121     prev=&temp;
122     /* TODO: Don't just take first block, find smallest sufficient block */
123     for( ; it!=0 && it->size<required_size; prev=it, it=it->next ) {}
124     if(it!=0) {
125         if( (((unsigned long)it->base) & MBLOCK_MASK) == 0) { /* MBlock aligned */
126             ret = (void*)it->base;
127             if(it->size==required_size) {
128                 prev->next=it->next;
129                 stgFree(it);
130             } else {
131                 it->base += required_size;
132                 it->size -=required_size;
133             }
134         } else {
135             char* need_base;
136             block_rec* next;
137             int new_size;
138             need_base = (char*)(((unsigned long)it->base) & ((unsigned long)~MBLOCK_MASK)) + MBLOCK_SIZE;
139             next = (block_rec*)stgMallocBytes(
140                     sizeof(block_rec)
141                     , "getMBlocks: findFreeBlocks: splitting");
142             new_size = need_base - it->base;
143             next->base = need_base +required_size;
144             next->size = it->size - (new_size+required_size);
145             it->size = new_size;
146             next->next = it->next;
147             it->next = next;
148             ret=(void*)need_base;
149         }
150     }
151     free_blocks=temp.next;
152     return ret;
153 }
154
155 /* VirtualAlloc MEM_COMMIT can't cross boundaries of VirtualAlloc MEM_RESERVE,
156    so we might need to do many VirtualAlloc MEM_COMMITs.  We simply walk the
157    (ordered) allocated blocks. */
158 static void
159 commitBlocks(char* base, int size) {
160     alloc_rec* it;
161     it=allocs;
162     for( ; it!=0 && (it->base+it->size)<=base; it=it->next ) {}
163     for( ; it!=0 && size>0; it=it->next ) {
164         int size_delta;
165         void* temp;
166         size_delta = it->size - (base-it->base);
167         if(size_delta>size) size_delta=size;
168         temp = VirtualAlloc(base, size_delta, MEM_COMMIT, PAGE_READWRITE);
169         if(temp==0) {
170             sysErrorBelch("getMBlocks: VirtualAlloc MEM_COMMIT failed");
171             stg_exit(EXIT_FAILURE);
172         }
173         size-=size_delta;
174         base+=size_delta;
175     }
176 }
177
178 void *
179 osGetMBlocks(nat n) {
180     void* ret;
181     ret = findFreeBlocks(n);
182     if(ret==0) {
183         alloc_rec* alloc;
184         alloc = allocNew(n);
185         /* We already belch in allocNew if it fails */
186         if (alloc == 0) {
187             stg_exit(EXIT_FAILURE);
188         } else {
189             insertFree(alloc->base, alloc->size);
190             ret = findFreeBlocks(n);
191         }
192     }
193
194     if(ret!=0) {
195         /* (In)sanity tests */
196         if (((W_)ret & MBLOCK_MASK) != 0) {
197             barf("getMBlocks: misaligned block returned");
198         }
199
200         commitBlocks(ret, MBLOCK_SIZE*n);
201     }
202
203     return ret;
204 }
205
206 void osFreeMBlocks(char *addr, nat n)
207 {
208     alloc_rec *p;
209     lnat nBytes = (lnat)n * MBLOCK_SIZE;
210
211     insertFree(addr, nBytes);
212
213     p = allocs;
214     while ((p != NULL) && (addr >= (p->base + p->size))) {
215         p = p->next;
216     }
217     while (nBytes > 0) {
218         if ((p == NULL) || (p->base > addr)) {
219             errorBelch("Memory to be freed isn't allocated\n");
220             stg_exit(EXIT_FAILURE);
221         }
222         if (p->base + p->size >= addr + nBytes) {
223             if (!VirtualFree(addr, nBytes, MEM_DECOMMIT)) {
224                 sysErrorBelch("osFreeMBlocks: VirtualFree MEM_DECOMMIT failed");
225                 stg_exit(EXIT_FAILURE);
226             }
227             nBytes = 0;
228         }
229         else {
230             lnat bytesToFree = p->base + p->size - addr;
231             if (!VirtualFree(addr, bytesToFree, MEM_DECOMMIT)) {
232                 sysErrorBelch("osFreeMBlocks: VirtualFree MEM_DECOMMIT failed");
233                 stg_exit(EXIT_FAILURE);
234             }
235             addr += bytesToFree;
236             nBytes -= bytesToFree;
237             p = p->next;
238         }
239     }
240 }
241
242 void
243 osFreeAllMBlocks(void)
244 {
245     {
246         block_rec* next;
247         block_rec* it;
248         next=0;
249         it = free_blocks;
250         for(; it!=0; ) {
251             next = it->next;
252             stgFree(it);
253             it=next;
254         }
255     }
256     {
257         alloc_rec* next;
258         alloc_rec* it;
259         next=0;
260         it=allocs;
261         for(; it!=0; ) {
262             if(!VirtualFree((void*)it->base, 0, MEM_RELEASE)) {
263                 sysErrorBelch("freeAllMBlocks: VirtualFree MEM_RELEASE failed");
264                 stg_exit(EXIT_FAILURE);
265             }
266             next = it->next;
267             stgFree(it);
268             it=next;
269         }
270     }
271 }
272
273 lnat getPageSize (void)
274 {
275     static lnat pagesize = 0;
276     if (pagesize) {
277         return pagesize;
278     } else {
279         SYSTEM_INFO sSysInfo;
280         GetSystemInfo(&sSysInfo);
281         pagesize = sSysInfo.dwPageSize;
282         return pagesize;
283     }
284 }
285
286 void setExecutable (void *p, lnat len, rtsBool exec)
287 {
288     DWORD dwOldProtect = 0;
289     if (VirtualProtect (p, len,
290                         exec ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
291                         &dwOldProtect) == 0)
292     {
293         sysErrorBelch("setExecutable: failed to protect 0x%p; old protection: %lu\n",
294                       p, (unsigned long)dwOldProtect);
295         stg_exit(EXIT_FAILURE);
296     }
297 }