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:
parent
799b3d88bc
commit
98c259b4c1
@ -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()?;
|
||||
|
||||
|
@ -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<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)?;
|
||||
|
||||
@ -193,7 +194,7 @@ pub fn update_user(
|
||||
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()?;
|
||||
|
||||
@ -265,7 +266,7 @@ pub fn update_user(
|
||||
/// Remove a user from the configuration file.
|
||||
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()?;
|
||||
|
||||
|
@ -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<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))?;
|
||||
|
||||
// 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<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()?;
|
||||
|
||||
|
@ -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<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()?;
|
||||
|
||||
@ -255,7 +256,7 @@ pub fn update_remote(
|
||||
/// Remove a remote from the configuration file.
|
||||
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()?;
|
||||
|
||||
|
@ -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<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))?;
|
||||
|
||||
// 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<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()?;
|
||||
|
||||
|
@ -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<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()?;
|
||||
|
||||
|
@ -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 {:?} - {}",
|
||||
|
@ -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<Vec<TaskListInfo>, 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) {
|
||||
|
73
src/tools.rs
73
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<T>(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<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
|
||||
/// smaller. Note: We cannot implement an `Iterator`, because iterators
|
||||
/// cannot return a borrowed buffer ref (we want zero-copy)
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user