Skip to content

Commit

Permalink
Switch caching to NSMapTable for safe, unlocked concurrent reads
Browse files Browse the repository at this point in the history
  • Loading branch information
EricRabil committed Apr 13, 2022
1 parent 0cd09f1 commit 5c68daa
Showing 1 changed file with 34 additions and 10 deletions.
44 changes: 34 additions & 10 deletions Sources/Swog/Drivers/OSLog/OSLogDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public extension String {

/// A logging driver that outputs to OSLog without the consequences of wrapping OSLog
public class OSLogDriver: LoggingDriver {
public var logs = [Int: OSLog]()
public var logs: NSMapTable<NSNumber, OSLog> = NSMapTable.strongToStrongObjects()

@usableFromInline internal let writeSemaphore = DispatchSemaphore(value: 1)

public static let shared = OSLogDriver()
Expand Down Expand Up @@ -55,28 +56,51 @@ public class OSLogDriver: LoggingDriver {
}
}

extension StaticString {
@_transparent
@usableFromInline
func firstIndex(of character: StaticString) -> Int? {
guard let pos = strchr(UnsafeRawPointer(utf8Start).assumingMemoryBound(to: Int8.self), Int32(character.utf8Start.pointee)) else {
return nil
}
return abs(utf8Start.distance(to: UnsafeRawPointer(pos).assumingMemoryBound(to: UInt8.self)))
}

@_transparent
@usableFromInline
func hashUpToCharacter(_ character: StaticString, hasher: inout Hasher) {
let index = firstIndex(of: character) ?? utf8CodeUnitCount
hasher.combine(bytes: UnsafeRawBufferPointer(start: utf8Start, count: index))
}
}

internal extension OSLogDriver {
@_transparent
@usableFromInline
func log(forCategory category: StaticString, fileID: StaticString) -> OSLog {
let subsystemID = String(String(fileID).split(separator: "/").first!)
let category = String(category)
let hash = [subsystemID, category].hashValue
var hasher = Hasher()
fileID.hashUpToCharacter("/", hasher: &hasher)
hasher.combine(bytes: UnsafeRawBufferPointer(start: category.utf8Start, count: category.utf8CodeUnitCount))
let hash = hasher.finalize() as NSNumber

if _fastPath(logs[hash] != nil) {
return logs[hash]!
var log = logs.object(forKey: hash)

if _fastPath(log != nil) {
return log!
}

writeSemaphore.wait()
defer {
writeSemaphore.signal()
}
if let log = logs[hash] {
if let log = logs.object(forKey: hash) {
// did someone else beat us to the punch?
return log
}
let log = OSLog(subsystem: String(subsystemPrefix) + subsystemID, category: category)
logs[hash] = log
return log

let subsystemID = String(String(fileID).split(separator: "/").first!)
log = OSLog(subsystem: String(subsystemPrefix) + subsystemID, category: String(category))
logs.setObject(log, forKey: hash)
return log!
}
}

0 comments on commit 5c68daa

Please sign in to comment.