1use behavior::{
4 Actions, Address, Behavior, BehaviorActed, BehaviorBase, Never, NoBirths, Protocol,
5 SendEffects, User,
6};
7#[cfg(test)]
8use behavior::{Delivery, Recipient};
9use thiserror::Error;
10
11use crate::DeliveryRoute;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct BarrierGeneration(pub u64);
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub struct BarrierReleased {
20 pub generation: BarrierGeneration,
22}
23
24pub struct BarrierMessage<K, Route> {
26 pub generation: BarrierGeneration,
28 pub participant: K,
30 pub reply_to: Route,
32}
33
34pub struct BarrierArrival<K, Route> {
36 pub participant: K,
38 pub reply_to: Route,
40}
41
42pub enum BarrierState<K, Route> {
44 Gathering {
46 generation: BarrierGeneration,
48 arrivals: Vec<BarrierArrival<K, Route>>,
50 },
51 Exhausted {
53 generation: BarrierGeneration,
55 },
56}
57
58#[derive(Debug, Error, Clone, PartialEq, Eq)]
60pub enum BarrierConfigError<K> {
61 #[error("barrier membership must not be empty")]
63 EmptyMembership,
64 #[error("barrier membership contains a duplicate participant")]
66 DuplicateParticipant(K),
67}
68
69pub struct BarrierMembership<K> {
71 members: Vec<K>,
72}
73
74impl<K: Clone + Eq> BarrierMembership<K> {
75 pub fn new(members: Vec<K>) -> Result<Self, BarrierConfigError<K>> {
81 if members.is_empty() {
82 return Err(BarrierConfigError::EmptyMembership);
83 }
84 for (index, participant) in members.iter().enumerate() {
85 if members[..index].contains(participant) {
86 return Err(BarrierConfigError::DuplicateParticipant(
87 participant.clone(),
88 ));
89 }
90 }
91 Ok(Self { members })
92 }
93}
94
95#[derive(Debug, Error, Clone, PartialEq, Eq)]
97pub enum BarrierError<K, Route> {
98 #[error("barrier participant is unknown")]
100 UnknownParticipant { participant: K, reply_to: Route },
101 #[error("barrier participant already arrived in this generation")]
103 DuplicateArrival {
104 participant: K,
106 generation: BarrierGeneration,
108 reply_to: Route,
110 },
111 #[error("barrier arrival is stale")]
113 StaleGeneration {
114 participant: K,
116 observed: BarrierGeneration,
118 current: BarrierGeneration,
120 reply_to: Route,
122 },
123 #[error("barrier arrival is for a future generation")]
125 FutureGeneration {
126 participant: K,
128 observed: BarrierGeneration,
130 current: BarrierGeneration,
132 reply_to: Route,
134 },
135 #[error("barrier generations are exhausted")]
137 Exhausted {
138 participant: K,
140 generation: BarrierGeneration,
142 reply_to: Route,
144 },
145}
146
147pub struct Barrier<A, K, Route>
160where
161 A: Address,
162 Route: DeliveryRoute,
163 Route::Protocol: Protocol<Addr = A, Msg = BarrierReleased>,
164{
165 members: Vec<K>,
166 state: BarrierState<K, Route>,
167 marker: core::marker::PhantomData<fn() -> A>,
168}
169
170impl<A, K, Route> Barrier<A, K, Route>
171where
172 A: Address,
173 K: Clone + Eq,
174 Route: DeliveryRoute,
175 Route::Protocol: Protocol<Addr = A, Msg = BarrierReleased>,
176{
177 #[must_use]
179 pub fn new(membership: BarrierMembership<K>) -> Self {
180 Self {
181 members: membership.members,
182 state: BarrierState::Gathering {
183 generation: BarrierGeneration(0),
184 arrivals: Vec::new(),
185 },
186 marker: core::marker::PhantomData,
187 }
188 }
189
190 #[must_use]
192 pub fn members(&self) -> &[K] {
193 &self.members
194 }
195
196 #[must_use]
198 pub const fn state(&self) -> &BarrierState<K, Route> {
199 &self.state
200 }
201}
202
203impl<A, K, Route> BehaviorBase for Barrier<A, K, Route>
204where
205 A: Address,
206 Route: DeliveryRoute,
207 Route::Protocol: Protocol<Addr = A, Msg = BarrierReleased>,
208{
209 type Base = Self;
210
211 fn base(&self) -> &Self {
212 self
213 }
214}
215
216impl<A, K, Route> behavior::Protocol for Barrier<A, K, Route>
217where
218 A: Address,
219 Route: DeliveryRoute,
220 Route::Protocol: Protocol<Addr = A, Msg = BarrierReleased>,
221{
222 type Addr = A;
223 type Msg = BarrierMessage<K, Route>;
224}
225
226impl<A, K, Route> Behavior for Barrier<A, K, Route>
227where
228 A: Address,
229 K: Clone + Eq,
230 Route: DeliveryRoute,
231 Route::Protocol: Protocol<Addr = A, Msg = BarrierReleased>,
232 Route::Sends: behavior::SendsFor<User<A, BarrierMessage<K, Route>>>,
233{
234 type Protocol = Self;
235 type Event = User<A, behavior::BehaviorMessage<Self>>;
236 type Sends = Route::Sends;
237 type Ph = Never;
238 type Error = BarrierError<K, Route>;
239 type Birth = NoBirths;
240
241 fn transition(&mut self, _: behavior::ActiveTurn, event: Self::Event) -> BehaviorActed<Self> {
242 let BarrierMessage {
243 generation: observed,
244 participant,
245 reply_to,
246 } = event.message;
247 if !self.members.contains(&participant) {
248 return Err(BarrierError::UnknownParticipant {
249 participant,
250 reply_to,
251 });
252 }
253 let (current, arrivals) = match &mut self.state {
254 BarrierState::Gathering {
255 generation,
256 arrivals,
257 } => (generation, arrivals),
258 BarrierState::Exhausted { generation } => {
259 return Err(BarrierError::Exhausted {
260 participant,
261 generation: *generation,
262 reply_to,
263 });
264 }
265 };
266 if observed < *current {
267 return Err(BarrierError::StaleGeneration {
268 participant,
269 observed,
270 current: *current,
271 reply_to,
272 });
273 }
274 if observed > *current {
275 return Err(BarrierError::FutureGeneration {
276 participant,
277 observed,
278 current: *current,
279 reply_to,
280 });
281 }
282 if arrivals
283 .iter()
284 .any(|arrival| arrival.participant == participant)
285 {
286 return Err(BarrierError::DuplicateArrival {
287 participant,
288 generation: *current,
289 reply_to,
290 });
291 }
292 arrivals.push(BarrierArrival {
293 participant,
294 reply_to,
295 });
296 if arrivals.len() < self.members.len() {
297 return Ok(Actions::cont());
298 }
299 let generation = *current;
300 let completed = core::mem::take(arrivals);
301 self.state =
302 generation
303 .0
304 .checked_add(1)
305 .map_or(BarrierState::Exhausted { generation }, |next| {
306 BarrierState::Gathering {
307 generation: BarrierGeneration(next),
308 arrivals: Vec::with_capacity(self.members.len()),
309 }
310 });
311 let mut sends = Route::Sends::empty();
312 for arrival in completed {
313 sends.append(arrival.reply_to.deliver(BarrierReleased { generation }));
314 }
315 Ok(Actions::send(sends))
316 }
317}
318
319#[cfg(test)]
320mod tests {
321 use super::*;
322 use crate::Activate as _;
323 use behavior::MailAddr;
324
325 #[test]
326 fn configuration_rejects_empty_and_duplicate_membership() {
327 assert!(matches!(
328 BarrierMembership::new(Vec::<u8>::new()),
329 Err(BarrierConfigError::EmptyMembership)
330 ));
331 assert!(matches!(
332 BarrierMembership::new(vec![1, 2, 1]),
333 Err(BarrierConfigError::DuplicateParticipant(1))
334 ));
335 }
336
337 #[test]
338 fn generation_releases_exact_membership_in_arrival_order() {
339 let one = Recipient::from(MailAddr(1));
340 let two = Recipient::from(MailAddr(2));
341 let mut barrier = Barrier::new(BarrierMembership::new(vec![1_u8, 2]).unwrap())
342 .initialize()
343 .unwrap()
344 .behavior;
345 let first = barrier
346 .receive(
347 MailAddr(9),
348 BarrierMessage {
349 generation: BarrierGeneration(0),
350 participant: 2,
351 reply_to: two,
352 },
353 )
354 .unwrap();
355 assert!(first.sends.is_empty());
356 let rejection = barrier.receive(
357 MailAddr(9),
358 BarrierMessage {
359 generation: BarrierGeneration(0),
360 participant: 2,
361 reply_to: two,
362 },
363 );
364 assert!(matches!(
365 rejection,
366 Err(BarrierError::DuplicateArrival {
367 participant: 2,
368 generation: BarrierGeneration(0),
369 reply_to,
370 }) if reply_to == two
371 ));
372 let released = barrier
373 .receive(
374 MailAddr(9),
375 BarrierMessage {
376 generation: BarrierGeneration(0),
377 participant: 1,
378 reply_to: one,
379 },
380 )
381 .unwrap();
382 assert!(
383 released.sends
384 == vec![
385 Delivery::new(
386 two,
387 BarrierReleased {
388 generation: BarrierGeneration(0),
389 },
390 ),
391 Delivery::new(
392 one,
393 BarrierReleased {
394 generation: BarrierGeneration(0),
395 },
396 ),
397 ]
398 );
399 assert!(matches!(
400 barrier.state(),
401 BarrierState::Gathering {
402 generation: BarrierGeneration(1),
403 arrivals,
404 } if arrivals.is_empty()
405 ));
406 let rejection = barrier.receive(
407 MailAddr(9),
408 BarrierMessage {
409 generation: BarrierGeneration(0),
410 participant: 1,
411 reply_to: one,
412 },
413 );
414 assert!(matches!(
415 rejection,
416 Err(BarrierError::StaleGeneration {
417 current: BarrierGeneration(1),
418 reply_to,
419 ..
420 }) if reply_to == one
421 ));
422 }
423
424 #[test]
425 fn final_generation_releases_then_exhausts_without_wraparound() {
426 let participant = Recipient::from(MailAddr(1));
427 let mut definition = Barrier::new(BarrierMembership::new(vec![1_u8]).unwrap());
428 definition.state = BarrierState::Gathering {
429 generation: BarrierGeneration(u64::MAX),
430 arrivals: Vec::new(),
431 };
432 let mut barrier = (definition).initialize().unwrap().behavior;
433 let released = barrier
434 .receive(
435 MailAddr(9),
436 BarrierMessage {
437 generation: BarrierGeneration(u64::MAX),
438 participant: 1,
439 reply_to: participant,
440 },
441 )
442 .unwrap();
443 assert_eq!(released.sends.len(), 1);
444 assert!(matches!(
445 barrier.state(),
446 BarrierState::Exhausted {
447 generation: BarrierGeneration(u64::MAX),
448 }
449 ));
450 }
451}