NdbTxn.swift (5190B)
1 // 2 // NdbTx.swift 3 // damus 4 // 5 // Created by William Casarin on 2023-08-30. 6 // 7 8 import Foundation 9 10 #if TXNDEBUG 11 fileprivate var txn_count: Int = 0 12 #endif 13 14 // Would use struct and ~Copyable but generics aren't supported well 15 class NdbTxn<T> { 16 var txn: ndb_txn 17 private var val: T! 18 var moved: Bool 19 var inherited: Bool 20 var ndb: Ndb 21 var generation: Int 22 var name: String 23 24 init?(ndb: Ndb, with: (NdbTxn<T>) -> T = { _ in () }, name: String? = nil) { 25 guard !ndb.is_closed else { return nil } 26 self.name = name ?? "txn" 27 self.ndb = ndb 28 self.generation = ndb.generation 29 if let active_txn = Thread.current.threadDictionary["ndb_txn"] as? ndb_txn { 30 // some parent thread is active, use that instead 31 print("txn: inherited txn") 32 self.txn = active_txn 33 self.inherited = true 34 self.generation = Thread.current.threadDictionary["txn_generation"] as! Int 35 } else { 36 self.txn = ndb_txn() 37 guard !ndb.is_closed else { return nil } 38 self.generation = ndb.generation 39 #if TXNDEBUG 40 txn_count += 1 41 #endif 42 let ok = ndb_begin_query(ndb.ndb.ndb, &self.txn) != 0 43 if !ok { 44 return nil 45 } 46 self.generation = ndb.generation 47 Thread.current.threadDictionary["ndb_txn"] = self.txn 48 Thread.current.threadDictionary["txn_generation"] = ndb.generation 49 self.inherited = false 50 } 51 #if TXNDEBUG 52 print("txn: open gen\(self.generation) '\(self.name)' \(txn_count)") 53 #endif 54 self.moved = false 55 self.val = with(self) 56 } 57 58 private init(ndb: Ndb, txn: ndb_txn, val: T, generation: Int, inherited: Bool, name: String) { 59 self.txn = txn 60 self.val = val 61 self.moved = false 62 self.inherited = inherited 63 self.ndb = ndb 64 self.generation = generation 65 self.name = name 66 } 67 68 /// Only access temporarily! Do not store database references for longterm use. If it's a primitive type you 69 /// can retrieve this value with `.value` 70 var unsafeUnownedValue: T { 71 precondition(!moved) 72 return val 73 } 74 75 deinit { 76 if self.generation != ndb.generation { 77 print("txn: OLD GENERATION (\(self.generation) != \(ndb.generation)), IGNORING") 78 return 79 } 80 if inherited { 81 print("txn: not closing. inherited ") 82 return 83 } 84 if moved { 85 //print("txn: not closing. moved") 86 return 87 } 88 if ndb.is_closed { 89 print("txn: not closing. db closed") 90 return 91 } 92 93 #if TXNDEBUG 94 txn_count -= 1; 95 print("txn: close gen\(generation) '\(name)' \(txn_count)") 96 #endif 97 ndb_end_query(&self.txn) 98 //self.skip_close = true 99 Thread.current.threadDictionary.removeObject(forKey: "ndb_txn") 100 } 101 102 // functor 103 func map<Y>(_ transform: (T) -> Y) -> NdbTxn<Y> { 104 self.moved = true 105 return .init(ndb: self.ndb, txn: self.txn, val: transform(val), generation: generation, inherited: inherited, name: self.name) 106 } 107 108 // comonad!? 109 // useful for moving ownership of a transaction to another value 110 func extend<Y>(_ with: (NdbTxn<T>) -> Y) -> NdbTxn<Y> { 111 self.moved = true 112 return .init(ndb: self.ndb, txn: self.txn, val: with(self), generation: generation, inherited: inherited, name: self.name) 113 } 114 } 115 116 protocol OptionalType { 117 associatedtype Wrapped 118 var optional: Wrapped? { get } 119 } 120 121 extension Optional: OptionalType { 122 typealias Wrapped = Wrapped 123 124 var optional: Wrapped? { 125 return self 126 } 127 } 128 129 extension NdbTxn where T: OptionalType { 130 func collect() -> NdbTxn<T.Wrapped>? { 131 guard let unwrappedVal: T.Wrapped = val.optional else { 132 return nil 133 } 134 self.moved = true 135 return NdbTxn<T.Wrapped>(ndb: self.ndb, txn: self.txn, val: unwrappedVal, generation: generation, inherited: inherited, name: name) 136 } 137 } 138 139 extension NdbTxn where T == Bool { var value: T { return self.unsafeUnownedValue } } 140 extension NdbTxn where T == Bool? { var value: T { return self.unsafeUnownedValue } } 141 extension NdbTxn where T == Int { var value: T { return self.unsafeUnownedValue } } 142 extension NdbTxn where T == Int? { var value: T { return self.unsafeUnownedValue } } 143 extension NdbTxn where T == Double { var value: T { return self.unsafeUnownedValue } } 144 extension NdbTxn where T == Double? { var value: T { return self.unsafeUnownedValue } } 145 extension NdbTxn where T == UInt64 { var value: T { return self.unsafeUnownedValue } } 146 extension NdbTxn where T == UInt64? { var value: T { return self.unsafeUnownedValue } } 147 extension NdbTxn where T == String { var value: T { return self.unsafeUnownedValue } } 148 extension NdbTxn where T == String? { var value: T { return self.unsafeUnownedValue } } 149 extension NdbTxn where T == NoteId? { var value: T { return self.unsafeUnownedValue } } 150 extension NdbTxn where T == NoteId { var value: T { return self.unsafeUnownedValue } }