-
-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
61 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
## 0.5.1 | ||
|
||
- Make server shutdown more robust. | ||
|
||
## 0.5.0 | ||
|
||
- UDP connections are now modeled as streams. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,25 @@ | ||
use std::sync::Arc; | ||
|
||
use anyhow::Result; | ||
use tokio::{ | ||
sync::{broadcast::Sender as BroadcastSender, RwLock}, | ||
task::JoinHandle, | ||
}; | ||
|
||
pub struct ShutdownTask { | ||
py_handle: JoinHandle<Result<()>>, | ||
wg_handle: JoinHandle<Result<()>>, | ||
sd_trigger: BroadcastSender<()>, | ||
sd_barrier: BroadcastSender<()>, | ||
} | ||
|
||
impl ShutdownTask { | ||
pub fn new( | ||
py_handle: JoinHandle<Result<()>>, | ||
wg_handle: JoinHandle<Result<()>>, | ||
sd_trigger: BroadcastSender<()>, | ||
sd_barrier: BroadcastSender<()>, | ||
) -> Self { | ||
ShutdownTask { | ||
py_handle, | ||
wg_handle, | ||
sd_trigger, | ||
sd_barrier, | ||
} | ||
} | ||
|
||
pub async fn run(self) { | ||
let mut sd_watcher = self.sd_trigger.subscribe(); | ||
let shutting_down = Arc::new(RwLock::new(false)); | ||
use tokio::sync::broadcast; | ||
use tokio::task::JoinSet; | ||
|
||
// wait for Python interop task to return | ||
let py_sd_trigger = self.sd_trigger.clone(); | ||
let py_shutting_down = shutting_down.clone(); | ||
let py_task_handle = tokio::spawn(async move { | ||
match self.py_handle.await { | ||
Ok(Ok(())) => (), | ||
Ok(Err(error)) => log::error!("Python interop task failed: {}", error), | ||
Err(error) => log::error!("Python interop task panicked: {}", error), | ||
pub async fn shutdown_task(mut tasks: JoinSet<Result<()>>, shutdown_done: broadcast::Sender<()>) { | ||
while let Some(task) = tasks.join_next().await { | ||
match task { | ||
Ok(Ok(())) => (), | ||
Ok(Err(error)) => { | ||
log::error!("Task failed: {}\n{}", error, error.backtrace().to_string()); | ||
tasks.shutdown().await; | ||
} | ||
|
||
if !*py_shutting_down.read().await { | ||
log::error!("Python interop task shut down early, exiting."); | ||
let _ = py_sd_trigger.send(()); | ||
Err(error) => { | ||
if error.is_cancelled() { | ||
log::error!("Task cancelled: {}", error); | ||
} else { | ||
log::error!("Task panicked: {}", error); | ||
} | ||
tasks.shutdown().await; | ||
} | ||
}); | ||
|
||
// wait for WireGuard server task to return | ||
let wg_sd_trigger = self.sd_trigger.clone(); | ||
let wg_shutting_down = shutting_down.clone(); | ||
let wg_task_handle = tokio::spawn(async move { | ||
match self.wg_handle.await { | ||
Ok(Ok(())) => (), | ||
Ok(Err(error)) => log::error!("Proxy server task failed: {}", error), | ||
Err(error) => log::error!("Proxy server task panicked: {}", error), | ||
} | ||
|
||
if !*wg_shutting_down.read().await { | ||
log::error!("Proxy server task shut down early, exiting."); | ||
let _ = wg_sd_trigger.send(()); | ||
} | ||
}); | ||
|
||
// wait for shutdown trigger: | ||
// - either `Server.stop` was called, or | ||
// - one of the subtasks failed early | ||
let _ = sd_watcher.recv().await; | ||
*shutting_down.write().await = true; | ||
|
||
// wait for all tasks to terminate and log any errors | ||
if let Err(error) = py_task_handle.await { | ||
log::error!("Shutdown of Python interop task failed: {}", error); | ||
} | ||
if let Err(error) = wg_task_handle.await { | ||
log::error!("Shutdown of WireGuard server task failed: {}", error); | ||
} | ||
|
||
// make `Server.wait_closed` method yield | ||
self.sd_barrier.send(()).ok(); | ||
} | ||
shutdown_done.send(()).ok(); | ||
} |