@@ -732,24 +732,25 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags,
732
732
AsyncTask *currentTask = swift_task_getCurrent ();
733
733
AsyncTask *parent = jobFlags.task_isChildTask () ? currentTask : nullptr ;
734
734
735
+ // / FIXME: this is the fail path
735
736
if (group) {
736
737
assert (parent && " a task created in a group must be a child task" );
737
-
738
- // Prevent task-local misuse;
739
- // We must not allow an addTask {} wrapped immediately with a withValue {}
740
- auto ParentLocal = parent->_private ().Local ;
741
- if (auto taskLocalHeadLinkType = ParentLocal.peekHeadLinkType ()) {
742
- if (taskLocalHeadLinkType ==
743
- swift::TaskLocal::NextLinkType::IsNextCreatedInTaskGroupBody) {
744
- #if !SWIFT_CONCURRENCY_EMBEDDED
745
- swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroup (
746
- nullptr , 0 , true , 0 );
747
- #endif
748
- // TODO(ktoso): If we were to keep this crash mode; offer a better failure for embedded swift
749
- abort ();
750
- }
751
- }
752
-
738
+ //
739
+ // // Prevent task-local misuse;
740
+ // // We must not allow an addTask {} wrapped immediately with a withValue {}
741
+ // auto ParentLocal = parent->_private().Local;
742
+ // if (auto taskLocalHeadLinkType = ParentLocal.peekHeadLinkType()) {
743
+ // if (taskLocalHeadLinkType ==
744
+ // swift::TaskLocal::NextLinkType::IsNextCreatedInTaskGroupBody) {
745
+ // #if !SWIFT_CONCURRENCY_EMBEDDED
746
+ // swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroup(
747
+ // nullptr, 0, true, 0);
748
+ // #endif
749
+ // // TODO(ktoso): If we were to keep this crash mode; offer a better failure for embedded swift
750
+ // abort();
751
+ // }
752
+ // }
753
+ //
753
754
// Add to the task group, if requested.
754
755
if (taskCreateFlags.addPendingGroupTaskUnconditionally ()) {
755
756
assert (group && " Missing group" );
@@ -998,12 +999,44 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags,
998
999
// In a task group we would not have allowed the `add` to create a child anymore,
999
1000
// however better safe than sorry and `async let` are not expressed as task groups,
1000
1001
// so they may have been spawned in any case still.
1001
- if (swift_task_isCancelled (parent) ||
1002
- (group && group->isCancelled ()))
1002
+ if ((group && group->isCancelled ()) || swift_task_isCancelled (parent))
1003
1003
swift_task_cancel (task);
1004
1004
1005
- // Initialize task locals with a link to the parent task.
1006
- task->_private ().Local .initializeLinkParent (task, parent);
1005
+ // Initialize task locals storage
1006
+ bool taskLocalStorageInitialized = false ;
1007
+
1008
+ // Inside a task group, we may have to perform some defensive copying,
1009
+ // check if doing so is necessary, and initialize storage using partial
1010
+ // defensive copies if necessary.
1011
+ if (group) {
1012
+ assert (parent && " a task created in a group must be a child task" );
1013
+ // We are a child task in a task group; and it may happen that we are calling
1014
+ // addTask specifically in such shape:
1015
+ //
1016
+ // $local.withValue(theValue) { addTask {} }
1017
+ //
1018
+ // If this is the case, we MUST copy `theValue` (and any other such directly
1019
+ // wrapping the addTask value bindings), because those values will be popped
1020
+ // when withValue returns - breaking our structured concurrency guarantees
1021
+ // that we rely on for the "link directly to parent's task local Item".
1022
+ //
1023
+ // Values set outside the task group are not subject to this problem, as
1024
+ // their structural lifetime guarantee is upheld by the group scope
1025
+ // out-living any addTask created tasks.
1026
+ auto ParentLocal = parent->_private ().Local ;
1027
+ if (auto taskLocalHeadLinkType = ParentLocal.peekHeadLinkType ()) {
1028
+ if (taskLocalHeadLinkType ==
1029
+ swift::TaskLocal::NextLinkType::IsNextCreatedInTaskGroupBody) {
1030
+ swift_task_localsOnlyCurrentCopyTo (task);
1031
+ taskLocalStorageInitialized = true ;
1032
+ }
1033
+ }
1034
+ }
1035
+
1036
+ if (!taskLocalStorageInitialized) {
1037
+ // just initialize the storage normally
1038
+ task->_private ().Local .initializeLinkParent (task, parent);
1039
+ }
1007
1040
}
1008
1041
1009
1042
// Configure the initial context.
@@ -1065,7 +1098,7 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags,
1065
1098
#endif
1066
1099
swift_retain (task);
1067
1100
task->flagAsAndEnqueueOnExecutor (
1068
- serialExecutor); // FIXME: pass the task executor explicitly?
1101
+ serialExecutor);
1069
1102
}
1070
1103
1071
1104
return {task, initialContext};
0 commit comments