StgInt newSpark (StgRegTable *reg, StgClosure *p) { Capability *cap = regTableToCapability(reg); SparkPool *pool = cap->sparks; /* I am not sure whether this is the right thing to do. * Maybe it is better to exploit the tag information * instead of throwing it away? */ p = UNTAG_CLOSURE(p); if (closure_SHOULD_SPARK(p)) { pushWSDeque(pool,p); cap->sparks_created++; } else { cap->sparks_dud++; } return 1; }
StgPtr todo_block_full (uint32_t size, gen_workspace *ws) { bool urgent_to_push, can_extend; StgPtr p; bdescr *bd; // todo_free has been pre-incremented by Evac.c:alloc_for_copy(). We // are expected to leave it bumped when we've finished here. ws->todo_free -= size; bd = ws->todo_bd; ASSERT(bd != NULL); ASSERT(bd->link == NULL); ASSERT(bd->gen == ws->gen); // We intentionally set ws->todo_lim lower than the full size of // the block, so that we can push out some work to the global list // and get the parallel threads working as soon as possible. // // So when ws->todo_lim is reached, we end up here and have to // decide whether it's worth pushing out the work we have or not. // If we have enough room in the block to evacuate the current // object, and it's not urgent to push this work, then we just // extend the limit and keep going. Where "urgent" is defined as: // the global pool is empty, and there's enough work in this block // to make it worth pushing. // urgent_to_push = looksEmptyWSDeque(ws->todo_q) && (ws->todo_free - bd->u.scan >= WORK_UNIT_WORDS / 2); // We can extend the limit for the current block if there's enough // room for the current object, *and* we're not into the second or // subsequent block of a large block (see Note [big objects]). can_extend = ws->todo_free + size <= bd->start + bd->blocks * BLOCK_SIZE_W && ws->todo_free < ws->todo_bd->start + BLOCK_SIZE_W; if (!urgent_to_push && can_extend) { ws->todo_lim = stg_min(bd->start + bd->blocks * BLOCK_SIZE_W, ws->todo_lim + stg_max(WORK_UNIT_WORDS,size)); debugTrace(DEBUG_gc, "increasing limit for %p to %p", bd->start, ws->todo_lim); p = ws->todo_free; ws->todo_free += size; return p; } gct->copied += ws->todo_free - bd->free; bd->free = 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 != gct->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. if (bd->u.scan == bd->free) { if (bd->free == bd->start) { // Normally the block would not be empty, because then // there would be enough room to copy the current // object. However, if the object we're copying is // larger than a block, then we might have an empty // block here. freeGroup_sync(bd); } else { push_scanned_block(bd, ws); } } // Otherwise, push this block out to the global list. else { DEBUG_ONLY( generation *gen ); DEBUG_ONLY( gen = ws->gen ); debugTrace(DEBUG_gc, "push todo block %p (%ld words), step %d, todo_q: %ld", bd->start, (unsigned long)(bd->free - bd->u.scan), gen->no, dequeElements(ws->todo_q)); if (!pushWSDeque(ws->todo_q, bd)) { bd->link = ws->todo_overflow; ws->todo_overflow = bd; ws->n_todo_overflow++; } } } ws->todo_bd = NULL; ws->todo_free = NULL; ws->todo_lim = NULL; alloc_todo_block(ws, size); p = ws->todo_free; ws->todo_free += size; return p; }