fix memory leak in closing connections
This commit is contained in:
parent
b0d74880dd
commit
de3248c3c6
7 changed files with 98 additions and 42 deletions
80
src/main.rs
80
src/main.rs
|
|
@ -1,8 +1,5 @@
|
|||
#![feature(test)]
|
||||
#![feature(sync_unsafe_cell)]
|
||||
#![feature(if_let_guard)]
|
||||
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
fs::{create_dir_all, File},
|
||||
io::{self, Error, ErrorKind},
|
||||
path::Path,
|
||||
|
|
@ -11,8 +8,9 @@ use std::{
|
|||
};
|
||||
|
||||
use chrono::Local;
|
||||
use debug_print::{debug_eprintln, debug_println};
|
||||
use flurry::{
|
||||
config::{GRID_LENGTH, HOST},
|
||||
config::{GRID_LENGTH, HOST, IMAGE_SAVE_INTERVAL},
|
||||
flutclient::FlutClient,
|
||||
grid::{self, Flut},
|
||||
COUNTER,
|
||||
|
|
@ -21,12 +19,11 @@ use image::{codecs::jpeg::JpegEncoder, GenericImageView, SubImage};
|
|||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt, BufReader, BufWriter},
|
||||
net::TcpListener,
|
||||
time::{interval, Instant},
|
||||
time::{interval, sleep, timeout, Instant},
|
||||
};
|
||||
|
||||
extern crate test;
|
||||
|
||||
async fn listen_handle() -> io::Result<()> {
|
||||
/// This function logs the current amount of changed pixels to stdout every second
|
||||
async fn pixel_change_stdout_log() -> () {
|
||||
let mut interval = tokio::time::interval(Duration::from_millis(1000));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
|
@ -35,15 +32,21 @@ async fn listen_handle() -> io::Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
async fn save_image_frames(grids: Arc<[grid::Flut<u32>]>) -> io::Result<()> {
|
||||
/// This function starts a timer that saves the current grid state every `duration`.
|
||||
/// These images may then be used for moderation or timelapses
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if it is unable to create or write to the file for the image
|
||||
async fn save_image_frames(grids: Arc<[grid::Flut<u32>]>, duration: Duration) -> io::Result<()> {
|
||||
let base_dir = Path::new("./recordings");
|
||||
let mut timer = interval(Duration::from_secs(5));
|
||||
let mut timer = interval(duration);
|
||||
create_dir_all(base_dir)?;
|
||||
loop {
|
||||
timer.tick().await;
|
||||
for grid in grids.as_ref() {
|
||||
let p = base_dir.join(format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")));
|
||||
println!("timer ticked, grid writing to {:?}", p);
|
||||
debug_println!("timer ticked, grid writing to {:?}", p);
|
||||
let mut file_writer = File::create(p)?;
|
||||
|
||||
let encoder = JpegEncoder::new_with_quality(&mut file_writer, 50);
|
||||
|
|
@ -59,20 +62,37 @@ async fn save_image_frames(grids: Arc<[grid::Flut<u32>]>) -> io::Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_flut(flut_listener: TcpListener, grids: Arc<[grid::Flut<u32>]>) -> io::Result<()> {
|
||||
let mut handles = Vec::new();
|
||||
loop {
|
||||
let (mut socket, _) = flut_listener.accept().await?;
|
||||
let grids = grids.clone();
|
||||
handles.push(tokio::spawn(async move {
|
||||
let (reader, writer) = socket.split();
|
||||
let mut connection = FlutClient::new(reader, writer, grids);
|
||||
let resp = connection.process_socket().await;
|
||||
match resp {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => Err(err),
|
||||
/// Handle connections made to the socket, keeps a vec of the currently active connections,
|
||||
/// uses timeout to loop through them and clean them up to stop a memory leak while not throwing
|
||||
/// everything away
|
||||
async fn handle_flut(flut_listener: TcpListener, grids: Arc<[grid::Flut<u32>]>) -> () {
|
||||
let mut handles = VecDeque::new();
|
||||
tokio::spawn(async {
|
||||
loop {
|
||||
if let Some(handle) = handles.pop_front() {
|
||||
match timeout(Duration::from_secs(10), handle).await {
|
||||
Ok(Ok(())) => debug_println!("connection closed ok"),
|
||||
Ok(Err(e)) => debug_eprintln!("connection error {:?}", e),
|
||||
Err(_) => handles.push_back(handle),
|
||||
}
|
||||
} else {
|
||||
sleep(Duration::from_secs(30)).await;
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
loop {
|
||||
if let Ok((mut socket, _)) = flut_listener.accept().await {
|
||||
let grids = grids.clone();
|
||||
handles.push_back(tokio::spawn(async move {
|
||||
let (reader, writer) = socket.split();
|
||||
let mut connection = FlutClient::new(reader, writer, grids);
|
||||
let resp = connection.process_socket().await;
|
||||
match resp {
|
||||
Ok(()) => Ok(()),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,11 +109,8 @@ async fn main() {
|
|||
println!("bound flut listener");
|
||||
|
||||
let handles = vec![
|
||||
// log the amount of changed pixels each second
|
||||
(tokio::spawn(listen_handle())),
|
||||
// save frames every 5 seconds
|
||||
(tokio::spawn(save_image_frames(grids.clone()))),
|
||||
// accept and handle flut connections
|
||||
(tokio::spawn(pixel_change_stdout_log())),
|
||||
(tokio::spawn(save_image_frames(grids.clone(), IMAGE_SAVE_INTERVAL))),
|
||||
(tokio::spawn(handle_flut(flut_listener, grids.clone()))),
|
||||
];
|
||||
|
||||
|
|
@ -101,6 +118,3 @@ async fn main() {
|
|||
println!("joined handle had result {:?}", handle.await);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue