Skip to content

Commit fb080c2

Browse files
author
Stjepan Glavina
committed
Merge crates into a single repo
1 parent 70de794 commit fb080c2

File tree

22 files changed

+2243
-1
lines changed

22 files changed

+2243
-1
lines changed

.github/workflows/build-and-test.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,4 @@ jobs:
4949
uses: actions-rs/cargo@v1
5050
with:
5151
command: test
52+
args: --all

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
/target
1+
target/
22
Cargo.lock

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,12 @@ async-barrier = "1.0.1"
1717
async-mutex = "1.3.0"
1818
async-rwlock = "1.1.0"
1919
async-semaphore = "1.1.0"
20+
21+
[workspace]
22+
members = [
23+
".",
24+
"async-barrier",
25+
"async-mutex",
26+
"async-rwlock",
27+
"async-semaphore",
28+
]

async-barrier/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "async-barrier"
3+
version = "1.0.1"
4+
authors = ["Stjepan Glavina <stjepang@gmail.com>"]
5+
edition = "2018"
6+
description = "An async barrier"
7+
license = "Apache-2.0 OR MIT"
8+
repository = "https://github.com/stjepang/async-lock"
9+
homepage = "https://github.com/stjepang/async-lock"
10+
documentation = "https://docs.rs/async-barrier"
11+
keywords = ["rendezvous", "sync", "async", "synchronize", "synchronization"]
12+
categories = ["asynchronous", "concurrency"]
13+
readme = "../README.md"
14+
15+
[dependencies]
16+
async-mutex = "1.1.5"
17+
event-listener = "2.4.0"
18+
19+
[dev-dependencies]
20+
async-channel = "1.4.1"
21+
futures-lite = "1.0.0"

async-barrier/src/lib.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
//! An async barrier.
2+
//!
3+
//! This crate is an async version of [`std::sync::Barrier`].
4+
5+
#![forbid(unsafe_code)]
6+
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
7+
8+
use async_mutex::Mutex;
9+
use event_listener::Event;
10+
11+
/// A counter to synchronize multiple tasks at the same time.
12+
#[derive(Debug)]
13+
pub struct Barrier {
14+
n: usize,
15+
state: Mutex<State>,
16+
event: Event,
17+
}
18+
19+
#[derive(Debug)]
20+
struct State {
21+
count: usize,
22+
generation_id: u64,
23+
}
24+
25+
impl Barrier {
26+
/// Creates a barrier that can block the given number of tasks.
27+
///
28+
/// A barrier will block `n`-1 tasks which call [`wait()`] and then wake up all tasks
29+
/// at once when the `n`th task calls [`wait()`].
30+
///
31+
/// [`wait()`]: `Barrier::wait()`
32+
///
33+
/// # Examples
34+
///
35+
/// ```
36+
/// use async_barrier::Barrier;
37+
///
38+
/// let barrier = Barrier::new(5);
39+
/// ```
40+
pub fn new(n: usize) -> Barrier {
41+
Barrier {
42+
n,
43+
state: Mutex::new(State {
44+
count: 0,
45+
generation_id: 0,
46+
}),
47+
event: Event::new(),
48+
}
49+
}
50+
51+
/// Blocks the current task until all tasks reach this point.
52+
///
53+
/// Barriers are reusable after all tasks have synchronized, and can be used continuously.
54+
///
55+
/// Returns a [`BarrierWaitResult`] indicating whether this task is the "leader", meaning the
56+
/// last task to call this method.
57+
///
58+
/// # Examples
59+
///
60+
/// ```
61+
/// use async_barrier::Barrier;
62+
/// use futures_lite::future;
63+
/// use std::sync::Arc;
64+
/// use std::thread;
65+
///
66+
/// let barrier = Arc::new(Barrier::new(5));
67+
///
68+
/// for _ in 0..5 {
69+
/// let b = barrier.clone();
70+
/// thread::spawn(move || {
71+
/// future::block_on(async {
72+
/// // The same messages will be printed together.
73+
/// // There will NOT be interleaving of "before" and "after".
74+
/// println!("before wait");
75+
/// b.wait().await;
76+
/// println!("after wait");
77+
/// });
78+
/// });
79+
/// }
80+
/// ```
81+
pub async fn wait(&self) -> BarrierWaitResult {
82+
let mut state = self.state.lock().await;
83+
let local_gen = state.generation_id;
84+
state.count += 1;
85+
86+
if state.count < self.n {
87+
while local_gen == state.generation_id && state.count < self.n {
88+
let listener = self.event.listen();
89+
drop(state);
90+
listener.await;
91+
state = self.state.lock().await;
92+
}
93+
BarrierWaitResult { is_leader: false }
94+
} else {
95+
state.count = 0;
96+
state.generation_id = state.generation_id.wrapping_add(1);
97+
self.event.notify(std::usize::MAX);
98+
BarrierWaitResult { is_leader: true }
99+
}
100+
}
101+
}
102+
103+
/// Returned by [`Barrier::wait()`] when all tasks have called it.
104+
///
105+
/// # Examples
106+
///
107+
/// ```
108+
/// # futures_lite::future::block_on(async {
109+
/// use async_barrier::Barrier;
110+
///
111+
/// let barrier = Barrier::new(1);
112+
/// let barrier_wait_result = barrier.wait().await;
113+
/// # });
114+
/// ```
115+
#[derive(Debug, Clone)]
116+
pub struct BarrierWaitResult {
117+
is_leader: bool,
118+
}
119+
120+
impl BarrierWaitResult {
121+
/// Returns `true` if this task was the last to call to [`Barrier::wait()`].
122+
///
123+
/// # Examples
124+
///
125+
/// ```
126+
/// # futures_lite::future::block_on(async {
127+
/// use async_barrier::Barrier;
128+
/// use futures_lite::future;
129+
///
130+
/// let barrier = Barrier::new(2);
131+
/// let (a, b) = future::zip(barrier.wait(), barrier.wait()).await;
132+
/// assert_eq!(a.is_leader(), false);
133+
/// assert_eq!(b.is_leader(), true);
134+
/// # });
135+
/// ```
136+
pub fn is_leader(&self) -> bool {
137+
self.is_leader
138+
}
139+
}

async-barrier/tests/smoke.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::sync::Arc;
2+
use std::thread;
3+
4+
use async_barrier::Barrier;
5+
use futures_lite::future;
6+
7+
#[test]
8+
fn smoke() {
9+
future::block_on(async move {
10+
const N: usize = 10;
11+
12+
let barrier = Arc::new(Barrier::new(N));
13+
14+
for _ in 0..10 {
15+
let (tx, rx) = async_channel::unbounded();
16+
17+
for _ in 0..N - 1 {
18+
let c = barrier.clone();
19+
let tx = tx.clone();
20+
21+
thread::spawn(move || {
22+
future::block_on(async move {
23+
let res = c.wait().await;
24+
tx.send(res.is_leader()).await.unwrap();
25+
})
26+
});
27+
}
28+
29+
// At this point, all spawned threads should be blocked,
30+
// so we shouldn't get anything from the cahnnel.
31+
let res = rx.try_recv();
32+
assert!(match res {
33+
Err(_err) => true,
34+
_ => false,
35+
});
36+
37+
let mut leader_found = barrier.wait().await.is_leader();
38+
39+
// Now, the barrier is cleared and we should get data.
40+
for _ in 0..N - 1 {
41+
if rx.recv().await.unwrap() {
42+
assert!(!leader_found);
43+
leader_found = true;
44+
}
45+
}
46+
assert!(leader_found);
47+
}
48+
});
49+
}

async-mutex/Cargo.toml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[package]
2+
name = "async-mutex"
3+
version = "1.3.0"
4+
authors = ["Stjepan Glavina <stjepang@gmail.com>"]
5+
edition = "2018"
6+
description = "Async mutex"
7+
license = "Apache-2.0 OR MIT"
8+
repository = "https://github.com/stjepang/async-lock"
9+
homepage = "https://github.com/stjepang/async-lock"
10+
documentation = "https://docs.rs/async-mutex"
11+
keywords = ["asynchronous", "mutex", "lock", "synchronization"]
12+
categories = ["asynchronous", "concurrency"]
13+
readme = "../README.md"
14+
15+
[dependencies]
16+
event-listener = "2.0.0"
17+
18+
[dev-dependencies]
19+
async-std = "1.6.2"
20+
futures = "0.3.5"
21+
futures-intrusive = "0.3.1"
22+
futures-lite = "1.0.0"
23+
smol = "0.1.18"
24+
tokio = { version = "0.2.21", features = ["sync", "parking_lot"] }

async-mutex/benches/async-mutex.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![feature(test)]
2+
3+
extern crate test;
4+
5+
use std::sync::Arc;
6+
7+
use async_mutex::Mutex;
8+
use async_std::task;
9+
use test::Bencher;
10+
11+
#[bench]
12+
fn create(b: &mut Bencher) {
13+
b.iter(|| Mutex::new(()));
14+
}
15+
16+
#[bench]
17+
fn contention(b: &mut Bencher) {
18+
b.iter(|| task::block_on(run(10, 1000)));
19+
}
20+
21+
#[bench]
22+
fn no_contention(b: &mut Bencher) {
23+
b.iter(|| task::block_on(run(1, 10000)));
24+
}
25+
26+
async fn run(task: usize, iter: usize) {
27+
let m = Arc::new(Mutex::new(()));
28+
let mut tasks = Vec::new();
29+
30+
for _ in 0..task {
31+
let m = m.clone();
32+
tasks.push(task::spawn(async move {
33+
for _ in 0..iter {
34+
let _ = m.lock().await;
35+
}
36+
}));
37+
}
38+
39+
for t in tasks {
40+
t.await;
41+
}
42+
}

async-mutex/benches/async-std.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![feature(test)]
2+
3+
extern crate test;
4+
5+
use std::sync::Arc;
6+
7+
use async_std::sync::Mutex;
8+
use async_std::task;
9+
use test::Bencher;
10+
11+
#[bench]
12+
fn create(b: &mut Bencher) {
13+
b.iter(|| Mutex::new(()));
14+
}
15+
16+
#[bench]
17+
fn contention(b: &mut Bencher) {
18+
b.iter(|| task::block_on(run(10, 1000)));
19+
}
20+
21+
#[bench]
22+
fn no_contention(b: &mut Bencher) {
23+
b.iter(|| task::block_on(run(1, 10000)));
24+
}
25+
26+
async fn run(task: usize, iter: usize) {
27+
let m = Arc::new(Mutex::new(()));
28+
let mut tasks = Vec::new();
29+
30+
for _ in 0..task {
31+
let m = m.clone();
32+
tasks.push(task::spawn(async move {
33+
for _ in 0..iter {
34+
let _ = m.lock().await;
35+
}
36+
}));
37+
}
38+
39+
for t in tasks {
40+
t.await;
41+
}
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![feature(test)]
2+
3+
extern crate test;
4+
5+
use std::sync::Arc;
6+
7+
use async_std::task;
8+
use futures_intrusive::sync::Mutex;
9+
use test::Bencher;
10+
11+
#[bench]
12+
fn create(b: &mut Bencher) {
13+
b.iter(|| Mutex::new((), true));
14+
}
15+
16+
#[bench]
17+
fn contention(b: &mut Bencher) {
18+
b.iter(|| task::block_on(run(10, 1000)));
19+
}
20+
21+
#[bench]
22+
fn no_contention(b: &mut Bencher) {
23+
b.iter(|| task::block_on(run(1, 10000)));
24+
}
25+
26+
async fn run(task: usize, iter: usize) {
27+
let m = Arc::new(Mutex::new((), true));
28+
let mut tasks = Vec::new();
29+
30+
for _ in 0..task {
31+
let m = m.clone();
32+
tasks.push(task::spawn(async move {
33+
for _ in 0..iter {
34+
let _ = m.lock().await;
35+
}
36+
}));
37+
}
38+
39+
for t in tasks {
40+
t.await;
41+
}
42+
}

0 commit comments

Comments
 (0)