Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ on:
jobs:
soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@0.0.2
with:
format_check_container_image: swift:6.1.0-noble
license_header_check_enabled: false
api_breakage_check_enabled: false

tests:
name: tests
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main
uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@0.0.2
with:
# Runners aren't set up for this currently in GitHub/PassiveLogic
enable_macos_checks: false
Expand Down
87 changes: 42 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,20 @@
# swift-dispatch-async

## ⚠️ WARNING - This is an 🧪experimental🧪 repository and should not be adopted at large.
DispatchAsync provides a pure-swift implementation of `Dispatch`, powered by Swift Concurrency. It is currently intended for use only
in WASM targets, but could be used in other scenarios should the need arise.

DispatchAsync is a temporary experimental repository aimed at implementing missing Dispatch support in the SwiftWasm toolchain.
Currently, [SwiftWasm doesn't include Dispatch](https://book.swiftwasm.org/getting-started/porting.html#swift-foundation-and-dispatch).
But, SwiftWasm does support Swift Concurrency. DispatchAsync implements a number of common Dispatch API's using Swift Concurrency
under the hood.
The Swift for WebAssembly SDK [doesn't currenty include Dispatch](https://book.swiftwasm.org/getting-started/porting.html#swift-foundation-and-dispatch).
But, it does support Swift Concurrency. DispatchAsync implements a number of common Dispatch API's using Swift Concurrency
under the hood. This allows `import DispatchAsync` to be used in wasm targets as a drop-in replacement anywhere `import Dispatch` is
currently in use.

Dispatch Async does not provide blocking API's such as `DispatchQueue.sync`, primarily due to the intentional lack of blocking
API's in Swift Concurrency.

# Toolchain Adoption Plans

DispatchAsync is not meant for consumption abroad directly as a new Swift Module. Rather, the intention is to provide eventual integration
as a drop-in replacement for Dispatch when compiling to Wasm.

There are a few paths to adoption into the Swift toolchain

- DispatchAsync can be emplaced inside the [libDispatch repository](https://github.com/swiftlang/swift-corelibs-libdispatch), and compiled
into the toolchain only for wasm targets.
- DispatchAsync can be consumed in place of libDispatch when building the Swift toolchain.

Ideally, with either approach, this repository would transfer ownership to the swiftlang organization.

In the interim, to move wasm support forward, portions of DispatchAsync may be inlined (copy-pasted)
into various libraries to enable wasm support. DispatchAsync is designed for this purpose, and has
special `#if` handling to ensure that existing temporary usages will be elided without breakage
the moment SwiftWasm adds support for `Dispatch` into the toolchain.

# DispatchSemaphore Limitations

The current implementation of `DispatchSemaphore` has some limitations. Blocking threads goes against the design goals of Swift Concurrency.
The `wait` function on `DispatchSemaphore` goes against this goal. Furthermore, most wasm targets run on a single thread from the web
browser, so any time the `wait` function ends up blocking the calling thread, it would almost certainly freeze the single-threaded wasm
executable.

To navigate these issues, there are some limitations:

- For wasm compilation targets, `DispatchSemaphore` assumes single-threaded execution, and lacks various safeguards that would otherwise
be needed for multi-threaded execution. This makes the implementation much easier.
- For wasm targets, calls to `signal` and `wait` must be balanced. An assertion triggers if `wait` is called more times than `signal`.
- DispatchSemaphore is deprecated for wasm targets, and AsyncSemaphore is encouraged as the replacement.
- For non-wasm targets, DispatchSemaphore is simply a typealias for `AsyncSemaphore`, and provides only a non-blocking async `wait`
function. This reduces potential issues that can arise from wait being a thread-blocking function.

# Usage

If you've scrolled this far, you probably saw the warning. But just to make sure…

> ⚠️ WARNING - This is an 🧪experimental🧪 repository and should not be adopted at large at the present time.

PassiveLogic is [actively working](https://github.com/PassiveLogic/swift-web-examples/issues/1) to mainstream this into the SwiftWasm
toolchain. But if you can't wait, here are some tips.
PassiveLogic plans to mainstream this into the SwiftWasm toolchain. If possible, it's better to wait until this is part of the
official Swift for WebAssembly SDK. But if you need this now, below are some usage tips.

## 1. Only use this for WASI platforms, and only if Dispatch cannot be imported.

Expand Down Expand Up @@ -133,6 +96,40 @@ DispatchQueue.main.async {
}
```

# Toolchain Adoption Plans

DispatchAsync is not meant for consumption abroad directly as a new Swift Module. Rather, the intention is to provide eventual integration
as a drop-in replacement for Dispatch when compiling to Wasm.

There are a few paths to adoption into the Swift toolchain

- DispatchAsync can be emplaced inside the [libDispatch repository](https://github.com/swiftlang/swift-corelibs-libdispatch), and compiled
into the toolchain only for wasm targets.
- DispatchAsync can be consumed in place of libDispatch when building the Swift toolchain.

Ideally, with either approach, this repository would transfer ownership to the swiftlang organization.

In the interim, to move wasm support forward, portions of DispatchAsync may be inlined (copy-pasted)
into various libraries to enable wasm support. DispatchAsync is designed for this purpose, and has
special `#if` handling to ensure that existing temporary usages will be elided without breakage
the moment SwiftWasm adds support for `Dispatch` into the toolchain.

# DispatchSemaphore Limitations

The current implementation of `DispatchSemaphore` has some limitations. Blocking threads goes against the design goals of Swift Concurrency.
The `wait` function on `DispatchSemaphore` goes against this goal. Furthermore, most wasm targets run on a single thread from the web
browser, so any time the `wait` function ends up blocking the calling thread, it would almost certainly freeze the single-threaded wasm
executable.

To navigate these issues, there are some limitations:

- For wasm compilation targets, `DispatchSemaphore` assumes single-threaded execution, and lacks various safeguards that would otherwise
be needed for multi-threaded execution. This makes the implementation much easier.
- For wasm targets, calls to `signal` and `wait` must be balanced. An assertion triggers if `wait` is called more times than `signal`.
- DispatchSemaphore is deprecated for wasm targets, and AsyncSemaphore is encouraged as the replacement.
- For non-wasm targets, DispatchSemaphore is simply a typealias for `AsyncSemaphore`, and provides only a non-blocking async `wait`
function. This reduces potential issues that can arise from wait being a thread-blocking function.

# LICENSE

This project is distributed by PassiveLogic under the Apache-2.0 license. See
Expand Down
2 changes: 0 additions & 2 deletions Sources/DispatchAsync/DispatchAsync.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
///
/// Platforms other than WASI shouldn't consume this library for now
/// except for testing and development purposes.
///
/// TODO: SM: Add github permalink to this, after it is merged.
#if !os(WASI)
@_spi(DispatchAsync)
#endif
Expand Down