1+ use hyper:: body:: Buf ;
2+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
3+ use std:: sync:: Arc ;
4+ use tokio:: sync:: Notify ;
5+
6+ #[ derive( Clone ) ]
7+ pub struct EosSignaler {
8+ notifier : Arc < Notify > ,
9+ }
10+
11+ impl EosSignaler {
12+ fn notify_eos ( & self ) {
13+ self . notifier . notify_waiters ( ) ;
14+ }
15+
16+ pub async fn wait_till_eos ( self ) {
17+ self . notifier . notified ( ) . await ;
18+ }
19+ }
20+
21+ pub struct AlertOnEos < B > {
22+ inner : B ,
23+ signaler : EosSignaler ,
24+ // I'd rather we consumed the signaler, but it would require something like AtomicOption,
25+ // arc_swap::ArcSwapOption was tried, but it only returns an Arc, and the value cannot be consumed (swapped).
26+ // One could write an AtomicOption type (like this https://docs.rs/atomic-option/0.1.2/atomic_option/),
27+ // but it requires both unsafe and additional heap allocation, which is not worth it.
28+ has_already_signaled : AtomicBool ,
29+ }
30+
31+ impl < B > AlertOnEos < B > {
32+ pub fn new ( inner : B ) -> ( Self , EosSignaler ) {
33+ let signal = EosSignaler {
34+ notifier : Arc :: new ( Notify :: new ( ) ) ,
35+ } ;
36+ let this = Self {
37+ inner,
38+ signaler : signal. clone ( ) ,
39+ has_already_signaled : AtomicBool :: new ( false ) ,
40+ } ;
41+ ( this, signal)
42+ }
43+ }
44+
45+ impl < B : Buf > Buf for AlertOnEos < B > {
46+ fn remaining ( & self ) -> usize {
47+ self . inner . remaining ( )
48+ }
49+
50+ fn chunk ( & self ) -> & [ u8 ] {
51+ self . inner . chunk ( )
52+ }
53+
54+ fn advance ( & mut self , cnt : usize ) {
55+ self . inner . advance ( cnt) ;
56+ if !self . inner . has_remaining ( ) && !self . has_already_signaled . swap ( true , Ordering :: AcqRel ) {
57+ self . signaler . notify_eos ( ) ;
58+ }
59+ }
60+ }
61+
62+ #[ cfg( test) ]
63+ mod tests {
64+
65+ }
0 commit comments