Skip to content

Conversation

@alltheseas
Copy link
Collaborator

Summary

• - 851e855 – Switch networking (findEvent, relay list) and EventCache to the
new borrow helpers to avoid holding raw NdbTxn values.

  • ae9b648 – Add withNote and withProfile synchronous borrow helpers to
    Ndb.swift, copying to owned values before returning to callers.
  • 34b1ea8 – Merge upstream-master into pr3336.
  • 52115d0 – Fix profile crash.
  • d651084 – Add AGENTS.md.
  • 161fd96 – Add regression test for async waitFor.
  • f628a34 – Return owned notes from Ndb async waiters.
  • 37c4349 – Fix profile crash.

Checklist

Experimental Feature Checklist

Tip

This Pull Request is an experimental feature for Damus Labs, and follows a fast-track review process.
The overall requirements are lowered and the review process is not as strict as usual. However, the feature will only be available for Purple users who opt-in.

  • I have read (or I am familiar with) the Contribution Guidelines.
  • I have done some testing on the changes in this PR to ensure it is at least functional.
  • I made sure that this new feature is only available when the user opts-in from the Damus Labs screen, and does not affect the rest of the app when turned off.
  • My PR is either small, or I have split it into smaller logical commits that are easier to review.
  • I have added the signoff line to all my commits. See Signing off your work.
  • I have added an appropriate changelog entry to my commit in this PR. See Adding changelog entries.
    • Example changelog entry: Changelog-Added: Added experimental feature <X> to Damus Labs

Standard PR Checklist

  • I have read (or I am familiar with) the Contribution Guidelines
  • I have tested the changes in this PR
  • I have profiled the changes to ensure there are no performance regressions, or I do not need to profile the changes.
  • I have opened or referred to an existing github issue related to this change.
  • My PR is either small, or I have split it into smaller logical commits that are easier to review
  • I have added the signoff line to all my commits. See Signing off your work
  • I have added appropriate changelog entries for the changes in this PR. See Adding changelog entries
    • I do not need to add a changelog entry. Reason: [Please provide a reason]
  • I have added appropriate Closes: or Fixes: tags in the commit messages wherever applicable, or made sure those are not needed. See Submitting patches

Test report

Please provide a test report for the changes in this PR. You can use the template below, but feel free to modify it as needed.

Device: [Please specify the device you used for testing]

iOS: [Please specify the iOS version you used for testing]

Damus: [Please specify the Damus version or commit hash you used for testing]

Setup: [Please provide a brief description of the setup you used for testing, if applicable]

Steps: [Please provide a list of steps you took to test the changes in this PR]

Results:

  • PASS
  • Partial PASS
    • Details: [Please provide details of the partial pass]

Other notes

[Please provide any other information that you think is relevant to this PR.]

danieldaquino and others added 6 commits November 19, 2025 20:23
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 damus-io#3329

Note 2: This relates to the fix of an unreleased feature, so therefore no
changelog is needed.

Changelog-None
Co-authored-by: alltheseas <[email protected]>
Closes: damus-io#3327
Signed-off-by: Daniel D’Aquino <[email protected]>
@danieldaquino danieldaquino added the pr-in-queue The author has multiple active PRs. So this has been placed in a queue behind their other PRs. label Nov 24, 2025
@alltheseas
Copy link
Collaborator Author

Changes in this PR do NOT fix #3343

- damus/Core/Networking/NostrNetworkManager/ProfilesManager.swift:84-104 still uses streamIndefinitely to yield NdbNoteLenders and then hands raw NdbTxn<ProfileRecord?
    > objects back to UI consumers via ProfileStreamItem (damus/Core/Networking/NostrNetworkManager/ProfilesManager.swift:143-148). ProfileObserver continues to hold those
    transactions alive on the main actor (damus/Core/Nostr/ProfileObserver.swift:22-34). None of the lifetime/ownership guardrails that would prevent the observed mdb_page_get/
    mdb_txn_commit crashes were added, so the two crash stacks coming out of ProfilesManager.listenToProfileChanges and ProfileObserver.watchProfileChanges remain unchanged.
  - The preloading pipeline that is crashing in preload_event (Thread 12 with mdb_cursor_open in the 2025-11-25_23-11-17 log) is untouched. preload_event and the async Task
    spawned by preload_events still render note content in the background before plan.data is mutated (damus/Shared/Utilities/EventCache.swift:370-420 and damus/Shared/
    Utilities/EventCache.swift:444-460). The only change in this file is swapping .lookup_note(...).to_owned() for withOwnedNote at line 225, which is just a wrapper around the
    same copy and doesn’t impact the code paths that hit LMDB cursors.
  - The crashes where Ndb.close() traps during background termination (2025-11-26_10-13-28 and _15-12-26) also remain. Ndb.close is still a straight call to ndb_destroy with
    no coordination with outstanding query tasks or the TaskManager in SubscriptionManager (nostrdb/Ndb.swift:210-217). The branch doesn’t introduce any ordering between
    ContentView tearing down and long-running nostrdb work, so the assertion failure seen in the crash logs is still possible.
  - The new “owned” helpers and the change to waitFor don’t alter runtime behaviour in the app. withOwnedNote/withOwnedProfile simply move the existing .to_owned() call into Ndb
    and are only invoked in places that were already copying (EventCache lookup, findEvent, relay list manager). waitFor now returns NostrEvent?, but rg shows no production code
    ever calls this API (only the new unit test), so it cannot affect any of the recorded crashes.

  Given that none of the code paths implicated in the crash logs (ProfilesManager/ProfileObserver, preload_event/ContentRenderer, Kingfisher rendering, or the Ndb.close
  lifecycle) changed in this branch, none of the recorded crashes are addressed by this PR.

@alltheseas
Copy link
Collaborator Author

This PR is too big, and does not seem to fix outstanding crashes. I will instead tackle recent crashes one by one in separate PRs.

@alltheseas alltheseas closed this Nov 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr-in-queue The author has multiple active PRs. So this has been placed in a queue behind their other PRs.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants