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
use super::{ Result, DurationExt };
use std::{
	self, time::Duration,
	io::{ Error as StdIoError, ErrorKind as IoErrorKind },
	ops::{ BitOr, BitAnd }
};


/// Interface to `libselect`
pub mod libselect {
	use std::os::raw::c_int;
	extern {
		pub fn wait_for_event(descriptor: u64, event_mask: u8, timeout_ms: u64) -> u8;
		pub fn set_blocking_mode(descriptor: u64, blocking: c_int) -> u8;
		pub fn get_errno() -> c_int;
	}
}
/// An event returned by `libselect`'s `wait_for_event`
#[repr(u8)]
pub enum Event {
	Read  = 1 << 1, // const uint8_t EVENT_READ   = 1 << 1;
	Write = 1 << 2, // const uint8_t EVENT_WRITE  = 1 << 2;
	Error = 1 << 3, // const uint8_t EVENT_ERROR  = 1 << 3;
	SyscallError = 1 << 7 // const uint8_t SYSCALL_ERROR = 1 << 7;
}
impl BitOr for Event {
	type Output = u8;
	fn bitor(self, rhs: Self) -> Self::Output {
		(self as u8) | (rhs as u8)
	}
}
impl BitAnd<Event> for u8 {
	type Output = bool;
	fn bitand(self, rhs: Event) -> Self::Output {
		self & (rhs as u8) != 0
	}
}


/// A wrapper-trait that unifies the `std::os::unix::io::AsRawFd` and
/// `std::os::windows::io::AsRawSocket` traits
pub trait RawFd {
	/// The underlying raw file descriptor
	fn raw_fd(&self) -> u64;
}
#[cfg(unix)]
impl<T: std::os::unix::io::AsRawFd> RawFd for T {
	fn raw_fd(&self) -> u64 { self.as_raw_fd() as u64 }
}
#[cfg(windows)]
impl<T: std::os::windows::io::AsRawSocket> RawFd for T {
	fn raw_fd(&self) -> u64 { self.as_raw_socket() as u64 }
}


/// This trait defines an API to wait for an event
pub trait WaitForEvent {
	/// Waits until `self` is ready for reading or `timeout` was reached
	///
	/// Parameters:
	///  - `timeout`: The maximum time this function will wait for `self` to become readable
	///
	/// Returns either __nothing__ or a corresponding `IoError`
	fn wait_until_readable(&self, timeout: Duration) -> Result<()>;
	/// Waits until `self` is ready for writing or `timeout` was reached
	///
	/// Parameters:
	///  - `timeout`: The maximum time this function will wait for `self` to become writeable
	///
	/// Returns either __nothing__ or a corresponding `IoError`
	fn wait_until_writeable(&self, timeout: Duration) -> Result<()>;
	
	/// Makes `self` blocking or non-blocking
	///
	/// Parameters:
	///  - `blocking`: Set to `true` if you want to make the socket blocking, `false` otherwise
	///
	/// Returns either __nothing__ or a corresponding `IoError`
	fn set_blocking_mode(&self, blocking: bool) -> Result<()>;
}
impl<T: RawFd> WaitForEvent for T {
	fn wait_until_readable(&self, timeout: Duration) -> Result<()> {
		// Wait for event
		let result = unsafe{ libselect::wait_for_event(
			self.raw_fd(),
			Event::Read | Event::Error,
			timeout.as_ms()
		) };
		// Read result
		match result {
			r if r & Event::SyscallError => throw_err!(StdIoError::from_raw_os_error(unsafe{ libselect::get_errno() }).into()),
			r if r & Event::Read => Ok(()),
			_ => throw_err!(IoErrorKind::TimedOut.into())
		}
	}
	fn wait_until_writeable(&self, timeout: Duration) -> Result<()> {
		// Wait for event
		let result = unsafe{ libselect::wait_for_event(
			self.raw_fd(),
			Event::Write | Event::Error,
			timeout.as_ms()
		) };
		// Read result
		match result {
			r if r & Event::SyscallError => throw_err!(StdIoError::from_raw_os_error(unsafe{ libselect::get_errno() }).into()),
			r if r & Event::Write => Ok(()),
			_ => throw_err!(IoErrorKind::TimedOut.into())
		}
	}
	fn set_blocking_mode(&self, blocking: bool) -> Result<()> {
		let result = unsafe{ libselect::set_blocking_mode(
			self.raw_fd(),
			if blocking { 1 } else { 0 }
		) };
		match result {
			0 => Ok(()),
			r if r & Event::SyscallError => throw_err!(StdIoError::from_raw_os_error(unsafe{ libselect::get_errno() }).into()),
			_ => unreachable!()
		}
	}
}