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.finish_current_task());
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 live_tasks: Vec<TaskId>,
362}
363
364impl std::fmt::Debug for ExecutionState {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 f.debug_struct("ExecutionState").finish_non_exhaustive()
367 }
368}
369
370#[derive(Debug, PartialEq, Eq, Clone, Copy)]
371enum ScheduledTask {
372 None, Some(TaskId), Stopped, Finished, }
377
378impl ScheduledTask {
379 fn id(&self) -> Option<TaskId> {
380 match self {
381 ScheduledTask::Some(tid) => Some(*tid),
382 _ => None,
383 }
384 }
385
386 fn take(&mut self) -> Self {
387 std::mem::replace(self, ScheduledTask::None)
388 }
389}
390
391#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
393pub enum ExecutionStateBorrowError {
394 NotSet,
396 AlreadyBorrowed,
398}
399
400impl ExecutionState {
401 fn new(config: Config, scheduler: Rc<RefCell<dyn Scheduler>>) -> Self {
402 Self {
403 config,
404 tasks: SmallVec::new(),
405 current_task: ScheduledTask::None,
406 next_task: ScheduledTask::None,
407 has_yielded: false,
408 context_switches: 0,
409 steps_reset_at: 0,
410 storage: StorageMap::new(),
411 scheduler,
412 in_cleanup: false,
413 #[cfg(debug_assertions)]
414 has_cleaned_up: false,
415 top_level_span: tracing::Span::current(),
416 runnable_tasks: Vec::with_capacity(DEFAULT_INLINE_TASKS),
417 live_tasks: Vec::with_capacity(DEFAULT_INLINE_TASKS),
418 }
419 }
420
421 #[inline]
425 #[track_caller]
426 pub fn with<F, T>(f: F) -> T
427 where
428 F: FnOnce(&mut ExecutionState) -> T,
429 {
430 Self::try_with(f).unwrap_or_else(|e| {
431 eprintln!("`ExecutionState::try_with` failed with error: {e:?}");
432 eprintln!(
433 "Backtrace for `with`: {:#?}",
434 std::backtrace::Backtrace::force_capture()
435 );
436 match e {
437 ExecutionStateBorrowError::AlreadyBorrowed => panic!("`ExecutionState::with` panicked because `ExecutionState` is already borrowed."),
438 ExecutionStateBorrowError::NotSet => panic!("`ExecutionState::with` panicked because `ExecutionState` is not set. Are you accessing a Shuttle primitive outside of a Shuttle test?"),
439 }
440 })
441 }
442
443 #[inline]
446 #[track_caller]
447 pub fn try_with<F, T>(f: F) -> Result<T, ExecutionStateBorrowError>
448 where
449 F: FnOnce(&mut ExecutionState) -> T,
450 {
451 trace!(
452 "ExecutionState::try_with called from {:?}",
453 std::panic::Location::caller()
454 );
455 if EXECUTION_STATE.is_set() {
456 EXECUTION_STATE.with(|cell| {
457 if let Ok(mut state) = cell.try_borrow_mut() {
458 Ok(f(&mut state))
459 } else {
460 Err(ExecutionStateBorrowError::AlreadyBorrowed)
461 }
462 })
463 } else {
464 Err(ExecutionStateBorrowError::NotSet)
465 }
466 }
467
468 pub fn me() -> TaskId {
470 Self::with(|s| s.current().id())
471 }
472
473 pub fn exit_current_truncates_execution(&self) -> bool {
479 if self.current().id() == TaskId::from(0) {
482 return true;
483 }
484
485 if self.current().is_detached() {
487 return false;
488 }
489
490 let mut single_unfinished_attached = false;
491 let mut has_unfinished_detached = false;
492 for t in self.tasks.iter() {
493 let unfinished_attached = !t.finished() && !t.detached;
494 if single_unfinished_attached && unfinished_attached {
495 return false;
497 }
498
499 single_unfinished_attached |= unfinished_attached;
500 has_unfinished_detached |= !t.finished() && t.detached;
501 }
502 has_unfinished_detached && single_unfinished_attached
503 }
504
505 fn set_labels_for_new_task(state: &ExecutionState, task_id: TaskId, name: Option<String>) {
506 LABELS.with(|cell| {
507 let mut map = cell.borrow_mut();
508
509 if let Some(parent_task_id) = state.try_current().map(|t| t.id()) {
511 let parent_map = map.get(&parent_task_id);
512 if let Some(parent_map) = parent_map {
513 let mut child_map = parent_map.clone();
514
515 if let Some(gen) = parent_map.get::<ChildLabelFn>() {
517 (gen.0)(task_id, &mut child_map);
518 }
519
520 map.insert(task_id, child_map);
521 }
522 }
523
524 if let Some(name) = name {
526 let m = map.entry(task_id).or_default();
527 m.insert(TaskName::from(name));
528 }
529 });
530 }
531
532 pub fn spawn_main_thread(
535 f: Box<dyn FnOnce() + 'static>,
536 stack_size: usize,
537 caller: &'static Location<'static>,
538 ) -> TaskId {
539 let name = "main-thread".to_string();
540 let mut clock = VectorClock::new();
541
542 let task_id = Self::with(|state| {
543 let parent_span_id = state.top_level_span.id();
544 let task_id = TaskId(state.tasks.len());
545 let tag = state.get_tag_or_default_for_current_task();
546
547 Self::set_labels_for_new_task(state, task_id, Some(name.clone()));
548
549 clock.extend(task_id); let schedule_len = CurrentSchedule::len();
552
553 let task = Task::from_closure(
554 f,
555 stack_size,
556 task_id,
557 Some(name),
558 clock,
559 parent_span_id,
560 schedule_len,
561 tag,
562 None,
563 TaskSignature::new_parentless(caller),
564 );
565 state.add_task(task);
566
567 task_id
568 });
569 crate::annotations::record_task_created(task_id, false);
570 task_id
571 }
572
573 pub fn spawn_future<F>(
578 future: F,
579 stack_size: usize,
580 name: Option<String>,
581 caller: &'static Location<'static>,
582 ) -> TaskId
583 where
584 F: Future<Output = ()> + 'static,
585 {
586 thread::switch();
587 let task_id = Self::with(|state| {
588 let schedule_len = CurrentSchedule::len();
589 let parent_span_id = state.top_level_span.id();
590
591 let task_id = TaskId(state.tasks.len());
592 let tag = state.get_tag_or_default_for_current_task();
593
594 Self::set_labels_for_new_task(state, task_id, name.clone());
595
596 let clock = state.increment_clock_mut(); clock.extend(task_id); let task = Task::from_future(
600 future,
601 stack_size,
602 task_id,
603 name,
604 clock.clone(),
605 parent_span_id,
606 schedule_len,
607 tag,
608 Some(state.current().id()),
609 state.current_mut().signature.new_child(caller),
610 );
611
612 state.add_task(task);
613
614 task_id
615 });
616 crate::annotations::record_task_created(task_id, true);
617 task_id
618 }
619
620 pub fn spawn_thread(
623 f: Box<dyn FnOnce() + 'static>,
624 stack_size: usize,
625 name: Option<String>,
626 mut initial_clock: Option<VectorClock>,
627 caller: &'static Location<'static>,
628 ) -> TaskId {
629 thread::switch();
630 let task_id = Self::with(|state| {
631 let parent_span_id = state.top_level_span.id();
632 let task_id = TaskId(state.tasks.len());
633 let tag = state.get_tag_or_default_for_current_task();
634
635 Self::set_labels_for_new_task(state, task_id, name.clone());
636
637 let clock = if let Some(ref mut clock) = initial_clock {
638 clock
639 } else {
640 state.increment_clock_mut()
642 };
643 clock.extend(task_id); let clock = clock.clone();
645
646 let task = Task::from_closure(
647 f,
648 stack_size,
649 task_id,
650 name,
651 clock,
652 parent_span_id,
653 CurrentSchedule::len(),
654 tag,
655 Some(state.current().id()),
656 state.current_mut().signature.new_child(caller),
657 );
658 state.add_task(task);
659
660 task_id
661 });
662 crate::annotations::record_task_created(task_id, false);
663 task_id
664 }
665
666 fn cleanup() {
669 let (mut tasks, final_state) = Self::with(|state| {
674 state.in_cleanup = true;
675 assert!(state.current_task == ScheduledTask::Stopped || state.current_task == ScheduledTask::Finished);
676 state.live_tasks.clear();
678 (std::mem::take(&mut state.tasks), state.current_task)
679 });
680
681 for task in tasks.drain(..) {
682 assert!(
683 final_state == ScheduledTask::Stopped || task.finished() || task.detached,
684 "execution finished but task is not"
685 );
686 Rc::try_unwrap(task.continuation)
687 .map_err(|_| ())
688 .expect("couldn't cleanup a future");
689 }
690
691 while Self::with(|state| state.storage.pop()).is_some() {}
692
693 TASK_ID_TO_TAGS.with(|cell| cell.borrow_mut().clear());
694 LABELS.with(|cell| cell.borrow_mut().clear());
695
696 #[cfg(debug_assertions)]
697 Self::with(|state| state.has_cleaned_up = true);
698
699 Self::with(|state| state.in_cleanup = false);
700 }
701
702 pub fn is_finished(&self) -> bool {
704 self.current_task == ScheduledTask::Stopped || self.current_task == ScheduledTask::Finished
705 }
706
707 pub fn maybe_yield() -> bool {
711 Self::with(|state| {
712 if std::thread::panicking() && !state.in_cleanup {
713 return true;
714 }
715
716 debug_assert!(
717 matches!(state.current_task, ScheduledTask::Some(_) | ScheduledTask::Finished)
718 && state.next_task == ScheduledTask::None,
719 "we're inside a task and scheduler should not yet have run"
720 );
721
722 let result = state.schedule();
723 if result.is_err() {
725 return true;
726 }
727
728 if state.current_task == state.next_task {
731 state.advance_to_next_task();
732 false
733 } else {
734 true
735 }
736 })
737 }
738
739 pub fn request_yield() {
742 Self::with(|state| {
743 state.has_yielded = true;
744 });
745 }
746
747 pub fn should_stop() -> bool {
755 std::thread::panicking()
756 || Self::with(|s| {
757 assert_ne!(s.current_task, ScheduledTask::Finished);
758 s.current_task == ScheduledTask::Stopped
759 })
760 }
761
762 pub fn failing_task() -> String {
766 Self::try_with(|state| {
767 if let Some(task) = state.try_current() {
768 task.name().unwrap_or_else(|| format!("task-{:?}", task.id().0))
769 } else {
770 "<unknown>".into()
771 }
772 })
773 .unwrap_or_else(|e| format!("Tried to get ExecutionState, but got the following error: {e:?}"))
774 }
775
776 #[inline]
778 pub fn next_u64() -> u64 {
779 Self::with(|state| {
780 CurrentSchedule::push_random();
781 state.scheduler.borrow_mut().next_u64()
782 })
783 }
784
785 pub fn current(&self) -> &Task {
786 self.get(self.current_task.id().unwrap())
787 }
788
789 pub fn current_mut(&mut self) -> &mut Task {
790 self.get_mut(self.current_task.id().unwrap())
791 }
792
793 pub fn try_current(&self) -> Option<&Task> {
794 self.try_get(self.current_task.id()?)
795 }
796
797 pub fn get(&self, id: TaskId) -> &Task {
798 self.try_get(id).unwrap()
799 }
800
801 fn add_task(&mut self, task: Task) {
804 debug_assert!(self.live_tasks.last().is_none_or(|last| *last < task.id()));
805 self.live_tasks.push(task.id());
806 self.tasks.push(task);
807 }
808
809 fn finish_task(&mut self, task_id: TaskId) {
811 self.get_mut(task_id).finish();
812 let idx = self
813 .live_tasks
814 .binary_search(&task_id)
815 .expect("finished task must be live");
816 self.live_tasks.remove(idx);
817 }
818
819 fn finish_current_task(&mut self) {
821 self.finish_task(self.current_task.id().unwrap());
822 }
823
824 pub fn get_mut(&mut self, id: TaskId) -> &mut Task {
825 self.tasks.get_mut(id.0).unwrap()
826 }
827
828 pub fn try_get(&self, id: TaskId) -> Option<&Task> {
829 self.tasks.get(id.0)
830 }
831
832 pub fn in_cleanup(&self) -> bool {
833 self.in_cleanup
834 }
835
836 pub fn context_switches() -> usize {
837 Self::with(|state| state.context_switches)
838 }
839
840 #[track_caller]
841 pub fn new_resource_signature(resource_type: ResourceType) -> ResourceSignature {
842 ExecutionState::with(|s| s.current_mut().signature.new_resource(resource_type))
843 }
844
845 pub fn get_storage<K: Into<StorageKey>, T: 'static>(&self, key: K) -> Option<&T> {
846 self.storage
847 .get(key.into())
848 .map(|result| result.expect("global storage is never destructed"))
849 }
850
851 pub fn init_storage<K: Into<StorageKey>, T: 'static>(&mut self, key: K, value: T) {
852 self.storage.init(key.into(), value);
853 }
854
855 pub fn get_clock(&self, id: TaskId) -> &VectorClock {
856 &self.tasks.get(id.0).unwrap().clock
857 }
858
859 pub fn get_clock_mut(&mut self, id: TaskId) -> &mut VectorClock {
860 &mut self.tasks.get_mut(id.0).unwrap().clock
861 }
862
863 pub fn update_clock(&mut self, clock: &VectorClock) {
865 let task = self.current_mut();
866 task.clock.increment(task.id);
867 task.clock.update(clock);
868 }
869
870 pub fn increment_clock(&mut self) -> &VectorClock {
872 let task = self.current_mut();
873 task.clock.increment(task.id);
874 &task.clock
875 }
876
877 pub fn increment_clock_mut(&mut self) -> &mut VectorClock {
879 let task = self.current_mut();
880 task.clock.increment(task.id);
881 &mut task.clock
882 }
883
884 fn is_step_bound_exceeded(&self, max_steps: usize) -> bool {
886 CurrentSchedule::len() - self.steps_reset_at >= max_steps
887 }
888
889 fn schedule(&mut self) -> Result<(), StepError> {
893 if self.next_task != ScheduledTask::None {
896 return Ok(());
897 }
898
899 self.context_switches += 1;
900
901 match self.config.max_steps {
902 MaxSteps::FailAfter(max_steps) => {
903 if self.is_step_bound_exceeded(max_steps) {
904 return Err(StepError::StepBoundExceeded);
905 }
906 }
907 MaxSteps::ContinueAfter(max_steps) => {
908 if self.is_step_bound_exceeded(max_steps) {
909 self.next_task = ScheduledTask::Stopped;
911 return Ok(());
912 }
913 }
914 MaxSteps::None => {}
915 }
916
917 let mut unfinished_attached = false;
918 let mut all_runnable_detached = true;
919 let mut any_runnable = false;
920
921 debug_assert!(
925 self.tasks
926 .iter()
927 .filter(|task| !task.finished() && task.runnable())
928 .all(|task| self.live_tasks.binary_search(&task.id()).is_ok()),
929 "live_tasks is missing a runnable unfinished task"
930 );
931
932 for &task_id in &self.live_tasks {
933 let task = &self.tasks[task_id.0];
934 debug_assert!(!task.finished());
935 unfinished_attached |= !task.detached;
936 let is_runnable = task.runnable();
937 any_runnable |= is_runnable;
938
939 if is_runnable {
940 all_runnable_detached &= task.detached;
941 self.runnable_tasks.push(task as *const Task);
942 } else if task.can_spuriously_wakeup() {
943 self.runnable_tasks.push(task as *const Task);
949 }
950 }
951
952 if !any_runnable || (!unfinished_attached && all_runnable_detached) {
958 self.next_task = ScheduledTask::Finished;
959 return Ok(());
960 }
961
962 let is_yielding = std::mem::replace(&mut self.has_yielded, false);
963
964 let task_refs = unsafe { std::mem::transmute::<&[*const Task], &[&Task]>(&self.runnable_tasks) };
971
972 self.next_task = self
973 .scheduler
974 .borrow_mut()
975 .next_task(task_refs, self.current_task.id(), is_yielding)
976 .map(ScheduledTask::Some)
977 .unwrap_or(ScheduledTask::Stopped);
978
979 self.top_level_span.in_scope(|| {
988 trace!(
989 i=CurrentSchedule::len(),
990 next_task=?self.next_task,
991 runnable=?task_refs.iter().map(|task| task.id()).collect::<SmallVec<[_; DEFAULT_INLINE_TASKS]>>(),
992 "scheduling decision"
993 );
994 });
995
996 if let Some(tid) = self.next_task.id() {
999 let task = self.get_mut(tid);
1000 assert!(task.runnable() || task.blocked());
1001 if task.blocked() {
1002 assert!(task.can_spuriously_wakeup());
1003 task.unblock();
1004 }
1005 }
1006
1007 self.runnable_tasks.clear();
1009
1010 Ok(())
1011 }
1012
1013 fn advance_to_next_task(&mut self) {
1015 debug_assert_ne!(self.next_task, ScheduledTask::None);
1016 self.current_task = self.next_task.take();
1017
1018 if let ScheduledTask::Some(tid) = self.current_task {
1019 CurrentSchedule::push_task(tid);
1020 }
1021 }
1022
1023 #[allow(deprecated)]
1026 pub fn set_tag_for_current_task(tag: Arc<dyn Tag>) -> Option<Arc<dyn Tag>> {
1027 ExecutionState::with(|s| s.current_mut().set_tag(tag))
1028 }
1029
1030 #[allow(deprecated)]
1031 fn get_tag_or_default_for_current_task(&self) -> Option<Arc<dyn Tag>> {
1032 self.try_current().and_then(|current| current.get_tag())
1033 }
1034
1035 #[allow(deprecated)]
1036 pub fn get_tag_for_current_task() -> Option<Arc<dyn Tag>> {
1037 ExecutionState::with(|s| s.get_tag_or_default_for_current_task())
1038 }
1039
1040 #[allow(deprecated)]
1041 pub fn set_tag_for_task(task: TaskId, tag: Arc<dyn Tag>) -> Option<Arc<dyn Tag>> {
1042 ExecutionState::with(|s| s.get_mut(task).set_tag(tag))
1043 }
1044}
1045
1046#[cfg(debug_assertions)]
1047impl Drop for ExecutionState {
1048 fn drop(&mut self) {
1049 assert!(self.has_cleaned_up || std::thread::panicking());
1050 }
1051}