Skip to content

Commit 4f708fa

Browse files
authored
Merge pull request swiftlang#35215 from etcwilde/ewilde/async-main
Adding async-main support Resolves: rdar://71828636
2 parents 2a888e5 + 13cc56b commit 4f708fa

File tree

7 files changed

+131
-1
lines changed

7 files changed

+131
-1
lines changed

include/swift/Runtime/Concurrency.h

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

360+
/// Drain the queue
361+
/// If the binary links CoreFoundation, uses CFRunLoopRun
362+
/// Otherwise it uses dispatchMain.
363+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
364+
void swift_task_asyncMainDrainQueue();
360365
}
361366

362367
#endif

lib/Sema/TypeCheckAttr.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1935,7 +1935,31 @@ synthesizeMainBody(AbstractFunctionDecl *fn, void *arg) {
19351935

19361936
Expr *returnedExpr;
19371937

1938-
if (mainFunction->hasThrows()) {
1938+
if (mainFunction->hasAsync()) {
1939+
// Pass main into _runAsyncMain(_ asyncFunc: () async throws -> ())
1940+
// Resulting $main looks like:
1941+
// $main() { _runAsyncMain(main) }
1942+
auto *concurrencyModule = context.getLoadedModule(context.Id_Concurrency);
1943+
assert(concurrencyModule != nullptr && "Failed to find Concurrency module");
1944+
1945+
SmallVector<ValueDecl *, 1> decls;
1946+
concurrencyModule->lookupQualified(
1947+
concurrencyModule,
1948+
DeclNameRef(context.getIdentifier("_runAsyncMain")),
1949+
NL_QualifiedDefault | NL_IncludeUsableFromInline,
1950+
decls);
1951+
assert(!decls.empty() && "Failed to find _runAsyncMain");
1952+
FuncDecl *runner = cast<FuncDecl>(decls[0]);
1953+
1954+
auto asyncRunnerDeclRef = ConcreteDeclRef(runner, substitutionMap);
1955+
1956+
DeclRefExpr *funcExpr = new (context) DeclRefExpr(asyncRunnerDeclRef,
1957+
DeclNameLoc(),
1958+
/*implicit=*/true);
1959+
funcExpr->setType(runner->getInterfaceType());
1960+
auto *callExpr = CallExpr::createImplicit(context, funcExpr, memberRefExpr, {});
1961+
returnedExpr = callExpr;
1962+
} else if (mainFunction->hasThrows()) {
19391963
auto *tryExpr = new (context) TryExpr(
19401964
callExpr->getLoc(), callExpr, context.TheEmptyTupleType, /*implicit=*/true);
19411965
returnedExpr = tryExpr;

stdlib/public/Concurrency/Task.cpp

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

25+
#include <dispatch/dispatch.h>
26+
27+
#if !defined(_WIN32)
28+
#include <dlfcn.h>
29+
#endif
30+
2531
using namespace swift;
2632
using FutureFragment = AsyncTask::FutureFragment;
2733
using GroupFragment = AsyncTask::GroupFragment;
@@ -528,3 +534,19 @@ SWIFT_CC(swift)
528534
void swift::swift_continuation_logFailedCheck(const char *message) {
529535
swift_reportError(0, message);
530536
}
537+
538+
void swift::swift_task_asyncMainDrainQueue() {
539+
#if !defined(_WIN32)
540+
auto runLoop =
541+
reinterpret_cast<void (*)(void)>(dlsym(RTLD_DEFAULT, "CFRunLoopRun"));
542+
if (runLoop)
543+
runLoop();
544+
else
545+
dispatch_main();
546+
#else
547+
// TODO: I don't have a windows box to get this working right now.
548+
// We need to either pull in the CFRunLoop if it's available, or do
549+
// something that will drain the main queue. Exploding for now.
550+
abort();
551+
#endif
552+
}

stdlib/public/Concurrency/Task.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,21 @@ 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_asyncMainDrainQueue")
327+
public func _asyncMainDrainQueue() -> Never
328+
329+
public func _runAsyncMain(_ asyncFun: @escaping () async throws -> ()) {
330+
let _ = Task.runDetached {
331+
do {
332+
try await asyncFun()
333+
exit(0)
334+
} catch {
335+
_errorInMain(error)
336+
}
337+
}
338+
_asyncMainDrainQueue()
339+
}
340+
326341
@_silgen_name("swift_task_future_wait")
327342
func _taskFutureWait(
328343
on task: Builtin.NativeObject

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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// RUN: %target-swift-frontend -dump-ast -enable-experimental-concurrency -parse-as-library %s | %FileCheck %s --check-prefix=CHECK-AST
2+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -Xfrontend -parse-as-library %s -o %t_binary
3+
// RUN: %target-run %t_binary | %FileCheck %s --check-prefix=CHECK-EXEC
4+
5+
// REQUIRES: concurrency
6+
// REQUIRES: executable_test
7+
// REQUIRES: OS=macosx || OS=ios
8+
9+
func asyncFunc() async {
10+
print("Hello World!")
11+
}
12+
13+
@main struct MyProgram {
14+
static func main() async {
15+
await asyncFunc()
16+
}
17+
}
18+
19+
// CHECK-EXEC: Hello World!
20+
21+
// CHECK-AST-LABEL: "main()" interface
22+
// CHECK-AST: (await_expr type='()'
23+
// CHECK-AST-NEXT: (call_expr type='()'
24+
// CHECK-AST-NEXT: (declref_expr type='() async -> ()'
25+
// CHECK-AST-SAME: decl=async_main.(file).asyncFunc()@
26+
27+
// CHECK-AST-LABEL: (func_decl implicit "$main()" interface
28+
// CHECK-AST: (brace_stmt
29+
// CHECK-AST-NEXT: (return_stmt implicit
30+
// CHECK-AST-NEXT: (call_expr implicit type='()'
31+
// CHECK-AST-NEXT: (declref_expr implicit
32+
// CHECK-AST-SAME: type='(@escaping () async throws -> ()) -> ()'
33+
// CHECK-AST-SAME: decl=_Concurrency.(file)._runAsyncMain
34+
// CHECK-AST-SAME: function_ref=single
35+
// CHECK-AST-NEXT: (paren_expr implicit type='(() async throws -> ())'
36+
// CHECK-AST-NEXT: (function_conversion_expr implicit type='() async throws -> ()'
37+
// CHECK-AST-NEXT: (dot_syntax_call_expr
38+
// CHECK-AST-NEXT: (autoclosure_expr implicit type='(MyProgram.Type) -> () async -> ()'
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency -Xfrontend -parse-as-library %s -o %t/main
3+
// RUN: %target-codesign %t/main
4+
// RUN: %target-run %t/main > %t/log 2>&1 || true
5+
// RUN: %FileCheck %s < %t/log
6+
7+
// REQUIRES: concurrency
8+
// REQUIRES: executable_test
9+
// REQUIRES: OS=macosx || OS=ios
10+
11+
enum Err : Error { case noGood }
12+
13+
func asyncFunc() async throws {
14+
throw Err.noGood
15+
}
16+
17+
// CHECK: Fatal error: Error raised at top level: main.Err.noGood
18+
@main struct MyProgram {
19+
static func main() async throws {
20+
try await asyncFunc()
21+
}
22+
}

0 commit comments

Comments
 (0)