ravel
Deterministic simulation testing for C++. Seed a bug, replay it exact.
Loading...
Searching...
No Matches
Classes | Public Member Functions | List of all members
ravel::Scheduler Class Reference

Cooperative, single-threaded task scheduler. More...

#include <scheduler.hpp>

Classes

class  SleepAwaiter
 What sleep() returns. It is only ever co_awaited. More...
 
class  YieldAwaiter
 What yield() returns. It is only ever co_awaited. More...
 

Public Member Functions

 Scheduler (VirtualClock &clock, VirtualRng &rng, Trace &trace) noexcept
 Created by Simulation; you do not construct one yourself.
 
 Scheduler (const Scheduler &)=delete
 Tasks hold references into the scheduler, so it must never move.
 
Scheduler & operator= (const Scheduler &)=delete
 
void spawn (std::string name, TaskFactory factory)
 Registers a task.
 
YieldAwaiter yield () noexcept
 co_await scheduler.yield(); lets the scheduler pick who runs next.
 
SleepAwaiter sleep (VirtualClock::Tick duration) noexcept
 co_await scheduler.sleep(50); suspends the task for 50 virtual ticks.
 
RunReport run_until_quiescent (std::uint64_t max_steps, VirtualClock::Tick time_limit=std::numeric_limits< VirtualClock::Tick >::max())
 Runs until nothing is left to happen, a task throws, max_steps steps have been taken, or the next timer falls after time_limit on the virtual clock.
 
const std::string & task_name (TaskId id) const
 The name a task was spawned with.
 
VirtualClock::Tick now () const noexcept
 The current virtual time.
 
TaskId current_task () const noexcept
 The task being resumed right now. Valid only inside a task.
 
void make_runnable (TaskId id)
 Lets a suspended task run again on a later step.
 
void call_after (VirtualClock::Tick delay, std::function< void()> action)
 Runs action once the clock reaches now() + delay.
 

Detailed Description

Cooperative, single-threaded task scheduler.

At every step it resumes one runnable task chosen by the seeded VirtualRng, so which task runs when depends only on the seed: never on wall-clock timing, thread scheduling, or hash-table iteration order. That is what makes a failing seed replayable, and what explores different interleavings across different seeds.

A simulation is single-threaded: while it runs, spawn() and call_after() (and so Channel::send and Disk operations) throw std::logic_error if called from any thread other than the one running it. That catches code under test that starts real threads and touches the simulation from them.

Time is virtual. When every task is asleep the clock jumps straight to the earliest wake-up, so simulated waiting costs no real time.

Member Function Documentation

◆ call_after()

void ravel::Scheduler::call_after ( VirtualClock::Tick  delay,
std::function< void()>  action 
)

Runs action once the clock reaches now() + delay.

Actions due at the same time run in the order they were scheduled.

◆ spawn()

void ravel::Scheduler::spawn ( std::string  name,
TaskFactory  factory 
)

Registers a task.

It first runs on a later step of run_until_quiescent() (or on the current run, if called from inside a task).


The documentation for this class was generated from the following file: