1use std::borrow::Cow;
4use std::path::Path;
5use std::sync::Arc;
6use std::sync::atomic::AtomicU8;
7use std::sync::atomic::Ordering;
8
9use anyhow::Result;
10use cloud_copy::TransferEvent;
11use crankshaft::events::Event as CrankshaftEvent;
12use indexmap::IndexMap;
13use tokio::sync::broadcast;
14use tokio_util::sync::CancellationToken;
15use tracing::error;
16use wdl_analysis::Document;
17use wdl_analysis::document::Task;
18use wdl_analysis::types::Type;
19use wdl_ast::Diagnostic;
20use wdl_ast::Span;
21use wdl_ast::SupportedVersion;
22
23use crate::EvaluationPath;
24use crate::GuestPath;
25use crate::HostPath;
26use crate::Outputs;
27use crate::Value;
28use crate::backend::TaskExecutionResult;
29use crate::config::FailureMode;
30use crate::http::Transferer;
31
32mod trie;
33pub mod v1;
34
35const ROOT_NAME: &str = ".root";
39
40const CANCELLATION_STATE_NOT_CANCELED: u8 = 0;
42
43const CANCELLATION_STATE_WAITING: u8 = 1;
48
49const CANCELLATION_STATE_CANCELING: u8 = 2;
53
54const CANCELLATION_STATE_ERROR: u8 = 4;
59
60const CANCELLATION_STATE_MASK: u8 = 0x3;
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum CancellationContextState {
66 NotCanceled,
68 Waiting,
71 Canceling,
74}
75
76impl CancellationContextState {
77 fn from_raw(raw: u8) -> Self {
79 match raw & CANCELLATION_STATE_MASK {
80 CANCELLATION_STATE_NOT_CANCELED => Self::NotCanceled,
81 CANCELLATION_STATE_WAITING => Self::Waiting,
82 CANCELLATION_STATE_CANCELING => Self::Canceling,
83 _ => unreachable!("unexpected cancellation context state"),
84 }
85 }
86
87 fn update(mode: FailureMode, error: bool, state: &Arc<AtomicU8>) -> Option<Self> {
92 let previous_state = state
94 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
95 if error && state != CANCELLATION_STATE_NOT_CANCELED {
97 return None;
98 }
99
100 let mut new_state = match state & CANCELLATION_STATE_MASK {
102 CANCELLATION_STATE_NOT_CANCELED => match mode {
103 FailureMode::Slow => CANCELLATION_STATE_WAITING,
104 FailureMode::Fast => CANCELLATION_STATE_CANCELING,
105 },
106 CANCELLATION_STATE_WAITING => CANCELLATION_STATE_CANCELING,
107 CANCELLATION_STATE_CANCELING => CANCELLATION_STATE_CANCELING,
108 _ => unreachable!("unexpected cancellation context state"),
109 };
110
111 if error {
113 new_state |= CANCELLATION_STATE_ERROR;
114 }
115
116 Some(new_state | (state & CANCELLATION_STATE_ERROR))
118 })
119 .ok()?;
120
121 match previous_state & CANCELLATION_STATE_MASK {
122 CANCELLATION_STATE_NOT_CANCELED => match mode {
123 FailureMode::Slow => Some(Self::Waiting),
124 FailureMode::Fast => Some(Self::Canceling),
125 },
126 CANCELLATION_STATE_WAITING => Some(Self::Canceling),
127 CANCELLATION_STATE_CANCELING => Some(Self::Canceling),
128 _ => unreachable!("unexpected cancellation context state"),
129 }
130 }
131}
132
133#[derive(Debug, Clone)]
137pub struct CancellationContext {
138 mode: FailureMode,
140 state: Arc<AtomicU8>,
142 parent: Option<Arc<CancellationContext>>,
145 first: CancellationToken,
147 second: CancellationToken,
151}
152
153impl CancellationContext {
154 pub fn new(mode: FailureMode) -> Self {
164 Self {
165 mode,
166 state: Arc::new(CANCELLATION_STATE_NOT_CANCELED.into()),
167 parent: None,
168 first: CancellationToken::new(),
169 second: CancellationToken::new(),
170 }
171 }
172
173 pub fn child(&self, mode: FailureMode) -> Self {
180 Self {
181 mode,
182 state: Arc::new(CANCELLATION_STATE_NOT_CANCELED.into()),
183 parent: Some(Arc::new(self.clone())),
184 first: self.first.child_token(),
185 second: self.second.child_token(),
186 }
187 }
188
189 fn effective(&self) -> (u8, bool) {
205 let raw = self.state.load(Ordering::SeqCst);
206 let level = raw & CANCELLATION_STATE_MASK;
207 let user =
208 level != CANCELLATION_STATE_NOT_CANCELED && (raw & CANCELLATION_STATE_ERROR == 0);
209
210 match &self.parent {
211 Some(parent) => match parent.effective() {
212 (parent_level, parent_user) if parent_level > level => (parent_level, parent_user),
213 _ => (level, user),
214 },
215 None => (level, user),
216 }
217 }
218
219 pub fn state(&self) -> CancellationContextState {
222 CancellationContextState::from_raw(self.effective().0)
223 }
224
225 #[must_use]
233 pub fn cancel(&self) -> CancellationContextState {
234 let state =
235 CancellationContextState::update(self.mode, false, &self.state).expect("should update");
236
237 match state {
238 CancellationContextState::NotCanceled => panic!("should be canceled"),
239 CancellationContextState::Waiting => self.first.cancel(),
240 CancellationContextState::Canceling => {
241 self.first.cancel();
242 self.second.cancel();
243 }
244 }
245
246 state
247 }
248
249 pub fn first(&self) -> CancellationToken {
259 self.first.clone()
260 }
261
262 pub fn second(&self) -> CancellationToken {
273 self.second.clone()
274 }
275
276 pub fn user_canceled(&self) -> bool {
279 self.effective().1
280 }
281
282 pub(crate) fn error(&self, error: &EvaluationError) {
289 if let Some(state) = CancellationContextState::update(self.mode, true, &self.state) {
290 let message: Cow<'_, str> = match error {
291 EvaluationError::Canceled => "evaluation was canceled".into(),
292 EvaluationError::Source(e) => e.diagnostic.message().into(),
293 EvaluationError::Other(e) => format!("{e:#}").into(),
294 };
295
296 match state {
297 CancellationContextState::NotCanceled => unreachable!("should be canceled"),
298 CancellationContextState::Waiting => {
299 self.first.cancel();
300
301 error!(
302 "an evaluation error occurred: waiting for any executing tasks to \
303 complete: {message}"
304 );
305 }
306 CancellationContextState::Canceling => {
307 self.first.cancel();
308 self.second.cancel();
309
310 error!(
311 "an evaluation error occurred: waiting for any executing tasks to cancel: \
312 {message}"
313 );
314 }
315 }
316 }
317 }
318}
319
320impl Default for CancellationContext {
321 fn default() -> Self {
322 Self::new(FailureMode::Slow)
323 }
324}
325
326#[derive(Debug, Clone)]
328pub enum EngineEvent {
329 ReusedCachedExecutionResult {
331 id: String,
333 },
334 TaskParked,
337 TaskUnparked {
339 canceled: bool,
341 },
342}
343
344#[derive(Debug, Clone, Default)]
346pub struct Events {
347 engine: Option<broadcast::Sender<EngineEvent>>,
351 crankshaft: Option<broadcast::Sender<CrankshaftEvent>>,
355 transfer: Option<broadcast::Sender<TransferEvent>>,
359}
360
361impl Events {
362 pub fn new(capacity: usize) -> Self {
364 Self {
365 engine: Some(broadcast::Sender::new(capacity)),
366 crankshaft: Some(broadcast::Sender::new(capacity)),
367 transfer: Some(broadcast::Sender::new(capacity)),
368 }
369 }
370
371 pub fn disabled() -> Self {
373 Self::default()
374 }
375
376 pub fn subscribe_engine(&self) -> Option<broadcast::Receiver<EngineEvent>> {
380 self.engine.as_ref().map(|s| s.subscribe())
381 }
382
383 pub fn subscribe_crankshaft(&self) -> Option<broadcast::Receiver<CrankshaftEvent>> {
387 self.crankshaft.as_ref().map(|s| s.subscribe())
388 }
389
390 pub fn subscribe_transfer(&self) -> Option<broadcast::Receiver<TransferEvent>> {
394 self.transfer.as_ref().map(|s| s.subscribe())
395 }
396
397 pub(crate) fn engine(&self) -> &Option<broadcast::Sender<EngineEvent>> {
399 &self.engine
400 }
401
402 pub(crate) fn crankshaft(&self) -> &Option<broadcast::Sender<CrankshaftEvent>> {
404 &self.crankshaft
405 }
406
407 pub(crate) fn transfer(&self) -> &Option<broadcast::Sender<TransferEvent>> {
409 &self.transfer
410 }
411}
412
413#[derive(Debug, Clone)]
415pub struct CallLocation {
416 pub document: Document,
418 pub span: Span,
420}
421
422#[derive(Debug)]
424pub struct SourceError {
425 pub document: Document,
427 pub diagnostic: Diagnostic,
429 pub backtrace: Vec<CallLocation>,
436}
437
438#[derive(Debug)]
440pub enum EvaluationError {
441 Canceled,
443 Source(Box<SourceError>),
445 Other(anyhow::Error),
447}
448
449impl EvaluationError {
450 pub fn new(document: Document, diagnostic: Diagnostic) -> Self {
452 Self::Source(Box::new(SourceError {
453 document,
454 diagnostic,
455 backtrace: Default::default(),
456 }))
457 }
458
459 #[allow(clippy::inherent_to_string)]
461 pub fn to_string(&self) -> String {
462 use std::collections::HashMap;
463
464 use codespan_reporting::diagnostic::Label;
465 use codespan_reporting::diagnostic::LabelStyle;
466 use codespan_reporting::files::SimpleFiles;
467 use codespan_reporting::term;
468 use codespan_reporting::term::Config;
469 use codespan_reporting::term::termcolor::Buffer;
470 use wdl_ast::AstNode;
471
472 match self {
473 Self::Canceled => "evaluation was canceled".to_string(),
474 Self::Source(e) => {
475 let mut files = SimpleFiles::new();
476 let mut map = HashMap::new();
477
478 let file_id = files.add(e.document.path(), e.document.root().text().to_string());
479
480 let diagnostic =
481 e.diagnostic
482 .to_codespan(file_id)
483 .with_labels_iter(e.backtrace.iter().map(|l| {
484 let id = l.document.id();
485 let file_id = *map.entry(id).or_insert_with(|| {
486 files.add(l.document.path(), l.document.root().text().to_string())
487 });
488
489 Label {
490 style: LabelStyle::Secondary,
491 file_id,
492 range: l.span.start()..l.span.end(),
493 message: "called from this location".into(),
494 }
495 }));
496
497 let mut buffer = Buffer::no_color();
498 term::emit_to_write_style(&mut buffer, &Config::default(), &files, &diagnostic)
499 .expect("failed to emit diagnostic");
500
501 String::from_utf8(buffer.into_inner()).expect("should be UTF-8")
502 }
503 Self::Other(e) => format!("{e:#}"),
504 }
505 }
506}
507
508impl From<anyhow::Error> for EvaluationError {
509 fn from(e: anyhow::Error) -> Self {
510 Self::Other(e)
511 }
512}
513
514pub type EvaluationResult<T> = Result<T, EvaluationError>;
516
517pub(crate) trait EvaluationContext: Send + Sync {
519 fn version(&self) -> SupportedVersion;
521
522 fn resolve_name(&self, name: &str, span: Span) -> Result<Value, Diagnostic>;
524
525 fn resolve_type_name(&self, name: &str, span: Span) -> Result<Type, Diagnostic>;
527
528 fn enum_variant_value(&self, enum_name: &str, variant_name: &str) -> Result<Value, Diagnostic>;
530
531 fn base_dir(&self) -> &EvaluationPath;
540
541 fn temp_dir(&self) -> &Path;
543
544 fn stdout(&self) -> Option<&Value> {
548 None
549 }
550
551 fn stderr(&self) -> Option<&Value> {
555 None
556 }
557
558 fn task(&self) -> Option<&Task> {
562 None
563 }
564
565 fn transferer(&self) -> &dyn Transferer;
567
568 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
573 let _ = path;
574 None
575 }
576
577 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
582 let _ = path;
583 None
584 }
585
586 fn notify_file_created(&mut self, path: &HostPath) -> Result<()> {
591 let _ = path;
592 Ok(())
593 }
594}
595
596#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
598struct ScopeIndex(usize);
599
600impl ScopeIndex {
601 pub const fn new(index: usize) -> Self {
603 Self(index)
604 }
605}
606
607impl From<usize> for ScopeIndex {
608 fn from(index: usize) -> Self {
609 Self(index)
610 }
611}
612
613impl From<ScopeIndex> for usize {
614 fn from(index: ScopeIndex) -> Self {
615 index.0
616 }
617}
618
619#[derive(Default, Debug)]
621struct Scope {
622 parent: Option<ScopeIndex>,
626 names: IndexMap<String, Value>,
628}
629
630impl Scope {
631 pub fn new(parent: ScopeIndex) -> Self {
633 Self {
634 parent: Some(parent),
635 names: Default::default(),
636 }
637 }
638
639 pub fn insert(&mut self, name: impl Into<String>, value: impl Into<Value>) {
641 let prev = self.names.insert(name.into(), value.into());
642 assert!(prev.is_none(), "conflicting name in scope");
643 }
644
645 pub fn local(&self) -> impl Iterator<Item = (&str, &Value)> + use<'_> {
647 self.names.iter().map(|(k, v)| (k.as_str(), v))
648 }
649
650 pub(crate) fn get_mut(&mut self, name: &str) -> Option<&mut Value> {
652 self.names.get_mut(name)
653 }
654
655 pub(crate) fn clear(&mut self) {
657 self.parent = None;
658 self.names.clear();
659 }
660
661 pub(crate) fn set_parent(&mut self, parent: ScopeIndex) {
663 self.parent = Some(parent);
664 }
665}
666
667impl From<Scope> for IndexMap<String, Value> {
668 fn from(scope: Scope) -> Self {
669 scope.names
670 }
671}
672
673impl From<Scope> for Outputs {
674 fn from(scope: Scope) -> Self {
675 scope.names.into()
676 }
677}
678
679#[derive(Debug, Clone, Copy)]
681struct ScopeRef<'a> {
682 scopes: &'a [Scope],
684 index: ScopeIndex,
686}
687
688impl<'a> ScopeRef<'a> {
689 pub fn new(scopes: &'a [Scope], index: impl Into<ScopeIndex>) -> Self {
691 Self {
692 scopes,
693 index: index.into(),
694 }
695 }
696
697 pub fn parent(&self) -> Option<Self> {
701 self.scopes[self.index.0].parent.map(|p| Self {
702 scopes: self.scopes,
703 index: p,
704 })
705 }
706
707 pub fn local(&self, name: &str) -> Option<&Value> {
711 self.scopes[self.index.0].names.get(name)
712 }
713
714 pub fn lookup(&self, name: &str) -> Option<&Value> {
718 let mut current = Some(self.index);
719
720 while let Some(index) = current {
721 if let Some(name) = self.scopes[index.0].names.get(name) {
722 return Some(name);
723 }
724
725 current = self.scopes[index.0].parent;
726 }
727
728 None
729 }
730}
731
732#[derive(Debug)]
740pub struct EvaluatedTask {
741 result: TaskExecutionResult,
743 outputs: Outputs,
745 error: Option<EvaluationError>,
749 cached: bool,
751}
752
753impl EvaluatedTask {
754 fn new(cached: bool, result: TaskExecutionResult, error: Option<EvaluationError>) -> Self {
756 Self {
757 result,
758 outputs: Default::default(),
759 error,
760 cached,
761 }
762 }
763
764 pub fn failed(&self) -> bool {
767 self.error.is_some()
768 }
769
770 pub fn cached(&self) -> bool {
773 self.cached
774 }
775
776 pub fn exit_code(&self) -> i32 {
778 self.result.exit_code
779 }
780
781 pub fn work_dir(&self) -> &EvaluationPath {
783 &self.result.work_dir
784 }
785
786 pub fn stdout(&self) -> &Value {
788 &self.result.stdout
789 }
790
791 pub fn stderr(&self) -> &Value {
793 &self.result.stderr
794 }
795
796 pub fn into_outputs(self) -> EvaluationResult<Outputs> {
801 match self.error {
802 Some(e) => Err(e),
803 None => Ok(self.outputs),
804 }
805 }
806}
807
808#[cfg(test)]
809mod test {
810 use super::*;
811
812 #[test]
813 fn cancellation_slow() {
814 let context = CancellationContext::new(FailureMode::Slow);
815 assert_eq!(context.state(), CancellationContextState::NotCanceled);
816
817 assert_eq!(context.cancel(), CancellationContextState::Waiting);
819 assert_eq!(context.state(), CancellationContextState::Waiting);
820 assert!(context.user_canceled());
821 assert!(context.first.is_cancelled());
822 assert!(!context.second.is_cancelled());
823
824 assert_eq!(context.cancel(), CancellationContextState::Canceling);
826 assert_eq!(context.state(), CancellationContextState::Canceling);
827 assert!(context.user_canceled());
828 assert!(context.first.is_cancelled());
829 assert!(context.second.is_cancelled());
830
831 assert_eq!(context.cancel(), CancellationContextState::Canceling);
833 assert_eq!(context.state(), CancellationContextState::Canceling);
834 assert!(context.user_canceled());
835 assert!(context.first.is_cancelled());
836 assert!(context.second.is_cancelled());
837 }
838
839 #[test]
840 fn cancellation_fast() {
841 let context = CancellationContext::new(FailureMode::Fast);
842 assert_eq!(context.state(), CancellationContextState::NotCanceled);
843
844 assert_eq!(context.cancel(), CancellationContextState::Canceling);
846 assert_eq!(context.state(), CancellationContextState::Canceling);
847 assert!(context.user_canceled());
848 assert!(context.first.is_cancelled());
849 assert!(context.second.is_cancelled());
850
851 assert_eq!(context.cancel(), CancellationContextState::Canceling);
853 assert_eq!(context.state(), CancellationContextState::Canceling);
854 assert!(context.user_canceled());
855 assert!(context.first.is_cancelled());
856 assert!(context.second.is_cancelled());
857 }
858
859 #[test]
860 fn cancellation_error_slow() {
861 let context = CancellationContext::new(FailureMode::Slow);
862 assert_eq!(context.state(), CancellationContextState::NotCanceled);
863
864 context.error(&EvaluationError::Canceled);
866 assert_eq!(context.state(), CancellationContextState::Waiting);
867 assert!(!context.user_canceled());
868 assert!(context.first.is_cancelled());
869 assert!(!context.second.is_cancelled());
870
871 context.error(&EvaluationError::Canceled);
873 assert_eq!(context.state(), CancellationContextState::Waiting);
874 assert!(!context.user_canceled());
875 assert!(context.first.is_cancelled());
876 assert!(!context.second.is_cancelled());
877
878 assert_eq!(context.cancel(), CancellationContextState::Canceling);
880 assert_eq!(context.state(), CancellationContextState::Canceling);
881 assert!(!context.user_canceled());
882 assert!(context.first.is_cancelled());
883 assert!(context.second.is_cancelled());
884 }
885
886 #[test]
887 fn cancellation_error_fast() {
888 let context = CancellationContext::new(FailureMode::Fast);
889 assert_eq!(context.state(), CancellationContextState::NotCanceled);
890
891 context.error(&EvaluationError::Canceled);
893 assert_eq!(context.state(), CancellationContextState::Canceling);
894 assert!(!context.user_canceled());
895 assert!(context.first.is_cancelled());
896 assert!(context.second.is_cancelled());
897
898 context.error(&EvaluationError::Canceled);
900 assert_eq!(context.state(), CancellationContextState::Canceling);
901 assert!(!context.user_canceled());
902 assert!(context.first.is_cancelled());
903 assert!(context.second.is_cancelled());
904
905 assert_eq!(context.cancel(), CancellationContextState::Canceling);
907 assert_eq!(context.state(), CancellationContextState::Canceling);
908 assert!(!context.user_canceled());
909 assert!(context.first.is_cancelled());
910 assert!(context.second.is_cancelled());
911 }
912
913 #[test]
914 fn cancellation_child() {
915 let context = CancellationContext::new(FailureMode::Fast);
916 assert_eq!(context.state(), CancellationContextState::NotCanceled);
917
918 let child = context.child(FailureMode::Slow);
920 assert_eq!(child.state(), CancellationContextState::NotCanceled);
921 assert_eq!(child.cancel(), CancellationContextState::Waiting);
922 assert_eq!(child.cancel(), CancellationContextState::Canceling);
923 assert!(child.user_canceled());
924 assert!(child.first.is_cancelled());
925 assert!(child.second.is_cancelled());
926
927 assert_eq!(context.state(), CancellationContextState::NotCanceled);
929
930 let child = context.child(FailureMode::Fast);
932 assert_eq!(context.cancel(), CancellationContextState::Canceling);
933 assert!(child.first.is_cancelled());
934 assert!(child.second.is_cancelled());
935 }
936
937 #[test]
938 fn child_inherits_parent_cancellation_state() {
939 let parent = CancellationContext::new(FailureMode::Fast);
940 let child = parent.child(FailureMode::Fast);
941 assert_eq!(child.state(), CancellationContextState::NotCanceled);
942
943 assert_eq!(parent.cancel(), CancellationContextState::Canceling);
945
946 assert_eq!(child.state(), CancellationContextState::Canceling);
948 assert!(child.user_canceled());
949
950 assert!(child.first.is_cancelled());
952 assert!(child.second.is_cancelled());
953 }
954
955 #[test]
956 fn child_keeps_local_error_cause_on_tie_with_parent() {
957 let parent = CancellationContext::new(FailureMode::Fast);
958 let child = parent.child(FailureMode::Fast);
959
960 assert_eq!(parent.cancel(), CancellationContextState::Canceling);
962
963 child.error(&EvaluationError::Canceled);
966
967 assert_eq!(child.state(), CancellationContextState::Canceling);
971 assert!(!child.user_canceled());
972 }
973
974 #[test]
975 fn later_parent_cancel_does_not_relabel_child_error() {
976 let parent = CancellationContext::new(FailureMode::Fast);
977 let child = parent.child(FailureMode::Fast);
978
979 child.error(&EvaluationError::Canceled);
981 assert_eq!(child.state(), CancellationContextState::Canceling);
982 assert!(!child.user_canceled());
983
984 assert_eq!(parent.cancel(), CancellationContextState::Canceling);
987 assert_eq!(child.state(), CancellationContextState::Canceling);
988 assert!(!child.user_canceled());
989 }
990
991 #[test]
992 fn higher_parent_level_escalates_child_and_carries_its_cause() {
993 let parent = CancellationContext::new(FailureMode::Fast);
994 let child = parent.child(FailureMode::Slow);
995
996 child.error(&EvaluationError::Canceled);
998 assert_eq!(child.state(), CancellationContextState::Waiting);
999 assert!(!child.user_canceled());
1000
1001 assert_eq!(parent.cancel(), CancellationContextState::Canceling);
1005 assert_eq!(child.state(), CancellationContextState::Canceling);
1006 assert!(child.user_canceled());
1007 }
1008
1009 #[test]
1010 fn closest_descendant_wins_tie_across_multiple_levels() {
1011 let root = CancellationContext::new(FailureMode::Fast);
1012 let child = root.child(FailureMode::Fast);
1013 let grandchild = child.child(FailureMode::Fast);
1014
1015 assert_eq!(root.cancel(), CancellationContextState::Canceling);
1017
1018 grandchild.error(&EvaluationError::Canceled);
1021 assert_eq!(grandchild.state(), CancellationContextState::Canceling);
1022 assert!(!grandchild.user_canceled());
1023 }
1024
1025 #[test]
1026 fn grandchild_inherits_ancestor_cancellation() {
1027 let root = CancellationContext::new(FailureMode::Fast);
1028 let child = root.child(FailureMode::Fast);
1029 let grandchild = child.child(FailureMode::Fast);
1030 assert_eq!(grandchild.state(), CancellationContextState::NotCanceled);
1031
1032 assert_eq!(root.cancel(), CancellationContextState::Canceling);
1033
1034 assert_eq!(grandchild.state(), CancellationContextState::Canceling);
1036 assert!(grandchild.user_canceled());
1037 }
1038
1039 #[test]
1040 fn child_cancellation_does_not_affect_parent_or_siblings() {
1041 let parent = CancellationContext::new(FailureMode::Fast);
1042 let a = parent.child(FailureMode::Fast);
1043 let b = parent.child(FailureMode::Fast);
1044
1045 assert_eq!(a.cancel(), CancellationContextState::Canceling);
1046
1047 assert_eq!(parent.state(), CancellationContextState::NotCanceled);
1049 assert!(!parent.user_canceled());
1050 assert_eq!(b.state(), CancellationContextState::NotCanceled);
1051 assert!(!b.user_canceled());
1052 }
1053}