Whitespace only, in rts/win32/OSMem.c
[ghc.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 }