Fix profile crash
This fixes a crash that would occasionally occur when visiting profiles. NdbTxn objects were being deinitialized on different threads from their initialization, causing incorrect reference count decrements in thread-local transaction dictionaries. This led to premature destruction of shared ndb_txn C objects still in use by other tasks, resulting in use-after-free crashes. The root cause is that Swift does not guarantee tasks resume on the same thread after await suspension points, while NdbTxn's init/deinit rely on thread-local storage to track inherited transaction reference counts. This means that `NdbTxn` objects cannot be used in async functions, as that may cause the garbage collector to deinitialize `NdbTxn` at the end of such function, which may be running on a different thread at that point, causing the issue explained above. The fix in this case is to eliminate the `async` version of the `NdbNoteLender.borrow` method, and update usages to utilize other available methods. Note: This is a rewrite of the fix in https://github.com/damus-io/damus/pull/3329 Note 2: This relates to the fix of an unreleased feature, so therefore no changelog is needed. Changelog-None Co-authored-by: alltheseas <64376233+alltheseas@users.noreply.github.com> Closes: https://github.com/damus-io/damus/issues/3327 Signed-off-by: Daniel D’Aquino <daniel@daquino.me>
This commit is contained in:
@@ -66,19 +66,6 @@ enum NdbNoteLender: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrows the note temporarily (asynchronously)
|
||||
func borrow<T>(_ lendingFunction: (_: borrowing UnownedNdbNote) async throws -> T) async throws -> T {
|
||||
switch self {
|
||||
case .ndbNoteKey(let ndb, let noteKey):
|
||||
guard !ndb.is_closed else { throw LendingError.ndbClosed }
|
||||
guard let ndbNoteTxn = ndb.lookup_note_by_key(noteKey) else { throw LendingError.errorLoadingNote }
|
||||
guard let unownedNote = UnownedNdbNote(ndbNoteTxn) else { throw LendingError.errorLoadingNote }
|
||||
return try await lendingFunction(unownedNote)
|
||||
case .owned(let note):
|
||||
return try await lendingFunction(UnownedNdbNote(note))
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets an owned copy of the note
|
||||
func getCopy() throws -> NdbNote {
|
||||
return try self.borrow({ ev in
|
||||
|
||||
Reference in New Issue
Block a user