1// Copyright (c) 2023 Huawei Device Co., Ltd.
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6//     http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use std::io;
15
16use crate::{Selector, Token};
17
18macro_rules! cfg_linux {
19    ($($item:item)*) => {
20        $(
21            #[cfg(target_os = "linux")]
22            $item
23        )*
24    }
25}
26
27macro_rules! cfg_macos {
28    ($($item:item)*) => {
29        $(
30            #[cfg(target_os = "macos")]
31            $item
32        )*
33    }
34}
35
36cfg_linux!(
37    use std::fs::File;
38    use std::io::{Read, Write};
39    use std::os::unix::io::FromRawFd;
40    use crate::Interest;
41
42    /// In Linux, `eventfd` is used to implement asynchronous wake-up. It is a
43    /// 64-bit counter. A fixed 8-byte (64-bit) unsigned integer is written to
44    /// ensure wake-up reliability.
45    #[derive(Debug)]
46    pub(crate) struct WakerInner {
47        fd: File,
48    }
49
50    impl WakerInner {
51        pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<WakerInner> {
52            let fd = unsafe { libc::eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) };
53            let file = unsafe { File::from_raw_fd(fd) };
54            if fd == -1 {
55                let err = io::Error::last_os_error();
56                Err(err)
57            } else {
58                selector
59                    .register(fd, token, Interest::READABLE)
60                    .map(|()| WakerInner { fd: file })
61            }
62        }
63
64        pub(crate) fn wake(&self) -> io::Result<()> {
65            let buf: [u8; 8] = 1u64.to_ne_bytes();
66            match (&self.fd).write(&buf) {
67                Ok(_) => Ok(()),
68                Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
69                    let mut buf: [u8; 8] = 0u64.to_ne_bytes();
70                    match (&self.fd).read(&mut buf) {
71                        Err(err) if err.kind() != io::ErrorKind::WouldBlock => Err(err),
72                        _ => self.wake(),
73                    }
74                }
75                Err(err) => Err(err),
76            }
77        }
78    }
79);
80
81cfg_macos!(
82    /// In MacOs, kqueue with `EVFILT_USER` is used to implement asynchronous wake-up.
83    #[derive(Debug)]
84    pub(crate) struct WakerInner {
85        selector: Selector,
86        token: Token,
87    }
88
89    impl WakerInner {
90        pub(crate) fn new(selector: &Selector, token: Token) -> io::Result<WakerInner> {
91            let selector = selector.try_clone()?;
92            selector.register_waker(token)?;
93            Ok(WakerInner { selector, token })
94        }
95
96        pub(crate) fn wake(&self) -> io::Result<()> {
97            self.selector.wake(self.token)
98        }
99    }
100);
101