Skip to content

Commit 0d39aa7

Browse files
Merge pull request #255 from swiftwasm/yt/tls-eventloop
Allocate JavaScriptEventLoop per thread in multi-threaded environment
2 parents 2bb0694 + eb47bcb commit 0d39aa7

File tree

5 files changed

+44
-8
lines changed

5 files changed

+44
-8
lines changed

.github/workflows/test.yml

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ jobs:
1717
- { os: ubuntu-22.04, toolchain: wasm-5.10.0-RELEASE, wasi-backend: Node }
1818

1919
# Ensure that test succeeds with all toolchains and wasi backend combinations
20-
- { os: ubuntu-20.04, toolchain: wasm-5.7.3-RELEASE, wasi-backend: Node }
2120
- { os: ubuntu-20.04, toolchain: wasm-5.8.0-RELEASE, wasi-backend: Node }
22-
- { os: ubuntu-20.04, toolchain: wasm-5.7.3-RELEASE, wasi-backend: MicroWASI }
21+
- { os: ubuntu-20.04, toolchain: wasm-5.10.0-RELEASE, wasi-backend: Node }
2322
- { os: ubuntu-20.04, toolchain: wasm-5.8.0-RELEASE, wasi-backend: MicroWASI }
2423
- { os: ubuntu-20.04, toolchain: wasm-5.9.1-RELEASE, wasi-backend: MicroWASI }
24+
- { os: ubuntu-20.04, toolchain: wasm-5.10.0-RELEASE, wasi-backend: MicroWASI }
2525
- os: ubuntu-22.04
2626
toolchain: DEVELOPMENT-SNAPSHOT-2024-05-01-a
2727
swift-sdk:
@@ -76,8 +76,6 @@ jobs:
7676
strategy:
7777
matrix:
7878
include:
79-
- os: macos-12
80-
xcode: Xcode_14.0
8179
- os: macos-13
8280
xcode: Xcode_14.3
8381
- os: macos-14

Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift

+24-3
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,28 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
5757
}
5858

5959
/// A singleton instance of the Executor
60-
public static let shared: JavaScriptEventLoop = {
60+
public static var shared: JavaScriptEventLoop {
61+
return _shared
62+
}
63+
64+
#if compiler(>=6.0) && _runtime(_multithreaded)
65+
// In multi-threaded environment, we have an event loop executor per
66+
// thread (per Web Worker). A job enqueued in one thread should be
67+
// executed in the same thread under this global executor.
68+
private static var _shared: JavaScriptEventLoop {
69+
if let tls = swjs_thread_local_event_loop {
70+
let eventLoop = Unmanaged<JavaScriptEventLoop>.fromOpaque(tls).takeUnretainedValue()
71+
return eventLoop
72+
}
73+
let eventLoop = create()
74+
swjs_thread_local_event_loop = Unmanaged.passRetained(eventLoop).toOpaque()
75+
return eventLoop
76+
}
77+
#else
78+
private static let _shared: JavaScriptEventLoop = create()
79+
#endif
80+
81+
private static func create() -> JavaScriptEventLoop {
6182
let promise = JSPromise(resolver: { resolver -> Void in
6283
resolver(.success(.undefined))
6384
})
@@ -79,7 +100,7 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
79100
}
80101
)
81102
return eventLoop
82-
}()
103+
}
83104

84105
private static var didInstallGlobalExecutor = false
85106

@@ -124,7 +145,7 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
124145
JavaScriptEventLoop.shared.unsafeEnqueue(job)
125146
}
126147
swift_task_enqueueMainExecutor_hook = unsafeBitCast(swift_task_enqueueMainExecutor_hook_impl, to: UnsafeMutableRawPointer?.self)
127-
148+
128149
didInstallGlobalExecutor = true
129150
}
130151

Sources/JavaScriptKit/FundamentalObjects/JSObject.swift

+10-1
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,16 @@ public class JSObject: Equatable {
137137

138138
/// A `JSObject` of the global scope object.
139139
/// This allows access to the global properties and global names by accessing the `JSObject` returned.
140-
public static let global = JSObject(id: _JS_Predef_Value_Global)
140+
public static var global: JSObject { return _global }
141+
142+
// `JSObject` storage itself is immutable, and use of `JSObject.global` from other
143+
// threads maintains the same semantics as `globalThis` in JavaScript.
144+
#if compiler(>=5.10)
145+
nonisolated(unsafe)
146+
static let _global = JSObject(id: _JS_Predef_Value_Global)
147+
#else
148+
static let _global = JSObject(id: _JS_Predef_Value_Global)
149+
#endif
141150

142151
deinit { swjs_release(id) }
143152

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "_CJavaScriptEventLoop.h"
2+
3+
_Thread_local void *swjs_thread_local_event_loop;

Sources/_CJavaScriptEventLoop/include/_CJavaScriptEventLoop.h

+5
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,9 @@ typedef SWIFT_CC(swift) void (*swift_task_asyncMainDrainQueue_override)(
6161
SWIFT_EXPORT_FROM(swift_Concurrency)
6262
extern void *_Nullable swift_task_asyncMainDrainQueue_hook;
6363

64+
65+
/// MARK: - thread local storage
66+
67+
extern _Thread_local void * _Nullable swjs_thread_local_event_loop;
68+
6469
#endif

0 commit comments

Comments
 (0)