Skip to content

Commit 4328db1

Browse files
Add stackSize parameter to WebWorkerTaskExecutor and WebWorkerDedicatedExecutor
This is useful for extending the stack size of the worker threads as wasi-libc's default stack size is typically 128KB, which may not be sufficient for some workloads.
1 parent d3b26d3 commit 4328db1

File tree

3 files changed

+33
-4
lines changed

3 files changed

+33
-4
lines changed

Diff for: Sources/JavaScriptEventLoop/WebWorkerDedicatedExecutor.swift

+8-1
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,19 @@ public final class WebWorkerDedicatedExecutor: SerialExecutor {
3939
private let underlying: WebWorkerTaskExecutor
4040

4141
/// - Parameters:
42+
/// - stackSize: The stack size for each worker thread. Default is `nil` (use the platform default stack size).
4243
/// - timeout: The maximum time to wait for all worker threads to be started. Default is 3 seconds.
4344
/// - checkInterval: The interval to check if all worker threads are started. Default is 5 microseconds.
4445
/// - Throws: An error if any worker thread fails to initialize within the timeout period.
45-
public init(timeout: Duration = .seconds(3), checkInterval: Duration = .microseconds(5)) async throws {
46+
/// - Note: The default stack size of wasi-libc is typically 128KB.
47+
public init(
48+
stackSize: Int? = nil,
49+
timeout: Duration = .seconds(3),
50+
checkInterval: Duration = .microseconds(5)
51+
) async throws {
4652
let underlying = try await WebWorkerTaskExecutor(
4753
numberOfThreads: 1,
54+
stackSize: stackSize,
4855
timeout: timeout,
4956
checkInterval: checkInterval
5057
)

Diff for: Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift

+19-3
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
347347
self.workers = workers
348348
}
349349

350-
func start(timeout: Duration, checkInterval: Duration) async throws {
350+
func start(stackSize: Int?, timeout: Duration, checkInterval: Duration) async throws {
351351
#if canImport(wasi_pthread) && compiler(>=6.1) && _runtime(_multithreaded)
352352
class Context: @unchecked Sendable {
353353
let executor: WebWorkerTaskExecutor.Executor
@@ -375,9 +375,21 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
375375
let unmanagedContext = Unmanaged.passRetained(context)
376376
contexts.append(unmanagedContext)
377377
let ptr = unmanagedContext.toOpaque()
378+
var attr = pthread_attr_t()
379+
pthread_attr_init(&attr)
380+
// Set the stack size if specified.
381+
if let stackSize {
382+
let ret = pthread_attr_setstacksize(&attr, stackSize)
383+
guard ret == 0 else {
384+
let strerror = String(cString: strerror(ret))
385+
throw SpawnError(
386+
reason: "Failed to set stack size (\(stackSize)) for thread (\(ret): \(strerror))"
387+
)
388+
}
389+
}
378390
let ret = pthread_create(
379391
nil,
380-
nil,
392+
&attr,
381393
{ ptr in
382394
// Cast to a optional pointer to absorb nullability variations between platforms.
383395
let ptr: UnsafeMutableRawPointer? = ptr
@@ -390,6 +402,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
390402
},
391403
ptr
392404
)
405+
pthread_attr_destroy(&attr)
393406
guard ret == 0 else {
394407
let strerror = String(cString: strerror(ret))
395408
throw SpawnError(reason: "Failed to create a thread (\(ret): \(strerror))")
@@ -467,16 +480,19 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
467480
///
468481
/// - Parameters:
469482
/// - numberOfThreads: The number of Web Worker threads to spawn.
483+
/// - stackSize: The stack size for each worker thread. Default is `nil` (use the platform default stack size).
470484
/// - timeout: The maximum time to wait for all worker threads to be started. Default is 3 seconds.
471485
/// - checkInterval: The interval to check if all worker threads are started. Default is 5 microseconds.
472486
/// - Throws: An error if any worker thread fails to initialize within the timeout period.
487+
/// - Note: The default stack size of wasi-libc is typically 128KB.
473488
public init(
474489
numberOfThreads: Int,
490+
stackSize: Int? = nil,
475491
timeout: Duration = .seconds(3),
476492
checkInterval: Duration = .microseconds(5)
477493
) async throws {
478494
self.executor = Executor(numberOfThreads: numberOfThreads)
479-
try await self.executor.start(timeout: timeout, checkInterval: checkInterval)
495+
try await self.executor.start(stackSize: stackSize, timeout: timeout, checkInterval: checkInterval)
480496
}
481497

482498
/// Terminates all worker threads managed by this executor.

Diff for: Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift

+6
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
122122
executor.terminate()
123123
}
124124

125+
func testThreadStackSize() async throws {
126+
// Sanity check for stackSize parameter
127+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 3, stackSize: 512 * 1024)
128+
executor.terminate()
129+
}
130+
125131
func testTaskGroupRunOnDifferentThreads() async throws {
126132
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 2)
127133

0 commit comments

Comments
 (0)