Skip to content

Add stackSize parameter to WebWorkerTaskExecutor and WebWorkerDedicatedExecutor #345

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ SWIFT_SDK_ID ?= wasm32-unknown-wasi
.PHONY: bootstrap
bootstrap:
npm ci
npx playwright install
npx playwright install chromium-headless-shell

.PHONY: unittest
unittest:
Expand Down
9 changes: 8 additions & 1 deletion Sources/JavaScriptEventLoop/WebWorkerDedicatedExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,19 @@ public final class WebWorkerDedicatedExecutor: SerialExecutor {
private let underlying: WebWorkerTaskExecutor

/// - Parameters:
/// - stackSize: The stack size for each worker thread. Default is `nil` (use the platform default stack size).
/// - timeout: The maximum time to wait for all worker threads to be started. Default is 3 seconds.
/// - checkInterval: The interval to check if all worker threads are started. Default is 5 microseconds.
/// - Throws: An error if any worker thread fails to initialize within the timeout period.
public init(timeout: Duration = .seconds(3), checkInterval: Duration = .microseconds(5)) async throws {
/// - Note: The default stack size of wasi-libc is typically 128KB.
public init(
stackSize: Int? = nil,
timeout: Duration = .seconds(3),
checkInterval: Duration = .microseconds(5)
) async throws {
let underlying = try await WebWorkerTaskExecutor(
numberOfThreads: 1,
stackSize: stackSize,
timeout: timeout,
checkInterval: checkInterval
)
Expand Down
22 changes: 19 additions & 3 deletions Sources/JavaScriptEventLoop/WebWorkerTaskExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
self.workers = workers
}

func start(timeout: Duration, checkInterval: Duration) async throws {
func start(stackSize: Int?, timeout: Duration, checkInterval: Duration) async throws {
#if canImport(wasi_pthread) && compiler(>=6.1) && _runtime(_multithreaded)
class Context: @unchecked Sendable {
let executor: WebWorkerTaskExecutor.Executor
Expand Down Expand Up @@ -375,9 +375,21 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
let unmanagedContext = Unmanaged.passRetained(context)
contexts.append(unmanagedContext)
let ptr = unmanagedContext.toOpaque()
var attr = pthread_attr_t()
pthread_attr_init(&attr)
// Set the stack size if specified.
if let stackSize {
let ret = pthread_attr_setstacksize(&attr, stackSize)
guard ret == 0 else {
let strerror = String(cString: strerror(ret))
throw SpawnError(
reason: "Failed to set stack size (\(stackSize)) for thread (\(ret): \(strerror))"
)
}
}
let ret = pthread_create(
nil,
nil,
&attr,
{ ptr in
// Cast to a optional pointer to absorb nullability variations between platforms.
let ptr: UnsafeMutableRawPointer? = ptr
Expand All @@ -390,6 +402,7 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
},
ptr
)
pthread_attr_destroy(&attr)
guard ret == 0 else {
let strerror = String(cString: strerror(ret))
throw SpawnError(reason: "Failed to create a thread (\(ret): \(strerror))")
Expand Down Expand Up @@ -467,16 +480,19 @@ public final class WebWorkerTaskExecutor: TaskExecutor {
///
/// - Parameters:
/// - numberOfThreads: The number of Web Worker threads to spawn.
/// - stackSize: The stack size for each worker thread. Default is `nil` (use the platform default stack size).
/// - timeout: The maximum time to wait for all worker threads to be started. Default is 3 seconds.
/// - checkInterval: The interval to check if all worker threads are started. Default is 5 microseconds.
/// - Throws: An error if any worker thread fails to initialize within the timeout period.
/// - Note: The default stack size of wasi-libc is typically 128KB.
public init(
numberOfThreads: Int,
stackSize: Int? = nil,
timeout: Duration = .seconds(3),
checkInterval: Duration = .microseconds(5)
) async throws {
self.executor = Executor(numberOfThreads: numberOfThreads)
try await self.executor.start(timeout: timeout, checkInterval: checkInterval)
try await self.executor.start(stackSize: stackSize, timeout: timeout, checkInterval: checkInterval)
}

/// Terminates all worker threads managed by this executor.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
executor.terminate()
}

func testThreadStackSize() async throws {
// Sanity check for stackSize parameter
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 3, stackSize: 512 * 1024)
executor.terminate()
}

func testTaskGroupRunOnDifferentThreads() async throws {
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 2)

Expand Down
Loading