[project @ 2001-01-16 11:54:25 by simonmar]
[ghc-hetmet.git] / ghc / rts / MBlock.c
1 /* -----------------------------------------------------------------------------
2  * $Id: MBlock.c,v 1.21 2001/01/16 11:54:25 simonmar Exp $
3  *
4  * (c) The GHC Team 1998-1999
5  *
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.
9  *
10  * ---------------------------------------------------------------------------*/
11
12 #define NON_POSIX_SOURCE
13
14 #include "Rts.h"
15 #include "RtsUtils.h"
16 #include "RtsFlags.h"
17 #include "MBlock.h"
18 #include "BlockAlloc.h"
19
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23
24 #ifdef HAVE_SYS_TYPES_H
25 #include <sys/types.h>
26 #endif
27
28 #ifndef mingw32_TARGET_OS
29 # ifdef HAVE_SYS_MMAN_H
30 # include <sys/mman.h>
31 # endif
32 #endif
33
34 #ifdef HAVE_FCNTL_H
35 #include <fcntl.h>
36 #endif
37
38 #if HAVE_WINDOWS_H
39 #include <windows.h>
40 #endif
41
42 lnat mblocks_allocated = 0;
43
44 void *
45 getMBlock(void)
46 {
47   return getMBlocks(1);
48 }
49
50 #ifndef _WIN32
51 void *
52 getMBlocks(nat n)
53 {
54   static caddr_t next_request = (caddr_t)HEAP_BASE;
55   caddr_t ret;
56   lnat size = MBLOCK_SIZE * n;
57  
58 #ifdef solaris2_TARGET_OS
59   { 
60       int fd = open("/dev/zero",O_RDONLY);
61       ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
62                  MAP_FIXED | MAP_PRIVATE, fd, 0);
63       close(fd);
64   }
65 #elif hpux_TARGET_OS
66  ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
67              MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
68 #elif macosx_TARGET_OS
69  ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
70             MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
71 #else
72   ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
73              MAP_ANON | MAP_PRIVATE, -1, 0);
74 #endif
75   
76   if (ret == (void *)-1) {
77     if (errno == ENOMEM) {
78       barf("getMBlock: out of memory");
79     } else {
80       barf("GetMBlock: mmap failed");
81     }
82   }
83
84   if (((W_)ret & MBLOCK_MASK) != 0) {
85     barf("GetMBlock: misaligned block %p returned when allocating %d megablock(s) at %p", ret, n, next_request);
86   }
87
88   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
89
90   next_request += size;
91
92   mblocks_allocated += n;
93   
94   return ret;
95 }
96
97 #else /* _WIN32 */
98
99 /*
100  On Win32 platforms we make use of the two-phased virtual memory API
101  to allocate mega blocks. We proceed as follows:
102
103  Reserve a large chunk of VM (128M at the time), but don't supply a 
104  base address that's aligned on a MB boundary. Instead we round up to the
105  nearest from the chunk of VM we're given back from the OS (at the
106  moment we just leave the 'slop' at the beginning of the reserved
107  chunk unused - ToDo: reuse it .)
108
109  Reserving memory doesn't allocate physical storage (not even in the
110  page file), this is done by committing pages (or mega-blocks in
111  our case).
112
113 */
114
115 char* base_non_committed = (char*)0;
116
117 /* Reserve VM 128M at the time to try to minimise the slop cost. */
118 #define SIZE_RESERVED_POOL  ( 128 * 1024 * 1024 )
119
120 /* This predicate should be inlined, really. */
121 /* TODO: this only works for a single chunk */
122 int
123 is_heap_alloced(const void* x)
124 {
125   return (((char*)(x) >= base_non_committed) && 
126           ((char*)(x) <= (base_non_committed + SIZE_RESERVED_POOL)));
127 }
128
129 void *
130 getMBlocks(nat n)
131 {
132   static char* base_mblocks       = (char*)0;
133   static char* next_request       = (char*)0;
134   void* ret                       = (void*)0;
135
136   lnat size = MBLOCK_SIZE * n;
137
138   if ( (base_non_committed == 0) || 
139        (next_request + size > base_non_committed + SIZE_RESERVED_POOL) ) {
140 #ifdef ENABLE_WIN32_DLL_SUPPORT
141     if (base_non_committed)
142         barf("Windows programs can only use 128Mb of heap; sorry!");
143 #endif
144     base_non_committed = VirtualAlloc ( NULL
145                                       , SIZE_RESERVED_POOL
146                                       , MEM_RESERVE
147                                       , PAGE_READWRITE
148                                       );
149     if ( base_non_committed == 0 ) {
150 # if 1 /*def DEBUG*/
151          fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
152 # endif
153          ret=(void*)-1;
154     } else {
155     /* The returned pointer is not aligned on a mega-block boundary. Make it. */
156        base_mblocks = (char*)((unsigned long)base_non_committed & (unsigned long)0xfff00000) + MBLOCK_SIZE;
157 # if 0
158        fprintf(stderr, "Dropping %d bytes off of 128M chunk\n", 
159                        (unsigned)base_mblocks - (unsigned)base_non_committed);
160 # endif
161
162        if ( ((char*)base_mblocks + size) > ((char*)base_non_committed + SIZE_RESERVED_POOL) ) {
163 # if 1 /*def DEBUG*/
164           fprintf(stderr, "oops, committed too small a region to start with.");
165 # endif
166           ret=(void*)-1;
167        } else {
168           next_request = base_mblocks;
169        }
170     }
171   }
172   /* Commit the mega block(s) to phys mem */
173   if ( ret != (void*)-1 ) {
174      ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
175      if (ret == NULL) {
176 # if 1 /*def DEBUG*/
177         fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
178 # endif
179         ret=(void*)-1;
180      }
181   }
182
183   if (((W_)ret & MBLOCK_MASK) != 0) {
184     barf("GetMBlock: misaligned block returned");
185   }
186
187   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
188
189   next_request = (char*)next_request + size;
190
191   mblocks_allocated += n;
192   
193   return ret;
194 }
195
196 /* Hand back the physical memory that is allocated to a mega-block. 
197    ToDo: chain the released mega block onto some list so that
198          getMBlocks() can get at it.
199
200    Currently unused.
201 */
202 #if 0
203 void
204 freeMBlock(void* p, nat n)
205 {
206   BOOL rc;
207
208   rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
209   
210   if (rc == FALSE) {
211 # ifdef DEBUG
212      fprintf(stderr, "freeMBlocks: VirtualFree failed with: %d\n", GetLastError());
213 # endif
214   }
215
216 }
217 #endif
218
219 #endif