Skip to content

Commit

Permalink
Detect when processes are not yielding. (#2084)
Browse files Browse the repository at this point in the history
  • Loading branch information
floitsch authored Feb 13, 2024
1 parent 68c088b commit da823cd
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 7 deletions.
12 changes: 12 additions & 0 deletions src/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,15 @@ class Process : public ProcessListFromProcessGroup::Element,
return root_certificates_;
}

/// Sets the timestamp for when this process was last preempted.
void set_run_timestamp(uint64 timestamp) {
run_timestamp_ = timestamp;
}

uint64 run_timestamp() const {
return run_timestamp_;
}

private:
Process(Program* program, ProcessRunner* runner, ProcessGroup* group, SystemMessage* termination, InitialMemoryManager* initial_memory);
void _append_message(Message* message);
Expand Down Expand Up @@ -318,6 +327,9 @@ class Process : public ProcessListFromProcessGroup::Element,
HeapObject* null_;

ResourceGroupListFromProcess resource_groups_;

uint64 run_timestamp_ = 0;

friend class HeapObject;
friend class Scheduler;
};
Expand Down
25 changes: 18 additions & 7 deletions src/scheduler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@

namespace toit {

// The maximum amount of us a process is allowed to run without preemption.
#ifdef TOIT_FREERTOS
const uint64 MAX_RUN_WITHOUT_PREEMPTION_US = 2000000;
#else
const uint64 MAX_RUN_WITHOUT_PREEMPTION_US = 10000000;
#endif

void SchedulerThread::entry() {
scheduler_->run(this);
}
Expand Down Expand Up @@ -624,6 +631,7 @@ void Scheduler::run_process(Locker& locker, Process* process, SchedulerThread* s
Interpreter* interpreter = scheduler_thread->interpreter();
interpreter->activate(process);
process->set_idle_since_gc(false);
process->set_run_timestamp(OS::get_monotonic_time());
if (process->signals() == 0) {
Unlocker unlock(locker);
result = interpreter->run();
Expand Down Expand Up @@ -885,26 +893,29 @@ void Scheduler::terminate_execution(Locker& locker, ExitState exit) {
void Scheduler::tick(Locker& locker, int64 now) {
tick_schedule(locker, now, true);

int first_non_empty_ready_queue = -1;
int first_non_empty_ready_queue = NUMBER_OF_READY_QUEUES;
for (int i = 0; i < NUMBER_OF_READY_QUEUES; i++) {
if (ready_queue_[i].is_empty()) continue;
first_non_empty_ready_queue = i;
break;
}

bool any_profiling = num_profiled_processes_ > 0;
if (!any_profiling && first_non_empty_ready_queue < 0) {
// No need to do preemption when there are no active profilers
// and no other processes ready to run.
return;
}

for (SchedulerThread* thread : threads_) {
Process* process = thread->interpreter()->process();
if (process == null) continue;
uint64 us_since_preemption = now - process->run_timestamp();
if (process->signals() & Process::PREEMPT) {
// The process is already suppossed to preempt.
// Check whether it is stuck.
if (us_since_preemption <= MAX_RUN_WITHOUT_PREEMPTION_US) continue;
FATAL("Potential dead-lock detected in process %d\n", process->id());
}
int ready_queue_index = compute_ready_queue_index(process->priority());
bool is_profiling = any_profiling && process->profiler() != null;
if (is_profiling || ready_queue_index >= first_non_empty_ready_queue) {
bool has_run_too_long = us_since_preemption > MAX_RUN_WITHOUT_PREEMPTION_US * 2 / 3;
if (has_run_too_long || is_profiling || ready_queue_index >= first_non_empty_ready_queue) {
process->signal(Process::PREEMPT);
}
}
Expand Down

0 comments on commit da823cd

Please sign in to comment.