1 /* -----------------------------------------------------------------------------
2 * $Id: MBlock.c,v 1.38 2002/11/07 14:42:25 simonmar Exp $
4 * (c) The GHC Team 1998-1999
6 * MegaBlock Allocator Interface. This file contains all the dirty
7 * architecture-dependent hackery required to get a chunk of aligned
8 * memory from the operating system.
10 * ---------------------------------------------------------------------------*/
12 /* This is non-posix compliant. */
13 /* #include "PosixSource.h" */
19 #include "BlockAlloc.h"
27 #ifdef HAVE_SYS_TYPES_H
28 #include <sys/types.h>
30 #ifndef mingw32_TARGET_OS
31 # ifdef HAVE_SYS_MMAN_H
32 # include <sys/mman.h>
42 #include <mach/vm_map.h>
47 lnat mblocks_allocated = 0;
49 /* -----------------------------------------------------------------------------
50 The MBlock Map: provides our implementation of HEAP_ALLOCED()
51 -------------------------------------------------------------------------- */
53 StgWord8 mblock_map[4096]; // initially all zeros
56 mblockIsHeap (void *p)
58 mblock_map[((StgWord)p & ~MBLOCK_MASK) >> MBLOCK_SHIFT] = 1;
61 /* -----------------------------------------------------------------------------
62 Allocate new mblock(s)
63 -------------------------------------------------------------------------- */
71 /* -----------------------------------------------------------------------------
74 On Unix-like systems, we use mmap() to allocate our memory. We
75 want memory in chunks of MBLOCK_SIZE, and aligned on an MBLOCK_SIZE
76 boundary. The mmap() interface doesn't give us this level of
77 control, so we have to use some heuristics.
79 In the general case, if we want a block of n megablocks, then we
80 allocate n+1 and trim off the slop from either side (using
81 munmap()) to get an aligned chunk of size n. However, the next
82 time we'll try to allocate directly after the previously allocated
83 chunk, on the grounds that this is aligned and likely to be free.
84 If it turns out that we were wrong, we have to munmap() and try
85 again using the general method.
86 -------------------------------------------------------------------------- */
88 #if !defined(mingw32_TARGET_OS) && !defined(cygwin32_TARGET_OS)
90 // A wrapper around mmap(), to abstract away from OS differences in
91 // the mmap() interface.
94 my_mmap (void *addr, int size)
98 #ifdef solaris2_TARGET_OS
100 int fd = open("/dev/zero",O_RDONLY);
101 ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
105 ret = mmap(addr, size, PROT_READ | PROT_WRITE,
106 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
107 #elif darwin_TARGET_OS
108 // Without MAP_FIXED, Apple's mmap ignores addr.
109 // With MAP_FIXED, it overwrites already mapped regions, whic
110 // mmap(0, ... MAP_FIXED ...) is worst of all: It unmaps the program text
111 // and replaces it with zeroes, causing instant death.
112 // This behaviour seems to be conformant with IEEE Std 1003.1-2001.
113 // Let's just use the underlying Mach Microkernel calls directly,
114 // they're much nicer.
118 if(addr) // try to allocate at adress
119 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, FALSE);
120 if(!addr || err) // try to allocate anywhere
121 err = vm_allocate(mach_task_self(),(vm_address_t*) &ret, size, TRUE);
123 if(err) // don't know what the error codes mean exactly
124 barf("memory allocation failed (requested %d bytes)", size);
126 vm_protect(mach_task_self(),ret,size,FALSE,VM_PROT_READ|VM_PROT_WRITE);
128 ret = mmap(addr, size, PROT_READ | PROT_WRITE,
129 MAP_ANON | MAP_PRIVATE, -1, 0);
132 if (ret == (void *)-1) {
133 if (errno == ENOMEM) {
134 barf("out of memory (requested %d bytes)", size);
136 barf("getMBlock: mmap failed");
143 // Implements the general case: allocate a chunk of memory of 'size'
147 gen_map_mblocks (int size)
152 // Try to map a larger block, and take the aligned portion from
153 // it (unmap the rest).
155 ret = my_mmap(0, size);
157 // unmap the slop bits around the chunk we allocated
158 slop = (W_)ret & MBLOCK_MASK;
160 if (munmap(ret, MBLOCK_SIZE - slop) == -1) {
161 barf("gen_map_mblocks: munmap failed");
163 if (slop > 0 && munmap(ret+size-slop, slop) == -1) {
164 barf("gen_map_mblocks: munmap failed");
167 // ToDo: if we happened to get an aligned block, then don't
168 // unmap the excess, just use it.
170 // next time, try after the block we just got.
171 ret += MBLOCK_SIZE - slop;
176 // The external interface: allocate 'n' mblocks, and return the
182 static caddr_t next_request = (caddr_t)HEAP_BASE;
184 lnat size = MBLOCK_SIZE * n;
187 if (next_request == 0) {
188 // use gen_map_mblocks the first time.
189 ret = gen_map_mblocks(size);
191 ret = my_mmap(next_request, size);
193 if (((W_)ret & MBLOCK_MASK) != 0) {
196 belch("warning: getMBlock: misaligned block %p returned when allocating %d megablock(s) at %p", ret, n, next_request);
199 // unmap this block...
200 if (munmap(ret, size) == -1) {
201 barf("getMBlock: munmap failed");
203 // and do it the hard way
204 ret = gen_map_mblocks(size);
208 // Next time, we'll try to allocate right after the block we just got.
209 // ToDo: check that we haven't already grabbed the memory at next_request
210 next_request = ret + size;
212 IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %p\n",n,ret));
215 for (i = 0; i < n; i++) {
216 mblockIsHeap( ret + i * MBLOCK_SIZE );
219 mblocks_allocated += n;
224 #else /* defined(mingw32_TARGET_OS) || defined(cygwin32_TARGET_OS) */
227 On Win32 platforms we make use of the two-phased virtual memory API
228 to allocate mega blocks. We proceed as follows:
230 Reserve a large chunk of VM (256M at the time, or what the user asked
231 for via the -M option), but don't supply a base address that's aligned on
232 a MB boundary. Instead we round up to the nearest mblock from the chunk of
233 VM we're handed back from the OS (at the moment we just leave the 'slop' at
234 the beginning of the reserved chunk unused - ToDo: reuse it .)
236 Reserving memory doesn't allocate physical storage (not even in the
237 page file), this is done later on by committing pages (or mega-blocks in
241 char* base_non_committed = (char*)0;
242 char* end_non_committed = (char*)0;
244 /* Default is to reserve 256M of VM to minimise the slop cost. */
245 #define SIZE_RESERVED_POOL ( 256 * 1024 * 1024 )
247 /* Number of bytes reserved */
248 static unsigned long size_reserved_pool = SIZE_RESERVED_POOL;
253 static char* base_mblocks = (char*)0;
254 static char* next_request = (char*)0;
255 void* ret = (void*)0;
258 lnat size = MBLOCK_SIZE * n;
260 if ( (base_non_committed == 0) || (next_request + size > end_non_committed) ) {
261 if (base_non_committed) {
262 barf("RTS exhausted max heap size (%d bytes)\n", size_reserved_pool);
264 if (RtsFlags.GcFlags.maxHeapSize != 0) {
265 size_reserved_pool = BLOCK_SIZE * RtsFlags.GcFlags.maxHeapSize;
266 if (size_reserved_pool < MBLOCK_SIZE) {
267 size_reserved_pool = 2*MBLOCK_SIZE;
270 base_non_committed = VirtualAlloc ( NULL
275 if ( base_non_committed == 0 ) {
276 fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %ld\n", GetLastError());
279 end_non_committed = (char*)base_non_committed + (unsigned long)size_reserved_pool;
280 /* The returned pointer is not aligned on a mega-block boundary. Make it. */
281 base_mblocks = (char*)((unsigned long)base_non_committed & (unsigned long)~MBLOCK_MASK) + MBLOCK_SIZE;
283 fprintf(stderr, "getMBlocks: Dropping %d bytes off of 256M chunk\n",
284 (unsigned)base_mblocks - (unsigned)base_non_committed);
287 if ( ((char*)base_mblocks + size) > end_non_committed ) {
288 fprintf(stderr, "getMBlocks: oops, committed too small a region to start with.");
291 next_request = base_mblocks;
295 /* Commit the mega block(s) to phys mem */
296 if ( ret != (void*)-1 ) {
297 ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
299 fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %ld\n", GetLastError());
304 if (((W_)ret & MBLOCK_MASK) != 0) {
305 barf("getMBlocks: misaligned block returned");
308 if (ret == (void*)-1) {
309 barf("getMBlocks: unknown memory allocation failure on Win32.");
312 IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at 0x%x\n",n,(nat)ret));
313 next_request = (char*)next_request + size;
315 mblocks_allocated += n;
318 for (i = 0; i < n; i++) {
319 mblockIsHeap( ret + i * MBLOCK_SIZE );
325 /* Hand back the physical memory that is allocated to a mega-block.
326 ToDo: chain the released mega block onto some list so that
327 getMBlocks() can get at it.
333 freeMBlock(void* p, nat n)
337 rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
341 fprintf(stderr, "freeMBlocks: VirtualFree failed with: %d\n", GetLastError());