src/Tools/cache_io.ML
changeset 82090 023845c29d48
parent 82087 aee15b076916
equal deleted inserted replaced
82089:cbcc3ee0b989 82090:023845c29d48
     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