mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-21 11:31:15 -03:00
Rewrite wait handles and wait handle store in Rust
This commit is contained in:
36
fish-rust/Cargo.lock
generated
36
fish-rust/Cargo.lock
generated
@@ -28,6 +28,17 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
@@ -355,6 +366,7 @@ dependencies = [
|
||||
"errno",
|
||||
"inventory",
|
||||
"libc",
|
||||
"lru",
|
||||
"miette",
|
||||
"nix",
|
||||
"num-traits",
|
||||
@@ -405,7 +417,16 @@ version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"ahash 0.7.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -436,7 +457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@@ -541,6 +562,15 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03f1160296536f10c833a82dca22267d5486734230d47bf00bf435885814ba1e"
|
||||
dependencies = [
|
||||
"hashbrown 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
@@ -984,7 +1014,7 @@ version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
"regex",
|
||||
]
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ once_cell = "1.17.0"
|
||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||
unixstring = "0.2.7"
|
||||
widestring = "1.0.2"
|
||||
lru = "0.10.0"
|
||||
|
||||
[build-dependencies]
|
||||
autocxx-build = "0.23.1"
|
||||
|
||||
@@ -35,6 +35,7 @@ fn main() -> miette::Result<()> {
|
||||
"src/tokenizer.rs",
|
||||
"src/topic_monitor.rs",
|
||||
"src/util.rs",
|
||||
"src/wait_handle.rs",
|
||||
"src/builtins/shared.rs",
|
||||
];
|
||||
cxx_build::bridges(&source_files)
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
builtin_missing_argument, builtin_print_help, builtin_unknown_option, io_streams_t,
|
||||
STATUS_CMD_OK, STATUS_INVALID_ARGS,
|
||||
};
|
||||
use crate::ffi::{job_t, parser_t, proc_wait_any, wait_handle_ref_t, Repin};
|
||||
use crate::ffi::{job_t, parser_t, proc_wait_any, Repin};
|
||||
use crate::signal::sigchecker_t;
|
||||
use crate::wait_handle::{WaitHandleRef, WaitHandleStore};
|
||||
use crate::wchar::{widestrs, wstr};
|
||||
use crate::wgetopt::{wgetopter_t, wopt, woption, woption_argument_t};
|
||||
use crate::wutil::{self, fish_wcstoi, wgettext_fmt};
|
||||
@@ -16,14 +17,10 @@ fn can_wait_on_job(j: &cxx::SharedPtr<job_t>) -> bool {
|
||||
}
|
||||
|
||||
/// \return true if a wait handle matches a pid or a process name.
|
||||
/// For convenience, this returns false if the wait handle is null.
|
||||
fn wait_handle_matches(query: WaitHandleQuery, wh: &wait_handle_ref_t) -> bool {
|
||||
if wh.is_null() {
|
||||
return false;
|
||||
}
|
||||
fn wait_handle_matches(query: WaitHandleQuery, wh: &WaitHandleRef) -> bool {
|
||||
match query {
|
||||
WaitHandleQuery::Pid(pid) => wh.get_pid().0 == pid,
|
||||
WaitHandleQuery::ProcName(proc_name) => proc_name == wh.get_base_name(),
|
||||
WaitHandleQuery::Pid(pid) => wh.pid == pid,
|
||||
WaitHandleQuery::ProcName(proc_name) => proc_name == wh.base_name,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,16 +29,6 @@ fn iswnumeric(s: &wstr) -> bool {
|
||||
s.chars().all(|c| c.is_ascii_digit())
|
||||
}
|
||||
|
||||
// Hack to copy wait handles into a vector.
|
||||
fn get_wait_handle_list(parser: &parser_t) -> Vec<wait_handle_ref_t> {
|
||||
let mut handles = Vec::new();
|
||||
let whs = parser.get_wait_handles1();
|
||||
for idx in 0..whs.size() {
|
||||
handles.push(whs.get(idx));
|
||||
}
|
||||
handles
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum WaitHandleQuery<'a> {
|
||||
Pid(pid_t),
|
||||
@@ -53,15 +40,16 @@ enum WaitHandleQuery<'a> {
|
||||
/// \return true if we found a matching job (even if not waitable), false if not.
|
||||
fn find_wait_handles(
|
||||
query: WaitHandleQuery<'_>,
|
||||
parser: &parser_t,
|
||||
handles: &mut Vec<wait_handle_ref_t>,
|
||||
parser: &mut parser_t,
|
||||
handles: &mut Vec<WaitHandleRef>,
|
||||
) -> bool {
|
||||
// Has a job already completed?
|
||||
// TODO: we can avoid traversing this list if searching by pid.
|
||||
let mut matched = false;
|
||||
for wh in get_wait_handle_list(parser) {
|
||||
if wait_handle_matches(query, &wh) {
|
||||
handles.push(wh);
|
||||
let wait_handles: &mut WaitHandleStore = parser.get_wait_handles_mut();
|
||||
for wh in wait_handles.iter() {
|
||||
if wait_handle_matches(query, wh) {
|
||||
handles.push(wh.clone());
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
@@ -71,11 +59,17 @@ fn find_wait_handles(
|
||||
// We want to set 'matched' to true if we could have matched, even if the job was stopped.
|
||||
let provide_handle = can_wait_on_job(j);
|
||||
for proc in j.get_procs() {
|
||||
let wh = proc.pin_mut().make_wait_handle(j.get_internal_job_id());
|
||||
let wh = proc
|
||||
.pin_mut()
|
||||
.unpin()
|
||||
.make_wait_handle(j.get_internal_job_id());
|
||||
let Some(wh) = wh else {
|
||||
continue;
|
||||
};
|
||||
if wait_handle_matches(query, &wh) {
|
||||
matched = true;
|
||||
if provide_handle {
|
||||
handles.push(wh);
|
||||
handles.push(wh.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,13 +77,9 @@ fn find_wait_handles(
|
||||
matched
|
||||
}
|
||||
|
||||
fn get_all_wait_handles(parser: &parser_t) -> Vec<wait_handle_ref_t> {
|
||||
let mut result = Vec::new();
|
||||
fn get_all_wait_handles(parser: &parser_t) -> Vec<WaitHandleRef> {
|
||||
// Get wait handles for reaped jobs.
|
||||
let wait_handles = parser.get_wait_handles1();
|
||||
for idx in 0..wait_handles.size() {
|
||||
result.push(wait_handles.get(idx));
|
||||
}
|
||||
let mut result = parser.get_wait_handles().get_list();
|
||||
|
||||
// Get wait handles for running jobs.
|
||||
for j in parser.get_jobs() {
|
||||
@@ -97,9 +87,8 @@ fn get_all_wait_handles(parser: &parser_t) -> Vec<wait_handle_ref_t> {
|
||||
continue;
|
||||
}
|
||||
for proc_ptr in j.get_procs().iter_mut() {
|
||||
let proc = proc_ptr.pin_mut();
|
||||
let wh = proc.make_wait_handle(j.get_internal_job_id());
|
||||
if !wh.is_null() {
|
||||
let proc = proc_ptr.pin_mut().unpin();
|
||||
if let Some(wh) = proc.make_wait_handle(j.get_internal_job_id()) {
|
||||
result.push(wh);
|
||||
}
|
||||
}
|
||||
@@ -107,7 +96,7 @@ fn get_all_wait_handles(parser: &parser_t) -> Vec<wait_handle_ref_t> {
|
||||
result
|
||||
}
|
||||
|
||||
fn is_completed(wh: &wait_handle_ref_t) -> bool {
|
||||
fn is_completed(wh: &WaitHandleRef) -> bool {
|
||||
wh.is_completed()
|
||||
}
|
||||
|
||||
@@ -116,7 +105,7 @@ fn is_completed(wh: &wait_handle_ref_t) -> bool {
|
||||
/// \return a status code.
|
||||
fn wait_for_completion(
|
||||
parser: &mut parser_t,
|
||||
whs: &[wait_handle_ref_t],
|
||||
whs: &[WaitHandleRef],
|
||||
any_flag: bool,
|
||||
) -> Option<c_int> {
|
||||
if whs.is_empty() {
|
||||
@@ -135,7 +124,7 @@ fn wait_for_completion(
|
||||
// Remove completed wait handles (at most 1 if any_flag is set).
|
||||
for wh in whs {
|
||||
if is_completed(wh) {
|
||||
parser.pin().get_wait_handles().remove(wh);
|
||||
parser.get_wait_handles_mut().remove(wh);
|
||||
if any_flag {
|
||||
break;
|
||||
}
|
||||
@@ -203,7 +192,7 @@ pub fn wait(
|
||||
}
|
||||
|
||||
// Get the list of wait handles for our waiting.
|
||||
let mut wait_handles: Vec<wait_handle_ref_t> = Vec::new();
|
||||
let mut wait_handles: Vec<WaitHandleRef> = Vec::new();
|
||||
for i in w.woptind..argc {
|
||||
if iswnumeric(argv[i]) {
|
||||
// argument is pid
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
use ::std::pin::Pin;
|
||||
#[rustfmt::skip]
|
||||
use ::std::slice;
|
||||
pub use crate::wait_handle::{
|
||||
WaitHandleRef, WaitHandleRefFFI, WaitHandleStore, WaitHandleStoreFFI,
|
||||
};
|
||||
use crate::wchar::wstr;
|
||||
use autocxx::prelude::*;
|
||||
use cxx::SharedPtr;
|
||||
@@ -33,6 +36,10 @@
|
||||
#include "wutil.h"
|
||||
#include "termsize.h"
|
||||
|
||||
// We need to block these types so when exposing C++ to Rust.
|
||||
block!("WaitHandleStoreFFI")
|
||||
block!("WaitHandleRefFFI")
|
||||
|
||||
safety!(unsafe_ffi)
|
||||
|
||||
generate_pod!("wcharz_t")
|
||||
@@ -57,6 +64,7 @@
|
||||
|
||||
generate!("block_t")
|
||||
generate!("parser_t")
|
||||
|
||||
generate!("job_t")
|
||||
generate!("process_t")
|
||||
generate!("library_data_t")
|
||||
@@ -76,9 +84,6 @@
|
||||
generate!("builtin_print_help")
|
||||
generate!("builtin_print_error_trailer")
|
||||
|
||||
generate!("wait_handle_t")
|
||||
generate!("wait_handle_store_t")
|
||||
|
||||
generate!("escape_string")
|
||||
generate!("sig2wcs")
|
||||
generate!("wcs2sig")
|
||||
@@ -107,6 +112,18 @@
|
||||
}
|
||||
|
||||
impl parser_t {
|
||||
pub fn get_wait_handles_mut(&mut self) -> &mut WaitHandleStore {
|
||||
let ptr = self.get_wait_handles_void() as *mut Box<WaitHandleStoreFFI>;
|
||||
assert!(!ptr.is_null());
|
||||
unsafe { (*ptr).from_ffi_mut() }
|
||||
}
|
||||
|
||||
pub fn get_wait_handles(&self) -> &WaitHandleStore {
|
||||
let ptr = self.get_wait_handles_void() as *const Box<WaitHandleStoreFFI>;
|
||||
assert!(!ptr.is_null());
|
||||
unsafe { (*ptr).from_ffi() }
|
||||
}
|
||||
|
||||
pub fn get_block_at_index(&self, i: usize) -> Option<&block_t> {
|
||||
let b = self.block_at_index(i);
|
||||
unsafe { b.as_ref() }
|
||||
@@ -145,6 +162,30 @@ pub fn get_procs(&self) -> &mut [UniquePtr<process_t>] {
|
||||
}
|
||||
}
|
||||
|
||||
impl process_t {
|
||||
/// \return the wait handle for the process, if it exists.
|
||||
pub fn get_wait_handle(&self) -> Option<WaitHandleRef> {
|
||||
let handle_ptr = self.get_wait_handle_void() as *const Box<WaitHandleRefFFI>;
|
||||
if handle_ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
let handle: &WaitHandleRefFFI = unsafe { &*handle_ptr };
|
||||
Some(handle.from_ffi().clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// \return the wait handle for the process, creating it if necessary.
|
||||
pub fn make_wait_handle(&mut self, jid: u64) -> Option<WaitHandleRef> {
|
||||
let handle_ref = self.pin().make_wait_handle_void(jid) as *const Box<WaitHandleRefFFI>;
|
||||
if handle_ref.is_null() {
|
||||
None
|
||||
} else {
|
||||
let handle: &WaitHandleRefFFI = unsafe { &*handle_ref };
|
||||
Some(handle.from_ffi().clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allow wcharz_t to be "into" wstr.
|
||||
impl From<wcharz_t> for &wchar::wstr {
|
||||
fn from(w: wcharz_t) -> Self {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
mod tokenizer;
|
||||
mod topic_monitor;
|
||||
mod util;
|
||||
mod wait_handle;
|
||||
mod wchar;
|
||||
mod wchar_ext;
|
||||
mod wchar_ffi;
|
||||
|
||||
270
fish-rust/src/wait_handle.rs
Normal file
270
fish-rust/src/wait_handle.rs
Normal file
@@ -0,0 +1,270 @@
|
||||
use crate::wchar::WString;
|
||||
use crate::wchar_ffi::WCharFromFFI;
|
||||
use cxx::CxxWString;
|
||||
use libc::pid_t;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cxx::bridge]
|
||||
mod wait_handle_ffi {
|
||||
extern "Rust" {
|
||||
type WaitHandleRefFFI;
|
||||
fn new_wait_handle_ffi(
|
||||
pid: i32,
|
||||
internal_job_id: u64,
|
||||
base_name: &CxxWString,
|
||||
) -> Box<WaitHandleRefFFI>;
|
||||
fn set_status_and_complete(self: &mut WaitHandleRefFFI, status: i32);
|
||||
|
||||
type WaitHandleStoreFFI;
|
||||
fn new_wait_handle_store_ffi() -> Box<WaitHandleStoreFFI>;
|
||||
fn remove_by_pid(self: &mut WaitHandleStoreFFI, pid: i32);
|
||||
fn get_job_id_by_pid(self: &WaitHandleStoreFFI, pid: i32) -> u64;
|
||||
|
||||
fn try_get_status_and_job_id(
|
||||
self: &WaitHandleStoreFFI,
|
||||
pid: i32,
|
||||
only_if_complete: bool,
|
||||
status: &mut i32,
|
||||
job_id: &mut u64,
|
||||
) -> bool;
|
||||
|
||||
fn add(self: &mut WaitHandleStoreFFI, wh: *const Box<WaitHandleRefFFI>);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitHandleRefFFI(WaitHandleRef);
|
||||
|
||||
impl WaitHandleRefFFI {
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn from_ffi(&self) -> &WaitHandleRef {
|
||||
&self.0
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn from_ffi_mut(&mut self) -> &mut WaitHandleRef {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
fn set_status_and_complete(self: &mut WaitHandleRefFFI, status: i32) {
|
||||
let wh = self.from_ffi();
|
||||
assert!(!wh.is_completed(), "wait handle already completed");
|
||||
wh.status.set(Some(status));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitHandleStoreFFI(WaitHandleStore);
|
||||
|
||||
impl WaitHandleStoreFFI {
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn from_ffi_mut(&mut self) -> &mut WaitHandleStore {
|
||||
&mut self.0
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn from_ffi(&self) -> &WaitHandleStore {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// \return the job ID for a pid, or 0 if None.
|
||||
fn get_job_id_by_pid(&self, pid: i32) -> u64 {
|
||||
self.from_ffi()
|
||||
.get_by_pid(pid)
|
||||
.map(|wh| wh.internal_job_id)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Try getting the status and job ID of a job.
|
||||
/// \return true if the job was found.
|
||||
/// If only_if_complete is true, then only return true if the job is completed.
|
||||
fn try_get_status_and_job_id(
|
||||
self: &WaitHandleStoreFFI,
|
||||
pid: i32,
|
||||
only_if_complete: bool,
|
||||
status: &mut i32,
|
||||
job_id: &mut u64,
|
||||
) -> bool {
|
||||
let whs = self.from_ffi();
|
||||
let Some(wh) = whs.get_by_pid(pid) else {
|
||||
return false;
|
||||
};
|
||||
if only_if_complete && !wh.is_completed() {
|
||||
return false;
|
||||
}
|
||||
*status = wh.status.get().unwrap_or(0);
|
||||
*job_id = wh.internal_job_id;
|
||||
true
|
||||
}
|
||||
|
||||
/// Remove the wait handle for a pid, if present in this store.
|
||||
fn remove_by_pid(&mut self, pid: i32) {
|
||||
self.from_ffi_mut().remove_by_pid(pid);
|
||||
}
|
||||
|
||||
fn add(self: &mut WaitHandleStoreFFI, wh: *const Box<WaitHandleRefFFI>) {
|
||||
if wh.is_null() {
|
||||
return;
|
||||
}
|
||||
let wh = unsafe { (*wh).from_ffi() };
|
||||
self.from_ffi_mut().add(wh.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn new_wait_handle_store_ffi() -> Box<WaitHandleStoreFFI> {
|
||||
Box::new(WaitHandleStoreFFI(WaitHandleStore::new()))
|
||||
}
|
||||
|
||||
fn new_wait_handle_ffi(
|
||||
pid: i32,
|
||||
internal_job_id: u64,
|
||||
base_name: &CxxWString,
|
||||
) -> Box<WaitHandleRefFFI> {
|
||||
Box::new(WaitHandleRefFFI(WaitHandle::new(
|
||||
pid as pid_t,
|
||||
internal_job_id,
|
||||
base_name.from_ffi(),
|
||||
)))
|
||||
}
|
||||
|
||||
pub type InternalJobId = u64;
|
||||
|
||||
/// The bits of a job necessary to support 'wait' and '--on-process-exit'.
|
||||
/// This may outlive the job.
|
||||
pub struct WaitHandle {
|
||||
/// The pid of this process.
|
||||
pub pid: pid_t,
|
||||
|
||||
/// The internal job id of the job which contained this process.
|
||||
pub internal_job_id: InternalJobId,
|
||||
|
||||
/// The "base name" of this process.
|
||||
/// For example if the process is "/bin/sleep" then this will be 'sleep'.
|
||||
pub base_name: WString,
|
||||
|
||||
/// The status, if completed; None if not completed.
|
||||
status: Cell<Option<i32>>,
|
||||
}
|
||||
|
||||
impl WaitHandle {
|
||||
/// \return true if this wait handle is completed.
|
||||
pub fn is_completed(&self) -> bool {
|
||||
self.status.get().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl WaitHandle {
|
||||
/// Construct from a pid, job id, and base name.
|
||||
pub fn new(pid: pid_t, internal_job_id: InternalJobId, base_name: WString) -> WaitHandleRef {
|
||||
Rc::new(WaitHandle {
|
||||
pid,
|
||||
internal_job_id,
|
||||
base_name,
|
||||
status: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub type WaitHandleRef = Rc<WaitHandle>;
|
||||
|
||||
const WAIT_HANDLE_STORE_DEFAULT_LIMIT: usize = 1024;
|
||||
|
||||
/// Support for storing a list of wait handles, with a max limit set at initialization.
|
||||
/// Note this class is not safe for concurrent access.
|
||||
pub struct WaitHandleStore {
|
||||
// Map from pid to wait handles.
|
||||
cache: lru::LruCache<pid_t, WaitHandleRef>,
|
||||
}
|
||||
|
||||
impl WaitHandleStore {
|
||||
/// Construct with the default capacity.
|
||||
pub fn new() -> WaitHandleStore {
|
||||
Self::new_with_capacity(WAIT_HANDLE_STORE_DEFAULT_LIMIT)
|
||||
}
|
||||
|
||||
pub fn new_with_capacity(capacity: usize) -> WaitHandleStore {
|
||||
let capacity = std::num::NonZeroUsize::new(capacity).unwrap();
|
||||
WaitHandleStore {
|
||||
cache: lru::LruCache::new(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a wait handle to the store. This may remove the oldest handle, if our limit is exceeded.
|
||||
/// It may also remove any existing handle with that pid.
|
||||
pub fn add(&mut self, wh: WaitHandleRef) {
|
||||
self.cache.put(wh.pid, wh);
|
||||
}
|
||||
|
||||
/// \return the wait handle for a pid, or None if there is none.
|
||||
/// This is a fast lookup.
|
||||
pub fn get_by_pid(&self, pid: pid_t) -> Option<WaitHandleRef> {
|
||||
self.cache.peek(&pid).cloned()
|
||||
}
|
||||
|
||||
/// Remove a given wait handle, if present in this store.
|
||||
pub fn remove(&mut self, wh: &WaitHandleRef) {
|
||||
// Note: this differs from remove_by_pid because we verify that the handle is the same.
|
||||
if let Some(key) = self.cache.peek(&wh.pid) {
|
||||
if Rc::ptr_eq(key, wh) {
|
||||
self.cache.pop(&wh.pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove the wait handle for a pid, if present in this store.
|
||||
pub fn remove_by_pid(&mut self, pid: pid_t) {
|
||||
self.cache.pop(&pid);
|
||||
}
|
||||
|
||||
/// Iterate over wait handles.
|
||||
pub fn iter(&self) -> impl Iterator<Item = &WaitHandleRef> {
|
||||
self.cache.iter().map(|(_, wh)| wh)
|
||||
}
|
||||
|
||||
/// Copy out the list of all wait handles, returning the most-recently-used first.
|
||||
pub fn get_list(&self) -> Vec<WaitHandleRef> {
|
||||
self.cache.iter().map(|(_, wh)| wh.clone()).collect()
|
||||
}
|
||||
|
||||
/// Convenience to return the size, for testing.
|
||||
pub fn size(&self) -> usize {
|
||||
self.cache.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wait_handles() {
|
||||
use crate::wchar::L;
|
||||
|
||||
let limit: usize = 4;
|
||||
let mut whs = WaitHandleStore::new_with_capacity(limit);
|
||||
assert_eq!(whs.size(), 0);
|
||||
|
||||
assert!(whs.get_by_pid(5).is_none());
|
||||
|
||||
// Duplicate pids drop oldest.
|
||||
whs.add(WaitHandle::new(5, 0, L!("first").to_owned()));
|
||||
whs.add(WaitHandle::new(5, 0, L!("second").to_owned()));
|
||||
assert_eq!(whs.size(), 1);
|
||||
assert_eq!(whs.get_by_pid(5).unwrap().base_name, "second");
|
||||
|
||||
whs.remove_by_pid(123);
|
||||
assert_eq!(whs.size(), 1);
|
||||
whs.remove_by_pid(5);
|
||||
assert_eq!(whs.size(), 0);
|
||||
|
||||
// Test evicting oldest.
|
||||
whs.add(WaitHandle::new(1, 0, L!("1").to_owned()));
|
||||
whs.add(WaitHandle::new(2, 0, L!("2").to_owned()));
|
||||
whs.add(WaitHandle::new(3, 0, L!("3").to_owned()));
|
||||
whs.add(WaitHandle::new(4, 0, L!("4").to_owned()));
|
||||
whs.add(WaitHandle::new(5, 0, L!("5").to_owned()));
|
||||
assert_eq!(whs.size(), 4);
|
||||
|
||||
let entries = whs.get_list();
|
||||
let mut iter = entries.iter();
|
||||
assert_eq!(iter.next().unwrap().base_name, "5");
|
||||
assert_eq!(iter.next().unwrap().base_name, "4");
|
||||
assert_eq!(iter.next().unwrap().base_name, "3");
|
||||
assert_eq!(iter.next().unwrap().base_name, "2");
|
||||
assert!(iter.next().is_none());
|
||||
}
|
||||
Reference in New Issue
Block a user