<commit_before>
#include "rust_sched_loop.h"
#include "rust_util.h"
#include "rust_scheduler.h"

#ifndef _WIN32
pthread_key_t rust_sched_loop::task_key;
#else
DWORD rust_sched_loop::task_key;
#endif

const size_t C_STACK_SIZE = 1024*1024;

bool rust_sched_loop::tls_initialized = false;

rust_sched_loop::rust_sched_loop(rust_scheduler *sched,int id) :
    _log(this),
    id(id),
    should_exit(false),
    cached_c_stack(NULL),
    dead_task(NULL),
    pump_signal(NULL),
    kernel(sched->kernel),
    sched(sched),
    log_lvl(log_debug),
    min_stack_size(kernel->env->min_stack_size),
    local_region(kernel->env, false),
    // TODO: calculate a per scheduler name.
    name("main")
{
    LOGPTR(this, "new dom", (uintptr_t)this);
    isaac_init(kernel, &rctx, NULL);

    if (!tls_initialized)
        init_tls();
}

void
rust_sched_loop::activate(rust_task *task) {
    lock.must_have_lock();
    task->ctx.next = &c_context;
    DLOG(this, task, "descheduling...");
    lock.unlock();
    prepare_c_stack(task);
    task->ctx.swap(c_context);
    task->cleanup_after_turn();
    unprepare_c_stack();
    lock.lock();
    DLOG(this, task, "task has returned");
}


void
rust_sched_loop::fail() {
    _log.log(NULL, log_err, "domain %s @0x%" PRIxPTR " root task failed",
        name, this);
    kernel->fail();
}

void
rust_sched_loop::kill_all_tasks() {
    std::vector<rust_task*> all_tasks;

    {
        scoped_lock with(lock);

        for (size_t i = 0; i < running_tasks.length(); i++) {
            all_tasks.push_back(running_tasks[i]);
        }

        for (size_t i = 0; i < blocked_tasks.length(); i++) {
            all_tasks.push_back(blocked_tasks[i]);
        }
    }

    while (!all_tasks.empty()) {
        rust_task *task = all_tasks.back();
        all_tasks.pop_back();
        // We don't want the failure of these tasks to propagate back
        // to the kernel again since we're already failing everything
        task->unsupervise();
        task->kill();
    }
}

size_t
rust_sched_loop::number_of_live_tasks() {
    return running_tasks.length() + blocked_tasks.length();
}

/**
 * Delete any dead tasks.
 */
void
rust_sched_loop::reap_dead_tasks() {
    lock.must_have_lock();

    if (dead_task == NULL) {
        return;
    }

    // Dereferencing the task will probably cause it to be released
    // from the scheduler, which may end up trying to take this lock
    lock.unlock();

    dead_task->delete_all_stacks();
    // Deref the task, which may cause it to request us to release it
    dead_task->deref();
    dead_task = NULL;

    lock.lock();
}

void
rust_sched_loop::release_task(rust_task *task) {
    // Nobody should have a ref to the task at this point
    assert(task->get_ref_count() == 0);
    // Now delete the task, which will require using this thread's
    // memory region.
    delete task;
    // Now release the task from the scheduler, which may trigger this
    // thread to exit
    sched->release_task();
}

/**
 * Schedules a running task for execution. Only running tasks can be
 * activated.  Blocked tasks have to be unblocked before they can be
 * activated.
 *
 * Returns NULL if no tasks can be scheduled.
 */
rust_task *
rust_sched_loop::schedule_task() {
    lock.must_have_lock();
    assert(this);
    // FIXME: in the face of failing tasks, this is not always right. (#2695)
    // assert(n_live_tasks() > 0);
    if (running_tasks.length() > 0) {
        size_t k = isaac_rand(&rctx);
        // Look around for a runnable task, starting at k.
        for(size_t j = 0; j < running_tasks.length(); ++j) {
            size_t  i = (j + k) % running_tasks.length();
            return (rust_task *)running_tasks[i];
        }
    }
    return NULL;
}

void
rust_sched_loop::log_state() {
    if (log_rt_task < log_debug) return;

    if (!running_tasks.is_empty()) {<commit_msg>Remove obsolete FIXME<commit_after>
#include "rust_sched_loop.h"
#include "rust_util.h"
#include "rust_scheduler.h"

#ifndef _WIN32
pthread_key_t rust_sched_loop::task_key;
#else
DWORD rust_sched_loop::task_key;
#endif

const size_t C_STACK_SIZE = 1024*1024;

bool rust_sched_loop::tls_initialized = false;

rust_sched_loop::rust_sched_loop(rust_scheduler *sched,int id) :
    _log(this),
    id(id),
    should_exit(false),
    cached_c_stack(NULL),
    dead_task(NULL),
    pump_signal(NULL),
    kernel(sched->kernel),
    sched(sched),
    log_lvl(log_debug),
    min_stack_size(kernel->env->min_stack_size),
    local_region(kernel->env, false),
    // TODO: calculate a per scheduler name.
    name("main")
{
    LOGPTR(this, "new dom", (uintptr_t)this);
    isaac_init(kernel, &rctx, NULL);

    if (!tls_initialized)
        init_tls();
}

void
rust_sched_loop::activate(rust_task *task) {
    lock.must_have_lock();
    task->ctx.next = &c_context;
    DLOG(this, task, "descheduling...");
    lock.unlock();
    prepare_c_stack(task);
    task->ctx.swap(c_context);
    task->cleanup_after_turn();
    unprepare_c_stack();
    lock.lock();
    DLOG(this, task, "task has returned");
}


void
rust_sched_loop::fail() {
    _log.log(NULL, log_err, "domain %s @0x%" PRIxPTR " root task failed",
        name, this);
    kernel->fail();
}

void
rust_sched_loop::kill_all_tasks() {
    std::vector<rust_task*> all_tasks;

    {
        scoped_lock with(lock);

        for (size_t i = 0; i < running_tasks.length(); i++) {
            all_tasks.push_back(running_tasks[i]);
        }

        for (size_t i = 0; i < blocked_tasks.length(); i++) {
            all_tasks.push_back(blocked_tasks[i]);
        }
    }

    while (!all_tasks.empty()) {
        rust_task *task = all_tasks.back();
        all_tasks.pop_back();
        // We don't want the failure of these tasks to propagate back
        // to the kernel again since we're already failing everything
        task->unsupervise();
        task->kill();
    }
}

size_t
rust_sched_loop::number_of_live_tasks() {
    return running_tasks.length() + blocked_tasks.length();
}

/**
 * Delete any dead tasks.
 */
void
rust_sched_loop::reap_dead_tasks() {
    lock.must_have_lock();

    if (dead_task == NULL) {
        return;
    }

    // Dereferencing the task will probably cause it to be released
    // from the scheduler, which may end up trying to take this lock
    lock.unlock();

    dead_task->delete_all_stacks();
    // Deref the task, which may cause it to request us to release it
    dead_task->deref();
    dead_task = NULL;

    lock.lock();
}

void
rust_sched_loop::release_task(rust_task *task) {
    // Nobody should have a ref to the task at this point
    assert(task->get_ref_count() == 0);
    // Now delete the task, which will require using this thread's
    // memory region.
    delete task;
    // Now release the task from the scheduler, which may trigger this
    // thread to exit
    sched->release_task();
}

/**
 * Schedules a running task for execution. Only running tasks can be
 * activated.  Blocked tasks have to be unblocked before they can be
 * activated.
 *
 * Returns NULL if no tasks can be scheduled.
 */
rust_task *
rust_sched_loop::schedule_task() {
    lock.must_have_lock();
    assert(this);
    if (running_tasks.length() > 0) {
        size_t k = isaac_rand(&rctx);
        // Look around for a runnable task, starting at k.
        for(size_t j = 0; j < running_tasks.length(); ++j) {
            size_t  i = (j + k) % running_tasks.length();
            return (rust_task *)running_tasks[i];
        }
    }
    return NULL;
}

void
rust_sched_loop::log_state() {
    if (log_rt_task < log_debug) return;

    if (!running_tasks.is_empty()) {
{'fraction_non_alphanumeric': 0.1271797561295398, 'fraction_numerical': 0.004851186574013374, 'mean_word_length': 3.010515247108307, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2943938', 'n_tokens_mistral': 2622, 'n_tokens_neox': 2558, 'n_words': 645}