This PR fixes #1277. `SandboxService.waiters` had a consistency issue (not exactly race). `SandboxService.wait` XPC can be executed on arbitrary `id`, and it will hang forever if no other handler resumes it. Without knowing this internal, the high level entity can run into this issue, and deadlock. This PR simplifies the mental model: **`SandboxService.waiters[id]: ExitWaiter(continuations, exitCode)` can only be in three states: i) non-existing, ii) existing with nil `exitCode`, and iii) existing with concrete `exitCode`.** **If it is non-existing, no handler has been registered to resume it later. If existing with nil `exitCode`, It is guaranteed the registered `continuations` will be resumed later with a concrete `exitCode`. Finally, if already a concrete `exitCode`, a handler has been registered, and already resumed (with that `exitCode`).** Thus, `SandboxService.wait` should return immediately if `waiters[id]` is non-existing or existing with a concrete `exitCode` (as no handler will resume it later). It should only block when `waiters[id]` is existing with nil `exitCode` as it is guaranteed to be resumed later. By doing so, we can guarantee there is no deadlock at all. For that this PR does followings: 1. Introduce `ExitMonitor` class to updates `continuations` and `exitCode` all together atomically. Initially, `state` variable saved the `exitCode`, but it cannot be tied with `continuations` as they are protected by different primitives (i.e., lock and actor). 2. Gather `waiters` related operations into a single actor method, guaranteeing those are performed atomically under actor protection---i.e., we actually don't need Mutex here. 3. Ensure initialized `waiters` are released (i.e., resumed) later (under any possible circumstances). 4. Move `process.wait` after `process.start` in `io.handleProcess` to run `SandboxService.wait` only after the `waiters[id]` is initialized. By doing fourth step, we can guarantee `SandboxService.wait` can meet only one of two following `ExitMonitor` state: i) existing with nil `exitCode`, or ii) existing with concrete `exitCode` (in case the process exited too early). In both cases, `exitCode` is preserved and returned. ## Type of Change - [X] Bug fix - [ ] New feature - [ ] Breaking change - [ ] Documentation update ## Motivation and Context [Why is this change needed?] ## Testing - [X] Tested locally - [ ] Added/updated tests - [ ] Added/updated docs
container
container is a tool that you can use to create and run Linux containers as lightweight virtual machines on your Mac. It's written in Swift, and optimized for Apple silicon.
The tool consumes and produces OCI-compatible container images, so you can pull and run images from any standard container registry. You can push images that you build to those registries as well, and run the images in any other OCI-compatible application.
container uses the Containerization Swift package for low level container, image, and process management.
Get started
Requirements
You need a Mac with Apple silicon to run container. To build it, see the BUILDING document.
container is supported on macOS 26, since it takes advantage of new features and enhancements to virtualization and networking in this release. We do not support older versions of macOS and the container maintainers typically will not address issues that cannot be reproduced on the macOS 26.
Initial install
Download the latest signed installer package for container from the GitHub release page.
To install the tool, double-click the package file and follow the instructions. Enter your administrator password when prompted, to give the installer permission to place the installed files under /usr/local.
Start the system service with:
container system start
Upgrade or downgrade
For both upgrading and downgrading, you can manually download and install the signed installer package by following the steps from initial install or use the update-container.sh script (installed to /usr/local/bin).
If you're upgrading and downgrading, you must stop your existing container:
container system stop
For upgrading to the latest release version, simply run the command below:
/usr/local/bin/update-container.sh
If you're downgrading, you must uninstall your existing container (the -k flag keeps your user data, while -d removes it):
/usr/local/bin/uninstall-container.sh -k
/usr/local/bin/update-container.sh -v 0.3.0
Start the system service with:
container system start
Uninstall
Use the uninstall-container.sh script (installed to /usr/local/bin) to remove container from your system. To remove your user data along with the tool, run:
/usr/local/bin/uninstall-container.sh -d
To retain your user data so that it is available should you reinstall later, run:
/usr/local/bin/uninstall-container.sh -k
Next steps
- Take a guided tour of
containerby building, running, and publishing a simple web server image. - Learn how to use various
containerfeatures. - Read a brief description and technical overview of
container. - Browse the full command reference.
- Build and run
containeron your own development system. - View the project API documentation.
Contributing
Contributions to container are welcomed and encouraged. Please see our main contributing guide for more information.
Project Status
The container project is currently under active development. Its stability, both for consuming the project as a Swift package and the container tool, is only guaranteed within patch versions, such as between 0.1.1 and 0.1.2. Minor version number releases may include breaking changes until we achieve a 1.0.0 release.
