Skip to content

Commit 7b5f662

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 f13f34a commit 7b5f662

7 files changed

Lines changed: 240 additions & 112 deletions

File tree

futures-util/benches/bilock.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fn contended(b: &mut Bencher) {
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 @@ fn lock_unlock(b: &mut Bencher) {
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 @@ fn concurrent(b: &mut Bencher) {
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 @@ fn concurrent(b: &mut Bencher) {
8585
*guard = false;
8686
count += 1;
8787
}
88-
drop(guard);
88+
x = guard.unlock();
8989
}
9090
});
9191

@@ -97,7 +97,7 @@ fn concurrent(b: &mut Bencher) {
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
@@ -19,7 +19,7 @@ pub struct WriteHalf<T> {
1919
}
2020

2121
fn lock_and_then<T, U, E, F>(
22-
lock: &BiLock<T>,
22+
lock: &mut BiLock<T>,
2323
cx: &mut Context<'_>,
2424
f: F
2525
) -> Poll<Result<U, E>>
@@ -55,38 +55,38 @@ impl<T: Unpin> WriteHalf<T> {
5555
}
5656

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

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

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

78-
fn poll_write_vectored(self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>])
78+
fn poll_write_vectored(mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>])
7979
-> Poll<io::Result<usize>>
8080
{
81-
lock_and_then(&self.handle, cx, |l, cx| l.poll_write_vectored(cx, bufs))
81+
lock_and_then(&mut self.handle, cx, |l, cx| l.poll_write_vectored(cx, bufs))
8282
}
8383

84-
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
85-
lock_and_then(&self.handle, cx, |l, cx| l.poll_flush(cx))
84+
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
85+
lock_and_then(&mut self.handle, cx, |l, cx| l.poll_flush(cx))
8686
}
8787

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

0 commit comments

Comments
 (0)