Skip to content

Commit

Permalink
Rename ReadWriteBox to ReadWriteLock
Browse files Browse the repository at this point in the history
  • Loading branch information
groue committed Sep 8, 2024
1 parent 8409768 commit ea4d208
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 108 deletions.
8 changes: 4 additions & 4 deletions GRDB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
5657AAB91D107001006283EF /* NSData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5657AAB81D107001006283EF /* NSData.swift */; };
5657AB0F1D10899D006283EF /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5657AB0E1D10899D006283EF /* URL.swift */; };
5659F4881EA8D94E004A4992 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5659F4871EA8D94E004A4992 /* Utils.swift */; };
5659F4901EA8D964004A4992 /* ReadWriteBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5659F48F1EA8D964004A4992 /* ReadWriteBox.swift */; };
5659F4901EA8D964004A4992 /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5659F48F1EA8D964004A4992 /* ReadWriteLock.swift */; };
5659F4981EA8D989004A4992 /* Pool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5659F4971EA8D989004A4992 /* Pool.swift */; };
5664759A1D97D8A000FF74B8 /* SQLCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 566475991D97D8A000FF74B8 /* SQLCollection.swift */; };
566475CC1D981D5E00FF74B8 /* SQLFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 566475CA1D981D5E00FF74B8 /* SQLFunctions.swift */; };
Expand Down Expand Up @@ -605,7 +605,7 @@
5657AB341D108BA9006283EF /* FoundationNSURLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationNSURLTests.swift; sourceTree = "<group>"; };
5657AB351D108BA9006283EF /* FoundationURLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationURLTests.swift; sourceTree = "<group>"; };
5659F4871EA8D94E004A4992 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
5659F48F1EA8D964004A4992 /* ReadWriteBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteBox.swift; sourceTree = "<group>"; };
5659F48F1EA8D964004A4992 /* ReadWriteLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = "<group>"; };
5659F4971EA8D989004A4992 /* Pool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pool.swift; sourceTree = "<group>"; };
565B0FEE1BBC7D980098DE03 /* FetchableRecordTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchableRecordTests.swift; sourceTree = "<group>"; };
565D5D701BBC694D00DC9BD4 /* Row+FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Row+FoundationTests.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1328,7 +1328,7 @@
563B8FC424A1D3B9007A48C9 /* OnDemandFuture.swift */,
563EF414215F87EB007DAACD /* OrderedDictionary.swift */,
5659F4971EA8D989004A4992 /* Pool.swift */,
5659F48F1EA8D964004A4992 /* ReadWriteBox.swift */,
5659F48F1EA8D964004A4992 /* ReadWriteLock.swift */,
563B8FB424A1D029007A48C9 /* ReceiveValuesOn.swift */,
56781B0A243F86E600650A83 /* Refinable.swift */,
5659F4871EA8D94E004A4992 /* Utils.swift */,
Expand Down Expand Up @@ -2185,7 +2185,7 @@
563B06AB217EF0CC00B38F35 /* ValueObservation.swift in Sources */,
56D110FA28AFC97E00E64463 /* MutablePersistableRecord+DAO.swift in Sources */,
56CEB5111EAA324B00BFAF62 /* FTS3+QueryInterface.swift in Sources */,
5659F4901EA8D964004A4992 /* ReadWriteBox.swift in Sources */,
5659F4901EA8D964004A4992 /* ReadWriteLock.swift in Sources */,
566A841A2041146100E50BFD /* DatabaseSnapshot.swift in Sources */,
569EF0E2200D2D8400A9FA45 /* DatabaseRegion.swift in Sources */,
56CEB4F11EAA2EFA00BFAF62 /* FetchableRecord.swift in Sources */,
Expand Down
86 changes: 47 additions & 39 deletions GRDB/Record/MutablePersistableRecord+DAO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -273,29 +273,33 @@ private struct InsertQuery: Hashable {
}

extension InsertQuery {
@ReadWriteBox private static var sqlCache: [InsertQuery: String] = [:]
private static let cacheLock: ReadWriteLock<[InsertQuery: String]> = ReadWriteLock([:])
var sql: String {
if let sql = Self.sqlCache[self] {
if let sql = Self.cacheLock.read({ $0[self] }) {
return sql
}
let columnsSQL = insertedColumns.map(\.quotedDatabaseIdentifier).joined(separator: ", ")
let valuesSQL = databaseQuestionMarks(count: insertedColumns.count)
let sql: String
switch onConflict {
case .abort:
sql = """
INSERT INTO \(tableName.quotedDatabaseIdentifier) (\(columnsSQL)) \
VALUES (\(valuesSQL))
"""
default:
sql = """
INSERT OR \(onConflict.rawValue) \
INTO \(tableName.quotedDatabaseIdentifier) (\(columnsSQL)) \
VALUES (\(valuesSQL))
"""

return Self.cacheLock.withLock { cache in
let columnsSQL = insertedColumns.map(\.quotedDatabaseIdentifier).joined(separator: ", ")
let valuesSQL = databaseQuestionMarks(count: insertedColumns.count)
let sql: String
switch onConflict {
case .abort:
sql = """
INSERT INTO \(tableName.quotedDatabaseIdentifier) (\(columnsSQL)) \
VALUES (\(valuesSQL))
"""
default:
sql = """
INSERT OR \(onConflict.rawValue) \
INTO \(tableName.quotedDatabaseIdentifier) (\(columnsSQL)) \
VALUES (\(valuesSQL))
"""
}

cache[self] = sql
return sql
}
Self.sqlCache[self] = sql
return sql
}
}

Expand All @@ -309,30 +313,34 @@ private struct UpdateQuery: Hashable {
}

extension UpdateQuery {
@ReadWriteBox private static var sqlCache: [UpdateQuery: String] = [:]
private static let cacheLock: ReadWriteLock<[UpdateQuery: String]> = ReadWriteLock([:])
var sql: String {
if let sql = Self.sqlCache[self] {
if let sql = Self.cacheLock.read({ $0[self] }) {
return sql
}
let updateSQL = updatedColumns.map { "\($0.quotedDatabaseIdentifier)=?" }.joined(separator: ", ")
let whereSQL = conditionColumns.map { "\($0.quotedDatabaseIdentifier)=?" }.joined(separator: " AND ")
let sql: String
switch onConflict {
case .abort:
sql = """
UPDATE \(tableName.quotedDatabaseIdentifier) \
SET \(updateSQL) \
WHERE \(whereSQL)
"""
default:
sql = """
UPDATE OR \(onConflict.rawValue) \(tableName.quotedDatabaseIdentifier) \
SET \(updateSQL) \
WHERE \(whereSQL)
"""

return Self.cacheLock.withLock { cache in
let updateSQL = updatedColumns.map { "\($0.quotedDatabaseIdentifier)=?" }.joined(separator: ", ")
let whereSQL = conditionColumns.map { "\($0.quotedDatabaseIdentifier)=?" }.joined(separator: " AND ")
let sql: String
switch onConflict {
case .abort:
sql = """
UPDATE \(tableName.quotedDatabaseIdentifier) \
SET \(updateSQL) \
WHERE \(whereSQL)
"""
default:
sql = """
UPDATE OR \(onConflict.rawValue) \(tableName.quotedDatabaseIdentifier) \
SET \(updateSQL) \
WHERE \(whereSQL)
"""
}

cache[self] = sql
return sql
}
Self.sqlCache[self] = sql
return sql
}
}

Expand Down
12 changes: 6 additions & 6 deletions GRDB/Utils/Pool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import Dispatch
/// got 3
final class Pool<T: Sendable>: Sendable {
private class Item: @unchecked Sendable {
// @unchecked because `isAvailable` is protected by `Pool.content`.
// @unchecked Sendable because `isAvailable` is protected by `contentLock`.
let element: T
var isAvailable: Bool

Expand All @@ -59,7 +59,7 @@ final class Pool<T: Sendable>: Sendable {
typealias ElementAndRelease = (element: T, release: @Sendable (PoolCompletion) -> Void)

private let makeElement: @Sendable (Int) throws -> T
private let content = ReadWriteBox(wrappedValue: Content(items: [], createdCount: 0))
private let contentLock = ReadWriteLock(Content(items: [], createdCount: 0))
private let itemsSemaphore: DispatchSemaphore // limits the number of elements
private let itemsGroup: DispatchGroup // knows when no element is used
private let barrierQueue: DispatchQueue
Expand Down Expand Up @@ -93,7 +93,7 @@ final class Pool<T: Sendable>: Sendable {
itemsSemaphore.wait()
itemsGroup.enter()
do {
let item = try content.update { content -> Item in
let item = try contentLock.withLock { content -> Item in
if let item = content.items.first(where: \.isAvailable) {
item.isAvailable = false
return item
Expand Down Expand Up @@ -142,7 +142,7 @@ final class Pool<T: Sendable>: Sendable {
}

private func release(_ item: Item, completion: PoolCompletion) {
content.update { content in
contentLock.withLock { content in
switch completion {
case .reuse:
// This is why Item is a class, not a struct: so that we can
Expand All @@ -162,7 +162,7 @@ final class Pool<T: Sendable>: Sendable {
/// Performs a block on each pool element, available or not.
/// The block is run is some arbitrary dispatch queue.
func forEach(_ body: (T) throws -> Void) rethrows {
try content.read { content in
try contentLock.read { content in
for item in content.items {
try body(item.element)
}
Expand All @@ -172,7 +172,7 @@ final class Pool<T: Sendable>: Sendable {
/// Removes all elements from the pool.
/// Currently used elements won't be reused.
func removeAll() {
content.update { $0.items.removeAll() }
contentLock.withLock { $0.items.removeAll() }
}

/// Blocks until no element is used, and runs the `barrier` function before
Expand Down
52 changes: 0 additions & 52 deletions GRDB/Utils/ReadWriteBox.swift

This file was deleted.

32 changes: 32 additions & 0 deletions GRDB/Utils/ReadWriteLock.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Dispatch

/// A ReadWriteLock grants multiple readers and single-writer guarantees on
/// a value. It is backed by a concurrent DispatchQueue.
final class ReadWriteLock<T> {
private var _value: T
private var queue: DispatchQueue

init(_ value: T) {
_value = value
queue = DispatchQueue(label: "GRDB.ReadWriteLock", attributes: [.concurrent])
}

/// Reads the value.
func read<U>(_ body: (T) throws -> U) rethrows -> U {
try queue.sync {
try body(_value)
}
}

/// Runs the provided closure while holding a lock on the value.
///
/// - parameter body: A closure that can modify the value.
func withLock<U>(_ body: (inout T) throws -> U) rethrows -> U {
try queue.sync(flags: [.barrier]) {
try body(&_value)
}
}
}

// @unchecked because `_value` is protected by `queue`
extension ReadWriteLock: @unchecked Sendable where T: Sendable { }
8 changes: 4 additions & 4 deletions GRDBCustom.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
5657AB611D108BA9006283EF /* FoundationNSURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5657AB341D108BA9006283EF /* FoundationNSURLTests.swift */; };
5657AB691D108BA9006283EF /* FoundationURLTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5657AB351D108BA9006283EF /* FoundationURLTests.swift */; };
5659F48A1EA8D94E004A4992 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5659F4871EA8D94E004A4992 /* Utils.swift */; };
5659F4921EA8D964004A4992 /* ReadWriteBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5659F48F1EA8D964004A4992 /* ReadWriteBox.swift */; };
5659F4921EA8D964004A4992 /* ReadWriteLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5659F48F1EA8D964004A4992 /* ReadWriteLock.swift */; };
5659F49A1EA8D989004A4992 /* Pool.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5659F4971EA8D989004A4992 /* Pool.swift */; };
565EFAF11D0436CE00A8FA9D /* NumericOverflowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 565EFAED1D0436CE00A8FA9D /* NumericOverflowTests.swift */; };
5665F868203EF4640084C6C0 /* ColumnInfoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5665F865203EF4590084C6C0 /* ColumnInfoTests.swift */; };
Expand Down Expand Up @@ -635,7 +635,7 @@
5657AB341D108BA9006283EF /* FoundationNSURLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationNSURLTests.swift; sourceTree = "<group>"; };
5657AB351D108BA9006283EF /* FoundationURLTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationURLTests.swift; sourceTree = "<group>"; };
5659F4871EA8D94E004A4992 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
5659F48F1EA8D964004A4992 /* ReadWriteBox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteBox.swift; sourceTree = "<group>"; };
5659F48F1EA8D964004A4992 /* ReadWriteLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReadWriteLock.swift; sourceTree = "<group>"; };
5659F4971EA8D989004A4992 /* Pool.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pool.swift; sourceTree = "<group>"; };
565B0FEE1BBC7D980098DE03 /* FetchableRecordTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchableRecordTests.swift; sourceTree = "<group>"; };
565D5D701BBC694D00DC9BD4 /* Row+FoundationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Row+FoundationTests.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1349,7 +1349,7 @@
563B8FBC24A1D388007A48C9 /* OnDemandFuture.swift */,
563EF41E215F8A76007DAACD /* OrderedDictionary.swift */,
5659F4971EA8D989004A4992 /* Pool.swift */,
5659F48F1EA8D964004A4992 /* ReadWriteBox.swift */,
5659F48F1EA8D964004A4992 /* ReadWriteLock.swift */,
563B8FB924A1D036007A48C9 /* ReceiveValuesOn.swift */,
56DF37A623D77AA0009AAA05 /* Refinable.swift */,
5659F4871EA8D94E004A4992 /* Utils.swift */,
Expand Down Expand Up @@ -2009,7 +2009,7 @@
56012B82257404A400B4925B /* CommonTableExpression.swift in Sources */,
5656A8972295BD56001FF3FF /* SQLRelation.swift in Sources */,
56D110FF28AFC9C600E64463 /* MutablePersistableRecord+DAO.swift in Sources */,
5659F4921EA8D964004A4992 /* ReadWriteBox.swift in Sources */,
5659F4921EA8D964004A4992 /* ReadWriteLock.swift in Sources */,
566A842D20413D9A00E50BFD /* DatabaseSnapshot.swift in Sources */,
56CEB4F31EAA2EFA00BFAF62 /* FetchableRecord.swift in Sources */,
5656A8512295BD56001FF3FF /* SQLInterpolation+QueryInterface.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
- [X] GRDB7: DatabaseCursor has a primary associated type (b11c5dd2)
- [ ] GRDB7: Enable Strict Concurrency Checks (6aa43ded)
- [X] GRDB7: Sendable: OrderedDictionary (e022c35b)
- [ ] GRDB7: Rename ReadWriteBox to ReadWriteLock (7f5205ef)
- [X] GRDB7: Rename ReadWriteBox to ReadWriteLock (7f5205ef)
- [X] GRDB7: Sendable: DatabaseRegionConvertible (b4677ded)
- [ ] GRDB7: Sendable: ValueConcurrentObserver (87b9db65, 5465d056)
- [ ] GRDB7: Sendable: ValueWriteOnlyObserver (ff2a7548)
Expand Down
4 changes: 2 additions & 2 deletions Tests/GRDBTests/PoolTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import XCTest
class PoolTests: XCTestCase {
/// Returns a Pool whose elements are incremented integers: 1, 2, 3...
private func makeCounterPool(maximumCount: Int) -> Pool<Int> {
let count = ReadWriteBox(wrappedValue: 0)
let countMutex = Mutex(0)
return Pool(maximumCount: maximumCount, makeElement: { _ in
count.increment()
countMutex.increment()
})
}

Expand Down

0 comments on commit ea4d208

Please sign in to comment.