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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// Library for concurrent I/O resource management using reactor pattern.
//
// SPDX-License-Identifier: Apache-2.0
//
// Written in 2021-2023 by
//     Dr. Maxim Orlovsky <orlovsky@ubideco.org>
//     Alexis Sellier <alexis@cloudhead.io>
//
// Copyright 2022-2023 UBIDECO Institute, Switzerland
// Copyright 2021 Alexis Sellier <alexis@cloudhead.io>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! OS and implementation-specific poll engines.

#[cfg(feature = "popol")]
pub mod popol;

use std::fmt::{self, Display, Formatter};
use std::os::unix::io::{AsRawFd, RawFd};
use std::time::Duration;
use std::{io, ops};

use crate::resource::Io;

/// Information about I/O events which has happened for a resource.
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub struct IoType {
    /// Specifies whether I/O source has data to read.
    pub read: bool,
    /// Specifies whether I/O source is ready for write operations.
    pub write: bool,
}

impl IoType {
    /// Indicates no I/O operations are tracked.
    pub fn none() -> Self {
        Self {
            read: false,
            write: false,
        }
    }

    /// Indicates interest in only read I/O events.
    pub fn read_only() -> Self {
        Self {
            read: true,
            write: false,
        }
    }

    /// Indicates interest in only write I/O events.
    pub fn write_only() -> Self {
        Self {
            read: false,
            write: true,
        }
    }

    /// Indicates interest in both read and write I/O events.
    pub fn read_write() -> Self {
        Self {
            read: true,
            write: true,
        }
    }

    /// Indicates no I/O operations has happened on a resource.
    pub fn is_none(self) -> bool { !self.read && !self.write }
    /// Indicates data available to be read from a resource.
    pub fn is_read_only(self) -> bool { self.read && !self.write }
    /// Indicates that the resource is ready to accept data.
    pub fn is_write_only(self) -> bool { !self.read && self.write }
    /// Indicates that the resource can accept data - and has aa data which can be read.
    pub fn is_read_write(self) -> bool { self.read && self.write }
}

impl ops::Not for IoType {
    type Output = Self;

    fn not(self) -> Self::Output {
        Self {
            read: !self.read,
            write: !self.write,
        }
    }
}

impl Iterator for IoType {
    type Item = Io;

    fn next(&mut self) -> Option<Self::Item> {
        if self.write {
            self.write = false;
            Some(Io::Write)
        } else if self.read {
            self.read = false;
            Some(Io::Read)
        } else {
            None
        }
    }
}

impl Display for IoType {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        if self.is_none() {
            f.write_str("none")
        } else if self.is_read_write() {
            f.write_str("read-write")
        } else if self.read {
            f.write_str("read")
        } else if self.write {
            f.write_str("write")
        } else {
            unreachable!()
        }
    }
}

/// Reasons for the poll operation failure for a specific resource.
#[derive(Copy, Clone, Debug, Display, Error)]
#[display(doc_comments)]
pub enum IoFail {
    /// connection is absent (POSIX events {0:#b})
    Connectivity(i16),
    /// OS-level error (POSIX events {0:#b})
    Os(i16),
}

/// An engine providing `poll` syscall interface to the [`crate::Reactor`].
///
/// Since `poll` syscalls are platform-dependent and multiple crates can expose it with a different
/// API and tradeoffs, the current library allows selection of a specific poll engine
/// implementation.
///
/// To read I/O events from the engine please use its Iterator interface.
pub trait Poll
where
    Self: Send + Iterator<Item = (RawFd, Result<IoType, IoFail>)>,
    for<'a> &'a mut Self: Iterator<Item = (RawFd, Result<IoType, IoFail>)>,
{
    /// Waker type used by the poll provider.
    type Waker: Waker;

    /// Registers a file-descriptor based resource for a poll.
    fn register(&mut self, fd: &impl AsRawFd, interest: IoType);
    /// Unregisters a file-descriptor based resource from a poll.
    fn unregister(&mut self, fd: &impl AsRawFd);
    /// Subscribes for a specific set of events for a given file descriptor-backed resource (see
    /// [`IoType`] for the details on event subscription).
    fn set_interest(&mut self, fd: &impl AsRawFd, interest: IoType) -> bool;

    /// Runs single poll syscall over all registered resources with an optional timeout.
    ///
    /// # Returns
    ///
    /// Number of generated events.
    fn poll(&mut self, timeout: Option<Duration>) -> io::Result<usize>;
}

/// Waker object provided by the poller.
pub trait Waker {
    /// Data type for sending wake signals to the poller.
    type Send: WakerSend;
    /// Data type for receiving wake signals inside the poller.
    type Recv: WakerRecv;

    /// Constructs pair of waker receiver and sender objects.
    fn pair() -> Result<(Self::Send, Self::Recv), io::Error>;
}

/// Sending part of the waker.
pub trait WakerSend: Send + Sync + Clone {
    /// Awakes the poller to read events.
    fn wake(&self) -> io::Result<()>;
}

/// Receiver part of the waker.
pub trait WakerRecv: AsRawFd + Send + io::Read {
    /// Resets the waker reader.
    fn reset(&self);
}