src/Tools/Cache_IO/cache_io.ML
changeset 35140 117247018b54
equal deleted inserted replaced
35139:082fa4bd403d 35140:117247018b54
       
     1 (*  Title:      Tools/Cache_IO/cache_io.ML
       
     2     Author:     Sascha Boehme, TU Muenchen
       
     3 
       
     4 Cache for output of external processes.
       
     5 *)
       
     6 
       
     7 signature CACHE_IO =
       
     8 sig
       
     9   val with_tmp_file: string -> (Path.T -> 'a) -> 'a
       
    10   val run: (Path.T -> string) -> Path.T -> string list
       
    11   val run': (Path.T -> Path.T -> string) -> Path.T -> string list * string list
       
    12 
       
    13   type cache
       
    14   val make: Path.T -> cache
       
    15   val cache_path_of: cache -> Path.T
       
    16   val cached: cache -> (Path.T -> string) -> Path.T -> string list
       
    17   val cached': cache -> (Path.T -> Path.T -> string) -> Path.T ->
       
    18     string list * string list
       
    19 end
       
    20 
       
    21 structure Cache_IO : CACHE_IO =
       
    22 struct
       
    23 
       
    24 fun with_tmp_file name f =
       
    25   let
       
    26     val path = File.tmp_path (Path.explode (name ^ serial_string ()))
       
    27     val x = Exn.capture f path
       
    28     val _ = try File.rm path
       
    29   in Exn.release x end
       
    30 
       
    31 fun run' make_cmd in_path =
       
    32   with_tmp_file "cache-io-" (fn out_path =>
       
    33     let
       
    34       val (out2, _) = bash_output (make_cmd in_path out_path)
       
    35       val out1 = the_default [] (try (rev o File.fold_lines cons out_path) [])
       
    36     in (out1, split_lines out2) end)
       
    37 
       
    38 fun run make_cmd = snd o run' (fn in_path => fn _ => make_cmd in_path)
       
    39 
       
    40 
       
    41 
       
    42 abstype cache = Cache of {
       
    43   path: Path.T,
       
    44   table: (int * (int * int * int) Symtab.table) Synchronized.var }
       
    45 with
       
    46 
       
    47 
       
    48 fun cache_path_of (Cache {path, ...}) = path
       
    49 
       
    50 
       
    51 fun load cache_path =
       
    52   let
       
    53     fun err () = error ("Cache IO: corrupted cache file: " ^
       
    54       File.shell_path cache_path)
       
    55 
       
    56     fun int_of_string s =
       
    57       (case read_int (explode s) of
       
    58         (i, []) => i
       
    59       | _ => err ())    
       
    60 
       
    61     fun split line =
       
    62       (case space_explode " " line of
       
    63         [key, len1, len2] => (key, int_of_string len1, int_of_string len2)
       
    64       | _ => err ())
       
    65 
       
    66     fun parse line ((i, l), tab) =
       
    67       if i = l
       
    68       then
       
    69         let val (key, l1, l2) = split line
       
    70         in ((i+1, l+l1+l2+1), Symtab.update (key, (i+1, l1, l2)) tab) end
       
    71       else ((i+1, l), tab)
       
    72   in apfst fst (File.fold_lines parse cache_path ((1, 1), Symtab.empty)) end 
       
    73 
       
    74 fun make path =
       
    75   let val table = if File.exists path then load path else (1, Symtab.empty)
       
    76   in Cache {path=path, table=Synchronized.var (Path.implode path) table} end
       
    77 
       
    78 
       
    79 fun get_hash_key path =
       
    80   let
       
    81     val arg = File.shell_path path
       
    82     val (out, res) = bash_output (getenv "COMPUTE_HASH_KEY" ^ " " ^ arg)
       
    83   in
       
    84     if res = 0 then hd (split_lines out)
       
    85     else error ("Cache IO: failed to generate hash key for file " ^ arg)
       
    86   end
       
    87 
       
    88 fun load_cached_result cache_path (p, len1, len2) =
       
    89   let
       
    90     fun load line (i, xsp) =
       
    91       if i < p then (i+1, xsp)
       
    92       else if i < p + len1 then (i+1, apfst (cons line) xsp)
       
    93       else if i < p + len2 then (i+1, apsnd (cons line) xsp)
       
    94       else (i, xsp)
       
    95   in pairself rev (snd (File.fold_lines load cache_path (1, ([], [])))) end
       
    96 
       
    97 fun cached' (Cache {path=cache_path, table}) make_cmd in_path =
       
    98   let val key = get_hash_key in_path
       
    99   in
       
   100     (case Symtab.lookup (snd (Synchronized.value table)) key of
       
   101       SOME pos => load_cached_result cache_path pos
       
   102     | NONE =>
       
   103         let
       
   104           val res as (out, err) = run' make_cmd in_path
       
   105           val (l1, l2) = pairself length res
       
   106           val header = key ^ " " ^ string_of_int l1 ^ " " ^ string_of_int l2
       
   107           val lines = map (suffix "\n") (header :: out @ err)
       
   108 
       
   109           val _ = Synchronized.change table (fn (p, tab) =>
       
   110             if Symtab.defined tab key then (p, tab)
       
   111             else
       
   112               let val _ = File.append_list cache_path lines
       
   113               in (p+l1+l2+1, Symtab.update (key, (p+1, l1, l2)) tab) end)
       
   114         in res end)
       
   115   end
       
   116 
       
   117 fun cached cache make_cmd =
       
   118   snd o cached' cache (fn in_path => fn _ => make_cmd in_path)
       
   119 
       
   120 end
       
   121 end