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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Compatibility wrappers for C-style event listeners.

use crate::core::work::task::TaskRef;
use crate::core::worker::Worker;
use crate::cshadow;
use crate::host::descriptor::FileState;
use crate::utility::HostTreePointer;

use super::host::Host;

/// An object that listens for status changes.
///
/// ~Deprecated: In new code consider using [`crate::host::descriptor::listener::StateEventSource`],
/// which supports Rust closures or directly takes [`crate::cshadow::StatusListener`].
#[derive(Debug)]
pub struct StatusListener {
    ptr: HostTreePointer<cshadow::StatusListener>,
}

impl Ord for StatusListener {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        Worker::with_active_host(|host| {
            // SAFETY: These pointers do not escape the `with_active_host` closure.
            let lhs = unsafe { self.ptr.ptr_with_host(host) };
            let rhs = unsafe { other.ptr.ptr_with_host(host) };

            match unsafe { cshadow::status_listener_compare(lhs.cast(), rhs.cast()) } {
                -1 => std::cmp::Ordering::Less,
                0 => std::cmp::Ordering::Equal,
                1 => std::cmp::Ordering::Greater,
                other => panic!("Unexpected comparison result: {other}"),
            }
        })
        .unwrap()
    }
}

impl PartialOrd for StatusListener {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Eq for StatusListener {}

impl PartialEq for StatusListener {
    fn eq(&self, other: &Self) -> bool {
        matches!(self.cmp(other), std::cmp::Ordering::Equal)
    }
}

impl StatusListener {
    /// Create an owned reference from `ptr`. Reference count is incremented on
    /// creation, and decremented on Drop.
    ///
    /// # Safety
    ///
    /// `ptr` must be safely dereferenceable.
    pub unsafe fn clone_from_c(host: &Host, ptr: *mut cshadow::StatusListener) -> Self {
        unsafe { cshadow::statuslistener_ref(ptr) };
        let ptr = HostTreePointer::new_for_host(host.id(), ptr);
        Self { ptr }
    }

    /// Create a [`StatusListener`] with the given notification function.
    pub fn new(host: &Host, notify: impl Fn(&Host) + Send + Sync + 'static) -> Self {
        extern "C-unwind" fn notify_fn(
            callback_object: *mut std::ffi::c_void,
            _callback_arg: *mut std::ffi::c_void,
        ) {
            let task: *const TaskRef = callback_object.cast();
            let task = unsafe { &*task };
            Worker::with_active_host(|host| task.execute(host)).unwrap()
        }
        extern "C-unwind" fn object_free_fn(callback_object: *mut std::ffi::c_void) {
            let task: *mut TaskRef = callback_object.cast();
            drop(unsafe { Box::from_raw(task) })
        }

        let callback_obj = Box::into_raw(Box::new(TaskRef::new(notify)));
        let status_listener = unsafe {
            cshadow::statuslistener_new(
                Some(notify_fn),
                callback_obj.cast::<std::ffi::c_void>(),
                Some(object_free_fn),
                std::ptr::null_mut(),
                None,
                host,
            )
        };
        Self {
            ptr: HostTreePointer::new_for_host(host.id(), status_listener),
        }
    }

    /// Called when a transition (bit flip) occurred on
    /// at least one of its status bits. (This function should only be called
    /// by status owners, i.e., the descriptor or futex base classes.)
    /// If this listener is monitoring (via setMonitorStatus) any of the status bits
    /// that just transitioned, then this function will trigger a notification
    /// via the callback supplied to the new func.
    pub fn handle_status_change(&self, host: &Host, current: FileState, transitions: FileState) {
        unsafe {
            cshadow::statuslistener_onStatusChanged(
                self.ptr.ptr_with_host(host),
                current,
                transitions,
            )
        }
    }

    /// Set the status bits that we should monitor for transitions (flips),
    /// and a filter that specifies which flips should cause the callback
    /// to be invoked.
    pub fn set_monitor_status(
        &self,
        host: &Host,
        status: FileState,
        filter: cshadow::StatusListenerFilter,
    ) {
        unsafe {
            cshadow::statuslistener_setMonitorStatus(self.ptr.ptr_with_host(host), status, filter)
        }
    }
}

impl Drop for StatusListener {
    fn drop(&mut self) {
        unsafe { cshadow::statuslistener_unref(self.ptr.ptr()) };
    }
}