(* * Copyright 2014, General Dynamics C4 Systems * * SPDX-License-Identifier: GPL-2.0-only *) (* Non-deterministic scheduler functionality. *) chapter "Scheduler" theory Schedule_A imports Arch_A begin context begin interpretation Arch . requalify_consts arch_switch_to_thread arch_switch_to_idle_thread end abbreviation "idle st \ st = Structures_A.IdleThreadState" text \Gets the TCB at an address if the thread can be scheduled.\ definition getActiveTCB :: "obj_ref \ 'z::state_ext state \ tcb option" where "getActiveTCB tcb_ref state \ case (get_tcb tcb_ref state) of None \ None | Some tcb \ if (runnable $ tcb_state tcb) then Some tcb else None" text \Gets all schedulable threads in the system.\ definition allActiveTCBs :: "(obj_ref set,'z::state_ext) s_monad" where "allActiveTCBs \ do state \ get; return {x. getActiveTCB x state \ None} od" text \Switches the current thread to the specified one.\ definition switch_to_thread :: "obj_ref \ (unit,'z::state_ext) s_monad" where "switch_to_thread t \ do state \ get; assert (get_tcb t state \ None); arch_switch_to_thread t; do_extended_op (tcb_sched_action (tcb_sched_dequeue) t); modify (\s. s \ cur_thread := t \) od" text \Asserts that a thread is runnable before switching to it.\ definition guarded_switch_to :: "obj_ref \ (unit,'z::state_ext) s_monad" where "guarded_switch_to thread \ do ts \ get_thread_state thread; assert (runnable ts); switch_to_thread thread od" text \Switches to the idle thread.\ definition switch_to_idle_thread :: "(unit,'z::state_ext) s_monad" where "switch_to_idle_thread \ do thread \ gets idle_thread; arch_switch_to_idle_thread; modify (\s. s \ cur_thread := thread \) od" class state_ext_sched = state_ext + fixes schedule :: "(unit,'a) s_monad" definition choose_thread :: "det_ext state \ (unit \ det_ext state) set \ bool" where "choose_thread \ do d \ gets cur_domain; queues \ gets (\s. ready_queues s d); if (\prio. queues prio = []) then (switch_to_idle_thread) else (guarded_switch_to (hd (max_non_empty_queue queues))) od" text \ Determine whether given priority is highest among queued ready threads in given domain. Trivially true if no threads are ready.\ definition is_highest_prio :: "domain \ priority \ det_ext state \ bool" where "is_highest_prio d p s \ (\prio. ready_queues s d prio = []) \ p \ Max {prio. ready_queues s d prio \ []}" instantiation det_ext_ext :: (type) state_ext_sched begin definition "schedule_switch_thread_fastfail ct it ct_prio target_prio \ if ct \ it then return (target_prio < ct_prio) else return True" definition "schedule_choose_new_thread \ do dom_time \ gets domain_time; when (dom_time = 0) next_domain; choose_thread; set_scheduler_action resume_cur_thread od" definition "schedule_det_ext_ext \ do ct \ gets cur_thread; ct_st \ get_thread_state ct; ct_runnable \ return $ runnable ct_st; action \ gets scheduler_action; (case action of resume_cur_thread \ do id \ gets idle_thread; assert (ct_runnable \ ct = id); return () od | choose_new_thread \ do when ct_runnable (tcb_sched_action tcb_sched_enqueue ct); schedule_choose_new_thread od | switch_thread candidate \ do when ct_runnable (tcb_sched_action tcb_sched_enqueue ct); it \ gets idle_thread; target_prio \ ethread_get tcb_priority candidate; \ \Infoflow does not like asking about the idle thread's priority or domain.\ ct_prio \ ethread_get_when (ct \ it) tcb_priority ct; \ \When to look at the bitmaps. This optimisation is used in the C fast path, but there we know @{text cur_thread} is not idle.\ fastfail \ schedule_switch_thread_fastfail ct it ct_prio target_prio; cur_dom \ gets cur_domain; highest \ gets (is_highest_prio cur_dom target_prio); if (fastfail \ \highest) then do \ \Candidate is not best candidate, choose a new thread\ tcb_sched_action tcb_sched_enqueue candidate; set_scheduler_action choose_new_thread; schedule_choose_new_thread od else if (ct_runnable \ ct_prio = target_prio) then do \ \Current thread was runnable and candidate is not strictly better want current thread to run next, so append the candidate to end of queue and choose again\ tcb_sched_action tcb_sched_append candidate; set_scheduler_action choose_new_thread; schedule_choose_new_thread od else do guarded_switch_to candidate; \ \Duplication assists in wp proof under different scheduler actions\ set_scheduler_action resume_cur_thread od od) od" instance .. end instantiation unit :: state_ext_sched begin text \ The scheduler is heavily underspecified. It is allowed to pick any active thread or the idle thread. If the thread the scheduler picked is the current thread, it may omit the call to @{const switch_to_thread}. Likewise it may omit the call to @{const switch_to_idle_thread} if the idle thread is the current thread. \ definition schedule_unit :: "(unit,unit) s_monad" where "schedule_unit \ (do cur \ gets cur_thread; threads \ allActiveTCBs; thread \ select threads; (if thread = cur then return () \ switch_to_thread thread else switch_to_thread thread) od) \ (do cur \ gets cur_thread; idl \ gets idle_thread; if idl = cur then return () \ switch_to_idle_thread else switch_to_idle_thread od)" instance .. end lemmas schedule_def = schedule_det_ext_ext_def schedule_unit_def end