aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTill Hoeppner2014-12-01 17:19:01 +0100
committerTill Hoeppner2014-12-01 17:19:01 +0100
commit0a782cc7c1e0fd32d91e2655a9b5142842fcaf26 (patch)
tree3cde73660c54dae3cc1583211ecc89838f5b0b71 /src
parent7798a8d1132128443cf616eb908dce8affb6e207 (diff)
downloadsersve-0a782cc7c1e0fd32d91e2655a9b5142842fcaf26.tar.gz
sersve-0a782cc7c1e0fd32d91e2655a9b5142842fcaf26.tar.xz
sersve-0a782cc7c1e0fd32d91e2655a9b5142842fcaf26.zip
Less allocation, possibly
Diffstat (limited to 'src')
-rw-r--r--src/sersve.rs75
1 files changed, 63 insertions, 12 deletions
diff --git a/src/sersve.rs b/src/sersve.rs
index 94a62c8..5983666 100644
--- a/src/sersve.rs
+++ b/src/sersve.rs
@@ -8,6 +8,7 @@ extern crate regex;
extern crate "conduit-mime-types" as conduit_mime;
extern crate mime;
extern crate mustache;
+extern crate libc;
use std::{ str, os };
use std::str::from_str;
@@ -15,7 +16,8 @@ use std::path::{ Path, GenericPath };
use std::io::{ fs, Reader };
use std::io::fs::{ File, PathExtensions };
use std::default::Default;
-use std::sync::{ Arc };
+use std::sync::Arc;
+use std::cell::{ RefCell };
use regex::Regex;
@@ -48,7 +50,9 @@ struct Options {
root: Option<Path>,
filter: Option<String>,
max_size: Option<u64>,
- template: Option<String>
+ template: Option<String>,
+ fork: Option<bool>,
+ threads: Option<uint>
}
struct OptCarrier;
@@ -75,6 +79,27 @@ const KEY_URL: &'static str = "url";
const KEY_SIZE: &'static str = "size";
const KEY_NAME: &'static str = "name";
+const DEF_LEN: uint = 10000;
+
+thread_local! (static OUT: RefCell<Vec<u8>> = RefCell::new(Vec::with_capacity(DEF_LEN)))
+
+fn fork() {
+ unsafe {
+ println!("Forking now!");
+ let pid = libc::funcs::posix88::unistd::fork();
+ if pid == 0 {
+ // we are child, now get to work
+ return;
+ } else if pid > 0 {
+ // fork succeeded, die
+ libc::funcs::c95::stdlib::exit(0);
+ } else if pid < 0 {
+ // unsuccessful, don't die
+ return;
+ }
+ }
+}
+
fn size_with_unit(mut size: u64) -> String {
let mut frac = 0;
let mut index = 0;
@@ -116,11 +141,19 @@ fn render<'a>(template: Template, root: Path, dir: Path, files: Vec<Path>, filte
vec
}).build();
- let mut out = Vec::new(); // with_capacity(template.len())
- template.render_data(&mut out, &data);
- // The template should be valid utf8, the filenames might not be
- unsafe { String::from_utf8_unchecked(out) }
-}
+ // Use thread-local storage to reduce allocation
+ OUT.with(|ref_out| {
+ {
+ let mut o = ref_out.borrow_mut();
+ o.clear();
+ template.render_data(&mut *o, &data);
+ }
+ // The template should be valid utf8, the filenames might not be
+ String::from_utf8(ref_out.borrow().clone()).unwrap_or_else(|v| {
+ String::from_utf8_lossy(v[]).into_owned()
+ })
+ })
+ }
fn plain<B: Bodyable>(content: B) -> IronResult<Response> {
Ok(Response::new()
@@ -179,7 +212,7 @@ fn serve(req: &mut Request) -> IronResult<Response> {
Err(e) => return html(e.desc)
};
content.sort_by(|a, b| a.filename_str().unwrap().cmp(b.filename_str().unwrap()));
- html(render(template, root, path, content, filter)[])
+ html(render(template, root, path, content, filter))
}
}
@@ -199,6 +232,8 @@ fn main() {
optopt("f", "filter", "a regular expression to filter the filenames", "REGEX"),
optopt("s", "size", "the maximum size of a file that will be served", "BYTES"),
optopt("t", "template", "a mustache template to use for rendering", "TEMPLATE"),
+ optopt("", "threads", "amount of threads to use for serving", "THREADS"),
+ optflag("", "fork", "fork sersve into a background process"),
optflag("h", "help", "print this help menu")
];
let matches = match getopts(args.tail(), opts) {
@@ -255,19 +290,35 @@ fn main() {
None => None,
_ => panic!("Invalid configuration file. `template` field must be a string.")
};
+ options.fork = match json.get("fork") {
+ Some(&Json::Boolean(b)) => Some(b),
+ None => None,
+ _ => panic!("Invalid configuration file. `fork` field must be a boolean")
+ };
+ options.threads = match json.get("threads") {
+ Some(&Json::U64(u)) => Some(u as uint),
+ None => None,
+ _ => panic!("Invalid configuration file. `threads` field must be a string.")
+ }
});
- let (host, port) = {
+ let (host, port, threads) = {
options.host = matches.opt_str("a").or(options.host);
options.port = matches.opt_str("p").and_then(|p| str::from_str(p[])).or(options.port);
options.root = matches.opt_str("r").and_then(|p| Path::new_opt(p)).or(options.root);
options.filter = matches.opt_str("f").or(options.filter);
options.max_size = matches.opt_str("s").and_then(|s| str::from_str(s[])).or(options.max_size);
options.template = matches.opt_str("t").or(options.template);
+ options.threads = matches.opt_str("threads").and_then(|s| str::from_str(s[])).or(options.threads);
(options.host.clone().unwrap_or(HOST.into_string()),
- options.port.clone().unwrap_or(PORT))
+ options.port.clone().unwrap_or(PORT),
+ options.threads.unwrap_or(os::num_cpus()))
};
+ if options.fork.unwrap_or(false) || matches.opt_present("fork") {
+ fork();
+ }
+
let template = mustache::compile_str(options.template.clone().unwrap_or(OPT_TEMPLATE.into_string())[]);
let state = State {
template: template,
@@ -277,8 +328,8 @@ fn main() {
let mut chain = ChainBuilder::new(serve);
chain.link(Read::<OptCarrier, Arc<Options>>::both(Arc::new(options)));
chain.link(Read::<StateCarrier, Arc<State>>::both(Arc::new(state)));
- match Iron::new(chain).listen((host[], port)) {
+ match Iron::new(chain).listen_with((host[], port), threads) {
Ok(_) => (),
- Err(e) => println!("I'm sorry, I failed you. {}", e)
+ Err(e) => println!("I'm sorry, I failed you.\nError: {}", e)
}
}