|
30 | 30 | #include "swift/ABI/Task.h" |
31 | 31 | #include "swift/ABI/TaskLocal.h" |
32 | 32 | #include "swift/ABI/TaskOptions.h" |
| 33 | +#include "swift/Basic/Lazy.h" |
33 | 34 | #include "swift/Runtime/Concurrency.h" |
| 35 | +#include "swift/Runtime/EnvironmentVariables.h" |
34 | 36 | #include "swift/Runtime/HeapObject.h" |
35 | 37 | #include "swift/Threading/Mutex.h" |
36 | 38 | #include <atomic> |
37 | 39 | #include <new> |
| 40 | +#include <unordered_set> |
38 | 41 |
|
39 | 42 | #if SWIFT_CONCURRENCY_ENABLE_DISPATCH |
40 | 43 | #include <dispatch/dispatch.h> |
@@ -1238,9 +1241,59 @@ swift_task_enqueueTaskOnExecutorImpl(AsyncTask *task, ExecutorRef executor) |
1238 | 1241 | task->flagAsAndEnqueueOnExecutor(executor); |
1239 | 1242 | } |
1240 | 1243 |
|
| 1244 | +namespace continuationChecking { |
| 1245 | + |
| 1246 | +enum class State : uint8_t { Uninitialized, On, Off }; |
| 1247 | + |
| 1248 | +static std::atomic<State> CurrentState; |
| 1249 | + |
| 1250 | +static LazyMutex ActiveContinuationsLock; |
| 1251 | +static Lazy<std::unordered_set<ContinuationAsyncContext *>> ActiveContinuations; |
| 1252 | + |
| 1253 | +static bool isEnabled() { |
| 1254 | + auto state = CurrentState.load(std::memory_order_relaxed); |
| 1255 | + if (state == State::Uninitialized) { |
| 1256 | + bool enabled = |
| 1257 | + runtime::environment::concurrencyValidateUncheckedContinuations(); |
| 1258 | + state = enabled ? State::On : State::Off; |
| 1259 | + CurrentState.store(state, std::memory_order_relaxed); |
| 1260 | + } |
| 1261 | + return state == State::On; |
| 1262 | +} |
| 1263 | + |
| 1264 | +static void init(ContinuationAsyncContext *context) { |
| 1265 | + if (!isEnabled()) |
| 1266 | + return; |
| 1267 | + |
| 1268 | + LazyMutex::ScopedLock guard(ActiveContinuationsLock); |
| 1269 | + auto result = ActiveContinuations.get().insert(context); |
| 1270 | + auto inserted = std::get<1>(result); |
| 1271 | + if (!inserted) |
| 1272 | + swift_Concurrency_fatalError( |
| 1273 | + 0, |
| 1274 | + "Initializing continuation context %p that was already initialized.\n", |
| 1275 | + context); |
| 1276 | +} |
| 1277 | + |
| 1278 | +static void willResume(ContinuationAsyncContext *context) { |
| 1279 | + if (!isEnabled()) |
| 1280 | + return; |
| 1281 | + |
| 1282 | + LazyMutex::ScopedLock guard(ActiveContinuationsLock); |
| 1283 | + auto removed = ActiveContinuations.get().erase(context); |
| 1284 | + if (!removed) |
| 1285 | + swift_Concurrency_fatalError(0, |
| 1286 | + "Resuming continuation context %p that was not awaited " |
| 1287 | + "(may have already been resumed).\n", |
| 1288 | + context); |
| 1289 | +} |
| 1290 | + |
| 1291 | +} // namespace continuationChecking |
| 1292 | + |
1241 | 1293 | SWIFT_CC(swift) |
1242 | 1294 | static AsyncTask *swift_continuation_initImpl(ContinuationAsyncContext *context, |
1243 | 1295 | AsyncContinuationFlags flags) { |
| 1296 | + continuationChecking::init(context); |
1244 | 1297 | context->Flags = ContinuationAsyncContext::FlagsType(); |
1245 | 1298 | if (flags.canThrow()) context->Flags.setCanThrow(true); |
1246 | 1299 | if (flags.isExecutorSwitchForced()) |
@@ -1341,6 +1394,8 @@ static void swift_continuation_awaitImpl(ContinuationAsyncContext *context) { |
1341 | 1394 |
|
1342 | 1395 | static void resumeTaskAfterContinuation(AsyncTask *task, |
1343 | 1396 | ContinuationAsyncContext *context) { |
| 1397 | + continuationChecking::willResume(context); |
| 1398 | + |
1344 | 1399 | auto &sync = context->AwaitSynchronization; |
1345 | 1400 | auto status = sync.load(std::memory_order_acquire); |
1346 | 1401 | assert(status != ContinuationStatus::Resumed && |
|
0 commit comments