@@ -222,92 +222,3 @@ extension ThreadSafeBox where Value == String {
222222}
223223
224224extension ThreadSafeBox : @unchecked Sendable where Value: Sendable { }
225-
226- /// Thread-safe value boxing structure that provides synchronized asynchronous memoization of a wrapped value.
227- public final class AsyncMemoizableThreadSafeBox < Value: Sendable > : @unchecked Sendable {
228- private var underlying : Value ?
229- private let lock = NSLock ( )
230- private let asyncCoordination = AsyncMemoizationCoordinator < Value > ( )
231-
232- public init ( ) { }
233-
234- /// Atomically retrieves the current value from the box.
235- ///
236- /// - Returns: The current value stored in the box, or nil if none is present.
237- public func get( ) -> Value ? {
238- self . lock. withLock {
239- self . underlying
240- }
241- }
242-
243- /// Atomically computes and caches a value produced by an async function, if not already present.
244- ///
245- /// If the box already contains a non-nil value that value is returned immediately.
246- /// Otherwise, the provided async closure is executed to compute the value, which is then
247- /// stored and returned.
248- ///
249- /// Concurrent calls to memoize will wait for the first call to complete and receive its result.
250- /// If the body throws an error, all pending calls receive that error and the state is reset.
251- ///
252- /// - Parameter body: An async closure that computes the value to store if none exists.
253- /// - Returns: The cached value or the newly computed value.
254- /// - Throws: Any error thrown by the computation closure.
255- @discardableResult
256- public func memoize( body: @Sendable @escaping ( ) async throws -> Value ) async throws -> Value {
257- if let value = self . get ( ) {
258- return value
259- }
260-
261- // Try to register as the executor, or get the existing task
262- let task : Task < Value , Error > = await self . asyncCoordination. getOrCreateTask {
263- // This closure is only called by the first caller
264- Task < Value , Error > {
265- // Double-check after acquiring coordination
266- if let value = self . get ( ) {
267- return value
268- }
269-
270- let value = try await body ( )
271-
272- // Store the value
273- self . lock. withLock {
274- self . underlying = value
275- }
276- return value
277- }
278- }
279-
280- // Everyone (including the first caller) awaits the same task
281- do {
282- let result = try await task. value
283- await self . asyncCoordination. clearTask ( )
284- return result
285- } catch {
286- await self . asyncCoordination. clearTask ( )
287- throw error
288- }
289- }
290-
291- // Actor for coordinating async memoization within a thread safe box
292- private actor AsyncMemoizationCoordinator < T: Sendable > : Sendable {
293- private var inProgressTask : Task < T , Error > ?
294-
295- /// Returns an existing task if one is in progress, or creates and stores a new one
296- func getOrCreateTask( _ createTask: @Sendable ( ) -> Task < T , Error > ) -> Task < T , Error > {
297- if let existingTask = inProgressTask {
298- return existingTask
299- }
300-
301- // We're the first - create and store the task
302- let task = createTask ( )
303- inProgressTask = task
304- return task
305- }
306-
307- /// Clears the current task
308- func clearTask( ) {
309- inProgressTask = nil
310- }
311- }
312- }
313-
0 commit comments