@@ -324,7 +324,7 @@ bool _task_serialExecutor_isSameExclusiveExecutionContext(
324
324
enum IsCurrentExecutorCheckMode: unsigned {
325
325
// / The default mode when an app was compiled against "new" enough SDK.
326
326
// / It allows crashing in isCurrentExecutor, and calls into `checkIsolated`.
327
- Default_UseCheckIsolated_AllowCrash ,
327
+ Swift6_UseCheckIsolated_AllowCrash ,
328
328
// / Legacy mode; Primarily to support old applications which used data race
329
329
// / detector with "warning" mode, which is no longer supported. When such app
330
330
// / is re-compiled against a new SDK, it will see crashes in what was
@@ -333,17 +333,18 @@ enum IsCurrentExecutorCheckMode: unsigned {
333
333
Legacy_NoCheckIsolated_NonCrashing,
334
334
};
335
335
static IsCurrentExecutorCheckMode isCurrentExecutorMode =
336
- Default_UseCheckIsolated_AllowCrash ;
336
+ Swift6_UseCheckIsolated_AllowCrash ;
337
337
338
338
339
339
// Shimming call to Swift runtime because Swift Embedded does not have
340
- // these symbols defined
340
+ // these symbols defined.
341
341
bool swift_bincompat_useLegacyNonCrashingExecutorChecks () {
342
342
#if !SWIFT_CONCURRENCY_EMBEDDED
343
343
return swift::runtime::bincompat::
344
344
swift_bincompat_useLegacyNonCrashingExecutorChecks ();
345
- #endif
345
+ #else
346
346
return false ;
347
+ #endif
347
348
}
348
349
349
350
// Check override of executor checking mode.
@@ -353,19 +354,17 @@ static void checkIsCurrentExecutorMode(void *context) {
353
354
354
355
// Potentially, override the platform detected mode, primarily used in tests.
355
356
#if SWIFT_STDLIB_HAS_ENVIRON
356
- if (const char *modeStr =
357
- runtime::environment::concurrencyIsCurrentExecutorLegacyModeOverride ()) {
358
- if (modeStr) {
359
- if (strcmp (modeStr, " nocrash" ) == 0 ) {
360
- useLegacyMode = true ;
361
- } else if (strcmp (modeStr, " crash" ) == 0 ) {
362
- useLegacyMode = false ;
363
- } // else, just use the platform detected mode
364
- }
357
+ if (const char *modeStr = runtime::environment::concurrencyIsCurrentExecutorLegacyModeOverride ()) {
358
+ if (strcmp (modeStr, " nocrash" ) == 0 || strcmp (modeStr, " legacy" ) == 0 ) {
359
+ useLegacyMode = true ;
360
+ } else if (strcmp (modeStr, " crash" ) == 0 || strcmp (modeStr, " swift6" ) == 0 ) {
361
+ useLegacyMode = false ;
362
+ } // else, just use the platform detected mode
365
363
}
366
364
#endif // SWIFT_STDLIB_HAS_ENVIRON
365
+
367
366
isCurrentExecutorMode = useLegacyMode ? Legacy_NoCheckIsolated_NonCrashing
368
- : Default_UseCheckIsolated_AllowCrash ;
367
+ : Swift6_UseCheckIsolated_AllowCrash ;
369
368
}
370
369
371
370
SWIFT_CC (swift)
@@ -389,20 +388,27 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
389
388
// the expected executor however, so we need to try a bit harder before
390
389
// we fail.
391
390
392
- // Are we expecting the main executor and are using the main thread?
393
- if (expectedExecutor.isMainExecutor () && isExecutingOnMainThread ()) {
394
- // Due to compatibility with pre-checkIsolated code, we cannot remove
395
- // this special handling. CheckIsolated can handle this if the expected
396
- // executor is the main queue / main executor, however, if we cannot call
397
- // checkIsolated we cannot rely on it to handle this.
398
- // TODO: consider removing this branch when `useCrashingCheckIsolated=true`
399
- return true ;
391
+ // Legacy special handling the main executor by detecting the main thread.
392
+ //
393
+ // When 'checkIsolated' is available it will perform a dispatch queue assertion
394
+ // against the main queue, potentially resulting in a crash (expected).
395
+ //
396
+ // In legacy mode, we cannot allow crashes here, and therefore we keep the
397
+ // special best-effort handling of the "main thread".
398
+ if (isCurrentExecutorMode == Legacy_NoCheckIsolated_NonCrashing) {
399
+ if (expectedExecutor.isMainExecutor () && isExecutingOnMainThread ()) {
400
+ return true ;
401
+ }
400
402
}
401
403
404
+ // We cannot use 'complexEquality' as it requires two executor instances,
405
+ // and we do not have a 'current' executor here.
406
+
402
407
// Otherwise, as last resort, let the expected executor check using
403
408
// external means, as it may "know" this thread is managed by it etc.
404
- if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
405
- swift_task_checkIsolated (expectedExecutor);
409
+ if (isCurrentExecutorMode == Swift6_UseCheckIsolated_AllowCrash) {
410
+ swift_task_checkIsolated (expectedExecutor); // will crash if not same context
411
+
406
412
// checkIsolated did not crash, so we are on the right executor, after all!
407
413
return true ;
408
414
}
@@ -425,34 +431,31 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
425
431
return true ;
426
432
}
427
433
428
- // If the expected executor is "default" then we should have matched
429
- // by pointer equality already with the current executor.
430
- if (expectedExecutor.isDefaultActor ()) {
431
- // If the expected executor is a default actor, it makes no sense to try
432
- // the 'checkIsolated' call, it must be equal to the other actor, or it is
433
- // not the same isolation domain.
434
- if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
435
- swift_Concurrency_fatalError (0 , " Incorrect actor executor assumption" );
436
- }
437
- return false ;
438
- }
439
-
440
- if (expectedExecutor.isMainExecutor () && !currentExecutor.isMainExecutor ()) {
441
- if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
442
- // TODO: Invoke checkIsolated() on "main" SerialQueue once it implements `checkIsolated`, for potentially better/more consistent error messages
443
- swift_Concurrency_fatalError (0 , " Incorrect actor executor assumption; Expected MainActor executor" );
444
- }
445
- return false ;
446
- } else if (!expectedExecutor.isMainExecutor () && currentExecutor.isMainExecutor ()) {
447
- if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
448
- // TODO: Invoke checkIsolated() on "main" SerialQueue once it implements `checkIsolated`, for potentially better/more consistent error messages
449
- swift_Concurrency_fatalError (0 , " Incorrect actor executor assumption; Expected not-MainActor executor" );
434
+ // Only in legacy mode:
435
+ // We check if the current xor expected executor are the main executor.
436
+ // If so only one of them is, we know that WITHOUT 'checkIsolated' or invoking
437
+ // 'dispatch_assert_queue' we cannot be truly sure the expected/current truly
438
+ // are "on the same queue". There exists no non-crashing API to check this,
439
+ // so we PESSIMISTICALLY return false here.
440
+ //
441
+ // In Swift6 mode:
442
+ // We don't do this naive check, because we'll fall back to
443
+ // `expected.checkIsolated()` which, if it is the main executor, will invoke
444
+ // the crashing 'dispatch_assert_queue(main queue)' which will either crash
445
+ // or confirm we actually are on the main queue; or the custom expected
446
+ // executor has a chance to implement a similar queue check.
447
+ if (isCurrentExecutorMode == Legacy_NoCheckIsolated_NonCrashing) {
448
+ if ((expectedExecutor.isMainExecutor () && !currentExecutor.isMainExecutor ()) ||
449
+ (!expectedExecutor.isMainExecutor () && currentExecutor.isMainExecutor ())) {
450
+ return false ;
450
451
}
451
- return false ;
452
452
}
453
453
454
+ // Complex equality means that if two executors of the same type have some
455
+ // special logic to check if they are "actually the same".
454
456
if (expectedExecutor.isComplexEquality ()) {
455
- if (currentExecutor.getIdentity () && expectedExecutor.getIdentity () &&
457
+ if (currentExecutor.getIdentity () &&
458
+ expectedExecutor.getIdentity () &&
456
459
swift_compareWitnessTables (
457
460
reinterpret_cast <const WitnessTable *>(
458
461
currentExecutor.getSerialExecutorWitnessTable ()),
@@ -470,7 +473,7 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
470
473
// chance to check.
471
474
if (isSameExclusiveExecutionContextResult) {
472
475
return true ;
473
- }
476
+ } // else, we must give 'checkIsolated' a last chance to verify isolation
474
477
}
475
478
}
476
479
@@ -494,20 +497,22 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
494
497
// Note that this only works because the closure in assumeIsolated is
495
498
// synchronous, and will not cause suspensions, as that would require the
496
499
// presence of a Task.
497
- // compat_invoke_swift_task_checkIsolated(expectedExecutor);
498
- if (isCurrentExecutorMode == Default_UseCheckIsolated_AllowCrash) {
499
- swift_task_checkIsolated (expectedExecutor);
500
+ if (isCurrentExecutorMode == Swift6_UseCheckIsolated_AllowCrash) {
501
+ swift_task_checkIsolated (expectedExecutor); // will crash if not same context
502
+
500
503
// The checkIsolated call did not crash, so we are on the right executor.
501
504
return true ;
502
505
}
503
506
507
+ // In the end, since 'checkIsolated' could not be used, so we must assume
508
+ // that the executors are not the same context.
504
509
assert (isCurrentExecutorMode == Legacy_NoCheckIsolated_NonCrashing);
505
510
return false ;
506
511
}
507
512
508
513
// / Logging level for unexpected executors:
509
- // / 0 - no logging
510
- // / 1 - warn on each instance
514
+ // / 0 - no logging -- will be IGNORED when Swift6 mode of isCurrentExecutor is used
515
+ // / 1 - warn on each instance -- will be IGNORED when Swift6 mode of isCurrentExecutor is used
511
516
// / 2 - fatal error
512
517
// /
513
518
// / NOTE: The default behavior on Apple platforms depends on the SDK version
@@ -524,9 +529,28 @@ static void checkUnexpectedExecutorLogLevel(void *context) {
524
529
if (!levelStr)
525
530
return ;
526
531
532
+ auto isCurrentExecutorLegacyMode =
533
+ swift_bincompat_useLegacyNonCrashingExecutorChecks ();
534
+
527
535
long level = strtol (levelStr, nullptr , 0 );
528
- if (level >= 0 && level < 3 )
529
- unexpectedExecutorLogLevel = level;
536
+ if (level >= 0 && level < 3 ) {
537
+ if (isCurrentExecutorLegacyMode) {
538
+ // legacy mode permits doing nothing or just logging, since the method
539
+ // used to perform the check itself is not going to crash:
540
+ unexpectedExecutorLogLevel = level;
541
+ } else {
542
+ // We are in swift6/crash mode of isCurrentExecutor which means that
543
+ // rather than returning false, that method will always CRASH when an
544
+ // executor mismatch is discovered.
545
+ //
546
+ // Thus, for clarity, we set this mode also to crashing, as runtime should
547
+ // not expect to be able to get any logging or ignoring done. In practice,
548
+ // the crash would happen before logging or "ignoring", but this should
549
+ // help avoid confusing situations like "I thought it should log" when
550
+ // debugging the runtime.
551
+ unexpectedExecutorLogLevel = 2 ;
552
+ }
553
+ }
530
554
#endif // SWIFT_STDLIB_HAS_ENVIRON
531
555
}
532
556
0 commit comments