Skip to content

Commit 6b16657

Browse files
committed
Explode on uncaught error thrown in main
This patch has two desirable effects for the price of one. 1. An uncaught error thrown from main will now explode 2. Move us off of using runAsyncAndBlock The issue with runAsyncAndBlock is that it blocks the main thread outright. UI and the main actor need to run on the main thread or bad things happen, so blocking the main thread results in a bad day for them. Instead, we're using CFRunLoopRun to run the core-foundation run loop on the main thread, or, dispatch_main if CFRunLoopRun isn't available. The issue with just using dispatch_main is that it doesn't actually guarantee that it will run the tasks on the main thread either, just that it clears the main queue. We don't want to require everything that uses concurrency to have to include CoreFoundation either, but dispatch is already required, which supplies dispatch_main, which just empties out the main queue.
1 parent c01b9f0 commit 6b16657

File tree

6 files changed

+38
-4
lines changed

6 files changed

+38
-4
lines changed

include/swift/Runtime/Concurrency.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@ void swift_continuation_throwingResumeWithError(/* +1 */ SwiftError *error,
349349
extern "C" SWIFT_CC(swift)
350350
void swift_continuation_logFailedCheck(const char *message);
351351

352+
/// Drain the queue
353+
/// If the binary links CoreFoundation, uses CFRunLoopRun
354+
/// Otherwise it uses dispatchMain.
355+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
356+
void swift_task_asyncMainDrainQueue();
352357
}
353358

354359
#endif

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1936,7 +1936,7 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) {
19361936
Expr *returnedExpr;
19371937

19381938
if (mainFunction->hasAsync()) {
1939-
// Pass main into _runAsyncMain(_ asyncFunc: () async -> ())
1939+
// Pass main into _runAsyncMain(_ asyncFunc: () async throws -> ())
19401940
// Resulting $main looks like:
19411941
// $main() { _runAsyncMain(main) }
19421942
auto *concurrencyModule = context.getLoadedModule(context.Id_Concurrency);

stdlib/public/Concurrency/Task.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
#include "TaskPrivate.h"
2323
#include "AsyncCall.h"
2424

25+
#include <dispatch/dispatch.h>
26+
27+
#include <dlfcn.h>
28+
2529
using namespace swift;
2630
using FutureFragment = AsyncTask::FutureFragment;
2731
using GroupFragment = AsyncTask::GroupFragment;
@@ -528,3 +532,12 @@ SWIFT_CC(swift)
528532
void swift::swift_continuation_logFailedCheck(const char *message) {
529533
swift_reportError(0, message);
530534
}
535+
536+
void swift::swift_task_asyncMainDrainQueue() {
537+
auto runLoop =
538+
reinterpret_cast<void (*)(void)>(dlsym(RTLD_SELF, "CFRunLoopRun"));
539+
if (runLoop)
540+
runLoop();
541+
else
542+
dispatch_main();
543+
}

stdlib/public/Concurrency/Task.swift

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,20 @@ func isTaskCancelled(_ task: Builtin.NativeObject) -> Bool
323323
@_silgen_name("swift_task_runAndBlockThread")
324324
public func runAsyncAndBlock(_ asyncFun: @escaping () async -> ())
325325

326-
@_silgen_name("swift_task_runAndBlockThread")
327-
public func _runAsyncMain(_ asyncFun: () async throws -> ())
326+
@_silgen_name("swift_task_asyncMainDrainQueue")
327+
public func _asyncMainDrainQueue() -> Never
328+
329+
public func _runAsyncMain(_ asyncFun: @escaping () async throws -> ()) {
330+
let _ = Task.runDetached {
331+
do {
332+
await try asyncFun()
333+
exit(EXIT_SUCCESS)
334+
} catch {
335+
_errorInMain(error)
336+
}
337+
}
338+
_asyncMainDrainQueue()
339+
}
328340

329341
@_silgen_name("swift_task_future_wait")
330342
func _taskFutureWait(

stdlib/public/SwiftShims/_SwiftConcurrency.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ typedef struct _SwiftContext {
2525
struct _SwiftContext *parentContext;
2626
} _SwiftContext;
2727

28+
void exit(int);
29+
30+
#define EXIT_SUCCESS 0
31+
2832
#ifdef __cplusplus
2933
} // extern "C"
3034
} // namespace swift

test/Concurrency/async_main.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func asyncFunc() async { }
2121
// CHECK-AST-NEXT: (return_stmt implicit
2222
// CHECK-AST-NEXT: (call_expr implicit type='()'
2323
// CHECK-AST-NEXT: (declref_expr implicit
24-
// CHECK-AST-SAME: type='(() async throws -> ()) -> ()'
24+
// CHECK-AST-SAME: type='(@escaping () async throws -> ()) -> ()'
2525
// CHECK-AST-SAME: decl=_Concurrency.(file)._runAsyncMain
2626
// CHECK-AST-SAME: function_ref=single
2727
// CHECK-AST-NEXT: (paren_expr implicit type='(() async throws -> ())'

0 commit comments

Comments
 (0)