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