Skip to content

Commit bb30f04

Browse files
committed
workcache: Add the ability to save and load the database...
...as well as the ability to discover inputs and outputs.
1 parent dd5c737 commit bb30f04

File tree

1 file changed

+139
-19
lines changed

1 file changed

+139
-19
lines changed

src/libextra/workcache.rs

Lines changed: 139 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,15 @@
1212

1313
use digest::Digest;
1414
use json;
15+
use json::ToJson;
1516
use sha1::Sha1;
1617
use serialize::{Encoder, Encodable, Decoder, Decodable};
1718
use arc::{Arc,RWArc};
1819
use treemap::TreeMap;
19-
2020
use std::cell::Cell;
2121
use std::comm::{PortOne, oneshot};
2222
use std::either::{Either, Left, Right};
23-
use std::io;
24-
use std::run;
25-
use std::task;
23+
use std::{io, os, task};
2624

2725
/**
2826
*
@@ -107,11 +105,27 @@ impl WorkKey {
107105
}
108106
}
109107

108+
// FIXME #8883: The key should be a WorkKey and not a ~str.
109+
// This is working around some JSON weirdness.
110+
#[deriving(Clone, Eq, Encodable, Decodable)]
111+
struct WorkMap(TreeMap<~str, KindMap>);
112+
110113
#[deriving(Clone, Eq, Encodable, Decodable)]
111-
struct WorkMap(TreeMap<WorkKey, ~str>);
114+
struct KindMap(TreeMap<~str, ~str>);
112115

113116
impl WorkMap {
114117
fn new() -> WorkMap { WorkMap(TreeMap::new()) }
118+
119+
fn insert_work_key(&mut self, k: WorkKey, val: ~str) {
120+
let WorkKey { kind, name } = k;
121+
match self.find_mut(&name) {
122+
Some(&KindMap(ref mut m)) => { m.insert(kind, val); return; }
123+
None => ()
124+
}
125+
let mut new_map = TreeMap::new();
126+
new_map.insert(kind, val);
127+
self.insert(name, KindMap(new_map));
128+
}
115129
}
116130

117131
struct Database {
@@ -123,11 +137,15 @@ struct Database {
123137
impl Database {
124138

125139
pub fn new(p: Path) -> Database {
126-
Database {
140+
let mut rslt = Database {
127141
db_filename: p,
128142
db_cache: TreeMap::new(),
129143
db_dirty: false
144+
};
145+
if os::path_exists(&rslt.db_filename) {
146+
rslt.load();
130147
}
148+
rslt
131149
}
132150

133151
pub fn prepare(&self,
@@ -154,6 +172,41 @@ impl Database {
154172
self.db_cache.insert(k,v);
155173
self.db_dirty = true
156174
}
175+
176+
// FIXME #4330: This should have &mut self and should set self.db_dirty to false.
177+
fn save(&self) {
178+
let f = io::file_writer(&self.db_filename, [io::Create, io::Truncate]).unwrap();
179+
self.db_cache.to_json().to_pretty_writer(f);
180+
}
181+
182+
fn load(&mut self) {
183+
assert!(!self.db_dirty);
184+
assert!(os::path_exists(&self.db_filename));
185+
let f = io::file_reader(&self.db_filename);
186+
match f {
187+
Err(e) => fail!("Couldn't load workcache database %s: %s",
188+
self.db_filename.to_str(), e.to_str()),
189+
Ok(r) =>
190+
match json::from_reader(r) {
191+
Err(e) => fail!("Couldn't parse workcache database (from file %s): %s",
192+
self.db_filename.to_str(), e.to_str()),
193+
Ok(r) => {
194+
let mut decoder = json::Decoder(r);
195+
self.db_cache = Decodable::decode(&mut decoder);
196+
}
197+
}
198+
}
199+
}
200+
}
201+
202+
// FIXME #4330: use &mut self here
203+
#[unsafe_destructor]
204+
impl Drop for Database {
205+
fn drop(&self) {
206+
if self.db_dirty {
207+
self.save();
208+
}
209+
}
157210
}
158211

159212
struct Logger {
@@ -172,12 +225,20 @@ impl Logger {
172225
}
173226
}
174227
228+
type FreshnessMap = TreeMap<~str,extern fn(&str,&str)->bool>;
229+
175230
#[deriving(Clone)]
176231
struct Context {
177232
db: RWArc<Database>,
178233
logger: RWArc<Logger>,
179234
cfg: Arc<json::Object>,
180-
freshness: Arc<TreeMap<~str,extern fn(&str,&str)->bool>>
235+
/// Map from kinds (source, exe, url, etc.) to a freshness function.
236+
/// The freshness function takes a name (e.g. file path) and value
237+
/// (e.g. hash of file contents) and determines whether it's up-to-date.
238+
/// For example, in the file case, this would read the file off disk,
239+
/// hash it, and return the result of comparing the given hash and the
240+
/// read hash for equality.
241+
freshness: Arc<FreshnessMap>
181242
}
182243
183244
struct Prep<'self> {
@@ -205,6 +266,7 @@ fn json_encode<T:Encodable<json::Encoder>>(t: &T) -> ~str {
205266
206267
// FIXME(#5121)
207268
fn json_decode<T:Decodable<json::Decoder>>(s: &str) -> T {
269+
debug!("json decoding: %s", s);
208270
do io::with_str_reader(s) |rdr| {
209271
let j = json::from_reader(rdr).unwrap();
210272
let mut decoder = json::Decoder(j);
@@ -230,11 +292,18 @@ impl Context {
230292
pub fn new(db: RWArc<Database>,
231293
lg: RWArc<Logger>,
232294
cfg: Arc<json::Object>) -> Context {
295+
Context::new_with_freshness(db, lg, cfg, Arc::new(TreeMap::new()))
296+
}
297+
298+
pub fn new_with_freshness(db: RWArc<Database>,
299+
lg: RWArc<Logger>,
300+
cfg: Arc<json::Object>,
301+
freshness: Arc<FreshnessMap>) -> Context {
233302
Context {
234303
db: db,
235304
logger: lg,
236305
cfg: cfg,
237-
freshness: Arc::new(TreeMap::new())
306+
freshness: freshness
238307
}
239308
}
240309

@@ -249,6 +318,35 @@ impl Context {
249318

250319
}
251320

321+
impl Exec {
322+
pub fn discover_input(&mut self, dependency_kind:&str,
323+
// Discovered input
324+
dependency_name: &str, dependency_val: &str) {
325+
debug!("Discovering input %s %s %s", dependency_kind, dependency_name, dependency_val);
326+
self.discovered_inputs.insert_work_key(WorkKey::new(dependency_kind, dependency_name),
327+
dependency_val.to_owned());
328+
}
329+
pub fn discover_output(&mut self, dependency_kind:&str,
330+
// Discovered output
331+
dependency_name: &str, dependency_val: &str) {
332+
debug!("Discovering output %s %s %s", dependency_kind, dependency_name, dependency_val);
333+
self.discovered_outputs.insert_work_key(WorkKey::new(dependency_kind, dependency_name),
334+
dependency_val.to_owned());
335+
}
336+
337+
// returns pairs of (kind, name)
338+
pub fn lookup_discovered_inputs(&self) -> ~[(~str, ~str)] {
339+
let mut rs = ~[];
340+
for (k, v) in self.discovered_inputs.iter() {
341+
for (k1, _) in v.iter() {
342+
rs.push((k1.clone(), k.clone()));
343+
}
344+
}
345+
rs
346+
}
347+
348+
}
349+
252350
impl<'self> Prep<'self> {
253351
fn new(ctxt: &'self Context, fn_name: &'self str) -> Prep<'self> {
254352
Prep {
@@ -257,18 +355,30 @@ impl<'self> Prep<'self> {
257355
declared_inputs: WorkMap::new()
258356
}
259357
}
358+
359+
pub fn lookup_declared_inputs(&self) -> ~[~str] {
360+
let mut rs = ~[];
361+
for (_, v) in self.declared_inputs.iter() {
362+
for (inp, _) in v.iter() {
363+
rs.push(inp.clone());
364+
}
365+
}
366+
rs
367+
}
260368
}
261369

262370
impl<'self> Prep<'self> {
263-
fn declare_input(&mut self, kind:&str, name:&str, val:&str) {
264-
self.declared_inputs.insert(WorkKey::new(kind, name),
371+
pub fn declare_input(&mut self, kind:&str, name:&str, val:&str) {
372+
debug!("Declaring input %s %s %s", kind, name, val);
373+
self.declared_inputs.insert_work_key(WorkKey::new(kind, name),
265374
val.to_owned());
266375
}
267376

268377
fn is_fresh(&self, cat: &str, kind: &str,
269378
name: &str, val: &str) -> bool {
270379
let k = kind.to_owned();
271380
let f = self.ctxt.freshness.get().find(&k);
381+
debug!("freshness for: %s/%s/%s/%s", cat, kind, name, val)
272382
let fresh = match f {
273383
None => fail!("missing freshness-function for '%s'", kind),
274384
Some(f) => (*f)(name, val)
@@ -286,27 +396,31 @@ impl<'self> Prep<'self> {
286396
}
287397

288398
fn all_fresh(&self, cat: &str, map: &WorkMap) -> bool {
289-
for (k, v) in map.iter() {
290-
if ! self.is_fresh(cat, k.kind, k.name, *v) {
291-
return false;
399+
for (k_name, kindmap) in map.iter() {
400+
for (k_kind, v) in kindmap.iter() {
401+
if ! self.is_fresh(cat, *k_kind, *k_name, *v) {
402+
return false;
292403
}
404+
}
293405
}
294406
return true;
295407
}
296408

297-
fn exec<T:Send +
409+
pub fn exec<T:Send +
298410
Encodable<json::Encoder> +
299411
Decodable<json::Decoder>>(
300-
&'self self, blk: ~fn(&Exec) -> T) -> T {
412+
&'self self, blk: ~fn(&mut Exec) -> T) -> T {
301413
self.exec_work(blk).unwrap()
302414
}
303415

304416
fn exec_work<T:Send +
305417
Encodable<json::Encoder> +
306418
Decodable<json::Decoder>>( // FIXME(#5121)
307-
&'self self, blk: ~fn(&Exec) -> T) -> Work<'self, T> {
419+
&'self self, blk: ~fn(&mut Exec) -> T) -> Work<'self, T> {
308420
let mut bo = Some(blk);
309421

422+
debug!("exec_work: looking up %s and %?", self.fn_name,
423+
self.declared_inputs);
310424
let cached = do self.ctxt.db.read |db| {
311425
db.prepare(self.fn_name, &self.declared_inputs)
312426
};
@@ -316,21 +430,26 @@ impl<'self> Prep<'self> {
316430
if self.all_fresh("declared input",&self.declared_inputs) &&
317431
self.all_fresh("discovered input", disc_in) &&
318432
self.all_fresh("discovered output", disc_out) => {
433+
debug!("Cache hit!");
434+
debug!("Trying to decode: %? / %? / %?",
435+
disc_in, disc_out, *res);
319436
Left(json_decode(*res))
320437
}
321438

322439
_ => {
440+
debug!("Cache miss!");
323441
let (port, chan) = oneshot();
324442
let blk = bo.take_unwrap();
325443
let chan = Cell::new(chan);
326444

445+
// What happens if the task fails?
327446
do task::spawn {
328-
let exe = Exec {
447+
let mut exe = Exec {
329448
discovered_inputs: WorkMap::new(),
330449
discovered_outputs: WorkMap::new(),
331450
};
332451
let chan = chan.take();
333-
let v = blk(&exe);
452+
let v = blk(&mut exe);
334453
chan.send((exe, v));
335454
}
336455
Right(port)
@@ -371,9 +490,10 @@ impl<'self, T:Send +
371490
}
372491

373492

374-
//#[test]
493+
#[test]
375494
fn test() {
376495
use std::io::WriterUtil;
496+
use std::run;
377497

378498
let pth = Path("foo.c");
379499
{

0 commit comments

Comments
 (0)