-
Notifications
You must be signed in to change notification settings - Fork 154
Add containerization extras tests #148
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
KushagraSikka
wants to merge
5
commits into
apple:main
Choose a base branch
from
KushagraSikka:add-containerization-extras-tests
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,155
−39
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
b006179
docs: improve documentation and error messages for ContainerizationEx…
KushagraSikka 817deeb
test: add comprehensive unit tests for AsyncLock and Timeout utilitie…
KushagraSikka ff3d0cb
fix: resolve Swift 6.2 concurrency and type safety issues in tests - …
KushagraSikka 93d7b6b
Merge branch 'main' into add-containerization-extras-tests
KushagraSikka 8ec94fc
Merge branch 'main' into add-containerization-extras-tests
KushagraSikka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,21 +16,95 @@ | |
|
||
import Foundation | ||
|
||
/// `AsyncLock` provides a familiar locking API, with the main benefit being that it | ||
/// is safe to call async methods while holding the lock. This is primarily used in spots | ||
/// where an actor makes sense, but we may need to ensure we don't fall victim to actor | ||
/// reentrancy issues. | ||
/// An async-safe mutual exclusion lock for coordinating access to shared resources. | ||
/// | ||
/// `AsyncLock` provides a familiar locking API with the key benefit that it's safe to call | ||
/// async methods while holding the lock. This addresses scenarios where traditional actors | ||
/// might suffer from reentrancy issues or where you need explicit sequential access control. | ||
/// | ||
/// ## Use Cases | ||
/// - Protecting shared mutable state that requires async operations | ||
/// - Coordinating access to resources that don't support concurrent operations | ||
/// - Avoiding actor reentrancy issues in complex async workflows | ||
/// - Ensuring sequential execution of async operations | ||
/// | ||
/// ## Example usage: | ||
/// ```swift | ||
/// actor ResourceManager { | ||
/// private let lock = AsyncLock() | ||
/// private var resources: [String] = [] | ||
/// | ||
/// func addResource(_ name: String) async { | ||
/// await lock.withLock { context in | ||
/// // Async operations are safe within the lock | ||
/// let processedName = await processResourceName(name) | ||
/// resources.append(processedName) | ||
/// await notifyObservers(about: processedName) | ||
/// } | ||
/// } | ||
/// | ||
/// func getResourceCount() async -> Int { | ||
/// await lock.withLock { context in | ||
/// return resources.count | ||
/// } | ||
/// } | ||
/// } | ||
/// ``` | ||
/// | ||
/// ## Threading Safety | ||
/// This lock is designed for use within actors or other async contexts and provides | ||
/// mutual exclusion without blocking threads. Operations are queued and resumed | ||
/// sequentially as the lock becomes available. | ||
public actor AsyncLock { | ||
private var busy = false | ||
private var queue: ArraySlice<CheckedContinuation<(), Never>> = [] | ||
|
||
/// A context object provided to closures executed within the lock. | ||
/// | ||
/// The context serves as proof that the code is executing within the lock's | ||
/// critical section. While currently empty, it may be extended in the future | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is empty by design because this is sufficient to serve as proof that the code is executed within the lock. |
||
/// to provide lock-specific functionality. | ||
public struct Context: Sendable { | ||
fileprivate init() {} | ||
} | ||
|
||
/// Creates a new AsyncLock instance. | ||
/// | ||
/// The lock starts in an unlocked state and is ready for immediate use. | ||
public init() {} | ||
|
||
/// withLock provides a scoped locking API to run a function while holding the lock. | ||
/// Executes a closure while holding the lock, ensuring exclusive access. | ||
/// | ||
/// - Parameter body: An async closure to execute while holding the lock. | ||
/// The closure receives a `Context` parameter as proof of lock ownership. | ||
/// - Returns: The value returned by the closure | ||
/// - Throws: Any error thrown by the closure | ||
/// | ||
/// This method provides scoped locking - the lock is automatically acquired before | ||
/// the closure executes and released when the closure completes (either normally | ||
/// or by throwing an error). | ||
/// | ||
/// If the lock is already held, the current operation will suspend until the lock | ||
/// becomes available. Operations are queued and executed in FIFO order. | ||
/// | ||
/// ## Example: | ||
/// ```swift | ||
/// let lock = AsyncLock() | ||
/// var counter = 0 | ||
/// | ||
/// // Safely increment counter with async work | ||
/// let result = await lock.withLock { context in | ||
/// let oldValue = counter | ||
/// await Task.sleep(nanoseconds: 1_000_000) // Simulate async work | ||
/// counter = oldValue + 1 | ||
/// return counter | ||
/// } | ||
/// ``` | ||
/// | ||
/// ## Performance Notes | ||
/// - The lock uses actor isolation, so there's no thread blocking | ||
/// - Suspended operations consume minimal memory | ||
/// - Lock contention is resolved in first-in-first-out order | ||
public func withLock<T: Sendable>(_ body: @Sendable @escaping (Context) async throws -> T) async rethrows -> T { | ||
while self.busy { | ||
await withCheckedContinuation { cc in | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add
func appendResource(_ resource: String, context: AsyncLock.Context)
to demonstrate howcontext
is used? The required context parameter ensures that the function is called only inside a lock.