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