@@ -316,10 +316,54 @@ bool _task_serialExecutor_isSameExclusiveExecutionContext(
316
316
const Metadata *selfType,
317
317
const SerialExecutorWitnessTable *wtable);
318
318
319
+ // We currently still support "legacy mode" in which isCurrentExecutor is NOT
320
+ // allowed to crash, because it is used to power "log warnings" data race
321
+ // detector. This mode is going away in Swift 6, but until then we allow this.
322
+ // This override exists primarily to be able to test both code-paths.
323
+ enum IsCurrentExecutorCheckMode: unsigned {
324
+ // / The default mode when an app was compiled against "new" enough SDK.
325
+ // / It allows crashing in isCurrentExecutor, and calls into `checkIsolated`.
326
+ Default_UseCheckIsolated_AllowCrash,
327
+ // / Legacy mode; Primarily to support old applications which used data race
328
+ // / detector with "warning" mode, which is no longer supported. When such app
329
+ // / is re-compiled against a new SDK, it will see crashes in what was
330
+ // / previously warnings; however, until until recompiled, warnings will be
331
+ // / used, and `checkIsolated` cannot be invoked.
332
+ Legacy_NoCheckIsolated_NonCrashing,
333
+ };
334
+ static IsCurrentExecutorCheckMode isCurrentExecutorMode =
335
+ Default_UseCheckIsolated_AllowCrash;
336
+
337
+ // Check override of executor checking mode.
338
+ static void checkIsCurrentExecutorMode (void *context) {
339
+ auto useLegacyMode =
340
+ swift_bincompat_useLegacyNonCrashingExecutorChecks ();
341
+
342
+ // Potentially, override the platform detected mode, primarily used in tests.
343
+ #if SWIFT_STDLIB_HAS_ENVIRON
344
+ const char *modeStr = getenv (" SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE" );
345
+ if (!modeStr)
346
+ return ;
347
+
348
+ if (strcmp (modeStr, " nocrash" ) == 0 ) {
349
+ useLegacyMode = Legacy_NoCheckIsolated_NonCrashing;
350
+ } else if (strcmp (modeStr, " crash" ) == 0 ) {
351
+ useLegacyMode = Default_UseCheckIsolated_AllowCrash;
352
+ } // else, just use the platform detected mode
353
+ #endif // SWIFT_STDLIB_HAS_ENVIRON
354
+ isCurrentExecutorMode = useLegacyMode ? Legacy_NoCheckIsolated_NonCrashing
355
+ : Default_UseCheckIsolated_AllowCrash;
356
+ }
357
+
319
358
SWIFT_CC (swift)
320
359
static bool swift_task_isCurrentExecutorImpl (SerialExecutorRef expectedExecutor) {
321
360
auto current = ExecutorTrackingInfo::current ();
322
361
362
+ // To support old applications on apple platforms which assumed this call
363
+ // does not crash, try to use a more compatible mode for those apps.
364
+ static swift::once_t checkModeToken;
365
+ swift::once (checkModeToken, checkIsCurrentExecutorMode, nullptr );
366
+
323
367
if (!current) {
324
368
// We have no current executor, i.e. we are running "outside" of Swift
325
369
// Concurrency. We could still be running on a thread/queue owned by
@@ -328,16 +372,24 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
328
372
329
373
// Are we expecting the main executor and are using the main thread?
330
374
if (expectedExecutor.isMainExecutor () && isExecutingOnMainThread ()) {
331
- // TODO(concurrency): consider removing this special case, as checkIsolated will compare against mainQueue already
375
+ // Due to compatibility with pre-checkIsolated code, we cannot remove
376
+ // this special handling. CheckIsolated can handle this if the expected
377
+ // executor is the main queue / main executor, however, if we cannot call
378
+ // checkIsolated we cannot rely on it to handle this.
379
+ // TODO: consider removing this branch when `useCrashingCheckIsolated=true`
332
380
return true ;
333
381
}
334
382
335
383
// Otherwise, as last resort, let the expected executor check using
336
384
// external means, as it may "know" this thread is managed by it etc.
337
- swift_task_checkIsolated (expectedExecutor);
385
+ if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
386
+ swift_task_checkIsolated (expectedExecutor);
387
+ // checkIsolated did not crash, so we are on the right executor, after all!
388
+ return true ;
389
+ }
338
390
339
- // checkIsolated did not crash, so we are on the right executor, after all!
340
- return true ;
391
+ assert (isCurrentExecutorMode == Legacy_NoCheckIsolated_NonCrashing);
392
+ return false ;
341
393
}
342
394
343
395
SerialExecutorRef currentExecutor = current->getActiveExecutor ();
@@ -416,10 +468,16 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
416
468
// Note that this only works because the closure in assumeIsolated is
417
469
// synchronous, and will not cause suspensions, as that would require the
418
470
// presence of a Task.
419
- swift_task_checkIsolated (expectedExecutor);
471
+ // compat_invoke_swift_task_checkIsolated(expectedExecutor);
472
+ if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
473
+ swift_task_checkIsolated (expectedExecutor);
474
+ // The checkIsolated call did not crash, so we are on the right executor.
475
+ return true ;
476
+ }
420
477
421
- // The checkIsolated call did not crash, so we are on the right executor.
422
- return true ;
478
+ // Using legacy mode, if no explicit executor match worked, we assume `false`
479
+ assert (isCurrentExecutorMode == Legacy_NoCheckIsolated_NonCrashing);
480
+ return false ;
423
481
}
424
482
425
483
// / Logging level for unexpected executors:
@@ -431,7 +489,7 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
431
489
// / an application was linked to. Since Swift 6 the default is to crash,
432
490
// / and the logging behavior is no longer available.
433
491
static unsigned unexpectedExecutorLogLevel =
434
- swift_bincompat_useLegacyWarningModeReportUnexpectedExecutor ()
492
+ swift_bincompat_useLegacyNonCrashingExecutorChecks ()
435
493
? 1 // legacy apps default to the logging mode, and cannot use `checkIsolated`
436
494
: 2 ; // new apps will only crash upon concurrency violations, and will call into `checkIsolated`
437
495
0 commit comments