1
1
#![ feature( const_fn) ]
2
2
#![ feature( thread_local) ]
3
3
#![ feature( external_doc) ]
4
+ #![ feature( deadline_api) ]
4
5
#![ feature( unsafe_block_in_unsafe_fn) ] // `unsafe fn` doesn't imply `unsafe {}`
5
6
#![ doc( include = "./lib.md" ) ]
6
7
#![ deny( unsafe_op_in_unsafe_fn) ]
@@ -16,7 +17,11 @@ use constance::{
16
17
} ;
17
18
use once_cell:: sync:: OnceCell ;
18
19
use parking_lot:: { lock_api:: RawMutex , Mutex } ;
19
- use std:: { cell:: Cell , time:: Instant } ;
20
+ use std:: {
21
+ cell:: Cell ,
22
+ sync:: mpsc,
23
+ time:: { Duration , Instant } ,
24
+ } ;
20
25
21
26
#[ cfg( unix) ]
22
27
#[ path = "threading_unix.rs" ]
@@ -53,6 +58,12 @@ pub const INTERRUPT_LINE_DISPATCH: InterruptNum = 1023;
53
58
/// The default interrupt priority for [`INTERRUPT_LINE_DISPATCH`].
54
59
pub const INTERRUPT_PRIORITY_DISPATCH : InterruptPriority = 16384 ;
55
60
61
+ /// The (software) interrupt line used for timer interrupts.
62
+ pub const INTERRUPT_LINE_TIMER : InterruptNum = 1022 ;
63
+
64
+ /// The default interrupt priority for [`INTERRUPT_LINE_TIMER`].
65
+ pub const INTERRUPT_PRIORITY_TIMER : InterruptPriority = 16383 ;
66
+
56
67
/// Implemented on a system type by [`use_port!`].
57
68
///
58
69
/// # Safety
@@ -73,6 +84,7 @@ pub unsafe trait PortInstance: Kernel + Port<PortTaskState = TaskState> {
73
84
pub struct State {
74
85
thread_group : OnceCell < ums:: ThreadGroup < sched:: SchedState > > ,
75
86
join_handle : Mutex < Option < ums:: ThreadGroupJoinHandle > > ,
87
+ timer_cmd_send : Mutex < Option < mpsc:: Sender < TimerCmd > > > ,
76
88
origin : AtomicRef < ' static , Instant > ,
77
89
}
78
90
@@ -99,6 +111,10 @@ enum Tsm {
99
111
Running ( ums:: ThreadId ) ,
100
112
}
101
113
114
+ enum TimerCmd {
115
+ SetTimeout { at : Instant } ,
116
+ }
117
+
102
118
/// The role of a thread.
103
119
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
104
120
enum ThreadRole {
@@ -171,20 +187,50 @@ impl State {
171
187
Self {
172
188
thread_group : OnceCell :: new ( ) ,
173
189
join_handle : Mutex :: const_new ( RawMutex :: INIT , None ) ,
190
+ timer_cmd_send : Mutex :: const_new ( RawMutex :: INIT , None ) ,
174
191
origin : AtomicRef :: new ( None ) ,
175
192
}
176
193
}
177
194
178
195
/// Initialize the user-mode scheduling system and boot the kernel.
179
196
///
180
197
/// Returns when the shutdown initiated by [`shutdown`] completes.
181
- pub fn port_boot < System : Kernel > ( & self ) {
198
+ pub fn port_boot < System : PortInstance > ( & self ) {
182
199
// Create a UMS thread group.
183
200
let ( thread_group, join_handle) = ums:: ThreadGroup :: new ( sched:: SchedState :: new :: < System > ( ) ) ;
184
201
185
202
self . thread_group . set ( thread_group) . ok ( ) . unwrap ( ) ;
186
203
* self . join_handle . lock ( ) = Some ( join_handle) ;
187
204
205
+ // Start a timer thread
206
+ let ( timer_cmd_send, timer_cmd_recv) = mpsc:: channel ( ) ;
207
+ log:: trace!( "starting the timer thread" ) ;
208
+ let timer_join_handle = std:: thread:: spawn ( move || {
209
+ let mut next_deadline = None ;
210
+ loop {
211
+ let recv_result = if let Some ( next_deadline) = next_deadline {
212
+ timer_cmd_recv. recv_deadline ( next_deadline)
213
+ } else {
214
+ timer_cmd_recv
215
+ . recv ( )
216
+ . map_err ( |_| mpsc:: RecvTimeoutError :: Disconnected )
217
+ } ;
218
+ match recv_result {
219
+ Err ( mpsc:: RecvTimeoutError :: Disconnected ) => {
220
+ break ;
221
+ }
222
+ Err ( mpsc:: RecvTimeoutError :: Timeout ) => {
223
+ pend_interrupt_line :: < System > ( INTERRUPT_LINE_TIMER ) . unwrap ( ) ;
224
+ next_deadline = None ;
225
+ }
226
+ Ok ( TimerCmd :: SetTimeout { at } ) => {
227
+ next_deadline = Some ( at) ;
228
+ }
229
+ }
230
+ }
231
+ } ) ;
232
+ * self . timer_cmd_send . lock ( ) = Some ( timer_cmd_send) ;
233
+
188
234
// Create the initial UMS worker thread, where the boot phase of the
189
235
// kernel runs
190
236
let mut lock = self . thread_group . get ( ) . unwrap ( ) . lock ( ) ;
@@ -200,11 +246,33 @@ impl State {
200
246
lock. scheduler ( ) . task_thread = Some ( thread_id) ;
201
247
lock. scheduler ( ) . recycle_thread ( thread_id) ;
202
248
lock. preempt ( ) ;
249
+
250
+ // Configure timer interrupt
251
+ lock. scheduler ( )
252
+ . update_line ( INTERRUPT_LINE_TIMER , |line| {
253
+ line. priority = INTERRUPT_PRIORITY_TIMER ;
254
+ line. enable = true ;
255
+ line. start = Some ( Self :: timer_handler :: < System > ) ;
256
+ } )
257
+ . ok ( )
258
+ . unwrap ( ) ;
259
+
203
260
drop ( lock) ;
204
261
205
262
// Wait until the thread group shuts down
206
263
let join_handle = self . join_handle . lock ( ) . take ( ) . unwrap ( ) ;
207
- if let Err ( e) = join_handle. join ( ) {
264
+ let result = join_handle. join ( ) ;
265
+
266
+ // Stop the timer thread.
267
+ // `timer_cmd_recv.recv` will return `Err(_)` when we drop the
268
+ // corresponding sender (`timer_cmd_send`).
269
+ log:: trace!( "stopping the timer thread" ) ;
270
+ * self . timer_cmd_send . lock ( ) = None ;
271
+ timer_join_handle. join ( ) . unwrap ( ) ;
272
+ log:: trace!( "stopped the timer thread" ) ;
273
+
274
+ // Propagate any panic that occured in a worker thread
275
+ if let Err ( e) = result {
208
276
std:: panic:: resume_unwind ( e) ;
209
277
}
210
278
}
@@ -222,6 +290,7 @@ impl State {
222
290
let mut lock = self . thread_group . get ( ) . unwrap ( ) . lock ( ) ;
223
291
224
292
// Configure PendSV
293
+ // TODO: move this (except for `pended = true`) to `port_boot`
225
294
lock. scheduler ( )
226
295
. update_line ( INTERRUPT_LINE_DISPATCH , |line| {
227
296
line. priority = INTERRUPT_PRIORITY_DISPATCH ;
@@ -550,12 +619,36 @@ impl State {
550
619
551
620
pub fn pend_tick_after < System : PortInstance > ( & self , tick_count_delta : UTicks ) {
552
621
expect_worker_thread :: < System > ( ) ;
553
- // TODO
622
+ log:: trace!( "pend_tick_after({:?})" , tick_count_delta) ;
623
+
624
+ // Calculate when `timer_tick` should be called
625
+ let now = Instant :: now ( ) + Duration :: from_micros ( tick_count_delta. into ( ) ) ;
626
+
627
+ // Lock the scheduler because we aren't sure what would happen if
628
+ // `Sender::send` was interrupted
629
+ let _sched_lock = lock_scheduler :: < System > ( ) ;
630
+
631
+ let timer_cmd_send = self . timer_cmd_send . lock ( ) ;
632
+ let timer_cmd_send = timer_cmd_send. as_ref ( ) . unwrap ( ) ;
633
+ timer_cmd_send
634
+ . send ( TimerCmd :: SetTimeout { at : now } )
635
+ . unwrap ( ) ;
554
636
}
555
637
556
- pub fn pend_tick < System : PortInstance > ( & self ) {
638
+ pub fn pend_tick < System : PortInstance > ( & ' static self ) {
557
639
expect_worker_thread :: < System > ( ) ;
558
- // TODO
640
+ log:: trace!( "pend_tick" ) ;
641
+
642
+ self . pend_interrupt_line :: < System > ( INTERRUPT_LINE_TIMER )
643
+ . unwrap ( ) ;
644
+ }
645
+
646
+ extern "C" fn timer_handler < System : PortInstance > ( ) {
647
+ assert_eq ! ( expect_worker_thread:: <System >( ) , ThreadRole :: Interrupt ) ;
648
+ log:: trace!( "timer_handler" ) ;
649
+
650
+ // Safety: CPU Lock inactive, an interrupt context
651
+ unsafe { <System as PortToKernel >:: timer_tick ( ) } ;
559
652
}
560
653
}
561
654
0 commit comments