1use crate::runtime::failure::{init_panic_hook, persist_failure};
2use crate::runtime::storage::{StorageKey, StorageMap};
3use crate::runtime::task::clock::VectorClock;
4use crate::runtime::task::labels::Labels;
5use crate::runtime::task::{ChildLabelFn, Task, TaskId, TaskName, TaskSignature, DEFAULT_INLINE_TASKS};
6use crate::runtime::thread;
7use crate::runtime::thread::continuation::PooledContinuation;
8use crate::scheduler::{Schedule, Scheduler};
9use crate::sync_types::{ResourceSignature, ResourceType};
10use crate::thread_support::thread_fn;
11use crate::{backtrace_enabled, Config, MaxSteps, UNGRACEFUL_SHUTDOWN_CONFIG};
12use scoped_tls::scoped_thread_local;
13use smallvec::SmallVec;
14use std::any::Any;
15use std::cell::RefCell;
16use std::collections::HashMap;
17use std::fmt::Debug;
18use std::future::Future;
19use std::panic::{self, Location};
20use std::rc::Rc;
21use std::sync::Arc;
22use tracing::{trace, Span};
23
24#[allow(deprecated)]
25use super::task::Tag;
26
27scoped_thread_local! {
30 static EXECUTION_STATE: RefCell<ExecutionState>
31}
32
33thread_local! {
41 static CURRENT_SCHEDULE: CurrentSchedule = CurrentSchedule::default();
42}
43
44#[derive(Debug, Default)]
45pub struct CurrentSchedule {
46 current_schedule: RefCell<Schedule>,
47}
48
49impl CurrentSchedule {
50 fn init(schedule: Schedule) {
51 CURRENT_SCHEDULE.with(|cs| *cs.current_schedule.borrow_mut() = schedule)
52 }
53
54 fn push_task(tid: TaskId) {
56 CURRENT_SCHEDULE.with(|cs| cs.current_schedule.borrow_mut().push_task(tid))
57 }
58
59 fn push_random() {
61 CURRENT_SCHEDULE.with(|cs| cs.current_schedule.borrow_mut().push_random())
62 }
63
64 pub fn len() -> usize {
66 CURRENT_SCHEDULE.with(|cs| (*cs.current_schedule.borrow()).len())
67 }
68
69 pub fn get_schedule() -> Schedule {
71 CURRENT_SCHEDULE.with(|cs| (*cs.current_schedule.borrow()).clone())
72 }
73}
74
75thread_local! {
76 #[allow(clippy::complexity)]
77 #[allow(deprecated)]
78 pub static TASK_ID_TO_TAGS: RefCell<HashMap<TaskId, Arc<dyn Tag>>> = RefCell::new(HashMap::new());
79}
80
81thread_local! {
82 pub static LABELS: RefCell<HashMap<TaskId, Labels>> = RefCell::new(HashMap::new());
83}
84
85pub struct Execution {
93 scheduler: Rc<RefCell<dyn Scheduler>>,
94 initial_schedule: Schedule,
95}
96
97impl std::fmt::Debug for Execution {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 f.debug_struct("Execution").finish_non_exhaustive()
100 }
101}
102
103impl Execution {
104 pub fn new(scheduler: Rc<RefCell<dyn Scheduler>>, initial_schedule: Schedule) -> Self {
107 Self {
108 scheduler,
109 initial_schedule,
110 }
111 }
112}
113
114#[derive(Debug)]
115enum StepError {
116 TaskFailure(Box<dyn Any + Send>),
118 SchedulingError,
120 Deadlock,
122 StepBoundExceeded,
124 TaskPanicEarlyReturn,
126}
127
128impl StepError {
129 fn persist_failure(&self, config: &Config) {
130 if let StepError::StepBoundExceeded = self {
131 if let MaxSteps::ContinueAfter(_) = config.max_steps {
132 return;
133 }
134 }
135 persist_failure(config);
136 }
137}
138
139impl Execution {
140 pub fn run<F>(mut self, config: &Config, f: F, caller: &'static Location<'static>)
144 where
145 F: FnOnce() + Send + 'static,
146 {
147 let state = RefCell::new(ExecutionState::new(config.clone(), Rc::clone(&self.scheduler)));
148
149 init_panic_hook(config.clone());
150 CurrentSchedule::init(self.initial_schedule.clone());
151 UNGRACEFUL_SHUTDOWN_CONFIG.set(config.ungraceful_shutdown_config);
152
153 EXECUTION_STATE.set(&state, move || {
154 ExecutionState::spawn_main_thread(
156 Box::new(move || thread_fn(f, true, Default::default())),
157 config.stack_size,
158 caller,
159 );
160
161 match self.run_to_completion(UNGRACEFUL_SHUTDOWN_CONFIG.get().immediately_return_on_panic) {
163 Ok(()) => {},
164 Err(e) => {
165 e.persist_failure(config);
166
167 match e {
168 StepError::TaskFailure(payload) => {
169 eprintln!("test panicked in task '{}'", ExecutionState::failing_task());
170
171 panic::resume_unwind(payload);
172 }
173 StepError::Deadlock => {
174 let blocked_tasks = ExecutionState::with(|state|
175 state
176 .tasks
177 .iter()
178 .filter(|t| !t.finished())
179 .map(|t| t.format_for_deadlock())
180 .collect::<Vec<_>>());
181
182 if !backtrace_enabled() {
184 eprintln!("Test deadlocked, and {} is not set. If either of those are set then the backtrace of each task will be collected and printed as part of the panic message.", crate::CAPTURE_BACKTRACE)
185 }
186
187 panic!("deadlock! blocked tasks: [{}]", blocked_tasks.join(", "));
188 }
189 StepError::SchedulingError => panic!("no task was scheduled\nThis indicates an issue with the scheduler."),
190 StepError::StepBoundExceeded => {
191 if let MaxSteps::FailAfter(max_steps) = config.max_steps {
192 panic!("exceeded max_steps bound {max_steps}. this might be caused by an unfair schedule (e.g., a spin loop)?");
193 }
194 }
195 StepError::TaskPanicEarlyReturn => panic::resume_unwind(Box::new("Task panicked, and early return is enabled.")),
196 }
197 }}
198
199
200 ExecutionState::cleanup();
202 });
203 }
204
205 fn enter_task_span() {
206 ExecutionState::with(|state| {
213 tracing::dispatcher::get_default(|subscriber| {
214 if let Some(span_id) = tracing::Span::current().id().as_ref() {
215 subscriber.exit(span_id);
216 }
217
218 while let Some(span) = state.current_mut().span_stack.pop() {
221 if let Some(span_id) = span.id().as_ref() {
222 subscriber.enter(span_id)
223 }
224 }
225
226 if state.config.record_steps_in_span {
227 state.current().step_span.record("i", CurrentSchedule::len());
228 }
229 });
230 });
231 }
232
233 fn exit_task_span() {
234 ExecutionState::with(|state| {
236 tracing::dispatcher::get_default(|subscriber| {
237 debug_assert!(state.current().span_stack.is_empty());
238 while let Some(span_id) = tracing::Span::current().id().as_ref() {
239 state.current_mut().span_stack.push(tracing::Span::current().clone());
240 subscriber.exit(span_id);
241 }
242
243 if let Some(span_id) = state.top_level_span.id().as_ref() {
244 subscriber.enter(span_id)
245 }
246 });
247 });
248 }
249
250 #[inline]
252 fn run_to_completion(&mut self, immediately_return_on_panic: bool) -> Result<(), StepError> {
253 loop {
254 let next_step: Option<Rc<RefCell<PooledContinuation>>> = ExecutionState::with(|state| {
255 state.schedule()?;
256 state.advance_to_next_task();
257
258 match state.current_task {
259 ScheduledTask::Some(tid) => {
260 let task = state.get(tid);
261 Ok(Some(task.continuation.clone()))
262 }
263 ScheduledTask::Finished => {
264 if state.tasks.iter().any(|t| !t.finished() && !t.detached) {
268 Err(StepError::Deadlock)
269 } else {
270 Ok(None)
271 }
272 }
273 ScheduledTask::Stopped => Ok(None),
274 ScheduledTask::None => Err(StepError::SchedulingError),
275 }
276 })?;
277
278 let ret = match next_step {
280 Some(continuation) => {
281 Execution::enter_task_span();
282
283 let result = panic::catch_unwind(panic::AssertUnwindSafe(|| continuation.borrow_mut().resume()));
284
285 Execution::exit_task_span();
286
287 result
288 }
289 None => return Ok(()),
290 };
291
292 match ret {
293 Ok(true) => {
295 crate::annotations::record_task_terminated();
296 ExecutionState::with(|state| state.current_mut().finish());
297 }
298 Ok(false) => {
300 if immediately_return_on_panic && std::thread::panicking() {
304 ExecutionState::with(|state| state.current_task = ScheduledTask::Stopped);
305 return Err(StepError::TaskPanicEarlyReturn);
306 }
307 }
308 Err(e) => return Err(StepError::TaskFailure(e)),
310 }
311 }
312 }
313}
314
315pub struct ExecutionState {
319 pub config: Config,
320 tasks: SmallVec<[Task; DEFAULT_INLINE_TASKS]>,
322 current_task: ScheduledTask,
324 next_task: ScheduledTask,
326 has_yielded: bool,
328 context_switches: usize,
330 pub steps_reset_at: usize,
332
333 storage: StorageMap,
335
336 scheduler: Rc<RefCell<dyn Scheduler>>,
337
338 in_cleanup: bool,
339
340 #[cfg(debug_assertions)]
341 has_cleaned_up: bool,
342
343 pub top_level_span: Span,
345
346 runnable_tasks: Vec<*const Task>,
349}
350
351impl std::fmt::Debug for ExecutionState {
352 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
353 f.debug_struct("ExecutionState").finish_non_exhaustive()
354 }
355}
356
357#[derive(Debug, PartialEq, Eq, Clone, Copy)]
358enum ScheduledTask {
359 None, Some(TaskId), Stopped, Finished, }
364
365impl ScheduledTask {
366 fn id(&self) -> Option<TaskId> {
367 match self {
368 ScheduledTask::Some(tid) => Some(*tid),
369 _ => None,
370 }
371 }
372
373 fn take(&mut self) -> Self {
374 std::mem::replace(self, ScheduledTask::None)
375 }
376}
377
378#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
380pub enum ExecutionStateBorrowError {
381 NotSet,
383 AlreadyBorrowed,
385}
386
387impl ExecutionState {
388 fn new(config: Config, scheduler: Rc<RefCell<dyn Scheduler>>) -> Self {
389 Self {
390 config,
391 tasks: SmallVec::new(),
392 current_task: ScheduledTask::None,
393 next_task: ScheduledTask::None,
394 has_yielded: false,
395 context_switches: 0,
396 steps_reset_at: 0,
397 storage: StorageMap::new(),
398 scheduler,
399 in_cleanup: false,
400 #[cfg(debug_assertions)]
401 has_cleaned_up: false,
402 top_level_span: tracing::Span::current(),
403 runnable_tasks: Vec::with_capacity(DEFAULT_INLINE_TASKS),
404 }
405 }
406
407 #[inline]
411 #[track_caller]
412 pub fn with<F, T>(f: F) -> T
413 where
414 F: FnOnce(&mut ExecutionState) -> T,
415 {
416 Self::try_with(f).unwrap_or_else(|e| {
417 eprintln!("`ExecutionState::try_with` failed with error: {e:?}");
418 eprintln!(
419 "Backtrace for `with`: {:#?}",
420 std::backtrace::Backtrace::force_capture()
421 );
422 match e {
423 ExecutionStateBorrowError::AlreadyBorrowed => panic!("`ExecutionState::with` panicked because `ExecutionState` is already borrowed."),
424 ExecutionStateBorrowError::NotSet => panic!("`ExecutionState::with` panicked because `ExecutionState` is not set. Are you accessing a Shuttle primitive outside of a Shuttle test?"),
425 }
426 })
427 }
428
429 #[inline]
432 #[track_caller]
433 pub fn try_with<F, T>(f: F) -> Result<T, ExecutionStateBorrowError>
434 where
435 F: FnOnce(&mut ExecutionState) -> T,
436 {
437 trace!(
438 "ExecutionState::try_with called from {:?}",
439 std::panic::Location::caller()
440 );
441 if EXECUTION_STATE.is_set() {
442 EXECUTION_STATE.with(|cell| {
443 if let Ok(mut state) = cell.try_borrow_mut() {
444 Ok(f(&mut state))
445 } else {
446 Err(ExecutionStateBorrowError::AlreadyBorrowed)
447 }
448 })
449 } else {
450 Err(ExecutionStateBorrowError::NotSet)
451 }
452 }
453
454 pub fn me() -> TaskId {
456 Self::with(|s| s.current().id())
457 }
458
459 pub fn exit_current_truncates_execution(&self) -> bool {
465 if self.current().id() == TaskId::from(0) {
468 return true;
469 }
470
471 if self.current().is_detached() {
473 return false;
474 }
475
476 let mut single_unfinished_attached = false;
477 let mut has_unfinished_detached = false;
478 for t in self.tasks.iter() {
479 let unfinished_attached = !t.finished() && !t.detached;
480 if single_unfinished_attached && unfinished_attached {
481 return false;
483 }
484
485 single_unfinished_attached |= unfinished_attached;
486 has_unfinished_detached |= !t.finished() && t.detached;
487 }
488 has_unfinished_detached && single_unfinished_attached
489 }
490
491 fn set_labels_for_new_task(state: &ExecutionState, task_id: TaskId, name: Option<String>) {
492 LABELS.with(|cell| {
493 let mut map = cell.borrow_mut();
494
495 if let Some(parent_task_id) = state.try_current().map(|t| t.id()) {
497 let parent_map = map.get(&parent_task_id);
498 if let Some(parent_map) = parent_map {
499 let mut child_map = parent_map.clone();
500
501 if let Some(gen) = parent_map.get::<ChildLabelFn>() {
503 (gen.0)(task_id, &mut child_map);
504 }
505
506 map.insert(task_id, child_map);
507 }
508 }
509
510 if let Some(name) = name {
512 let m = map.entry(task_id).or_default();
513 m.insert(TaskName::from(name));
514 }
515 });
516 }
517
518 pub fn spawn_main_thread(
521 f: Box<dyn FnOnce() + 'static>,
522 stack_size: usize,
523 caller: &'static Location<'static>,
524 ) -> TaskId {
525 let name = "main-thread".to_string();
526 let mut clock = VectorClock::new();
527
528 let task_id = Self::with(|state| {
529 let parent_span_id = state.top_level_span.id();
530 let task_id = TaskId(state.tasks.len());
531 let tag = state.get_tag_or_default_for_current_task();
532
533 Self::set_labels_for_new_task(state, task_id, Some(name.clone()));
534
535 clock.extend(task_id); let schedule_len = CurrentSchedule::len();
538
539 let task = Task::from_closure(
540 f,
541 stack_size,
542 task_id,
543 Some(name),
544 clock,
545 parent_span_id,
546 schedule_len,
547 tag,
548 None,
549 TaskSignature::new_parentless(caller),
550 );
551 state.tasks.push(task);
552
553 task_id
554 });
555 crate::annotations::record_task_created(task_id, false);
556 task_id
557 }
558
559 pub fn spawn_future<F>(
564 future: F,
565 stack_size: usize,
566 name: Option<String>,
567 caller: &'static Location<'static>,
568 ) -> TaskId
569 where
570 F: Future<Output = ()> + 'static,
571 {
572 thread::switch();
573 let task_id = Self::with(|state| {
574 let schedule_len = CurrentSchedule::len();
575 let parent_span_id = state.top_level_span.id();
576
577 let task_id = TaskId(state.tasks.len());
578 let tag = state.get_tag_or_default_for_current_task();
579
580 Self::set_labels_for_new_task(state, task_id, name.clone());
581
582 let clock = state.increment_clock_mut(); clock.extend(task_id); let task = Task::from_future(
586 future,
587 stack_size,
588 task_id,
589 name,
590 clock.clone(),
591 parent_span_id,
592 schedule_len,
593 tag,
594 Some(state.current().id()),
595 state.current_mut().signature.new_child(caller),
596 );
597
598 state.tasks.push(task);
599
600 task_id
601 });
602 crate::annotations::record_task_created(task_id, true);
603 task_id
604 }
605
606 pub fn spawn_thread(
609 f: Box<dyn FnOnce() + 'static>,
610 stack_size: usize,
611 name: Option<String>,
612 mut initial_clock: Option<VectorClock>,
613 caller: &'static Location<'static>,
614 ) -> TaskId {
615 thread::switch();
616 let task_id = Self::with(|state| {
617 let parent_span_id = state.top_level_span.id();
618 let task_id = TaskId(state.tasks.len());
619 let tag = state.get_tag_or_default_for_current_task();
620
621 Self::set_labels_for_new_task(state, task_id, name.clone());
622
623 let clock = if let Some(ref mut clock) = initial_clock {
624 clock
625 } else {
626 state.increment_clock_mut()
628 };
629 clock.extend(task_id); let clock = clock.clone();
631
632 let task = Task::from_closure(
633 f,
634 stack_size,
635 task_id,
636 name,
637 clock,
638 parent_span_id,
639 CurrentSchedule::len(),
640 tag,
641 Some(state.current().id()),
642 state.current_mut().signature.new_child(caller),
643 );
644 state.tasks.push(task);
645
646 task_id
647 });
648 crate::annotations::record_task_created(task_id, false);
649 task_id
650 }
651
652 fn cleanup() {
655 let (mut tasks, final_state) = Self::with(|state| {
660 state.in_cleanup = true;
661 assert!(state.current_task == ScheduledTask::Stopped || state.current_task == ScheduledTask::Finished);
662 (std::mem::take(&mut state.tasks), state.current_task)
663 });
664
665 for task in tasks.drain(..) {
666 assert!(
667 final_state == ScheduledTask::Stopped || task.finished() || task.detached,
668 "execution finished but task is not"
669 );
670 Rc::try_unwrap(task.continuation)
671 .map_err(|_| ())
672 .expect("couldn't cleanup a future");
673 }
674
675 while Self::with(|state| state.storage.pop()).is_some() {}
676
677 TASK_ID_TO_TAGS.with(|cell| cell.borrow_mut().clear());
678 LABELS.with(|cell| cell.borrow_mut().clear());
679
680 #[cfg(debug_assertions)]
681 Self::with(|state| state.has_cleaned_up = true);
682
683 Self::with(|state| state.in_cleanup = false);
684 }
685
686 pub fn is_finished(&self) -> bool {
688 self.current_task == ScheduledTask::Stopped || self.current_task == ScheduledTask::Finished
689 }
690
691 pub fn maybe_yield() -> bool {
695 Self::with(|state| {
696 if std::thread::panicking() && !state.in_cleanup {
697 return true;
698 }
699
700 debug_assert!(
701 matches!(state.current_task, ScheduledTask::Some(_) | ScheduledTask::Finished)
702 && state.next_task == ScheduledTask::None,
703 "we're inside a task and scheduler should not yet have run"
704 );
705
706 let result = state.schedule();
707 if result.is_err() {
709 return true;
710 }
711
712 if state.current_task == state.next_task {
715 state.advance_to_next_task();
716 false
717 } else {
718 true
719 }
720 })
721 }
722
723 pub fn request_yield() {
726 Self::with(|state| {
727 state.has_yielded = true;
728 });
729 }
730
731 pub fn should_stop() -> bool {
739 std::thread::panicking()
740 || Self::with(|s| {
741 assert_ne!(s.current_task, ScheduledTask::Finished);
742 s.current_task == ScheduledTask::Stopped
743 })
744 }
745
746 pub fn failing_task() -> String {
750 Self::try_with(|state| {
751 if let Some(task) = state.try_current() {
752 task.name().unwrap_or_else(|| format!("task-{:?}", task.id().0))
753 } else {
754 "<unknown>".into()
755 }
756 })
757 .unwrap_or_else(|e| format!("Tried to get ExecutionState, but got the following error: {e:?}"))
758 }
759
760 #[inline]
762 pub fn next_u64() -> u64 {
763 Self::with(|state| {
764 CurrentSchedule::push_random();
765 state.scheduler.borrow_mut().next_u64()
766 })
767 }
768
769 pub fn current(&self) -> &Task {
770 self.get(self.current_task.id().unwrap())
771 }
772
773 pub fn current_mut(&mut self) -> &mut Task {
774 self.get_mut(self.current_task.id().unwrap())
775 }
776
777 pub fn try_current(&self) -> Option<&Task> {
778 self.try_get(self.current_task.id()?)
779 }
780
781 pub fn get(&self, id: TaskId) -> &Task {
782 self.try_get(id).unwrap()
783 }
784
785 pub fn get_mut(&mut self, id: TaskId) -> &mut Task {
786 self.tasks.get_mut(id.0).unwrap()
787 }
788
789 pub fn try_get(&self, id: TaskId) -> Option<&Task> {
790 self.tasks.get(id.0)
791 }
792
793 pub fn in_cleanup(&self) -> bool {
794 self.in_cleanup
795 }
796
797 pub fn context_switches() -> usize {
798 Self::with(|state| state.context_switches)
799 }
800
801 #[track_caller]
802 pub fn new_resource_signature(resource_type: ResourceType) -> ResourceSignature {
803 ExecutionState::with(|s| s.current_mut().signature.new_resource(resource_type))
804 }
805
806 pub fn get_storage<K: Into<StorageKey>, T: 'static>(&self, key: K) -> Option<&T> {
807 self.storage
808 .get(key.into())
809 .map(|result| result.expect("global storage is never destructed"))
810 }
811
812 pub fn init_storage<K: Into<StorageKey>, T: 'static>(&mut self, key: K, value: T) {
813 self.storage.init(key.into(), value);
814 }
815
816 pub fn get_clock(&self, id: TaskId) -> &VectorClock {
817 &self.tasks.get(id.0).unwrap().clock
818 }
819
820 pub fn get_clock_mut(&mut self, id: TaskId) -> &mut VectorClock {
821 &mut self.tasks.get_mut(id.0).unwrap().clock
822 }
823
824 pub fn update_clock(&mut self, clock: &VectorClock) {
826 let task = self.current_mut();
827 task.clock.increment(task.id);
828 task.clock.update(clock);
829 }
830
831 pub fn increment_clock(&mut self) -> &VectorClock {
833 let task = self.current_mut();
834 task.clock.increment(task.id);
835 &task.clock
836 }
837
838 pub fn increment_clock_mut(&mut self) -> &mut VectorClock {
840 let task = self.current_mut();
841 task.clock.increment(task.id);
842 &mut task.clock
843 }
844
845 fn is_step_bound_exceeded(&self, max_steps: usize) -> bool {
847 CurrentSchedule::len() - self.steps_reset_at >= max_steps
848 }
849
850 fn schedule(&mut self) -> Result<(), StepError> {
854 if self.next_task != ScheduledTask::None {
857 return Ok(());
858 }
859
860 self.context_switches += 1;
861
862 match self.config.max_steps {
863 MaxSteps::FailAfter(max_steps) => {
864 if self.is_step_bound_exceeded(max_steps) {
865 return Err(StepError::StepBoundExceeded);
866 }
867 }
868 MaxSteps::ContinueAfter(max_steps) => {
869 if self.is_step_bound_exceeded(max_steps) {
870 self.next_task = ScheduledTask::Stopped;
872 return Ok(());
873 }
874 }
875 MaxSteps::None => {}
876 }
877
878 let mut unfinished_attached = false;
879 let mut all_runnable_detached = true;
880 let mut any_runnable = false;
881
882 for task in &self.tasks {
883 if task.finished() {
884 continue;
885 }
886 unfinished_attached |= !task.detached;
887 let is_runnable = task.runnable();
888 any_runnable |= is_runnable;
889
890 if is_runnable {
891 all_runnable_detached &= task.detached;
892 self.runnable_tasks.push(task as *const Task);
893 } else if task.can_spuriously_wakeup() {
894 self.runnable_tasks.push(task as *const Task);
900 }
901 }
902
903 if !any_runnable || (!unfinished_attached && all_runnable_detached) {
909 self.next_task = ScheduledTask::Finished;
910 return Ok(());
911 }
912
913 let is_yielding = std::mem::replace(&mut self.has_yielded, false);
914
915 let task_refs = unsafe { std::mem::transmute::<&[*const Task], &[&Task]>(&self.runnable_tasks) };
922
923 self.next_task = self
924 .scheduler
925 .borrow_mut()
926 .next_task(task_refs, self.current_task.id(), is_yielding)
927 .map(ScheduledTask::Some)
928 .unwrap_or(ScheduledTask::Stopped);
929
930 self.top_level_span.in_scope(|| {
939 trace!(
940 i=CurrentSchedule::len(),
941 next_task=?self.next_task,
942 runnable=?task_refs.iter().map(|task| task.id()).collect::<SmallVec<[_; DEFAULT_INLINE_TASKS]>>(),
943 "scheduling decision"
944 );
945 });
946
947 if let Some(tid) = self.next_task.id() {
950 let task = self.get_mut(tid);
951 assert!(task.runnable() || task.blocked());
952 if task.blocked() {
953 assert!(task.can_spuriously_wakeup());
954 task.unblock();
955 }
956 }
957
958 self.runnable_tasks.clear();
960
961 Ok(())
962 }
963
964 fn advance_to_next_task(&mut self) {
966 debug_assert_ne!(self.next_task, ScheduledTask::None);
967 self.current_task = self.next_task.take();
968
969 if let ScheduledTask::Some(tid) = self.current_task {
970 CurrentSchedule::push_task(tid);
971 }
972 }
973
974 #[allow(deprecated)]
977 pub fn set_tag_for_current_task(tag: Arc<dyn Tag>) -> Option<Arc<dyn Tag>> {
978 ExecutionState::with(|s| s.current_mut().set_tag(tag))
979 }
980
981 #[allow(deprecated)]
982 fn get_tag_or_default_for_current_task(&self) -> Option<Arc<dyn Tag>> {
983 self.try_current().and_then(|current| current.get_tag())
984 }
985
986 #[allow(deprecated)]
987 pub fn get_tag_for_current_task() -> Option<Arc<dyn Tag>> {
988 ExecutionState::with(|s| s.get_tag_or_default_for_current_task())
989 }
990
991 #[allow(deprecated)]
992 pub fn set_tag_for_task(task: TaskId, tag: Arc<dyn Tag>) -> Option<Arc<dyn Tag>> {
993 ExecutionState::with(|s| s.get_mut(task).set_tag(tag))
994 }
995}
996
997#[cfg(debug_assertions)]
998impl Drop for ExecutionState {
999 fn drop(&mut self) {
1000 assert!(self.has_cleaned_up || std::thread::panicking());
1001 }
1002}