chore: use a plain future impl instead of a joinset

This commit is contained in:
Tal 2026-01-06 14:45:33 +01:00
parent 7e1a97fa7b
commit 9bffe77b2f
4 changed files with 29 additions and 31 deletions

2
Cargo.lock generated
View File

@ -1350,7 +1350,7 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]] [[package]]
name = "lowfi" name = "lowfi"
version = "2.0.0" version = "2.0.1"
dependencies = [ dependencies = [
"arc-swap", "arc-swap",
"bytes", "bytes",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "lowfi" name = "lowfi"
version = "2.0.0" version = "2.0.1"
rust-version = "1.83.0" rust-version = "1.83.0"
edition = "2021" edition = "2021"
description = "An extremely simple lofi player." description = "An extremely simple lofi player."

View File

@ -130,11 +130,15 @@ async fn main() -> eyre::Result<()> {
let stream = audio::stream()?; let stream = audio::stream()?;
let environment = ui::Environment::ready(&args)?; let environment = ui::Environment::ready(&args)?;
let (mut player, mut tasks) = Player::init(args, stream.mixer()) let (mut player, tasks) = Player::init(args, stream.mixer())
.await .await
.inspect_err(|_| environment.cleanup(false).unwrap())?; .inspect_err(|_| environment.cleanup(false).unwrap())?;
let result = tasks.wait(player.run()).await; let result = tokio::select! {
r = player.run() => r,
r = tasks => r,
};
environment.cleanup(result.is_ok())?; environment.cleanup(result.is_ok())?;
player.close().await?; player.close().await?;

View File

@ -3,15 +3,15 @@
//! This file aims to abstract a lot of potentially annoying Rust async logic, which may be //! This file aims to abstract a lot of potentially annoying Rust async logic, which may be
//! subject to change. //! subject to change.
use futures_util::TryFutureExt; use futures_util::{FutureExt, TryFutureExt};
use std::future::Future; use std::{future::Future, pin::Pin, task::Poll};
use tokio::{select, sync::mpsc, task::JoinSet}; use tokio::{sync::mpsc, task::JoinHandle};
/// Handles all of the processes within lowfi. /// Handles all of the processes within lowfi.
/// This entails initializing/closing tasks, and handling any potential errors that arise. /// This entails initializing/closing tasks, and handling any potential errors that arise.
pub struct Tasks { pub struct Tasks {
/// The [`JoinSet`], which contains all of the task handles. /// A simple [`Vec`] of [`JoinHandle`]s.
pub set: JoinSet<crate::Result<()>>, pub handles: Vec<JoinHandle<crate::Result<()>>>,
/// A sender, which is kept for convenience to be used when /// A sender, which is kept for convenience to be used when
/// initializing various other tasks. /// initializing various other tasks.
@ -20,45 +20,39 @@ pub struct Tasks {
impl Tasks { impl Tasks {
/// Creates a new task manager. /// Creates a new task manager.
pub fn new(tx: mpsc::Sender<crate::Message>) -> Self { pub const fn new(tx: mpsc::Sender<crate::Message>) -> Self {
Self { Self {
tx, tx,
set: JoinSet::new(), handles: Vec::new(),
} }
} }
/// Processes a task, and adds it to the internal [`JoinSet`]. /// Processes a task, and adds it to the internal buffer.
pub fn spawn<E: Into<crate::Error> + Send + Sync + 'static>( pub fn spawn<E: Into<crate::Error> + Send + Sync + 'static>(
&mut self, &mut self,
future: impl Future<Output = Result<(), E>> + Send + 'static, future: impl Future<Output = Result<(), E>> + Send + 'static,
) { ) {
self.set.spawn(future.map_err(Into::into)); self.handles.push(tokio::spawn(future.map_err(Into::into)));
} }
/// Gets a copy of the internal [`mpsc::Sender`]. /// Gets a copy of the internal [`mpsc::Sender`].
pub fn tx(&self) -> mpsc::Sender<crate::Message> { pub fn tx(&self) -> mpsc::Sender<crate::Message> {
self.tx.clone() self.tx.clone()
} }
}
/// Actively polls all of the handles previously added. impl Future for Tasks {
/// type Output = crate::Result<()>;
/// An additional `runner` is for the main player future, which
/// can't be added as a "task" because it shares data with the fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
/// main thread. for handle in &mut self.get_mut().handles {
/// match handle.poll_unpin(cx) {
/// This either returns when the runner completes, or if an error occurs Poll::Ready(Ok(x)) => return Poll::Ready(x),
/// in any of the internally held tasks. Poll::Ready(Err(x)) => return Poll::Ready(Err(crate::Error::JoinError(x))),
pub async fn wait( Poll::Pending => (),
&mut self,
runner: impl Future<Output = Result<(), crate::Error>> + std::marker::Send,
) -> crate::Result<()> {
select! {
result = runner => result,
Some(result) = self.set.join_next() => match result {
Ok(res) => res,
Err(e) if !e.is_cancelled() => Err(crate::Error::JoinError(e)),
Err(_) => Ok(()),
} }
} }
Poll::Pending
} }
} }