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::Object;
27use crate::Outputs;
28use crate::Value;
29use crate::backend::TaskExecutionResult;
30use crate::config::FailureMode;
31use crate::http::Transferer;
32
33mod trie;
34pub mod v1;
35
36const ROOT_NAME: &str = ".root";
40
41const CANCELLATION_STATE_NOT_CANCELED: u8 = 0;
43
44const CANCELLATION_STATE_WAITING: u8 = 1;
49
50const CANCELLATION_STATE_CANCELING: u8 = 2;
54
55const CANCELLATION_STATE_ERROR: u8 = 4;
60
61const CANCELLATION_STATE_MASK: u8 = 0x3;
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
66pub enum CancellationContextState {
67 NotCanceled,
69 Waiting,
72 Canceling,
75}
76
77impl CancellationContextState {
78 fn from_raw(raw: u8) -> Self {
80 match raw & CANCELLATION_STATE_MASK {
81 CANCELLATION_STATE_NOT_CANCELED => Self::NotCanceled,
82 CANCELLATION_STATE_WAITING => Self::Waiting,
83 CANCELLATION_STATE_CANCELING => Self::Canceling,
84 _ => unreachable!("unexpected cancellation context state"),
85 }
86 }
87
88 fn update(mode: FailureMode, error: bool, state: &Arc<AtomicU8>) -> Option<Self> {
93 let previous_state = state
95 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
96 if error && state != CANCELLATION_STATE_NOT_CANCELED {
98 return None;
99 }
100
101 let mut new_state = match state & CANCELLATION_STATE_MASK {
103 CANCELLATION_STATE_NOT_CANCELED => match mode {
104 FailureMode::Slow => CANCELLATION_STATE_WAITING,
105 FailureMode::Fast => CANCELLATION_STATE_CANCELING,
106 },
107 CANCELLATION_STATE_WAITING => CANCELLATION_STATE_CANCELING,
108 CANCELLATION_STATE_CANCELING => CANCELLATION_STATE_CANCELING,
109 _ => unreachable!("unexpected cancellation context state"),
110 };
111
112 if error {
114 new_state |= CANCELLATION_STATE_ERROR;
115 }
116
117 Some(new_state | (state & CANCELLATION_STATE_ERROR))
119 })
120 .ok()?;
121
122 match previous_state & CANCELLATION_STATE_MASK {
123 CANCELLATION_STATE_NOT_CANCELED => match mode {
124 FailureMode::Slow => Some(Self::Waiting),
125 FailureMode::Fast => Some(Self::Canceling),
126 },
127 CANCELLATION_STATE_WAITING => Some(Self::Canceling),
128 CANCELLATION_STATE_CANCELING => Some(Self::Canceling),
129 _ => unreachable!("unexpected cancellation context state"),
130 }
131 }
132}
133
134#[derive(Debug, Clone)]
138pub struct CancellationContext {
139 mode: FailureMode,
141 state: Arc<AtomicU8>,
143 parent: Option<Arc<CancellationContext>>,
146 first: CancellationToken,
148 second: CancellationToken,
152}
153
154impl CancellationContext {
155 pub fn new(mode: FailureMode) -> Self {
165 Self {
166 mode,
167 state: Arc::new(CANCELLATION_STATE_NOT_CANCELED.into()),
168 parent: None,
169 first: CancellationToken::new(),
170 second: CancellationToken::new(),
171 }
172 }
173
174 pub fn child(&self, mode: FailureMode) -> Self {
181 Self {
182 mode,
183 state: Arc::new(CANCELLATION_STATE_NOT_CANCELED.into()),
184 parent: Some(Arc::new(self.clone())),
185 first: self.first.child_token(),
186 second: self.second.child_token(),
187 }
188 }
189
190 fn effective(&self) -> (u8, bool) {
206 let raw = self.state.load(Ordering::SeqCst);
207 let level = raw & CANCELLATION_STATE_MASK;
208 let user =
209 level != CANCELLATION_STATE_NOT_CANCELED && (raw & CANCELLATION_STATE_ERROR == 0);
210
211 match &self.parent {
212 Some(parent) => match parent.effective() {
213 (parent_level, parent_user) if parent_level > level => (parent_level, parent_user),
214 _ => (level, user),
215 },
216 None => (level, user),
217 }
218 }
219
220 pub fn state(&self) -> CancellationContextState {
223 CancellationContextState::from_raw(self.effective().0)
224 }
225
226 #[must_use]
234 pub fn cancel(&self) -> CancellationContextState {
235 let state =
236 CancellationContextState::update(self.mode, false, &self.state).expect("should update");
237
238 match state {
239 CancellationContextState::NotCanceled => panic!("should be canceled"),
240 CancellationContextState::Waiting => self.first.cancel(),
241 CancellationContextState::Canceling => {
242 self.first.cancel();
243 self.second.cancel();
244 }
245 }
246
247 state
248 }
249
250 pub fn first(&self) -> CancellationToken {
260 self.first.clone()
261 }
262
263 pub fn second(&self) -> CancellationToken {
274 self.second.clone()
275 }
276
277 pub fn user_canceled(&self) -> bool {
280 self.effective().1
281 }
282
283 pub(crate) fn error(&self, error: &EvaluationError) {
290 if let Some(state) = CancellationContextState::update(self.mode, true, &self.state) {
291 let message: Cow<'_, str> = match error {
292 EvaluationError::Canceled => "evaluation was canceled".into(),
293 EvaluationError::Source(e) => e.diagnostic.message().into(),
294 EvaluationError::Other(e) => format!("{e:#}").into(),
295 };
296
297 match state {
298 CancellationContextState::NotCanceled => unreachable!("should be canceled"),
299 CancellationContextState::Waiting => {
300 self.first.cancel();
301
302 error!(
303 "an evaluation error occurred: waiting for any executing tasks to \
304 complete: {message}"
305 );
306 }
307 CancellationContextState::Canceling => {
308 self.first.cancel();
309 self.second.cancel();
310
311 error!(
312 "an evaluation error occurred: waiting for any executing tasks to cancel: \
313 {message}"
314 );
315 }
316 }
317 }
318 }
319}
320
321impl Default for CancellationContext {
322 fn default() -> Self {
323 Self::new(FailureMode::Slow)
324 }
325}
326
327#[derive(Debug, Clone)]
329pub enum EngineEvent {
330 ReusedCachedExecutionResult {
332 id: String,
334 },
335 TaskParked,
338 TaskUnparked {
340 canceled: bool,
342 },
343}
344
345#[derive(Debug, Clone, Default)]
347pub struct Events {
348 engine: Option<broadcast::Sender<EngineEvent>>,
352 crankshaft: Option<broadcast::Sender<CrankshaftEvent>>,
356 transfer: Option<broadcast::Sender<TransferEvent>>,
360}
361
362impl Events {
363 pub fn new(capacity: usize) -> Self {
365 Self {
366 engine: Some(broadcast::Sender::new(capacity)),
367 crankshaft: Some(broadcast::Sender::new(capacity)),
368 transfer: Some(broadcast::Sender::new(capacity)),
369 }
370 }
371
372 pub fn disabled() -> Self {
374 Self::default()
375 }
376
377 pub fn subscribe_engine(&self) -> Option<broadcast::Receiver<EngineEvent>> {
381 self.engine.as_ref().map(|s| s.subscribe())
382 }
383
384 pub fn subscribe_crankshaft(&self) -> Option<broadcast::Receiver<CrankshaftEvent>> {
388 self.crankshaft.as_ref().map(|s| s.subscribe())
389 }
390
391 pub fn subscribe_transfer(&self) -> Option<broadcast::Receiver<TransferEvent>> {
395 self.transfer.as_ref().map(|s| s.subscribe())
396 }
397
398 pub(crate) fn engine(&self) -> &Option<broadcast::Sender<EngineEvent>> {
400 &self.engine
401 }
402
403 pub(crate) fn crankshaft(&self) -> &Option<broadcast::Sender<CrankshaftEvent>> {
405 &self.crankshaft
406 }
407
408 pub(crate) fn transfer(&self) -> &Option<broadcast::Sender<TransferEvent>> {
410 &self.transfer
411 }
412}
413
414#[derive(Debug, Clone)]
416pub struct CallLocation {
417 pub document: Document,
419 pub span: Span,
421}
422
423#[derive(Debug)]
425pub struct SourceError {
426 pub document: Document,
428 pub diagnostic: Diagnostic,
430 pub backtrace: Vec<CallLocation>,
437}
438
439#[derive(Debug)]
441pub enum EvaluationError {
442 Canceled,
444 Source(Box<SourceError>),
446 Other(anyhow::Error),
448}
449
450impl EvaluationError {
451 pub fn new(document: Document, diagnostic: Diagnostic) -> Self {
453 Self::Source(Box::new(SourceError {
454 document,
455 diagnostic,
456 backtrace: Default::default(),
457 }))
458 }
459
460 #[allow(clippy::inherent_to_string)]
462 pub fn to_string(&self) -> String {
463 use std::collections::HashMap;
464
465 use codespan_reporting::diagnostic::Label;
466 use codespan_reporting::diagnostic::LabelStyle;
467 use codespan_reporting::files::SimpleFiles;
468 use codespan_reporting::term;
469 use codespan_reporting::term::Config;
470 use codespan_reporting::term::termcolor::Buffer;
471 use wdl_ast::AstNode;
472
473 match self {
474 Self::Canceled => "evaluation was canceled".to_string(),
475 Self::Source(e) => {
476 let mut files = SimpleFiles::new();
477 let mut map = HashMap::new();
478
479 let file_id = files.add(e.document.path(), e.document.root().text().to_string());
480
481 let diagnostic =
482 e.diagnostic
483 .to_codespan(file_id)
484 .with_labels_iter(e.backtrace.iter().map(|l| {
485 let id = l.document.id();
486 let file_id = *map.entry(id).or_insert_with(|| {
487 files.add(l.document.path(), l.document.root().text().to_string())
488 });
489
490 Label {
491 style: LabelStyle::Secondary,
492 file_id,
493 range: l.span.start()..l.span.end(),
494 message: "called from this location".into(),
495 }
496 }));
497
498 let mut buffer = Buffer::no_color();
499 term::emit_to_write_style(&mut buffer, &Config::default(), &files, &diagnostic)
500 .expect("failed to emit diagnostic");
501
502 String::from_utf8(buffer.into_inner()).expect("should be UTF-8")
503 }
504 Self::Other(e) => format!("{e:#}"),
505 }
506 }
507}
508
509impl From<anyhow::Error> for EvaluationError {
510 fn from(e: anyhow::Error) -> Self {
511 Self::Other(e)
512 }
513}
514
515pub type EvaluationResult<T> = Result<T, EvaluationError>;
517
518pub(crate) trait EvaluationContext: Send + Sync {
520 fn version(&self) -> SupportedVersion;
522
523 fn resolve_name(&self, name: &str, span: Span) -> Result<Value, Diagnostic>;
525
526 fn resolve_type_name(&self, name: &str, span: Span) -> Result<Type, Diagnostic>;
528
529 fn enum_choice_value(&self, enum_name: &str, choice_name: &str) -> Result<Value, Diagnostic>;
531
532 fn base_dir(&self) -> &EvaluationPath;
541
542 fn temp_dir(&self) -> &Path;
544
545 fn stdout(&self) -> Option<&Value> {
549 None
550 }
551
552 fn stderr(&self) -> Option<&Value> {
556 None
557 }
558
559 fn task(&self) -> Option<&Task> {
563 None
564 }
565
566 fn transferer(&self) -> &dyn Transferer;
568
569 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
574 let _ = path;
575 None
576 }
577
578 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
583 let _ = path;
584 None
585 }
586
587 fn notify_file_created(&mut self, path: &HostPath) -> Result<()> {
592 let _ = path;
593 Ok(())
594 }
595
596 fn object_access(&self, object: &Object, name: &str) -> Option<Value> {
603 let _ = (object, name);
604 None
605 }
606}
607
608#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
610struct ScopeIndex(usize);
611
612impl ScopeIndex {
613 pub const fn new(index: usize) -> Self {
615 Self(index)
616 }
617}
618
619impl From<usize> for ScopeIndex {
620 fn from(index: usize) -> Self {
621 Self(index)
622 }
623}
624
625impl From<ScopeIndex> for usize {
626 fn from(index: ScopeIndex) -> Self {
627 index.0
628 }
629}
630
631#[derive(Default, Debug)]
633struct Scope {
634 parent: Option<ScopeIndex>,
638 names: IndexMap<String, Value>,
640}
641
642impl Scope {
643 pub fn new(parent: ScopeIndex) -> Self {
645 Self {
646 parent: Some(parent),
647 names: Default::default(),
648 }
649 }
650
651 pub fn insert(&mut self, name: impl Into<String>, value: impl Into<Value>) {
653 let prev = self.names.insert(name.into(), value.into());
654 assert!(prev.is_none(), "conflicting name in scope");
655 }
656
657 pub fn local(&self) -> impl Iterator<Item = (&str, &Value)> + use<'_> {
659 self.names.iter().map(|(k, v)| (k.as_str(), v))
660 }
661
662 pub(crate) fn get_mut(&mut self, name: &str) -> Option<&mut Value> {
664 self.names.get_mut(name)
665 }
666
667 pub(crate) fn clear(&mut self) {
669 self.parent = None;
670 self.names.clear();
671 }
672
673 pub(crate) fn set_parent(&mut self, parent: ScopeIndex) {
675 self.parent = Some(parent);
676 }
677}
678
679impl From<Scope> for IndexMap<String, Value> {
680 fn from(scope: Scope) -> Self {
681 scope.names
682 }
683}
684
685impl From<Scope> for Outputs {
686 fn from(scope: Scope) -> Self {
687 scope.names.into()
688 }
689}
690
691#[derive(Debug, Clone, Copy)]
693struct ScopeRef<'a> {
694 scopes: &'a [Scope],
696 index: ScopeIndex,
698}
699
700impl<'a> ScopeRef<'a> {
701 pub fn new(scopes: &'a [Scope], index: impl Into<ScopeIndex>) -> Self {
703 Self {
704 scopes,
705 index: index.into(),
706 }
707 }
708
709 pub fn parent(&self) -> Option<Self> {
713 self.scopes[self.index.0].parent.map(|p| Self {
714 scopes: self.scopes,
715 index: p,
716 })
717 }
718
719 pub fn local(&self, name: &str) -> Option<&Value> {
723 self.scopes[self.index.0].names.get(name)
724 }
725
726 pub fn lookup(&self, name: &str) -> Option<&Value> {
730 let mut current = Some(self.index);
731
732 while let Some(index) = current {
733 if let Some(name) = self.scopes[index.0].names.get(name) {
734 return Some(name);
735 }
736
737 current = self.scopes[index.0].parent;
738 }
739
740 None
741 }
742}
743
744#[derive(Debug)]
752pub struct EvaluatedTask {
753 result: TaskExecutionResult,
755 outputs: Outputs,
757 error: Option<EvaluationError>,
761 cached: bool,
763}
764
765impl EvaluatedTask {
766 fn new(cached: bool, result: TaskExecutionResult, error: Option<EvaluationError>) -> Self {
768 Self {
769 result,
770 outputs: Default::default(),
771 error,
772 cached,
773 }
774 }
775
776 pub fn failed(&self) -> bool {
779 self.error.is_some()
780 }
781
782 pub fn cached(&self) -> bool {
785 self.cached
786 }
787
788 pub fn exit_code(&self) -> i32 {
790 self.result.exit_code
791 }
792
793 pub fn work_dir(&self) -> &EvaluationPath {
795 &self.result.work_dir
796 }
797
798 pub fn stdout(&self) -> &Value {
800 &self.result.stdout
801 }
802
803 pub fn stderr(&self) -> &Value {
805 &self.result.stderr
806 }
807
808 pub fn into_outputs(self) -> EvaluationResult<Outputs> {
813 match self.error {
814 Some(e) => Err(e),
815 None => Ok(self.outputs),
816 }
817 }
818}
819
820#[cfg(test)]
821mod test {
822 use super::*;
823
824 #[test]
825 fn cancellation_slow() {
826 let context = CancellationContext::new(FailureMode::Slow);
827 assert_eq!(context.state(), CancellationContextState::NotCanceled);
828
829 assert_eq!(context.cancel(), CancellationContextState::Waiting);
831 assert_eq!(context.state(), CancellationContextState::Waiting);
832 assert!(context.user_canceled());
833 assert!(context.first.is_cancelled());
834 assert!(!context.second.is_cancelled());
835
836 assert_eq!(context.cancel(), CancellationContextState::Canceling);
838 assert_eq!(context.state(), CancellationContextState::Canceling);
839 assert!(context.user_canceled());
840 assert!(context.first.is_cancelled());
841 assert!(context.second.is_cancelled());
842
843 assert_eq!(context.cancel(), CancellationContextState::Canceling);
845 assert_eq!(context.state(), CancellationContextState::Canceling);
846 assert!(context.user_canceled());
847 assert!(context.first.is_cancelled());
848 assert!(context.second.is_cancelled());
849 }
850
851 #[test]
852 fn cancellation_fast() {
853 let context = CancellationContext::new(FailureMode::Fast);
854 assert_eq!(context.state(), CancellationContextState::NotCanceled);
855
856 assert_eq!(context.cancel(), CancellationContextState::Canceling);
858 assert_eq!(context.state(), CancellationContextState::Canceling);
859 assert!(context.user_canceled());
860 assert!(context.first.is_cancelled());
861 assert!(context.second.is_cancelled());
862
863 assert_eq!(context.cancel(), CancellationContextState::Canceling);
865 assert_eq!(context.state(), CancellationContextState::Canceling);
866 assert!(context.user_canceled());
867 assert!(context.first.is_cancelled());
868 assert!(context.second.is_cancelled());
869 }
870
871 #[test]
872 fn cancellation_error_slow() {
873 let context = CancellationContext::new(FailureMode::Slow);
874 assert_eq!(context.state(), CancellationContextState::NotCanceled);
875
876 context.error(&EvaluationError::Canceled);
878 assert_eq!(context.state(), CancellationContextState::Waiting);
879 assert!(!context.user_canceled());
880 assert!(context.first.is_cancelled());
881 assert!(!context.second.is_cancelled());
882
883 context.error(&EvaluationError::Canceled);
885 assert_eq!(context.state(), CancellationContextState::Waiting);
886 assert!(!context.user_canceled());
887 assert!(context.first.is_cancelled());
888 assert!(!context.second.is_cancelled());
889
890 assert_eq!(context.cancel(), CancellationContextState::Canceling);
892 assert_eq!(context.state(), CancellationContextState::Canceling);
893 assert!(!context.user_canceled());
894 assert!(context.first.is_cancelled());
895 assert!(context.second.is_cancelled());
896 }
897
898 #[test]
899 fn cancellation_error_fast() {
900 let context = CancellationContext::new(FailureMode::Fast);
901 assert_eq!(context.state(), CancellationContextState::NotCanceled);
902
903 context.error(&EvaluationError::Canceled);
905 assert_eq!(context.state(), CancellationContextState::Canceling);
906 assert!(!context.user_canceled());
907 assert!(context.first.is_cancelled());
908 assert!(context.second.is_cancelled());
909
910 context.error(&EvaluationError::Canceled);
912 assert_eq!(context.state(), CancellationContextState::Canceling);
913 assert!(!context.user_canceled());
914 assert!(context.first.is_cancelled());
915 assert!(context.second.is_cancelled());
916
917 assert_eq!(context.cancel(), CancellationContextState::Canceling);
919 assert_eq!(context.state(), CancellationContextState::Canceling);
920 assert!(!context.user_canceled());
921 assert!(context.first.is_cancelled());
922 assert!(context.second.is_cancelled());
923 }
924
925 #[test]
926 fn cancellation_child() {
927 let context = CancellationContext::new(FailureMode::Fast);
928 assert_eq!(context.state(), CancellationContextState::NotCanceled);
929
930 let child = context.child(FailureMode::Slow);
932 assert_eq!(child.state(), CancellationContextState::NotCanceled);
933 assert_eq!(child.cancel(), CancellationContextState::Waiting);
934 assert_eq!(child.cancel(), CancellationContextState::Canceling);
935 assert!(child.user_canceled());
936 assert!(child.first.is_cancelled());
937 assert!(child.second.is_cancelled());
938
939 assert_eq!(context.state(), CancellationContextState::NotCanceled);
941
942 let child = context.child(FailureMode::Fast);
944 assert_eq!(context.cancel(), CancellationContextState::Canceling);
945 assert!(child.first.is_cancelled());
946 assert!(child.second.is_cancelled());
947 }
948
949 #[test]
950 fn child_inherits_parent_cancellation_state() {
951 let parent = CancellationContext::new(FailureMode::Fast);
952 let child = parent.child(FailureMode::Fast);
953 assert_eq!(child.state(), CancellationContextState::NotCanceled);
954
955 assert_eq!(parent.cancel(), CancellationContextState::Canceling);
957
958 assert_eq!(child.state(), CancellationContextState::Canceling);
960 assert!(child.user_canceled());
961
962 assert!(child.first.is_cancelled());
964 assert!(child.second.is_cancelled());
965 }
966
967 #[test]
968 fn child_keeps_local_error_cause_on_tie_with_parent() {
969 let parent = CancellationContext::new(FailureMode::Fast);
970 let child = parent.child(FailureMode::Fast);
971
972 assert_eq!(parent.cancel(), CancellationContextState::Canceling);
974
975 child.error(&EvaluationError::Canceled);
978
979 assert_eq!(child.state(), CancellationContextState::Canceling);
983 assert!(!child.user_canceled());
984 }
985
986 #[test]
987 fn later_parent_cancel_does_not_relabel_child_error() {
988 let parent = CancellationContext::new(FailureMode::Fast);
989 let child = parent.child(FailureMode::Fast);
990
991 child.error(&EvaluationError::Canceled);
993 assert_eq!(child.state(), CancellationContextState::Canceling);
994 assert!(!child.user_canceled());
995
996 assert_eq!(parent.cancel(), CancellationContextState::Canceling);
999 assert_eq!(child.state(), CancellationContextState::Canceling);
1000 assert!(!child.user_canceled());
1001 }
1002
1003 #[test]
1004 fn higher_parent_level_escalates_child_and_carries_its_cause() {
1005 let parent = CancellationContext::new(FailureMode::Fast);
1006 let child = parent.child(FailureMode::Slow);
1007
1008 child.error(&EvaluationError::Canceled);
1010 assert_eq!(child.state(), CancellationContextState::Waiting);
1011 assert!(!child.user_canceled());
1012
1013 assert_eq!(parent.cancel(), CancellationContextState::Canceling);
1017 assert_eq!(child.state(), CancellationContextState::Canceling);
1018 assert!(child.user_canceled());
1019 }
1020
1021 #[test]
1022 fn closest_descendant_wins_tie_across_multiple_levels() {
1023 let root = CancellationContext::new(FailureMode::Fast);
1024 let child = root.child(FailureMode::Fast);
1025 let grandchild = child.child(FailureMode::Fast);
1026
1027 assert_eq!(root.cancel(), CancellationContextState::Canceling);
1029
1030 grandchild.error(&EvaluationError::Canceled);
1033 assert_eq!(grandchild.state(), CancellationContextState::Canceling);
1034 assert!(!grandchild.user_canceled());
1035 }
1036
1037 #[test]
1038 fn grandchild_inherits_ancestor_cancellation() {
1039 let root = CancellationContext::new(FailureMode::Fast);
1040 let child = root.child(FailureMode::Fast);
1041 let grandchild = child.child(FailureMode::Fast);
1042 assert_eq!(grandchild.state(), CancellationContextState::NotCanceled);
1043
1044 assert_eq!(root.cancel(), CancellationContextState::Canceling);
1045
1046 assert_eq!(grandchild.state(), CancellationContextState::Canceling);
1048 assert!(grandchild.user_canceled());
1049 }
1050
1051 #[test]
1052 fn child_cancellation_does_not_affect_parent_or_siblings() {
1053 let parent = CancellationContext::new(FailureMode::Fast);
1054 let a = parent.child(FailureMode::Fast);
1055 let b = parent.child(FailureMode::Fast);
1056
1057 assert_eq!(a.cancel(), CancellationContextState::Canceling);
1058
1059 assert_eq!(parent.state(), CancellationContextState::NotCanceled);
1061 assert!(!parent.user_canceled());
1062 assert_eq!(b.state(), CancellationContextState::NotCanceled);
1063 assert!(!b.user_canceled());
1064 }
1065}