+ ASSERT(bd->step == ws->step);
+
+ bd->free = ws->todo_free;
+
+ // If the global list is not empty, or there's not much work in
+ // this block to push, and there's enough room in
+ // this block to evacuate the current object, then just increase
+ // the limit.
+ if (ws->step->todos != NULL ||
+ (bd->free - bd->u.scan < WORK_UNIT_WORDS / 2)) {
+ if (bd->free + size < bd->start + BLOCK_SIZE_W) {
+ debugTrace(DEBUG_gc, "increasing limit for %p", bd->start);
+ ws->todo_lim = stg_min(bd->start + BLOCK_SIZE_W,
+ ws->todo_lim + stg_max(WORK_UNIT_WORDS,size));
+ return ws->todo_free;
+ }
+ }
+
+ ASSERT(bd->u.scan >= bd->start && bd->u.scan <= bd->free);
+
+ // If this block is not the scan block, we want to push it out and
+ // make room for a new todo block.
+ if (bd != ws->scan_bd)
+ {
+ // If this block does not have enough space to allocate the
+ // current object, but it also doesn't have any work to push, then
+ // push it on to the scanned list. It cannot be empty, because
+ // then there would be enough room to copy the current object.
+ if (bd->u.scan == bd->free)
+ {
+ ASSERT(bd->free != bd->start);
+ push_scanned_block(bd, ws);
+ }
+ // Otherwise, push this block out to the global list.
+ else
+ {
+ step *stp;
+ stp = ws->step;
+ trace(TRACE_gc|DEBUG_gc, "push todo block %p (%d words), step %d, n_todos: %d",
+ bd->start, bd->free - bd->u.scan, stp->abs_no, stp->n_todos);
+ // ToDo: use buffer_todo
+ ACQUIRE_SPIN_LOCK(&stp->sync_todo);
+ if (stp->todos_last == NULL) {
+ stp->todos_last = bd;
+ stp->todos = bd;
+ } else {
+ stp->todos_last->link = bd;
+ stp->todos_last = bd;
+ }
+ stp->n_todos++;
+ RELEASE_SPIN_LOCK(&stp->sync_todo);
+ }
+ }