exercises

Log | Files | Refs | README

poll.rs (5564B)


      1 //! This contains the main abstraction, which is a thin layer over `epoll`.
      2 //! There are two main abstractions over epoll. One is a structure called
      3 //! `Poll` and the other is called `Registry`
      4 
      5 use crate::ffi;
      6 /// Its convenient to use `io::Result` type since most errors will stem from one
      7 /// of our calls into the operating system, and an operating system error can
      8 /// be mapped to an `io::Error` type
      9 use std::{
     10     io::{self, Result},
     11     net::TcpStream,
     12     os::fd::AsRawFd,
     13 };
     14 
     15 type Events = Vec<ffi::Event>;
     16 
     17 /// Poll is a struct that represents the event queue itself.
     18 pub struct Poll {
     19     registry: Registry,
     20 }
     21 
     22 impl Poll {
     23     /// Creates a new event queue
     24     pub fn new() -> Result<Self> {
     25         let res = unsafe { ffi::epoll_create(1) };
     26         if res < 0 {
     27             return Err(io::Error::last_os_error());
     28         }
     29 
     30         Ok(Self {
     31             registry: Registry { raw_fd: res },
     32         })
     33     }
     34 
     35     /// Returns a reference to the registry that can be used to register
     36     /// interest to be notified about new events
     37     pub fn registry(&self) -> &Registry {
     38         &self.registry
     39     }
     40 
     41     /// Blocks the thread it's called on until an event is ready or it times
     42     /// out, whichever occurs first
     43     pub fn poll(&mut self, events: &mut Events, timeout: Option<i32>) -> Result<()> {
     44         // The first thing we do is to get the raw file descriptor for
     45         // the event queue and store it in the fd variable.
     46         let fd = self.registry.raw_fd;
     47 
     48         // Next is our timeout. If it’s Some, we unwrap that value,
     49         // and if it’s None, we set it to –1, which is the value
     50         // that tells the operating system that we want to block
     51         // until an event occurs even though that might never happen.
     52         let timeout = timeout.unwrap_or(-1);
     53 
     54         // At the top of the file, we defined Events as a type alias
     55         // for Vec<ffi::Event>, so the next thing we do is to get
     56         // the capacity of that Vec. It’s important that we don’t
     57         // rely on Vec::len since that reports how many items we have
     58         // in the Vec. Vec::capacity reports the space we’ve allocated
     59         // and that’s what we’re after.
     60         let max_events = events.capacity() as i32;
     61 
     62         // Next up is the call to ffi::epoll_wait. This call will return
     63         // successfully if it has a value of 0 or larger, telling us
     64         // how many events have occurred.
     65         let res = unsafe { ffi::epoll_wait(fd, events.as_mut_ptr(), max_events, timeout) };
     66 
     67         // Note: We would get a value of 0 if a timeout elapses before
     68         // an event has happened.
     69         if res < 0 {
     70             return Err(io::Error::last_os_error());
     71         };
     72 
     73         // The last thing we do is to make an unsafe call to
     74         // events.set_len(res as usize). This function is unsafe since
     75         // we could potentially set the length so that we would access
     76         // memory that’s not been initialized yet in safe Rust.
     77         // We know from the guarantee the operating system gives us
     78         // that the number of events it returns is pointing to valid
     79         // data in our Vec, so this is safe in our case.
     80         unsafe {
     81             events.set_len(res as usize);
     82         };
     83         Ok(())
     84     }
     85 }
     86 
     87 /// While `Poll` represents the event queue, `Registry` is a handle that allows
     88 /// us to register interest in new events
     89 pub struct Registry {
     90     raw_fd: i32,
     91 }
     92 
     93 impl Registry {
     94     /// `Registry` will only have one method: `register`
     95     /// https://docs.rs/mio/0.8.8/mio/struct.Registry.html
     96     /// The `interests` argument will indicate what kind of events we want our
     97     /// event queue to keep track of.
     98     ///
     99     /// The register function takes a &TcpStream as a source,
    100     /// a token of type usize, and a bitmask named interests,
    101     /// which is of type i32.
    102     pub fn register(&self, source: &TcpStream, token: usize, interests: i32) -> Result<()> {
    103         // The first thing we do is to create an ffi::Event object.
    104         // The events field is simply set to the bitmask we received
    105         // and named interests, and epoll_data is set to the value
    106         // we passed in the token argument.
    107         let mut event = ffi::Event {
    108             events: interests as u32,
    109             epoll_data: token,
    110         };
    111 
    112         // The operation we want to perform on the epoll queue is
    113         // adding interest in events on a new file descriptor.
    114         // Therefore, we set the op argument to the ffi::EPOLL_CTL_ADD
    115         // constant value.
    116         let op = ffi::EPOLL_CTL_ADD;
    117 
    118         // Next up is the call to ffi::epoll_ctl. We pass in the file
    119         // descriptor to the epoll instance first, then we pass in
    120         // the op argument to indicate what kind of operation we want
    121         // to perform. The last two arguments are the file descriptor
    122         // we want the queue to track and the Event object we created
    123         // to indicate what kind of events we’re interested in getting
    124         // notifications for.
    125         let res = unsafe { ffi::epoll_ctl(self.raw_fd, op, source.as_raw_fd(), &mut event) };
    126 
    127         if res < 0 {
    128             return Err(io::Error::last_os_error());
    129         }
    130         Ok(())
    131     }
    132 }
    133 
    134 impl Drop for Registry {
    135     fn drop(&mut self) {
    136         // The Drop implementation simply calls ffi::close on the epoll
    137         // file descriptor.
    138         let res = unsafe { ffi::close(self.raw_fd) };
    139         if res < 0 {
    140             let err = io::Error::last_os_error();
    141             eprintln!("ERROR: {err:?}");
    142         }
    143     }
    144 }