Skip to content

fix(sdk): Remove a cyclic reference of Client in ThreadSubscriptionCatchup#6594

Merged
Hywan merged 3 commits into
matrix-org:mainfrom
Hywan:fix-issue-6573
May 22, 2026
Merged

fix(sdk): Remove a cyclic reference of Client in ThreadSubscriptionCatchup#6594
Hywan merged 3 commits into
matrix-org:mainfrom
Hywan:fix-issue-6573

Conversation

@Hywan
Copy link
Copy Markdown
Member

@Hywan Hywan commented May 19, 2026

This patch fixes cyclic reference of Client in ThreadSubscriptionCatchup.

ThreadSubscriptionCatchup starts a task that call thread_subscriptions_catchup_task. This function captures a clone of ThreadSubscriptionCatchup (why not…) which contains a WeakClient, all good, no cycle here! However, the real task (not the function) captures Client to call enabled_thread_subscriptions to know if the function thread_subscriptions_catchup_task must be called. Consequently, the task captures a clone of Client, boom, we have a cycle.

This patch fixes the problem by spawning the task if and only if Client::enabled_thread_subscriptions returns Ok(true). This check is done outside the task. It seems saner and avoid creating this cycle.

Consequently, the ThreadSubscriptionCatchup::new method becomes async, which is perfectly fine as it was already wrapper inside an async block when initialised by the Client.

Without the fix, the test fails with:

thread 'client::thread_subscriptions::timed_tests::test_issue_6573_client_can_drop_thread_subscriptions_task' (2625650) panicked at crates/matrix-sdk/src/client/thread_subscriptions.rs:514:9:
assertion `left == right` failed
  left: 1
 right: 0

as expected.



  • I've documented the public API changes in the appropriate changelog files
    (see Writing changelog entries).
  • This PR was made with the help of AI.

…nCatchup`.

This patch fixes cyclic reference of `Client` in
`ThreadSubscriptionCatchup`.

`ThreadSubscriptionCatchup` starts a task that call
`thread_subscriptions_catchup_task`. This function captures a clone of
`ThreadSubscriptionCatchup` (why not…) which contains a `WeakClient`,
all good, no cycle here! However, the real task (not the function)
captures `Client` to call `enabled_thread_subscriptions` to know
if the function `thread_subscriptions_catchup_task` must be called.
Consequently, the task captures a clone of `Client`, boom, we have
a cycle.

This patch fixes the problem by spawning the task if and only if
`Client::enabled_thread_subscriptions` returns `Ok(true)`. This check is
done outside the task. It seems saner and avoid creating this cycle.

Consequently, the `ThreadSubscriptionCatchup::new` method becomes
`async`, which is perfectly fine as it was already wrapper inside an
`async` block when initialised by the `Client`.
@Hywan Hywan marked this pull request as ready for review May 19, 2026 14:25
@Hywan Hywan requested a review from a team as a code owner May 19, 2026 14:25
@Hywan Hywan requested review from stefanceriu and removed request for a team May 19, 2026 14:25
@Hywan Hywan force-pushed the fix-issue-6573 branch from 807622a to 6c65603 Compare May 19, 2026 14:35
@Hywan Hywan requested review from poljar and removed request for stefanceriu May 19, 2026 14:37
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 19, 2026

Merging this PR will not alter performance

✅ 50 untouched benchmarks


Comparing Hywan:fix-issue-6573 (a7cbf88) with main (b847983)

Open in CodSpeed

@codecov
Copy link
Copy Markdown

codecov Bot commented May 19, 2026

Codecov Report

❌ Patch coverage is 97.36842% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 89.95%. Comparing base (7677f09) to head (a7cbf88).
⚠️ Report is 10 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...ates/matrix-sdk/src/client/thread_subscriptions.rs 97.14% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6594      +/-   ##
==========================================
- Coverage   89.96%   89.95%   -0.01%     
==========================================
  Files         382      382              
  Lines      107797   107810      +13     
  Branches   107797   107810      +13     
==========================================
+ Hits        96979    96982       +3     
- Misses       7153     7160       +7     
- Partials     3665     3668       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Copy Markdown
Contributor

@poljar poljar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit annoying that we added a test-only field, though it seems to me that we can get rid of it.

Comment on lines +131 to +133

#[cfg(test)]
task_is_running: AtomicBool,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? Can't you use OnceCell::initialized() on the thread_subscription_catchup field for this?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using OnceLock, not OnceCell, which also has an initialized method, but it's private! I've changed for tsc._task.get().is_some() and it works nicely.

Thanks for noticing this!

Rebasing and merging.

@Hywan Hywan force-pushed the fix-issue-6573 branch from 6c65603 to a7cbf88 Compare May 22, 2026 09:08
@Hywan Hywan enabled auto-merge (rebase) May 22, 2026 09:11
@Hywan Hywan merged commit 1205c2b into matrix-org:main May 22, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Arc<ClientInner> reference cycle via ThreadSubscriptionCatchup prevents Client drop in matrix-sdk 0.17.0

2 participants