2005-04-12 Dick Porter <dick@ximian.com>
[mono.git] / mono / metadata / rawbuffer.c
1 /*
2  * rawbuffer.c: Manages buffers that might have been mmapped or malloced
3  *
4  * Author:
5  *   Miguel de Icaza (miguel@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9 #include <config.h>
10 #if defined(PLATFORM_WIN32)
11 #define USE_WIN32_API           1
12 #endif
13
14 #include <unistd.h>
15 #include <errno.h>
16 #ifdef USE_WIN32_API
17 #include <winsock2.h>
18 #include <windows.h>
19 #include <io.h>
20 #else
21 #include <sys/mman.h>
22 #endif
23 #include <sys/types.h>
24 #include <glib.h>
25 #include "rawbuffer.h"
26
27 #include <mono/io-layer/io-layer.h>
28
29 #define ROUND_DOWN(VALUE,SIZE)  ((VALUE) & ~((SIZE) - 1))
30 #define ROUND_UP(VALUE,SIZE)    (ROUND_DOWN((VALUE) + (SIZE) - 1, (SIZE)))
31 #if SIZEOF_VOID_P == 8
32 #define UINTPTR_TYPE guint64
33 #else
34 #define UINTPTR_TYPE guint32
35 #endif
36
37 static GHashTable *mmap_map = NULL;
38 static size_t alignment = 0;
39 static CRITICAL_SECTION mmap_mutex;
40
41 static void
42 get_alignment (void)
43 {
44 #ifdef USE_WIN32_API
45         SYSTEM_INFO info;
46
47         GetSystemInfo (&info);
48         alignment = info.dwAllocationGranularity;
49 #else
50         alignment = getpagesize ();
51 #endif
52 }
53
54 static void *
55 mono_raw_buffer_load_malloc (int fd, int is_writable, guint32 base, size_t size)
56 {
57         void *ptr;
58
59         ptr = g_malloc (size);
60         if (ptr == NULL)
61                 return NULL;
62
63         if (lseek (fd, base, 0) == (off_t) -1) {
64                 g_free (ptr);
65                 return NULL;
66         }
67
68         read (fd, ptr, size);
69         return ptr;
70 }
71
72 static void
73 mono_raw_buffer_free_malloc (void *base)
74 {
75         g_free (base);
76 }
77
78 void
79 mono_raw_buffer_init (void)
80 {
81         InitializeCriticalSection (&mmap_mutex);
82
83         get_alignment ();
84
85         mmap_map = g_hash_table_new (NULL, NULL);
86 }
87
88 static void *
89 mono_raw_buffer_load_mmap (int fd, int is_writable, guint32 base, size_t size)
90 {
91 #ifdef USE_WIN32_API
92         /* FileMapping implementation */
93
94         DWORD start, end;
95         int prot, access;
96         void *ptr;
97         HANDLE file, mapping;
98
99         start = ROUND_DOWN (base, alignment);
100         end = base + size;
101         
102         if (is_writable) {
103                 prot = PAGE_WRITECOPY;
104                 access = FILE_MAP_COPY;
105         }
106         else {
107                 prot = PAGE_READONLY;
108                 access = FILE_MAP_READ;
109         }
110
111         file = (HANDLE) _get_osfhandle (fd);
112         mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
113         if (mapping == NULL)
114                 return 0;
115
116         ptr = MapViewOfFile (mapping, access, 0, start, end - start);
117         if (ptr == NULL) {
118                 CloseHandle (mapping);
119                 return 0;
120         }
121
122         EnterCriticalSection (&mmap_mutex);
123         g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (mapping));
124         LeaveCriticalSection (&mmap_mutex);
125         
126         return ((char *)ptr) + (base - start);
127
128 #else
129         /* mmap implementation */
130
131
132         size_t start, end;
133         int prot = PROT_READ;
134         int flags = 0;
135         void *ptr;
136
137         start = ROUND_DOWN (base, alignment);
138         end = ROUND_UP (base + size, alignment);
139
140         if (is_writable){
141                 prot |= PROT_WRITE;
142                 flags = MAP_SHARED;
143         } else {
144                 flags = MAP_PRIVATE;
145         }
146
147         ptr = mmap (0, end - start, prot, flags, fd, start);
148
149         if (ptr == (void *) -1)
150                 return 0;
151
152         /* 
153          * This seems to prevent segmentation faults on Fedora Linux, no
154          * idea why :). See
155          * http://bugzilla.ximian.com/show_bug.cgi?id=49499
156          * for more info.
157          */
158         if (mprotect (ptr, end - start, prot | PROT_EXEC) != 0)
159                 g_warning (G_GNUC_PRETTY_FUNCTION
160                                    ": mprotect failed: %s", g_strerror (errno));
161         
162         EnterCriticalSection (&mmap_mutex);
163         g_hash_table_insert (mmap_map, ptr, GINT_TO_POINTER (size));
164         LeaveCriticalSection (&mmap_mutex);
165
166         return ((char *)ptr) + (base - start);
167 #endif
168 }
169
170 static void
171 mono_raw_buffer_free_mmap (void *base)
172 {
173         int value;
174
175         EnterCriticalSection (&mmap_mutex);
176         value = GPOINTER_TO_INT (g_hash_table_lookup (mmap_map, base));
177         LeaveCriticalSection (&mmap_mutex);
178
179 #ifdef USE_WIN32_API
180         UnmapViewOfFile (base);
181         CloseHandle ((HANDLE) value);
182 #else
183         munmap (base, value);
184 #endif
185 }
186
187 static void
188 mono_raw_buffer_update_mmap (void *base, size_t size)
189 {
190 #ifdef USE_WIN32_API
191         FlushViewOfFile (base, size);
192 #else
193         msync (base, size, MS_SYNC);
194 #endif
195 }
196
197 void *
198 mono_raw_buffer_load (int fd, int is_writable, guint32 base, size_t size)
199 {
200         void *ptr;
201
202         ptr = mono_raw_buffer_load_mmap (fd, is_writable, base, size);
203         if (ptr == 0)
204                 ptr = mono_raw_buffer_load_malloc (fd, is_writable, base, size);
205         
206         return ptr;
207 }
208
209 void
210 mono_raw_buffer_update (void *buffer, size_t size)
211 {
212         char *mmap_base;
213         gboolean exists;
214
215         mmap_base =  (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment));
216
217         EnterCriticalSection (&mmap_mutex);
218         exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL;
219         LeaveCriticalSection (&mmap_mutex);
220         if (exists)
221                 mono_raw_buffer_update_mmap (mmap_base, size);
222 }
223
224 void
225 mono_raw_buffer_free (void *buffer)
226 {
227         char *mmap_base;
228         gboolean exists;
229
230         mmap_base = (gpointer)(ROUND_DOWN ((UINTPTR_TYPE) (buffer), alignment));
231         
232         exists = g_hash_table_lookup (mmap_map, mmap_base) != NULL;
233         if (exists)
234                 mono_raw_buffer_free_mmap (mmap_base);
235         else
236                 mono_raw_buffer_free_malloc (buffer);
237 }
238