1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use super::*;
use crate::object::*;
use alloc::sync::{Arc, Weak};

/// Mutually signalable pair of events for concurrent programming
///
/// ## SYNOPSIS
///
/// Event Pairs are linked pairs of user-signalable objects. The 8 signal
/// bits reserved for userspace (`ZX_USER_SIGNAL_0` through
/// `ZX_USER_SIGNAL_7`) may be set or cleared on the local or opposing
/// endpoint of an Event Pair.
pub struct EventPair {
    base: KObjectBase,
    _counter: CountHelper,
    peer: Weak<EventPair>,
}

impl_kobject!(EventPair
    fn allowed_signals(&self) -> Signal {
        Signal::USER_ALL | Signal::SIGNALED
    }
    fn peer(&self) -> ZxResult<Arc<dyn KernelObject>> {
        let peer = self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)?;
        Ok(peer)
    }
    fn related_koid(&self) -> KoID {
        self.peer.upgrade().map(|p| p.id()).unwrap_or(0)
    }
);
define_count_helper!(EventPair);

impl EventPair {
    /// Create a pair of event.
    #[allow(unsafe_code)]
    pub fn create() -> (Arc<Self>, Arc<Self>) {
        let event0 = Arc::new(EventPair {
            base: KObjectBase::default(),
            _counter: CountHelper::new(),
            peer: Weak::default(),
        });
        let event1 = Arc::new(EventPair {
            base: KObjectBase::default(),
            _counter: CountHelper::new(),
            peer: Arc::downgrade(&event0),
        });
        // no other reference of `channel0`
        unsafe { &mut *(Arc::as_ptr(&event0) as *mut EventPair) }.peer = Arc::downgrade(&event1);
        (event0, event1)
    }

    /// Get the peer event.
    pub fn peer(&self) -> ZxResult<Arc<Self>> {
        self.peer.upgrade().ok_or(ZxError::PEER_CLOSED)
    }
}

impl Drop for EventPair {
    fn drop(&mut self) {
        if let Some(peer) = self.peer.upgrade() {
            peer.base.signal_set(Signal::PEER_CLOSED);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_allowed_signals() {
        let (event0, event1) = EventPair::create();
        assert!(Signal::verify_user_signal(
            event0.allowed_signals(),
            (Signal::USER_SIGNAL_5 | Signal::SIGNALED).bits().into()
        )
        .is_ok());
        assert_eq!(event0.allowed_signals(), event1.allowed_signals());

        event0.peer().unwrap();
    }

    #[test]
    fn peer_closed() {
        let (event0, event1) = EventPair::create();
        assert!(Arc::ptr_eq(&event0.peer().unwrap(), &event1));
        assert_eq!(event0.related_koid(), event1.id());

        drop(event1);
        assert_eq!(event0.signal(), Signal::PEER_CLOSED);
        assert_eq!(event0.peer().err(), Some(ZxError::PEER_CLOSED));
        assert_eq!(event0.related_koid(), 0);
    }
}