Skip to content

Commit 288adb0

Browse files
Test: Cover JSArray.count on worker thread
1 parent a7a57d0 commit 288adb0

File tree

2 files changed

+51
-15
lines changed

2 files changed

+51
-15
lines changed

Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift

+11-2
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
200200
parentTaskExecutor = executor
201201
// Store the thread ID to the worker. This notifies the main thread that the worker is started.
202202
self.tid.store(tid, ordering: .sequentiallyConsistent)
203+
trace("Worker.start tid=\(tid)")
203204
}
204205

205206
/// Process jobs in the queue.
@@ -212,7 +213,14 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
212213
guard let executor = parentTaskExecutor else {
213214
preconditionFailure("The worker must be started with a parent executor.")
214215
}
215-
assert(state.load(ordering: .sequentiallyConsistent) == .running, "Invalid state: not running")
216+
do {
217+
// Assert the state at the beginning of the run.
218+
let state = state.load(ordering: .sequentiallyConsistent)
219+
assert(
220+
state == .running || state == .terminated,
221+
"Invalid state: not running (tid=\(self.tid.load(ordering: .sequentiallyConsistent)), \(state))"
222+
)
223+
}
216224
while true {
217225
// Pop a job from the queue.
218226
let job = jobQueue.withLock { queue -> UnownedJob? in
@@ -247,7 +255,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
247255

248256
/// Terminate the worker.
249257
func terminate() {
250-
trace("Worker.terminate")
258+
trace("Worker.terminate tid=\(tid.load(ordering: .sequentiallyConsistent))")
251259
state.store(.terminated, ordering: .sequentiallyConsistent)
252260
let tid = self.tid.load(ordering: .sequentiallyConsistent)
253261
guard tid != 0 else {
@@ -283,6 +291,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
283291
self.worker = worker
284292
}
285293
}
294+
trace("Executor.start")
286295
// Start worker threads via pthread_create.
287296
for worker in workers {
288297
// NOTE: The context must be allocated on the heap because

Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift

+40-13
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
3838

3939
func testAwaitInsideTask() async throws {
4040
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
41+
defer { executor.terminate() }
4142

4243
let task = Task(executorPreference: executor) {
4344
await Task.yield()
@@ -46,8 +47,6 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
4647
}
4748
let taskRunOnMainThread = try await task.value
4849
XCTAssertFalse(taskRunOnMainThread)
49-
50-
executor.terminate()
5150
}
5251

5352
func testSleepInsideTask() async throws {
@@ -170,6 +169,7 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
170169
let result = await task.value
171170
XCTAssertEqual(result, 100)
172171
XCTAssertEqual(Check.value, 42)
172+
executor.terminate()
173173
}
174174

175175
func testLazyThreadLocalPerThreadInitialization() async throws {
@@ -198,6 +198,7 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
198198
let result = await task.value
199199
XCTAssertEqual(result, 100)
200200
XCTAssertEqual(Check.countOfInitialization, 2)
201+
executor.terminate()
201202
}
202203

203204
func testJSValueDecoderOnWorker() async throws {
@@ -211,10 +212,10 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
211212
let prop_3: Bool
212213
let prop_7: Float
213214
let prop_8: String
215+
let prop_9: [String]
214216
}
215217

216-
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
217-
let task = Task(executorPreference: executor) {
218+
func decodeJob() throws {
218219
let json = """
219220
{
220221
"prop_1": {
@@ -223,20 +224,46 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
223224
"prop_2": 100,
224225
"prop_3": true,
225226
"prop_7": 3.14,
226-
"prop_8": "Hello, World!"
227+
"prop_8": "Hello, World!",
228+
"prop_9": ["a", "b", "c"]
227229
}
228230
"""
229231
let object = JSObject.global.JSON.parse(json)
230232
let decoder = JSValueDecoder()
231-
let decoded = try decoder.decode(DecodeMe.self, from: object)
232-
return decoded
233+
let result = try decoder.decode(DecodeMe.self, from: object)
234+
XCTAssertEqual(result.prop_1.nested_prop, 42)
235+
XCTAssertEqual(result.prop_2, 100)
236+
XCTAssertEqual(result.prop_3, true)
237+
XCTAssertEqual(result.prop_7, 3.14)
238+
XCTAssertEqual(result.prop_8, "Hello, World!")
239+
XCTAssertEqual(result.prop_9, ["a", "b", "c"])
240+
}
241+
// Run the job on the main thread first to initialize the object cache
242+
try decodeJob()
243+
244+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
245+
defer { executor.terminate() }
246+
let task = Task(executorPreference: executor) {
247+
// Run the job on the worker thread to test the object cache
248+
// is not shared with the main thread
249+
try decodeJob()
250+
}
251+
try await task.value
252+
}
253+
254+
func testJSArrayCountOnWorker() async throws {
255+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
256+
func check() {
257+
let object = JSObject.global.Array.function!.new(1, 2, 3, 4, 5)
258+
let array = JSArray(object)!
259+
XCTAssertEqual(array.count, 5)
233260
}
234-
let result = try await task.value
235-
XCTAssertEqual(result.prop_1.nested_prop, 42)
236-
XCTAssertEqual(result.prop_2, 100)
237-
XCTAssertEqual(result.prop_3, true)
238-
XCTAssertEqual(result.prop_7, 3.14)
239-
XCTAssertEqual(result.prop_8, "Hello, World!")
261+
check()
262+
let task = Task(executorPreference: executor) {
263+
check()
264+
}
265+
await task.value
266+
executor.terminate()
240267
}
241268

242269
/*

0 commit comments

Comments
 (0)