damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

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 } }