5c674de1984ecd28015a430f7469e91f3445ef35
[bpt/guile.git] / libguile / gc-segment.c
1 /* Copyright (C) 1995,1996,1997,1998,1999,2000,2001, 2002, 2006 Free Software Foundation, Inc.
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #include <assert.h>
19 #include <stdio.h>
20 #include <string.h>
21
22 #include "libguile/_scm.h"
23 #include "libguile/pairs.h"
24 #include "libguile/gc.h"
25 #include "libguile/private-gc.h"
26
27 size_t scm_max_segment_size;
28
29 scm_t_heap_segment *
30 scm_i_make_empty_heap_segment (scm_t_cell_type_statistics *fl)
31 {
32 scm_t_heap_segment * shs = malloc (sizeof (scm_t_heap_segment));
33
34 if (!shs)
35 {
36 fprintf (stderr, "scm_i_get_new_heap_segment: out of memory.\n");
37 abort ();
38 }
39
40 shs->bounds[0] = NULL;
41 shs->bounds[1] = NULL;
42 shs->malloced = NULL;
43 shs->span = fl->span;
44 shs->freelist = fl;
45 shs->next_free_card = NULL;
46
47 return shs;
48 }
49
50
51 void
52 scm_i_heap_segment_statistics (scm_t_heap_segment *seg, SCM tab)
53 {
54 scm_t_cell *p = seg->bounds[0];
55 while (p < seg->bounds[1])
56 {
57 scm_i_card_statistics (p, tab, seg);
58 p += SCM_GC_CARD_N_CELLS;
59 }
60 }
61
62 /*
63 Fill SEGMENT with memory both for data and mark bits.
64
65 RETURN: 1 on success, 0 failure
66 */
67 int
68 scm_i_initialize_heap_segment_data (scm_t_heap_segment * segment, size_t requested)
69 {
70 /*
71 round upwards
72 */
73 int card_data_cell_count = (SCM_GC_CARD_N_CELLS - SCM_GC_CARD_N_HEADER_CELLS);
74 int card_count =1 + (requested / sizeof (scm_t_cell)) / card_data_cell_count;
75
76 /*
77 one card extra due to alignment
78 */
79 size_t mem_needed = (1+card_count) * SCM_GC_SIZEOF_CARD
80 + SCM_GC_CARD_BVEC_SIZE_IN_LONGS * card_count * SCM_SIZEOF_LONG
81 ;
82 scm_t_c_bvec_long * bvec_ptr = 0;
83 scm_t_cell * memory = 0;
84
85 /*
86 We use calloc to alloc the heap. On GNU libc this is
87 equivalent to mmapping /dev/zero
88 */
89 SCM_SYSCALL (memory = (scm_t_cell * ) calloc (1, mem_needed));
90
91 if (memory == NULL)
92 return 0;
93
94 segment->malloced = memory;
95 segment->bounds[0] = SCM_GC_CARD_UP (memory);
96 segment->bounds[1] = segment->bounds[0] + card_count * SCM_GC_CARD_N_CELLS;
97
98 segment->freelist->heap_size += scm_i_segment_cell_count (segment);
99
100 bvec_ptr = (scm_t_c_bvec_long*) segment->bounds[1];
101
102 /*
103 Don't init the mem or the bitvector. This is handled by lazy
104 sweeping.
105 */
106
107 segment->next_free_card = segment->bounds[0];
108 segment->first_time = 1;
109 return 1;
110 }
111
112 int
113 scm_i_segment_card_count (scm_t_heap_segment * seg)
114 {
115 return (seg->bounds[1] - seg->bounds[0]) / SCM_GC_CARD_N_CELLS;
116 }
117
118 /*
119 Return the number of available single-cell data cells.
120 */
121 int
122 scm_i_segment_cell_count (scm_t_heap_segment * seg)
123 {
124 return scm_i_segment_card_count (seg) * (SCM_GC_CARD_N_CELLS - SCM_GC_CARD_N_HEADER_CELLS)
125 + ((seg->span == 2) ? -1 : 0);
126 }
127
128 void
129 scm_i_clear_segment_mark_space (scm_t_heap_segment *seg)
130 {
131 scm_t_cell * markspace = seg->bounds[1];
132
133 memset (markspace, 0x00,
134 scm_i_segment_card_count (seg) * SCM_GC_CARD_BVEC_SIZE_IN_LONGS * SCM_SIZEOF_LONG);
135 }
136
137 /* Sweep cards from SEG until we've gathered THRESHOLD cells. On return,
138 SWEEP_STATS contains the number of cells that have been visited and
139 collected. A freelist is returned, potentially empty. */
140 SCM
141 scm_i_sweep_some_cards (scm_t_heap_segment *seg,
142 scm_t_sweep_statistics *sweep_stats)
143 {
144 SCM cells = SCM_EOL;
145 int threshold = 512;
146 int collected = 0;
147 int (*sweeper) (scm_t_cell *, SCM *, scm_t_heap_segment* )
148 = (seg->first_time) ? &scm_i_init_card_freelist : &scm_i_sweep_card;
149
150 scm_t_cell * next_free = seg->next_free_card;
151 int cards_swept = 0;
152
153 while (collected < threshold && next_free < seg->bounds[1])
154 {
155 collected += (*sweeper) (next_free, &cells, seg);
156 next_free += SCM_GC_CARD_N_CELLS;
157 cards_swept ++;
158 }
159
160 sweep_stats->swept = cards_swept * seg->span
161 * (SCM_GC_CARD_N_CELLS - SCM_GC_CARD_N_HEADER_CELLS);
162
163 if (!seg->first_time)
164 {
165 /* scm_cells_allocated -= collected * seg->span; */
166 sweep_stats->collected = collected * seg->span;
167 }
168 else
169 sweep_stats->collected = 0;
170
171 seg->freelist->collected += collected * seg->span;
172
173 if(next_free == seg->bounds[1])
174 {
175 seg->first_time = 0;
176 }
177
178 seg->next_free_card = next_free;
179 return cells;
180 }
181
182
183 /*
184 Force a sweep of this entire segment. This doesn't modify sweep
185 statistics, it just frees the memory pointed to by to-be-swept
186 cells.
187
188 Implementation is slightly ugh.
189
190 FIXME: if you do scm_i_sweep_segment(), and then allocate from this
191 segment again, the statistics are off.
192 */
193 void
194 scm_i_sweep_segment (scm_t_heap_segment *seg,
195 scm_t_sweep_statistics *sweep_stats)
196 {
197 scm_t_sweep_statistics sweep;
198 scm_t_cell * p = seg->next_free_card;
199
200 scm_i_sweep_statistics_init (sweep_stats);
201
202 scm_i_sweep_statistics_init (&sweep);
203 while (scm_i_sweep_some_cards (seg, &sweep) != SCM_EOL)
204 {
205 scm_i_sweep_statistics_sum (sweep_stats, sweep);
206 scm_i_sweep_statistics_init (&sweep);
207 }
208
209 seg->next_free_card =p;
210 }
211
212 void
213 scm_i_sweep_all_segments (char const *reason,
214 scm_t_sweep_statistics *sweep_stats)
215 {
216 unsigned i= 0;
217
218 scm_i_sweep_statistics_init (sweep_stats);
219 for (i = 0; i < scm_i_heap_segment_table_size; i++)
220 {
221 scm_t_sweep_statistics sweep;
222
223 scm_i_sweep_segment (scm_i_heap_segment_table[i], &sweep);
224 scm_i_sweep_statistics_sum (sweep_stats, sweep);
225 }
226 }
227
228
229 /*
230 Heap segment table.
231
232 The table is sorted by the address of the data itself. This makes
233 for easy lookups. This is not portable: according to ANSI C,
234 pointers can only be compared within the same object (i.e. the same
235 block of malloced memory.). For machines with weird architectures,
236 this should be revised.
237
238 (Apparently, for this reason 1.6 and earlier had macros for pointer
239 comparison. )
240
241 perhaps it is worthwhile to remove the 2nd level of indirection in
242 the table, but this certainly makes for cleaner code.
243 */
244 scm_t_heap_segment ** scm_i_heap_segment_table;
245 size_t scm_i_heap_segment_table_size;
246 scm_t_cell *lowest_cell;
247 scm_t_cell *highest_cell;
248
249
250 void
251 scm_i_clear_mark_space (void)
252 {
253 int i = 0;
254 for (; i < scm_i_heap_segment_table_size; i++)
255 {
256 scm_i_clear_segment_mark_space (scm_i_heap_segment_table[i]);
257 }
258 }
259
260
261 /*
262 RETURN: index of inserted segment.
263 */
264 int
265 scm_i_insert_segment (scm_t_heap_segment * seg)
266 {
267 size_t size = (scm_i_heap_segment_table_size + 1) * sizeof (scm_t_heap_segment *);
268 SCM_SYSCALL(scm_i_heap_segment_table = ((scm_t_heap_segment **)
269 realloc ((char *)scm_i_heap_segment_table, size)));
270
271 /*
272 We can't alloc 4 more bytes. This is hopeless.
273 */
274 if (!scm_i_heap_segment_table)
275 {
276 fprintf (stderr, "scm_i_get_new_heap_segment: Could not grow heap segment table.\n");
277 abort ();
278 }
279
280 if (!lowest_cell)
281 {
282 lowest_cell = seg->bounds[0];
283 highest_cell = seg->bounds[1];
284 }
285 else
286 {
287 lowest_cell = SCM_MIN (lowest_cell, seg->bounds[0]);
288 highest_cell = SCM_MAX (highest_cell, seg->bounds[1]);
289 }
290
291
292 {
293 int i = 0;
294 int j = 0;
295
296 while (i < scm_i_heap_segment_table_size
297 && scm_i_heap_segment_table[i]->bounds[0] <= seg->bounds[0])
298 i++;
299
300 /*
301 We insert a new entry; if that happens to be before the
302 "current" segment of a freelist, we must move the freelist index
303 as well.
304 */
305 if (scm_i_master_freelist.heap_segment_idx >= i)
306 scm_i_master_freelist.heap_segment_idx ++;
307 if (scm_i_master_freelist2.heap_segment_idx >= i)
308 scm_i_master_freelist2.heap_segment_idx ++;
309
310 for (j = scm_i_heap_segment_table_size; j > i; --j)
311 scm_i_heap_segment_table[j] = scm_i_heap_segment_table[j - 1];
312
313 scm_i_heap_segment_table [i] = seg;
314 scm_i_heap_segment_table_size ++;
315
316 return i;
317 }
318 }
319
320 SCM
321 scm_i_sweep_some_segments (scm_t_cell_type_statistics *fl,
322 scm_t_sweep_statistics *sweep_stats)
323 {
324 int i = fl->heap_segment_idx;
325 SCM collected = SCM_EOL;
326
327 scm_i_sweep_statistics_init (sweep_stats);
328 if (i == -1)
329 i++;
330
331 for (;
332 i < scm_i_heap_segment_table_size; i++)
333 {
334 scm_t_sweep_statistics sweep;
335
336 if (scm_i_heap_segment_table[i]->freelist != fl)
337 continue;
338
339 scm_i_sweep_statistics_init (&sweep);
340 collected = scm_i_sweep_some_cards (scm_i_heap_segment_table[i],
341 &sweep);
342
343 scm_i_sweep_statistics_sum (sweep_stats, sweep);
344
345 if (collected != SCM_EOL) /* Don't increment i */
346 break;
347 }
348
349 fl->heap_segment_idx = i;
350
351 return collected;
352 }
353
354
355 void
356 scm_i_reset_segments (void)
357 {
358 int i = 0;
359 for (; i < scm_i_heap_segment_table_size; i++)
360 {
361 scm_t_heap_segment * seg = scm_i_heap_segment_table[i];
362 seg->next_free_card = seg->bounds[0];
363 }
364 }
365
366 /*
367 Return a hashtab with counts of live objects, with tags as keys.
368 */
369
370
371 SCM
372 scm_i_all_segments_statistics (SCM tab)
373 {
374 int i = 0;
375 for (; i < scm_i_heap_segment_table_size; i++)
376 {
377 scm_t_heap_segment * seg = scm_i_heap_segment_table[i];
378 scm_i_heap_segment_statistics (seg, tab);
379 }
380
381 return tab;
382 }
383
384 /*
385 Determine whether the given value does actually represent a cell in
386 some heap segment. If this is the case, the number of the heap
387 segment is returned. Otherwise, -1 is returned. Binary search is
388 used to determine the heap segment that contains the cell.
389
390
391 I think this function is too long to be inlined. --hwn
392 */
393 long int
394 scm_i_find_heap_segment_containing_object (SCM obj)
395 {
396 if (!CELL_P (obj))
397 return -1;
398
399 if ((scm_t_cell* ) obj < lowest_cell || (scm_t_cell*) obj >= highest_cell)
400 return -1;
401
402
403 {
404 scm_t_cell * ptr = SCM2PTR (obj);
405 unsigned long int i = 0;
406 unsigned long int j = scm_i_heap_segment_table_size - 1;
407
408 if (ptr < scm_i_heap_segment_table[i]->bounds[0])
409 return -1;
410 else if (scm_i_heap_segment_table[j]->bounds[1] <= ptr)
411 return -1;
412 else
413 {
414 while (i < j)
415 {
416 if (ptr < scm_i_heap_segment_table[i]->bounds[1])
417 {
418 break;
419 }
420 else if (scm_i_heap_segment_table[j]->bounds[0] <= ptr)
421 {
422 i = j;
423 break;
424 }
425 else
426 {
427 unsigned long int k = (i + j) / 2;
428
429 if (k == i)
430 return -1;
431 else if (ptr < scm_i_heap_segment_table[k]->bounds[1])
432 {
433 j = k;
434 ++i;
435 if (ptr < scm_i_heap_segment_table[i]->bounds[0])
436 return -1;
437 }
438 else if (scm_i_heap_segment_table[k]->bounds[0] <= ptr)
439 {
440 i = k;
441 --j;
442 if (scm_i_heap_segment_table[j]->bounds[1] <= ptr)
443 return -1;
444 }
445 }
446 }
447
448 if (!SCM_DOUBLECELL_ALIGNED_P (obj) && scm_i_heap_segment_table[i]->span == 2)
449 return -1;
450 else if (SCM_GC_IN_CARD_HEADERP (ptr))
451 return -1;
452 else
453 return i;
454 }
455 }
456 }
457
458
459 /* Important entry point: try to grab some memory, and make it into a
460 segment; return the index of the segment. SWEEP_STATS should contain
461 global GC sweep statistics collected since the last full GC. */
462 int
463 scm_i_get_new_heap_segment (scm_t_cell_type_statistics *freelist,
464 scm_t_sweep_statistics sweep_stats,
465 policy_on_error error_policy)
466 {
467 size_t len;
468
469 {
470 /* Assure that the new segment is predicted to be large enough.
471 *
472 * New yield should at least equal GC fraction of new heap size, i.e.
473 *
474 * y + dh > f * (h + dh)
475 *
476 * y : yield
477 * f : min yield fraction
478 * h : heap size
479 * dh : size of new heap segment
480 *
481 * This gives dh > (f * h - y) / (1 - f)
482 */
483 float f = freelist->min_yield_fraction / 100.0;
484 float h = SCM_HEAP_SIZE;
485 float min_cells = (f * h - sweep_stats.collected) / (1.0 - f);
486
487 /* Make heap grow with factor 1.5 */
488 len = freelist->heap_size / 2;
489 #ifdef DEBUGINFO
490 fprintf (stderr, "(%ld < %ld)", (long) len, (long) min_cells);
491 #endif
492
493 if (len < min_cells)
494 len = (unsigned long) min_cells;
495
496 len *= sizeof (scm_t_cell);
497 /* force new sampling */
498 freelist->collected = LONG_MAX;
499 }
500
501 if (len > scm_max_segment_size)
502 len = scm_max_segment_size;
503
504 if (len < SCM_MIN_HEAP_SEG_SIZE)
505 len = SCM_MIN_HEAP_SEG_SIZE;
506
507 {
508 scm_t_heap_segment * seg = scm_i_make_empty_heap_segment (freelist);
509
510 /* Allocate with decaying ambition. */
511 while (len >= SCM_MIN_HEAP_SEG_SIZE)
512 {
513 if (scm_i_initialize_heap_segment_data (seg, len))
514 {
515 return scm_i_insert_segment (seg);
516 }
517
518 len /= 2;
519 }
520 }
521
522 if (error_policy == abort_on_error)
523 {
524 fprintf (stderr, "scm_i_get_new_heap_segment: Could not grow heap.\n");
525 abort ();
526 }
527 return -1;
528 }
529
530 void
531 scm_i_make_initial_segment (int init_heap_size, scm_t_cell_type_statistics *freelist)
532 {
533 scm_t_heap_segment * seg = scm_i_make_empty_heap_segment (freelist);
534
535 if (init_heap_size < 1)
536 {
537 init_heap_size = SCM_DEFAULT_INIT_HEAP_SIZE_1;
538 }
539
540 if (scm_i_initialize_heap_segment_data (seg, init_heap_size))
541 {
542 freelist->heap_segment_idx = scm_i_insert_segment (seg);
543 }
544
545 /*
546 Why the fuck try twice? --hwn
547 */
548 if (!seg->malloced)
549 {
550 scm_i_initialize_heap_segment_data (seg, SCM_HEAP_SEG_SIZE);
551 }
552
553 if (freelist->min_yield_fraction)
554 freelist->min_yield = (freelist->heap_size * freelist->min_yield_fraction
555 / 100);
556 }