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
|
/*
This source file is part of the Swift.org open source project
Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/
import class Foundation.NSLock
// FIXME: This wrapper could benefit from local static variables, in which case
// we could embed the cache object inside the accessor.
//
/// Thread-safe lazily cached methods.
///
/// The `lazy` annotation in Swift does not result in a thread-safe accessor,
/// which can make it an easy source of hard-to-find concurrency races. This
/// class defines a wrapper designed to be used as an alternative for
/// `lazy`. Example usage:
///
/// ```
/// class Foo {
/// var bar: Int { return barCache.getValue(self) }
/// var barCache = LazyCache(someExpensiveMethod)
///
/// func someExpensiveMethod() -> Int { ... }
/// }
/// ```
///
/// See: https://bugs.swift.org/browse/SR-1042
@available(*, deprecated, message: "This implementation does not work -- https://github.com/apple/swift-tools-support-core/issues/385")
public struct LazyCache<Class, T> {
// FIXME: It would be nice to avoid a per-instance lock, but this type isn't
// intended for creating large numbers of instances of. We also really want
// a reader-writer lock or something similar here.
private var lock = NSLock()
let body: (Class) -> () -> T
var cachedValue: T?
/// Create a lazy cache from a method value.
public init(_ body: @escaping (Class) -> () -> T) {
self.body = body
}
/// Get the cached value, computing it if necessary.
public mutating func getValue(_ instance: Class) -> T {
// FIXME: This is unfortunate, see note w.r.t. the lock.
return lock.withLock {
if let value = cachedValue {
return value
} else {
let result = body(instance)()
cachedValue = result
return result
}
}
}
}
#if swift(>=5.6)
@available(*, unavailable) // until https://github.com/apple/swift-tools-support-core/issues/385 is fixed
extension LazyCache: Sendable {}
#endif
|