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