Skip to content

Commit 42f1583

Browse files
authored
bug: Avoid panic in Backtrace::capture if query_stack is already borrowed (#835)
1 parent 5b5e982 commit 42f1583

File tree

2 files changed

+34
-17
lines changed

2 files changed

+34
-17
lines changed

src/active_query.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ pub struct Backtrace(Box<[CapturedQuery]>);
377377
impl Backtrace {
378378
pub fn capture() -> Option<Self> {
379379
crate::with_attached_database(|db| {
380-
db.zalsa_local().with_query_stack(|stack| {
380+
db.zalsa_local().try_with_query_stack(|stack| {
381381
Backtrace(
382382
stack
383383
.iter()
@@ -392,7 +392,7 @@ impl Backtrace {
392392
.collect(),
393393
)
394394
})
395-
})
395+
})?
396396
}
397397
}
398398

src/zalsa_local.rs

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,32 @@ impl ZalsaLocal {
110110
}
111111
}
112112

113-
/// Executes a closure within the context of the current active query stacks.
113+
/// Executes a closure within the context of the current active query stacks (mutable).
114114
#[inline(always)]
115-
pub(crate) fn with_query_stack<R>(
115+
pub(crate) fn with_query_stack_mut<R>(
116116
&self,
117117
c: impl UnwindSafe + FnOnce(&mut QueryStack) -> R,
118118
) -> R {
119119
c(&mut self.query_stack.borrow_mut())
120120
}
121121

122+
#[inline(always)]
123+
pub(crate) fn with_query_stack<R>(&self, c: impl UnwindSafe + FnOnce(&QueryStack) -> R) -> R {
124+
c(&mut self.query_stack.borrow())
125+
}
126+
127+
#[inline(always)]
128+
pub(crate) fn try_with_query_stack<R>(
129+
&self,
130+
c: impl UnwindSafe + FnOnce(&QueryStack) -> R,
131+
) -> Option<R> {
132+
self.query_stack
133+
.try_borrow()
134+
.ok()
135+
.as_ref()
136+
.map(|stack| c(stack))
137+
}
138+
122139
/// Returns the index of the active query along with its *current* durability/changed-at
123140
/// information. As the query continues to execute, naturally, that information may change.
124141
pub(crate) fn active_query(&self) -> Option<(DatabaseKeyIndex, Stamp)> {
@@ -137,7 +154,7 @@ impl ZalsaLocal {
137154
index: IngredientIndex,
138155
value: A,
139156
) -> Result<(), ()> {
140-
self.with_query_stack(|stack| {
157+
self.with_query_stack_mut(|stack| {
141158
if let Some(top_query) = stack.last_mut() {
142159
top_query.accumulate(index, value);
143160
Ok(())
@@ -149,7 +166,7 @@ impl ZalsaLocal {
149166

150167
/// Add an output to the current query's list of dependencies
151168
pub(crate) fn add_output(&self, entity: DatabaseKeyIndex) {
152-
self.with_query_stack(|stack| {
169+
self.with_query_stack_mut(|stack| {
153170
if let Some(top_query) = stack.last_mut() {
154171
top_query.add_output(entity)
155172
}
@@ -158,7 +175,7 @@ impl ZalsaLocal {
158175

159176
/// Check whether `entity` is an output of the currently active query (if any)
160177
pub(crate) fn is_output_of_active_query(&self, entity: DatabaseKeyIndex) -> bool {
161-
self.with_query_stack(|stack| {
178+
self.with_query_stack_mut(|stack| {
162179
if let Some(top_query) = stack.last_mut() {
163180
top_query.is_output(entity)
164181
} else {
@@ -182,7 +199,7 @@ impl ZalsaLocal {
182199
"report_tracked_read(input={:?}, durability={:?}, changed_at={:?})",
183200
input, durability, changed_at
184201
);
185-
self.with_query_stack(|stack| {
202+
self.with_query_stack_mut(|stack| {
186203
if let Some(top_query) = stack.last_mut() {
187204
top_query.add_read(
188205
input,
@@ -208,7 +225,7 @@ impl ZalsaLocal {
208225
"report_tracked_read(input={:?}, durability={:?}, changed_at={:?})",
209226
input, durability, changed_at
210227
);
211-
self.with_query_stack(|stack| {
228+
self.with_query_stack_mut(|stack| {
212229
if let Some(top_query) = stack.last_mut() {
213230
top_query.add_read_simple(input, durability, changed_at);
214231
}
@@ -222,7 +239,7 @@ impl ZalsaLocal {
222239
/// * `current_revision`, the current revision
223240
#[inline(always)]
224241
pub(crate) fn report_untracked_read(&self, current_revision: Revision) {
225-
self.with_query_stack(|stack| {
242+
self.with_query_stack_mut(|stack| {
226243
if let Some(top_query) = stack.last_mut() {
227244
top_query.add_untracked_read(current_revision);
228245
}
@@ -234,7 +251,7 @@ impl ZalsaLocal {
234251
// FIXME: Use or remove this.
235252
#[allow(dead_code)]
236253
pub(crate) fn report_synthetic_read(&self, durability: Durability, revision: Revision) {
237-
self.with_query_stack(|stack| {
254+
self.with_query_stack_mut(|stack| {
238255
if let Some(top_query) = stack.last_mut() {
239256
top_query.add_synthetic_read(durability, revision);
240257
}
@@ -253,7 +270,7 @@ impl ZalsaLocal {
253270
/// * the disambiguator index
254271
#[track_caller]
255272
pub(crate) fn disambiguate(&self, key: IdentityHash) -> (Stamp, Disambiguator) {
256-
self.with_query_stack(|stack| {
273+
self.with_query_stack_mut(|stack| {
257274
let top_query = stack.last_mut().expect(
258275
"cannot create a tracked struct disambiguator outside of a tracked function",
259276
);
@@ -274,7 +291,7 @@ impl ZalsaLocal {
274291

275292
#[track_caller]
276293
pub(crate) fn store_tracked_struct_id(&self, identity: Identity, id: Id) {
277-
self.with_query_stack(|stack| {
294+
self.with_query_stack_mut(|stack| {
278295
let top_query = stack
279296
.last_mut()
280297
.expect("cannot store a tracked struct ID outside of a tracked function");
@@ -481,7 +498,7 @@ pub(crate) struct ActiveQueryGuard<'me> {
481498
impl ActiveQueryGuard<'_> {
482499
/// Initialize the tracked struct ids with the values from the prior execution.
483500
pub(crate) fn seed_tracked_struct_ids(&self, tracked_struct_ids: &IdentityMap) {
484-
self.local_state.with_query_stack(|stack| {
501+
self.local_state.with_query_stack_mut(|stack| {
485502
#[cfg(debug_assertions)]
486503
assert_eq!(stack.len(), self.push_len);
487504
let frame = stack.last_mut().unwrap();
@@ -495,7 +512,7 @@ impl ActiveQueryGuard<'_> {
495512
where
496513
I: IntoIterator<Item = DatabaseKeyIndex> + UnwindSafe,
497514
{
498-
self.local_state.with_query_stack(|stack| {
515+
self.local_state.with_query_stack_mut(|stack| {
499516
#[cfg(debug_assertions)]
500517
assert_eq!(stack.len(), self.push_len);
501518
let frame = stack.last_mut().unwrap();
@@ -508,7 +525,7 @@ impl ActiveQueryGuard<'_> {
508525

509526
/// Invoked when the query has successfully completed execution.
510527
fn complete(self) -> QueryRevisions {
511-
let query = self.local_state.with_query_stack(|stack| {
528+
let query = self.local_state.with_query_stack_mut(|stack| {
512529
stack.pop_into_revisions(
513530
self.database_key_index,
514531
#[cfg(debug_assertions)]
@@ -530,7 +547,7 @@ impl ActiveQueryGuard<'_> {
530547

531548
impl Drop for ActiveQueryGuard<'_> {
532549
fn drop(&mut self) {
533-
self.local_state.with_query_stack(|stack| {
550+
self.local_state.with_query_stack_mut(|stack| {
534551
stack.pop(
535552
self.database_key_index,
536553
#[cfg(debug_assertions)]

0 commit comments

Comments
 (0)