Skip to content

Commit 00f2614

Browse files
committed
refactor: Keep edge condvar on stack instead of allocating it in an Arc
1 parent 9b51992 commit 00f2614

File tree

1 file changed

+46
-29
lines changed

1 file changed

+46
-29
lines changed

src/runtime/dependency_graph.rs

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use std::pin::Pin;
12
use std::thread::ThreadId;
23

34
use parking_lot::MutexGuard;
45
use rustc_hash::FxHashMap;
56
use smallvec::SmallVec;
67

78
use crate::key::DatabaseKeyIndex;
9+
use crate::runtime::dependency_graph::edge::EdgeCondvar;
810
use crate::runtime::WaitResult;
911

1012
#[derive(Debug, Default)]
@@ -61,7 +63,12 @@ impl DependencyGraph {
6163
to_id: ThreadId,
6264
query_mutex_guard: QueryMutexGuard,
6365
) -> WaitResult {
64-
let edge = me.add_edge(from_id, database_key, to_id);
66+
let cvar = std::pin::pin!(EdgeCondvar::default());
67+
let cvar = cvar.as_ref();
68+
// SAFETY: We are blocking until the result is removed from `DependencyGraph::wait_results`
69+
// at which point the `edge` won't signal the condvar anymore.
70+
// As such we are keeping the cond var alive until the reference in the edge drops.
71+
unsafe { me.add_edge(from_id, database_key, to_id, cvar) };
6572

6673
// Release the mutex that prevents `database_key`
6774
// from completing, now that the edge has been added.
@@ -72,29 +79,35 @@ impl DependencyGraph {
7279
debug_assert!(!me.edges.contains_key(&from_id));
7380
return result;
7481
}
75-
edge.wait(&mut me);
82+
cvar.wait(&mut me);
7683
}
7784
}
7885

7986
/// Helper for `block_on`: performs actual graph modification
8087
/// to add a dependency edge from `from_id` to `to_id`, which is
8188
/// computing `database_key`.
82-
fn add_edge(
89+
///
90+
/// # Safety
91+
///
92+
/// The caller needs to keep the referent of `cvar` alive until the corresponding
93+
/// [`Self::wait_results`] entry has been inserted.
94+
unsafe fn add_edge(
8395
&mut self,
8496
from_id: ThreadId,
8597
database_key: DatabaseKeyIndex,
8698
to_id: ThreadId,
87-
) -> edge::EdgeGuard {
99+
cvar: Pin<&EdgeCondvar>,
100+
) {
88101
assert_ne!(from_id, to_id);
89102
debug_assert!(!self.edges.contains_key(&from_id));
90103
debug_assert!(!self.depends_on(to_id, from_id));
91-
let (edge, guard) = edge::Edge::new(to_id);
104+
// SAFETY: The caller is responsible for ensuring that the `EdgeGuard` outlives the `Edge`.
105+
let edge = unsafe { edge::Edge::new(to_id, cvar) };
92106
self.edges.insert(from_id, edge);
93107
self.query_dependents
94108
.entry(database_key)
95109
.or_default()
96110
.push(from_id);
97-
guard
98111
}
99112

100113
/// Invoked when runtime `to_id` completes executing
@@ -128,45 +141,49 @@ impl DependencyGraph {
128141
}
129142

130143
mod edge {
131-
use std::sync::Arc;
132-
use std::thread::ThreadId;
144+
use std::{pin::Pin, thread::ThreadId};
145+
146+
use parking_lot::Condvar;
133147

134-
use parking_lot::MutexGuard;
148+
#[derive(Default, Debug)]
149+
pub(super) struct EdgeCondvar {
150+
condvar: Condvar,
151+
_phantom_pin: std::marker::PhantomPinned,
152+
}
135153

136-
use crate::runtime::dependency_graph::DependencyGraph;
154+
impl EdgeCondvar {
155+
#[inline]
156+
pub(super) fn wait<T: ?Sized>(&self, mutex_guard: &mut parking_lot::MutexGuard<'_, T>) {
157+
self.condvar.wait(mutex_guard)
158+
}
159+
}
137160

138161
#[derive(Debug)]
139162
pub(super) struct Edge {
140163
pub(super) blocked_on_id: ThreadId,
141164

142165
/// Signalled whenever a query with dependents completes.
143166
/// Allows those dependents to check if they are ready to unblock.
144-
condvar: Arc<parking_lot::Condvar>,
145-
}
146-
147-
pub struct EdgeGuard {
148-
condvar: Arc<parking_lot::Condvar>,
149-
}
150-
151-
impl EdgeGuard {
152-
pub fn wait(&self, mutex_guard: &mut MutexGuard<'_, DependencyGraph>) {
153-
self.condvar.wait(mutex_guard)
154-
}
167+
// condvar: unsafe<'stack_frame> Pin<&'stack_frame parking_lot::Condvar>,
168+
condvar: Pin<&'static EdgeCondvar>,
155169
}
156170

157171
impl Edge {
158-
pub(super) fn new(blocked_on_id: ThreadId) -> (Self, EdgeGuard) {
159-
let condvar = Arc::new(parking_lot::Condvar::new());
160-
let edge = Self {
172+
/// # SAFETY
173+
///
174+
/// The caller must ensure that the [`EdgeCondvar`] is kept alive until the [`Edge`] is dropped.
175+
pub(super) unsafe fn new(blocked_on_id: ThreadId, condvar: Pin<&EdgeCondvar>) -> Self {
176+
Self {
161177
blocked_on_id,
162-
condvar: condvar.clone(),
163-
};
164-
let edge_guard = EdgeGuard { condvar };
165-
(edge, edge_guard)
178+
condvar: unsafe {
179+
std::mem::transmute::<Pin<&EdgeCondvar>, Pin<&'static EdgeCondvar>>(condvar)
180+
},
181+
}
166182
}
167183

184+
#[inline]
168185
pub(super) fn notify(self) {
169-
self.condvar.notify_one();
186+
self.condvar.condvar.notify_one();
170187
}
171188
}
172189
}

0 commit comments

Comments
 (0)