Skip to content
This repository was archived by the owner on Jun 27, 2018. It is now read-only.

Commit cab190e

Browse files
committed
Add a global cache for executed requests
Closes #219
1 parent 863f0b5 commit cab190e

File tree

3 files changed

+119
-89
lines changed

3 files changed

+119
-89
lines changed

src/bin/playbot.rs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,28 @@ extern crate url;
1010
use std::fs::File;
1111
use std::io::{self, Read};
1212
use std::str;
13-
use std::u16;
13+
use std::sync::Arc;
1414
use std::thread;
15+
use std::u16;
1516

1617
use hyper::client::Client;
1718
use irc::client::prelude::*;
18-
use rust_playpen::ReleaseChannel;
19+
use rust_playpen::{ReleaseChannel, Cache};
1920
use rustc_serialize::json;
2021
use url::form_urlencoded;
2122

2223
static DEFAULT_CHANNEL: ReleaseChannel = ReleaseChannel::Stable;
2324

24-
fn get_rust_versions() -> Vec<String> {
25+
fn get_rust_versions(cache: &Cache) -> Vec<String> {
2526
let mut versions = Vec::new();
2627
// Note: Keep these in the same order as their discriminant values
2728
for channel in &[ReleaseChannel::Stable,
2829
ReleaseChannel::Beta,
2930
ReleaseChannel::Nightly] {
30-
let (status, output) = rust_playpen::exec(*channel,
31-
"rustc",
32-
vec![String::from("-V")],
33-
String::new()).unwrap();
31+
let (status, output) = cache.exec(*channel,
32+
"rustc",
33+
vec![String::from("-V")],
34+
String::new()).unwrap();
3435
assert!(status.success(), "couldn't get version (this currently needs to run as root)");
3536
let version = str::from_utf8(&output).unwrap();
3637
// Strip the trailing newline
@@ -46,6 +47,7 @@ struct Playbot {
4647
conn: IrcServer,
4748
rust_versions: Vec<String>,
4849
shorten_key: String,
50+
cache: Arc<Cache>,
4951
}
5052

5153
impl Playbot {
@@ -77,10 +79,10 @@ impl Playbot {
7779
full_code: &str,
7880
channel: ReleaseChannel)
7981
-> io::Result<String> {
80-
let (_status, output) = try!(rust_playpen::exec(channel,
81-
"/usr/local/bin/evaluate.sh",
82-
Vec::new(),
83-
String::from(full_code)));
82+
let (_status, output) = try!(self.cache.exec(channel,
83+
"/usr/local/bin/evaluate.sh",
84+
Vec::new(),
85+
String::from(full_code)));
8486

8587
let output_merged = output.splitn(2, |b| *b == b'\xff')
8688
.map(|sub| String::from_utf8_lossy(sub).into_owned())
@@ -206,7 +208,8 @@ fn main() {{
206208
fn main() {
207209
env_logger::init().unwrap();
208210

209-
let rust_versions = get_rust_versions();
211+
let cache = Arc::new(Cache::new());
212+
let rust_versions = get_rust_versions(&cache);
210213

211214
// FIXME All these unwraps are pretty bad UX, but they should only panic on misconfiguration
212215
let mut config = String::new();
@@ -235,17 +238,16 @@ fn main() {
235238
..Config::default()
236239
};
237240

238-
let rust_versions = rust_versions.clone();
239-
let bitly_key = bitly_key.clone();
241+
let server = IrcServer::from_config(conf).unwrap();
242+
server.identify().unwrap();
243+
let mut bot = Playbot {
244+
conn: server,
245+
rust_versions: rust_versions.clone(),
246+
shorten_key: bitly_key.clone(),
247+
cache: cache.clone(),
248+
};
240249
threads.push(thread::spawn(move || {
241-
let server = IrcServer::from_config(conf).unwrap();
242-
server.identify().unwrap();
243-
244-
Playbot {
245-
conn: server,
246-
rust_versions: rust_versions,
247-
shorten_key: bitly_key,
248-
}.main_loop();
250+
bot.main_loop();
249251
}));
250252
}
251253
}

src/bin/playpen.rs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,26 @@ extern crate rustc_serialize;
88
extern crate staticfile;
99
extern crate unicase;
1010

11-
use rust_playpen::*;
12-
13-
use iron::prelude::*;
14-
use iron::status;
15-
use iron::headers;
16-
use iron::middleware::AfterMiddleware;
17-
use iron::modifiers::Header;
18-
use iron::method::Method;
19-
use hyper::header;
20-
use staticfile::Static;
21-
use router::Router;
22-
use unicase::UniCase;
23-
use rustc_serialize::json;
24-
2511
use std::env;
2612
use std::fmt;
2713
use std::io::Read;
2814
use std::path::Path;
2915
use std::process::Command;
16+
use std::sync::Arc;
17+
18+
use hyper::header;
19+
use iron::headers;
20+
use iron::method::Method;
21+
use iron::middleware::{BeforeMiddleware, AfterMiddleware};
22+
use iron::modifiers::Header;
23+
use iron::typemap;
24+
use iron::prelude::*;
25+
use iron::status;
26+
use router::Router;
27+
use rust_playpen::*;
28+
use rustc_serialize::json;
29+
use staticfile::Static;
30+
use unicase::UniCase;
3031

3132
#[derive(Clone, Debug)]
3233
struct XXssProtection(bool);
@@ -100,10 +101,11 @@ fn evaluate(req: &mut Request) -> IronResult<Response> {
100101
args.push(String::from("--test"));
101102
}
102103

103-
let (_status, output) = itry!(rust_playpen::exec(version,
104-
"/usr/local/bin/evaluate.sh",
105-
args,
106-
data.code));
104+
let cache = req.extensions.get::<AddCache>().unwrap();
105+
let (_status, output) = itry!(cache.exec(version,
106+
"/usr/local/bin/evaluate.sh",
107+
args,
108+
data.code));
107109

108110
let mut obj = json::Object::new();
109111
if separate_output {
@@ -167,8 +169,11 @@ fn compile(req: &mut Request) -> IronResult<Response> {
167169
args.push(String::from("--color=always"));
168170
}
169171

170-
let (_status, output) = itry!(
171-
rust_playpen::exec(version, "/usr/local/bin/compile.sh", args, data.code));
172+
let cache = req.extensions.get::<AddCache>().unwrap();
173+
let (_status, output) = itry!(cache.exec(version,
174+
"/usr/local/bin/compile.sh",
175+
args,
176+
data.code));
172177
let mut split = output.splitn(2, |b| *b == b'\xff');
173178
let rustc = String::from_utf8(split.next().unwrap().into()).unwrap();
174179

@@ -201,8 +206,11 @@ fn format(req: &mut Request) -> IronResult<Response> {
201206
let data: FormatReq = itry!(json::decode(&body));
202207
let version = itry!(data.version.map(|v| v.parse()).unwrap_or(Ok(ReleaseChannel::Stable)));
203208

204-
let (status, output) = itry!(
205-
rust_playpen::exec(version, "rustfmt", Vec::new(), data.code));
209+
let cache = req.extensions.get::<AddCache>().unwrap();
210+
let (status, output) = itry!(cache.exec(version,
211+
"rustfmt",
212+
Vec::new(),
213+
data.code));
206214
let output = String::from_utf8(output).unwrap();
207215
let mut response_obj = json::Object::new();
208216
if status.success() {
@@ -229,6 +237,19 @@ impl AfterMiddleware for EnablePostCors {
229237
}
230238
}
231239

240+
struct AddCache {
241+
cache: Arc<Cache>,
242+
}
243+
244+
impl typemap::Key for AddCache { type Value = Arc<Cache>; }
245+
246+
impl BeforeMiddleware for AddCache {
247+
fn before(&self, req: &mut Request) -> IronResult<()> {
248+
req.extensions.insert::<AddCache>(self.cache.clone());
249+
Ok(())
250+
}
251+
}
252+
232253
fn main() {
233254
env_logger::init().unwrap();
234255

@@ -244,6 +265,7 @@ fn main() {
244265

245266
// Use our router as the middleware, and pass the generated response through `EnablePostCors`
246267
let mut chain = Chain::new(router);
268+
chain.link_before(AddCache { cache: Arc::new(Cache::new()) });
247269
chain.link_after(EnablePostCors);
248270

249271
let addr = env::args().skip(1).next().unwrap_or("127.0.0.1".to_string());

src/lib.rs

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ extern crate wait_timeout;
66

77
use lru_cache::LruCache;
88

9-
use std::cell::RefCell;
109
use std::error::Error;
1110
use std::fmt;
1211
use std::io::Write;
1312
use std::io;
1413
use std::process::{Command, ExitStatus, Stdio};
1514
use std::str::FromStr;
15+
use std::sync::Mutex;
1616
use std::time::Duration;
1717

1818
use docker::Container;
@@ -60,57 +60,63 @@ impl FromStr for ReleaseChannel {
6060
}
6161
}
6262

63-
/// Helper method for safely invoking a command inside a playpen
64-
pub fn exec(channel: ReleaseChannel,
65-
cmd: &str,
66-
args: Vec<String>,
67-
input: String)
68-
-> io::Result<(ExitStatus, Vec<u8>)> {
69-
#[derive(PartialEq, Eq, Hash)]
70-
struct CacheKey {
71-
channel: ReleaseChannel,
72-
cmd: String,
73-
args: Vec<String>,
74-
input: String,
75-
}
63+
pub struct Cache {
64+
cache: Mutex<LruCache<CacheKey, (ExitStatus, Vec<u8>)>>,
65+
}
7666

77-
thread_local! {
78-
static CACHE: RefCell<LruCache<CacheKey, (ExitStatus, Vec<u8>)>> =
79-
RefCell::new(LruCache::new(256))
80-
}
67+
#[derive(PartialEq, Eq, Hash)]
68+
struct CacheKey {
69+
channel: ReleaseChannel,
70+
cmd: String,
71+
args: Vec<String>,
72+
input: String,
73+
}
8174

82-
// Build key to look up
83-
let key = CacheKey {
84-
channel: channel,
85-
cmd: cmd.to_string(),
86-
args: args,
87-
input: input,
88-
};
89-
let prev = CACHE.with(|cache| {
90-
cache.borrow_mut().get_mut(&key).map(|x| x.clone())
91-
});
92-
if let Some(prev) = prev {
93-
return Ok(prev)
75+
impl Cache {
76+
pub fn new() -> Cache {
77+
Cache {
78+
cache: Mutex::new(LruCache::new(256)),
79+
}
9480
}
9581

96-
let chan = match channel {
97-
ReleaseChannel::Stable => "stable",
98-
ReleaseChannel::Beta => "beta",
99-
ReleaseChannel::Nightly => "nightly",
100-
};
101-
let container = format!("rust-playpen-{}", chan);
82+
/// Helper method for safely invoking a command inside a playpen
83+
pub fn exec(&self,
84+
channel: ReleaseChannel,
85+
cmd: &str,
86+
args: Vec<String>,
87+
input: String)
88+
-> io::Result<(ExitStatus, Vec<u8>)> {
89+
// Build key to look up
90+
let key = CacheKey {
91+
channel: channel,
92+
cmd: cmd.to_string(),
93+
args: args,
94+
input: input,
95+
};
96+
let mut cache = self.cache.lock().unwrap();
97+
if let Some(prev) = cache.get_mut(&key) {
98+
return Ok(prev.clone())
99+
}
100+
drop(cache);
102101

103-
let container = try!(Container::new(cmd, &key.args, &container));
102+
let chan = match channel {
103+
ReleaseChannel::Stable => "stable",
104+
ReleaseChannel::Beta => "beta",
105+
ReleaseChannel::Nightly => "nightly",
106+
};
107+
let container = format!("rust-playpen-{}", chan);
104108

105-
let tuple = try!(container.run(key.input.as_bytes(), Duration::new(5, 0)));
106-
let (status, mut output, timeout) = tuple;
107-
if timeout {
108-
output.extend_from_slice(b"\ntimeout triggered!");
109+
let container = try!(Container::new(cmd, &key.args, &container));
110+
111+
let tuple = try!(container.run(key.input.as_bytes(), Duration::new(5, 0)));
112+
let (status, mut output, timeout) = tuple;
113+
if timeout {
114+
output.extend_from_slice(b"\ntimeout triggered!");
115+
}
116+
let mut cache = self.cache.lock().unwrap();
117+
cache.insert(key, (status.clone(), output.clone()));
118+
Ok((status, output))
109119
}
110-
CACHE.with(|cache| {
111-
cache.borrow_mut().insert(key, (status.clone(), output.clone()));
112-
});
113-
Ok((status, output))
114120
}
115121

116122
pub enum AsmFlavor {

0 commit comments

Comments
 (0)