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