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 
14 use std::io;
15 
16 use crate::{Selector, Token};
17 
18 macro_rules! cfg_linux {
19     ($($item:item)*) => {
20         $(
21             #[cfg(target_os = "linux")]
22             $item
23         )*
24     }
25 }
26 
27 macro_rules! cfg_macos {
28     ($($item:item)*) => {
29         $(
30             #[cfg(target_os = "macos")]
31             $item
32         )*
33     }
34 }
35 
36 cfg_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 
81 cfg_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