Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c6f4f49

Browse files
committedApr 20, 2025·
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 c6f4f49

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed
 

‎Sources/JavaScriptEventLoop/WebWorkerDedicatedExecutor.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,15 @@ 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(stackSize: Int? = nil, timeout: Duration = .seconds(3), checkInterval: Duration = .microseconds(5)) async throws {
4648
let underlying = try await WebWorkerTaskExecutor(
4749
numberOfThreads: 1,
50+
stackSize: stackSize,
4851
timeout: timeout,
4952
checkInterval: checkInterval
5053
)

‎Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift

Lines changed: 17 additions & 3 deletions
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,19 @@ 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(reason: "Failed to set stack size (\(stackSize)) for thread (\(ret): \(strerror))")
386+
}
387+
}
378388
let ret = pthread_create(
379389
nil,
380-
nil,
390+
&attr,
381391
{ ptr in
382392
// Cast to a optional pointer to absorb nullability variations between platforms.
383393
let ptr: UnsafeMutableRawPointer? = ptr
@@ -390,6 +400,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
390400
},
391401
ptr
392402
)
403+
pthread_attr_destroy(&attr)
393404
guard ret == 0 else {
394405
let strerror = String(cString: strerror(ret))
395406
throw SpawnError(reason: "Failed to create a thread (\(ret): \(strerror))")
@@ -467,16 +478,19 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
467478
///
468479
/// - Parameters:
469480
/// - numberOfThreads: The number of Web Worker threads to spawn.
481+
/// - stackSize: The stack size for each worker thread. Default is `nil` (use the platform default stack size).
470482
/// - timeout: The maximum time to wait for all worker threads to be started. Default is 3 seconds.
471483
/// - checkInterval: The interval to check if all worker threads are started. Default is 5 microseconds.
472484
/// - Throws: An error if any worker thread fails to initialize within the timeout period.
485+
/// - Note: The default stack size of wasi-libc is typically 128KB.
473486
public init(
474487
numberOfThreads: Int,
488+
stackSize: Int? = nil,
475489
timeout: Duration = .seconds(3),
476490
checkInterval: Duration = .microseconds(5)
477491
) async throws {
478492
self.executor = Executor(numberOfThreads: numberOfThreads)
479-
try await self.executor.start(timeout: timeout, checkInterval: checkInterval)
493+
try await self.executor.start(stackSize: stackSize, timeout: timeout, checkInterval: checkInterval)
480494
}
481495

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

‎Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift

Lines changed: 6 additions & 0 deletions
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)
Please sign in to comment.