1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
//! Exposes testing, profiling and tracing capabilities.
use std::{fmt, time};
use crate::util::DbgTypeId;
use crate::{scheduler, system};
/// A handler that receives scheduling-related events in dynec.
///
/// New implementations should use [`#[tracer]`](crate::tracer)
/// to auto implement required types and methods for future compatibility.
#[dynec_codegen::tracer_def(
max_tuple_len = 32,
import = crate::util::DbgTypeId,
import = crate::scheduler,
import = crate::system,
)]
pub trait Tracer: Sync {
/// Context from [`start_cycle`](Self::start_cycle) to [`end_cycle`](Self::end_cycle).
#[dynec(log_time)]
type CycleContext;
/// A cycle starts.
#[dynec(log_return_now)]
fn start_cycle(&self) -> Self::CycleContext;
/// A cycle ends.
fn end_cycle(&self, #[dynec(log_with = ElapsedFmt)] arg: Self::CycleContext);
/// Context from [`start_prepare_ealloc_shards`](Self::start_prepare_ealloc_shards)
/// to [`end_prepare_ealloc_shards`](Self::end_prepare_ealloc_shards).
#[dynec(log_time)]
type PrepareEallocShardsContext;
/// The executor starts preparing ealloc shards for each worker thread.
#[dynec(log_return_now)]
fn start_prepare_ealloc_shards(&self) -> Self::PrepareEallocShardsContext;
/// The executor has partitioned ealloc into different worker threads.
fn end_prepare_ealloc_shards(
&self,
#[dynec(log_with = ElapsedFmt)] arg: Self::PrepareEallocShardsContext,
);
/// Context from [`start_flush_ealloc`](Self::start_flush_ealloc)
/// to [`end_flush_ealloc`](Self::end_flush_ealloc).
#[dynec(log_time)]
type FlushEallocContext;
/// The executor starts preparing ealloc shards for each worker thread.
#[dynec(log_return_now)]
fn start_flush_ealloc(&self, archetype: DbgTypeId) -> Self::FlushEallocContext;
/// The executor has partitioned ealloc into different worker threads.
fn end_flush_ealloc(
&self,
#[dynec(log_with = ElapsedFmt)] arg: Self::FlushEallocContext,
archetype: DbgTypeId,
);
/// A thread tries to steal a task, but all tasks have started.
fn steal_return_complete(&self, thread: Thread);
/// A thread tries to steal a task, but no tasks are in the runnable pool.
fn steal_return_pending(&self, thread: Thread);
/// A node is marked as runnable because all blockers have been removed.
fn mark_runnable(&self, node: scheduler::Node);
/// A node is unmarked as runnable because an exclusive node has been stolen.
fn unmark_runnable(&self, node: scheduler::Node);
/// A system has completed. Also passes the number of remaining nodes.
fn complete_system(&self, node: scheduler::Node, remaining: usize);
/// Context from [`start_run_sendable`](Self::start_run_sendable)
/// to [`end_run_sendable`](Self::end_run_sendable).
#[dynec(log_time)]
type RunSendableContext;
/// A thread-safe system starts running.
#[dynec(log_return_now)]
fn start_run_sendable(
&self,
thread: Thread,
node: scheduler::Node,
debug_name: &str,
#[dynec(log_skip)] system: &mut dyn system::Sendable,
) -> Self::RunSendableContext;
/// A thread-safe system stops running.
fn end_run_sendable(
&self,
#[dynec(log_with = ElapsedFmt)] context: Self::RunSendableContext,
thread: Thread,
node: scheduler::Node,
debug_name: &str,
#[dynec(log_skip)] system: &mut dyn system::Sendable,
);
/// Context from [`start_run_unsendable`](Self::start_run_unsendable)
/// to [`end_run_unsendable`](Self::end_run_unsendable).
#[dynec(log_time)]
type RunUnsendableContext;
/// A thread-unsafe system starts running.
#[dynec(log_return_now)]
fn start_run_unsendable(
&self,
thread: Thread,
node: scheduler::Node,
debug_name: &str,
#[dynec(log_skip)] system: &mut dyn system::Unsendable,
) -> Self::RunUnsendableContext;
/// A thread-unsafe system stops running.
fn end_run_unsendable(
&self,
#[dynec(log_with = ElapsedFmt)] context: Self::RunUnsendableContext,
thread: Thread,
node: scheduler::Node,
debug_name: &str,
#[dynec(log_skip)] system: &mut dyn system::Unsendable,
);
/// A partition completes.
fn partition(
&self,
node: scheduler::Node,
#[dynec(log_with = PartitionFmt)] partition: &dyn system::Partition,
);
}
struct PartitionFmt<'t>(&'t dyn system::Partition);
impl<'t> fmt::Display for PartitionFmt<'t> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.describe(f) }
}
struct ElapsedFmt(time::Instant);
impl fmt::Display for ElapsedFmt {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self.0.elapsed()) }
}
/// An empty tracer.
pub struct Noop;
/// Groups multiple tracers into a tuple and dispatches each call to them in serial.
pub struct Aggregate<T>(
/// A tuple of child tracers to execute in serial.
pub T,
);
/// A tracer that logs all events.
pub struct Log(
/// The log level to log events with.
pub log::Level,
);
/// The thread ID for a system executor.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Thread {
/// The main thread, typically used for executing thread-unsafe systems.
Main,
/// A worker thread. The index is in the range `0..concurrency`.
Worker(usize),
}