6c75b34aba8976c46e9384a2a4ad0d6805f65b23
[cacao.git] / src / mm / memory.c
1 /* src/mm/memory.c - 
2
3    Copyright (C) 1996-2005, 2006 R. Grafl, A. Krall, C. Kruegel,
4    C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
5    E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
6    J. Wenninger, Institut f. Computersprachen - TU Wien
7
8    This file is part of CACAO.
9
10    This program is free software; you can redistribute it and/or
11    modify it under the terms of the GNU General Public License as
12    published by the Free Software Foundation; either version 2, or (at
13    your option) any later version.
14
15    This program is distributed in the hope that it will be useful, but
16    WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23    02110-1301, USA.
24
25    Contact: cacao@cacaojvm.org
26
27    Authors: Reinhard Grafl
28
29    Changes: Christian Thalinger
30                         Edwin Steiner
31
32    $Id: memory.c 4908 2006-05-12 16:49:50Z edwin $
33
34 */
35
36
37 #include <assert.h>
38 #include <errno.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/mman.h>
44
45 #if defined(__DARWIN__)
46 /* If we compile with -ansi on darwin, <sys/types.h> is not included. So      */
47 /* let's do it here.                                                          */
48 # include <sys/types.h>
49 #endif
50
51 #include "config.h"
52 #include "vm/types.h"
53
54 #include "arch.h"
55
56 #include "mm/memory.h"
57 #include "native/native.h"
58
59 #if defined(USE_THREADS)
60 # if defined(NATIVE_THREADS)
61 #  include "threads/native/threads.h"
62 # else
63 #  include "threads/green/threads.h"
64 # endif
65 #endif
66
67 #include "toolbox/logging.h"
68 #include "vm/exceptions.h"
69 #include "vm/global.h"
70 #include "vm/options.h"
71 #include "vm/statistics.h"
72 #include "vm/stringlocal.h"
73
74
75 /*******************************************************************************
76
77   This structure is used for dump memory allocation if cacao
78   runs without threads.
79
80 *******************************************************************************/
81
82 #if !defined(USE_THREADS) || (defined(USE_THREADS) && !defined(NATIVE_THREADS))
83 static dumpinfo _no_threads_dumpinfo;
84 #endif
85
86 #if defined(USE_THREADS) && defined(NATIVE_THREADS)
87 #define DUMPINFO    &((threadobject *) THREADOBJECT)->dumpinfo
88 #else
89 #define DUMPINFO    &_no_threads_dumpinfo
90 #endif
91
92
93 /* global code memory variables ***********************************************/
94
95 #define DEFAULT_CODEMEM_SIZE    128 * 1024  /* defaulting to 128kB            */
96
97 #if defined(USE_THREADS)
98 static java_objectheader *codememlock = NULL;
99 #endif
100 static int                codememsize = 0;
101 static void              *codememptr  = NULL;
102
103
104 /* memory_init *****************************************************************
105
106    Initialize the memory subsystem.
107
108 *******************************************************************************/
109
110 bool memory_init(void)
111 {
112 #if defined(USE_THREADS)
113         codememlock = NEW(java_objectheader);
114
115 # if defined(NATIVE_THREADS)
116         lock_init_object_lock(codememlock);
117 # endif
118 #endif /* defined(USE_THREADS) */
119
120         /* everything's ok */
121
122         return true;
123 }
124
125
126 /* memory_checked_alloc ********************************************************
127
128    Allocated zeroed-out memory and does an OOM check.
129
130    ERROR HANDLING:
131       XXX If no memory could be allocated, this function justs *exists*.
132
133 *******************************************************************************/
134
135 static void *memory_checked_alloc(s4 size)
136 {
137         /* always allocate memory zeroed out */
138
139         void *p = calloc(size, 1);
140
141         if (!p)
142                 exceptions_throw_outofmemory_exit();
143
144         return p;
145 }
146
147
148 /* memory_cnew *****************************************************************
149
150    Allocates memory from the heap, aligns it to architecutres PAGESIZE
151    and make the memory read-, write-, and executeable.
152
153 *******************************************************************************/
154
155 void *memory_cnew(s4 size)
156 {
157         void *p;
158         int   pagesize;
159
160 #if defined(USE_THREADS)
161         builtin_monitorenter(codememlock);
162 #endif
163
164         size = ALIGN(size, ALIGNSIZE);
165
166         /* check if enough memory is available */
167
168         if (size > codememsize) {
169                 /* set default code size */
170
171                 codememsize = DEFAULT_CODEMEM_SIZE;
172
173                 /* do we need more? */
174
175                 if (size > codememsize)
176                         codememsize = size;
177
178                 /* get the pagesize of this architecture */
179
180                 pagesize = getpagesize();
181
182                 /* allocate normal heap memory */
183
184                 if ((p = memory_checked_alloc(codememsize + pagesize - 1)) == NULL)
185                         return NULL;
186
187 #if defined(ENABLE_STATISTICS)
188                 if (opt_stat) {
189                         codememusage += codememsize + pagesize - 1;
190
191                         if (codememusage > maxcodememusage)
192                                 maxcodememusage = codememusage;
193                 }
194 #endif
195
196                 /* align the memory allocated to a multiple of PAGESIZE,
197                    mprotect requires this */
198
199                 p = (void *) (((ptrint) p + pagesize - 1) & ~(pagesize - 1));
200
201                 /* make the memory read-, write-, and executeable */
202
203                 if (mprotect(p, codememsize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1)
204                         throw_cacao_exception_exit(string_java_lang_InternalError,
205                                                                            strerror(errno));
206
207                 /* set global code memory pointer */
208
209                 codememptr = p;
210         }
211
212         /* get a memory chunk of the allocated memory */
213
214         p = codememptr;
215         codememptr = (void *) ((ptrint) codememptr + size);
216         codememsize -= size;
217
218 #if defined(USE_THREADS)
219         builtin_monitorexit(codememlock);
220 #endif
221
222         return p;
223 }
224
225
226 void *mem_alloc(s4 size)
227 {
228         if (size == 0)
229                 return NULL;
230
231 #if defined(ENABLE_STATISTICS)
232         if (opt_stat) {
233                 memoryusage += size;
234
235                 if (memoryusage > maxmemusage)
236                         maxmemusage = memoryusage;
237         }
238 #endif
239
240         return memory_checked_alloc(size);
241 }
242
243
244 void *mem_realloc(void *src, s4 len1, s4 len2)
245 {
246         void *dst;
247
248         if (!src) {
249                 if (len1 != 0) {
250                         log_text("reallocating memoryblock with address NULL, length != 0");
251                         assert(0);
252                 }
253         }
254
255 #if defined(ENABLE_STATISTICS)
256         if (opt_stat)
257                 memoryusage = (memoryusage - len1) + len2;
258 #endif
259
260         dst = realloc(src, len2);
261
262         if (!dst)
263                 exceptions_throw_outofmemory_exit();
264
265         return dst;
266 }
267
268
269 void mem_free(void *m, s4 size)
270 {
271         if (!m) {
272                 if (size == 0)
273                         return;
274
275                 log_text("returned memoryblock with address NULL, length != 0");
276                 assert(0);
277         }
278
279 #if defined(ENABLE_STATISTICS)
280         if (opt_stat)
281                 memoryusage -= size;
282 #endif
283
284         free(m);
285 }
286
287
288 /* dump_alloc ******************************************************************
289
290    Allocate memory in the dump area.
291
292    IN:
293       size.........size of block to allocate, in bytes
294                                    may be zero, in which case NULL is returned
295
296    RETURN VALUE:
297       pointer to allocated memory, or
298           NULL iff `size` was zero
299
300    ERROR HANDLING:
301       XXX This function uses `memory_checked_alloc`, which *exits* if no 
302           memory could be allocated.
303
304    THREADS:
305       dump_alloc is thread safe. Each thread has its own dump memory area.
306
307    dump_alloc is a fast allocator suitable for scratch memory that can be
308    collectively freed when the current activity (eg. compiling) is done.
309
310    You cannot selectively free dump memory. Before you start allocating it, 
311    you remember the current size returned by `dump_size`. Later, when you no 
312    longer need the memory, call `dump_release` with the remembered size and
313    all dump memory allocated since the call to `dump_size` will be freed.
314
315 *******************************************************************************/
316
317 void *dump_alloc(s4 size)
318 {
319 #if defined(DISABLE_DUMP)
320
321         /* use malloc memory for dump memory (for debugging only!) */
322
323         return mem_alloc(size);
324
325 #else /* !defined(DISABLE_DUMP) */
326
327         void     *m;
328         dumpinfo *di;
329
330         /* If no threads are used, the dumpinfo structure is a static structure   */
331         /* defined at the top of this file.                                       */
332
333         di = DUMPINFO;
334
335         if (size == 0)
336                 return NULL;
337
338         size = ALIGN(size, ALIGNSIZE);
339
340         if (di->useddumpsize + size > di->allocateddumpsize) {
341                 dumpblock *newdumpblock;
342                 s4         newdumpblocksize;
343
344                 /* allocate a new dumplist structure */
345
346                 newdumpblock = memory_checked_alloc(sizeof(dumpblock));
347
348                 /* If requested size is greater than the default, make the new dump   */
349                 /* block as big as the size requested. Else use the default size.     */
350
351                 if (size > DUMPBLOCKSIZE) {
352                         newdumpblocksize = size;
353
354                 } else {
355                         newdumpblocksize = DUMPBLOCKSIZE;
356                 }
357
358                 /* allocate dumpblock memory */
359
360                 newdumpblock->dumpmem = memory_checked_alloc(newdumpblocksize);
361
362                 newdumpblock->prev = di->currentdumpblock;
363                 newdumpblock->size = newdumpblocksize;
364                 di->currentdumpblock = newdumpblock;
365
366                 /* Used dump size is previously allocated dump size, because the      */
367                 /* remaining free memory of the previous dump block cannot be used.   */
368
369                 di->useddumpsize = di->allocateddumpsize;
370
371                 /* increase the allocated dump size by the size of the new dump block */
372
373                 di->allocateddumpsize += newdumpblocksize;
374
375 #if defined(ENABLE_STATISTICS)
376                 /* the amount of globally allocated dump memory (thread save) */
377
378                 if (opt_stat)
379                         globalallocateddumpsize += newdumpblocksize;
380 #endif
381         }
382
383         /* current dump block base address + the size of the current dump block - */
384         /* the size of the unused memory = new start address                      */
385
386         m = di->currentdumpblock->dumpmem + di->currentdumpblock->size -
387                 (di->allocateddumpsize - di->useddumpsize);
388
389         /* increase used dump size by the allocated memory size */
390
391         di->useddumpsize += size;
392
393 #if defined(ENABLE_STATISTICS)
394         if (opt_stat)
395                 if (di->useddumpsize > maxdumpsize)
396                         maxdumpsize = di->useddumpsize;
397 #endif
398
399         return m;
400
401 #endif /* defined(DISABLE_DUMP) */
402 }
403
404
405 /* dump_realloc ****************************************************************
406
407    Stupid realloc implementation for dump memory. Avoid, if possible.
408
409 *******************************************************************************/
410
411 void *dump_realloc(void *src, s4 len1, s4 len2)
412 {
413 #if defined(DISABLE_DUMP)
414         /* use malloc memory for dump memory (for debugging only!) */
415
416         return mem_realloc(src, len1, len2);
417 #else
418         void *dst = dump_alloc(len2);
419
420         memcpy(dst, src, len1);
421
422         return dst;
423 #endif
424 }
425
426
427 /* dump_release ****************************************************************
428
429    Release dump memory above the given size.
430
431    IN:
432        size........All dump memory above this mark will be freed. Usually
433                        `size` will be the return value of a `dump_size` call
434                                    made earlier.
435
436         ERROR HANDLING:
437            XXX If the given size is invalid, this function *exits* with an
438                error message.
439                                    
440         See `dump_alloc`.
441
442 *******************************************************************************/
443
444 void dump_release(s4 size)
445 {
446 #if defined(DISABLE_DUMP)
447
448         /* use malloc memory for dump memory (for debugging only!) */
449
450         /* do nothing */
451
452 #else /* !defined(DISABLE_DUMP) */
453
454         dumpinfo *di;
455
456         /* If no threads are used, the dumpinfo structure is a static structure   */
457         /* defined at the top of this file.                                       */
458
459         di = DUMPINFO;
460
461         if (size < 0 || size > di->useddumpsize)
462                 throw_cacao_exception_exit(string_java_lang_InternalError,
463                                                                    "Illegal dump release size %d", size);
464
465         /* reset the used dump size to the size specified */
466
467         di->useddumpsize = size;
468
469         while (di->currentdumpblock && di->allocateddumpsize - di->currentdumpblock->size >= di->useddumpsize) {
470                 dumpblock *tmp = di->currentdumpblock;
471
472 #if 0
473                 /* XXX TWISTI: can someone explain this to me? */
474 #ifdef TRACECALLARGS
475                 /* Keep the first dumpblock if we don't free memory. Otherwise
476                  * a new dumpblock is allocated each time and we run out of
477                  * memory.
478                  */
479                 if (!oldtop->prev) break;
480 #endif
481 #endif
482
483                 di->allocateddumpsize -= tmp->size;
484                 di->currentdumpblock = tmp->prev;
485
486 #if defined(ENABLE_STATISTICS)
487                 /* the amount of globally allocated dump memory (thread save) */
488
489                 if (opt_stat)
490                         globalallocateddumpsize -= tmp->size;
491 #endif
492
493                 /* release the dump memory and the dumpinfo structure */
494
495                 free(tmp->dumpmem);
496                 free(tmp);
497         }
498
499 #endif /* defined(DISABLE_DUMP) */
500 }
501
502
503 /* dump_size *******************************************************************
504
505    Return the current size of the dump memory area. See `dump_alloc`.
506
507 *******************************************************************************/
508
509 s4 dump_size(void)
510 {
511 #if defined(DISABLE_DUMP)
512         /* use malloc memory for dump memory (for debugging only!) */
513
514         return 0;
515
516 #else /* !defined(DISABLE_DUMP) */
517
518         dumpinfo *di;
519
520         /* If no threads are used, the dumpinfo structure is a static structure   */
521         /* defined at the top of this file.                                       */
522
523         di = DUMPINFO;
524
525         if (!di)
526                 return 0;
527
528         return di->useddumpsize;
529
530 #endif /* defined(DISABLE_DUMP) */
531 }
532
533
534 /*
535  * These are local overrides for various environment variables in Emacs.
536  * Please do not remove this and leave it at the end of the file, where
537  * Emacs will automagically detect them.
538  * ---------------------------------------------------------------------
539  * Local variables:
540  * mode: c
541  * indent-tabs-mode: t
542  * c-basic-offset: 4
543  * tab-width: 4
544  * End:
545  * vim:noexpandtab:sw=4:ts=4:
546  */