diff --git a/Sources/Logging/Locks.swift b/Sources/Logging/Locks.swift index 98ba700b..f8ffbcd9 100644 --- a/Sources/Logging/Locks.swift +++ b/Sources/Logging/Locks.swift @@ -28,9 +28,7 @@ #if canImport(WASILibc) // No locking on WASILibc -#else - -#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) +#elseif canImport(Darwin) import Darwin #elseif os(Windows) import WinSDK @@ -138,7 +136,9 @@ extension Lock { /// one used by NIO. On Windows, the lock is based on the substantially similar /// `SRWLOCK` type. internal final class ReadWriteLock { - #if os(Windows) + #if canImport(WASILibc) + // WASILibc is single threaded, provides no locks + #elseif os(Windows) fileprivate let rwlock: UnsafeMutablePointer = UnsafeMutablePointer.allocate(capacity: 1) fileprivate var shared: Bool = true @@ -149,7 +149,9 @@ internal final class ReadWriteLock { /// Create a new lock. public init() { - #if os(Windows) + #if canImport(WASILibc) + // WASILibc is single threaded, provides no locks + #elseif os(Windows) InitializeSRWLock(self.rwlock) #else let err = pthread_rwlock_init(self.rwlock, nil) @@ -158,7 +160,9 @@ internal final class ReadWriteLock { } deinit { - #if os(Windows) + #if canImport(WASILibc) + // WASILibc is single threaded, provides no locks + #elseif os(Windows) // SRWLOCK does not need to be free'd #else let err = pthread_rwlock_destroy(self.rwlock) @@ -172,7 +176,9 @@ internal final class ReadWriteLock { /// Whenever possible, consider using `withReaderLock` instead of this /// method and `unlock`, to simplify lock handling. public func lockRead() { - #if os(Windows) + #if canImport(WASILibc) + // WASILibc is single threaded, provides no locks + #elseif os(Windows) AcquireSRWLockShared(self.rwlock) self.shared = true #else @@ -186,7 +192,9 @@ internal final class ReadWriteLock { /// Whenever possible, consider using `withWriterLock` instead of this /// method and `unlock`, to simplify lock handling. public func lockWrite() { - #if os(Windows) + #if canImport(WASILibc) + // WASILibc is single threaded, provides no locks + #elseif os(Windows) AcquireSRWLockExclusive(self.rwlock) self.shared = false #else @@ -201,7 +209,9 @@ internal final class ReadWriteLock { /// instead of this method and `lockRead` and `lockWrite`, to simplify lock /// handling. public func unlock() { - #if os(Windows) + #if canImport(WASILibc) + // WASILibc is single threaded, provides no locks + #elseif os(Windows) if self.shared { ReleaseSRWLockShared(self.rwlock) } else { @@ -261,4 +271,3 @@ extension ReadWriteLock { try self.withWriterLock(body) } } -#endif diff --git a/Sources/Logging/Logging.swift b/Sources/Logging/Logging.swift index 12a0e3ef..ab49d21c 100644 --- a/Sources/Logging/Logging.swift +++ b/Sources/Logging/Logging.swift @@ -650,13 +650,7 @@ extension Logger { /// configured. `LoggingSystem` is set up just once in a given program to set up the desired logging backend /// implementation. public enum LoggingSystem { - #if canImport(WASILibc) - // WASILibc is single threaded, provides no locks - #else - fileprivate static let lock = ReadWriteLock() - #endif - fileprivate static var factory: (String) -> LogHandler = StreamLogHandler.standardOutput - fileprivate static var initialized = false + private static let _factory = FactoryBox(StreamLogHandler.standardOutput) /// `bootstrap` is a one-time configuration function which globally selects the desired logging backend /// implementation. `bootstrap` can be called at maximum once in any given program, calling it more than once will @@ -665,28 +659,40 @@ public enum LoggingSystem { /// - parameters: /// - factory: A closure that given a `Logger` identifier, produces an instance of the `LogHandler`. public static func bootstrap(_ factory: @escaping (String) -> LogHandler) { - #if canImport(WASILibc) - precondition(!self.initialized, "logging system can only be initialized once per process.") - self.factory = factory - self.initialized = true - #else - self.lock.withWriterLock { - precondition(!self.initialized, "logging system can only be initialized once per process.") - self.factory = factory - self.initialized = true - } - #endif + self._factory.replaceFactory(factory, validate: true) } // for our testing we want to allow multiple bootstraping internal static func bootstrapInternal(_ factory: @escaping (String) -> LogHandler) { - #if canImport(WASILibc) - self.factory = factory - #else - self.lock.withWriterLock { - self.factory = factory + self._factory.replaceFactory(factory, validate: false) + } + + fileprivate static var factory: (String) -> LogHandler { + return self._factory.underlying + } + + private final class FactoryBox { + private let lock = ReadWriteLock() + fileprivate var _underlying: (String) -> LogHandler + private var initialized = false + + init(_ underlying: @escaping (String) -> LogHandler) { + self._underlying = underlying + } + + func replaceFactory(_ factory: @escaping (String) -> LogHandler, validate: Bool) { + self.lock.withWriterLock { + precondition(!validate || !self.initialized, "logging system can only be initialized once per process.") + self._underlying = factory + self.initialized = true + } + } + + var underlying: (String) -> LogHandler { + return self.lock.withReaderLock { + return self._underlying + } } - #endif } } @@ -774,11 +780,7 @@ extension Logger { /// - parameters: /// - label: An identifier for the creator of a `Logger`. public init(label: String) { - #if canImport(WASILibc) self.init(label: label, LoggingSystem.factory(label)) - #else - self = LoggingSystem.lock.withReaderLock { Logger(label: label, LoggingSystem.factory(label)) } - #endif } /// Construct a `Logger` given a `label` identifying the creator of the `Logger` or a non-standard `LogHandler`.