1cac7dca0Sopenharmony_ci// Copyright (c) 2023 Huawei Device Co., Ltd.
2cac7dca0Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
3cac7dca0Sopenharmony_ci// you may not use this file except in compliance with the License.
4cac7dca0Sopenharmony_ci// You may obtain a copy of the License at
5cac7dca0Sopenharmony_ci//
6cac7dca0Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
7cac7dca0Sopenharmony_ci//
8cac7dca0Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
9cac7dca0Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
10cac7dca0Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11cac7dca0Sopenharmony_ci// See the License for the specific language governing permissions and
12cac7dca0Sopenharmony_ci// limitations under the License.
13cac7dca0Sopenharmony_ci
14cac7dca0Sopenharmony_ciuse std::io;
15cac7dca0Sopenharmony_ciuse std::os::raw::c_int;
16cac7dca0Sopenharmony_ciuse std::sync::atomic::{AtomicUsize, Ordering};
17cac7dca0Sopenharmony_ciuse std::time::Duration;
18cac7dca0Sopenharmony_ci
19cac7dca0Sopenharmony_ciuse crate::{EventTrait, Interest, Token};
20cac7dca0Sopenharmony_ci
21cac7dca0Sopenharmony_cistatic NEXT_ID: AtomicUsize = AtomicUsize::new(1);
22cac7dca0Sopenharmony_ci
23cac7dca0Sopenharmony_ci/// An wrapper for different OS polling system.
24cac7dca0Sopenharmony_ci/// Linux: epoll
25cac7dca0Sopenharmony_ci/// Windows: iocp
26cac7dca0Sopenharmony_ci/// macos: kqueue
27cac7dca0Sopenharmony_cipub struct Selector {
28cac7dca0Sopenharmony_ci    // selector id
29cac7dca0Sopenharmony_ci    id: usize,
30cac7dca0Sopenharmony_ci    // epoll fd
31cac7dca0Sopenharmony_ci    ep: i32,
32cac7dca0Sopenharmony_ci}
33cac7dca0Sopenharmony_ci
34cac7dca0Sopenharmony_ciimpl Selector {
35cac7dca0Sopenharmony_ci    /// Creates a new Selector.
36cac7dca0Sopenharmony_ci    ///
37cac7dca0Sopenharmony_ci    /// # Error
38cac7dca0Sopenharmony_ci    /// If the underlying syscall fails, returns the corresponding error.
39cac7dca0Sopenharmony_ci    pub fn new() -> io::Result<Selector> {
40cac7dca0Sopenharmony_ci        let ep = match syscall!(epoll_create1(libc::EPOLL_CLOEXEC)) {
41cac7dca0Sopenharmony_ci            Ok(ep_sys) => ep_sys,
42cac7dca0Sopenharmony_ci            Err(err) => {
43cac7dca0Sopenharmony_ci                return Err(err);
44cac7dca0Sopenharmony_ci            }
45cac7dca0Sopenharmony_ci        };
46cac7dca0Sopenharmony_ci
47cac7dca0Sopenharmony_ci        Ok(Selector {
48cac7dca0Sopenharmony_ci            id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
49cac7dca0Sopenharmony_ci            ep,
50cac7dca0Sopenharmony_ci        })
51cac7dca0Sopenharmony_ci    }
52cac7dca0Sopenharmony_ci
53cac7dca0Sopenharmony_ci    /// Waits for io events to come within a time limit.
54cac7dca0Sopenharmony_ci    pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
55cac7dca0Sopenharmony_ci        // Convert to milliseconds, if input time is none, it means the timeout is -1
56cac7dca0Sopenharmony_ci        // and wait permanently.
57cac7dca0Sopenharmony_ci        let timeout = timeout.map(|time| time.as_millis() as c_int).unwrap_or(-1);
58cac7dca0Sopenharmony_ci
59cac7dca0Sopenharmony_ci        events.clear();
60cac7dca0Sopenharmony_ci
61cac7dca0Sopenharmony_ci        match syscall!(epoll_wait(
62cac7dca0Sopenharmony_ci            self.ep,
63cac7dca0Sopenharmony_ci            events.as_mut_ptr(),
64cac7dca0Sopenharmony_ci            events.capacity() as i32,
65cac7dca0Sopenharmony_ci            timeout
66cac7dca0Sopenharmony_ci        )) {
67cac7dca0Sopenharmony_ci            Ok(n_events) => unsafe { events.set_len(n_events as usize) },
68cac7dca0Sopenharmony_ci            Err(err) => {
69cac7dca0Sopenharmony_ci                return Err(err);
70cac7dca0Sopenharmony_ci            }
71cac7dca0Sopenharmony_ci        }
72cac7dca0Sopenharmony_ci        Ok(())
73cac7dca0Sopenharmony_ci    }
74cac7dca0Sopenharmony_ci
75cac7dca0Sopenharmony_ci    /// Registers the fd with specific interested events
76cac7dca0Sopenharmony_ci    pub fn register(&self, fd: i32, token: Token, interests: Interest) -> io::Result<()> {
77cac7dca0Sopenharmony_ci        let mut sys_event = libc::epoll_event {
78cac7dca0Sopenharmony_ci            events: interests.into_io_event(),
79cac7dca0Sopenharmony_ci            u64: usize::from(token) as u64,
80cac7dca0Sopenharmony_ci        };
81cac7dca0Sopenharmony_ci
82cac7dca0Sopenharmony_ci        match syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_ADD, fd, &mut sys_event)) {
83cac7dca0Sopenharmony_ci            Ok(_) => Ok(()),
84cac7dca0Sopenharmony_ci            Err(err) => Err(err),
85cac7dca0Sopenharmony_ci        }
86cac7dca0Sopenharmony_ci    }
87cac7dca0Sopenharmony_ci
88cac7dca0Sopenharmony_ci    /// Re-registers the fd with specific interested events
89cac7dca0Sopenharmony_ci    pub fn reregister(&self, fd: i32, token: Token, interests: Interest) -> io::Result<()> {
90cac7dca0Sopenharmony_ci        let mut sys_event = libc::epoll_event {
91cac7dca0Sopenharmony_ci            events: interests.into_io_event(),
92cac7dca0Sopenharmony_ci            u64: usize::from(token) as u64,
93cac7dca0Sopenharmony_ci        };
94cac7dca0Sopenharmony_ci
95cac7dca0Sopenharmony_ci        match syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_MOD, fd, &mut sys_event)) {
96cac7dca0Sopenharmony_ci            Ok(_) => Ok(()),
97cac7dca0Sopenharmony_ci            Err(err) => Err(err),
98cac7dca0Sopenharmony_ci        }
99cac7dca0Sopenharmony_ci    }
100cac7dca0Sopenharmony_ci
101cac7dca0Sopenharmony_ci    /// De-registers the fd.
102cac7dca0Sopenharmony_ci    pub fn deregister(&self, fd: i32) -> io::Result<()> {
103cac7dca0Sopenharmony_ci        match syscall!(epoll_ctl(
104cac7dca0Sopenharmony_ci            self.ep,
105cac7dca0Sopenharmony_ci            libc::EPOLL_CTL_DEL,
106cac7dca0Sopenharmony_ci            fd,
107cac7dca0Sopenharmony_ci            std::ptr::null_mut()
108cac7dca0Sopenharmony_ci        )) {
109cac7dca0Sopenharmony_ci            Ok(_) => Ok(()),
110cac7dca0Sopenharmony_ci            Err(err) => Err(err),
111cac7dca0Sopenharmony_ci        }
112cac7dca0Sopenharmony_ci    }
113cac7dca0Sopenharmony_ci}
114cac7dca0Sopenharmony_ci
115cac7dca0Sopenharmony_ciimpl Drop for Selector {
116cac7dca0Sopenharmony_ci    fn drop(&mut self) {
117cac7dca0Sopenharmony_ci        if let Err(_err) = syscall!(close(self.ep)) {
118cac7dca0Sopenharmony_ci            // todo: log the error
119cac7dca0Sopenharmony_ci        }
120cac7dca0Sopenharmony_ci    }
121cac7dca0Sopenharmony_ci}
122cac7dca0Sopenharmony_ci
123cac7dca0Sopenharmony_ciimpl std::fmt::Debug for Selector {
124cac7dca0Sopenharmony_ci    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125cac7dca0Sopenharmony_ci        write!(fmt, "epoll fd: {}, Select id: {}", self.ep, self.id)
126cac7dca0Sopenharmony_ci    }
127cac7dca0Sopenharmony_ci}
128cac7dca0Sopenharmony_ci
129cac7dca0Sopenharmony_ci/// A vector of events
130cac7dca0Sopenharmony_cipub type Events = Vec<Event>;
131cac7dca0Sopenharmony_ci
132cac7dca0Sopenharmony_ci/// An io event
133cac7dca0Sopenharmony_cipub type Event = libc::epoll_event;
134cac7dca0Sopenharmony_ci
135cac7dca0Sopenharmony_ciimpl EventTrait for Event {
136cac7dca0Sopenharmony_ci    fn token(&self) -> Token {
137cac7dca0Sopenharmony_ci        Token(self.u64 as usize)
138cac7dca0Sopenharmony_ci    }
139cac7dca0Sopenharmony_ci
140cac7dca0Sopenharmony_ci    fn is_readable(&self) -> bool {
141cac7dca0Sopenharmony_ci        (self.events as libc::c_int & libc::EPOLLIN) != 0
142cac7dca0Sopenharmony_ci            || (self.events as libc::c_int & libc::EPOLLPRI) != 0
143cac7dca0Sopenharmony_ci    }
144cac7dca0Sopenharmony_ci
145cac7dca0Sopenharmony_ci    fn is_writable(&self) -> bool {
146cac7dca0Sopenharmony_ci        (self.events as libc::c_int & libc::EPOLLOUT) != 0
147cac7dca0Sopenharmony_ci    }
148cac7dca0Sopenharmony_ci
149cac7dca0Sopenharmony_ci    fn is_read_closed(&self) -> bool {
150cac7dca0Sopenharmony_ci        self.events as libc::c_int & libc::EPOLLHUP != 0
151cac7dca0Sopenharmony_ci            || (self.events as libc::c_int & libc::EPOLLIN != 0
152cac7dca0Sopenharmony_ci                && self.events as libc::c_int & libc::EPOLLRDHUP != 0)
153cac7dca0Sopenharmony_ci    }
154cac7dca0Sopenharmony_ci
155cac7dca0Sopenharmony_ci    fn is_write_closed(&self) -> bool {
156cac7dca0Sopenharmony_ci        self.events as libc::c_int & libc::EPOLLHUP != 0
157cac7dca0Sopenharmony_ci            || (self.events as libc::c_int & libc::EPOLLOUT != 0
158cac7dca0Sopenharmony_ci                && self.events as libc::c_int & libc::EPOLLERR != 0)
159cac7dca0Sopenharmony_ci            || self.events as libc::c_int == libc::EPOLLERR
160cac7dca0Sopenharmony_ci    }
161cac7dca0Sopenharmony_ci
162cac7dca0Sopenharmony_ci    fn is_error(&self) -> bool {
163cac7dca0Sopenharmony_ci        (self.events as libc::c_int & libc::EPOLLERR) != 0
164cac7dca0Sopenharmony_ci    }
165cac7dca0Sopenharmony_ci}
166cac7dca0Sopenharmony_ci
167cac7dca0Sopenharmony_ci#[cfg(test)]
168cac7dca0Sopenharmony_cimod test {
169cac7dca0Sopenharmony_ci    use crate::sys::socket;
170cac7dca0Sopenharmony_ci    use crate::{Event, EventTrait, Interest, Selector, Token};
171cac7dca0Sopenharmony_ci
172cac7dca0Sopenharmony_ci    /// UT cases for `Selector::reregister`.
173cac7dca0Sopenharmony_ci    ///
174cac7dca0Sopenharmony_ci    /// # Brief
175cac7dca0Sopenharmony_ci    /// 1. Create a Selector
176cac7dca0Sopenharmony_ci    /// 2. Reregister the selector
177cac7dca0Sopenharmony_ci    #[test]
178cac7dca0Sopenharmony_ci    fn ut_epoll_reregister() {
179cac7dca0Sopenharmony_ci        let selector = Selector::new().unwrap();
180cac7dca0Sopenharmony_ci        let sock = socket::socket_new(libc::AF_UNIX, libc::SOCK_STREAM).unwrap();
181cac7dca0Sopenharmony_ci        let ret = selector.register(sock, Token::from_usize(0), Interest::READABLE);
182cac7dca0Sopenharmony_ci        assert!(ret.is_ok());
183cac7dca0Sopenharmony_ci        let ret = selector.reregister(sock, Token::from_usize(0), Interest::WRITABLE);
184cac7dca0Sopenharmony_ci        assert!(ret.is_ok());
185cac7dca0Sopenharmony_ci    }
186cac7dca0Sopenharmony_ci
187cac7dca0Sopenharmony_ci    /// UT case for `Event::is_error`
188cac7dca0Sopenharmony_ci    ///
189cac7dca0Sopenharmony_ci    /// # Brief
190cac7dca0Sopenharmony_ci    /// 1. Create an event from libc::EPOLLERR
191cac7dca0Sopenharmony_ci    /// 2. Check if it's an error
192cac7dca0Sopenharmony_ci    #[test]
193cac7dca0Sopenharmony_ci    fn ut_event_is_err() {
194cac7dca0Sopenharmony_ci        let event = Event {
195cac7dca0Sopenharmony_ci            events: libc::EPOLLERR as u32,
196cac7dca0Sopenharmony_ci            u64: 0,
197cac7dca0Sopenharmony_ci        };
198cac7dca0Sopenharmony_ci        assert!(event.is_error());
199cac7dca0Sopenharmony_ci    }
200cac7dca0Sopenharmony_ci}
201