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
//!Cross-platform wrapper over select.
//!
//!This library provides simple interface over POSIX's `select` enabling you to write
//!very simple async programs using `std` networking primitives.
//!
//!But if you want performance you should look for `tokio` or `mio`

mod sys;

use sys::FdSet;
pub use sys::AsRawFd;

use std::io;
use core::time;

pub use sys::FD_LIMIT;

///Represents successful select call.
///
///It contains number of fd ready.
///And also set of these ids, allowing to query which fd is ready.
pub struct SelectResult {
    count: usize,
    read: FdSet,
    write: FdSet,
    except: FdSet,
}

impl SelectResult {
    #[inline]
    ///Returns total number of file descriptors ready.
    pub const fn len(&self) -> usize {
        self.count
    }

    #[inline]
    ///Returns whether specified `source` is read ready.
    pub fn is_read<T: AsRawFd>(&self, source: &T) -> bool {
        self.read.is_present(source)
    }

    #[inline]
    ///Returns whether specified `source` is write ready.
    pub fn is_write<T: AsRawFd>(&self, source: &T) -> bool {
        self.write.is_present(source)
    }

    #[inline]
    ///Returns whether specified `source` is except ready.
    pub fn is_except<T: AsRawFd>(&self, source: &T) -> bool {
        self.except.is_present(source)
    }

}

#[derive(Clone)]
///Select abstraction allowing you to monitor specified sockets.
///
///Selector itself represents set of file descriptors to monitor, as such it is possible
///to copy it.
///Normally select modifies list of file descriptors, but `Selector` always copies existing list,
///avoiding these modifications.
///
///## Performance recommendations
///
///Generally limited to 64 file descriptors, but you should only use select when you have ~10 fds
///otherwise modern API like `kqueue` or `epoll` would yield much better performance.
///
///- Call select only when getting would block from syscall;
///- Limit number of selects, allowing it to accumulate events;
pub struct Selector {
    read: FdSet,
    write: FdSet,
    except: FdSet,
}

impl Selector {
    #[inline]
    ///Creates new instance with no sockets to monitor.
    pub fn new() -> Self {
        Self {
            read: FdSet::new(),
            write: FdSet::new(),
            except: FdSet::new(),
        }
    }

    #[inline]
    ///Adds `source` to monitor for read ops.
    ///
    ///Panics when goes over `FD_LIMIT`
    pub fn add_read<T: AsRawFd>(&mut self, source: &T) {
        assert!(self.read.len() < sys::FD_LIMIT);
        self.read.add(source);
    }

    #[inline]
    ///Adds `source` to monitor for write ops.
    ///
    ///Panics when goes over `FD_LIMIT`
    pub fn add_write<T: AsRawFd>(&mut self, source: &T) {
        assert!(self.write.len() < sys::FD_LIMIT);
        self.write.add(source);
    }

    #[inline]
    ///Adds `source` to monitor for exceptional ops.
    ///
    ///Panics when goes over `FD_LIMIT`
    pub fn add_except<T: AsRawFd>(&mut self, source: &T) {
        assert!(self.except.len() < sys::FD_LIMIT);
        self.except.add(source);
    }


    #[inline]
    ///Removes all fds from read monitoring
    pub fn clear_read(&mut self) {
        self.read.clear();
    }

    #[inline]
    ///Removes all fds from write monitoring
    pub fn clear_write(&mut self) {
        self.write.clear();
    }

    #[inline]
    ///Removes all fds from except monitoring
    pub fn clear_except(&mut self) {
        self.except.clear();
    }


    #[inline]
    ///Performs select, awaiting indefinitely until at least one descriptor has changes.
    pub fn select(&self) -> io::Result<SelectResult> {
        let mut result = SelectResult {
            count: 0,
            read: self.read.clone(),
            write: self.write.clone(),
            except: self.except.clone(),
        };

        result.count = sys::select(&mut result.read, &mut result.write, &mut result.except)?;
        Ok(result)
    }

    #[inline]
    ///Performs select, checking file descriptors for changes and returning immediately.
    pub fn try_select(&self) -> io::Result<SelectResult> {
        let mut result = SelectResult {
            count: 0,
            read: self.read.clone(),
            write: self.write.clone(),
            except: self.except.clone(),
        };

        result.count = sys::select_timeout(&mut result.read, &mut result.write, &mut result.except, time::Duration::from_secs(0))?;
        Ok(result)
    }

    #[inline]
    ///Performs select, awaiting at most `time` until at least one descriptor has changes.
    pub fn select_timeout(&self, time: time::Duration) -> io::Result<SelectResult> {
        let mut result = SelectResult {
            count: 0,
            read: self.read.clone(),
            write: self.write.clone(),
            except: self.except.clone(),
        };

        result.count = sys::select_timeout(&mut result.read, &mut result.write, &mut result.except, time)?;
        Ok(result)
    }
}