Skip to content

fix(store): take the resource table and stores locks in one order - #3746

Open
963s wants to merge 4 commits into
tauri-apps:v2from
963s:fix/store-lock-order
Open

963s wants to merge 4 commits into
tauri-apps:v2from
963s:fix/store-lock-order

Conversation

@963s

@963s 963s commented Oct 29, 2026 •

Copy link
Copy Markdown

load and close on the same store at the same time deadlock
close (resources|close) holds the resource table then takes stores.write()
load (build_inner) holds stores.write() for the whole build then locks the table
get_store and the exit save loop use that order too
close is a sync command so the main thread hangs and the app freezes for good
not the same as #3394 or #3570

fix
build_inner looks up with a short read lock, builds with no lock held, then locks table first and stores second like close does
if another load of the same path won it returns that store so still one store per path
if a close lands between the lookup and the table get it builds a fresh one instead of failing with BadResourceId
get_store and the exit loop copy the rid out before touching the table

repro without an app

cargo test -p tauri-plugin-store --test load_close_race

hangs on v2 and v3 till its 30s timeout, ~0.05s here

real app on tauri 2.12.5 + store 2.5.0 from crates.io, page only calls load and close
before hung 5 of 8 runs, after 8 of 8 fine
hung run stack: main thread in Store::close waiting on the rwlock, tokio worker in build_inner waiting on the table mutex
a react strict mode or hmr remount can hit this from js alone, maybe #3361

behavior otherwise same (same rid on reload, createNew, parallel loads get one rid)
clippy fmt tests pass, macos only

@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Package Changes Through dfe58b1

There are 4 changes which include opener with patch, opener-js with patch, store with patch, store-js with patch

Planned Package Versions

The following package releases are the planned based on the context of changes in this pull request.

package current next
api-example 2.0.57 2.0.58
api-example-js 2.0.53 2.0.54
opener 2.7.2 2.7.3
opener-js 2.7.2 2.7.3
store 2.5.0 2.5.1
store-js 2.5.0 2.5.1

Add another change file through the GitHub UI by following this link.


Read about change files or the docs at github.com/jbolda/covector

@Legend-Master

Copy link
Copy Markdown
Contributor

Could you provide a minimal reproducible example that we could test on?

@963s

963s commented Nov 3, 2026

Copy link
Copy Markdown
Author

load holds the stores lock and waits for the resource table
close does the reverse so overlapping calls deadlock
from js close runs on the main thread so the window just freezes
strict mode or hmr remount can trigger it
btw could be what #3361 hits

repro hangs on v2 after ~30s and passes here

cargo test -p tauri-plugin-store --test load_close_race

@Legend-Master

Legend-Master commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

I see, thanks for the repro! This is quite tricky that we can't lock the resource table and state together 🤔 I'll take a closer look at the PR.

Edit: I see, the locking order must always stay the same and both of them must always be locked together

@Legend-Master Legend-Master left a comment

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.

Really nice catch, thanks! I have a few questions and we can get this merged.

Comment thread plugins/store/src/lib.rs Resolved
Comment thread plugins/store/src/lib.rs Resolved
if self.create_new {
if let Some(rid) = stores.remove(&self.path) {
let _ = self.app.resources_table().take::<Store<R>>(rid);
if !self.create_new {

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 there any reason we did this check twice? here and below

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants