@@ -35,8 +35,18 @@ pub(crate) struct SyncState {
3535 /// waiting for this query to complete.
3636 anyone_waiting : bool ,
3737
38+ /// Whether any other query has transferred its lock ownership to this query.
39+ /// This is only an optimization so that the expensive unblocking of transferred queries
40+ /// can be skipped if `false`. This field might be `true` in cases where queries *were* transferred
41+ /// to this query, but have since then been transferred to another query (in a later iteration).
3842 is_transfer_target : bool ,
39- claimed_twice : bool ,
43+
44+ /// Whether this query has been claimed by the query that currently owns it.
45+ ///
46+ /// If `a` has been transferred to `b` and the stack for t1 is `b -> a`, then `a` can be claimed
47+ /// and `claimed_transferred` is set to `true`. However, t2 won't be able to claim `a` because
48+ /// it doesn't own `b`.
49+ claimed_transferred : bool ,
4050}
4151
4252impl SyncTable {
@@ -47,8 +57,12 @@ impl SyncTable {
4757 }
4858 }
4959
60+ /// Claims the given key index, or blocks if it is running on another thread.
61+ ///
62+ /// `REENTRANT` controls whether claiming a query whose ownership has been transferred to another query
63+ /// should result in a cycle (`false`) or can be claimed (`true`), if not already done so.
5064 #[ inline]
51- pub ( crate ) fn try_claim < ' me , const REENTRANT : bool > (
65+ pub ( crate ) fn try_claim < ' me , const TRANSFERRED : bool > (
5266 & ' me self ,
5367 zalsa : & ' me Zalsa ,
5468 key_index : Id ,
@@ -59,7 +73,8 @@ impl SyncTable {
5973 let id = match occupied_entry. get ( ) . id {
6074 SyncOwnerId :: Thread ( id) => id,
6175 SyncOwnerId :: Transferred => {
62- return match self . try_claim_transferred :: < REENTRANT > ( zalsa, occupied_entry)
76+ return match self
77+ . try_claim_transferred :: < TRANSFERRED > ( zalsa, occupied_entry)
6378 {
6479 Ok ( claimed) => claimed,
6580 Err ( other_thread) => match other_thread. block ( write) {
@@ -96,7 +111,7 @@ impl SyncTable {
96111 id : SyncOwnerId :: Thread ( thread:: current ( ) . id ( ) ) ,
97112 anyone_waiting : false ,
98113 is_transfer_target : false ,
99- claimed_twice : false ,
114+ claimed_transferred : false ,
100115 } ) ;
101116 ClaimResult :: Claimed ( ClaimGuard {
102117 key_index,
@@ -128,7 +143,9 @@ impl SyncTable {
128143 }
129144 ClaimTransferredResult :: Reentrant => {
130145 let SyncState {
131- id, claimed_twice, ..
146+ id,
147+ claimed_transferred : claimed_twice,
148+ ..
132149 } = entry. into_mut ( ) ;
133150 debug_assert ! ( !* claimed_twice) ;
134151
@@ -150,7 +167,7 @@ impl SyncTable {
150167 id : SyncOwnerId :: Thread ( thread:: current ( ) . id ( ) ) ,
151168 anyone_waiting : false ,
152169 is_transfer_target : false ,
153- claimed_twice : false ,
170+ claimed_transferred : false ,
154171 } ) ;
155172 Ok ( ClaimResult :: Claimed ( ClaimGuard {
156173 key_index,
@@ -175,10 +192,18 @@ impl SyncTable {
175192
176193#[ derive( Copy , Clone , Debug ) ]
177194pub ( crate ) enum SyncOwnerId {
178- /// Entry is owned by this thread
195+ /// Query is owned by this thread
179196 Thread ( thread:: ThreadId ) ,
180- /// Entry has been transferred and is owned by another thread.
181- /// The id is known by the `DependencyGraph`.
197+
198+ /// The query's lock ownership has been transferred to another query.
199+ /// E.g. if `a` transfers its ownership to `b`, then only the thread in the critical path
200+ /// to complete b` can claim `a` (in most instances, only the thread owning `b` can claim `a`).
201+ ///
202+ /// The thread owning `a` is stored in the `DependencyGraph`.
203+ ///
204+ /// A query can be marked as `Transferred` even if it has since then been released by the owning query.
205+ /// In that case, the query is effectively unclaimed and the `Transferred` state is stale. The reason
206+ /// for this is that it avoids the need for locking each sync table when releasing the transferred queries.
182207 Transferred ,
183208}
184209
@@ -220,7 +245,7 @@ impl<'me> ClaimGuard<'me> {
220245 let SyncState {
221246 anyone_waiting,
222247 is_transfer_target,
223- claimed_twice,
248+ claimed_transferred : claimed_twice,
224249 ..
225250 } = state;
226251
@@ -250,8 +275,8 @@ impl<'me> ClaimGuard<'me> {
250275 panic ! ( "key claimed twice?" ) ;
251276 } ;
252277
253- if state. get ( ) . claimed_twice {
254- state. get_mut ( ) . claimed_twice = false ;
278+ if state. get ( ) . claimed_transferred {
279+ state. get_mut ( ) . claimed_transferred = false ;
255280 state. get_mut ( ) . id = SyncOwnerId :: Transferred ;
256281 } else {
257282 self . release ( WaitResult :: Completed , state. remove ( ) ) ;
@@ -261,6 +286,7 @@ impl<'me> ClaimGuard<'me> {
261286 #[ cold]
262287 #[ inline( never) ]
263288 pub ( crate ) fn transfer ( & self , new_owner : DatabaseKeyIndex ) {
289+ tracing:: info!( "transfer" ) ;
264290 let self_key = self . database_key_index ( ) ;
265291
266292 let owner_ingredient = self . zalsa . lookup_ingredient ( new_owner. ingredient_index ( ) ) ;
@@ -283,7 +309,7 @@ impl<'me> ClaimGuard<'me> {
283309 let SyncState {
284310 anyone_waiting,
285311 id,
286- claimed_twice,
312+ claimed_transferred : claimed_twice,
287313 ..
288314 } = syncs. get_mut ( & self . key_index ) . expect ( "key claimed twice?" ) ;
289315
@@ -326,23 +352,28 @@ impl std::fmt::Debug for SyncTable {
326352 }
327353}
328354
355+ /// Controls how the lock is released when the `ClaimGuard` is dropped.
329356#[ derive( Copy , Clone , Debug , Default ) ]
330357pub ( crate ) enum ReleaseMode {
331358 /// The default release mode.
332359 ///
333- /// Releases the lock of the current query for claims that are not transferred. Queries who's ownership
334- /// were transferred to this query will be transitively unlocked.
335- ///
336- /// If this lock is owned by another query (because it was transferred), then releasing is a no-op.
360+ /// Releases the query for which this claim guard holds the lock and any queries that have
361+ /// transferred ownership to this query.
337362 #[ default]
338363 Default ,
339364
365+ /// Only releases the lock for this query. Any query that has transferred ownership to this query
366+ /// will remain locked.
367+ ///
368+ /// If this thread panics, the query will be released as normal (default mode).
340369 SelfOnly ,
341370
342371 /// Transfers the ownership of the lock to the specified query.
343372 ///
344- /// All waiting queries will be awakened so that they can retry and block on the new owner thread.
345- /// The new owner thread (or any thread it blocks on) will be able to acquire the lock (reentrant).
373+ /// The query will remain locked except the query that's currently blocking this query from completing
374+ /// (to avoid deadlocks).
375+ ///
376+ /// If this thread panics, the query will be released as normal (default mode).
346377 TransferTo ( DatabaseKeyIndex ) ,
347378}
348379
0 commit comments