4 Cache for output of external processes. |
4 Cache for output of external processes. |
5 *) |
5 *) |
6 |
6 |
7 signature CACHE_IO = |
7 signature CACHE_IO = |
8 sig |
8 sig |
9 (*IO wrapper*) |
|
10 val raw_run: (Path.T -> Path.T -> string) -> string -> Path.T -> Path.T -> Process_Result.T |
|
11 val run: (Path.T -> Path.T -> string) -> string -> Process_Result.T |
|
12 |
|
13 (*cache*) |
|
14 type cache |
9 type cache |
15 val unsynchronized_init: Path.T -> cache |
10 val unsynchronized_init: Path.T -> cache |
16 val cache_path_of: cache -> Path.T |
11 val cache_path_of: cache -> Path.T |
17 val lookup: cache -> string -> Process_Result.T option * string |
12 val lookup: cache -> string -> Process_Result.T option * string |
18 val run_and_cache: cache -> string -> (Path.T -> Path.T -> string) -> string -> Process_Result.T |
13 val run: Bash.params -> string -> Process_Result.T |
19 val run_cached: cache -> (Path.T -> Path.T -> string) -> string -> Process_Result.T |
14 val run_and_cache: cache -> string -> Bash.params -> string -> Process_Result.T |
|
15 val run_cached: cache -> Bash.params -> string -> Process_Result.T |
20 end |
16 end |
21 |
17 |
22 structure Cache_IO : CACHE_IO = |
18 structure Cache_IO : CACHE_IO = |
23 struct |
19 struct |
24 |
|
25 (* IO wrapper *) |
|
26 |
|
27 val cache_io_prefix = "cache-io-" |
|
28 |
|
29 fun try_read_lines path = |
|
30 let |
|
31 fun loop n = |
|
32 (case try File.read_lines path of |
|
33 SOME lines => lines |
|
34 | NONE => if n > 0 then (OS.Process.sleep (seconds 0.05); loop (n - 1)) else []) |
|
35 in if File.exists path then loop (if ML_System.platform_is_windows then 20 else 0) else [] end |
|
36 |
|
37 fun raw_run make_cmd str in_path out_path = |
|
38 let |
|
39 val _ = File.write in_path str |
|
40 val (err, rc) = Isabelle_System.bash_output (make_cmd in_path out_path) |
|
41 val out_lines = try_read_lines out_path |
|
42 in |
|
43 Process_Result.make |
|
44 {rc = rc, out_lines = out_lines, err_lines = split_lines err, timing = Timing.zero} |
|
45 end |
|
46 |
|
47 fun run make_cmd str = |
|
48 Isabelle_System.with_tmp_file cache_io_prefix "" (fn in_path => |
|
49 Isabelle_System.with_tmp_file cache_io_prefix "" (fn out_path => |
|
50 raw_run make_cmd str in_path out_path)) |
|
51 |
|
52 |
|
53 (* cache *) |
|
54 |
20 |
55 abstype cache = Cache of { |
21 abstype cache = Cache of { |
56 path: Path.T, |
22 path: Path.T, |
57 table: (int * (int * int * int) Symtab.table) Synchronized.var } |
23 table: (int * (int * int * int) Symtab.table) Synchronized.var } |
58 with |
24 with |
104 val result = |
70 val result = |
105 Process_Result.make {rc = 0, out_lines = out, err_lines = err, timing = Timing.zero} |
71 Process_Result.make {rc = 0, out_lines = out, err_lines = err, timing = Timing.zero} |
106 in ((SOME result, key), tab) end)) |
72 in ((SOME result, key), tab) end)) |
107 end |
73 end |
108 |
74 |
109 fun run_and_cache (Cache {path = cache_path, table}) key make_cmd str = |
75 fun run cmd input = |
110 let |
76 let |
111 val result = run make_cmd str |
77 val result = cmd |
|
78 |> Bash.input (Bytes.string input) |
|
79 |> Bash.redirect |
|
80 |> Isabelle_System.bash_process |
|
81 val rc = Process_Result.rc result |
|
82 in |
|
83 if rc = Process_Result.startup_failure_rc then |
|
84 Process_Result.make |
|
85 {rc = rc, |
|
86 err_lines = Process_Result.out_lines result, |
|
87 out_lines = [], |
|
88 timing = Process_Result.timing result} |
|
89 else result |
|
90 end |
|
91 |
|
92 fun run_and_cache (Cache {path = cache_path, table}) key cmd input = |
|
93 let |
|
94 val result = run cmd input |
112 val out_lines = Process_Result.out_lines result |
95 val out_lines = Process_Result.out_lines result |
113 val err_lines = Process_Result.err_lines result |
96 val err_lines = Process_Result.err_lines result |
114 val (l1, l2) = apply2 length (out_lines, err_lines) |
97 val (l1, l2) = apply2 length (out_lines, err_lines) |
115 val header = key ^ " " ^ string_of_int l1 ^ " " ^ string_of_int l2 |
98 val header = key ^ " " ^ string_of_int l1 ^ " " ^ string_of_int l2 |
116 val lines = map (suffix "\n") (header :: out_lines @ err_lines) |
99 val lines = map (suffix "\n") (header :: out_lines @ err_lines) |
120 else |
103 else |
121 let val _ = File.append_list cache_path lines |
104 let val _ = File.append_list cache_path lines |
122 in (p+l1+l2+1, Symtab.update (key, (p+1, l1, l2)) tab) end) |
105 in (p+l1+l2+1, Symtab.update (key, (p+1, l1, l2)) tab) end) |
123 in result end |
106 in result end |
124 |
107 |
125 fun run_cached cache make_cmd str = |
108 fun run_cached cache cmd input = |
126 (case lookup cache str of |
109 (case lookup cache input of |
127 (NONE, key) => run_and_cache cache key make_cmd str |
110 (NONE, key) => run_and_cache cache key cmd input |
128 | (SOME output, _) => output) |
111 | (SOME output, _) => output) |
129 |
112 |
130 end |
113 end |
131 |
114 |
132 end |
115 end |