Created
August 20, 2024 17:13
-
-
Save thomsmed/90b535275f0fe4380cc84f0de71456a3 to your computer and use it in GitHub Desktop.
Simple example on how to restrict access to one or more "resources" (local storage, remote endpoint, etc). Very similar to how one would use a semaphore for protected access.
This file contains 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
// | |
// RestrictiveResourceLoader.swift | |
// | |
import Foundation | |
/// Simple example on how to restrict access to one or more "resources" (local storage, remote endpoint, etc). | |
/// Very similar to how one would use a semaphore for protected access. | |
final actor RestrictiveResourceLoader { | |
private var isFetchingResource: Bool = false | |
private var waitingContinuations: [CheckedContinuation<Void, Never>] = [] | |
/// Wait for in line for the "resource(s)" to be available. | |
private func grab() async { | |
if isFetchingResource { | |
await withCheckedContinuation { continuation in | |
waitingContinuations.insert(continuation, at: 0) | |
} | |
} | |
isFetchingResource = true | |
} | |
/// Release the "resource(s)", making the "resource(s)" available for other callers. | |
private func release() { | |
// Make sure to either update `isFetchingResource` or resume next waiting continuation. | |
if let nextWaitingContinuation = waitingContinuations.popLast() { | |
nextWaitingContinuation.resume() | |
} else { | |
isFetchingResource = false | |
} | |
} | |
private func actuallyGrabResource() async throws -> String { | |
// Simulate some asynchronous work. | |
try await Task.sleep(for: .seconds(2)) | |
return "Some text resource" | |
} | |
} | |
extension RestrictiveResourceLoader { | |
/// Restricting resource grabbing to only one task at the time. | |
/// | |
/// Note: Cancelation could be handled better. | |
func grabResource() async throws -> String { | |
try Task.checkCancellation() | |
await grab() | |
defer { release() } | |
try Task.checkCancellation() | |
return try await actuallyGrabResource() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment