Implemented OrderedCollection class
[panda.git] / src / st-memory.c
blobed19f30bd78ec32e3cb1031690729aca915db778
1 /*
2 * st-object-memory.c
4 * Copyright (C) 2008 Vincent Geddes
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 #include "st-memory.h"
26 #include "st-types.h"
27 #include "st-utils.h"
28 #include "st-descriptor.h"
29 #include "st-object.h"
30 #include "st-behavior.h"
31 #include "st-array.h"
32 #include "st-context.h"
33 #include "st-method.h"
34 #include "st-association.h"
35 #include "st-array.h"
36 #include "st-system.h"
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <sys/mman.h>
43 #include <string.h>
44 #include <time.h>
46 static inline st_oop remap_oop (st_oop ref);
47 static void garbage_collect ();
51 static void
52 timer_start (struct timespec *spec)
54 clock_gettime (CLOCK_PROCESS_CPUTIME_ID, spec);
57 static void
58 timer_stop (struct timespec *spec)
60 struct timespec tmp;
61 clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &tmp);
62 st_timespec_difference (spec, &tmp, spec);
66 // RESERVE 256 MB worth of virtual address space
67 #define RESERVED_SIZE (1000 * 1024 * 1024)
68 #define INITIAL_COMMIT_SIZE (64 * 1024 * 1024)
70 // 500 KB for mark stack
71 #define MARK_STACK_SIZE (500 * 1024)
72 #define MARK_STACK_SIZE_OOPS (MARK_STACK_SIZE / sizeof (st_oop))
74 #define BLOCK_SIZE 256
75 #define BLOCK_SIZE_OOPS (BLOCK_SIZE / sizeof (st_oop))
77 static void
78 verify (st_oop object)
80 st_assert (st_object_is_mark (ST_HEADER (object)->mark));
83 static inline st_uint
84 round_pagesize (st_uint size)
86 return ((size + st_system_pagesize () - 1) / st_system_pagesize ()) * st_system_pagesize ();
89 static void
90 ensure_metadata (void)
92 /* The bit arrays are implemented using bytes as smallest element of storage.
93 * If there are N oops in the heap, then we need ((N + 7) / 8) bytes
94 * for a bit array with 32 elements. We need to reserve space for
95 * two bit arrays (mark, alloc), as well as the offsets array.
97 st_uint size, bits_size, offsets_size;
98 st_uchar *metadata_start;
100 size = memory->end - memory->start;
101 bits_size = ((size + 7) / 8);
102 offsets_size = (size / BLOCK_SIZE_OOPS) * sizeof (st_oop *);
104 st_free (memory->mark_bits);
105 st_free (memory->alloc_bits);
106 st_free (memory->offsets);
108 memory->mark_bits = st_malloc (bits_size);
109 memory->alloc_bits = st_malloc (bits_size);
110 memory->bits_size = bits_size;
112 memory->offsets = st_malloc (offsets_size);
113 memory->offsets_size = offsets_size;
116 static void
117 grow_heap (void)
119 /* we grow the heap by roughly a quarter (0.4) */
121 st_uint size, grow_size;
122 st_heap *heap;
124 heap = memory->heap;
125 size = heap->p - heap->start;
126 grow_size = size / 4;
128 st_heap_grow (heap, grow_size);
130 memory->start = (st_oop *) heap->start;
131 memory->end = (st_oop *) heap->p;
133 ensure_metadata ();
136 st_memory *
137 st_memory_new (void)
139 st_oop *ptr;
140 st_ulong size_bits;
141 st_heap *heap;
143 heap = st_heap_new (RESERVED_SIZE);
144 if (!heap)
145 abort ();
147 if (!st_heap_grow (heap, INITIAL_COMMIT_SIZE))
148 abort ();
150 memory = st_new0 (st_memory);
152 memory->heap = heap;
153 memory->start = (st_oop *) heap->start;
154 memory->end = (st_oop *) heap->p;
155 memory->p = memory->start;
157 memory->roots = ptr_array_new (15);
159 memory->total_pause_time.tv_sec = 0;
160 memory->total_pause_time.tv_nsec = 0;
161 memory->counter = 0;
163 memory->mark_stack = st_malloc (MARK_STACK_SIZE);
165 memory->mark_bits = NULL;
166 memory->alloc_bits = NULL;
167 memory->offsets = NULL;
169 ensure_metadata ();
171 return memory;
174 void
175 st_memory_destroy (void)
177 st_free (memory);
180 void
181 st_memory_add_root (st_oop object)
183 ptr_array_append (memory->roots, (st_pointer) object);
186 void
187 st_memory_remove_root (st_oop object)
189 ptr_array_remove_fast (memory->roots, (st_pointer) object);
192 st_oop
193 st_memory_allocate (st_uint size)
195 st_oop *chunk;
197 st_assert (size >= 2);
199 if (memory->counter > ST_COLLECTION_THRESHOLD)
200 garbage_collect ();
201 if ((memory->p + size) >= memory->end)
202 grow_heap ();
204 chunk = memory->p;
205 memory->p += size;
207 memory->counter += (size * sizeof (st_oop));
208 memory->bytes_allocated += (size * sizeof (st_oop));
210 return ST_OOP (chunk);
213 static inline st_oop
214 tag (st_oop *ptr)
216 return ST_OOP (ptr);
219 static inline st_oop *
220 detag (st_oop oop)
222 return (st_oop *) ST_POINTER (oop);
225 static inline bool
226 get_bit (st_uchar *bits, st_uint index)
228 return (bits[index >> 3] >> (index & 0x7)) & 1;
231 static inline void
232 set_bit (st_uchar *bits, st_uint index)
234 bits[index >> 3] |= 1 << (index & 0x7);
237 static inline st_uint
238 bit_index (st_memory *memory, st_oop object)
240 return detag (object) - memory->start;
243 static inline bool
244 ismarked (st_memory *memory, st_oop object)
246 return get_bit (memory->mark_bits, bit_index (memory, object));
249 static inline void
250 set_marked (st_memory *memory, st_oop object)
252 set_bit (memory->mark_bits, bit_index (memory, object));
255 static inline bool
256 get_alloc_bit (st_memory *memory, st_oop object)
258 return get_bit (memory->alloc_bits, bit_index (memory, object));
261 static inline void
262 set_alloc_bit (st_memory *memory, st_oop object)
264 set_bit (memory->alloc_bits, bit_index (memory, object));
267 static inline st_uint
268 get_block_index (st_memory *memory, st_oop *object)
270 return (object - memory->start) / BLOCK_SIZE_OOPS;
273 static inline st_uint
274 compute_ordinal_number (st_memory *memory, st_oop ref)
276 st_uint i, ordinal = 0;
277 st_ulong b, k;
279 b = bit_index (memory, ref) & ~(BLOCK_SIZE_OOPS - 1);
280 k = bit_index (memory, ref);
282 for (i = 0; i < BLOCK_SIZE_OOPS; i++) {
283 if (get_bit (memory->mark_bits, b + i)) {
284 ordinal++;
285 if ((b + i) == k)
286 return ordinal;
290 abort ();
291 return 0;
294 static inline st_oop
295 remap_oop (st_oop ref)
297 st_uint b, i = 0, j = 0;
298 st_uint ordinal;
299 st_oop *offset;
301 if (!st_object_is_heap (ref) || ref == st_nil)
302 return ref;
304 ordinal = compute_ordinal_number (memory, ref);
305 offset = memory->offsets[get_block_index (memory, detag (ref))];
306 b = bit_index (memory, tag (offset));
308 while (true) {
309 if (get_bit (memory->alloc_bits, b + i)) {
310 if (++j == ordinal) {
311 return tag (offset + i);
314 i++;
317 abort ();
318 return 0;
321 static void
322 remap (void)
324 st_oop *oops, *p;
325 st_uint size;
327 p = memory->start;
328 while (p < memory->p) {
330 /* remap class field */
331 p[2] = remap_oop (p[2]);
332 /* remap ivars */
333 st_object_contents (tag (p), &oops, &size);
334 for (st_uint i = 0; i < size; i++) {
335 oops[i] = remap_oop (oops[i]);
337 p += st_object_size (tag (p));
341 static void
342 compact (void)
344 st_oop *p, *from, *to;
345 st_uint size;
346 st_uint block = 0;
348 p = memory->start;
350 while (ismarked (memory, tag (p)) && p < memory->p) {
351 set_alloc_bit (memory, tag (p));
352 if (block < (get_block_index (memory, p) + 1)) {
353 block = get_block_index (memory, p);
354 memory->offsets[block] = p;
355 block += 1;
357 p += st_object_size (tag (p));
359 to = p;
361 while (!ismarked (memory, tag (p)) && p < memory->p) {
362 st_assert (st_object_is_mark (*p));
363 p += st_object_size (tag (p));
365 from = p;
367 if (from == to && p >= memory->p)
368 goto out;
370 while (from < memory->p) {
372 if (ismarked (memory, tag (from))) {
374 set_alloc_bit (memory, tag (to));
375 size = st_object_size (tag (from));
376 st_oops_move (to, from, size);
378 if (block < (get_block_index (memory, from) + 1)) {
379 block = get_block_index (memory, from);
380 memory->offsets[block] = to;
381 block += 1;
384 to += size;
385 from += size;
386 } else {
387 from += st_object_size (tag (from));
391 out:
393 memory->bytes_collected = (memory->p - to) * sizeof (st_oop);
394 memory->bytes_allocated -= memory->bytes_collected;
395 memory->p = to;
398 static void
399 mark (void)
401 st_oop object;
402 st_oop *oops, *stack;
403 st_uint size, sp;
405 sp = 0;
406 stack = memory->mark_stack;
408 for (st_uint i = 0; i < memory->roots->length; i++)
409 stack[sp++] = (st_oop) ptr_array_get_index (memory->roots, i);
410 stack[sp++] = proc->context;
412 while (sp > 0) {
414 object = stack[--sp];
415 if (!st_object_is_heap (object) || ismarked (memory, object))
416 continue;
418 set_marked (memory, object);
419 st_object_contents (object, &oops, &size);
420 if (ST_UNLIKELY ((sp + size + 1) >= MARK_STACK_SIZE_OOPS))
421 goto out;
423 stack[sp++] = ST_HEADER (object)->class;
424 for (st_uint i = 0; i < size; i++) {
425 if (oops[i] != st_nil) {
426 stack[sp++] = oops[i];
431 return;
432 out:
433 /* !!! FIX */
434 fprintf (stderr, "panda: error: marking stack overflowed\n");
435 abort ();
438 static void
439 remap_processor (st_processor *pr)
441 st_oop context, home;
443 context = remap_oop (pr->context);
444 if (ST_HEADER (context)->class == st_block_context_class) {
445 home = ST_BLOCK_CONTEXT (context)->home;
446 pr->method = ST_METHOD_CONTEXT (home)->method;
447 pr->receiver = ST_METHOD_CONTEXT (home)->receiver;
448 pr->literals = st_array_elements (ST_METHOD (pr->method)->literals);
449 pr->temps = ST_METHOD_CONTEXT_TEMPORARY_FRAME (home);
450 pr->stack = ST_BLOCK_CONTEXT (context)->stack;
451 } else {
452 pr->method = ST_METHOD_CONTEXT (context)->method;
453 pr->receiver = ST_METHOD_CONTEXT (context)->receiver;
454 pr->literals = st_array_elements (ST_METHOD (pr->method)->literals);
455 pr->temps = ST_METHOD_CONTEXT_TEMPORARY_FRAME (context);
456 pr->stack = ST_METHOD_CONTEXT_STACK (context);
459 pr->context = context;
460 pr->bytecode = st_method_bytecode_bytes (pr->method);
461 pr->message_receiver = remap_oop (pr->message_receiver);
462 pr->message_selector = remap_oop (pr->message_selector);
463 pr->new_method = remap_oop (pr->new_method);
466 static void
467 remap_globals (void)
469 st_uint i;
471 for (i = 0; i < ST_N_ELEMENTS (globals); i++)
472 globals[i] = remap_oop (globals[i]);
474 for (i = 0; i < ST_N_ELEMENTS (st_specials); i++)
475 st_specials[i] = remap_oop (st_specials[i]);
478 for (i = 0; i < memory->roots->length; i++) {
479 ptr_array_set_index (memory->roots, i,
480 (st_pointer) remap_oop ((st_oop) ptr_array_get_index (memory->roots, i)));
484 static void
485 clear_metadata (void)
487 memset (memory->mark_bits, 0, memory->bits_size);
488 memset (memory->alloc_bits, 0, memory->bits_size);
489 memset (memory->offsets, 0, memory->offsets_size);
492 static void
493 garbage_collect (void)
495 double times[3];
496 struct timespec tm;
498 clear_metadata ();
500 /* marking */
501 timer_start (&tm);
502 mark ();
503 timer_stop (&tm);
505 times[0] = st_timespec_to_double_seconds (&tm);
506 st_timespec_add (&memory->total_pause_time, &tm, &memory->total_pause_time);
508 /* compaction */
509 timer_start (&tm);
510 compact ();
511 timer_stop (&tm);
513 times[1] = st_timespec_to_double_seconds (&tm);
514 st_timespec_add (&memory->total_pause_time, &tm, &memory->total_pause_time);
516 /* remapping */
517 timer_start (&tm);
518 remap ();
519 remap_globals ();
520 remap_processor (proc);
521 timer_stop (&tm);
523 times[2] = st_timespec_to_double_seconds (&tm);
524 st_timespec_add (&memory->total_pause_time, &tm, &memory->total_pause_time);
526 st_processor_clear_caches (proc);
527 memory->counter = 0;
529 if (st_verbose_mode ()) {
530 fprintf (stderr, "** gc: collected: %uK, heapSize: %uK, marking: %.6fs, compaction: %.6fs, remapping: %.6fs\n",
531 memory->bytes_collected / 1024,
532 (memory->bytes_collected + memory->bytes_allocated) / 1024,
533 times[0], times[1], times[2]);
537 st_heap *
538 st_heap_new (st_uint reserved_size)
540 /* Create a new heap with a reserved address space.
541 * Returns NULL if address space could not be reserved
543 st_pointer result;
544 st_heap *heap;
545 st_uint size;
547 st_assert (reserved_size > 0);
548 size = round_pagesize (reserved_size);
550 result = st_system_reserve_memory (NULL, size);
551 if (result == NULL)
552 return NULL;
554 heap = st_new0 (st_heap);
556 heap->start = result;
557 heap->end = result + size;
558 heap->p = result;
560 return heap;
563 bool
564 st_heap_grow (st_heap *heap, st_uint grow_size)
566 /* Grows the heap by the specified amount (in bytes).
567 * The primitive will not succeed if the heap runs out
568 * of reserved address space.
570 st_pointer result;
571 st_uint size;
573 st_assert (grow_size > 0);
574 size = round_pagesize (grow_size);
576 if ((heap->p + size) >= heap->end)
577 return false;
579 result = st_system_commit_memory (heap->p, size);
580 if (result == NULL)
581 return false;
583 heap->p += size;
585 return true;
588 bool
589 st_heap_shrink (st_heap *heap, st_uint shrink_size)
591 /* Shrinks the heap by the specified amount (in bytes).
593 st_pointer result;
594 st_uint size;
596 st_assert (shrink_size > 0);
597 size = round_pagesize (shrink_size);
599 if ((heap->p - size) < heap->start)
600 return false;
602 result = st_system_decommit_memory (heap->p - size, size);
603 if (result == NULL)
604 return false;
606 heap->p -= size;
608 return true;
611 void
612 st_heap_destroy (st_heap *heap)
614 st_system_release_memory (heap->start, heap->end - heap->start);
615 st_free (heap);