From: Wolfgang Bumiller Date: Tue, 4 Aug 2020 09:33:02 +0000 (+0200) Subject: remove timer and lock functions, fix building with proxmox 0.3.2 X-Git-Tag: v0.8.10~28 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=98c259b4c1e5db18e27dcbe2066a9ab021d898fc;p=proxmox-backup.git remove timer and lock functions, fix building with proxmox 0.3.2 Signed-off-by: Wolfgang Bumiller --- diff --git a/src/api2/access/acl.rs b/src/api2/access/acl.rs index 4fed4652..7187538f 100644 --- a/src/api2/access/acl.rs +++ b/src/api2/access/acl.rs @@ -2,6 +2,7 @@ use anyhow::{bail, Error}; use ::serde::{Deserialize, Serialize}; use proxmox::api::{api, Router, RpcEnvironment, Permission}; +use proxmox::tools::fs::open_file_locked; use crate::api2::types::*; use crate::config::acl; @@ -174,7 +175,7 @@ pub fn update_acl( _rpcenv: &mut dyn RpcEnvironment, ) -> 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()?; diff --git a/src/api2/access/user.rs b/src/api2/access/user.rs index 2d6b35e7..b3f70dcf 100644 --- a/src/api2/access/user.rs +++ b/src/api2/access/user.rs @@ -3,6 +3,7 @@ use serde_json::Value; use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; use proxmox::api::schema::{Schema, StringSchema}; +use proxmox::tools::fs::open_file_locked; use crate::api2::types::*; use crate::config::user; @@ -87,7 +88,7 @@ pub fn list_users( /// Create new user. pub fn create_user(password: Option, 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)?; @@ -193,7 +194,7 @@ pub fn update_user( digest: Option, ) -> 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()?; @@ -265,7 +266,7 @@ pub fn update_user( /// Remove a user from the configuration file. pub fn delete_user(userid: String, digest: Option) -> 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()?; diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs index 7bb13c4b..1ee303f8 100644 --- a/src/api2/config/datastore.rs +++ b/src/api2/config/datastore.rs @@ -5,6 +5,7 @@ use serde_json::Value; use ::serde::{Deserialize, Serialize}; use proxmox::api::{api, Router, RpcEnvironment, Permission}; +use proxmox::tools::fs::open_file_locked; use crate::api2::types::*; use crate::backup::*; @@ -99,7 +100,7 @@ pub fn list_datastores( /// Create new datastore config. 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())?; @@ -253,7 +254,7 @@ pub fn update_datastore( digest: Option, ) -> 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 let (mut config, expected_digest) = datastore::config()?; @@ -327,7 +328,7 @@ pub fn update_datastore( /// Remove a datastore configuration. pub fn delete_datastore(name: String, digest: Option) -> 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()?; diff --git a/src/api2/config/remote.rs b/src/api2/config/remote.rs index 187515b8..237e7b14 100644 --- a/src/api2/config/remote.rs +++ b/src/api2/config/remote.rs @@ -4,6 +4,7 @@ use ::serde::{Deserialize, Serialize}; use base64; use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; +use proxmox::tools::fs::open_file_locked; use crate::api2::types::*; use crate::config::remote; @@ -78,7 +79,7 @@ pub fn list_remotes( /// Create new remote. 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(); data["password"] = Value::from(base64::encode(password.as_bytes())); @@ -194,7 +195,7 @@ pub fn update_remote( digest: Option, ) -> 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()?; @@ -255,7 +256,7 @@ pub fn update_remote( /// Remove a remote from the configuration file. pub fn delete_remote(name: String, digest: Option) -> 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()?; diff --git a/src/api2/config/sync.rs b/src/api2/config/sync.rs index 860dcc36..8273a4b4 100644 --- a/src/api2/config/sync.rs +++ b/src/api2/config/sync.rs @@ -3,6 +3,7 @@ use serde_json::Value; use ::serde::{Deserialize, Serialize}; use proxmox::api::{api, Router, RpcEnvironment}; +use proxmox::tools::fs::open_file_locked; use crate::api2::types::*; use crate::config::sync::{self, SyncJobConfig}; @@ -68,7 +69,7 @@ pub fn list_sync_jobs( /// Create a new sync job. 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())?; @@ -184,7 +185,7 @@ pub fn update_sync_job( digest: Option, ) -> 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 let (mut config, expected_digest) = sync::config()?; @@ -247,7 +248,7 @@ pub fn update_sync_job( /// Remove a sync job configuration pub fn delete_sync_job(id: String, digest: Option) -> 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()?; diff --git a/src/api2/node/network.rs b/src/api2/node/network.rs index c0c55333..69ce0df6 100644 --- a/src/api2/node/network.rs +++ b/src/api2/node/network.rs @@ -4,6 +4,7 @@ use ::serde::{Deserialize, Serialize}; use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; use proxmox::api::schema::parse_property_string; +use proxmox::tools::fs::open_file_locked; use crate::config::network::{self, NetworkConfig}; 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(¶m, "type")?; 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()?; @@ -463,7 +464,7 @@ pub fn update_interface( param: Value, ) -> 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()?; @@ -586,7 +587,7 @@ pub fn update_interface( /// Remove network interface configuration. pub fn delete_interface(iface: String, digest: Option) -> 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()?; diff --git a/src/backup/backup_info.rs b/src/backup/backup_info.rs index ba5ea1a3..37dc7aa1 100644 --- a/src/backup/backup_info.rs +++ b/src/backup/backup_info.rs @@ -155,7 +155,7 @@ impl BackupGroup { // acquire in non-blocking mode, no point in waiting here since other // 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| { format_err!( "unable to acquire lock on backup group {:?} - {}", diff --git a/src/server/worker_task.rs b/src/server/worker_task.rs index fdbbacbe..f0e28488 100644 --- a/src/server/worker_task.rs +++ b/src/server/worker_task.rs @@ -15,7 +15,7 @@ use tokio::sync::oneshot; use proxmox::sys::linux::procfs; 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; @@ -247,7 +247,7 @@ fn update_active_workers(new_upid: Option<&UPID>) -> Result, E 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))?; let reader = match File::open(PROXMOX_BACKUP_ACTIVE_TASK_FN) { diff --git a/src/tools.rs b/src/tools.rs index c1045c16..8792bf0c 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -4,9 +4,9 @@ use std::any::Any; use std::collections::HashMap; use std::hash::BuildHasher; -use std::fs::{File, OpenOptions}; +use std::fs::File; 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::time::Duration; use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; @@ -17,7 +17,6 @@ use openssl::hash::{hash, DigestBytes, MessageDigest}; use percent_encoding::AsciiSet; use proxmox::tools::vec; -use proxmox::sys::error::SysResult; pub use proxmox::tools::fd::Fd; @@ -32,7 +31,6 @@ pub mod format; pub mod lru_cache; pub mod runtime; pub mod ticket; -pub mod timer; pub mod statistics; pub mod systemd; pub mod nom; @@ -90,73 +88,6 @@ pub fn map_struct_mut(buffer: &mut [u8]) -> Result<&mut T, Error> { 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( - file: &mut F, - exclusive: bool, - timeout: Option, -) -> 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>(path: P, timeout: Duration) -> Result { - 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 /// smaller. Note: We cannot implement an `Iterator`, because iterators /// cannot return a borrowed buffer ref (we want zero-copy) diff --git a/src/tools/timer.rs b/src/tools/timer.rs deleted file mode 100644 index 40877a19..00000000 --- a/src/tools/timer.rs +++ /dev/null @@ -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 for Signal { - fn into(self) -> c_int { - self.0 - } -} - -impl From 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, - - /// 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, -} - -// Helpers to convert between libc::timespec and Option -fn opt_duration_to_timespec(v: Option) -> 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 { - 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) -> Self { - TimerSpec { - value, - interval: self.interval, - } - } - - /// Change the specification to have a specific interval. - pub fn interval(self, interval: Option) -> 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 { - // 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 { - let newspec = spec.to_itimerspec(); - let mut oldspec = MaybeUninit::::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::::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::::uninit(); - let mut oldset = MaybeUninit::::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::::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()); - } - } -}