Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit deeddb0

Browse files
committedFeb 15, 2019
Added shutdown hook so we stop the idling docker containers when
stopping the playground Also containers are now stopped in parallel
1 parent 25387c9 commit deeddb0

File tree

4 files changed

+85
-24
lines changed

4 files changed

+85
-24
lines changed
 

‎ui/Cargo.lock

+24
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎ui/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ router = "0.6.0"
3131
openssl-probe = "0.1.2"
3232
dotenv = "0.13.0"
3333
snafu = { version = "0.1.2", features = ["unstable_display_attribute"] }
34+
ctrlc = { version = "3.1.1", features = ["termination"] }
3435

3536
[dependencies.playground-middleware]
3637
git = "https://github.com/integer32llc/playground-middleware"

‎ui/src/main.rs

+17-7
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use std::any::Any;
3131
use std::convert::{TryFrom, TryInto};
3232
use std::env;
3333
use std::path::PathBuf;
34+
use std::process;
3435
use std::sync::{Arc, Mutex};
3536
use std::time::{Duration, Instant};
3637

@@ -66,6 +67,13 @@ const ONE_YEAR_IN_SECONDS: u64 = 60 * 60 * 24 * 365;
6667

6768
const SANDBOX_CACHE_TIME_TO_LIVE_IN_SECONDS: u64 = ONE_HOUR_IN_SECONDS as u64;
6869

70+
fn set_graceful_shutdown_hook(containers: Arc<Mutex<DockerContainers>>) {
71+
ctrlc::set_handler(move || {
72+
containers.lock().unwrap().terminate();
73+
process::exit(0);
74+
}).expect("Error setting Ctrl-C handler");
75+
}
76+
6977
fn main() {
7078
// Dotenv may be unable to load environment variables, but that's ok in production
7179
let _ = dotenv::dotenv();
@@ -81,7 +89,9 @@ fn main() {
8189
let cors_enabled = env::var_os("PLAYGROUND_CORS_ENABLED").is_some();
8290
let docker_containers_pool_size = env::var("DOCKER_CONTAINER_POOL_SIZE").ok().and_then(|v| v.parse().ok()).unwrap_or(DEFAULT_DOCKER_CONTAINER_POOL_SIZE);
8391

84-
let containers = Arc::new(DockerContainers::new(docker_containers_pool_size));
92+
let containers = Arc::new(Mutex::new(DockerContainers::new(docker_containers_pool_size)));
93+
94+
set_graceful_shutdown_hook(containers.clone());
8595

8696
let files = Staticfile::new(&root).expect("Unable to open root directory");
8797
let mut files = Chain::new(files);
@@ -165,21 +175,21 @@ impl iron::typemap::Key for GhToken {
165175
type Value = Self;
166176
}
167177

168-
fn compile(req: &mut Request, containers: &DockerContainers) -> IronResult<Response> {
178+
fn compile(req: &mut Request, containers: &Mutex<DockerContainers>) -> IronResult<Response> {
169179
with_sandbox(req, |sandbox, req: CompileRequest| {
170180
let req: sandbox::CompileRequest = try!(req.try_into());
171-
let container = containers.pop(req.channel).unwrap();
181+
let container = containers.lock().unwrap().pop(req.channel).unwrap();
172182
sandbox
173183
.compile(&req, &container)
174184
.map(CompileResponse::from)
175185
.eager_context(Compilation)
176186
})
177187
}
178188

179-
fn handle(req: &mut Request, containers: &DockerContainers) -> IronResult<Response> {
189+
fn handle(req: &mut Request, containers: &Mutex<DockerContainers>) -> IronResult<Response> {
180190
with_sandbox(req, |sandbox, req: ExecuteRequest| {
181191
let req: sandbox::ExecuteRequest = try!(req.try_into());
182-
let container = containers.pop(req.channel).unwrap();
192+
let container = containers.lock().unwrap().pop(req.channel).unwrap();
183193
sandbox
184194
.execute(&req, &container)
185195
.map(ExecuteResponse::from)
@@ -294,10 +304,10 @@ fn meta_gist_get(req: &mut Request) -> IronResult<Response> {
294304

295305
// This is a backwards compatibilty shim. The Rust homepage and the
296306
// documentation use this to run code in place.
297-
fn evaluate(req: &mut Request, containers: &DockerContainers) -> IronResult<Response> {
307+
fn evaluate(req: &mut Request, containers: &Mutex<DockerContainers>) -> IronResult<Response> {
298308
with_sandbox(req, |sandbox, req: EvaluateRequest| {
299309
let req = req.try_into()?;
300-
let container = containers.pop(Channel::Stable).unwrap();
310+
let container = containers.lock().unwrap().pop(Channel::Stable).unwrap();
301311
sandbox
302312
.execute(&req, &container)
303313
.map(EvaluateResponse::from)

‎ui/src/sandbox.rs

+43-17
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use std::thread;
1515

1616
use tempdir::TempDir;
1717
use std::time::Duration;
18+
use std::thread::JoinHandle;
1819

1920
#[derive(Debug, Deserialize)]
2021
struct CrateInformationInner {
@@ -396,17 +397,33 @@ pub struct DockerContainer {
396397
_temp_dir: TempDir, //we need to keep this, or it's dropped (= deleted)
397398
pub src_dir: PathBuf,
398399
pub output_dir: PathBuf,
400+
terminated: Arc<Mutex<bool>>
399401
}
400402

401403
impl DockerContainer {
402404

403-
pub fn terminate(&self) {
404-
let mut cmd = Command::new("docker");
405-
cmd
406-
.arg("exec")
407-
.arg(&self.id)
408-
.args(&["pkill", "sleep"])
409-
.status();
405+
pub fn terminate(&mut self) -> JoinHandle<()> {
406+
let mut terminated = self.terminated.lock().unwrap();
407+
*terminated = true;
408+
409+
let container_id = self.id.clone();
410+
thread::spawn(move || {
411+
let mut cmd = Command::new("docker");
412+
cmd
413+
.arg("exec")
414+
.arg(&container_id)
415+
.args(&["pkill", "sleep"])
416+
.status()
417+
.expect(&format!("Unable to kill container with id {}", container_id));
418+
})
419+
}
420+
421+
// this was initially used to implement the Drop trait, keeping it for future reference
422+
#[allow(dead_code)]
423+
fn is_terminated(&self) -> bool {
424+
let terminated = &self.terminated.clone();
425+
let terminated = terminated.lock().unwrap();
426+
*terminated
410427
}
411428

412429
}
@@ -431,7 +448,7 @@ impl DockerContainers {
431448
thread::spawn(move || {
432449
let mut active = true;
433450
while active {
434-
sender.send(DockerContainers::start_container(channel)).unwrap_or(());
451+
sender.send(Self::start_container(channel)).unwrap_or(());
435452
active = *active_mutex.lock().unwrap();
436453
}
437454
});
@@ -474,7 +491,7 @@ impl DockerContainers {
474491
Ok(child) => {
475492
let id = Self::container_id_from_stdout(child.stdout.unwrap());
476493

477-
DockerContainer { id, _temp_dir: temp_dir, src_dir, output_dir }
494+
DockerContainer { id, _temp_dir: temp_dir, src_dir, output_dir, terminated: Arc::new(Mutex::new(false)) }
478495
}
479496
Err(err) => panic!("Error starting container {}: {}", channel.container_name(), err)
480497
}
@@ -483,19 +500,28 @@ impl DockerContainers {
483500
pub fn pop(&self, channel: Channel) -> Result<DockerContainer, RecvError> {
484501
self.receivers.get(&channel).unwrap().lock().unwrap().recv()
485502
}
486-
}
487-
488-
impl Drop for DockerContainers {
489503

490-
fn drop(&mut self) {
504+
pub fn terminate(&self) {
505+
info!("Shutting down docker containers pool...");
491506
let mut active = self.active.lock().unwrap();
492507
*active = false;
493508

509+
let mut terminate_handles = Vec::new();
494510
for (_, receiver) in self.receivers.iter() {
495-
while let Ok(container) = receiver.lock().unwrap().recv_timeout(Duration::new(1, 0)) {
496-
container.terminate();
511+
while let Ok(mut container) = receiver.lock().unwrap().recv_timeout(Duration::new(1, 0)) {
512+
terminate_handles.push(container.terminate());
497513
}
498514
}
515+
for handle in terminate_handles.into_iter() {
516+
handle.join().expect("error when joining");
517+
}
518+
}
519+
}
520+
521+
impl Drop for DockerContainers {
522+
523+
fn drop(&mut self) {
524+
self.terminate();
499525
}
500526

501527
}
@@ -1447,8 +1473,8 @@ mod test {
14471473
let containers = DockerContainers::new(0);
14481474
thread::sleep(Duration::new(2, 0));
14491475

1450-
let container = containers.pop(Channel::Stable).unwrap();
1476+
let mut container = containers.pop(Channel::Stable).unwrap();
14511477
assert_eq!(false, container.id.is_empty());
1452-
container.terminate();
1478+
container.terminate().join().expect("error when joining");
14531479
}
14541480
}

0 commit comments

Comments
 (0)
Please sign in to comment.