Skip to content

Commit 707fd53

Browse files
authored
Merge pull request #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)