remove timer and lock functions, fix building with proxmox 0.3.2

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-08-04 11:33:02 +02:00
parent 799b3d88bc
commit 98c259b4c1
10 changed files with 27 additions and 460 deletions

View File

@ -2,6 +2,7 @@ use anyhow::{bail, Error};
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Router, RpcEnvironment, Permission}; use proxmox::api::{api, Router, RpcEnvironment, Permission};
use proxmox::tools::fs::open_file_locked;
use crate::api2::types::*; use crate::api2::types::*;
use crate::config::acl; use crate::config::acl;
@ -174,7 +175,7 @@ pub fn update_acl(
_rpcenv: &mut dyn RpcEnvironment, _rpcenv: &mut dyn RpcEnvironment,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(acl::ACL_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(acl::ACL_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut tree, expected_digest) = acl::config()?; let (mut tree, expected_digest) = acl::config()?;

View File

@ -3,6 +3,7 @@ use serde_json::Value;
use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
use proxmox::api::schema::{Schema, StringSchema}; use proxmox::api::schema::{Schema, StringSchema};
use proxmox::tools::fs::open_file_locked;
use crate::api2::types::*; use crate::api2::types::*;
use crate::config::user; use crate::config::user;
@ -87,7 +88,7 @@ pub fn list_users(
/// Create new user. /// Create new user.
pub fn create_user(password: Option<String>, param: Value) -> Result<(), Error> { pub fn create_user(password: Option<String>, param: Value) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let user: user::User = serde_json::from_value(param)?; let user: user::User = serde_json::from_value(param)?;
@ -193,7 +194,7 @@ pub fn update_user(
digest: Option<String>, digest: Option<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = user::config()?; let (mut config, expected_digest) = user::config()?;
@ -265,7 +266,7 @@ pub fn update_user(
/// Remove a user from the configuration file. /// Remove a user from the configuration file.
pub fn delete_user(userid: String, digest: Option<String>) -> Result<(), Error> { pub fn delete_user(userid: String, digest: Option<String>) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = user::config()?; let (mut config, expected_digest) = user::config()?;

View File

@ -5,6 +5,7 @@ use serde_json::Value;
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Router, RpcEnvironment, Permission}; use proxmox::api::{api, Router, RpcEnvironment, Permission};
use proxmox::tools::fs::open_file_locked;
use crate::api2::types::*; use crate::api2::types::*;
use crate::backup::*; use crate::backup::*;
@ -99,7 +100,7 @@ pub fn list_datastores(
/// Create new datastore config. /// Create new datastore config.
pub fn create_datastore(param: Value) -> Result<(), Error> { pub fn create_datastore(param: Value) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let datastore: datastore::DataStoreConfig = serde_json::from_value(param.clone())?; let datastore: datastore::DataStoreConfig = serde_json::from_value(param.clone())?;
@ -253,7 +254,7 @@ pub fn update_datastore(
digest: Option<String>, digest: Option<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
// pass/compare digest // pass/compare digest
let (mut config, expected_digest) = datastore::config()?; let (mut config, expected_digest) = datastore::config()?;
@ -327,7 +328,7 @@ pub fn update_datastore(
/// Remove a datastore configuration. /// Remove a datastore configuration.
pub fn delete_datastore(name: String, digest: Option<String>) -> Result<(), Error> { pub fn delete_datastore(name: String, digest: Option<String>) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(datastore::DATASTORE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = datastore::config()?; let (mut config, expected_digest) = datastore::config()?;

View File

@ -4,6 +4,7 @@ use ::serde::{Deserialize, Serialize};
use base64; use base64;
use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
use proxmox::tools::fs::open_file_locked;
use crate::api2::types::*; use crate::api2::types::*;
use crate::config::remote; use crate::config::remote;
@ -78,7 +79,7 @@ pub fn list_remotes(
/// Create new remote. /// Create new remote.
pub fn create_remote(password: String, param: Value) -> Result<(), Error> { pub fn create_remote(password: String, param: Value) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let mut data = param.clone(); let mut data = param.clone();
data["password"] = Value::from(base64::encode(password.as_bytes())); data["password"] = Value::from(base64::encode(password.as_bytes()));
@ -194,7 +195,7 @@ pub fn update_remote(
digest: Option<String>, digest: Option<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = remote::config()?; let (mut config, expected_digest) = remote::config()?;
@ -255,7 +256,7 @@ pub fn update_remote(
/// Remove a remote from the configuration file. /// Remove a remote from the configuration file.
pub fn delete_remote(name: String, digest: Option<String>) -> Result<(), Error> { pub fn delete_remote(name: String, digest: Option<String>) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(remote::REMOTE_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = remote::config()?; let (mut config, expected_digest) = remote::config()?;

View File

@ -3,6 +3,7 @@ use serde_json::Value;
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, Router, RpcEnvironment}; use proxmox::api::{api, Router, RpcEnvironment};
use proxmox::tools::fs::open_file_locked;
use crate::api2::types::*; use crate::api2::types::*;
use crate::config::sync::{self, SyncJobConfig}; use crate::config::sync::{self, SyncJobConfig};
@ -68,7 +69,7 @@ pub fn list_sync_jobs(
/// Create a new sync job. /// Create a new sync job.
pub fn create_sync_job(param: Value) -> Result<(), Error> { pub fn create_sync_job(param: Value) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let sync_job: sync::SyncJobConfig = serde_json::from_value(param.clone())?; let sync_job: sync::SyncJobConfig = serde_json::from_value(param.clone())?;
@ -184,7 +185,7 @@ pub fn update_sync_job(
digest: Option<String>, digest: Option<String>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
// pass/compare digest // pass/compare digest
let (mut config, expected_digest) = sync::config()?; let (mut config, expected_digest) = sync::config()?;
@ -247,7 +248,7 @@ pub fn update_sync_job(
/// Remove a sync job configuration /// Remove a sync job configuration
pub fn delete_sync_job(id: String, digest: Option<String>) -> Result<(), Error> { pub fn delete_sync_job(id: String, digest: Option<String>) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(sync::SYNC_CFG_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = sync::config()?; let (mut config, expected_digest) = sync::config()?;

View File

@ -4,6 +4,7 @@ use ::serde::{Deserialize, Serialize};
use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
use proxmox::api::schema::parse_property_string; use proxmox::api::schema::parse_property_string;
use proxmox::tools::fs::open_file_locked;
use crate::config::network::{self, NetworkConfig}; use crate::config::network::{self, NetworkConfig};
use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY}; use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_MODIFY};
@ -230,7 +231,7 @@ pub fn create_interface(
let interface_type = crate::tools::required_string_param(&param, "type")?; let interface_type = crate::tools::required_string_param(&param, "type")?;
let interface_type: NetworkInterfaceType = serde_json::from_value(interface_type.into())?; let interface_type: NetworkInterfaceType = serde_json::from_value(interface_type.into())?;
let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, _digest) = network::config()?; let (mut config, _digest) = network::config()?;
@ -463,7 +464,7 @@ pub fn update_interface(
param: Value, param: Value,
) -> Result<(), Error> { ) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = network::config()?; let (mut config, expected_digest) = network::config()?;
@ -586,7 +587,7 @@ pub fn update_interface(
/// Remove network interface configuration. /// Remove network interface configuration.
pub fn delete_interface(iface: String, digest: Option<String>) -> Result<(), Error> { pub fn delete_interface(iface: String, digest: Option<String>) -> Result<(), Error> {
let _lock = crate::tools::open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?; let _lock = open_file_locked(network::NETWORK_LOCKFILE, std::time::Duration::new(10, 0))?;
let (mut config, expected_digest) = network::config()?; let (mut config, expected_digest) = network::config()?;

View File

@ -155,7 +155,7 @@ impl BackupGroup {
// acquire in non-blocking mode, no point in waiting here since other // acquire in non-blocking mode, no point in waiting here since other
// backups could still take a very long time // backups could still take a very long time
tools::lock_file(&mut handle, true, Some(Duration::from_nanos(0))) proxmox::tools::fs::lock_file(&mut handle, true, Some(Duration::from_nanos(0)))
.map_err(|err| { .map_err(|err| {
format_err!( format_err!(
"unable to acquire lock on backup group {:?} - {}", "unable to acquire lock on backup group {:?} - {}",

View File

@ -15,7 +15,7 @@ use tokio::sync::oneshot;
use proxmox::sys::linux::procfs; use proxmox::sys::linux::procfs;
use proxmox::try_block; use proxmox::try_block;
use proxmox::tools::fs::{create_path, replace_file, CreateOptions}; use proxmox::tools::fs::{create_path, open_file_locked, replace_file, CreateOptions};
use super::UPID; use super::UPID;
@ -247,7 +247,7 @@ fn update_active_workers(new_upid: Option<&UPID>) -> Result<Vec<TaskListInfo>, E
let backup_user = crate::backup::backup_user()?; let backup_user = crate::backup::backup_user()?;
let lock = crate::tools::open_file_locked(PROXMOX_BACKUP_TASK_LOCK_FN, std::time::Duration::new(10, 0))?; let lock = open_file_locked(PROXMOX_BACKUP_TASK_LOCK_FN, std::time::Duration::new(10, 0))?;
nix::unistd::chown(PROXMOX_BACKUP_TASK_LOCK_FN, Some(backup_user.uid), Some(backup_user.gid))?; nix::unistd::chown(PROXMOX_BACKUP_TASK_LOCK_FN, Some(backup_user.uid), Some(backup_user.gid))?;
let reader = match File::open(PROXMOX_BACKUP_ACTIVE_TASK_FN) { let reader = match File::open(PROXMOX_BACKUP_ACTIVE_TASK_FN) {

View File

@ -4,9 +4,9 @@
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::BuildHasher; use std::hash::BuildHasher;
use std::fs::{File, OpenOptions}; use std::fs::File;
use std::io::{self, BufRead, ErrorKind, Read}; use std::io::{self, BufRead, ErrorKind, Read};
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::RawFd;
use std::path::Path; use std::path::Path;
use std::time::Duration; use std::time::Duration;
use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH};
@ -17,7 +17,6 @@ use openssl::hash::{hash, DigestBytes, MessageDigest};
use percent_encoding::AsciiSet; use percent_encoding::AsciiSet;
use proxmox::tools::vec; use proxmox::tools::vec;
use proxmox::sys::error::SysResult;
pub use proxmox::tools::fd::Fd; pub use proxmox::tools::fd::Fd;
@ -32,7 +31,6 @@ pub mod format;
pub mod lru_cache; pub mod lru_cache;
pub mod runtime; pub mod runtime;
pub mod ticket; pub mod ticket;
pub mod timer;
pub mod statistics; pub mod statistics;
pub mod systemd; pub mod systemd;
pub mod nom; pub mod nom;
@ -90,73 +88,6 @@ pub fn map_struct_mut<T>(buffer: &mut [u8]) -> Result<&mut T, Error> {
Ok(unsafe { &mut *(buffer.as_ptr() as *mut T) }) Ok(unsafe { &mut *(buffer.as_ptr() as *mut T) })
} }
/// Create a file lock using fntl. This function allows you to specify
/// a timeout if you want to avoid infinite blocking.
///
/// With timeout set to 0, non-blocking mode is used and the function
/// will fail immediately if the lock can't be acquired.
pub fn lock_file<F: AsRawFd>(
file: &mut F,
exclusive: bool,
timeout: Option<Duration>,
) -> Result<(), io::Error> {
let lockarg = if exclusive {
nix::fcntl::FlockArg::LockExclusive
} else {
nix::fcntl::FlockArg::LockShared
};
let timeout = match timeout {
None => {
nix::fcntl::flock(file.as_raw_fd(), lockarg).into_io_result()?;
return Ok(());
}
Some(t) => t,
};
if timeout.as_nanos() == 0 {
let lockarg = if exclusive {
nix::fcntl::FlockArg::LockExclusiveNonblock
} else {
nix::fcntl::FlockArg::LockSharedNonblock
};
nix::fcntl::flock(file.as_raw_fd(), lockarg).into_io_result()?;
return Ok(());
}
// unblock the timeout signal temporarily
let _sigblock_guard = timer::unblock_timeout_signal();
// setup a timeout timer
let mut timer = timer::Timer::create(
timer::Clock::Realtime,
timer::TimerEvent::ThisThreadSignal(timer::SIGTIMEOUT),
)?;
timer.arm(
timer::TimerSpec::new()
.value(Some(timeout))
.interval(Some(Duration::from_millis(10))),
)?;
nix::fcntl::flock(file.as_raw_fd(), lockarg).into_io_result()?;
Ok(())
}
/// Open or create a lock file (append mode). Then try to
/// acquire a lock using `lock_file()`.
pub fn open_file_locked<P: AsRef<Path>>(path: P, timeout: Duration) -> Result<File, Error> {
let path = path.as_ref();
let mut file = match OpenOptions::new().create(true).append(true).open(path) {
Ok(file) => file,
Err(err) => bail!("Unable to open lock {:?} - {}", path, err),
};
match lock_file(&mut file, true, Some(timeout)) {
Ok(_) => Ok(file),
Err(err) => bail!("Unable to acquire lock {:?} - {}", path, err),
}
}
/// Split a file into equal sized chunks. The last chunk may be /// Split a file into equal sized chunks. The last chunk may be
/// smaller. Note: We cannot implement an `Iterator`, because iterators /// smaller. Note: We cannot implement an `Iterator`, because iterators
/// cannot return a borrowed buffer ref (we want zero-copy) /// cannot return a borrowed buffer ref (we want zero-copy)

View File

@ -1,370 +0,0 @@
//! POSIX per-process timer interface.
//!
//! This module provides a wrapper around POSIX timers (see `timer_create(2)`) and utilities to
//! setup thread-targeted signaling and signal masks.
use std::mem::MaybeUninit;
use std::time::Duration;
use std::{io, mem};
use libc::{c_int, clockid_t, pid_t};
/// Timers can use various clocks. See `timer_create(2)`.
pub enum Clock {
/// Use `CLOCK_REALTIME` for the timer.
Realtime,
/// Use `CLOCK_MONOTONIC` for the timer.
Monotonic,
}
/// Strong thread-id type to prevent accidental conversion of pid_t.
pub struct Tid(pid_t);
/// Convenience helper to get the current thread ID suitable to pass to a
/// `TimerEvent::ThreadSignal` entry.
pub fn gettid() -> Tid {
Tid(unsafe { libc::syscall(libc::SYS_gettid) } as pid_t)
}
/// Strong signal type which is more advanced than nix::sys::signal::Signal as
/// it doesn't prevent you from using signals that the nix crate is unaware
/// of...!
pub struct Signal(c_int);
impl Into<c_int> for Signal {
fn into(self) -> c_int {
self.0
}
}
impl From<c_int> for Signal {
fn from(v: c_int) -> Signal {
Signal(v)
}
}
/// When instantiating a Timer, it needs to have an event type associated with
/// it to be fired whenever the timer expires. Most of the time this will be a
/// `Signal`. Sometimes we need to be able to send signals to specific threads.
pub enum TimerEvent {
/// This will act like passing `NULL` to `timer_create()`, which maps to
/// using the same as `Signal(SIGALRM)`.
None,
/// When the timer expires, send a specific signal to the current process.
Signal(Signal),
/// When the timer expires, send a specific signal to a specific thread.
ThreadSignal(Tid, Signal),
/// Convenience value to send a signal to the current thread. This is
/// equivalent to using `ThreadSignal(gettid(), signal)`.
ThisThreadSignal(Signal),
}
// timer_t is a pointer type, so we create a strongly typed internal handle
// type for it
#[repr(C)]
struct InternalTimerT(u32);
type TimerT = *mut InternalTimerT;
// These wrappers are defined in -lrt.
#[link(name = "rt")]
extern "C" {
fn timer_create(clockid: clockid_t, evp: *mut libc::sigevent, timer: *mut TimerT) -> c_int;
fn timer_delete(timer: TimerT) -> c_int;
fn timer_settime(
timerid: TimerT,
flags: c_int,
new_value: *const libc::itimerspec,
old_value: *mut libc::itimerspec,
) -> c_int;
}
/// Represents a POSIX per-process timer as created via `timer_create(2)`.
pub struct Timer {
timer: TimerT,
}
/// Timer specification used to arm a `Timer`.
#[derive(Default)]
pub struct TimerSpec {
/// The timeout to the next timer event.
pub value: Option<Duration>,
/// When a timer expires, it may be automatically rearmed with another
/// timeout. This will keep happening until this is explicitly disabled
/// or the timer deleted.
pub interval: Option<Duration>,
}
// Helpers to convert between libc::timespec and Option<Duration>
fn opt_duration_to_timespec(v: Option<Duration>) -> libc::timespec {
match v {
None => libc::timespec {
tv_sec: 0,
tv_nsec: 0,
},
Some(value) => libc::timespec {
tv_sec: value.as_secs() as i64,
tv_nsec: value.subsec_nanos() as i64,
},
}
}
fn timespec_to_opt_duration(v: libc::timespec) -> Option<Duration> {
if v.tv_sec == 0 && v.tv_nsec == 0 {
None
} else {
Some(Duration::new(v.tv_sec as u64, v.tv_nsec as u32))
}
}
impl TimerSpec {
// Helpers to convert between TimerSpec and libc::itimerspec
fn to_itimerspec(&self) -> libc::itimerspec {
libc::itimerspec {
it_value: opt_duration_to_timespec(self.value),
it_interval: opt_duration_to_timespec(self.interval),
}
}
fn from_itimerspec(ts: libc::itimerspec) -> Self {
TimerSpec {
value: timespec_to_opt_duration(ts.it_value),
interval: timespec_to_opt_duration(ts.it_interval),
}
}
/// Create an empty timer specification representing a disabled timer.
pub fn new() -> Self {
TimerSpec {
value: None,
interval: None,
}
}
/// Change the specification to have a specific value.
pub fn value(self, value: Option<Duration>) -> Self {
TimerSpec {
value,
interval: self.interval,
}
}
/// Change the specification to have a specific interval.
pub fn interval(self, interval: Option<Duration>) -> Self {
TimerSpec {
value: self.value,
interval,
}
}
}
impl Timer {
/// Create a Timer object governing a POSIX timer.
pub fn create(clock: Clock, event: TimerEvent) -> io::Result<Timer> {
// Map from our clock type to the libc id
let clkid = match clock {
Clock::Realtime => libc::CLOCK_REALTIME,
Clock::Monotonic => libc::CLOCK_MONOTONIC,
} as clockid_t;
// Map the TimerEvent to libc::sigevent
let mut ev: libc::sigevent = unsafe { mem::zeroed() };
match event {
TimerEvent::None => ev.sigev_notify = libc::SIGEV_NONE,
TimerEvent::Signal(signo) => {
ev.sigev_signo = signo.0;
ev.sigev_notify = libc::SIGEV_SIGNAL;
}
TimerEvent::ThreadSignal(tid, signo) => {
ev.sigev_signo = signo.0;
ev.sigev_notify = libc::SIGEV_THREAD_ID;
ev.sigev_notify_thread_id = tid.0;
}
TimerEvent::ThisThreadSignal(signo) => {
ev.sigev_signo = signo.0;
ev.sigev_notify = libc::SIGEV_THREAD_ID;
ev.sigev_notify_thread_id = gettid().0;
}
}
// Create the timer
let mut timer: TimerT = unsafe { mem::zeroed() };
let rc = unsafe { timer_create(clkid, &mut ev, &mut timer) };
if rc != 0 {
Err(io::Error::last_os_error())
} else {
Ok(Timer { timer })
}
}
/// Arm a timer. This returns the previous timer specification.
pub fn arm(&mut self, spec: TimerSpec) -> io::Result<TimerSpec> {
let newspec = spec.to_itimerspec();
let mut oldspec = MaybeUninit::<libc::itimerspec>::uninit();
let rc = unsafe { timer_settime(self.timer, 0, &newspec, &mut *oldspec.as_mut_ptr()) };
if rc != 0 {
return Err(io::Error::last_os_error());
}
Ok(TimerSpec::from_itimerspec(unsafe { oldspec.assume_init() }))
}
}
impl Drop for Timer {
fn drop(&mut self) {
unsafe {
timer_delete(self.timer);
}
}
}
/// This is the signal number we use in our timeout implementations. We expect
/// the signal handler for this signal to never be replaced by some other
/// library. If this does happen, we need to find another signal. There should
/// be plenty.
/// Currently this is SIGRTMIN+4, the 5th real-time signal. glibc reserves the
/// first two for pthread internals.
pub const SIGTIMEOUT: Signal = Signal(32 + 4);
// Our timeout handler does exactly nothing. We only need it to interrupt
// system calls.
extern "C" fn sig_timeout_handler(_: c_int) {}
// See setup_timeout_handler().
fn do_setup_timeout_handler() -> io::Result<()> {
// Unfortunately nix::sys::signal::Signal cannot represent real time
// signals, so we need to use libc instead...
//
// This WOULD be a nicer impl though:
//nix::sys::signal::sigaction(
// SIGTIMEOUT,
// nix::sys::signal::SigAction::new(
// nix::sys::signal::SigHandler::Handler(sig_timeout_handler),
// nix::sys::signal::SaFlags::empty(),
// nix::sys::signal::SigSet::all()))
// .map(|_|())
unsafe {
let mut sa_mask = MaybeUninit::<libc::sigset_t>::uninit();
if libc::sigemptyset(&mut *sa_mask.as_mut_ptr()) != 0
|| libc::sigaddset(&mut *sa_mask.as_mut_ptr(), SIGTIMEOUT.0) != 0
{
return Err(io::Error::last_os_error());
}
let sa = libc::sigaction {
sa_sigaction:
// libc::sigaction uses `usize` for the function pointer...
sig_timeout_handler as *const extern "C" fn(i32) as usize,
sa_mask: sa_mask.assume_init(),
sa_flags: 0,
sa_restorer: None,
};
if libc::sigaction(SIGTIMEOUT.0, &sa, std::ptr::null_mut()) != 0 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
// The first time we unblock SIGTIMEOUT should cause approprate initialization:
static SETUP_TIMEOUT_HANDLER: std::sync::Once = std::sync::Once::new();
/// Setup our timeout-signal workflow. This establishes the signal handler for
/// our `SIGTIMEOUT` and should be called once during initialization.
#[inline]
pub fn setup_timeout_handler() {
SETUP_TIMEOUT_HANDLER.call_once(|| {
// We unwrap here.
// If setting up this handler fails you have other problems already,
// plus, if setting up fails you can't *use* it either, so everything
// goes to die.
do_setup_timeout_handler().unwrap();
});
}
/// This guards the state of the timeout signal: We want it blocked usually.
pub struct TimeoutBlockGuard(bool);
impl Drop for TimeoutBlockGuard {
fn drop(&mut self) {
if self.0 {
block_timeout_signal();
} else {
unblock_timeout_signal().forget();
}
}
}
impl TimeoutBlockGuard {
/// Convenience helper to "forget" to restore the signal block mask.
#[inline(always)]
pub fn forget(self) {
std::mem::forget(self);
}
/// Convenience helper to trigger the guard behavior immediately.
#[inline(always)]
pub fn trigger(self) {
std::mem::drop(self); // be explicit here...
}
}
/// Unblock the timeout signal for the current thread. By default we block the
/// signal this behavior should be restored when done using timeouts, therefor this
/// returns a guard:
#[inline(always)]
pub fn unblock_timeout_signal() -> TimeoutBlockGuard {
// This calls std::sync::Once:
setup_timeout_handler();
//let mut set = nix::sys::signal::SigSet::empty();
//set.add(SIGTIMEOUT.0);
//set.thread_unblock()?;
//Ok(TimeoutBlockGuard{})
// Again, nix crate and its signal limitations...
// NOTE:
// sigsetops(3) and pthread_sigmask(3) can only fail if invalid memory is
// passed to the kernel, or signal numbers are "invalid", since we know
// neither is the case we will panic on error...
let was_blocked = unsafe {
let mut mask = MaybeUninit::<libc::sigset_t>::uninit();
let mut oldset = MaybeUninit::<libc::sigset_t>::uninit();
if libc::sigemptyset(&mut *mask.as_mut_ptr()) != 0
|| libc::sigaddset(&mut *mask.as_mut_ptr(), SIGTIMEOUT.0) != 0
|| libc::pthread_sigmask(
libc::SIG_UNBLOCK,
&mask.assume_init(),
&mut *oldset.as_mut_ptr(),
) != 0
{
panic!("Impossibly failed to unblock SIGTIMEOUT");
//return Err(io::Error::last_os_error());
}
libc::sigismember(&oldset.assume_init(), SIGTIMEOUT.0) == 1
};
TimeoutBlockGuard(was_blocked)
}
/// Block the timeout signal for the current thread. This is the default.
#[inline(always)]
pub fn block_timeout_signal() {
//let mut set = nix::sys::signal::SigSet::empty();
//set.add(SIGTIMEOUT);
//set.thread_block()
unsafe {
let mut mask = MaybeUninit::<libc::sigset_t>::uninit();
if libc::sigemptyset(&mut *mask.as_mut_ptr()) != 0
|| libc::sigaddset(&mut *mask.as_mut_ptr(), SIGTIMEOUT.0) != 0
|| libc::pthread_sigmask(libc::SIG_BLOCK, &mask.assume_init(), std::ptr::null_mut())
!= 0
{
panic!("Impossibly failed to block SIGTIMEOUT");
//return Err(io::Error::last_os_error());
}
}
}