Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
my_top = GC_mark_stack_top;
n_on_stack = my_top - my_first_nonempty + 1;
if (0 == n_on_stack) {
GC_acquire_mark_lock();
my_top = GC_mark_stack_top;
n_on_stack = my_top - my_first_nonempty + 1;
if (0 == n_on_stack) {
GC_active_count--;
GC_ASSERT(GC_active_count <= GC_helper_count);
/* Other markers may redeposit objects */
/* on the stack. */
if (0 == GC_active_count) GC_notify_all_marker();
while (GC_active_count > 0
&& GC_first_nonempty > GC_mark_stack_top) {
/* We will be notified if either GC_active_count */
/* reaches zero, or if more objects are pushed on */
/* the global mark stack. */
GC_wait_marker();
}
if (GC_active_count == 0 &&
GC_first_nonempty > GC_mark_stack_top) {
GC_bool need_to_notify = FALSE;
/* The above conditions can't be falsified while we */
/* hold the mark lock, since neither */
/* GC_active_count nor GC_mark_stack_top can */
/* change. GC_first_nonempty can only be */
/* incremented asynchronously. Thus we know that */
/* both conditions actually held simultaneously. */
GC_helper_count--;
if (0 == GC_helper_count) need_to_notify = TRUE;
# ifdef PRINTSTATS
GC_printf1(
"Finished mark helper %lu\n", (unsigned long)id);
# endif
GC_release_mark_lock();
if (need_to_notify) GC_notify_all_marker();
return;
}
/* else there's something on the stack again, or */
GC_active_count++;
GC_ASSERT(GC_active_count > 0);
GC_release_mark_lock();
continue;
} else {
GC_release_mark_lock();
}
}
n_to_get = ENTRIES_TO_GET;
if (n_on_stack < 2 * ENTRIES_TO_GET) n_to_get = 1;
local_top = GC_steal_mark_stack(my_first_nonempty, my_top,
local_mark_stack, n_to_get,
&my_first_nonempty);
GC_ASSERT(my_first_nonempty >= GC_mark_stack &&
my_first_nonempty <= GC_mark_stack_top + 1);
GC_do_local_mark(local_mark_stack, local_top);
}
/* Perform Parallel mark. */
/* We hold the GC lock, not the mark lock. */
/* Currently runs until the mark stack is */
/* empty. */
void GC_do_parallel_mark()
{
mse local_mark_stack[LOCAL_MARK_STACK_SIZE];
mse * local_top;
mse * my_top;
GC_acquire_mark_lock();
GC_ASSERT(I_HOLD_LOCK());
/* This could be a GC_ASSERT, but it seems safer to keep it on */
/* all the time, especially since it's cheap. */
if (GC_help_wanted || GC_active_count != 0 || GC_helper_count != 0)
ABORT("Tried to start parallel mark in bad state");
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
# ifdef PRINTSTATS
GC_printf1("Starting marking for mark phase number %lu\n",
(unsigned long)GC_mark_no);
# endif
GC_first_nonempty = GC_mark_stack;
GC_active_count = 0;
GC_helper_count = 1;
GC_help_wanted = TRUE;
GC_release_mark_lock();
GC_notify_all_marker();
/* Wake up potential helpers. */
GC_mark_local(local_mark_stack, 0);
GC_acquire_mark_lock();
GC_help_wanted = FALSE;
/* Done; clean up. */
while (GC_helper_count > 0) GC_wait_marker();
/* GC_helper_count cannot be incremented while GC_help_wanted == FALSE */
# ifdef PRINTSTATS
GC_printf1(
"Finished marking for mark phase number %lu\n",
(unsigned long)GC_mark_no);
# endif
GC_mark_no++;
GC_release_mark_lock();
GC_notify_all_marker();
}
/* Try to help out the marker, if it's running. */
/* We do not hold the GC lock, but the requestor does. */
void GC_help_marker(word my_mark_no)
{
mse local_mark_stack[LOCAL_MARK_STACK_SIZE];
unsigned my_id;
mse * my_first_nonempty;
if (!GC_parallel) return;
GC_acquire_mark_lock();
while (GC_mark_no < my_mark_no
|| !GC_help_wanted && GC_mark_no == my_mark_no) {
GC_wait_marker();
}
my_id = GC_helper_count;
if (GC_mark_no != my_mark_no || my_id >= GC_markers) {
/* Second test is useful only if original threads can also */
/* act as helpers. Under Linux they can't. */
GC_release_mark_lock();
return;
}
GC_helper_count = my_id + 1;
GC_release_mark_lock();
GC_mark_local(local_mark_stack, my_id);
/* GC_mark_local decrements GC_helper_count. */
}
#endif /* PARALLEL_MARK */
/* Allocate or reallocate space for mark stack of size s words */
/* May silently fail. */
static void alloc_mark_stack(n)
word n;
{
mse * new_stack = (mse *)GC_scratch_alloc(n * sizeof(struct GC_ms_entry));
GC_mark_stack_too_small = FALSE;
if (GC_mark_stack_size != 0) {
if (new_stack != 0) {
word displ = (word)GC_mark_stack & (GC_page_size - 1);
signed_word size = GC_mark_stack_size * sizeof(struct GC_ms_entry);
/* Recycle old space */
if (0 != displ) displ = GC_page_size - displ;
size = (size - displ) & ~(GC_page_size - 1);
if (size > 0) {
GC_add_to_heap((struct hblk *)
((word)GC_mark_stack + displ), (word)size);
}
GC_mark_stack_limit = new_stack + n;
# ifdef CONDPRINT
if (GC_print_stats) {
GC_printf1("Grew mark stack to %lu frames\n",
(unsigned long) GC_mark_stack_size);
# ifdef CONDPRINT
if (GC_print_stats) {
GC_printf1("Failed to grow mark stack to %lu frames\n",
(unsigned long) n);
# endif
}
} else {
if (new_stack == 0) {
GC_err_printf0("No space for mark stack\n");
EXIT();
}
GC_mark_stack = new_stack;
GC_mark_stack_size = n;
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
}
GC_mark_stack_top = GC_mark_stack-1;
}
void GC_mark_init()
{
alloc_mark_stack(INITIAL_MARK_STACK_SIZE);
}
/*
* Push all locations between b and t onto the mark stack.
* b is the first location to be checked. t is one past the last
* location to be checked.
* Should only be used if there is no possibility of mark stack
* overflow.
*/
void GC_push_all(bottom, top)
ptr_t bottom;
ptr_t top;
{
register word length;
bottom = (ptr_t)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1));
top = (ptr_t)(((word) top) & ~(ALIGNMENT-1));
if (top == 0 || bottom == top) return;
GC_mark_stack_top++;
if (GC_mark_stack_top >= GC_mark_stack_limit) {
ABORT("unexpected mark stack overflow");
}
length = top - bottom;
# if GC_DS_TAGS > ALIGNMENT - 1
length += GC_DS_TAGS;
length &= ~GC_DS_TAGS;
# endif
GC_mark_stack_top -> mse_start = (word *)bottom;
GC_mark_stack_top -> mse_descr = length;
}
/*
* Analogous to the above, but push only those pages h with dirty_fn(h) != 0.
* Used both to selectively push dirty pages, or to push a block
* in piecemeal fashion, to allow for more marking concurrency.
* Will not overflow mark stack if push_fn pushes a small fixed number
* of entries. (This is invoked only if push_fn pushes a single entry,
* or if it marks each object before pushing it, thus ensuring progress
* in the event of a stack overflow.)
*/
void GC_push_selected(bottom, top, dirty_fn, push_fn)
int (*dirty_fn) GC_PROTO((struct hblk * h));
void (*push_fn) GC_PROTO((ptr_t bottom, ptr_t top));
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
{
register struct hblk * h;
bottom = (ptr_t)(((long) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1));
top = (ptr_t)(((long) top) & ~(ALIGNMENT-1));
if (top == 0 || bottom == top) return;
h = HBLKPTR(bottom + HBLKSIZE);
if (top <= (ptr_t) h) {
if ((*dirty_fn)(h-1)) {
(*push_fn)(bottom, top);
}
return;
}
if ((*dirty_fn)(h-1)) {
(*push_fn)(bottom, (ptr_t)h);
}
while ((ptr_t)(h+1) <= top) {
if ((*dirty_fn)(h)) {
if ((word)(GC_mark_stack_top - GC_mark_stack)
> 3 * GC_mark_stack_size / 4) {
/* Danger of mark stack overflow */
(*push_fn)((ptr_t)h, top);
return;
} else {
(*push_fn)((ptr_t)h, (ptr_t)(h+1));
}
}
h++;
}
if ((ptr_t)h != top) {
if ((*dirty_fn)(h)) {
(*push_fn)((ptr_t)h, top);
}
}
if (GC_mark_stack_top >= GC_mark_stack_limit) {
ABORT("unexpected mark stack overflow");
}
}
# ifndef SMALL_CONFIG
#ifdef PARALLEL_MARK
/* Break up root sections into page size chunks to better spread */
/* out work. */
GC_bool GC_true_func(struct hblk *h) { return TRUE; }
# define GC_PUSH_ALL(b,t) GC_push_selected(b,t,GC_true_func,GC_push_all);
#else
# define GC_PUSH_ALL(b,t) GC_push_all(b,t);
#endif
void GC_push_conditional(bottom, top, all)
ptr_t bottom;
ptr_t top;
int all;
{
if (all) {
if (GC_dirty_maintained) {
# ifdef PROC_VDB
/* Pages that were never dirtied cannot contain pointers */
GC_push_selected(bottom, top, GC_page_was_ever_dirty, GC_push_all);
# else
GC_push_all(bottom, top);
# endif
} else {
GC_push_all(bottom, top);
}
} else {
GC_push_selected(bottom, top, GC_page_was_dirty, GC_push_all);
void __cdecl GC_push_one(p)
# else
void GC_push_one(p)
# endif
word p;
{
GC_PUSH_ONE_STACK(p, MARKED_FROM_REGISTER);
struct GC_ms_entry *GC_mark_and_push(obj, mark_stack_ptr, mark_stack_limit, src)
GC_PTR obj;
struct GC_ms_entry * mark_stack_ptr;
struct GC_ms_entry * mark_stack_limit;
GC_PTR *src;
{
PREFETCH(obj);
PUSH_CONTENTS(obj, mark_stack_ptr /* modified */, mark_stack_limit, src,
was_marked /* internally generated exit label */);
return mark_stack_ptr;
}
# ifdef __STDC__
# define BASE(p) (word)GC_base((void *)(p))
# else
# define BASE(p) (word)GC_base((char *)(p))
# endif
/* Mark and push (i.e. gray) a single object p onto the main */
/* mark stack. Consider p to be valid if it is an interior */
/* pointer. */
/* The object p has passed a preliminary pointer validity */
/* test, but we do not definitely know whether it is valid. */
/* Mark bits are NOT atomically updated. Thus this must be the */
/* only thread setting them. */
# if defined(PRINT_BLACK_LIST) || defined(KEEP_BACK_PTRS)
# define source 0
# endif
register word p;
{
register word r;
register hdr * hhdr;
register int displ;
GET_HDR(p, hhdr);
if (IS_FORWARDING_ADDR_OR_NIL(hhdr)) {
r = BASE(p);
hhdr = HDR(r);
displ = BYTES_TO_WORDS(HBLKDISPL(r));
}
} else {
register map_entry_type map_entry;
displ = HBLKDISPL(p);
map_entry = MAP_ENTRY((hhdr -> hb_map), displ);
if (map_entry >= MAX_OFFSET) {
if (map_entry == OFFSET_TOO_BIG || !GC_all_interior_pointers) {
r = BASE(p);
displ = BYTES_TO_WORDS(HBLKDISPL(r));
if (r == 0) hhdr = 0;
} else {
/* Offset invalid, but map reflects interior pointers */
} else {
displ = BYTES_TO_WORDS(displ);
displ -= map_entry;
r = (word)((word *)(HBLKPTR(p)) + displ);
}
}
/* If hhdr != 0 then r == GC_base(p), only we did it faster. */
/* displ is the word index within the block. */
if (hhdr == 0) {
# ifdef PRINT_BLACK_LIST
GC_add_to_black_list_stack(p, source);
# else
GC_add_to_black_list_stack(p);
# endif
# undef source /* In case we had to define it. */
} else {
if (!mark_bit_from_hdr(hhdr, displ)) {
set_mark_bit_from_hdr(hhdr, displ);
GC_STORE_BACK_PTR(source, (ptr_t)r);
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
}
}
}
# ifdef TRACE_BUF
# define TRACE_ENTRIES 1000
struct trace_entry {
char * kind;
word gc_no;
word words_allocd;
word arg1;
word arg2;
} GC_trace_buf[TRACE_ENTRIES];
int GC_trace_buf_ptr = 0;
void GC_add_trace_entry(char *kind, word arg1, word arg2)
{
GC_trace_buf[GC_trace_buf_ptr].kind = kind;
GC_trace_buf[GC_trace_buf_ptr].gc_no = GC_gc_no;
GC_trace_buf[GC_trace_buf_ptr].words_allocd = GC_words_allocd;
GC_trace_buf[GC_trace_buf_ptr].arg1 = arg1 ^ 0x80000000;
GC_trace_buf[GC_trace_buf_ptr].arg2 = arg2 ^ 0x80000000;
GC_trace_buf_ptr++;
if (GC_trace_buf_ptr >= TRACE_ENTRIES) GC_trace_buf_ptr = 0;
}
void GC_print_trace(word gc_no, GC_bool lock)
{
int i;
struct trace_entry *p;
if (lock) LOCK();
for (i = GC_trace_buf_ptr-1; i != GC_trace_buf_ptr; i--) {
if (i < 0) i = TRACE_ENTRIES-1;
p = GC_trace_buf + i;
if (p -> gc_no < gc_no || p -> kind == 0) return;
printf("Trace:%s (gc:%d,words:%d) 0x%X, 0x%X\n",
p -> kind, p -> gc_no, p -> words_allocd,
(p -> arg1) ^ 0x80000000, (p -> arg2) ^ 0x80000000);
}
printf("Trace incomplete\n");
if (lock) UNLOCK();
}
# endif /* TRACE_BUF */
/*
* A version of GC_push_all that treats all interior pointers as valid
* and scans the entire region immediately, in case the contents
* change.
void GC_push_all_eager(bottom, top)
word * b = (word *)(((word) bottom + ALIGNMENT-1) & ~(ALIGNMENT-1));
word * t = (word *)(((word) top) & ~(ALIGNMENT-1));
register word *p;
register word q;
register word *lim;
register ptr_t greatest_ha = GC_greatest_plausible_heap_addr;
register ptr_t least_ha = GC_least_plausible_heap_addr;
# define GC_greatest_plausible_heap_addr greatest_ha
# define GC_least_plausible_heap_addr least_ha
if (top == 0) return;
/* check all pointers in range and push if they appear */
/* to be valid. */
lim = t - 1 /* longword */;
for (p = b; p <= lim; p = (word *)(((char *)p) + ALIGNMENT)) {
q = *p;
}
# undef GC_greatest_plausible_heap_addr
# undef GC_least_plausible_heap_addr
}
#ifndef THREADS
/*
* A version of GC_push_all that treats all interior pointers as valid
* and scans part of the area immediately, to make sure that saved
* register values are not lost.
* Cold_gc_frame delimits the stack section that must be scanned
* eagerly. A zero value indicates that no eager scanning is needed.
*/
void GC_push_all_stack_partially_eager(bottom, top, cold_gc_frame)
ptr_t bottom;
ptr_t top;
ptr_t cold_gc_frame;
{
# define EAGER_BYTES 1024
/* Push the hot end of the stack eagerly, so that register values */
/* saved inside GC frames are marked before they disappear. */
/* The rest of the marking can be deferred until later. */
if (0 == cold_gc_frame) {
GC_push_all_stack(bottom, top);
return;
}
GC_ASSERT(bottom <= cold_gc_frame && cold_gc_frame <= top);
# ifdef STACK_GROWS_DOWN
GC_push_all(cold_gc_frame - sizeof(ptr_t), top);
# else /* STACK_GROWS_UP */
GC_push_all(bottom, cold_gc_frame + sizeof(ptr_t));
# ifdef TRACE_BUF
GC_add_trace_entry("GC_push_all_stack", bottom, top);
# endif
}
#endif /* !THREADS */
void GC_push_all_stack(bottom, top)
ptr_t bottom;
ptr_t top;
{
#if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES)
/* Push all objects reachable from marked objects in the given block */
/* of size 1 objects. */
void GC_push_marked1(h, hhdr)
struct hblk *h;
register hdr * hhdr;
{
word * mark_word_addr = &(hhdr->hb_marks[0]);
register word *p;
word *plim;
register int i;
register word q;
register word mark_word;
register ptr_t greatest_ha = GC_greatest_plausible_heap_addr;
register ptr_t least_ha = GC_least_plausible_heap_addr;
register mse * mark_stack_top = GC_mark_stack_top;
register mse * mark_stack_limit = GC_mark_stack_limit;
# define GC_mark_stack_top mark_stack_top
# define GC_mark_stack_limit mark_stack_limit
# define GC_greatest_plausible_heap_addr greatest_ha
# define GC_least_plausible_heap_addr least_ha
p = (word *)(h->hb_body);
plim = (word *)(((word)h) + HBLKSIZE);
/* go through all words in block */
while( p < plim ) {
mark_word = *mark_word_addr++;
i = 0;
while(mark_word != 0) {
if (mark_word & 1) {
q = p[i];
}
i++;
mark_word >>= 1;
}
p += WORDSZ;
}
# undef GC_greatest_plausible_heap_addr
# undef GC_least_plausible_heap_addr
# undef GC_mark_stack_top
# undef GC_mark_stack_limit
GC_mark_stack_top = mark_stack_top;
}
#ifndef UNALIGNED
/* Push all objects reachable from marked objects in the given block */
/* of size 2 objects. */
void GC_push_marked2(h, hhdr)
struct hblk *h;
register hdr * hhdr;
{
word * mark_word_addr = &(hhdr->hb_marks[0]);
register word *p;
word *plim;
register int i;
register word q;
register word mark_word;
register ptr_t greatest_ha = GC_greatest_plausible_heap_addr;
register ptr_t least_ha = GC_least_plausible_heap_addr;
register mse * mark_stack_top = GC_mark_stack_top;
register mse * mark_stack_limit = GC_mark_stack_limit;
# define GC_mark_stack_top mark_stack_top
# define GC_mark_stack_limit mark_stack_limit
# define GC_greatest_plausible_heap_addr greatest_ha
# define GC_least_plausible_heap_addr least_ha
p = (word *)(h->hb_body);
plim = (word *)(((word)h) + HBLKSIZE);
/* go through all words in block */
while( p < plim ) {
mark_word = *mark_word_addr++;
i = 0;
while(mark_word != 0) {
if (mark_word & 1) {
q = p[i];
}
i += 2;
mark_word >>= 2;
}
p += WORDSZ;
}
# undef GC_greatest_plausible_heap_addr
# undef GC_least_plausible_heap_addr
# undef GC_mark_stack_top
# undef GC_mark_stack_limit
GC_mark_stack_top = mark_stack_top;
}
/* Push all objects reachable from marked objects in the given block */
/* of size 4 objects. */
/* There is a risk of mark stack overflow here. But we handle that. */
/* And only unmarked objects get pushed, so it's not very likely. */
void GC_push_marked4(h, hhdr)
struct hblk *h;
register hdr * hhdr;
{
word * mark_word_addr = &(hhdr->hb_marks[0]);
register word *p;
word *plim;
register int i;
register word q;
register word mark_word;
register ptr_t greatest_ha = GC_greatest_plausible_heap_addr;
register ptr_t least_ha = GC_least_plausible_heap_addr;
register mse * mark_stack_top = GC_mark_stack_top;
register mse * mark_stack_limit = GC_mark_stack_limit;
# define GC_mark_stack_top mark_stack_top
# define GC_mark_stack_limit mark_stack_limit
# define GC_greatest_plausible_heap_addr greatest_ha
# define GC_least_plausible_heap_addr least_ha
p = (word *)(h->hb_body);
plim = (word *)(((word)h) + HBLKSIZE);
/* go through all words in block */
while( p < plim ) {
mark_word = *mark_word_addr++;
i = 0;
while(mark_word != 0) {
if (mark_word & 1) {
q = p[i];
}
i += 4;
mark_word >>= 4;
}
p += WORDSZ;
}
# undef GC_greatest_plausible_heap_addr
# undef GC_least_plausible_heap_addr
# undef GC_mark_stack_top
# undef GC_mark_stack_limit
GC_mark_stack_top = mark_stack_top;
}
#endif /* UNALIGNED */
#endif /* SMALL_CONFIG */
/* Push all objects reachable from marked objects in the given block */
void GC_push_marked(h, hhdr)
struct hblk *h;
register hdr * hhdr;
{
register int sz = hhdr -> hb_sz;
register int descr = hhdr -> hb_descr;
register word * p;
register int word_no;
register word * lim;
register mse * GC_mark_stack_top_reg;
register mse * mark_stack_limit = GC_mark_stack_limit;
} else {
lim = (word *)(h + 1) - sz;
}
switch(sz) {
# if !defined(SMALL_CONFIG) && !defined(USE_MARK_BYTES)
case 1:
GC_push_marked1(h, hhdr);
break;
# endif
# if !defined(SMALL_CONFIG) && !defined(UNALIGNED) && \
!defined(USE_MARK_BYTES)
case 2:
GC_push_marked2(h, hhdr);
break;
case 4:
GC_push_marked4(h, hhdr);
break;
# endif
default:
GC_mark_stack_top_reg = GC_mark_stack_top;
for (p = (word *)h, word_no = 0; p <= lim; p += sz, word_no += sz) {
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
if (mark_bit_from_hdr(hhdr, word_no)) {
/* Mark from fields inside the object */
PUSH_OBJ((word *)p, hhdr, GC_mark_stack_top_reg, mark_stack_limit);
# ifdef GATHERSTATS
/* Subtract this object from total, since it was */
/* added in twice. */
GC_composite_in_use -= sz;
# endif
}
}
GC_mark_stack_top = GC_mark_stack_top_reg;
}
}
#ifndef SMALL_CONFIG
/* Test whether any page in the given block is dirty */
GC_bool GC_block_was_dirty(h, hhdr)
struct hblk *h;
register hdr * hhdr;
{
register int sz = hhdr -> hb_sz;
return(GC_page_was_dirty(h));
} else {
register ptr_t p = (ptr_t)h;
sz = WORDS_TO_BYTES(sz);
while (p < (ptr_t)h + sz) {
if (GC_page_was_dirty((struct hblk *)p)) return(TRUE);
p += HBLKSIZE;
}
return(FALSE);
}
}
#endif /* SMALL_CONFIG */
/* Similar to GC_push_next_marked, but return address of next block */
struct hblk * GC_push_next_marked(h)
struct hblk *h;
{
register hdr * hhdr;
if (h == 0) return(0);
hhdr = HDR(h);
GC_push_marked(h, hhdr);
return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz));
}
#ifndef SMALL_CONFIG
/* Identical to above, but mark only from dirty pages */
struct hblk * GC_push_next_marked_dirty(h)
struct hblk *h;
{
if (!GC_dirty_maintained) { ABORT("dirty bits not set up"); }
for (;;) {
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
if (h == 0) return(0);
hhdr = HDR(h);
# ifdef STUBBORN_ALLOC
if (hhdr -> hb_obj_kind == STUBBORN) {
if (GC_page_was_changed(h) && GC_block_was_dirty(h, hhdr)) {
break;
}
} else {
if (GC_block_was_dirty(h, hhdr)) break;
}
# else
if (GC_block_was_dirty(h, hhdr)) break;
# endif
h += OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz);
}
GC_push_marked(h, hhdr);
return(h + OBJ_SZ_TO_BLOCKS(hhdr -> hb_sz));
}
#endif
/* Similar to above, but for uncollectable pages. Needed since we */
/* do not clear marks for such pages, even for full collections. */
struct hblk * GC_push_next_marked_uncollectable(h)
struct hblk *h;
{
register hdr * hhdr = HDR(h);
for (;;) {