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