Summary: The ProcLocker uses files as locks and relies on the guarantees of the `Unix.open_file` function when using `O_CREAT` and `O_EXCL` simultaneously. - `setup`: creates a directory for the lock files inside `infe-out` and deletes its content if it already existed. - `clean`: does nothing for now. Any file locks that may have been left unlocked are removed by the `setup` in the next run. This way the user can see what locks were taken if the program crashes. - `lock_exn`: try to lock the `Procname` and if it can't releases all the locks that is currently holding. - `unlock`: removes the corresponding file. Reviewed By: ngorogiannis Differential Revision: D19639402 fbshipit-source-id: e02f277ffmaster
parent
9648632bd5
commit
ce32a1b917
@ -0,0 +1,29 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
|
||||
exception UnlockNotLocked of Procname.t
|
||||
|
||||
let locks_dir = Config.procnames_locks_dir
|
||||
|
||||
let setup () = Utils.rmtree locks_dir ; Utils.create_dir locks_dir
|
||||
|
||||
let clean () = ()
|
||||
|
||||
let filename_from pname = locks_dir ^/ Procname.to_filename pname
|
||||
|
||||
let unlock pname =
|
||||
try Unix.unlink (filename_from pname)
|
||||
with Unix.Unix_error (Unix.ENOENT, _, _) -> raise (UnlockNotLocked pname)
|
||||
|
||||
|
||||
let try_lock pname =
|
||||
try
|
||||
Unix.openfile ~mode:[O_CREAT; O_EXCL; O_RDONLY] (filename_from pname) |> Unix.close ;
|
||||
true
|
||||
with Unix.Unix_error (Unix.EEXIST, _, _) -> false
|
@ -0,0 +1,24 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
|
||||
exception UnlockNotLocked of Procname.t
|
||||
|
||||
val setup : unit -> unit
|
||||
(** This should be called once before trying to lock Anything. *)
|
||||
|
||||
val try_lock : Procname.t -> bool
|
||||
(** true = the lock belongs to the calling process. false = the lock belongs to a different worker *)
|
||||
|
||||
val unlock : Procname.t -> unit
|
||||
(** This will work as a cleanup function because after calling unlock all the workers that need an
|
||||
unlocked Proc should find it's summary already Cached. Throws if the lock had not been taken. *)
|
||||
|
||||
val clean : unit -> unit
|
||||
(** This should be called when locks will no longer be used to remove any files or state that's not
|
||||
necessary. *)
|
@ -0,0 +1,42 @@
|
||||
(*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*)
|
||||
|
||||
open! IStd
|
||||
open OUnit2
|
||||
|
||||
let a_pname = Procname.from_string_c_fun "a_c_fun_name"
|
||||
|
||||
let test_try_lock_already_locked _test_ctxt =
|
||||
ProcLocker.(
|
||||
setup () ;
|
||||
try_lock a_pname |> ignore ;
|
||||
assert_bool "Should not be able to lock a Procname that's already locked."
|
||||
(not (try_lock a_pname)))
|
||||
|
||||
|
||||
let test_lock_after_unlock _test_ctxt =
|
||||
ProcLocker.(
|
||||
setup () ;
|
||||
try_lock a_pname |> ignore ;
|
||||
unlock a_pname ;
|
||||
try_lock a_pname |> ignore ;
|
||||
unlock a_pname)
|
||||
|
||||
|
||||
let test_unlocking_unlocked_fails _text_ctxt =
|
||||
ProcLocker.(
|
||||
setup () ;
|
||||
try_lock a_pname |> ignore ;
|
||||
unlock a_pname ;
|
||||
assert_raises (UnlockNotLocked a_pname) (fun () -> unlock a_pname))
|
||||
|
||||
|
||||
let tests =
|
||||
"restart_scheduler_suite"
|
||||
>::: [ "test_try_lock_already_locked" >:: test_try_lock_already_locked
|
||||
; "test_lock_after_unlock" >:: test_lock_after_unlock
|
||||
; "test_unlocking_unlocked_fails" >:: test_unlocking_unlocked_fails ]
|
Loading…
Reference in new issue