Skip to content

Commit

Permalink
Fix HBMemoryPersistDriver (#220)
Browse files Browse the repository at this point in the history
Found out I completely messed up the concurrent access and was allowing access to the value dictionary from multiple event loops
  • Loading branch information
adam-fowler committed Aug 9, 2023
1 parent ab14d8a commit 6092c8f
Showing 1 changed file with 11 additions and 5 deletions.
16 changes: 11 additions & 5 deletions Sources/Hummingbird/Storage/MemoryPersistDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import NIOCore
/// In memory driver for persist system for storing persistent cross request key/value pairs
public final class HBMemoryPersistDriver: HBPersistDriver {
public init(eventLoopGroup: EventLoopGroup) {
self.eventLoop = eventLoopGroup.next()
self.values = [:]
self.task = eventLoopGroup.next().scheduleRepeatedTask(initialDelay: .hours(1), delay: .hours(1)) { _ in
self.task = self.eventLoop.scheduleRepeatedTask(initialDelay: .hours(1), delay: .hours(1)) { _ in
self.tidy()
}
}
Expand All @@ -33,20 +34,20 @@ public final class HBMemoryPersistDriver: HBPersistDriver {
}

public func create<Object: Codable>(key: String, value: Object, expires: TimeAmount?, request: HBRequest) -> EventLoopFuture<Void> {
return request.eventLoop.submit {
return self.eventLoop.submit {
guard self.values[key] == nil else { throw HBPersistError.duplicate }
self.values[key] = .init(value: value, expires: expires)
}
}

public func set<Object: Codable>(key: String, value: Object, expires: TimeAmount?, request: HBRequest) -> EventLoopFuture<Void> {
return request.eventLoop.submit {
return self.eventLoop.submit {
self.values[key] = .init(value: value, expires: expires)
}
}

public func get<Object: Codable>(key: String, as: Object.Type, request: HBRequest) -> EventLoopFuture<Object?> {
return request.eventLoop.submit {
return self.eventLoop.submit {
guard let item = self.values[key] else { return nil }
guard let expires = item.epochExpires else { return item.value as? Object }
guard Item.getEpochTime() <= expires else { return nil }
Expand All @@ -55,7 +56,7 @@ public final class HBMemoryPersistDriver: HBPersistDriver {
}

public func remove(key: String, request: HBRequest) -> EventLoopFuture<Void> {
return request.eventLoop.submit {
return self.eventLoop.submit {
self.values[key] = nil
}
}
Expand Down Expand Up @@ -90,6 +91,11 @@ public final class HBMemoryPersistDriver: HBPersistDriver {
}
}

let eventLoop: EventLoop
var values: [String: Item]
var task: RepeatedTask?
}

// We are able to conform HBMemoryPersistDriver to `@unchecked Sendable` as the value dictionary
// is only ever access on the one event loop and the task is only set in the `init`
extension HBMemoryPersistDriver: @unchecked Sendable {}

0 comments on commit 6092c8f

Please sign in to comment.