[project @ 2000-12-04 12:31:19 by simonmar]
[ghc-hetmet.git] / ghc / rts / MBlock.c
1 /* -----------------------------------------------------------------------------
2  * $Id: MBlock.c,v 1.19 2000/12/04 12:31:21 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 #else
69   ret = mmap(next_request, size, PROT_READ | PROT_WRITE, 
70              MAP_ANON | MAP_PRIVATE, -1, 0);
71 #endif
72   
73   if (ret == (void *)-1) {
74     if (errno == ENOMEM) {
75       barf("getMBlock: out of memory");
76     } else {
77       barf("GetMBlock: mmap failed");
78     }
79   }
80
81   if (((W_)ret & MBLOCK_MASK) != 0) {
82     barf("GetMBlock: misaligned block returned");
83   }
84
85   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
86
87   next_request += size;
88
89   mblocks_allocated += n;
90   
91   return ret;
92 }
93
94 #else /* _WIN32 */
95
96 /*
97  On Win32 platforms we make use of the two-phased virtual memory API
98  to allocate mega blocks. We proceed as follows:
99
100  Reserve a large chunk of VM (128M at the time), but don't supply a 
101  base address that's aligned on a MB boundary. Instead we round up to the
102  nearest from the chunk of VM we're given back from the OS (at the
103  moment we just leave the 'slop' at the beginning of the reserved
104  chunk unused - ToDo: reuse it .)
105
106  Reserving memory doesn't allocate physical storage (not even in the
107  page file), this is done by committing pages (or mega-blocks in
108  our case).
109
110 */
111
112 char* base_non_committed = (char*)0;
113
114 /* Reserve VM 128M at the time to try to minimise the slop cost. */
115 #define SIZE_RESERVED_POOL  ( 128 * 1024 * 1024 )
116
117 /* This predicate should be inlined, really. */
118 /* TODO: this only works for a single chunk */
119 int
120 is_heap_alloced(const void* x)
121 {
122   return (((char*)(x) >= base_non_committed) && 
123           ((char*)(x) <= (base_non_committed + SIZE_RESERVED_POOL)));
124 }
125
126 void *
127 getMBlocks(nat n)
128 {
129   static char* base_mblocks       = (char*)0;
130   static char* next_request       = (char*)0;
131   void* ret                       = (void*)0;
132
133   lnat size = MBLOCK_SIZE * n;
134
135   if ( (base_non_committed == 0) || 
136        (next_request + size > base_non_committed + SIZE_RESERVED_POOL) ) {
137 #ifdef ENABLE_WIN32_DLL_SUPPORT
138     if (base_non_committed)
139         barf("Windows programs can only use 128Mb of heap; sorry!");
140 #endif
141     base_non_committed = VirtualAlloc ( NULL
142                                       , SIZE_RESERVED_POOL
143                                       , MEM_RESERVE
144                                       , PAGE_READWRITE
145                                       );
146     if ( base_non_committed == 0 ) {
147 # if 1 /*def DEBUG*/
148          fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
149 # endif
150          ret=(void*)-1;
151     } else {
152     /* The returned pointer is not aligned on a mega-block boundary. Make it. */
153        base_mblocks = (char*)((unsigned long)base_non_committed & (unsigned long)0xfff00000) + MBLOCK_SIZE;
154 # if 0
155        fprintf(stderr, "Dropping %d bytes off of 128M chunk\n", 
156                        (unsigned)base_mblocks - (unsigned)base_non_committed);
157 # endif
158
159        if ( ((char*)base_mblocks + size) > ((char*)base_non_committed + SIZE_RESERVED_POOL) ) {
160 # if 1 /*def DEBUG*/
161           fprintf(stderr, "oops, committed too small a region to start with.");
162 # endif
163           ret=(void*)-1;
164        } else {
165           next_request = base_mblocks;
166        }
167     }
168   }
169   /* Commit the mega block(s) to phys mem */
170   if ( ret != (void*)-1 ) {
171      ret = VirtualAlloc(next_request, size, MEM_COMMIT, PAGE_READWRITE);
172      if (ret == NULL) {
173 # if 1 /*def DEBUG*/
174         fprintf(stderr, "getMBlocks: VirtualAlloc failed with: %d\n", GetLastError());
175 # endif
176         ret=(void*)-1;
177      }
178   }
179
180   if (((W_)ret & MBLOCK_MASK) != 0) {
181     barf("GetMBlock: misaligned block returned");
182   }
183
184   IF_DEBUG(gc,fprintf(stderr,"Allocated %d megablock(s) at %x\n",n,(nat)ret));
185
186   next_request = (char*)next_request + size;
187
188   mblocks_allocated += n;
189   
190   return ret;
191 }
192
193 /* Hand back the physical memory that is allocated to a mega-block. 
194    ToDo: chain the released mega block onto some list so that
195          getMBlocks() can get at it.
196
197    Currently unused.
198 */
199 #if 0
200 void
201 freeMBlock(void* p, nat n)
202 {
203   BOOL rc;
204
205   rc = VirtualFree(p, n * MBLOCK_SIZE , MEM_DECOMMIT );
206   
207   if (rc == FALSE) {
208 # ifdef DEBUG
209      fprintf(stderr, "freeMBlocks: VirtualFree failed with: %d\n", GetLastError());
210 # endif
211   }
212
213 }
214 #endif
215
216 #endif