Fix memory leak caused by unbounded channels not freeing memory

This commit is contained in:
Lucas Schumacher 2024-06-12 13:07:49 -04:00
parent 7abb089c04
commit 4724d23459
5 changed files with 58 additions and 29 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
target target
.DS_Store .DS_Store
dist dist
heaptrack.*.zst

View File

@ -1,5 +1,5 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::mpsc::{self, Sender}; use std::sync::mpsc;
use egui::{Context, Ui}; use egui::{Context, Ui};
use egui_plot::{Line, Plot, PlotBounds, PlotPoints}; use egui_plot::{Line, Plot, PlotBounds, PlotPoints};
@ -10,25 +10,44 @@ pub enum PlotData {
//F32(Vec<f32>), //F32(Vec<f32>),
Bode32(Vec<Complex32>), Bode32(Vec<Complex32>),
} }
#[derive(Clone)]
pub struct DebugPlotSender {
tx: mpsc::SyncSender<(&'static str, PlotData)>,
}
impl DebugPlotSender {
pub fn send(
&self,
plot_name: &'static str,
plot_data: PlotData,
) -> Result<(), mpsc::SendError<PlotData>> {
match self.tx.try_send((plot_name, plot_data)) {
Err(mpsc::TrySendError::Full(_)) => {
log::warn!("Debug buffer is full!");
Ok(())
}
Err(mpsc::TrySendError::Disconnected((_, d))) => Err(mpsc::SendError(d)),
Ok(()) => Ok(()),
}
}
}
pub struct DebugPlots { pub struct DebugPlots {
plots: HashMap<&'static str, PlotData>, plots: HashMap<&'static str, PlotData>,
plot_en: HashMap<&'static str, bool>, plot_en: HashMap<&'static str, bool>,
rx: mpsc::Receiver<(&'static str, PlotData)>, rx: mpsc::Receiver<(&'static str, PlotData)>,
tx: mpsc::Sender<(&'static str, PlotData)>, tx: DebugPlotSender,
} }
impl DebugPlots { impl DebugPlots {
pub fn new() -> Self { pub fn new() -> Self {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::sync_channel(128);
DebugPlots { DebugPlots {
plots: HashMap::new(), plots: HashMap::new(),
plot_en: HashMap::new(), plot_en: HashMap::new(),
rx, rx,
tx, tx: DebugPlotSender { tx },
} }
} }
pub fn get_sender(&self) -> Sender<(&'static str, PlotData)> { pub fn get_sender(&self) -> DebugPlotSender {
self.tx.clone() self.tx.clone()
} }
pub fn update_plots(&mut self) { pub fn update_plots(&mut self) {

View File

@ -1,24 +1,21 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use realfft::RealFftPlanner; use realfft::RealFftPlanner;
use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::mpsc::{self, Receiver, SyncSender, TrySendError};
use super::debug_plot::PlotData; use super::debug_plot::{DebugPlotSender, PlotData};
pub struct Fft { pub struct Fft {
pub tx: Sender<Vec<f32>>, pub tx: SyncSender<Vec<f32>>,
pub output_len: usize, pub output_len: usize,
} }
impl Fft { impl Fft {
pub fn new( pub fn new(size: usize, plot_tx: DebugPlotSender) -> Result<(Self, mpsc::Receiver<Vec<u8>>)> {
size: usize,
plot_tx: Sender<(&'static str, PlotData)>,
) -> Result<(Self, mpsc::Receiver<Vec<u8>>)> {
let output_len = size / 2 + 1; let output_len = size / 2 + 1;
// Create mpsc queue // Create mpsc queue
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::sync_channel(10);
let (in_tx, in_rx): (Sender<Vec<f32>>, Receiver<Vec<f32>>) = mpsc::channel(); let (in_tx, in_rx): (SyncSender<Vec<f32>>, Receiver<Vec<f32>>) = mpsc::sync_channel(10);
// Setup fft use f32 for now // Setup fft use f32 for now
let mut fft_planner = RealFftPlanner::<f32>::new(); let mut fft_planner = RealFftPlanner::<f32>::new();
@ -36,7 +33,7 @@ impl Fft {
fft.process_with_scratch(&mut fft_in, &mut fft_out, &mut fft_scratch) fft.process_with_scratch(&mut fft_in, &mut fft_out, &mut fft_scratch)
.unwrap(); .unwrap();
plot_tx plot_tx
.send(("FFT Output", PlotData::Bode32(fft_out.clone()))) .send("FFT Output", PlotData::Bode32(fft_out.clone()))
.unwrap(); .unwrap();
fft_in.clear(); fft_in.clear();
let output: Vec<u8> = fft_out let output: Vec<u8> = fft_out
@ -48,9 +45,15 @@ impl Fft {
.collect(); .collect();
assert_eq!(output_len, output.len()); assert_eq!(output_len, output.len());
plot_tx plot_tx
.send(("FFT Processed Output", PlotData::U8(output.clone()))) .send("FFT Processed Output", PlotData::U8(output.clone()))
.unwrap(); .unwrap();
tx.send(output).unwrap(); match tx.try_send(output) {
Ok(_) => {}
Err(TrySendError::Full(_)) => log::warn!("Waterfall buffer full."),
Err(TrySendError::Disconnected(_)) => {
panic!("The fft thread has disconnected from the waterfall!")
}
}
} }
} }
}); });

View File

@ -4,9 +4,9 @@ use cpal::{
traits::{DeviceTrait, HostTrait}, traits::{DeviceTrait, HostTrait},
BufferSize, BufferSize,
}; };
use std::sync::mpsc::Sender; use std::sync::mpsc::{SyncSender, TrySendError};
use crate::app::debug_plot::PlotData; use crate::app::debug_plot::DebugPlotSender;
pub struct Audio { pub struct Audio {
pub stream: cpal::Stream, pub stream: cpal::Stream,
@ -15,13 +15,19 @@ impl Audio {
pub fn new( pub fn new(
device: &cpal::Device, device: &cpal::Device,
config: cpal::StreamConfig, config: cpal::StreamConfig,
fft_input: Sender<Vec<f32>>, fft_input: SyncSender<Vec<f32>>,
_plot_tx: Sender<(&'static str, PlotData)>, _plot_tx: DebugPlotSender,
) -> Result<Self> { ) -> Result<Self> {
let stream = device.build_input_stream( let stream = device.build_input_stream(
&config, &config,
move |data: &[f32], _: &cpal::InputCallbackInfo| { move |data: &[f32], _: &cpal::InputCallbackInfo| {
fft_input.send(data.to_vec()).unwrap(); match fft_input.try_send(data.to_vec()) {
Err(TrySendError::Disconnected(_)) => panic!(
"Error: Audio backend has lost connection to frontend! Can not continue!"
),
Err(TrySendError::Full(_)) => log::warn!("Audio Backend buffer full."),
Ok(()) => {}
};
}, },
move |err| log::error!("Audio Thread Error: {err}"), move |err| log::error!("Audio Thread Error: {err}"),
None, None,
@ -88,8 +94,8 @@ impl super::Backend for AudioBackend {
fn build_device( fn build_device(
&mut self, &mut self,
fft_input: Sender<Vec<f32>>, fft_input: SyncSender<Vec<f32>>,
_plot_tx: Sender<(&'static str, PlotData)>, _plot_tx: DebugPlotSender,
) -> anyhow::Result<Box<dyn super::Device>> { ) -> anyhow::Result<Box<dyn super::Device>> {
let config = cpal::StreamConfig { let config = cpal::StreamConfig {
channels: 1, channels: 1,

View File

@ -1,8 +1,8 @@
use std::sync::mpsc::Sender; use std::sync::mpsc::SyncSender;
use egui::Ui; use egui::Ui;
use crate::app::debug_plot::PlotData; use crate::app::debug_plot::DebugPlotSender;
mod audio; mod audio;
pub trait Device { pub trait Device {
fn show_settings(&mut self, ui: &mut Ui); fn show_settings(&mut self, ui: &mut Ui);
@ -14,8 +14,8 @@ pub trait Backend {
fn show_device_selection(&mut self, ui: &mut Ui); fn show_device_selection(&mut self, ui: &mut Ui);
fn build_device( fn build_device(
&mut self, &mut self,
fft_input: Sender<Vec<f32>>, fft_input: SyncSender<Vec<f32>>,
_plot_tx: Sender<(&'static str, PlotData)>, _plot_tx: DebugPlotSender,
) -> anyhow::Result<Box<dyn Device>>; ) -> anyhow::Result<Box<dyn Device>>;
} }
pub struct Backends(pub Vec<Box<dyn Backend>>); pub struct Backends(pub Vec<Box<dyn Backend>>);