(* * Copyright (c) 2009 - 2013 Monoidics ltd. * Copyright (c) 2013 - present Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. *) open! Utils module L = Logging module F = Format (** Generic serializer *) type 'a serializer = (string -> 'a option) * (DB.filename -> 'a option) * (DB.filename -> 'a -> unit) (** Serialization key, used to distinguish versions of serializers and avoid assert faults *) type key = int (** current key for tenv, procedure summary, cfg, error trace, call graph *) let tenv_key, summary_key, cfg_key, trace_key, cg_key, analysis_results_key, cluster_key, attributes_key, lint_issues_key = 425184201, 160179325, 1062389858, 221487792, 477305409, 799050016, 579094948, 972393003, 852343110 (** version of the binary files, to be incremented for each change *) let version = 26 (** Retry the function while an exception filtered is thrown, or until the timeout in seconds expires. *) let retry_exception timeout catch_exn f x = let init_time = Unix.gettimeofday () in let expired () = Unix.gettimeofday () -. init_time >= timeout in let rec retry () = try f x with | e when catch_exn e && not (expired ()) -> retry () in retry () let create_serializer (key : key) : 'a serializer = let match_data ((key': key), (version': int), (value: 'a)) source_msg = if key <> key' then begin L.err "Wrong key in when loading data from %s@\n" source_msg; None end else if version <> version' then begin L.err "Wrong version in when loading data from %s@\n" source_msg; None end else Some value in let from_string (str : string) : 'a option = try match_data (Marshal.from_string str 0) "string" with Sys_error _ -> None in let from_file (fname_ : DB.filename) : 'a option = let fname = DB.filename_to_string fname_ in match open_in_bin fname with | exception Sys_error _ -> None | inc -> let read () = try seek_in inc 0 ; match_data (Marshal.from_channel inc) fname with | Sys_error _ -> None in let timeout = 1.0 in let catch_exn = function | End_of_file -> true | Failure _ -> true (* handle input_value: truncated object *) | _ -> false in (* Retry to read for 1 second in case of end of file, *) (* which indicates that another process is writing the same file. *) SymOp.try_finally (fun () -> retry_exception timeout catch_exn read ()) (fun () -> close_in inc) in let to_file (fname : DB.filename) (value : 'a) = let fname_str = DB.filename_to_string fname in (* support nonblocking reads and writes in parallel: *) (* write to a tmp file and use rename which is atomic *) let fname_tmp = Filename.temp_file ~temp_dir:(Filename.dirname fname_str) (Filename.basename fname_str) ".tmp" in let outc = open_out_bin fname_tmp in Marshal.to_channel outc (key, version, value) []; close_out outc; Unix.rename fname_tmp fname_str in (from_string, from_file, to_file) let from_string (serializer : 'a serializer) = let (s, _, _) = serializer in s let from_file (serializer : 'a serializer) = let (_, s, _) = serializer in s let to_file (serializer : 'a serializer) = let (_, _, s) = serializer in s (* (** Generate random keys, to be used in an ocaml toplevel *) let generate_keys () = Random.self_init (); let max_rand_int = 0x3FFFFFFF (* determined by Rand library *) in let gen () = Random.int max_rand_int in gen (), gen (), gen (), gen (), gen (), gen () *)