Example #1
0
/* Called by a thread when it thinks it is done with GC. It may get some more
 * work yet, though. */
static void finish_gc(MVMThreadContext *tc, MVMuint8 gen) {
    MVMuint32 put_vote = 1, i;

    /* Loop until other threads have terminated, processing any extra work
     * that we are given. */
    while (tc->instance->gc_finish) {
        MVMuint32 failed = 0;
        MVMuint32 i = 0;

        for ( ; i < tc->gc_work_count; i++) {
            process_in_tray(tc->gc_work[i].tc, gen, &put_vote);
            failed = process_sent_items(tc->gc_work[i].tc, &put_vote) | failed;
        }

        if (!failed && put_vote) {
            MVM_atomic_decr(&tc->instance->gc_finish);
            put_vote = 0;
        }
    }
/*    GCORCH_LOG(tc, "Thread %d run %d : Discovered GC termination\n");*/

    /* Reset GC status flags and cleanup sent items for any work threads. */
    /* This is also where thread destruction happens, and it needs to happen
     * before we acknowledge this GC run is finished. */
    for (i = 0; i < tc->gc_work_count; i++) {
        MVMThreadContext *other = tc->gc_work[i].tc;
        MVMThread *thread_obj = other->thread_obj;
        cleanup_sent_items(other);
        if (thread_obj->body.stage == MVM_thread_stage_clearing_nursery) {
            GCORCH_LOG(tc, "Thread %d run %d : freeing gen2 of thread %d\n", other->thread_id);
            /* always free gen2 */
            MVM_gc_collect_free_gen2_unmarked(other);
            GCORCH_LOG(tc, "Thread %d run %d : transferring gen2 of thread %d\n", other->thread_id);
            MVM_gc_gen2_transfer(other, tc);
            GCORCH_LOG(tc, "Thread %d run %d : destroying thread %d\n", other->thread_id);
            MVM_tc_destroy(other);
            tc->gc_work[i].tc = thread_obj->body.tc = NULL;
            thread_obj->body.stage = MVM_thread_stage_destroyed;
        }
        else {
            if (thread_obj->body.stage == MVM_thread_stage_exited) {
                /* don't bother freeing gen2; we'll do it next time */
                thread_obj->body.stage = MVM_thread_stage_clearing_nursery;
//                    GCORCH_LOG(tc, "Thread %d run %d : set thread %d clearing nursery stage to %d\n", other->thread_id, thread_obj->body.stage);
            }
            apr_atomic_cas32(&other->gc_status, MVMGCStatus_UNABLE,
                MVMGCStatus_STOLEN);
            apr_atomic_cas32(&other->gc_status, MVMGCStatus_NONE,
                MVMGCStatus_INTERRUPT);
        }
    }
    MVM_atomic_decr(&tc->instance->gc_ack);
}
Example #2
0
/* Run the global destruction phase. */
void MVM_gc_global_destruction(MVMThreadContext *tc) {
    char *nursery_tmp;

    /* Fake a nursery collection run by swapping the semi-
     * space nurseries. */
    nursery_tmp = tc->nursery_fromspace;
    tc->nursery_fromspace = tc->nursery_tospace;
    tc->nursery_tospace = nursery_tmp;

    /* Run the objects' finalizers */
    MVM_gc_collect_free_nursery_uncopied(tc, tc->nursery_alloc);
    MVM_gc_root_gen2_cleanup(tc);
    MVM_gc_collect_free_gen2_unmarked(tc, 1);
    MVM_gc_collect_free_stables(tc);
}
Example #3
0
static void run_gc(MVMThreadContext *tc, MVMuint8 what_to_do) {
    MVMuint8   gen;
    MVMuint32  i, n;

    /* Decide nursery or full collection. */
    gen = is_full_collection(tc) ? MVMGCGenerations_Both : MVMGCGenerations_Nursery;

    /* Do GC work for ourselves and any work threads. */
    for (i = 0, n = tc->gc_work_count ; i < n; i++) {
        MVMThreadContext *other = tc->gc_work[i].tc;
        tc->gc_work[i].limit = other->nursery_alloc;
        GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE, "Thread %d run %d : starting collection for thread %d\n",
            other->thread_id);
        other->gc_promoted_bytes = 0;
        MVM_gc_collect(other, (other == tc ? what_to_do : MVMGCWhatToDo_NoInstance), gen);
    }

    /* Wait for everybody to agree we're done. */
    finish_gc(tc, gen, what_to_do == MVMGCWhatToDo_All);

    /* Now we're all done, it's safe to finalize any objects that need it. */
	/* XXX TODO explore the feasability of doing this in a background
	 * finalizer/destructor thread and letting the main thread(s) continue
	 * on their merry way(s). */
    for (i = 0, n = tc->gc_work_count ; i < n; i++) {
        MVMThreadContext *other = tc->gc_work[i].tc;

        /* The thread might've been destroyed */
        if (!other)
            continue;

        /* Contribute this thread's promoted bytes. */
        MVM_add(&tc->instance->gc_promoted_bytes_since_last_full, other->gc_promoted_bytes);

        /* Collect nursery and gen2 as needed. */
        GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
            "Thread %d run %d : collecting nursery uncopied of thread %d\n",
            other->thread_id);
        MVM_gc_collect_free_nursery_uncopied(other, tc->gc_work[i].limit);
        if (gen == MVMGCGenerations_Both) {
            GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
                "Thread %d run %d : freeing gen2 of thread %d\n",
                other->thread_id);
            MVM_gc_collect_free_gen2_unmarked(other);
        }
    }
}
Example #4
0
static void run_gc(MVMThreadContext *tc, MVMuint8 what_to_do) {
    MVMuint8   gen;
    MVMThread *child;
    MVMuint32  i, n;

    /* Do GC work for this thread, or at least all we know about. */
    gen = tc->instance->gc_seq_number % MVM_GC_GEN2_RATIO == 0
        ? MVMGCGenerations_Both
        : MVMGCGenerations_Nursery;

    /* Do GC work for any work threads. */
    for (i = 0, n = tc->gc_work_count ; i < n; i++) {
        MVMThreadContext *other = tc->gc_work[i].tc;
        tc->gc_work[i].limit = other->nursery_alloc;
        GCORCH_LOG(tc, "Thread %d run %d : starting collection for thread %d\n",
            other->thread_id);
        MVM_gc_collect(other, (other == tc ? what_to_do : MVMGCWhatToDo_NoInstance), gen);
    }

    /* Wait for everybody to agree we're done. */
    finish_gc(tc, gen);

    /* Now we're all done, it's safe to finalize any objects that need it. */
    for (i = 0, n = tc->gc_work_count ; i < n; i++) {
        MVMThreadContext *other = tc->gc_work[i].tc;
        MVMThread *thread_obj;

        /* the thread might've been destroyed */
        if (!other) continue;

        thread_obj = other->thread_obj;

        MVM_gc_collect_free_nursery_uncopied(other, tc->gc_work[i].limit);

        if (gen == MVMGCGenerations_Both) {
            GCORCH_LOG(tc, "Thread %d run %d : freeing gen2 of thread %d\n", other->thread_id);
            MVM_gc_collect_cleanup_gen2roots(other);
            MVM_gc_collect_free_gen2_unmarked(other);
        }
    }
}
Example #5
0
/* Run the global destruction phase. */
void MVM_gc_global_destruction(MVMThreadContext *tc) {
    char *nursery_tmp;

    /* Must wait until we're the only thread... */
    while (tc->instance->num_user_threads) {
        GC_SYNC_POINT(tc);
        MVM_platform_thread_yield();
    }

    /* Fake a nursery collection run by swapping the semi-
     * space nurseries. */
    nursery_tmp = tc->nursery_fromspace;
    tc->nursery_fromspace = tc->nursery_tospace;
    tc->nursery_tospace = nursery_tmp;

    /* Run the objects' finalizers */
    MVM_gc_collect_free_nursery_uncopied(tc, tc->nursery_alloc);
    MVM_gc_root_gen2_cleanup(tc);
    MVM_gc_collect_free_gen2_unmarked(tc);
    MVM_gc_collect_free_stables(tc);
}
Example #6
0
static void finish_gc(MVMThreadContext *tc, MVMuint8 gen, MVMuint8 is_coordinator) {
    MVMuint32 i, did_work;

    /* Do any extra work that we have been passed. */
    GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
        "Thread %d run %d : doing any work in thread in-trays\n");
    did_work = 1;
    while (did_work) {
        did_work = 0;
        for (i = 0; i < tc->gc_work_count; i++)
            did_work += process_in_tray(tc->gc_work[i].tc, gen);
    }

    /* Decrement gc_finish to say we're done, and wait for termination. */
    GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE, "Thread %d run %d : Voting to finish\n");
    MVM_decr(&tc->instance->gc_finish);
    while (MVM_load(&tc->instance->gc_finish)) {
        for (i = 0; i < 1000; i++)
            ; /* XXX Something HT-efficienter. */
        /* XXX Here we can look to see if we got passed any work, and if so
         * try to un-vote. */
    }
    GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE, "Thread %d run %d : Termination agreed\n");

    /* Co-ordinator should do final check over all the in-trays, and trigger
     * collection until all is settled. Rest should wait. Additionally, after
     * in-trays are settled, coordinator walks threads looking for anything
     * that needs adding to the finalize queue. It then will make another
     * iteration over in-trays to handle cross-thread references to objects
     * needing finalization. For full collections, collected objects are then
     * cleaned from all inter-generational sets, and finally any objects to
     * be freed at the fixed size allocator's next safepoint are freed. */
    if (is_coordinator) {
        GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
            "Thread %d run %d : Co-ordinator handling in-tray clearing completion\n");
        clear_intrays(tc, gen);

        GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
            "Thread %d run %d : Co-ordinator handling finalizers\n");
        MVM_finalize_walk_queues(tc, gen);
        clear_intrays(tc, gen);

        if (gen == MVMGCGenerations_Both) {
            MVMThread *cur_thread = (MVMThread *)MVM_load(&tc->instance->threads);
            GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
                "Thread %d run %d : Co-ordinator handling inter-gen root cleanup\n");
            while (cur_thread) {
                if (cur_thread->body.tc)
                    MVM_gc_root_gen2_cleanup(cur_thread->body.tc);
                cur_thread = cur_thread->body.next;
            }
        }

        GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
            "Thread %d run %d : Co-ordinator handling fixed-size allocator safepoint frees\n");
        MVM_fixed_size_safepoint(tc, tc->instance->fsa);

        MVM_profile_heap_take_snapshot(tc);

        GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
            "Thread %d run %d : Co-ordinator signalling in-trays clear\n");
        MVM_store(&tc->instance->gc_intrays_clearing, 0);
    }
    else {
        GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
            "Thread %d run %d : Waiting for in-tray clearing completion\n");
        while (MVM_load(&tc->instance->gc_intrays_clearing))
            for (i = 0; i < 1000; i++)
                ; /* XXX Something HT-efficienter. */
        GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
            "Thread %d run %d : Got in-tray clearing complete notice\n");
    }

    /* Reset GC status flags. This is also where thread destruction happens,
     * and it needs to happen before we acknowledge this GC run is finished. */
    for (i = 0; i < tc->gc_work_count; i++) {
        MVMThreadContext *other = tc->gc_work[i].tc;
        MVMThread *thread_obj = other->thread_obj;
        if (MVM_load(&thread_obj->body.stage) == MVM_thread_stage_clearing_nursery) {
            GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
                "Thread %d run %d : transferring gen2 of thread %d\n", other->thread_id);
            MVM_gc_gen2_transfer(other, tc);
            GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
                "Thread %d run %d : destroying thread %d\n", other->thread_id);
            MVM_tc_destroy(other);
            tc->gc_work[i].tc = thread_obj->body.tc = NULL;
            MVM_store(&thread_obj->body.stage, MVM_thread_stage_destroyed);
        }
        else {
            /* Free gen2 unmarked if full collection. */
            if (gen == MVMGCGenerations_Both) {
                GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
                    "Thread %d run %d : freeing gen2 of thread %d\n",
                    other->thread_id);
                MVM_gc_collect_free_gen2_unmarked(other, 0);
            }

            /* Contribute this thread's promoted bytes. */
            MVM_add(&tc->instance->gc_promoted_bytes_since_last_full, other->gc_promoted_bytes);

            /* Collect nursery. */
            GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
                "Thread %d run %d : collecting nursery uncopied of thread %d\n",
                other->thread_id);
            MVM_gc_collect_free_nursery_uncopied(other, tc->gc_work[i].limit);

            /* Handle exited threads. */
            if (MVM_load(&thread_obj->body.stage) == MVM_thread_stage_exited) {
                /* Don't bother freeing gen2; we'll do it next time */
                MVM_store(&thread_obj->body.stage, MVM_thread_stage_clearing_nursery);
                GCDEBUG_LOG(tc, MVM_GC_DEBUG_ORCHESTRATE,
                    "Thread %d run %d : set thread %d clearing nursery stage to %d\n",
                    other->thread_id, (int)MVM_load(&thread_obj->body.stage));
            }

            /* Mark thread free to continue. */
            MVM_cas(&other->gc_status, MVMGCStatus_STOLEN, MVMGCStatus_UNABLE);
            MVM_cas(&other->gc_status, MVMGCStatus_INTERRUPT, MVMGCStatus_NONE);
        }
    }

    /* Signal acknowledgement of completing the cleanup,
     * except for STables, and if we're the final to do
     * so, free the STables, which have been linked. */
    if (MVM_decr(&tc->instance->gc_ack) == 2) {
        /* Set it to zero (we're guaranteed the only ones trying to write to
         * it here). Actual STable free in MVM_gc_enter_from_allocator. */
        MVM_store(&tc->instance->gc_ack, 0);
    }
}