Skip to content

Commit a4daecd

Browse files
Generalize the thread channel functions not limited to wake-up events
1 parent 358633c commit a4daecd

File tree

7 files changed

+181
-148
lines changed

7 files changed

+181
-148
lines changed

IntegrationTests/lib.js

+6-6
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ export async function startWasiChildThread(event) {
7979
const swift = new SwiftRuntime({
8080
sharedMemory: true,
8181
threadChannel: {
82-
wakeUpMainThread: parentPort.postMessage.bind(parentPort),
83-
listenWakeEventFromMainThread: (listener) => {
82+
postMessageToMainThread: parentPort.postMessage.bind(parentPort),
83+
listenMessageFromMainThread: (listener) => {
8484
parentPort.on("message", listener)
8585
}
8686
}
@@ -138,9 +138,9 @@ class ThreadRegistry {
138138
return this.workers.get(tid);
139139
}
140140

141-
wakeUpWorkerThread(tid) {
141+
wakeUpWorkerThread(tid, message) {
142142
const worker = this.workers.get(tid);
143-
worker.postMessage(null);
143+
worker.postMessage(message);
144144
}
145145
}
146146

@@ -159,8 +159,8 @@ export const startWasiTask = async (wasmPath, wasiConstructorKey = selectWASIBac
159159
const swift = new SwiftRuntime({
160160
sharedMemory,
161161
threadChannel: {
162-
wakeUpWorkerThread: threadRegistry.wakeUpWorkerThread.bind(threadRegistry),
163-
listenMainJobFromWorkerThread: (tid, listener) => {
162+
postMessageToWorkerThread: threadRegistry.wakeUpWorkerThread.bind(threadRegistry),
163+
listenMessageFromWorkerThread: (tid, listener) => {
164164
const worker = threadRegistry.worker(tid);
165165
worker.on("message", listener);
166166
}

Runtime/src/index.ts

+84-67
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,27 @@ import {
1010
import * as JSValue from "./js-value.js";
1111
import { Memory } from "./memory.js";
1212

13+
type MainToWorkerMessage = {
14+
type: "wake";
15+
};
16+
17+
type WorkerToMainMessage = {
18+
type: "job";
19+
data: number;
20+
};
21+
1322
/**
1423
* A thread channel is a set of functions that are used to communicate between
1524
* the main thread and the worker thread. The main thread and the worker thread
16-
* can send jobs to each other using these functions.
25+
* can send messages to each other using these functions.
1726
*
1827
* @example
1928
* ```javascript
2029
* // worker.js
2130
* const runtime = new SwiftRuntime({
2231
* threadChannel: {
23-
* wakeUpMainThread: (unownedJob) => {
24-
* // Send the job to the main thread
25-
* postMessage(unownedJob);
26-
* },
27-
* listenWakeEventFromMainThread: (listener) => {
32+
* postMessageToMainThread: postMessage,
33+
* listenMessageFromMainThread: (listener) => {
2834
* self.onmessage = (event) => {
2935
* listener(event.data);
3036
* };
@@ -36,10 +42,10 @@ import { Memory } from "./memory.js";
3642
* const worker = new Worker("worker.js");
3743
* const runtime = new SwiftRuntime({
3844
* threadChannel: {
39-
* wakeUpWorkerThread: (tid, data) => {
45+
* postMessageToWorkerThread: (tid, data) => {
4046
* worker.postMessage(data);
4147
* },
42-
* listenMainJobFromWorkerThread: (tid, listener) => {
48+
* listenMessageFromWorkerThread: (tid, listener) => {
4349
* worker.onmessage = (event) => {
4450
listener(event.data);
4551
* };
@@ -50,40 +56,42 @@ import { Memory } from "./memory.js";
5056
*/
5157
export type SwiftRuntimeThreadChannel =
5258
| {
53-
/**
54-
* This function is called when the Web Worker thread sends a job to the main thread.
55-
* The unownedJob is the pointer to the unowned job object in the Web Worker thread.
56-
* The job submitted by this function expected to be listened by `listenMainJobFromWorkerThread`.
57-
*/
58-
wakeUpMainThread: (unownedJob: number) => void;
59+
/**
60+
* This function is used to send messages from the worker thread to the main thread.
61+
* The message submitted by this function is expected to be listened by `listenMessageFromWorkerThread`.
62+
* @param message The message to be sent to the main thread.
63+
*/
64+
postMessageToMainThread: (message: WorkerToMainMessage) => void;
5965
/**
6066
* This function is expected to be set in the worker thread and should listen
61-
* to the wake event from the main thread sent by `wakeUpWorkerThread`.
62-
* The passed listener function awakes the Web Worker thread.
67+
* to messages from the main thread sent by `postMessageToWorkerThread`.
68+
* @param listener The listener function to be called when a message is received from the main thread.
6369
*/
64-
listenWakeEventFromMainThread: (listener: (data: unknown) => void) => void;
70+
listenMessageFromMainThread: (listener: (message: MainToWorkerMessage) => void) => void;
6571
}
6672
| {
6773
/**
68-
* This function is expected to be set in the main thread and called
69-
* when the main thread sends a wake event to the Web Worker thread.
70-
* The `tid` is the thread ID of the worker thread to be woken up.
71-
* The `data` is the data to be sent to the worker thread.
72-
* The wake event is expected to be listened by `listenWakeEventFromMainThread`.
74+
* This function is expected to be set in the main thread.
75+
* The message submitted by this function is expected to be listened by `listenMessageFromMainThread`.
76+
* @param tid The thread ID of the worker thread.
77+
* @param message The message to be sent to the worker thread.
7378
*/
74-
wakeUpWorkerThread: (tid: number, data: unknown) => void;
79+
postMessageToWorkerThread: (tid: number, message: MainToWorkerMessage) => void;
7580
/**
7681
* This function is expected to be set in the main thread and shuold listen
77-
* to the main job sent by `wakeUpMainThread` from the worker thread.
82+
* to messsages sent by `postMessageToMainThread` from the worker thread.
83+
* @param tid The thread ID of the worker thread.
84+
* @param listener The listener function to be called when a message is received from the worker thread.
7885
*/
79-
listenMainJobFromWorkerThread: (
86+
listenMessageFromWorkerThread: (
8087
tid: number,
81-
listener: (unownedJob: number) => void
88+
listener: (message: WorkerToMainMessage) => void
8289
) => void;
8390

8491
/**
8592
* This function is expected to be set in the main thread and called
8693
* when the worker thread is terminated.
94+
* @param tid The thread ID of the worker thread.
8795
*/
8896
terminateWorkerThread?: (tid: number) => void;
8997
};
@@ -578,60 +586,49 @@ export class SwiftRuntime {
578586
swjs_unsafe_event_loop_yield: () => {
579587
throw new UnsafeEventLoopYield();
580588
},
581-
// This function is called by WebWorkerTaskExecutor on Web Worker thread.
582589
swjs_send_job_to_main_thread: (unowned_job) => {
583-
const threadChannel = this.options.threadChannel;
584-
if (threadChannel && "wakeUpMainThread" in threadChannel) {
585-
threadChannel.wakeUpMainThread(unowned_job);
586-
} else {
587-
throw new Error(
588-
"wakeUpMainThread is not set in options given to SwiftRuntime. Please set it to send jobs to the main thread."
589-
);
590-
}
590+
this.postMessageToMainThread({ type: "job", data: unowned_job });
591591
},
592-
swjs_listen_wake_event_from_main_thread: () => {
593-
// After the thread is started,
594-
const swjs_wake_worker_thread =
595-
this.exports.swjs_wake_worker_thread;
592+
swjs_listen_message_from_main_thread: () => {
596593
const threadChannel = this.options.threadChannel;
597-
if (
598-
threadChannel &&
599-
"listenWakeEventFromMainThread" in threadChannel
600-
) {
601-
threadChannel.listenWakeEventFromMainThread(() => {
602-
swjs_wake_worker_thread();
603-
});
604-
} else {
594+
if (!(threadChannel && "listenMessageFromMainThread" in threadChannel)) {
605595
throw new Error(
606-
"listenWakeEventFromMainThread is not set in options given to SwiftRuntime. Please set it to listen to wake events from the main thread."
596+
"listenMessageFromMainThread is not set in options given to SwiftRuntime. Please set it to listen to wake events from the main thread."
607597
);
608598
}
599+
threadChannel.listenMessageFromMainThread((message) => {
600+
switch (message.type) {
601+
case "wake":
602+
this.exports.swjs_wake_worker_thread();
603+
break;
604+
default:
605+
const unknownMessage: never = message.type;
606+
throw new Error(`Unknown message type: ${unknownMessage}`);
607+
}
608+
});
609609
},
610610
swjs_wake_up_worker_thread: (tid) => {
611-
const threadChannel = this.options.threadChannel;
612-
if (threadChannel && "wakeUpWorkerThread" in threadChannel) {
613-
// Currently, the data is not used, but it can be used in the future.
614-
threadChannel.wakeUpWorkerThread(tid, {});
615-
} else {
616-
throw new Error(
617-
"wakeUpWorkerThread is not set in options given to SwiftRuntime. Please set it to wake up worker threads."
618-
);
619-
}
611+
this.postMessageToWorkerThread(tid, { type: "wake" });
620612
},
621-
swjs_listen_main_job_from_worker_thread: (tid) => {
613+
swjs_listen_message_from_worker_thread: (tid) => {
622614
const threadChannel = this.options.threadChannel;
623-
if (
624-
threadChannel &&
625-
"listenMainJobFromWorkerThread" in threadChannel
626-
) {
627-
threadChannel.listenMainJobFromWorkerThread(
628-
tid, this.exports.swjs_enqueue_main_job_from_worker,
629-
);
630-
} else {
615+
if (!(threadChannel && "listenMessageFromWorkerThread" in threadChannel)) {
631616
throw new Error(
632-
"listenMainJobFromWorkerThread is not set in options given to SwiftRuntime. Please set it to listen to jobs from worker threads."
617+
"listenMessageFromWorkerThread is not set in options given to SwiftRuntime. Please set it to listen to jobs from worker threads."
633618
);
634619
}
620+
threadChannel.listenMessageFromWorkerThread(
621+
tid, (message) => {
622+
switch (message.type) {
623+
case "job":
624+
this.exports.swjs_enqueue_main_job_from_worker(message.data);
625+
break;
626+
default:
627+
const unknownMessage: never = message.type;
628+
throw new Error(`Unknown message type: ${unknownMessage}`);
629+
}
630+
},
631+
);
635632
},
636633
swjs_terminate_worker_thread: (tid) => {
637634
const threadChannel = this.options.threadChannel;
@@ -645,6 +642,26 @@ export class SwiftRuntime {
645642
},
646643
};
647644
}
645+
646+
private postMessageToMainThread(message: WorkerToMainMessage) {
647+
const threadChannel = this.options.threadChannel;
648+
if (!(threadChannel && "postMessageToMainThread" in threadChannel)) {
649+
throw new Error(
650+
"postMessageToMainThread is not set in options given to SwiftRuntime. Please set it to send messages to the main thread."
651+
);
652+
}
653+
threadChannel.postMessageToMainThread(message);
654+
}
655+
656+
private postMessageToWorkerThread(tid: number, message: MainToWorkerMessage) {
657+
const threadChannel = this.options.threadChannel;
658+
if (!(threadChannel && "postMessageToWorkerThread" in threadChannel)) {
659+
throw new Error(
660+
"postMessageToWorkerThread is not set in options given to SwiftRuntime. Please set it to send messages to worker threads."
661+
);
662+
}
663+
threadChannel.postMessageToWorkerThread(tid, message);
664+
}
648665
}
649666

650667
/// This error is thrown when yielding event loop control from `swift_task_asyncMainDrainQueue`

Runtime/src/types.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ export interface ImportedFunctions {
107107
swjs_i64_to_bigint_slow(lower: number, upper: number, signed: bool): ref;
108108
swjs_unsafe_event_loop_yield: () => void;
109109
swjs_send_job_to_main_thread: (unowned_job: number) => void;
110-
swjs_listen_wake_event_from_main_thread: () => void;
110+
swjs_listen_message_from_main_thread: () => void;
111111
swjs_wake_up_worker_thread: (tid: number) => void;
112-
swjs_listen_main_job_from_worker_thread: (tid: number) => void;
112+
swjs_listen_message_from_worker_thread: (tid: number) => void;
113113
swjs_terminate_worker_thread: (tid: number) => void;
114114
swjs_get_worker_thread_id: () => number;
115115
}

Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,10 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
188188
// `self` outlives the worker thread because `Executor` retains the worker.
189189
// Thus it's safe to store the reference without extra retain.
190190
swjs_thread_local_task_executor_worker = Unmanaged.passUnretained(self).toOpaque()
191-
// Start listening wake-up events from the main thread.
191+
// Start listening events from the main thread.
192192
// This must be called after setting the swjs_thread_local_task_executor_worker
193193
// because the event listener enqueues jobs to the TLS worker.
194-
swjs_listen_wake_event_from_main_thread()
194+
swjs_listen_message_from_main_thread()
195195
// Set the parent executor.
196196
parentTaskExecutor = executor
197197
// Store the thread ID to the worker. This notifies the main thread that the worker is started.
@@ -310,7 +310,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
310310
tid = worker.tid.load(ordering: .sequentiallyConsistent)
311311
try await clock.sleep(for: checkInterval)
312312
} while tid == 0
313-
swjs_listen_main_job_from_worker_thread(tid)
313+
swjs_listen_message_from_worker_thread(tid)
314314
}
315315
}
316316

0 commit comments

Comments
 (0)