Replace PosixTimer with async timers
parent
57e6cf3980
commit
6392904047
18
src/bin.rs
18
src/bin.rs
|
@ -49,9 +49,6 @@ static GLOBAL: System = System;
|
||||||
extern crate melib;
|
extern crate melib;
|
||||||
use melib::*;
|
use melib::*;
|
||||||
|
|
||||||
mod unix;
|
|
||||||
use unix::*;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod types;
|
pub mod types;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
@ -104,7 +101,6 @@ fn notify(
|
||||||
nix::fcntl::FcntlArg::F_SETFL(nix::fcntl::OFlag::O_NONBLOCK),
|
nix::fcntl::FcntlArg::F_SETFL(nix::fcntl::OFlag::O_NONBLOCK),
|
||||||
);
|
);
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let mut buf = [0; 1];
|
|
||||||
let mut ctr = 0;
|
let mut ctr = 0;
|
||||||
loop {
|
loop {
|
||||||
ctr %= 3;
|
ctr %= 3;
|
||||||
|
@ -117,16 +113,6 @@ fn notify(
|
||||||
for signal in signals.pending() {
|
for signal in signals.pending() {
|
||||||
let _ = s.send_timeout(signal, Duration::from_millis(500)).ok();
|
let _ = s.send_timeout(signal, Duration::from_millis(500)).ok();
|
||||||
}
|
}
|
||||||
while nix::unistd::read(alarm_pipe_r, buf.as_mut())
|
|
||||||
.map(|s| s > 0)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
let value = buf[0];
|
|
||||||
let _ = sender.send_timeout(
|
|
||||||
ThreadEvent::UIEvent(UIEvent::Timer(value)),
|
|
||||||
Duration::from_millis(2000),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||||
ctr += 1;
|
ctr += 1;
|
||||||
|
@ -377,7 +363,9 @@ fn run_app(opt: Opt) -> Result<()> {
|
||||||
|
|
||||||
#[cfg(all(target_os = "linux", feature = "dbus-notifications"))]
|
#[cfg(all(target_os = "linux", feature = "dbus-notifications"))]
|
||||||
{
|
{
|
||||||
let dbus_notifications = Box::new(components::notifications::DbusNotifications::new());
|
let dbus_notifications = Box::new(components::notifications::DbusNotifications::new(
|
||||||
|
&state.context,
|
||||||
|
));
|
||||||
state.register_component(dbus_notifications);
|
state.register_component(dbus_notifications);
|
||||||
}
|
}
|
||||||
state.register_component(Box::new(
|
state.register_component(Box::new(
|
||||||
|
|
|
@ -19,273 +19,270 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum KeySelection {
|
pub enum KeySelection {
|
||||||
LoadingKeys {
|
LoadingKeys {
|
||||||
handle: JoinHandle<Result<Vec<melib::gpgme::Key>>>,
|
handle: JoinHandle<Result<Vec<melib::gpgme::Key>>>,
|
||||||
progress_spinner: ProgressSpinner,
|
progress_spinner: ProgressSpinner,
|
||||||
secret: bool,
|
secret: bool,
|
||||||
local: bool,
|
local: bool,
|
||||||
pattern: String,
|
pattern: String,
|
||||||
allow_remote_lookup: ToggleFlag,
|
allow_remote_lookup: ToggleFlag,
|
||||||
},
|
},
|
||||||
Error {
|
Error {
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
err: MeliError,
|
err: MeliError,
|
||||||
},
|
},
|
||||||
Loaded {
|
Loaded {
|
||||||
widget: UIDialog<melib::gpgme::Key>,
|
widget: UIDialog<melib::gpgme::Key>,
|
||||||
keys: Vec<melib::gpgme::Key>,
|
keys: Vec<melib::gpgme::Key>,
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for KeySelection {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "select pgp keys")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for KeySelection {
|
impl KeySelection {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
pub fn new(
|
||||||
write!(f, "select pgp keys")
|
secret: bool,
|
||||||
|
local: bool,
|
||||||
|
pattern: String,
|
||||||
|
allow_remote_lookup: ToggleFlag,
|
||||||
|
context: &mut Context,
|
||||||
|
) -> Result<Self> {
|
||||||
|
use melib::gpgme::*;
|
||||||
|
debug!("KeySelection::new");
|
||||||
|
debug!(&secret);
|
||||||
|
debug!(&local);
|
||||||
|
debug!(&pattern);
|
||||||
|
debug!(&allow_remote_lookup);
|
||||||
|
let mut ctx = Context::new()?;
|
||||||
|
if local {
|
||||||
|
ctx.set_auto_key_locate(LocateKey::LOCAL)?;
|
||||||
|
} else {
|
||||||
|
ctx.set_auto_key_locate(LocateKey::WKD | LocateKey::LOCAL)?;
|
||||||
|
}
|
||||||
|
let job = ctx.keylist(secret, Some(pattern.clone()))?;
|
||||||
|
let handle = context.job_executor.spawn_specialized(job);
|
||||||
|
let mut progress_spinner = ProgressSpinner::new(8, context);
|
||||||
|
progress_spinner.start();
|
||||||
|
Ok(KeySelection::LoadingKeys {
|
||||||
|
handle,
|
||||||
|
secret,
|
||||||
|
local,
|
||||||
|
pattern,
|
||||||
|
allow_remote_lookup,
|
||||||
|
progress_spinner,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for KeySelection {
|
||||||
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
|
match self {
|
||||||
|
KeySelection::LoadingKeys {
|
||||||
|
ref mut progress_spinner,
|
||||||
|
..
|
||||||
|
} => progress_spinner.draw(grid, center_area(area, (2, 2)), context),
|
||||||
|
KeySelection::Error { ref err, .. } => {
|
||||||
|
let theme_default = crate::conf::value(context, "theme_default");
|
||||||
|
write_string_to_grid(
|
||||||
|
&err.to_string(),
|
||||||
|
grid,
|
||||||
|
theme_default.fg,
|
||||||
|
theme_default.bg,
|
||||||
|
theme_default.attrs,
|
||||||
|
center_area(area, (15, 2)),
|
||||||
|
Some(0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
KeySelection::Loaded { ref mut widget, .. } => {
|
||||||
|
widget.draw(grid, center_area(area, widget.content.size()), context)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KeySelection {
|
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||||
pub fn new(
|
debug!(&self);
|
||||||
secret: bool,
|
debug!(&event);
|
||||||
local: bool,
|
match self {
|
||||||
pattern: String,
|
KeySelection::LoadingKeys {
|
||||||
allow_remote_lookup: ToggleFlag,
|
ref mut progress_spinner,
|
||||||
context: &mut Context,
|
ref mut handle,
|
||||||
) -> Result<Self> {
|
|
||||||
use melib::gpgme::*;
|
|
||||||
debug!("KeySelection::new");
|
|
||||||
debug!(&secret);
|
|
||||||
debug!(&local);
|
|
||||||
debug!(&pattern);
|
|
||||||
debug!(&allow_remote_lookup);
|
|
||||||
let mut ctx = Context::new()?;
|
|
||||||
if local {
|
|
||||||
ctx.set_auto_key_locate(LocateKey::LOCAL)?;
|
|
||||||
} else {
|
|
||||||
ctx.set_auto_key_locate(LocateKey::WKD | LocateKey::LOCAL)?;
|
|
||||||
}
|
|
||||||
let job = ctx.keylist(secret, Some(pattern.clone()))?;
|
|
||||||
let handle = context.job_executor.spawn_specialized(job);
|
|
||||||
let mut progress_spinner = ProgressSpinner::new(8);
|
|
||||||
progress_spinner.start();
|
|
||||||
Ok(KeySelection::LoadingKeys {
|
|
||||||
handle,
|
|
||||||
secret,
|
secret,
|
||||||
local,
|
local,
|
||||||
pattern,
|
ref mut pattern,
|
||||||
allow_remote_lookup,
|
allow_remote_lookup,
|
||||||
progress_spinner,
|
..
|
||||||
})
|
} => match event {
|
||||||
}
|
UIEvent::StatusEvent(StatusEvent::JobFinished(ref id)) if *id == handle.job_id => {
|
||||||
}
|
match handle.chan.try_recv().unwrap().unwrap() {
|
||||||
|
Ok(keys) => {
|
||||||
impl Component for KeySelection {
|
if keys.is_empty() {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
let id = progress_spinner.id();
|
||||||
match self {
|
if allow_remote_lookup.is_true() {
|
||||||
KeySelection::LoadingKeys {
|
match Self::new(
|
||||||
ref mut progress_spinner,
|
*secret,
|
||||||
..
|
*local,
|
||||||
} => progress_spinner.draw(grid, center_area(area, (2, 2)), context),
|
std::mem::replace(pattern, String::new()),
|
||||||
KeySelection::Error { ref err, .. } => {
|
*allow_remote_lookup,
|
||||||
let theme_default = crate::conf::value(context, "theme_default");
|
context,
|
||||||
write_string_to_grid(
|
) {
|
||||||
&err.to_string(),
|
Ok(w) => {
|
||||||
grid,
|
*self = w;
|
||||||
theme_default.fg,
|
|
||||||
theme_default.bg,
|
|
||||||
theme_default.attrs,
|
|
||||||
center_area(area, (15, 2)),
|
|
||||||
Some(0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
KeySelection::Loaded { ref mut widget, .. } => {
|
|
||||||
widget.draw(grid, center_area(area, widget.content.size()), context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
|
||||||
debug!(&self);
|
|
||||||
debug!(&event);
|
|
||||||
match self {
|
|
||||||
KeySelection::LoadingKeys {
|
|
||||||
ref mut progress_spinner,
|
|
||||||
ref mut handle,
|
|
||||||
secret,
|
|
||||||
local,
|
|
||||||
ref mut pattern,
|
|
||||||
allow_remote_lookup,
|
|
||||||
..
|
|
||||||
} => match event {
|
|
||||||
UIEvent::StatusEvent(StatusEvent::JobFinished(ref id))
|
|
||||||
if *id == handle.job_id =>
|
|
||||||
{
|
|
||||||
match handle.chan.try_recv().unwrap().unwrap() {
|
|
||||||
Ok(keys) => {
|
|
||||||
if keys.is_empty() {
|
|
||||||
let id = progress_spinner.id();
|
|
||||||
if allow_remote_lookup.is_true() {
|
|
||||||
match Self::new(
|
|
||||||
*secret,
|
|
||||||
*local,
|
|
||||||
std::mem::replace(pattern, String::new()),
|
|
||||||
*allow_remote_lookup,
|
|
||||||
context,
|
|
||||||
) {
|
|
||||||
Ok(w) => {
|
|
||||||
*self = w;
|
|
||||||
}
|
|
||||||
Err(err) => *self = KeySelection::Error { err, id },
|
|
||||||
}
|
|
||||||
} else if !*local && allow_remote_lookup.is_ask() {
|
|
||||||
*self = KeySelection::Error {
|
|
||||||
err: MeliError::new(format!(
|
|
||||||
"No keys found for {}, perform remote lookup?",
|
|
||||||
pattern
|
|
||||||
)),
|
|
||||||
id,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*self = KeySelection::Error {
|
|
||||||
err: MeliError::new(format!(
|
|
||||||
"No keys found for {}.",
|
|
||||||
pattern
|
|
||||||
)),
|
|
||||||
id,
|
|
||||||
}
|
}
|
||||||
|
Err(err) => *self = KeySelection::Error { err, id },
|
||||||
}
|
}
|
||||||
if let KeySelection::Error { ref err, .. } = self {
|
} else if !*local && allow_remote_lookup.is_ask() {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
*self = KeySelection::Error {
|
||||||
StatusEvent::DisplayMessage(err.to_string()),
|
err: MeliError::new(format!(
|
||||||
));
|
"No keys found for {}, perform remote lookup?",
|
||||||
let res: Option<melib::gpgme::Key> = None;
|
pattern
|
||||||
context.replies.push_back(UIEvent::FinishedUIDialog(
|
)),
|
||||||
id,
|
id,
|
||||||
Box::new(res),
|
}
|
||||||
));
|
} else {
|
||||||
|
*self = KeySelection::Error {
|
||||||
|
err: MeliError::new(format!(
|
||||||
|
"No keys found for {}.",
|
||||||
|
pattern
|
||||||
|
)),
|
||||||
|
id,
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
let mut widget = UIDialog::new(
|
if let KeySelection::Error { ref err, .. } = self {
|
||||||
"select key",
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
keys.iter()
|
StatusEvent::DisplayMessage(err.to_string()),
|
||||||
.map(|k| {
|
));
|
||||||
(
|
let res: Option<melib::gpgme::Key> = None;
|
||||||
k.clone(),
|
context
|
||||||
if let Some(primary_uid) = k.primary_uid() {
|
.replies
|
||||||
format!("{} {}", k.fingerprint(), primary_uid)
|
.push_back(UIEvent::FinishedUIDialog(id, Box::new(res)));
|
||||||
} else {
|
}
|
||||||
k.fingerprint().to_string()
|
return true;
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<(melib::gpgme::Key, String)>>(),
|
|
||||||
true,
|
|
||||||
Some(Box::new(
|
|
||||||
move |id: ComponentId, results: &[melib::gpgme::Key]| {
|
|
||||||
Some(UIEvent::FinishedUIDialog(
|
|
||||||
id,
|
|
||||||
Box::new(results.get(0).map(|k| k.clone())),
|
|
||||||
))
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
widget.set_dirty(true);
|
|
||||||
*self = KeySelection::Loaded { widget, keys };
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
*self = KeySelection::Error {
|
|
||||||
err,
|
|
||||||
id: ComponentId::new_v4(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
let mut widget = UIDialog::new(
|
||||||
|
"select key",
|
||||||
|
keys.iter()
|
||||||
|
.map(|k| {
|
||||||
|
(
|
||||||
|
k.clone(),
|
||||||
|
if let Some(primary_uid) = k.primary_uid() {
|
||||||
|
format!("{} {}", k.fingerprint(), primary_uid)
|
||||||
|
} else {
|
||||||
|
k.fingerprint().to_string()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<(melib::gpgme::Key, String)>>(),
|
||||||
|
true,
|
||||||
|
Some(Box::new(
|
||||||
|
move |id: ComponentId, results: &[melib::gpgme::Key]| {
|
||||||
|
Some(UIEvent::FinishedUIDialog(
|
||||||
|
id,
|
||||||
|
Box::new(results.get(0).map(|k| k.clone())),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
)),
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
widget.set_dirty(true);
|
||||||
|
*self = KeySelection::Loaded { widget, keys };
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
*self = KeySelection::Error {
|
||||||
|
err,
|
||||||
|
id: ComponentId::new_v4(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
true
|
|
||||||
}
|
}
|
||||||
_ => progress_spinner.process_event(event, context),
|
true
|
||||||
},
|
|
||||||
KeySelection::Error { .. } => false,
|
|
||||||
KeySelection::Loaded { ref mut widget, .. } => widget.process_event(event, context),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_dirty(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
KeySelection::LoadingKeys {
|
|
||||||
ref progress_spinner,
|
|
||||||
..
|
|
||||||
} => progress_spinner.is_dirty(),
|
|
||||||
KeySelection::Error { .. } => true,
|
|
||||||
KeySelection::Loaded { ref widget, .. } => widget.is_dirty(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_dirty(&mut self, value: bool) {
|
|
||||||
match self {
|
|
||||||
KeySelection::LoadingKeys {
|
|
||||||
ref mut progress_spinner,
|
|
||||||
..
|
|
||||||
} => progress_spinner.set_dirty(value),
|
|
||||||
KeySelection::Error { .. } => {}
|
|
||||||
KeySelection::Loaded { ref mut widget, .. } => widget.set_dirty(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kill(&mut self, _uuid: Uuid, _context: &mut Context) {}
|
|
||||||
|
|
||||||
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
|
||||||
match self {
|
|
||||||
KeySelection::LoadingKeys { .. } | KeySelection::Error { .. } => {
|
|
||||||
ShortcutMaps::default()
|
|
||||||
}
|
}
|
||||||
KeySelection::Loaded { ref widget, .. } => widget.get_shortcuts(context),
|
_ => progress_spinner.process_event(event, context),
|
||||||
}
|
},
|
||||||
}
|
KeySelection::Error { .. } => false,
|
||||||
|
KeySelection::Loaded { ref mut widget, .. } => widget.process_event(event, context),
|
||||||
fn id(&self) -> ComponentId {
|
|
||||||
match self {
|
|
||||||
KeySelection::LoadingKeys {
|
|
||||||
ref progress_spinner,
|
|
||||||
..
|
|
||||||
} => progress_spinner.id(),
|
|
||||||
KeySelection::Error { ref id, .. } => *id,
|
|
||||||
KeySelection::Loaded { ref widget, .. } => widget.id(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_id(&mut self, new_id: ComponentId) {
|
|
||||||
match self {
|
|
||||||
KeySelection::LoadingKeys {
|
|
||||||
ref mut progress_spinner,
|
|
||||||
..
|
|
||||||
} => progress_spinner.set_id(new_id),
|
|
||||||
KeySelection::Error { ref mut id, .. } => *id = new_id,
|
|
||||||
KeySelection::Loaded { ref mut widget, .. } => widget.set_id(new_id),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
fn is_dirty(&self) -> bool {
|
||||||
pub struct GpgComposeState {
|
match self {
|
||||||
pub sign_mail: ToggleFlag,
|
KeySelection::LoadingKeys {
|
||||||
pub encrypt_mail: ToggleFlag,
|
ref progress_spinner,
|
||||||
pub encrypt_keys: Vec<melib::gpgme::Key>,
|
..
|
||||||
pub encrypt_for_self: bool,
|
} => progress_spinner.is_dirty(),
|
||||||
pub sign_keys: Vec<melib::gpgme::Key>,
|
KeySelection::Error { .. } => true,
|
||||||
}
|
KeySelection::Loaded { ref widget, .. } => widget.is_dirty(),
|
||||||
|
|
||||||
impl GpgComposeState {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
GpgComposeState {
|
|
||||||
sign_mail: ToggleFlag::Unset,
|
|
||||||
encrypt_mail: ToggleFlag::Unset,
|
|
||||||
encrypt_keys: vec![],
|
|
||||||
encrypt_for_self: true,
|
|
||||||
sign_keys: vec![],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_dirty(&mut self, value: bool) {
|
||||||
|
match self {
|
||||||
|
KeySelection::LoadingKeys {
|
||||||
|
ref mut progress_spinner,
|
||||||
|
..
|
||||||
|
} => progress_spinner.set_dirty(value),
|
||||||
|
KeySelection::Error { .. } => {}
|
||||||
|
KeySelection::Loaded { ref mut widget, .. } => widget.set_dirty(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kill(&mut self, _uuid: Uuid, _context: &mut Context) {}
|
||||||
|
|
||||||
|
fn get_shortcuts(&self, context: &Context) -> ShortcutMaps {
|
||||||
|
match self {
|
||||||
|
KeySelection::LoadingKeys { .. } | KeySelection::Error { .. } => {
|
||||||
|
ShortcutMaps::default()
|
||||||
|
}
|
||||||
|
KeySelection::Loaded { ref widget, .. } => widget.get_shortcuts(context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn id(&self) -> ComponentId {
|
||||||
|
match self {
|
||||||
|
KeySelection::LoadingKeys {
|
||||||
|
ref progress_spinner,
|
||||||
|
..
|
||||||
|
} => progress_spinner.id(),
|
||||||
|
KeySelection::Error { ref id, .. } => *id,
|
||||||
|
KeySelection::Loaded { ref widget, .. } => widget.id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_id(&mut self, new_id: ComponentId) {
|
||||||
|
match self {
|
||||||
|
KeySelection::LoadingKeys {
|
||||||
|
ref mut progress_spinner,
|
||||||
|
..
|
||||||
|
} => progress_spinner.set_id(new_id),
|
||||||
|
KeySelection::Error { ref mut id, .. } => *id = new_id,
|
||||||
|
KeySelection::Loaded { ref mut widget, .. } => widget.set_id(new_id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GpgComposeState {
|
||||||
|
pub sign_mail: ToggleFlag,
|
||||||
|
pub encrypt_mail: ToggleFlag,
|
||||||
|
pub encrypt_keys: Vec<melib::gpgme::Key>,
|
||||||
|
pub encrypt_for_self: bool,
|
||||||
|
pub sign_keys: Vec<melib::gpgme::Key>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpgComposeState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
GpgComposeState {
|
||||||
|
sign_mail: ToggleFlag::Unset,
|
||||||
|
encrypt_mail: ToggleFlag::Unset,
|
||||||
|
encrypt_keys: vec![],
|
||||||
|
encrypt_for_self: true,
|
||||||
|
sign_keys: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1391,7 +1391,7 @@ impl Listing {
|
||||||
dirty: true,
|
dirty: true,
|
||||||
cursor_pos: (0, 0),
|
cursor_pos: (0, 0),
|
||||||
menu_cursor_pos: (0, 0),
|
menu_cursor_pos: (0, 0),
|
||||||
startup_checks_rate: RateLimit::new(2, 1000),
|
startup_checks_rate: RateLimit::new(2, 1000, context.job_executor.clone()),
|
||||||
theme_default: conf::value(context, "theme_default"),
|
theme_default: conf::value(context, "theme_default"),
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
show_divider: false,
|
show_divider: false,
|
||||||
|
|
|
@ -47,9 +47,9 @@ mod dbus {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DbusNotifications {
|
impl DbusNotifications {
|
||||||
pub fn new() -> Self {
|
pub fn new(context: &Context) -> Self {
|
||||||
DbusNotifications {
|
DbusNotifications {
|
||||||
rate_limit: RateLimit::new(1000, 1000),
|
rate_limit: RateLimit::new(1000, 1000, context.job_executor.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ impl fmt::Display for StatusBar {
|
||||||
|
|
||||||
impl StatusBar {
|
impl StatusBar {
|
||||||
pub fn new(context: &Context, container: Box<dyn Component>) -> Self {
|
pub fn new(context: &Context, container: Box<dyn Component>) -> Self {
|
||||||
let mut progress_spinner = ProgressSpinner::new(19);
|
let mut progress_spinner = ProgressSpinner::new(19, context);
|
||||||
match context.settings.terminal.progress_spinner_sequence.as_ref() {
|
match context.settings.terminal.progress_spinner_sequence.as_ref() {
|
||||||
Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => {
|
Some(conf::terminal::ProgressSpinnerSequence::Integer(k)) => {
|
||||||
progress_spinner.set_kind(*k);
|
progress_spinner.set_kind(*k);
|
||||||
|
|
|
@ -1154,7 +1154,7 @@ impl ScrollBar {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProgressSpinner {
|
pub struct ProgressSpinner {
|
||||||
timer: crate::timer::PosixTimer,
|
timer: crate::jobs::Timer,
|
||||||
stage: usize,
|
stage: usize,
|
||||||
pub kind: std::result::Result<usize, Vec<String>>,
|
pub kind: std::result::Result<usize, Vec<String>>,
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
|
@ -1201,13 +1201,11 @@ impl ProgressSpinner {
|
||||||
|
|
||||||
const INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
|
const INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
|
||||||
|
|
||||||
pub fn new(kind: usize) -> Self {
|
pub fn new(kind: usize, context: &Context) -> Self {
|
||||||
let timer = crate::timer::PosixTimer::new_with_signal(
|
let timer = context
|
||||||
std::time::Duration::from_millis(0),
|
.job_executor
|
||||||
std::time::Duration::from_millis(0),
|
.clone()
|
||||||
nix::sys::signal::Signal::SIGALRM,
|
.create_timer(Self::INTERVAL, Self::INTERVAL);
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let kind = kind % Self::KINDS.len();
|
let kind = kind % Self::KINDS.len();
|
||||||
let width = Self::KINDS[kind]
|
let width = Self::KINDS[kind]
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1255,19 +1253,13 @@ impl ProgressSpinner {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.active = true;
|
self.active = true;
|
||||||
self.timer
|
self.timer.rearm();
|
||||||
.set_interval(Self::INTERVAL)
|
|
||||||
.set_value(Self::INTERVAL)
|
|
||||||
.rearm()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
pub fn stop(&mut self) {
|
||||||
self.active = false;
|
self.active = false;
|
||||||
self.stage = 0;
|
self.stage = 0;
|
||||||
self.timer
|
self.timer.disable();
|
||||||
.set_interval(std::time::Duration::from_millis(0))
|
|
||||||
.set_value(std::time::Duration::from_millis(0))
|
|
||||||
.rearm()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1309,7 +1301,7 @@ impl Component for ProgressSpinner {
|
||||||
|
|
||||||
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
|
fn process_event(&mut self, event: &mut UIEvent, _context: &mut Context) -> bool {
|
||||||
match event {
|
match event {
|
||||||
UIEvent::Timer(id) if *id == self.timer.si_value => {
|
UIEvent::Timer(id) if *id == self.timer.id() => {
|
||||||
match self.kind.as_ref() {
|
match self.kind.as_ref() {
|
||||||
Ok(kind) => {
|
Ok(kind) => {
|
||||||
self.stage = (self.stage + 1).wrapping_rem(Self::KINDS[*kind].len());
|
self.stage = (self.stage + 1).wrapping_rem(Self::KINDS[*kind].len());
|
||||||
|
|
141
src/jobs.rs
141
src/jobs.rs
|
@ -28,14 +28,15 @@
|
||||||
|
|
||||||
use melib::error::Result;
|
use melib::error::Result;
|
||||||
use melib::smol;
|
use melib::smol;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::panic::catch_unwind;
|
use std::panic::catch_unwind;
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::types::ThreadEvent;
|
use crate::types::{ThreadEvent, UIEvent};
|
||||||
use crossbeam::deque::{Injector, Stealer, Worker};
|
use crossbeam::deque::{Injector, Stealer, Worker};
|
||||||
use crossbeam::sync::{Parker, Unparker};
|
use crossbeam::sync::{Parker, Unparker};
|
||||||
use crossbeam::Sender;
|
use crossbeam::Sender;
|
||||||
|
@ -97,6 +98,7 @@ uuid_hash_type!(JobId);
|
||||||
pub struct MeliTask {
|
pub struct MeliTask {
|
||||||
task: AsyncTask,
|
task: AsyncTask,
|
||||||
id: JobId,
|
id: JobId,
|
||||||
|
timer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -105,6 +107,37 @@ pub struct JobExecutor {
|
||||||
workers: Vec<Stealer<MeliTask>>,
|
workers: Vec<Stealer<MeliTask>>,
|
||||||
sender: Sender<ThreadEvent>,
|
sender: Sender<ThreadEvent>,
|
||||||
parkers: Vec<Unparker>,
|
parkers: Vec<Unparker>,
|
||||||
|
timers: Arc<Mutex<HashMap<Uuid, TimerPrivate>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct TimerPrivate {
|
||||||
|
/// Interval for periodic timer.
|
||||||
|
interval: Duration,
|
||||||
|
/// Time until next expiration.
|
||||||
|
value: Duration,
|
||||||
|
active: bool,
|
||||||
|
handle: Option<async_task::JoinHandle<(), ()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Timer {
|
||||||
|
id: Uuid,
|
||||||
|
job_executor: Arc<JobExecutor>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timer {
|
||||||
|
pub fn id(&self) -> Uuid {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rearm(&self) {
|
||||||
|
self.job_executor.rearm(self.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable(&self) {
|
||||||
|
self.job_executor.disable_timer(self.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JobExecutor {
|
impl JobExecutor {
|
||||||
|
@ -116,6 +149,7 @@ impl JobExecutor {
|
||||||
workers: vec![],
|
workers: vec![],
|
||||||
parkers: vec![],
|
parkers: vec![],
|
||||||
sender,
|
sender,
|
||||||
|
timers: Arc::new(Mutex::new(HashMap::default())),
|
||||||
};
|
};
|
||||||
let mut workers = vec![];
|
let mut workers = vec![];
|
||||||
for _ in 0..num_cpus::get().max(1) {
|
for _ in 0..num_cpus::get().max(1) {
|
||||||
|
@ -146,10 +180,14 @@ impl JobExecutor {
|
||||||
parker.park_timeout(Duration::from_millis(100));
|
parker.park_timeout(Duration::from_millis(100));
|
||||||
let task = find_task(&local, &global, stealers.as_slice());
|
let task = find_task(&local, &global, stealers.as_slice());
|
||||||
if let Some(meli_task) = task {
|
if let Some(meli_task) = task {
|
||||||
let MeliTask { task, id } = meli_task;
|
let MeliTask { task, id, timer } = meli_task;
|
||||||
debug!("Worker {} got task {:?}", i, id);
|
if !timer {
|
||||||
|
debug!("Worker {} got task {:?}", i, id);
|
||||||
|
}
|
||||||
let _ = catch_unwind(|| task.run());
|
let _ = catch_unwind(|| task.run());
|
||||||
debug!("Worker {} returned after {:?}", i, id);
|
if !timer {
|
||||||
|
debug!("Worker {} returned after {:?}", i, id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -177,7 +215,13 @@ impl JobExecutor {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
},
|
||||||
move |task| injector.push(MeliTask { task, id: job_id }),
|
move |task| {
|
||||||
|
injector.push(MeliTask {
|
||||||
|
task,
|
||||||
|
id: job_id,
|
||||||
|
timer: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
(),
|
(),
|
||||||
);
|
);
|
||||||
task.schedule();
|
task.schedule();
|
||||||
|
@ -200,6 +244,91 @@ impl JobExecutor {
|
||||||
{
|
{
|
||||||
self.spawn_specialized(smol::unblock(move || futures::executor::block_on(future)))
|
self.spawn_specialized(smol::unblock(move || futures::executor::block_on(future)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_timer(self: Arc<JobExecutor>, interval: Duration, value: Duration) -> Timer {
|
||||||
|
let id = Uuid::new_v4();
|
||||||
|
let timer = TimerPrivate {
|
||||||
|
interval,
|
||||||
|
value,
|
||||||
|
active: true,
|
||||||
|
handle: None,
|
||||||
|
};
|
||||||
|
self.timers.lock().unwrap().insert(id, timer);
|
||||||
|
self.arm_timer(id, value);
|
||||||
|
Timer {
|
||||||
|
id,
|
||||||
|
job_executor: self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rearm(&self, timer_id: Uuid) {
|
||||||
|
let mut timers_lck = self.timers.lock().unwrap();
|
||||||
|
if let Some(timer) = timers_lck.get_mut(&timer_id) {
|
||||||
|
if let Some(handle) = timer.handle.take() {
|
||||||
|
handle.cancel();
|
||||||
|
}
|
||||||
|
let value = timer.value;
|
||||||
|
drop(timers_lck);
|
||||||
|
self.arm_timer(timer_id, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arm_timer(&self, id: Uuid, value: Duration) {
|
||||||
|
let job_id = JobId::new();
|
||||||
|
let sender = self.sender.clone();
|
||||||
|
let injector = self.global_queue.clone();
|
||||||
|
let timers = self.timers.clone();
|
||||||
|
let (task, handle) = async_task::spawn(
|
||||||
|
async move {
|
||||||
|
let mut value = value;
|
||||||
|
loop {
|
||||||
|
smol::Timer::after(value).await;
|
||||||
|
sender
|
||||||
|
.send(ThreadEvent::UIEvent(UIEvent::Timer(id)))
|
||||||
|
.unwrap();
|
||||||
|
if let Some(interval) = timers.lock().unwrap().get(&id).and_then(|timer| {
|
||||||
|
if timer.interval.as_millis() == 0 && timer.interval.as_secs() == 0 {
|
||||||
|
None
|
||||||
|
} else if timer.active {
|
||||||
|
Some(timer.interval)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
value = interval;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
move |task| {
|
||||||
|
injector.push(MeliTask {
|
||||||
|
task,
|
||||||
|
id: job_id,
|
||||||
|
timer: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
self.timers.lock().unwrap().entry(id).and_modify(|timer| {
|
||||||
|
timer.handle = Some(handle);
|
||||||
|
timer.active = true;
|
||||||
|
});
|
||||||
|
task.schedule();
|
||||||
|
for unparker in self.parkers.iter() {
|
||||||
|
unparker.unpark();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disable_timer(&self, id: Uuid) {
|
||||||
|
let mut timers_lck = self.timers.lock().unwrap();
|
||||||
|
if let Some(timer) = timers_lck.get_mut(&id) {
|
||||||
|
if let Some(handle) = timer.handle.take() {
|
||||||
|
handle.cancel();
|
||||||
|
}
|
||||||
|
timer.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type JobChannel<T> = oneshot::Receiver<T>;
|
pub type JobChannel<T> = oneshot::Receiver<T>;
|
||||||
|
|
|
@ -37,9 +37,6 @@ extern crate termion;
|
||||||
use melib::backends::imap::managesieve::new_managesieve_connection;
|
use melib::backends::imap::managesieve::new_managesieve_connection;
|
||||||
use melib::Result;
|
use melib::Result;
|
||||||
|
|
||||||
mod unix;
|
|
||||||
use unix::*;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod types;
|
pub mod types;
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
|
@ -327,7 +327,7 @@ impl State {
|
||||||
components: Vec::with_capacity(8),
|
components: Vec::with_capacity(8),
|
||||||
overlay: Vec::new(),
|
overlay: Vec::new(),
|
||||||
timer,
|
timer,
|
||||||
draw_rate_limit: RateLimit::new(1, 3),
|
draw_rate_limit: RateLimit::new(1, 3, job_executor.clone()),
|
||||||
draw_horizontal_segment_fn: if settings.terminal.use_color() {
|
draw_horizontal_segment_fn: if settings.terminal.use_color() {
|
||||||
State::draw_horizontal_segment
|
State::draw_horizontal_segment
|
||||||
} else {
|
} else {
|
||||||
|
@ -361,9 +361,6 @@ impl State {
|
||||||
receiver,
|
receiver,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
s.draw_rate_limit
|
|
||||||
.timer
|
|
||||||
.set_value(std::time::Duration::from_millis(3));
|
|
||||||
if s.context.settings.terminal.ascii_drawing {
|
if s.context.settings.terminal.ascii_drawing {
|
||||||
s.grid.set_ascii_drawing(true);
|
s.grid.set_ascii_drawing(true);
|
||||||
s.overlay_grid.set_ascii_drawing(true);
|
s.overlay_grid.set_ascii_drawing(true);
|
||||||
|
|
20
src/types.rs
20
src/types.rs
|
@ -36,9 +36,10 @@ mod helpers;
|
||||||
pub use self::helpers::*;
|
pub use self::helpers::*;
|
||||||
|
|
||||||
use super::command::Action;
|
use super::command::Action;
|
||||||
use super::jobs::JobId;
|
use super::jobs::{JobExecutor, JobId};
|
||||||
use super::terminal::*;
|
use super::terminal::*;
|
||||||
use crate::components::{Component, ComponentId};
|
use crate::components::{Component, ComponentId};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use melib::backends::{AccountHash, BackendEvent, MailboxHash};
|
use melib::backends::{AccountHash, BackendEvent, MailboxHash};
|
||||||
use melib::{EnvelopeHash, RefreshEvent, ThreadHash};
|
use melib::{EnvelopeHash, RefreshEvent, ThreadHash};
|
||||||
|
@ -144,7 +145,7 @@ pub enum UIEvent {
|
||||||
FinishedUIDialog(ComponentId, UIMessage),
|
FinishedUIDialog(ComponentId, UIMessage),
|
||||||
Callback(CallbackFn),
|
Callback(CallbackFn),
|
||||||
GlobalUIDialog(Box<dyn Component>),
|
GlobalUIDialog(Box<dyn Component>),
|
||||||
Timer(u8),
|
Timer(Uuid),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) -> () + Send + 'static>);
|
pub struct CallbackFn(pub Box<dyn FnOnce(&mut crate::Context) -> () + Send + 'static>);
|
||||||
|
@ -313,7 +314,7 @@ pub mod segment_tree {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RateLimit {
|
pub struct RateLimit {
|
||||||
last_tick: std::time::Instant,
|
last_tick: std::time::Instant,
|
||||||
pub timer: crate::timer::PosixTimer,
|
pub timer: crate::jobs::Timer,
|
||||||
rate: std::time::Duration,
|
rate: std::time::Duration,
|
||||||
reqs: u64,
|
reqs: u64,
|
||||||
millis: std::time::Duration,
|
millis: std::time::Duration,
|
||||||
|
@ -322,16 +323,13 @@ pub struct RateLimit {
|
||||||
|
|
||||||
//FIXME: tests.
|
//FIXME: tests.
|
||||||
impl RateLimit {
|
impl RateLimit {
|
||||||
pub fn new(reqs: u64, millis: u64) -> Self {
|
pub fn new(reqs: u64, millis: u64, job_executor: Arc<JobExecutor>) -> Self {
|
||||||
RateLimit {
|
RateLimit {
|
||||||
last_tick: std::time::Instant::now(),
|
last_tick: std::time::Instant::now(),
|
||||||
timer: crate::timer::PosixTimer::new_with_signal(
|
timer: job_executor.create_timer(
|
||||||
std::time::Duration::from_secs(0),
|
std::time::Duration::from_secs(0),
|
||||||
std::time::Duration::from_millis(millis),
|
std::time::Duration::from_millis(millis),
|
||||||
nix::sys::signal::Signal::SIGALRM,
|
),
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
|
|
||||||
rate: std::time::Duration::from_millis(millis / reqs),
|
rate: std::time::Duration::from_millis(millis / reqs),
|
||||||
reqs,
|
reqs,
|
||||||
millis: std::time::Duration::from_millis(millis),
|
millis: std::time::Duration::from_millis(millis),
|
||||||
|
@ -357,8 +355,8 @@ impl RateLimit {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn id(&self) -> u8 {
|
pub fn id(&self) -> Uuid {
|
||||||
self.timer.si_value
|
self.timer.id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
|
|
184
src/unix.rs
184
src/unix.rs
|
@ -1,184 +0,0 @@
|
||||||
/*
|
|
||||||
* meli
|
|
||||||
*
|
|
||||||
* Copyright 2017-2018 Manos Pitsidianakis
|
|
||||||
*
|
|
||||||
* This file is part of meli.
|
|
||||||
*
|
|
||||||
* meli is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* meli is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*! UNIX and POSIX interfaces.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub mod timer {
|
|
||||||
//! POSIX timers
|
|
||||||
//!
|
|
||||||
//! # Example usage
|
|
||||||
//! ```no_run
|
|
||||||
//! let timer = crate::timer::PosixTimer::new_with_signal(
|
|
||||||
//! std::time::Duration::from_secs(0),
|
|
||||||
//! std::time::Duration::from_secs(1),
|
|
||||||
//! nix::sys::signal::Signal::SIGALRM,
|
|
||||||
//! )
|
|
||||||
//! .unwrap();
|
|
||||||
//!
|
|
||||||
//! // some time passes, we should receive and handle the SIGALRM
|
|
||||||
//! // The timer remains unarmed since the interval given was zero, until we rearm it explicitly.
|
|
||||||
//! timer.rearm();
|
|
||||||
//! ```
|
|
||||||
use libc::clockid_t;
|
|
||||||
use libc::sigevent;
|
|
||||||
use libc::{itimerspec, timespec};
|
|
||||||
use melib::{MeliError, Result};
|
|
||||||
use nix::sys::signal::{SigEvent, SigevNotify};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
thread_local!(static TIMER_IDS: RefCell<u8> = RefCell::new(0));
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub type timer_t = libc::intptr_t;
|
|
||||||
|
|
||||||
#[link(name = "rt")]
|
|
||||||
extern "C" {
|
|
||||||
fn timer_create(clockid: clockid_t, sevp: *const sigevent, timerid: *mut timer_t) -> i32;
|
|
||||||
fn timer_settime(
|
|
||||||
timerid: timer_t,
|
|
||||||
flags: i32,
|
|
||||||
new_value: *const itimerspec,
|
|
||||||
old_value: *const itimerspec,
|
|
||||||
) -> i32;
|
|
||||||
|
|
||||||
fn timer_delete(timerid: timer_t) -> i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PosixTimer {
|
|
||||||
timer_id: timer_t,
|
|
||||||
/// Interval for periodic timer.
|
|
||||||
interval: Duration,
|
|
||||||
/// Time until next expiration.
|
|
||||||
value: Duration,
|
|
||||||
/// `si_value` is a byte accessible from the signal handler when it receives signals from this timer.
|
|
||||||
pub si_value: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for PosixTimer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
timer_delete(self.timer_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PosixTimer {
|
|
||||||
/// Arm without changing interval and value.
|
|
||||||
pub fn rearm(&self) {
|
|
||||||
let spec = itimerspec {
|
|
||||||
it_interval: timespec {
|
|
||||||
tv_sec: self.interval.as_secs().try_into().unwrap_or(0),
|
|
||||||
tv_nsec: self.interval.subsec_nanos().try_into().unwrap_or(0),
|
|
||||||
},
|
|
||||||
it_value: timespec {
|
|
||||||
tv_sec: self.value.as_secs().try_into().unwrap_or(0),
|
|
||||||
tv_nsec: self.value.subsec_nanos().try_into().unwrap_or(0),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let ret =
|
|
||||||
unsafe { timer_settime(self.timer_id, 0, &spec as *const _, std::ptr::null_mut()) };
|
|
||||||
if ret != 0 {
|
|
||||||
match ret {
|
|
||||||
libc::EFAULT => {
|
|
||||||
panic!(
|
|
||||||
"EFAULT: new_value, old_value, or curr_value is not a valid pointer."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
libc::EINVAL => {
|
|
||||||
panic!("EINVAL: timerid is invalid.");
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets value without arming timer
|
|
||||||
pub fn set_value(&mut self, value: Duration) -> &mut Self {
|
|
||||||
self.value = value;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets interval without arming timer
|
|
||||||
pub fn set_interval(&mut self, interval: Duration) -> &mut Self {
|
|
||||||
self.interval = interval;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_signal(
|
|
||||||
interval: Duration,
|
|
||||||
value: Duration,
|
|
||||||
signal: nix::sys::signal::Signal,
|
|
||||||
) -> Result<PosixTimer> {
|
|
||||||
let mut timer_id = Default::default();
|
|
||||||
|
|
||||||
let mut si_value = 0;
|
|
||||||
TIMER_IDS.with(|t| {
|
|
||||||
si_value = *t.borrow_mut();
|
|
||||||
*t.borrow_mut() += 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
let sigev_notify = SigevNotify::SigevSignal {
|
|
||||||
signal,
|
|
||||||
si_value: si_value as isize,
|
|
||||||
};
|
|
||||||
let event = SigEvent::new(sigev_notify);
|
|
||||||
|
|
||||||
let ret = unsafe {
|
|
||||||
timer_create(
|
|
||||||
libc::CLOCK_MONOTONIC,
|
|
||||||
&event.sigevent() as *const _,
|
|
||||||
&mut timer_id as *mut _,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if ret != 0 {
|
|
||||||
match ret {
|
|
||||||
libc::EAGAIN => {
|
|
||||||
return Err(MeliError::new(
|
|
||||||
"Temporary error during kernel allocation of timer",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
libc::EINVAL => {
|
|
||||||
panic!("Clock ID, sigev_notify, sigev_signo, or sigev_notify_thread_id is invalid.");
|
|
||||||
}
|
|
||||||
libc::ENOMEM => {
|
|
||||||
return Err(MeliError::new("Could not allocate memory."));
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ret = PosixTimer {
|
|
||||||
timer_id,
|
|
||||||
interval,
|
|
||||||
value,
|
|
||||||
si_value,
|
|
||||||
};
|
|
||||||
|
|
||||||
ret.rearm();
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue