Skip to content

Commit 3a99951

Browse files
Stop blocking the main thread when starting Web Worker threads
Seems like blocking the main thread also blocks the Web Worker threads from starting on some browsers.
1 parent 9161102 commit 3a99951

File tree

2 files changed

+25
-16
lines changed

2 files changed

+25
-16
lines changed

Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift

+13-4
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
263263
self.workers = workers
264264
}
265265

266-
func start() {
266+
func start(timeout: Duration, checkInterval: Duration) async throws {
267267
class Context: @unchecked Sendable {
268268
let executor: WebWorkerTaskExecutor.Executor
269269
let worker: Worker
@@ -293,10 +293,16 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
293293
}
294294
// Wait until all worker threads are started and wire up messaging channels
295295
// between the main thread and workers to notify job enqueuing events each other.
296+
let clock = ContinuousClock()
297+
let workerInitStarted = clock.now
296298
for worker in workers {
297299
var tid: pid_t
298300
repeat {
301+
if workerInitStarted.duration(to: .now) > timeout {
302+
fatalError("Worker thread initialization timeout exceeded (\(timeout))")
303+
}
299304
tid = worker.tid.load(ordering: .sequentiallyConsistent)
305+
try await clock.sleep(for: checkInterval)
300306
} while tid == 0
301307
swjs_listen_main_job_from_worker_thread(tid)
302308
}
@@ -330,10 +336,13 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
330336

331337
/// Create a new Web Worker task executor.
332338
///
333-
/// - Parameter numberOfThreads: The number of Web Worker threads to spawn.
334-
public init(numberOfThreads: Int) {
339+
/// - Parameters:
340+
/// - numberOfThreads: The number of Web Worker threads to spawn.
341+
/// - timeout: The timeout to wait for all worker threads to be started.
342+
/// - checkInterval: The interval to check if all worker threads are started.
343+
public init(numberOfThreads: Int, timeout: Duration = .seconds(3), checkInterval: Duration = .microseconds(5)) async throws {
335344
self.executor = Executor(numberOfThreads: numberOfThreads)
336-
self.executor.start()
345+
try await self.executor.start(timeout: timeout, checkInterval: checkInterval)
337346
}
338347

339348
/// Terminate child Web Worker threads.

Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift

+12-12
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
1212
WebWorkerTaskExecutor.installGlobalExecutor()
1313
}
1414

15-
func testTaskRunOnMainThread() async {
16-
let executor = WebWorkerTaskExecutor(numberOfThreads: 1)
15+
func testTaskRunOnMainThread() async throws {
16+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
1717

1818
XCTAssertTrue(isMainThread())
1919

@@ -29,15 +29,15 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
2929
executor.terminate()
3030
}
3131

32-
func testWithPreferenceBlock() async {
33-
let executor = WebWorkerTaskExecutor(numberOfThreads: 1)
32+
func testWithPreferenceBlock() async throws {
33+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
3434
await withTaskExecutorPreference(executor) {
3535
XCTAssertFalse(isMainThread())
3636
}
3737
}
3838

3939
func testAwaitInsideTask() async throws {
40-
let executor = WebWorkerTaskExecutor(numberOfThreads: 1)
40+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
4141

4242
let task = Task(executorPreference: executor) {
4343
await Task.yield()
@@ -51,7 +51,7 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
5151
}
5252

5353
func testSleepInsideTask() async throws {
54-
let executor = WebWorkerTaskExecutor(numberOfThreads: 1)
54+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
5555

5656
let task = Task(executorPreference: executor) {
5757
XCTAssertFalse(isMainThread())
@@ -69,8 +69,8 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
6969
executor.terminate()
7070
}
7171

72-
func testMainActorRun() async {
73-
let executor = WebWorkerTaskExecutor(numberOfThreads: 1)
72+
func testMainActorRun() async throws {
73+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
7474

7575
let task = Task(executorPreference: executor) {
7676
await MainActor.run {
@@ -87,8 +87,8 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
8787
executor.terminate()
8888
}
8989

90-
func testTaskGroupRunOnSameThread() async {
91-
let executor = WebWorkerTaskExecutor(numberOfThreads: 3)
90+
func testTaskGroupRunOnSameThread() async throws {
91+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 3)
9292

9393
let mainTid = swjs_get_worker_thread_id()
9494
await withTaskExecutorPreference(executor) {
@@ -112,8 +112,8 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
112112
executor.terminate()
113113
}
114114

115-
func testTaskGroupRunOnDifferentThreads() async {
116-
let executor = WebWorkerTaskExecutor(numberOfThreads: 2)
115+
func testTaskGroupRunOnDifferentThreads() async throws {
116+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 2)
117117

118118
struct Item: Hashable {
119119
let type: String

0 commit comments

Comments
 (0)