fix(service): Switch from flume to postage to fix out-of-order communication

This commit is contained in:
Michael Aaron Murphy 2021-08-17 12:44:41 +02:00
parent 490c6a6e8b
commit 8b4fbf441f
12 changed files with 156 additions and 208 deletions

173
Cargo.lock generated
View file

@ -144,6 +144,15 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "atomic"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3410529e8288c463bedb5930f82833bc0c90e5d2fe639a56582a4d09220b281"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.0.0" version = "1.0.0"
@ -182,12 +191,6 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "bumpalo"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
@ -265,6 +268,26 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "crossbeam-queue"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
dependencies = [
"cfg-if 1.0.0",
"lazy_static",
]
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.13.0" version = "0.13.0"
@ -367,19 +390,6 @@ dependencies = [
"instant", "instant",
] ]
[[package]]
name = "flume"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddad16e8529759736a9ce4cdf078ed702e45d3c5b0474a1c65f5149e9fa7f1eb"
dependencies = [
"futures-core",
"futures-sink",
"nanorand",
"pin-project 1.0.8",
"spinning_top",
]
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -552,10 +562,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"js-sys",
"libc", "libc",
"wasi", "wasi",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -579,15 +587,6 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
version = "0.3.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce791b7ca6638aae45be056e068fc756d871eb3b3b10b8efa62d1c9cec616752"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -600,15 +599,6 @@ version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
[[package]]
name = "lock_api"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
dependencies = [
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.14" version = "0.4.14"
@ -639,15 +629,6 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "nanorand"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac1378b66f7c93a1c0f8464a19bf47df8795083842e5090f4b7305973d5a22d0"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "nb-connect" name = "nb-connect"
version = "1.2.0" version = "1.2.0"
@ -777,6 +758,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "pollster"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cce106fd2646acbe31a0e4006f75779d535c26a44f153ada196e9edcfc6d944"
[[package]] [[package]]
name = "pop-launcher" name = "pop-launcher"
version = "1.0.0" version = "1.0.0"
@ -805,13 +792,13 @@ dependencies = [
name = "pop-launcher-plugins" name = "pop-launcher-plugins"
version = "1.0.0" version = "1.0.0"
dependencies = [ dependencies = [
"flume",
"fork", "fork",
"freedesktop-desktop-entry", "freedesktop-desktop-entry",
"futures-lite", "futures-lite",
"futures_codec", "futures_codec",
"new_mime_guess", "new_mime_guess",
"pop-launcher", "pop-launcher",
"postage",
"ron", "ron",
"serde", "serde",
"serde_json", "serde_json",
@ -833,11 +820,12 @@ dependencies = [
"async-io", "async-io",
"async-oneshot", "async-oneshot",
"async-trait", "async-trait",
"flume",
"futures-lite", "futures-lite",
"futures_codec", "futures_codec",
"gen-z", "gen-z",
"pollster",
"pop-launcher", "pop-launcher",
"postage",
"regex", "regex",
"ron", "ron",
"serde", "serde",
@ -845,13 +833,27 @@ dependencies = [
"serde_with", "serde_with",
"slab", "slab",
"smol", "smol",
"spinning_top",
"strsim", "strsim",
"toml", "toml",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
] ]
[[package]]
name = "postage"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a63d25391d04a097954b76aba742b6b5b74f213dfe3dbaeeb36e8ddc1c657f0b"
dependencies = [
"atomic",
"crossbeam-queue",
"log",
"pin-project 1.0.8",
"pollster",
"static_assertions",
"thiserror",
]
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "0.1.5" version = "0.1.5"
@ -965,12 +967,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.127" version = "1.0.127"
@ -1105,13 +1101,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "spinning_top" name = "static_assertions"
version = "0.2.2" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e529d73e80d64b5f2631f9035113347c578a1c9c7774b83a2b880788459ab36" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
dependencies = [
"lock_api",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
@ -1288,60 +1281,6 @@ version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasm-bindgen"
version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b608ecc8f4198fe8680e2ed18eccab5f0cd4caaf3d83516fa5fb2e927fda2586"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "580aa3a91a63d23aac5b6b267e2d13cb4f363e31dce6c352fca4752ae12e479f"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "171ebf0ed9e1458810dfcb31f2e766ad6b3a89dbda42d8901f2b268277e5f09c"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c2657dd393f03aa2a659c25c6ae18a13a4048cebd220e147933ea837efc589f"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e0c4a743a309662d45f4ede961d7afa4ba4131a59a639f29b0069c3798bbcc2"
[[package]] [[package]]
name = "wepoll-ffi" name = "wepoll-ffi"
version = "0.1.2" version = "0.1.2"

View file

@ -6,7 +6,6 @@ edition = "2018"
publish = false publish = false
[dependencies] [dependencies]
flume = "0.10"
freedesktop-desktop-entry = "0.3" freedesktop-desktop-entry = "0.3"
futures_codec = "0.4" futures_codec = "0.4"
futures-lite = "1" futures-lite = "1"
@ -24,3 +23,4 @@ zvariant = "=2.6" # Restrict for 1.47
ron = "0.6.4" ron = "0.6.4"
urlencoding = "2.1.0" urlencoding = "2.1.0"
slab = "0.4.4" slab = "0.4.4"
postage = "0.4.1"

View file

@ -1,5 +1,7 @@
use futures_lite::*; use futures_lite::*;
use pop_launcher::*; use pop_launcher::*;
use postage::mpsc;
use postage::prelude::{Sink, Stream};
use smol::process::{ChildStdout, Command, Stdio}; use smol::process::{ChildStdout, Command, Stdio};
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::Cell; use std::cell::Cell;
@ -13,10 +15,10 @@ enum Event {
} }
pub async fn main() { pub async fn main() {
let (event_tx, event_rx) = flume::unbounded::<Event>(); let (mut event_tx, mut event_rx) = mpsc::channel::<Event>(8);
// Channel for cancelling searches that are in progress. // Channel for cancelling searches that are in progress.
let (interrupt_tx, interrupt_rx) = flume::bounded::<()>(0); let (interrupt_tx, interrupt_rx) = mpsc::channel::<()>(1);
// Indicates if a search is being performed in the background. // Indicates if a search is being performed in the background.
let active = Rc::new(Cell::new(false)); let active = Rc::new(Cell::new(false));
@ -30,7 +32,7 @@ pub async fn main() {
// Manages the external process, tracks search results, and executes activate requests // Manages the external process, tracks search results, and executes activate requests
let search_handler = async move { let search_handler = async move {
while let Ok(search) = event_rx.recv_async().await { while let Some(search) = event_rx.recv().await {
match search { match search {
Event::Activate(id) => { Event::Activate(id) => {
if let Some(selection) = app.search_results.get(id as usize) { if let Some(selection) = app.search_results.get(id as usize) {
@ -52,10 +54,14 @@ pub async fn main() {
// Forwards requests to the search handler, and performs an interrupt as necessary. // Forwards requests to the search handler, and performs an interrupt as necessary.
let request_handler = async move { let request_handler = async move {
let interrupt = || async { let interrupt = || {
if active.get() && !interrupt_tx.is_full() { let active = active.clone();
tracing::debug!("sending interrupt"); let mut tx = interrupt_tx.clone();
let _ = interrupt_tx.send_async(()).await; async move {
if active.get() {
tracing::debug!("sending interrupt");
let _ = tx.try_send(());
}
} }
}; };
@ -66,7 +72,7 @@ pub async fn main() {
Ok(request) => match request { Ok(request) => match request {
// Launch the default application with the selected file // Launch the default application with the selected file
Request::Activate(id) => { Request::Activate(id) => {
event_tx.send_async(Event::Activate(id)).await?; event_tx.send(Event::Activate(id)).await?;
} }
// Interrupt any active searches being performed // Interrupt any active searches being performed
@ -81,7 +87,7 @@ pub async fn main() {
None => &query, None => &query,
}; };
event_tx.send_async(Event::Search(query.to_owned())).await?; event_tx.send(Event::Search(query.to_owned())).await?;
active.set(true); active.set(true);
} }
@ -94,7 +100,7 @@ pub async fn main() {
} }
} }
Ok::<(), flume::SendError<Event>>(()) Ok::<(), postage::sink::SendError<Event>>(())
}; };
let _ = future::zip(request_handler, search_handler).await; let _ = future::zip(request_handler, search_handler).await;
@ -103,7 +109,7 @@ pub async fn main() {
/// Maintains state for search requests /// Maintains state for search requests
struct SearchContext { struct SearchContext {
pub active: Rc<Cell<bool>>, pub active: Rc<Cell<bool>>,
pub interrupt_rx: flume::Receiver<()>, pub interrupt_rx: mpsc::Receiver<()>,
pub out: smol::Unblock<io::Stdout>, pub out: smol::Unblock<io::Stdout>,
pub search_results: Vec<PathBuf>, pub search_results: Vec<PathBuf>,
} }
@ -157,7 +163,7 @@ impl SearchContext {
'stream: loop { 'stream: loop {
let interrupt = async { let interrupt = async {
let _ = self.interrupt_rx.recv_async().await; let _ = self.interrupt_rx.recv().await;
None None
}; };

View file

@ -1,8 +1,9 @@
use crate::*; use crate::*;
use pop_launcher::*; use pop_launcher::*;
use flume::Sender;
use futures_lite::{AsyncBufReadExt, StreamExt}; use futures_lite::{AsyncBufReadExt, StreamExt};
use postage::mpsc::Sender;
use postage::prelude::*;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
use std::{ use std::{
@ -73,7 +74,7 @@ impl App {
} }
async fn reload(&mut self) { async fn reload(&mut self) {
let (tx, rx) = flume::unbounded::<ScriptInfo>(); let (tx, mut rx) = postage::mpsc::channel::<ScriptInfo>(8);
let mut queue = VecDeque::new(); let mut queue = VecDeque::new();
@ -92,7 +93,7 @@ impl App {
}; };
let script_receiver = async { let script_receiver = async {
while let Ok(script) = rx.recv_async().await { while let Some(script) = rx.recv().await {
tracing::debug!("appending script: {:?}", script); tracing::debug!("appending script: {:?}", script);
self.scripts.push(script); self.scripts.push(script);
} }
@ -148,7 +149,7 @@ struct ScriptInfo {
async fn load_from(path: &Path, paths: &mut VecDeque<PathBuf>, tx: Sender<ScriptInfo>) { async fn load_from(path: &Path, paths: &mut VecDeque<PathBuf>, tx: Sender<ScriptInfo>) {
if let Ok(directory) = path.read_dir() { if let Ok(directory) = path.read_dir() {
for entry in directory.filter_map(Result::ok) { for entry in directory.filter_map(Result::ok) {
let tx = tx.clone(); let mut tx = tx.clone();
let path = entry.path(); let path = entry.path();
if path.is_dir() { if path.is_dir() {
@ -199,7 +200,7 @@ async fn load_from(path: &Path, paths: &mut VecDeque<PathBuf>, tx: Sender<Script
} }
} }
let _ = tx.send_async(info).await; let _ = tx.send(info).await;
}) })
.detach(); .detach();
} }

View file

@ -35,6 +35,10 @@
matches: ["dev"], matches: ["dev"],
queries: [(name: "DEV Community", query: "dev.to/search?q=")], queries: [(name: "DEV Community", query: "dev.to/search?q=")],
), ),
(
matches: ["docs"],
queries: [(name: "Docs.rs", query: "docs.rs/")]
)
( (
matches: ["fh"], matches: ["fh"],
queries: [(name: "Flathub", query: "flathub.org/apps/search/")] queries: [(name: "Flathub", query: "flathub.org/apps/search/")]

View file

@ -20,9 +20,7 @@ pub async fn main() {
Request::Exit => break, Request::Exit => break,
_ => (), _ => (),
}, },
Err(why) => { Err(why) => tracing::error!("malformed JSON input: {}", why),
tracing::error!("malformed JSON input: {}", why)
}
} }
} }
} }

View file

@ -9,7 +9,6 @@ anyhow = "1"
async-io = "1" async-io = "1"
async-oneshot = "0.5" async-oneshot = "0.5"
async-trait = "0.1" async-trait = "0.1"
flume = "=0.10.7" # Restrict version to build with 1.47.0
futures_codec = "0.4" futures_codec = "0.4"
futures-lite = "1" futures-lite = "1"
regex = "1.5" regex = "1.5"
@ -25,6 +24,8 @@ tracing = "0.1"
tracing-subscriber = { version = "0.2", features = ["fmt"] } tracing-subscriber = { version = "0.2", features = ["fmt"] }
gen-z = "0.1" gen-z = "0.1"
pop-launcher = { path = "../" } pop-launcher = { path = "../" }
postage = "0.4.1"
# Required for rustc 1.47 # Required for rustc 1.47
spinning_top = "=0.2.2" pollster = "=0.2.3"

View file

@ -1,10 +1,10 @@
mod plugins; mod plugins;
use crate::plugins::*; use crate::plugins::*;
use pop_launcher::*;
use flume::{unbounded, Receiver, Sender};
use futures_lite::{future, StreamExt}; use futures_lite::{future, StreamExt};
use pop_launcher::*;
use postage::mpsc;
use postage::prelude::*;
use regex::Regex; use regex::Regex;
use slab::Slab; use slab::Slab;
use std::{ use std::{
@ -56,13 +56,13 @@ impl<O: Write> Service<O> {
} }
pub async fn exec(mut self) { pub async fn exec(mut self) {
let (service_tx, service_rx) = unbounded(); let (service_tx, service_rx) = mpsc::channel(1);
{ {
let (plugins_tx, plugins_rx) = unbounded(); let (plugins_tx, mut plugins_rx) = mpsc::channel(8);
let plugin_loader = plugins::external::load::from_paths(plugins_tx); let plugin_loader = plugins::external::load::from_paths(plugins_tx);
let plugin_receiver = async { let plugin_receiver = async {
while let Ok((exec, config, regex)) = plugins_rx.recv_async().await { while let Some((exec, config, regex)) = plugins_rx.recv().await {
tracing::info!("found plugin \"{}\"", exec.display()); tracing::info!("found plugin \"{}\"", exec.display());
if self if self
.plugins .plugins
@ -84,13 +84,11 @@ impl<O: Write> Service<O> {
future::zip(plugin_loader, plugin_receiver).await; future::zip(plugin_loader, plugin_receiver).await;
} }
let internal = service_tx.clone();
self.register_plugin( self.register_plugin(
service_tx.clone(), service_tx.clone(),
plugins::help::CONFIG, plugins::help::CONFIG,
Some(Regex::new(plugins::help::REGEX.as_ref()).expect("failed to compile help regex")), Some(Regex::new(plugins::help::REGEX.as_ref()).expect("failed to compile help regex")),
move |id, tx| HelpPlugin::new(id, internal.clone(), tx), move |id, tx| HelpPlugin::new(id, tx),
); );
let f1 = request_handler(service_tx); let f1 = request_handler(service_tx);
@ -99,23 +97,23 @@ impl<O: Write> Service<O> {
future::zip(f1, f2).await; future::zip(f1, f2).await;
} }
async fn response_handler(&mut self, service_rx: Receiver<Event>) { async fn response_handler(&mut self, mut service_rx: mpsc::Receiver<Event>) {
while let Ok(event) = service_rx.recv_async().await { while let Some(event) = service_rx.recv().await {
match event { match event {
Event::Request(request) => { Event::Request(request) => {
match request { match request {
Request::Search(query) => self.search(query), Request::Search(query) => self.search(query).await,
Request::Interrupt => self.interrupt(), Request::Interrupt => self.interrupt().await,
Request::Activate(id) => self.activate(id), Request::Activate(id) => self.activate(id).await,
Request::Complete(id) => self.complete(id), Request::Complete(id) => self.complete(id).await,
Request::Quit(id) => self.quit(id), Request::Quit(id) => self.quit(id).await,
// When requested to exit, the service will forward that // When requested to exit, the service will forward that
// request to all of its plugins before exiting itself // request to all of its plugins before exiting itself
Request::Exit => { Request::Exit => {
for (_key, plugin) in self.plugins.iter_mut() { for (_key, plugin) in self.plugins.iter_mut() {
let tx = plugin.sender_exec(); let tx = plugin.sender_exec();
let _ = tx.send(Request::Exit); let _ = tx.send(Request::Exit).await;
} }
break; break;
@ -128,7 +126,7 @@ impl<O: Write> Service<O> {
PluginResponse::Clear => self.clear(), PluginResponse::Clear => self.clear(),
PluginResponse::Close => self.close(), PluginResponse::Close => self.close(),
PluginResponse::Fill(text) => self.fill(text), PluginResponse::Fill(text) => self.fill(text),
PluginResponse::Finished => self.finished(plugin), PluginResponse::Finished => self.finished(plugin).await,
PluginResponse::DesktopEntry(path) => { PluginResponse::DesktopEntry(path) => {
self.respond(&Response::DesktopEntry(path)); self.respond(&Response::DesktopEntry(path));
} }
@ -154,9 +152,12 @@ impl<O: Write> Service<O> {
} }
} }
fn register_plugin<P: Plugin, I: Fn(usize, Sender<Event>) -> P + Send + Sync + 'static>( fn register_plugin<
P: Plugin,
I: Fn(usize, mpsc::Sender<Event>) -> P + Send + Sync + 'static,
>(
&mut self, &mut self,
service_tx: Sender<Event>, service_tx: mpsc::Sender<Event>,
config: PluginConfig, config: PluginConfig,
regex: Option<regex::Regex>, regex: Option<regex::Regex>,
init: I, init: I,
@ -170,7 +171,7 @@ impl<O: Write> Service<O> {
config, config,
regex, regex,
Box::new(move || { Box::new(move || {
let (request_tx, request_rx) = unbounded(); let (request_tx, request_rx) = mpsc::channel(8);
let init = init.clone(); let init = init.clone();
let service_tx = service_tx.clone(); let service_tx = service_tx.clone();
@ -184,14 +185,13 @@ impl<O: Write> Service<O> {
)); ));
} }
fn activate(&mut self, id: u32) { async fn activate(&mut self, id: u32) {
if let Some((plugin, meta)) = self.search_result(id as usize) { if let Some((plugin, meta)) = self.search_result(id as usize) {
let _ = plugin.sender_exec().send(Request::Activate(meta.id)); let _ = plugin.sender_exec().send(Request::Activate(meta.id)).await;
} }
} }
fn append(&mut self, plugin: PluginKey, append: PluginSearchResult) { fn append(&mut self, plugin: PluginKey, append: PluginSearchResult) {
eprintln!("appending {:?}", append);
self.active_search.push((plugin, append)); self.active_search.push((plugin, append));
} }
@ -203,9 +203,9 @@ impl<O: Write> Service<O> {
self.respond(&Response::Close); self.respond(&Response::Close);
} }
fn complete(&mut self, id: u32) { async fn complete(&mut self, id: u32) {
if let Some((plugin, meta)) = self.search_result(id as usize) { if let Some((plugin, meta)) = self.search_result(id as usize) {
let _ = plugin.sender_exec().send(Request::Complete(meta.id)); let _ = plugin.sender_exec().send(Request::Complete(meta.id)).await;
} }
} }
@ -213,32 +213,30 @@ impl<O: Write> Service<O> {
self.respond(&Response::Fill(text)); self.respond(&Response::Fill(text));
} }
fn finished(&mut self, plugin: PluginKey) { async fn finished(&mut self, plugin: PluginKey) {
self.awaiting_results.remove(&plugin); self.awaiting_results.remove(&plugin);
if self.awaiting_results.is_empty() { if self.awaiting_results.is_empty() {
if self.search_scheduled { if self.search_scheduled {
self.search(String::new()); self.search(String::new()).await;
return; return;
} }
eprintln!("updating with {:?}", self.active_search);
let search_list = self.sort(); let search_list = self.sort();
self.respond(&Response::Update(search_list)) self.respond(&Response::Update(search_list))
} }
} }
fn interrupt(&mut self) { async fn interrupt(&mut self) {
for (_, plugin) in self.plugins.iter_mut() { for (_, plugin) in self.plugins.iter_mut() {
if let Some(sender) = plugin.sender.as_mut() { if let Some(sender) = plugin.sender.as_mut() {
let _ = sender.send(Request::Interrupt); let _ = sender.send(Request::Interrupt).await;
} }
} }
} }
fn quit(&mut self, id: u32) { async fn quit(&mut self, id: u32) {
if let Some((plugin, meta)) = self.search_result(id as usize) { if let Some((plugin, meta)) = self.search_result(id as usize) {
let _ = plugin.sender_exec().send(Request::Quit(meta.id)); let _ = plugin.sender_exec().send(Request::Quit(meta.id)).await;
} }
} }
@ -250,11 +248,11 @@ impl<O: Write> Service<O> {
} }
} }
fn search(&mut self, query: String) { async fn search(&mut self, query: String) {
if !self.awaiting_results.is_empty() { if !self.awaiting_results.is_empty() {
tracing::debug!("backing off from search until plugins are ready"); tracing::debug!("backing off from search until plugins are ready");
if !self.search_scheduled { if !self.search_scheduled {
self.interrupt(); self.interrupt().await;
self.search_scheduled = true; self.search_scheduled = true;
} }
@ -300,9 +298,9 @@ impl<O: Write> Service<O> {
if plugin if plugin
.sender_exec() .sender_exec()
.send(Request::Search(query.to_owned())) .send(Request::Search(query.to_owned()))
.await
.is_ok() .is_ok()
{ {
tracing::debug!("submitted query to {}", plugin.config.name);
self.awaiting_results.insert(isolated); self.awaiting_results.insert(isolated);
self.no_sort = plugin.config.query.no_sort; self.no_sort = plugin.config.query.no_sort;
} }
@ -313,9 +311,9 @@ impl<O: Write> Service<O> {
if plugin if plugin
.sender_exec() .sender_exec()
.send(Request::Search(query.to_owned())) .send(Request::Search(query.to_owned()))
.await
.is_ok() .is_ok()
{ {
tracing::debug!("submitted query to {}", plugin.config.name);
self.awaiting_results.insert(plugin_id); self.awaiting_results.insert(plugin_id);
} }
} }
@ -456,7 +454,7 @@ impl<O: Write> Service<O> {
} }
/// Handles Requests received from a frontend /// Handles Requests received from a frontend
async fn request_handler(tx: Sender<Event>) { async fn request_handler(mut tx: mpsc::Sender<Event>) {
let mut requested_to_exit = false; let mut requested_to_exit = false;
let mut request_stream = json_input_stream(async_stdin()); let mut request_stream = json_input_stream(async_stdin());
@ -467,7 +465,7 @@ async fn request_handler(tx: Sender<Event>) {
requested_to_exit = true requested_to_exit = true
} }
let _ = tx.send(Event::Request(request)); let _ = tx.send(Event::Request(request)).await;
if requested_to_exit { if requested_to_exit {
break; break;

View file

@ -1,7 +1,9 @@
use crate::PluginConfig; use crate::PluginConfig;
use flume::Sender;
use futures_lite::{future::zip, Stream, StreamExt}; use futures_lite::{future::zip, Stream, StreamExt};
use postage::mpsc::Sender;
use postage::prelude::Stream as PostageStream;
use postage::prelude::*;
use regex::Regex; use regex::Regex;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -9,8 +11,8 @@ use std::path::{Path, PathBuf};
/// ///
/// Searches plugin paths from highest to least priority. User plugins will override /// Searches plugin paths from highest to least priority. User plugins will override
/// distribution plugins. Plugins are loaded in the order they are found. /// distribution plugins. Plugins are loaded in the order they are found.
pub async fn from_paths(tx: Sender<(PathBuf, PluginConfig, Option<Regex>)>) { pub async fn from_paths(mut tx: Sender<(PathBuf, PluginConfig, Option<Regex>)>) {
let (tasks_tx, tasks_rx) = flume::unbounded(); let (mut tasks_tx, mut tasks_rx) = postage::mpsc::channel(8);
// Spawns a background task to run in parallel for each plugin found // Spawns a background task to run in parallel for each plugin found
let task_spawner = async move { let task_spawner = async move {
@ -20,7 +22,7 @@ pub async fn from_paths(tx: Sender<(PathBuf, PluginConfig, Option<Regex>)>) {
while let Some((source, config)) = loadable_plugins.next().await { while let Some((source, config)) = loadable_plugins.next().await {
let future = smol::unblock(move || crate::plugins::config::load(&source, &config)); let future = smol::unblock(move || crate::plugins::config::load(&source, &config));
if tasks_tx.send_async(smol::spawn(future)).await.is_err() { if tasks_tx.send(smol::spawn(future)).await.is_err() {
break; break;
} }
} }
@ -29,9 +31,9 @@ pub async fn from_paths(tx: Sender<(PathBuf, PluginConfig, Option<Regex>)>) {
// This future ensures that plugins are returned in the order they were spawned. // This future ensures that plugins are returned in the order they were spawned.
let task_listener = async move { let task_listener = async move {
while let Ok(task) = tasks_rx.recv_async().await { while let Some(task) = tasks_rx.recv().await {
if let Some(plugin) = task.await { if let Some(plugin) = task.await {
if tx.send_async(plugin).await.is_err() { if tx.send(plugin).await.is_err() {
break; break;
} }
} }

View file

@ -11,8 +11,9 @@ use std::{
use crate::{Event, Plugin, PluginResponse, Request}; use crate::{Event, Plugin, PluginResponse, Request};
use async_oneshot::oneshot; use async_oneshot::oneshot;
use flume::Sender;
use futures_lite::{AsyncWriteExt, FutureExt, StreamExt}; use futures_lite::{AsyncWriteExt, FutureExt, StreamExt};
use postage::mpsc::Sender;
use postage::prelude::*;
use smol::{ use smol::{
process::{Child, Command, Stdio}, process::{Child, Command, Stdio},
Task, Task,
@ -66,13 +67,13 @@ impl ExternalPlugin {
let detached = self.detached.clone(); let detached = self.detached.clone();
let searching = self.searching.clone(); let searching = self.searching.clone();
let (trip_tx, trip_rx) = oneshot::<()>(); let (trip_tx, trip_rx) = oneshot::<()>();
let tx = self.tx.clone(); let mut tx = self.tx.clone();
let name = self.name().to_owned(); let name = self.name().to_owned();
let id = self.id; let id = self.id;
// Spawn a background task to forward JSON responses from the child process. // Spawn a background task to forward JSON responses from the child process.
let task = smol::spawn(async move { let task = smol::spawn(async move {
let tx_ = tx.clone(); let mut tx_ = tx.clone();
let searching_ = searching.clone(); let searching_ = searching.clone();
let name_ = name.clone(); let name_ = name.clone();
@ -87,11 +88,10 @@ impl ExternalPlugin {
searching_.store(false, Ordering::SeqCst); searching_.store(false, Ordering::SeqCst);
} }
tracing::debug!("{}: responding with {:?}", name_, response); let _ = tx_.send(Event::Response((id, response))).await;
let _ = tx_.send(Event::Response((id, response)));
} }
Err(why) => { Err(why) => {
event!(Level::ERROR, "{}: serde error: {:?}", name_, why); tracing::error!("{}: serde error: {:?}", name_, why);
} }
} }
} }
@ -107,7 +107,9 @@ impl ExternalPlugin {
// Ensure that a task that was searching sends a finished signal if it dies. // Ensure that a task that was searching sends a finished signal if it dies.
if searching.swap(false, Ordering::SeqCst) { if searching.swap(false, Ordering::SeqCst) {
let _ = tx.send(Event::Response((id, PluginResponse::Finished))); let _ = tx
.send(Event::Response((id, PluginResponse::Finished)))
.await;
} }
detached.store(true, Ordering::SeqCst); detached.store(true, Ordering::SeqCst);
@ -194,7 +196,7 @@ impl Plugin for ExternalPlugin {
} else { } else {
let _ = self let _ = self
.tx .tx
.send_async(Event::Response((self.id, PluginResponse::Finished))) .send(Event::Response((self.id, PluginResponse::Finished)))
.await; .await;
} }
} }

View file

@ -1,6 +1,7 @@
use crate::*; use crate::*;
use flume::Sender;
use pop_launcher::*; use pop_launcher::*;
use postage::mpsc::Sender;
use postage::prelude::*;
use slab::Slab; use slab::Slab;
use std::borrow::Cow; use std::borrow::Cow;
@ -22,23 +23,21 @@ pub const CONFIG: PluginConfig = PluginConfig {
pub struct HelpPlugin { pub struct HelpPlugin {
pub id: usize, pub id: usize,
pub details: Slab<PluginHelp>, pub details: Slab<PluginHelp>,
pub internal: Sender<Event>,
pub tx: Sender<Event>, pub tx: Sender<Event>,
} }
impl HelpPlugin { impl HelpPlugin {
pub fn new(id: usize, internal: Sender<Event>, tx: Sender<Event>) -> Self { pub fn new(id: usize, tx: Sender<Event>) -> Self {
Self { Self {
id, id,
details: Slab::new(), details: Slab::new(),
internal,
tx, tx,
} }
} }
async fn reload(&mut self) { async fn reload(&mut self) {
let (tx, rx) = async_oneshot::oneshot(); let (tx, rx) = async_oneshot::oneshot();
let _ = self.internal.send_async(Event::Help(tx)).await; let _ = self.tx.send(Event::Help(tx)).await;
self.details = rx.await.expect("internal error fetching help info"); self.details = rx.await.expect("internal error fetching help info");
} }
} }
@ -50,7 +49,7 @@ impl Plugin for HelpPlugin {
if let Some(help) = detail.help.as_ref() { if let Some(help) = detail.help.as_ref() {
let _ = self let _ = self
.tx .tx
.send_async(Event::Response(( .send(Event::Response((
self.id, self.id,
PluginResponse::Fill(help.clone()), PluginResponse::Fill(help.clone()),
))) )))
@ -84,16 +83,13 @@ impl Plugin for HelpPlugin {
..Default::default() ..Default::default()
}); });
let _ = self let _ = self.tx.send(Event::Response((self.id, response))).await;
.tx
.send_async(Event::Response((self.id, response)))
.await;
} }
} }
let _ = self let _ = self
.tx .tx
.send_async(Event::Response((self.id, PluginResponse::Finished))) .send(Event::Response((self.id, PluginResponse::Finished)))
.await; .await;
} }

View file

@ -8,7 +8,8 @@ pub use self::help::HelpPlugin;
use crate::{PluginHelp, Request}; use crate::{PluginHelp, Request};
use async_trait::async_trait; use async_trait::async_trait;
use flume::{Receiver, Sender}; use postage::mpsc::{Receiver, Sender};
use postage::prelude::*;
use regex::Regex; use regex::Regex;
#[async_trait] #[async_trait]
@ -31,8 +32,8 @@ where
async fn quit(&mut self, id: u32); async fn quit(&mut self, id: u32);
async fn run(&mut self, rx: Receiver<Request>) { async fn run(&mut self, mut rx: Receiver<Request>) {
while let Ok(request) = rx.recv_async().await { while let Some(request) = rx.recv().await {
tracing::event!( tracing::event!(
tracing::Level::DEBUG, tracing::Level::DEBUG,
"{}: received {:?}", "{}: received {:?}",