1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
/// Essentially a dictionary where results are asynchronously computed on access.
package class Cache<Key: Sendable & Hashable, Result: Sendable> {
private var storage: [Key: Task<Result, Error>] = [:]
package init() {}
package func get(
_ key: Key,
isolation: isolated any Actor,
compute: @Sendable @escaping (Key) async throws -> Result
) async throws -> Result {
let task: Task<Result, Error>
if let cached = storage[key] {
task = cached
} else {
task = Task {
try await compute(key)
}
storage[key] = task
}
return try await task.value
}
/// Get the value cached for `key`. If no value exists for `key`, try deriving the result from an existing cache entry
/// that satisfies `canReuseKey` by applying `transform` to that result.
package func getDerived(
isolation: isolated any Actor,
_ key: Key,
canReuseKey: @Sendable @escaping (Key) -> Bool,
transform: @Sendable @escaping (_ cachedResult: Result) -> Result
) async throws -> Result? {
if let cached = storage[key] {
// If we have a value for the requested key, prefer that
return try await cached.value
}
// See if don't have an entry for this key, see if we can derive the value from a cached entry.
for (cachedKey, cachedValue) in storage {
guard canReuseKey(cachedKey) else {
continue
}
let transformed = Task { try await transform(cachedValue.value) }
// Cache the transformed result.
storage[key] = transformed
return try await transformed.value
}
return nil
}
package func clear(isolation: isolated any Actor, where condition: (Key) -> Bool) {
for key in storage.keys {
if condition(key) {
storage[key] = nil
}
}
}
package func clearAll(isolation: isolated any Actor) {
storage.removeAll()
}
}
|