Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allocate JavaScriptEventLoop per thread in multi-threaded environment #255

Merged
merged 6 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ jobs:
- { os: ubuntu-22.04, toolchain: wasm-5.10.0-RELEASE, wasi-backend: Node }

# Ensure that test succeeds with all toolchains and wasi backend combinations
- { os: ubuntu-20.04, toolchain: wasm-5.7.3-RELEASE, wasi-backend: Node }
- { os: ubuntu-20.04, toolchain: wasm-5.8.0-RELEASE, wasi-backend: Node }
- { os: ubuntu-20.04, toolchain: wasm-5.7.3-RELEASE, wasi-backend: MicroWASI }
- { os: ubuntu-20.04, toolchain: wasm-5.10.0-RELEASE, wasi-backend: Node }
- { os: ubuntu-20.04, toolchain: wasm-5.8.0-RELEASE, wasi-backend: MicroWASI }
- { os: ubuntu-20.04, toolchain: wasm-5.9.1-RELEASE, wasi-backend: MicroWASI }
- { os: ubuntu-20.04, toolchain: wasm-5.10.0-RELEASE, wasi-backend: MicroWASI }
- os: ubuntu-22.04
toolchain: DEVELOPMENT-SNAPSHOT-2024-05-01-a
swift-sdk:
Expand Down Expand Up @@ -76,8 +76,6 @@ jobs:
strategy:
matrix:
include:
- os: macos-12
xcode: Xcode_14.0
- os: macos-13
xcode: Xcode_14.3
- os: macos-14
Expand Down
27 changes: 24 additions & 3 deletions Sources/JavaScriptEventLoop/JavaScriptEventLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,28 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
}

/// A singleton instance of the Executor
public static let shared: JavaScriptEventLoop = {
public static var shared: JavaScriptEventLoop {
return _shared
}

#if compiler(>=6.0) && _runtime(_multithreaded)
// In multi-threaded environment, we have an event loop executor per
// thread (per Web Worker). A job enqueued in one thread should be
// executed in the same thread under this global executor.
private static var _shared: JavaScriptEventLoop {
if let tls = swjs_thread_local_event_loop {
let eventLoop = Unmanaged<JavaScriptEventLoop>.fromOpaque(tls).takeUnretainedValue()
return eventLoop
}
let eventLoop = create()
swjs_thread_local_event_loop = Unmanaged.passRetained(eventLoop).toOpaque()
return eventLoop
}
#else
private static let _shared: JavaScriptEventLoop = create()
#endif

private static func create() -> JavaScriptEventLoop {
let promise = JSPromise(resolver: { resolver -> Void in
resolver(.success(.undefined))
})
Expand All @@ -79,7 +100,7 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
}
)
return eventLoop
}()
}

private static var didInstallGlobalExecutor = false

Expand Down Expand Up @@ -124,7 +145,7 @@ public final class JavaScriptEventLoop: SerialExecutor, @unchecked Sendable {
JavaScriptEventLoop.shared.unsafeEnqueue(job)
}
swift_task_enqueueMainExecutor_hook = unsafeBitCast(swift_task_enqueueMainExecutor_hook_impl, to: UnsafeMutableRawPointer?.self)

didInstallGlobalExecutor = true
}

Expand Down
11 changes: 10 additions & 1 deletion Sources/JavaScriptKit/FundamentalObjects/JSObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,16 @@ public class JSObject: Equatable {

/// A `JSObject` of the global scope object.
/// This allows access to the global properties and global names by accessing the `JSObject` returned.
public static let global = JSObject(id: _JS_Predef_Value_Global)
public static var global: JSObject { return _global }

// `JSObject` storage itself is immutable, and use of `JSObject.global` from other
// threads maintains the same semantics as `globalThis` in JavaScript.
#if compiler(>=5.10)
nonisolated(unsafe)
static let _global = JSObject(id: _JS_Predef_Value_Global)
#else
static let _global = JSObject(id: _JS_Predef_Value_Global)
#endif

deinit { swjs_release(id) }

Expand Down
3 changes: 3 additions & 0 deletions Sources/_CJavaScriptEventLoop/_CJavaScriptEventLoop.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "_CJavaScriptEventLoop.h"

_Thread_local void *swjs_thread_local_event_loop;
5 changes: 5 additions & 0 deletions Sources/_CJavaScriptEventLoop/include/_CJavaScriptEventLoop.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@ typedef SWIFT_CC(swift) void (*swift_task_asyncMainDrainQueue_override)(
SWIFT_EXPORT_FROM(swift_Concurrency)
extern void *_Nullable swift_task_asyncMainDrainQueue_hook;


/// MARK: - thread local storage

extern _Thread_local void * _Nullable swjs_thread_local_event_loop;

#endif
Loading