Skip to content

Commit 707fd53

Browse files
authored
Merge pull request async-rs#1056 from Enselic/prevent-lock-guard-races
Prevent races between dropping File LockGuard and waking its tasks
2 parents bf316b0 + d22585d commit 707fd53

File tree

1 file changed

+26
-7
lines changed

1 file changed

+26
-7
lines changed

src/fs/file.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -517,14 +517,16 @@ impl<T> Lock<T> {
517517
}
518518

519519
// The lock was successfully acquired.
520-
Poll::Ready(LockGuard(self.0.clone()))
520+
Poll::Ready(LockGuard(Some(self.0.clone())))
521521
}
522522
}
523523

524524
/// A lock guard.
525525
///
526526
/// When dropped, ownership of the inner value is returned back to the lock.
527-
struct LockGuard<T>(Arc<LockState<T>>);
527+
/// The inner value is always Some, except when the lock is dropped, where we
528+
/// set it to None. See comment in drop().
529+
struct LockGuard<T>(Option<Arc<LockState<T>>>);
528530

529531
unsafe impl<T: Send> Send for LockGuard<T> {}
530532
unsafe impl<T: Sync> Sync for LockGuard<T> {}
@@ -534,7 +536,7 @@ impl<T> LockGuard<T> {
534536
///
535537
/// When this lock guard gets dropped, all registered tasks will be woken up.
536538
fn register(&self, cx: &Context<'_>) {
537-
let mut list = self.0.wakers.lock().unwrap();
539+
let mut list = self.0.as_ref().unwrap().wakers.lock().unwrap();
538540

539541
if list.iter().all(|w| !w.will_wake(cx.waker())) {
540542
list.push(cx.waker().clone());
@@ -544,11 +546,22 @@ impl<T> LockGuard<T> {
544546

545547
impl<T> Drop for LockGuard<T> {
546548
fn drop(&mut self) {
549+
// Set the Option to None and take its value so we can drop the Arc
550+
// before we wake up the tasks.
551+
let lock = self.0.take().unwrap();
552+
553+
// Prepare to wake up all registered tasks interested in acquiring the lock.
554+
let wakers: Vec<_> = lock.wakers.lock().unwrap().drain(..).collect();
555+
547556
// Release the lock.
548-
self.0.locked.store(false, Ordering::Release);
557+
lock.locked.store(false, Ordering::Release);
558+
559+
// Drop the Arc _before_ waking up the tasks, to avoid races. See
560+
// reproducer and discussion in https://github.com/async-rs/async-std/issues/1001.
561+
drop(lock);
549562

550563
// Wake up all registered tasks interested in acquiring the lock.
551-
for w in self.0.wakers.lock().unwrap().drain(..) {
564+
for w in wakers {
552565
w.wake();
553566
}
554567
}
@@ -558,13 +571,19 @@ impl<T> Deref for LockGuard<T> {
558571
type Target = T;
559572

560573
fn deref(&self) -> &T {
561-
unsafe { &*self.0.value.get() }
574+
// SAFETY: Safe because the lock is held when this method is called. And
575+
// the inner value is always Some since it is only set to None in
576+
// drop().
577+
unsafe { &*self.0.as_ref().unwrap().value.get() }
562578
}
563579
}
564580

565581
impl<T> DerefMut for LockGuard<T> {
566582
fn deref_mut(&mut self) -> &mut T {
567-
unsafe { &mut *self.0.value.get() }
583+
// SAFETY: Safe because the lock is held when this method is called. And
584+
// the inner value is always Some since it is only set to None in
585+
// drop().
586+
unsafe { &mut *self.0.as_ref().unwrap().value.get() }
568587
}
569588
}
570589

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy