Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i#6959 unsched exit: Fix stay-unsched bug #7011

Merged
merged 4 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions clients/drcachesim/scheduler/scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3274,14 +3274,11 @@ scheduler_tmpl_t<RecordType, ReaderType>::pick_next_input(output_ordinal_t outpu
} else {
auto lock =
std::unique_lock<mutex_dbg_owned>(*inputs_[prev_index].lock);
// If we can't go back to the current input, we're EOF or idle.
// TODO i#6959: We should go the EOF/idle route if
// inputs_[prev_index].unscheduled as otherwise we're ignoring its
// unscheduled transition: although if there are no other threads
// at all (not just an empty queue) this turns into the
// eof_or_idle() all-unscheduled scenario. Once we have some kind
// of early exit option we'll add the unscheduled check here.
if (inputs_[prev_index].at_eof) {
// If we can't go back to the current input because it's EOF
// or unscheduled indefinitely (we already checked blocked_time
// above: it's 0 here), this output is either idle or EOF.
if (inputs_[prev_index].at_eof ||
inputs_[prev_index].unscheduled) {
lock.unlock();
sched_type_t::stream_status_t status =
eof_or_idle(output, prev_index);
Expand Down Expand Up @@ -3478,6 +3475,9 @@ scheduler_tmpl_t<RecordType, ReaderType>::process_marker(input_info_t &input,
input.unscheduled = true;
if (input.syscall_timeout_arg > 0) {
input.blocked_time = scale_blocked_time(input.syscall_timeout_arg);
// Clamp at 1 since 0 means an infinite timeout for unscheduled=true.
if (input.blocked_time == 0)
input.blocked_time = 1;
input.blocked_start_time = get_output_time(output);
VPRINT(this, 3, "input %d unscheduled for %" PRIu64 " @%" PRIu64 "\n",
input.index, input.blocked_time, input.reader->get_last_timestamp());
Expand Down Expand Up @@ -3506,6 +3506,9 @@ scheduler_tmpl_t<RecordType, ReaderType>::process_marker(input_info_t &input,
input.unscheduled = true;
if (input.syscall_timeout_arg > 0) {
input.blocked_time = scale_blocked_time(input.syscall_timeout_arg);
// Clamp at 1 since 0 means an infinite timeout for unscheduled=true.
if (input.blocked_time == 0)
input.blocked_time = 1;
input.blocked_start_time = get_output_time(output);
VPRINT(this, 3, "input %d unscheduled for %" PRIu64 " @%" PRIu64 "\n",
input.index, input.blocked_time, input.reader->get_last_timestamp());
Expand Down
128 changes: 124 additions & 4 deletions clients/drcachesim/tests/scheduler_unit_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4534,7 +4534,7 @@ test_direct_switch()
}

static void
test_unscheduled()
test_unscheduled_base()
{
std::cerr << "\n----------------\nTesting unscheduled inputs\n";
// We have just 1 output to better control the order and avoid flakiness.
Expand Down Expand Up @@ -5166,6 +5166,129 @@ test_unscheduled_initially_roi()
#endif
}

static void
test_unscheduled_small_timeout()
{
// Test that a small timeout scaled to 0 does not turn into an infinite timeout.
std::cerr << "\n----------------\nTesting unscheduled input with small timeout\n";
static constexpr int NUM_OUTPUTS = 1;
// 4*0.1 rounds to 0 (the scheduler's cast rounds any fraction down).
static constexpr int WAIT_TIMEOUT = 4;
static constexpr double BLOCK_SCALE = 0.1;
static constexpr memref_tid_t TID_A = 100;
std::vector<trace_entry_t> refs_A = {
make_thread(TID_A),
make_pid(1),
make_version(TRACE_ENTRY_VERSION),
make_timestamp(1001),
make_marker(TRACE_MARKER_TYPE_CPU_ID, 0),
make_instr(/*pc=*/101),
make_timestamp(1002),
make_marker(TRACE_MARKER_TYPE_CPU_ID, 0),
make_marker(TRACE_MARKER_TYPE_SYSCALL, 999),
make_marker(TRACE_MARKER_TYPE_MAYBE_BLOCKING_SYSCALL, 0),
make_marker(TRACE_MARKER_TYPE_SYSCALL_ARG_TIMEOUT, WAIT_TIMEOUT),
make_marker(TRACE_MARKER_TYPE_SYSCALL_UNSCHEDULE, 0),
make_timestamp(2002),
make_instr(/*pc=*/102),
make_exit(TID_A),
};
{
std::vector<scheduler_t::input_reader_t> readers;
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_A)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_A);
static const char *const CORE0_SCHED_STRING = "...A......._A.";

std::vector<scheduler_t::input_workload_t> sched_inputs;
sched_inputs.emplace_back(std::move(readers));
scheduler_t::scheduler_options_t sched_ops(scheduler_t::MAP_TO_ANY_OUTPUT,
scheduler_t::DEPENDENCY_TIMESTAMPS,
scheduler_t::SCHEDULER_DEFAULTS,
/*verbosity=*/3);
// We use our mock's time==instruction count for a deterministic result.
sched_ops.quantum_unit = scheduler_t::QUANTUM_TIME;
sched_ops.time_units_per_us = 1.;
sched_ops.block_time_multiplier = BLOCK_SCALE;
scheduler_t scheduler;
if (scheduler.init(sched_inputs, NUM_OUTPUTS, std::move(sched_ops)) !=
scheduler_t::STATUS_SUCCESS)
assert(false);
std::vector<std::string> sched_as_string =
run_lockstep_simulation(scheduler, NUM_OUTPUTS, TID_A, /*send_time=*/true);
for (int i = 0; i < NUM_OUTPUTS; i++) {
std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n";
}
assert(sched_as_string[0] == CORE0_SCHED_STRING);
}
}

static void
test_unscheduled_no_alternative()
{
// Test that an unscheduled 0-timeout input is not incorrectly executed if
// there is nothing else to run (i#6959).
std::cerr << "\n----------------\nTesting unscheduled no alternative (i#6959)\n";
static constexpr int NUM_OUTPUTS = 1;
static constexpr uint64_t REBALANCE_PERIOD_US = 50;
static constexpr memref_tid_t TID_A = 100;
std::vector<trace_entry_t> refs_A = {
make_thread(TID_A),
make_pid(1),
make_version(TRACE_ENTRY_VERSION),
make_timestamp(1001),
make_marker(TRACE_MARKER_TYPE_CPU_ID, 0),
make_instr(/*pc=*/101),
make_timestamp(1002),
make_marker(TRACE_MARKER_TYPE_CPU_ID, 0),
make_marker(TRACE_MARKER_TYPE_SYSCALL, 999),
make_marker(TRACE_MARKER_TYPE_MAYBE_BLOCKING_SYSCALL, 0),
// No timeout means infinite (until the fallback kicks in).
make_marker(TRACE_MARKER_TYPE_SYSCALL_UNSCHEDULE, 0),
make_timestamp(2002),
make_instr(/*pc=*/102),
make_exit(TID_A),
};
{
std::vector<scheduler_t::input_reader_t> readers;
readers.emplace_back(std::unique_ptr<mock_reader_t>(new mock_reader_t(refs_A)),
std::unique_ptr<mock_reader_t>(new mock_reader_t()), TID_A);
static const char *const CORE0_SCHED_STRING =
"...A......__________________________________________________A.";

std::vector<scheduler_t::input_workload_t> sched_inputs;
sched_inputs.emplace_back(std::move(readers));
scheduler_t::scheduler_options_t sched_ops(scheduler_t::MAP_TO_ANY_OUTPUT,
scheduler_t::DEPENDENCY_TIMESTAMPS,
scheduler_t::SCHEDULER_DEFAULTS,
/*verbosity=*/3);
// We use our mock's time==instruction count for a deterministic result.
sched_ops.quantum_unit = scheduler_t::QUANTUM_TIME;
sched_ops.time_units_per_us = 1.;
sched_ops.rebalance_period_us = REBALANCE_PERIOD_US;
scheduler_t scheduler;
if (scheduler.init(sched_inputs, NUM_OUTPUTS, std::move(sched_ops)) !=
scheduler_t::STATUS_SUCCESS)
assert(false);
std::vector<std::string> sched_as_string =
run_lockstep_simulation(scheduler, NUM_OUTPUTS, TID_A, /*send_time=*/true);
for (int i = 0; i < NUM_OUTPUTS; i++) {
std::cerr << "cpu #" << i << " schedule: " << sched_as_string[i] << "\n";
}
assert(sched_as_string[0] == CORE0_SCHED_STRING);
}
}

static void
test_unscheduled()
{
test_unscheduled_base();
test_unscheduled_fallback();
test_unscheduled_initially();
test_unscheduled_initially_roi();
test_unscheduled_small_timeout();
test_unscheduled_no_alternative();
}

static void
test_kernel_switch_sequences()
{
Expand Down Expand Up @@ -5802,9 +5925,6 @@ test_main(int argc, const char *argv[])
test_inactive();
test_direct_switch();
test_unscheduled();
test_unscheduled_fallback();
test_unscheduled_initially();
test_unscheduled_initially_roi();
test_kernel_switch_sequences();
test_random_schedule();
test_record_scheduler();
Expand Down
Loading