Skip to content

Commit 37c66f6

Browse files
committed
Rewrite BiLock lock/unlock to be allocation free
Inspired by a previous attempt to remove allocation (#606), this version uses three tokens to synchronize access to the locked value and the waker. These tokens are swapped between the inner struct of the lock and the bilock halves. The tokens are initalized with the LOCK token held by the inner struct, the WAKE token held by one bilock half, and the NULL token held by the other bilock half. To poll the lock, our half swaps its token with the inner token: if we get the LOCK token we now have the lock, if we get the NULL token we swap again and loop if we get the WAKE token we store our waker in the inner and swap again, if we then get the LOCK token we now have the lock, otherwise we return Poll::Pending To unlock the lock, our half swaps its token (which we know must be the LOCK token) with the inner token: if we get the NULL token, there is no contention so we return if we get the WAKE token, we wake the waker stored in the inner Additionally, this change makes the bilock methods require &mut self
1 parent 2be999d commit 37c66f6

File tree

7 files changed

+238
-110
lines changed

7 files changed

+238
-110
lines changed

futures-util/benches/bilock.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ mod bench {
1616
let mut ctx = noop_context();
1717

1818
b.iter(|| {
19-
let (x, y) = BiLock::new(1);
19+
let (mut x, mut y) = BiLock::new(1);
2020

2121
for _ in 0..1000 {
2222
let x_guard = match x.poll_lock(&mut ctx) {
@@ -48,7 +48,7 @@ mod bench {
4848
let mut ctx = noop_context();
4949

5050
b.iter(|| {
51-
let (x, y) = BiLock::new(1);
51+
let (mut x, mut y) = BiLock::new(1);
5252

5353
for _ in 0..1000 {
5454
let x_guard = match x.poll_lock(&mut ctx) {
@@ -74,7 +74,7 @@ mod bench {
7474
use std::thread;
7575

7676
b.iter(|| {
77-
let (x, y) = BiLock::new(false);
77+
let (mut x, mut y) = BiLock::new(false);
7878
const ITERATION_COUNT: usize = 1000;
7979

8080
let a = thread::spawn(move || {
@@ -85,7 +85,7 @@ mod bench {
8585
*guard = false;
8686
count += 1;
8787
}
88-
drop(guard);
88+
x = guard.unlock();
8989
}
9090
});
9191

@@ -97,7 +97,7 @@ mod bench {
9797
*guard = true;
9898
count += 1;
9999
}
100-
drop(guard);
100+
y = guard.unlock();
101101
}
102102
});
103103

futures-util/src/io/split.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub struct WriteHalf<T> {
1818
handle: BiLock<T>,
1919
}
2020

21-
fn lock_and_then<T, U, E, F>(lock: &BiLock<T>, cx: &mut Context<'_>, f: F) -> Poll<Result<U, E>>
21+
fn lock_and_then<T, U, E, F>(lock: &mut BiLock<T>, cx: &mut Context<'_>, f: F) -> Poll<Result<U, E>>
2222
where
2323
F: FnOnce(Pin<&mut T>, &mut Context<'_>) -> Poll<Result<U, E>>,
2424
{
@@ -53,45 +53,45 @@ impl<T: Unpin> WriteHalf<T> {
5353

5454
impl<R: AsyncRead> AsyncRead for ReadHalf<R> {
5555
fn poll_read(
56-
self: Pin<&mut Self>,
56+
mut self: Pin<&mut Self>,
5757
cx: &mut Context<'_>,
5858
buf: &mut [u8],
5959
) -> Poll<io::Result<usize>> {
60-
lock_and_then(&self.handle, cx, |l, cx| l.poll_read(cx, buf))
60+
lock_and_then(&mut self.handle, cx, |l, cx| l.poll_read(cx, buf))
6161
}
6262

6363
fn poll_read_vectored(
64-
self: Pin<&mut Self>,
64+
mut self: Pin<&mut Self>,
6565
cx: &mut Context<'_>,
6666
bufs: &mut [IoSliceMut<'_>],
6767
) -> Poll<io::Result<usize>> {
68-
lock_and_then(&self.handle, cx, |l, cx| l.poll_read_vectored(cx, bufs))
68+
lock_and_then(&mut self.handle, cx, |l, cx| l.poll_read_vectored(cx, bufs))
6969
}
7070
}
7171

7272
impl<W: AsyncWrite> AsyncWrite for WriteHalf<W> {
7373
fn poll_write(
74-
self: Pin<&mut Self>,
74+
mut self: Pin<&mut Self>,
7575
cx: &mut Context<'_>,
7676
buf: &[u8],
7777
) -> Poll<io::Result<usize>> {
78-
lock_and_then(&self.handle, cx, |l, cx| l.poll_write(cx, buf))
78+
lock_and_then(&mut self.handle, cx, |l, cx| l.poll_write(cx, buf))
7979
}
8080

8181
fn poll_write_vectored(
82-
self: Pin<&mut Self>,
82+
mut self: Pin<&mut Self>,
8383
cx: &mut Context<'_>,
8484
bufs: &[IoSlice<'_>],
8585
) -> Poll<io::Result<usize>> {
86-
lock_and_then(&self.handle, cx, |l, cx| l.poll_write_vectored(cx, bufs))
86+
lock_and_then(&mut self.handle, cx, |l, cx| l.poll_write_vectored(cx, bufs))
8787
}
8888

89-
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
90-
lock_and_then(&self.handle, cx, |l, cx| l.poll_flush(cx))
89+
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
90+
lock_and_then(&mut self.handle, cx, |l, cx| l.poll_flush(cx))
9191
}
9292

93-
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
94-
lock_and_then(&self.handle, cx, |l, cx| l.poll_close(cx))
93+
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
94+
lock_and_then(&mut self.handle, cx, |l, cx| l.poll_close(cx))
9595
}
9696
}
9797

0 commit comments

Comments
 (0)