Skip to content

Commit 9a764c2

Browse files
authored
Merge branch 'main' into emlautarom1/expbackoff
2 parents 2b5528d + 1416f9b commit 9a764c2

9 files changed

Lines changed: 2324 additions & 27 deletions

File tree

Cargo.lock

Lines changed: 48 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,23 @@ publish = false
2424

2525
[workspace.dependencies]
2626
axum = "0.8.6"
27+
blst = "0.3.13"
28+
cancellation = "0.1.0"
2729
chrono = { version = "0.4", features = ["serde"] }
30+
crossbeam = "0.8.4"
2831
hex = { version = "^0.4.3" }
32+
prost = "0.14"
33+
prost-build = "0.14"
34+
prost-types = "0.14"
35+
rand = { version = "0.8", features = ["std_rng"] }
36+
rand_core = "0.6"
2937
serde = { version = "1.0", features = ["derive"] }
3038
serde_json = { version = "^1.0" }
39+
thiserror = "2.0.12"
3140
tokio = { version = "1", features = ["full"] }
3241
tower = { version = "0.5.2", features = ["full"] }
3342
libp2p = { version = "0.56", features = ["full", "secp256k1"] }
34-
prost = "0.14"
35-
prost-types = "0.14"
36-
prost-build = "0.14"
37-
blst = "0.3.13"
38-
rand_core = "0.6"
39-
thiserror = "2.0.12"
40-
rand = {version = "0.8", features = ["std_rng"]}
41-
uuid = {version = "1.16", features = ["serde", "v4"] }
43+
uuid = { version = "1.16", features = ["serde", "v4"] }
4244
serde_with = { version = "3", features = ["hex", "base64"] }
4345
base64 = { version = "0.22.1" }
4446
sha3 = { version = "0.10.8" }

crates/charon-core/Cargo.toml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,19 @@ license.workspace = true
77
publish.workspace = true
88

99
[dependencies]
10+
cancellation.workspace = true
11+
chrono.workspace = true
12+
crossbeam.workspace = true
13+
hex.workspace = true
1014
serde.workspace = true
1115
serde_json.workspace = true
16+
thiserror.workspace = true
17+
libp2p.workspace = true
18+
prost.workspace = true
19+
prost-types.workspace = true
20+
21+
[dev-dependencies]
22+
rand.workspace = true
1223
libp2p.workspace = true
1324
prost.workspace = true
1425
prost-types.workspace = true
@@ -19,4 +30,4 @@ chrono.workspace = true
1930
charon-build-proto.workspace = true
2031

2132
[lints]
22-
workspace = true
33+
workspace = true

crates/charon-core/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! This crate provides the fundamental building blocks, data structures, and
55
//! core algorithms used throughout the Charon system.
66
7+
pub mod qbft;
78
/// Types for the Charon core.
89
pub mod types;
910

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use crossbeam::channel as mpmc;
2+
use std::{
3+
collections::HashMap,
4+
sync::{Arc, Mutex},
5+
thread,
6+
time::{Duration, Instant},
7+
};
8+
9+
#[derive(Clone)]
10+
pub struct FakeClock {
11+
inner: Arc<Mutex<FakeClockInner>>,
12+
}
13+
14+
struct FakeClockInner {
15+
start: Instant,
16+
now: Instant,
17+
last_id: usize,
18+
clients: HashMap<usize, (mpmc::Sender<Instant>, Instant)>,
19+
}
20+
21+
impl FakeClock {
22+
pub fn new(now: Instant) -> Self {
23+
Self {
24+
inner: Arc::new(Mutex::new(FakeClockInner {
25+
start: now,
26+
now,
27+
last_id: 1,
28+
clients: Default::default(),
29+
})),
30+
}
31+
}
32+
33+
pub fn new_timer(
34+
&self,
35+
duration: Duration,
36+
) -> (
37+
mpmc::Receiver<Instant>,
38+
Box<dyn Fn() + Send + Sync + 'static>,
39+
) {
40+
let (tx, rx) = mpmc::bounded::<Instant>(1);
41+
42+
let client_id = {
43+
let mut inner = self.inner.lock().unwrap();
44+
let id = inner.last_id;
45+
let deadline = inner.now + duration;
46+
47+
inner.last_id += 1;
48+
inner.clients.insert(id, (tx, deadline));
49+
50+
id
51+
};
52+
53+
let inner = Arc::clone(&self.inner);
54+
let cancel = Box::new(move || {
55+
let mut inner = inner.lock().unwrap();
56+
inner.clients.remove(&client_id);
57+
});
58+
59+
(rx, cancel)
60+
}
61+
62+
pub fn advance(&self, duration: Duration) {
63+
// Advance time and collect expired senders under lock, but perform sends
64+
// without holding lock.
65+
let mut expired = vec![];
66+
67+
let now = {
68+
let mut inner = self.inner.lock().unwrap();
69+
inner.now += duration;
70+
let now = inner.now;
71+
72+
for (&id, (ch, deadline)) in inner.clients.iter() {
73+
if *deadline <= now {
74+
expired.push((id, ch.clone()));
75+
}
76+
}
77+
78+
for (id, _) in expired.iter() {
79+
inner.clients.remove(id);
80+
}
81+
82+
now
83+
};
84+
85+
for (_, ch) in expired {
86+
let _ = ch.send(now);
87+
}
88+
}
89+
90+
pub fn elapsed(&self) -> Duration {
91+
let inner = self.inner.lock().unwrap();
92+
inner.now - inner.start
93+
}
94+
95+
pub fn cancel(&self) {
96+
let mut inner = self.inner.lock().unwrap();
97+
inner.clients.clear();
98+
}
99+
}
100+
101+
impl Drop for FakeClock {
102+
fn drop(&mut self) {
103+
self.cancel();
104+
}
105+
}
106+
107+
#[test]
108+
fn multiple_threads_timers() {
109+
let clock = FakeClock::new(Instant::now());
110+
111+
let start = Instant::now();
112+
thread::scope(|s| {
113+
let c1 = clock.clone();
114+
let (ch_1, _) = c1.new_timer(Duration::from_secs(5));
115+
s.spawn(move || {
116+
let _ = ch_1.recv();
117+
});
118+
119+
let c2 = clock.clone();
120+
let (ch_2, _) = c2.new_timer(Duration::from_secs(5));
121+
s.spawn(move || {
122+
let _ = ch_2.recv();
123+
});
124+
125+
clock.advance(Duration::from_secs(6));
126+
});
127+
128+
println!("start={:?}, clock={:?}", start.elapsed(), clock.elapsed());
129+
}
130+
131+
#[test]
132+
fn multiple_threads_cancellation() {
133+
let clock = FakeClock::new(Instant::now());
134+
135+
let start = Instant::now();
136+
thread::scope(|s| {
137+
let c1 = clock.clone();
138+
let (ch_1, _) = c1.new_timer(Duration::from_secs(5));
139+
s.spawn(move || {
140+
let _ = ch_1.recv();
141+
});
142+
143+
let c2 = clock.clone();
144+
let (ch_2, _) = c2.new_timer(Duration::from_secs(5));
145+
s.spawn(move || {
146+
let _ = ch_2.recv();
147+
});
148+
149+
clock.cancel();
150+
});
151+
152+
println!("start={:?}, clock={:?}", start.elapsed(), clock.elapsed());
153+
}

0 commit comments

Comments
 (0)