@@ -8,48 +8,65 @@ use std::{
8
8
9
9
use async_task:: { Runnable , Task } ;
10
10
11
- use crate :: DroppableFuture ;
11
+ use crate :: { DroppableFuture , TaskIdentifier } ;
12
+
13
+ #[ derive( Debug ) ]
14
+ pub enum TaskState {
15
+ Spawn ( TaskIdentifier ) ,
16
+ Wake ( TaskIdentifier ) ,
17
+ Tick ( TaskIdentifier ) ,
18
+ Drop ( TaskIdentifier ) ,
19
+ }
20
+
21
+ type Payload = ( TaskIdentifier , Runnable ) ;
12
22
13
- pub struct TickedAsyncExecutor {
14
- channel : ( mpsc:: Sender < Runnable > , mpsc:: Receiver < Runnable > ) ,
23
+ pub struct TickedAsyncExecutor < O > {
24
+ channel : ( mpsc:: Sender < Payload > , mpsc:: Receiver < Payload > ) ,
15
25
num_woken_tasks : Arc < AtomicUsize > ,
16
26
num_spawned_tasks : Arc < AtomicUsize > ,
27
+ observer : O ,
17
28
}
18
29
19
- impl Default for TickedAsyncExecutor {
20
- fn default ( ) -> Self {
21
- Self :: new ( )
22
- }
23
- }
24
-
25
- // TODO, Observer: Task spawn/wake/drop events
26
- // TODO, Task Identifier String
27
- impl TickedAsyncExecutor {
28
- pub fn new ( ) -> Self {
30
+ impl < O > TickedAsyncExecutor < O >
31
+ where
32
+ O : Fn ( TaskState ) + Clone + Send + Sync + ' static ,
33
+ {
34
+ pub fn new ( observer : O ) -> Self {
29
35
Self {
30
36
channel : mpsc:: channel ( ) ,
31
37
num_woken_tasks : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
32
38
num_spawned_tasks : Arc :: new ( AtomicUsize :: new ( 0 ) ) ,
39
+ observer,
33
40
}
34
41
}
35
42
36
- pub fn spawn < T > ( & self , future : impl Future < Output = T > + Send + ' static ) -> Task < T >
43
+ pub fn spawn < T > (
44
+ & self ,
45
+ identifier : impl Into < TaskIdentifier > ,
46
+ future : impl Future < Output = T > + Send + ' static ,
47
+ ) -> Task < T >
37
48
where
38
49
T : Send + ' static ,
39
50
{
40
- let future = self . droppable_future ( future) ;
41
- let schedule = self . runnable_schedule_cb ( ) ;
51
+ let identifier = identifier. into ( ) ;
52
+ let future = self . droppable_future ( identifier. clone ( ) , future) ;
53
+ let schedule = self . runnable_schedule_cb ( identifier) ;
42
54
let ( runnable, task) = async_task:: spawn ( future, schedule) ;
43
55
runnable. schedule ( ) ;
44
56
task
45
57
}
46
58
47
- pub fn spawn_local < T > ( & self , future : impl Future < Output = T > + ' static ) -> Task < T >
59
+ pub fn spawn_local < T > (
60
+ & self ,
61
+ identifier : impl Into < TaskIdentifier > ,
62
+ future : impl Future < Output = T > + ' static ,
63
+ ) -> Task < T >
48
64
where
49
65
T : ' static ,
50
66
{
51
- let future = self . droppable_future ( future) ;
52
- let schedule = self . runnable_schedule_cb ( ) ;
67
+ let identifier = identifier. into ( ) ;
68
+ let future = self . droppable_future ( identifier. clone ( ) , future) ;
69
+ let schedule = self . runnable_schedule_cb ( identifier) ;
53
70
let ( runnable, task) = async_task:: spawn_local ( future, schedule) ;
54
71
runnable. schedule ( ) ;
55
72
task
@@ -68,91 +85,98 @@ impl TickedAsyncExecutor {
68
85
. 1
69
86
. try_iter ( )
70
87
. take ( num_woken_tasks)
71
- . for_each ( |runnable| {
88
+ . for_each ( |( identifier, runnable) | {
89
+ ( self . observer ) ( TaskState :: Tick ( identifier) ) ;
72
90
runnable. run ( ) ;
73
91
} ) ;
74
92
self . num_woken_tasks
75
93
. fetch_sub ( num_woken_tasks, Ordering :: Relaxed ) ;
76
94
}
77
95
78
- fn droppable_future < F > ( & self , future : F ) -> DroppableFuture < F , impl Fn ( ) >
96
+ fn droppable_future < F > (
97
+ & self ,
98
+ identifier : TaskIdentifier ,
99
+ future : F ,
100
+ ) -> DroppableFuture < F , impl Fn ( ) >
79
101
where
80
102
F : Future ,
81
103
{
104
+ let observer = self . observer . clone ( ) ;
105
+
106
+ // Spawn Task
82
107
self . num_spawned_tasks . fetch_add ( 1 , Ordering :: Relaxed ) ;
108
+ observer ( TaskState :: Spawn ( identifier. clone ( ) ) ) ;
109
+
110
+ // Droppable Future registering on_drop callback
83
111
let num_spawned_tasks = self . num_spawned_tasks . clone ( ) ;
84
112
DroppableFuture :: new ( future, move || {
85
113
num_spawned_tasks. fetch_sub ( 1 , Ordering :: Relaxed ) ;
114
+ observer ( TaskState :: Drop ( identifier. clone ( ) ) ) ;
86
115
} )
87
116
}
88
117
89
- fn runnable_schedule_cb ( & self ) -> impl Fn ( Runnable ) {
118
+ fn runnable_schedule_cb ( & self , identifier : TaskIdentifier ) -> impl Fn ( Runnable ) {
90
119
let sender = self . channel . 0 . clone ( ) ;
91
120
let num_woken_tasks = self . num_woken_tasks . clone ( ) ;
121
+ let observer = self . observer . clone ( ) ;
92
122
move |runnable| {
93
- sender. send ( runnable) . unwrap_or ( ( ) ) ;
123
+ sender. send ( ( identifier . clone ( ) , runnable) ) . unwrap_or ( ( ) ) ;
94
124
num_woken_tasks. fetch_add ( 1 , Ordering :: Relaxed ) ;
125
+ observer ( TaskState :: Wake ( identifier. clone ( ) ) ) ;
95
126
}
96
127
}
97
128
}
98
129
99
130
#[ cfg( test) ]
100
131
mod tests {
132
+ use tokio:: join;
133
+
101
134
use super :: * ;
102
135
103
136
#[ test]
104
137
fn test_multiple_tasks ( ) {
105
- let executor = TickedAsyncExecutor :: new ( ) ;
138
+ let executor = TickedAsyncExecutor :: new ( |_state| { } ) ;
106
139
executor
107
- . spawn_local ( async move {
108
- println ! ( "A: Start" ) ;
140
+ . spawn_local ( "A" , async move {
109
141
tokio:: task:: yield_now ( ) . await ;
110
- println ! ( "A: End" ) ;
111
142
} )
112
143
. detach ( ) ;
113
144
114
145
executor
115
- . spawn_local ( async move {
116
- println ! ( "B: Start" ) ;
146
+ . spawn_local ( format ! ( "B" ) , async move {
117
147
tokio:: task:: yield_now ( ) . await ;
118
- println ! ( "B: End" ) ;
119
148
} )
120
149
. detach ( ) ;
121
150
122
- // A, B, C: Start
123
151
executor. tick ( ) ;
124
152
assert_eq ! ( executor. num_tasks( ) , 2 ) ;
125
153
126
- // A, B, C: End
127
154
executor. tick ( ) ;
128
155
assert_eq ! ( executor. num_tasks( ) , 0 ) ;
129
156
}
130
157
131
158
#[ test]
132
159
fn test_task_cancellation ( ) {
133
- let executor = TickedAsyncExecutor :: new ( ) ;
134
- let task1 = executor. spawn_local ( async move {
160
+ let executor = TickedAsyncExecutor :: new ( |_state| println ! ( "{_state:?}" ) ) ;
161
+ let task1 = executor. spawn_local ( "A" , async move {
135
162
loop {
136
- println ! ( "A: Start" ) ;
137
163
tokio:: task:: yield_now ( ) . await ;
138
- println ! ( "A: End" ) ;
139
164
}
140
165
} ) ;
141
166
142
- let task2 = executor. spawn_local ( async move {
167
+ let task2 = executor. spawn_local ( format ! ( "B" ) , async move {
143
168
loop {
144
- println ! ( "B: Start" ) ;
145
169
tokio:: task:: yield_now ( ) . await ;
146
- println ! ( "B: End" ) ;
147
170
}
148
171
} ) ;
149
172
assert_eq ! ( executor. num_tasks( ) , 2 ) ;
150
173
executor. tick ( ) ;
151
174
152
175
executor
153
- . spawn_local ( async move {
154
- task1. cancel ( ) . await ;
155
- task2. cancel ( ) . await ;
176
+ . spawn_local ( "CancelTasks" , async move {
177
+ let ( t1, t2) = join ! ( task1. cancel( ) , task2. cancel( ) ) ;
178
+ assert_eq ! ( t1, None ) ;
179
+ assert_eq ! ( t2, None ) ;
156
180
} )
157
181
. detach ( ) ;
158
182
assert_eq ! ( executor. num_tasks( ) , 3 ) ;
0 commit comments