2 * Copyright (c) 2014, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
13 let lock_fds = ref SMap.empty
16 * Basic lock operations.
18 * We use these for two reasons:
19 * 1. making sure we are only running one instance of hh_server per person on a given dev box
20 * 2. giving a way to hh_client to check if a server is running.
23 let register_lock lock_file
=
24 Sys_utils.with_umask
0o111
begin fun () ->
25 let fd = Unix.descr_of_out_channel
(open_out lock_file
) in
26 let st = Unix.fstat
fd in
27 lock_fds := SMap.add lock_file
(fd, st) !lock_fds;
32 * Grab or check if a file lock is available.
34 * Returns true if the lock is/was available, false otherwise.
36 let _operations lock_file op
: bool =
38 let fd = match SMap.get lock_file
!lock_fds with
39 | None
-> register_lock lock_file
43 (* Note: I'm carefully avoiding opening another fd to the
44 * lock_file when doing this check, because closing any file
45 * descriptor to a given file will release the locks on *all*
46 * file descriptors that point to that file. Fortunately, stat()
47 * gets us our information without opening a fd *)
48 let current_st = Unix.stat lock_file
in
49 Unix.(st.st_dev
= current_st.st_dev
&&
50 st.st_ino
= current_st.st_ino
)
54 if not
(Sys.win32
|| identical_file) then
55 (* Looks like someone (tmpwatch?) deleted the lock file; don't
56 * create another one, because our socket is probably gone too.
57 * We are dead in the water. *)
63 try Unix.lockf
fd op
1
64 with _ when Sys.win32
&& (op
= Unix.F_TLOCK
|| op
= Unix.F_TEST
) ->
65 (* On Windows, F_TLOCK and F_TEST fail if we have the lock ourself *)
66 (* However, we then are the only one to be able to write there. *)
67 ignore
(Unix.lseek
fd 0 Unix.SEEK_SET
: int);
68 (* If we don't have the lock, the following 'write' will
69 throw an exception. *)
70 let wb = Unix.write
fd " " 0 1 in
71 (* When not throwing an exception, the current
72 implementation of `Unix.write` always return `1`. But let's
73 be protective against semantic changes, and better fails
74 than wrongly assume that we own a lock. *)
81 * Grabs the file lock and returns true if it the lock was grabbed
83 let grab lock_file
: bool = _operations lock_file
Unix.F_TLOCK
86 * Releases a file lock.
88 let release lock_file
: bool = _operations lock_file
Unix.F_ULOCK
91 * Gets the server instance-unique integral fd for a given lock file.
93 let fd_of lock_file
: int =
94 match SMap.get lock_file
!lock_fds with
96 | Some
fd -> Obj.magic
fd
99 * Check if the file lock is available without grabbing it.
100 * Returns true if the lock is free.
102 let check lock_file
: bool = _operations lock_file
Unix.F_TEST