diff --git a/Package.swift b/Package.swift index 0f7ce4e77..dcfa42b87 100644 --- a/Package.swift +++ b/Package.swift @@ -132,7 +132,7 @@ let package = Package( dependencies: ["WalletConnectUtils", "WalletConnectNetworking"]), .target( name: "Database", - dependencies: []), + dependencies: ["WalletConnectUtils"]), .target( name: "WalletConnectModal", dependencies: ["QRCode", "WalletConnectSign"], diff --git a/Sources/Database/DatabaseImports.swift b/Sources/Database/DatabaseImports.swift new file mode 100644 index 000000000..39a0c89b7 --- /dev/null +++ b/Sources/Database/DatabaseImports.swift @@ -0,0 +1,3 @@ +#if !CocoaPods +@_exported import WalletConnectUtils +#endif diff --git a/Sources/Database/DiskSqlite.swift b/Sources/Database/DiskSqlite.swift index 19ffba05e..8d7b77c49 100644 --- a/Sources/Database/DiskSqlite.swift +++ b/Sources/Database/DiskSqlite.swift @@ -7,40 +7,50 @@ public final class DiskSqlite: Sqlite { private var db: OpaquePointer? + private let lock = UnfairLock() + public init(path: String) { self.path = path } public func openDatabase() throws { - guard sqlite3_open_v2(path, &db, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_FULLMUTEX, nil) == SQLITE_OK else { - throw SQLiteError.openDatabase(path: path) + try lock.locked { + guard sqlite3_open_v2(path, &db, SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_FULLMUTEX, nil) == SQLITE_OK else { + throw SQLiteError.openDatabase(path: path) + } } } public func query(sql: String) throws -> [Row] { - var queryStatement: OpaquePointer? - guard sqlite3_prepare_v2(db, sql, -1, &queryStatement, nil) == SQLITE_OK else { - throw SQLiteError.queryPrepare(statement: sql) - } - var rows: [Row] = [] - while sqlite3_step(queryStatement) == SQLITE_ROW { - let decoder = SqliteRowDecoder(statement: queryStatement) - guard let row = try? Row(decoder: decoder) else { continue } - rows.append(row) + return try lock.locked { + var queryStatement: OpaquePointer? + guard sqlite3_prepare_v2(db, sql, -1, &queryStatement, nil) == SQLITE_OK else { + throw SQLiteError.queryPrepare(statement: sql) + } + var rows: [Row] = [] + while sqlite3_step(queryStatement) == SQLITE_ROW { + let decoder = SqliteRowDecoder(statement: queryStatement) + guard let row = try? Row(decoder: decoder) else { continue } + rows.append(row) + } + sqlite3_finalize(queryStatement) + return rows } - sqlite3_finalize(queryStatement) - return rows } public func execute(sql: String) throws { - var error: UnsafeMutablePointer? - guard sqlite3_exec(db, sql, nil, nil, &error) == SQLITE_OK else { - let message = error.map { String(cString: $0) } - throw SQLiteError.exec(error: message) + try lock.locked { + var error: UnsafeMutablePointer? + guard sqlite3_exec(db, sql, nil, nil, &error) == SQLITE_OK else { + let message = error.map { String(cString: $0) } + throw SQLiteError.exec(error: message) + } } } public func closeConnection() { - sqlite3_close(db) + lock.locked { + sqlite3_close(db) + } } } diff --git a/Sources/WalletConnectUtils/UnfairLock.swift b/Sources/WalletConnectUtils/UnfairLock.swift new file mode 100644 index 000000000..3e22d97d9 --- /dev/null +++ b/Sources/WalletConnectUtils/UnfairLock.swift @@ -0,0 +1,21 @@ +import Foundation + +public final class UnfairLock { + private var lock: UnsafeMutablePointer + + public init() { + lock = UnsafeMutablePointer.allocate(capacity: 1) + lock.initialize(to: os_unfair_lock()) + } + + deinit { + lock.deallocate() + } + + @discardableResult + public func locked(_ f: () throws -> ReturnValue) rethrows -> ReturnValue { + os_unfair_lock_lock(lock) + defer { os_unfair_lock_unlock(lock) } + return try f() + } +}