1 /*****************************************************************************/
2 /* memory accounting */
3 /*****************************************************************************/
4 #ifdef NEWGC_BTC_ACCOUNT
5
6 #include "../src/schpriv.h"
7 /* BTC_ prefixed functions are called by newgc.c */
8 /* btc_ prefixed functions are internal to mem_account.c */
9
10 static const int btc_redirect_thread = 511;
11 static const int btc_redirect_custodian = 510;
12 static const int btc_redirect_ephemeron = 509;
13 static const int btc_redirect_cust_box = 508;
14 static const int btc_redirect_bi_chan = 507;
15
16 inline static void account_memory(NewGC *gc, int set, intptr_t amount, int to_master);
17
18 /*****************************************************************************/
19 /* thread list */
20 /*****************************************************************************/
21 inline static int current_owner(NewGC *gc, Scheme_Custodian *c);
22
BTC_register_new_thread(void * t,void * c)23 inline static void BTC_register_new_thread(void *t, void *c)
24 {
25 NewGC *gc = GC_get_GC();
26 GC_Thread_Info *work;
27
28 work = (GC_Thread_Info *)ofm_malloc(sizeof(GC_Thread_Info));
29 if (((Scheme_Object *)t)->type == scheme_thread_type)
30 ((Scheme_Thread *)t)->gc_info = work;
31 else
32 ((Scheme_Place *)t)->gc_info = work;
33 work->owner = current_owner(gc, (Scheme_Custodian *)c);
34 work->thread = t;
35
36 work->next = gc->thread_infos;
37 gc->thread_infos = work;
38 }
39
BTC_register_thread(void * t,void * c)40 inline static void BTC_register_thread(void *t, void *c)
41 /* Might be called in a future thread to change to a custodian that
42 has a set number */
43 {
44 NewGC *gc = GC_get_GC();
45 GC_Thread_Info *work;
46
47 if (((Scheme_Object *)t)->type == scheme_thread_type)
48 work = ((Scheme_Thread *)t)->gc_info;
49 else
50 work = ((Scheme_Place *)t)->gc_info;
51 work->owner = current_owner(gc, (Scheme_Custodian *)c);
52 }
53
mark_threads(NewGC * gc,int owner)54 inline static void mark_threads(NewGC *gc, int owner)
55 {
56 GC_Thread_Info *work;
57 Mark2_Proc thread_mark = gc->mark_table[btc_redirect_thread];
58
59 for(work = gc->thread_infos; work; work = work->next) {
60 if (work->owner == owner) {
61 if (((Scheme_Object *)work->thread)->type == scheme_thread_type) {
62 /* thread */
63 if (((Scheme_Thread *)work->thread)->running) {
64 thread_mark(work->thread, gc);
65 if (work->thread == scheme_current_thread) {
66 GC_mark_variable_stack(GC_variable_stack, 0, get_stack_base(gc), NULL);
67 }
68 }
69 } else {
70 /* place */
71 #ifdef MZ_USE_PLACES
72 /* add in the memory used by the place's GC */
73 intptr_t sz;
74 Scheme_Place_Object *place_obj = ((Scheme_Place *)work->thread)->place_obj;
75 if (place_obj) {
76 mzrt_mutex_lock(place_obj->lock);
77 sz = place_obj->memory_use;
78 mzrt_mutex_unlock(place_obj->lock);
79 account_memory(gc, owner, gcBYTES_TO_WORDS(sz), 0);
80 }
81 #endif
82 }
83 }
84 }
85 }
86
clean_up_thread_list(NewGC * gc)87 inline static void clean_up_thread_list(NewGC *gc)
88 {
89 GC_Thread_Info *work = gc->thread_infos;
90 GC_Thread_Info *prev = NULL;
91
92 while(work) {
93 if (marked(gc, work->thread)) {
94 work->thread = GC_resolve2(work->thread, gc);
95 prev = work;
96 work = work->next;
97 } else {
98 GC_Thread_Info *next = work->next;
99
100 if(prev) prev->next = next;
101 if(!prev) gc->thread_infos = next;
102 ofm_free(work, sizeof(GC_Thread_Info));
103 work = next;
104 }
105 }
106 }
107
thread_get_owner(void * p)108 inline static int thread_get_owner(void *p)
109 {
110 return ((Scheme_Thread *)p)->gc_info->owner;
111 }
112
113 #define OWNER_TABLE_INIT_AMT 10
114
create_blank_owner_set(NewGC * gc)115 inline static int create_blank_owner_set(NewGC *gc)
116 {
117 int i;
118 unsigned int curr_size = gc->owner_table_size;
119 OTEntry **owner_table = gc->owner_table;
120 unsigned int old_size;
121 OTEntry **naya;
122
123 for (i = 1; i < curr_size; i++) {
124 if (!owner_table[i]) {
125 owner_table[i] = ofm_malloc(sizeof(OTEntry));
126 bzero(owner_table[i], sizeof(OTEntry));
127 return i;
128 }
129 }
130
131 old_size = curr_size;
132 if (!curr_size) {
133 curr_size = OWNER_TABLE_INIT_AMT;
134 }
135 else {
136 curr_size *= 2;
137 }
138 gc->owner_table_size = curr_size;
139
140 naya = (OTEntry **)ofm_malloc(curr_size * sizeof(OTEntry*));
141 if (old_size)
142 memcpy(naya, owner_table, old_size*sizeof(OTEntry*));
143 gc->owner_table = owner_table = naya;
144 bzero(((char*)owner_table) + (sizeof(OTEntry*) * old_size),
145 (curr_size - old_size) * sizeof(OTEntry*));
146
147 return create_blank_owner_set(gc);
148 }
149
custodian_to_owner_set(NewGC * gc,Scheme_Custodian * cust)150 inline static int custodian_to_owner_set(NewGC *gc, Scheme_Custodian *cust)
151 {
152 int i;
153
154 GC_ASSERT(SAME_TYPE(SCHEME_TYPE(cust), scheme_custodian_type));
155
156 if (cust->gc_owner_set)
157 return cust->gc_owner_set;
158
159 i = create_blank_owner_set(gc);
160 gc->owner_table[i]->originator = cust;
161 cust->gc_owner_set = i;
162
163 return i;
164 }
165
current_owner(NewGC * gc,Scheme_Custodian * c)166 inline static int current_owner(NewGC *gc, Scheme_Custodian *c)
167 {
168 if (!scheme_current_thread)
169 return 1;
170 else if (!c)
171 return thread_get_owner(scheme_current_thread);
172 else
173 return custodian_to_owner_set(gc, c);
174 }
175
BTC_register_root_custodian(void * _c)176 void BTC_register_root_custodian(void *_c)
177 {
178 NewGC *gc = GC_get_GC();
179 Scheme_Custodian *c = (Scheme_Custodian *)_c;
180
181 if (gc->owner_table) {
182 /* Reset */
183 ofm_free(gc->owner_table, sizeof(OTEntry*) * gc->owner_table_size);
184 gc->owner_table = NULL;
185 gc->owner_table_size = 0;
186 }
187
188 if (create_blank_owner_set(gc) != 1) {
189 GCPRINT(GCOUTF, "Something extremely weird (and bad) has happened.\n");
190 abort();
191 }
192
193 gc->owner_table[1]->originator = c;
194 c->gc_owner_set = 1;
195 }
196
custodian_member_owner_set(NewGC * gc,void * cust,int set)197 inline static int custodian_member_owner_set(NewGC *gc, void *cust, int set)
198 {
199 Scheme_Custodian_Reference *box;
200 Scheme_Custodian *work = (Scheme_Custodian *) gc->owner_table[set]->originator;
201
202 while(work) {
203 if(work == cust) return 1;
204 box = work->parent;
205 work = box ? SCHEME_PTR1_VAL(box) : NULL;
206 }
207 return 0;
208 }
209
account_memory(NewGC * gc,int set,intptr_t amount,int to_master)210 inline static void account_memory(NewGC *gc, int set, intptr_t amount, int to_master)
211 {
212 if (to_master)
213 gc->owner_table[set]->master_memory_use += amount;
214 else
215 gc->owner_table[set]->memory_use += amount;
216 }
217
free_owner_set(NewGC * gc,int set)218 inline static void free_owner_set(NewGC *gc, int set)
219 {
220 OTEntry **owner_table = gc->owner_table;
221 if(owner_table[set]) {
222 ofm_free(owner_table[set], sizeof(OTEntry));
223 }
224 owner_table[set] = NULL;
225 }
226
clean_up_owner_table(NewGC * gc)227 inline static void clean_up_owner_table(NewGC *gc)
228 {
229 OTEntry **owner_table = gc->owner_table;
230 const int table_size = gc->owner_table_size;
231 int i, really_doing_accounting = 0;
232
233 for(i = 1; i < table_size; i++)
234 if(owner_table[i]) {
235 /* repair or delete the originator */
236 if(!marked(gc, owner_table[i]->originator)) {
237 owner_table[i]->originator = NULL;
238 } else {
239 owner_table[i]->originator = GC_resolve2(owner_table[i]->originator, gc);
240 if (((Scheme_Custodian *)owner_table[i]->originator)->really_doing_accounting) {
241 really_doing_accounting = 1;
242 }
243 }
244
245 /* potential delete */
246 if(i != 1)
247 if((owner_table[i]->memory_use == 0) && !owner_table[i]->originator)
248 free_owner_set(gc, i);
249 }
250
251 gc->next_really_doing_accounting |= really_doing_accounting;
252 }
253
custodian_usage(NewGC * gc,void * custodian)254 inline static uintptr_t custodian_usage(NewGC*gc, void *custodian)
255 {
256 OTEntry **owner_table;
257 uintptr_t retval = 0;
258 int i;
259
260 ((Scheme_Custodian *)custodian)->really_doing_accounting = 1;
261
262 if(!gc->really_doing_accounting) {
263 if (!gc->avoid_collection) {
264 CHECK_PARK_UNUSED(gc);
265 gc->park[0] = custodian;
266 gc->next_really_doing_accounting = 1;
267 garbage_collect(gc, 1, 0, 0, NULL);
268 custodian = gc->park[0];
269 gc->park[0] = NULL;
270 }
271 }
272
273 i = custodian_to_owner_set(gc, (Scheme_Custodian *)custodian);
274
275 owner_table = gc->owner_table;
276 if (owner_table[i])
277 retval = (owner_table[i]->memory_use + owner_table[i]->master_memory_use);
278 else
279 retval = 0;
280
281 return gcWORDS_TO_BYTES(retval);
282 }
283
284 #ifdef MZ_USE_PLACES
285
286 static mzrt_mutex *master_btc_lock;
287 static mzrt_sema *master_btc_sema;
288 static int master_btc_lock_count = 0;
289 static int master_btc_lock_waiters = 0;
290
init_master_btc_locks()291 void init_master_btc_locks()
292 {
293 mzrt_mutex_create(&master_btc_lock);
294 mzrt_sema_create(&master_btc_sema, 0);
295 }
296
check_master_btc_mark(NewGC * gc,mpage * page)297 static void check_master_btc_mark(NewGC *gc, mpage *page)
298 {
299 if (!gc->master_page_btc_mark_checked) {
300 int pause = 1;
301 RELEASE_PAGE_LOCK(1, page);
302 while (pause) {
303 mzrt_mutex_lock(master_btc_lock);
304 if (master_btc_lock_count
305 && (gc->new_btc_mark != MASTERGC->new_btc_mark)) {
306 pause = 1;
307 master_btc_lock_waiters++;
308 } else {
309 pause = 0;
310 MASTERGC->new_btc_mark = gc->new_btc_mark;
311 master_btc_lock_count++;
312 }
313 mzrt_mutex_unlock(master_btc_lock);
314
315 if (pause)
316 mzrt_sema_wait(master_btc_sema);
317 }
318 TAKE_PAGE_LOCK(1, page);
319 gc->master_page_btc_mark_checked = 1;
320 }
321 }
322
release_master_btc_mark(NewGC * gc)323 static void release_master_btc_mark(NewGC *gc)
324 {
325 if (gc->master_page_btc_mark_checked) {
326 /* release the lock on the master's new_btc_mark value */
327 mzrt_mutex_lock(master_btc_lock);
328 --master_btc_lock_count;
329 if (!master_btc_lock_count && master_btc_lock_waiters) {
330 --master_btc_lock_waiters;
331 mzrt_sema_post(master_btc_sema);
332 }
333 mzrt_mutex_unlock(master_btc_lock);
334 }
335 }
336
337 #else
338
check_master_btc_mark(NewGC * gc,mpage * page)339 static void check_master_btc_mark(NewGC *gc, mpage *page) { }
release_master_btc_mark(NewGC * gc)340 static void release_master_btc_mark(NewGC *gc) { }
341
342 #endif
343
BTC_memory_account_mark(NewGC * gc,mpage * page,void * ptr,int is_a_master_page)344 inline static void BTC_memory_account_mark(NewGC *gc, mpage *page, void *ptr, int is_a_master_page)
345 {
346 GCDEBUG((DEBUGOUTF, "BTC_memory_account_mark: %p/%p\n", page, ptr));
347
348 /* In the case of is_a_master_page, whether this place is charged is
349 a little random: there's no guarantee that the btc_mark values
350 are in sync, and there are races among places. Approximations are
351 ok for accounting, though, as long as the probably for completely
352 wrong accounting is very low.
353
354 At the same time, we need to synchronize enough so that two
355 places with different new_btc_mark values don't send each other
356 into infinite loops (with the btc_mark value bouncing back and
357 forth) or overcounting. We synchronize enough by having a single
358 new_btc_mark value for master pages, and we stall if the value
359 isn't what this place wants. */
360
361 if (is_a_master_page)
362 check_master_btc_mark(gc, page);
363
364 if(page->size_class) {
365 if(page->size_class > 1) {
366 /* big page */
367 objhead *info = BIG_PAGE_TO_OBJHEAD(page);
368
369 if(info->btc_mark == gc->old_btc_mark) {
370 info->btc_mark = gc->new_btc_mark;
371 account_memory(gc, gc->current_mark_owner, gcBYTES_TO_WORDS(page->size), is_a_master_page);
372 push_ptr(gc, TAG_AS_BIG_PAGE_PTR(ptr), 0);
373 }
374 } else {
375 /* medium page */
376 objhead *info = MED_OBJHEAD(ptr, page->size);
377
378 if(info->btc_mark == gc->old_btc_mark) {
379 info->btc_mark = gc->new_btc_mark;
380 account_memory(gc, gc->current_mark_owner, info->size, is_a_master_page);
381 ptr = OBJHEAD_TO_OBJPTR(info);
382 push_ptr(gc, ptr, 0);
383 }
384 }
385 } else {
386 objhead *info = OBJPTR_TO_OBJHEAD(ptr);
387
388 if(info->btc_mark == gc->old_btc_mark) {
389 info->btc_mark = gc->new_btc_mark;
390 account_memory(gc, gc->current_mark_owner, info->size, 0);
391 if (page->generation != AGE_GEN_HALF)
392 push_ptr(gc, ptr, 0);
393 }
394 }
395 }
396
mark_cust_boxes(NewGC * gc,Scheme_Custodian * cur)397 inline static void mark_cust_boxes(NewGC *gc, Scheme_Custodian *cur)
398 {
399 Scheme_Object *pr, *prev = NULL, *next;
400 GC_Weak_Box *wb;
401 Mark2_Proc cust_box_mark = gc->mark_table[btc_redirect_cust_box];
402
403 /* cust boxes is a list of weak boxes to cust boxes */
404
405 pr = cur->cust_boxes;
406 while (pr) {
407 wb = (GC_Weak_Box *)SCHEME_CAR(pr);
408 next = SCHEME_CDR(pr);
409 if (wb->val) {
410 cust_box_mark(wb->val, gc);
411 prev = pr;
412 } else {
413 if (prev)
414 SCHEME_CDR(prev) = next;
415 else
416 cur->cust_boxes = next;
417 --cur->num_cust_boxes;
418 }
419 pr = next;
420 }
421 cur->checked_cust_boxes = cur->num_cust_boxes;
422 }
423
BTC_thread_mark(void * p,struct NewGC * gc)424 int BTC_thread_mark(void *p, struct NewGC *gc)
425 {
426 if (gc->doing_memory_accounting) {
427 return OBJPTR_TO_OBJHEAD(p)->size;
428 }
429 return gc->mark_table[btc_redirect_thread](p, gc);
430 }
431
BTC_custodian_mark(void * p,struct NewGC * gc)432 int BTC_custodian_mark(void *p, struct NewGC *gc)
433 {
434 if (gc->doing_memory_accounting) {
435 if(custodian_to_owner_set(gc, p) == gc->current_mark_owner)
436 return gc->mark_table[btc_redirect_custodian](p, gc);
437 else
438 return OBJPTR_TO_OBJHEAD(p)->size;
439 }
440 return gc->mark_table[btc_redirect_custodian](p, gc);
441 }
442
BTC_cust_box_mark(void * p,struct NewGC * gc)443 int BTC_cust_box_mark(void *p, struct NewGC *gc)
444 {
445 if (gc->doing_memory_accounting) {
446 return OBJPTR_TO_OBJHEAD(p)->size;
447 }
448 return gc->mark_table[btc_redirect_cust_box](p, gc);
449 }
450
BTC_bi_chan_mark(void * p,struct NewGC * gc)451 int BTC_bi_chan_mark(void *p, struct NewGC *gc)
452 {
453 if (gc->doing_memory_accounting) {
454 Scheme_Place_Bi_Channel *bc = (Scheme_Place_Bi_Channel *)p;
455 /* The `link` field can be NULL if the channel is still being
456 set up: */
457 if (bc->link) {
458 /* Race conditions here on `mem_size', and likely double counting
459 when the same async channels are accessible from paired bi
460 channels --- but those approximations are ok for accounting. */
461 if (bc->link->sendch)
462 account_memory(gc, gc->current_mark_owner, gcBYTES_TO_WORDS(bc->link->sendch->mem_size), 0);
463 if (bc->link->recvch)
464 account_memory(gc, gc->current_mark_owner, gcBYTES_TO_WORDS(bc->link->recvch->mem_size), 0);
465 }
466 }
467 return gc->mark_table[btc_redirect_bi_chan](p, gc);
468 }
469
btc_overmem_abort(NewGC * gc)470 static void btc_overmem_abort(NewGC *gc)
471 {
472 gc->kill_propagation_loop = 1;
473 GCWARN((GCOUTF, "WARNING: Ran out of memory accounting. "
474 "Info will be wrong.\n"));
475 }
476
propagate_accounting_marks(NewGC * gc,int no_full)477 static void propagate_accounting_marks(NewGC *gc, int no_full)
478 {
479 void *p;
480 int fuel = (gc->gc_full
481 ? -1
482 : (no_full
483 ? INCREMENTAL_COLLECT_FUEL_PER_100M / INCREMENTAL_MINOR_REQUEST_DIVISOR
484 : (INCREMENTAL_COLLECT_FUEL_PER_100M * AS_100M(gc->memory_in_use)) / 2));
485
486 while (pop_ptr(gc, &p, 0) && !gc->kill_propagation_loop) {
487 gc->traverse_count = 0;
488
489 /* GCDEBUG((DEBUGOUTF, "btc_account: popped off page %p:%p, ptr %p\n", page, page->addr, p)); */
490 propagate_marks_worker(gc, p, 0);
491
492 if (fuel >= 0) {
493 fuel--;
494 fuel -= (gc->traverse_count >> 2);
495 if (gc->unprotected_page) {
496 gc->unprotected_page = 0;
497 fuel -= 100;
498 }
499 if (fuel <= 0)
500 break;
501 }
502 }
503
504 if (gc->kill_propagation_loop)
505 reset_pointer_stack(gc);
506 }
507
BTC_initialize_mark_table(NewGC * gc)508 inline static void BTC_initialize_mark_table(NewGC *gc) {
509 gc->mark_table[scheme_thread_type] = BTC_thread_mark;
510 gc->mark_table[scheme_custodian_type] = BTC_custodian_mark;
511 gc->mark_table[gc->ephemeron_tag] = BTC_ephemeron_mark;
512 gc->mark_table[gc->cust_box_tag] = BTC_cust_box_mark;
513 gc->mark_table[scheme_place_bi_channel_type] = BTC_bi_chan_mark;
514 }
515
BTC_get_redirect_tag(NewGC * gc,int tag)516 inline static int BTC_get_redirect_tag(NewGC *gc, int tag) {
517 if (tag == scheme_thread_type) { tag = btc_redirect_thread; }
518 else if (tag == scheme_custodian_type) { tag = btc_redirect_custodian; }
519 else if (tag == gc->ephemeron_tag) { tag = btc_redirect_ephemeron; }
520 else if (tag == gc->cust_box_tag) { tag = btc_redirect_cust_box; }
521 else if (tag == scheme_place_bi_channel_type) { tag = btc_redirect_bi_chan; }
522 return tag;
523 }
524
BTC_do_accounting(NewGC * gc,int no_full)525 static void BTC_do_accounting(NewGC *gc, int no_full)
526 {
527 const int table_size = gc->owner_table_size;
528 int init_table_start, init_table_end, do_mark_threads;
529 OTEntry **owner_table = gc->owner_table;
530 MarkSegment *orig_mark_stack;
531
532 GC_ASSERT(gc->gc_full || gc->finished_incremental);
533 GC_ASSERT(gc->gc_full || !gc->accounted_incremental);
534
535 if (gc->gc_full) {
536 if (!gc->acct_mark_stack)
537 gc->really_doing_accounting = gc->next_really_doing_accounting;
538 gc->next_really_doing_accounting = 0;
539 } else {
540 if (gc->next_really_doing_accounting)
541 gc->really_doing_accounting = 1;
542
543 GC_ASSERT(!gc->mark_gen1);
544 GC_ASSERT(!gc->inc_gen1);
545 GC_ASSERT(!gc->check_gen1);
546
547 gc->mark_gen1 = 1;
548 gc->check_gen1 = 1;
549 gc->inc_gen1 = 1;
550 }
551
552 if(gc->really_doing_accounting) {
553 Scheme_Custodian *cur = owner_table[current_owner(gc, NULL)]->originator, *last, *parent;
554 Scheme_Custodian_Reference *box = NULL;
555 int i;
556
557 GC_ASSERT(SAME_TYPE(SCHEME_TYPE(cur), scheme_custodian_type));
558
559 GCDEBUG((DEBUGOUTF, "\nBEGINNING MEMORY ACCOUNTING\n"));
560 gc->doing_memory_accounting = 1;
561 gc->in_unsafe_allocation_mode = 1;
562 gc->unsafe_allocation_abort = btc_overmem_abort;
563 gc->master_page_btc_mark_checked = 0;
564
565 if (!gc->gc_full || gc->acct_mark_stack) {
566 orig_mark_stack = gc->mark_stack;
567 if (gc->acct_mark_stack) {
568 gc->mark_stack = gc->acct_mark_stack;
569 init_table_start = 2;
570 do_mark_threads = 0;
571 } else {
572 gc->mark_stack = NULL;
573 mark_stack_initialize(gc);
574 init_table_start = 1;
575 do_mark_threads = 1;
576 }
577 if (gc->gc_full)
578 init_table_end = table_size;
579 else
580 init_table_end = 2;
581 } else {
582 orig_mark_stack = NULL;
583 init_table_start = 1;
584 init_table_end = table_size;
585 do_mark_threads = 1;
586 }
587
588 /* clear the memory use numbers out */
589 for(i = init_table_start; i < init_table_end; i++)
590 if(owner_table[i]) {
591 owner_table[i]->memory_use = 0;
592 #ifdef MZ_USE_PLACES
593 if (MASTERGC && MASTERGC->major_places_gc)
594 owner_table[i]->master_memory_use = 0;
595 #endif
596 }
597
598 /* start with root: */
599 while (cur->parent && SCHEME_PTR1_VAL(cur->parent)) {
600 cur = SCHEME_PTR1_VAL(cur->parent);
601 GC_ASSERT(SAME_TYPE(SCHEME_TYPE(cur), scheme_custodian_type));
602 }
603
604 /* walk forward for the order we want (blame parents instead of children) */
605 last = cur;
606 while(cur) {
607 int owner = custodian_to_owner_set(gc, cur);
608
609 GC_ASSERT(gc->gc_full || (owner == 1));
610
611 GC_ASSERT(owner >= 0);
612 GC_ASSERT(owner <= gc->owner_table_size);
613
614 gc->acct_phantom_count = 0;
615
616 gc->current_mark_owner = owner;
617 GCDEBUG((DEBUGOUTF,"MARKING THREADS OF OWNER %i (CUST %p)\n", owner, cur));
618 gc->kill_propagation_loop = 0;
619 if (do_mark_threads) {
620 mark_threads(gc, owner);
621 mark_cust_boxes(gc, cur);
622 }
623 GCDEBUG((DEBUGOUTF, "Propagating accounting marks\n"));
624 propagate_accounting_marks(gc, no_full);
625
626 owner_table = gc->owner_table;
627 owner_table[owner]->memory_use = add_no_overflow(owner_table[owner]->memory_use,
628 gcBYTES_TO_WORDS(gc->acct_phantom_count));
629
630 if (!gc->gc_full)
631 break;
632
633 last = cur;
634 box = cur->global_next;
635 cur = box ? SCHEME_PTR1_VAL(box) : NULL;
636
637 do_mark_threads = 1;
638 }
639
640 release_master_btc_mark(gc);
641
642 if (gc->gc_full) {
643 /* walk backward folding totals into parent */
644 cur = last;
645 while (cur) {
646 int owner = custodian_to_owner_set(gc, cur);
647
648 box = cur->parent; parent = box ? SCHEME_PTR1_VAL(box) : NULL;
649 if (parent) {
650 int powner = custodian_to_owner_set(gc, parent);
651
652 owner_table = gc->owner_table;
653 owner_table[powner]->memory_use = add_no_overflow(owner_table[powner]->memory_use,
654 owner_table[owner]->memory_use);
655 owner_table[powner]->master_memory_use += owner_table[owner]->master_memory_use;
656 }
657
658 box = cur->global_prev; cur = box ? SCHEME_PTR1_VAL(box) : NULL;
659 }
660
661 if (orig_mark_stack) {
662 free_stack_pages_at(gc->mark_stack);
663 gc->acct_mark_stack = NULL;
664 gc->mark_stack = orig_mark_stack;
665 }
666 } else {
667 gc->acct_mark_stack = gc->mark_stack;
668 gc->mark_stack = orig_mark_stack;
669 }
670
671 gc->in_unsafe_allocation_mode = 0;
672 gc->doing_memory_accounting = 0;
673 if (gc->gc_full) {
674 gc->old_btc_mark = gc->new_btc_mark;
675 gc->new_btc_mark = !gc->new_btc_mark;
676 }
677 }
678
679 if (!gc->gc_full) {
680 gc->mark_gen1 = 0;
681 gc->check_gen1 = 0;
682 gc->inc_gen1 = 0;
683 }
684 }
685
BTC_add_account_hook(int type,void * c1,void * c2,uintptr_t b)686 inline static void BTC_add_account_hook(int type,void *c1,void *c2,uintptr_t b)
687 {
688 NewGC *gc = GC_get_GC();
689 AccountHook *work;
690
691 ((Scheme_Custodian *)c1)->really_doing_accounting = 1;
692
693 if(!gc->really_doing_accounting) {
694 if (!gc->avoid_collection) {
695 CHECK_PARK_UNUSED(gc);
696 gc->park[0] = c1;
697 gc->park[1] = c2;
698 gc->next_really_doing_accounting = 1;
699 garbage_collect(gc, 1, 0, 0, NULL);
700 c1 = gc->park[0]; gc->park[0] = NULL;
701 c2 = gc->park[1]; gc->park[1] = NULL;
702 }
703 }
704
705 if (type == MZACCT_LIMIT)
706 gc->reset_limits = 1;
707 if (type == MZACCT_REQUIRE)
708 gc->reset_required = 1;
709
710 for(work = gc->hooks; work; work = work->next) {
711 if((work->type == type) && (work->c2 == c2) && (work->c1 == c1)) {
712 if(type == MZACCT_REQUIRE) {
713 if(b > work->amount) work->amount = b;
714 } else { /* (type == MZACCT_LIMIT) */
715 if(b < work->amount) work->amount = b;
716 }
717 break;
718 }
719 }
720
721 if(!work) {
722 work = ofm_malloc(sizeof(AccountHook));
723 work->type = type;
724 work->c1 = c1;
725 work->c2 = c2;
726 work->amount = b;
727
728 /* push work onto hooks */
729 work->next = gc->hooks;
730 gc->hooks = work;
731 }
732 }
733
clean_up_account_hooks(NewGC * gc)734 inline static void clean_up_account_hooks(NewGC *gc)
735 {
736 AccountHook *work = gc->hooks;
737 AccountHook *prev = NULL;
738
739 while(work) {
740 if((!work->c1 || marked(gc, work->c1)) && marked(gc, work->c2)) {
741 work->c1 = GC_resolve2(work->c1, gc);
742 work->c2 = GC_resolve2(work->c2, gc);
743 prev = work;
744 work = work->next;
745 } else {
746 /* remove work hook */
747 AccountHook *next = work->next;
748
749 if(prev) prev->next = next;
750 if(!prev) gc->hooks = next;
751 ofm_free(work, sizeof(AccountHook));
752 work = next;
753 }
754 }
755 }
756
custodian_super_require(NewGC * gc,void * c)757 static uintptr_t custodian_super_require(NewGC *gc, void *c)
758 {
759 int set = ((Scheme_Custodian *)c)->gc_owner_set;
760 const int table_size = gc->owner_table_size;
761 OTEntry **owner_table = gc->owner_table;
762
763 if (gc->reset_required) {
764 int i;
765 for(i = 1; i < table_size; i++)
766 if (owner_table[i])
767 owner_table[i]->required_set = 0;
768 gc->reset_required = 0;
769 }
770
771 if (!owner_table[set]->required_set) {
772 uintptr_t req = 0, r;
773 AccountHook *work = gc->hooks;
774
775 while(work) {
776 if ((work->type == MZACCT_REQUIRE) && (c == work->c2)) {
777 r = work->amount + custodian_super_require(gc, work->c1);
778 if (r > req)
779 req = r;
780 }
781 work = work->next;
782 }
783 owner_table[set]->super_required = req;
784 owner_table[set]->required_set = 1;
785 }
786
787 return owner_table[set]->super_required;
788 }
789
BTC_run_account_hooks(NewGC * gc)790 inline static void BTC_run_account_hooks(NewGC *gc)
791 {
792 AccountHook *work = gc->hooks;
793 AccountHook *prev = NULL;
794
795 while (work) {
796 if( ((work->type == MZACCT_REQUIRE) &&
797 ((gc->used_pages > (gc->max_pages_for_use / 2))
798 || ((((gc->max_pages_for_use / 2) - gc->used_pages) * APAGE_SIZE)
799 < (work->amount + custodian_super_require(gc, work->c1)))))
800 ||
801 ((work->type == MZACCT_LIMIT) &&
802 (GC_get_memory_use(work->c1) > work->amount))) {
803 AccountHook *next = work->next;
804
805 if(prev) prev->next = next;
806 if(!prev) gc->hooks = next;
807 scheme_schedule_custodian_close(work->c2);
808 ofm_free(work, sizeof(AccountHook));
809 work = next;
810 } else {
811 prev = work;
812 work = work->next;
813 }
814 }
815 }
816
custodian_single_time_limit(NewGC * gc,int set)817 static uintptr_t custodian_single_time_limit(NewGC *gc, int set)
818 {
819 OTEntry **owner_table = gc->owner_table;
820 const int table_size = gc->owner_table_size;
821
822 if (!set)
823 return gc->place_memory_limit;
824
825 if (gc->reset_limits) {
826 int i;
827 for(i = 1; i < table_size; i++)
828 if (owner_table[i])
829 owner_table[i]->limit_set = 0;
830 gc->reset_limits = 0;
831 }
832
833 if (!owner_table[set]->limit_set) {
834 /* Check for limits on this custodian or one of its ancestors: */
835 uintptr_t limit = gc->place_memory_limit;
836 Scheme_Custodian *orig = (Scheme_Custodian *) owner_table[set]->originator, *c;
837 AccountHook *work = gc->hooks;
838
839 while(work) {
840 if ((work->type == MZACCT_LIMIT) && (work->c1 == work->c2)) {
841 c = orig;
842 while (1) {
843 if (work->c2 == c) {
844 if (work->amount < limit)
845 limit = work->amount;
846 break;
847 }
848 if (!c->parent)
849 break;
850 c = (Scheme_Custodian*)SCHEME_PTR1_VAL(c->parent);
851 if (!c)
852 break;
853 }
854 }
855 work = work->next;
856 }
857 owner_table[set]->single_time_limit = limit;
858 owner_table[set]->limit_set = 1;
859 }
860
861 return owner_table[set]->single_time_limit;
862 }
863
BTC_get_memory_use(NewGC * gc,void * o)864 intptr_t BTC_get_memory_use(NewGC* gc, void *o)
865 {
866 Scheme_Object *arg = (Scheme_Object*)o;
867 if(SAME_TYPE(SCHEME_TYPE(arg), scheme_custodian_type)) {
868 return custodian_usage(gc, arg);
869 }
870
871 return 0;
872 }
873
BTC_single_allocation_limit(NewGC * gc,size_t sizeb)874 int BTC_single_allocation_limit(NewGC *gc, size_t sizeb)
875 /* Use this function to check for allocations that exceed a single-time
876 * limit. Otherwise, the limit doesn't work as intended, because
877 * a program can allocate a large block that nearly exhausts memory,
878 * and then a subsequent allocation can fail. As long as the limit
879 * is much smaller than the actual available memory, and as long as
880 * GC_out_of_memory protects any user-requested allocation whose size
881 * is independent of any existing object, then we can enforce the limit. */
882 {
883 if (gc->alternate_accounting_custodian) {
884 int set = custodian_to_owner_set(gc, gc->alternate_accounting_custodian);
885 return (custodian_single_time_limit(gc, set) < sizeb);
886 } else {
887 Scheme_Thread *p = scheme_current_thread;
888 if (p)
889 return (custodian_single_time_limit(gc, thread_get_owner(p)) < sizeb);
890 else
891 return (gc->place_memory_limit < sizeb);
892 }
893 }
894
BTC_get_account_hook(void * c1)895 static uintptr_t BTC_get_account_hook(void *c1)
896 {
897 NewGC *gc = GC_get_GC();
898 uintptr_t mem;
899
900 if (!gc->really_doing_accounting)
901 return 0;
902
903 mem = custodian_single_time_limit(gc, custodian_to_owner_set(gc, c1));
904
905 if (mem == (uintptr_t)(intptr_t)-1)
906 return 0;
907
908 return mem;
909 }
910
911
BTC_clean_up(NewGC * gc)912 static inline void BTC_clean_up(NewGC *gc) {
913 clean_up_thread_list(gc);
914 clean_up_owner_table(gc);
915 clean_up_account_hooks(gc);
916 }
917
BTC_set_btc_mark(NewGC * gc,objhead * info)918 static inline void BTC_set_btc_mark(NewGC *gc, objhead* info) {
919 info->btc_mark = gc->old_btc_mark;
920 }
921 #endif
922