123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106(**
* Copyright (c) 2015, 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.
*
*)letlock_fds=refSMap.empty(**
* Basic lock operations.
*
* We use these for two reasons:
* 1. making sure we are only running one instance of hh_server per person on a given dev box
* 2. giving a way to hh_client to check if a server is running.
*)letregister_locklock_file=let_=Sys_utils.mkdir_no_fail(Filename.dirnamelock_file)inSys_utils.with_umask0o111beginfun()->letfd=Unix.descr_of_out_channel(open_outlock_file)inletst=Unix.fstatfdinlock_fds:=SMap.addlock_file(fd,st)!lock_fds;fdend(**
* Grab or check if a file lock is available.
*
* Returns true if the lock is/was available, false otherwise.
*)let_operationslock_fileop:bool=tryletfd=matchSMap.getlock_file!lock_fdswith|None->register_locklock_file|Some(fd,st)->letidentical_file=try(* Note: I'm carefully avoiding opening another fd to the
* lock_file when doing this check, because closing any file
* descriptor to a given file will release the locks on *all*
* file descriptors that point to that file. Fortunately, stat()
* gets us our information without opening a fd *)letcurrent_st=Unix.statlock_fileinUnix.(st.st_dev=current_st.st_dev&&st.st_ino=current_st.st_ino)with_->falseinifnot(Sys.win32||identical_file)then(* Looks like someone (tmpwatch?) deleted the lock file; don't
* create another one, because our socket is probably gone too.
* We are dead in the water. *)raiseExitelsefdinlet_=tryUnix.lockffdop1with_whenSys.win32&&(op=Unix.F_TLOCK||op=Unix.F_TEST)->(* On Windows, F_TLOCK and F_TEST fail if we have the lock ourself *)(* However, we then are the only one to be able to write there. *)ignore(Unix.lseekfd0Unix.SEEK_SET:int);(* If we don't have the lock, the following 'write' will
throw an exception. *)letwb=Unix.writefd(Bytes.make1' ')01in(* When not throwing an exception, the current
implementation of `Unix.write` always return `1`. But let's
be protective against semantic changes, and better fails
than wrongly assume that we own a lock. *)assert(wb=1)intruewith_->false(**
* Grabs the file lock and returns true if it the lock was grabbed
*)letgrablock_file:bool=_operationslock_fileUnix.F_TLOCK(**
* Releases a file lock.
*)letreleaselock_file:bool=_operationslock_fileUnix.F_ULOCKletblocking_grab_then_releaselock_file=ignore(_operationslock_fileUnix.F_LOCK);ignore(releaselock_file)(**
* Gets the server instance-unique integral fd for a given lock file.
*)letfd_oflock_file:int=matchSMap.getlock_file!lock_fdswith|None->-1|Somefd->Obj.magicfd(**
* Check if the file lock is available without grabbing it.
* Returns true if the lock is free.
*)letchecklock_file:bool=_operationslock_fileUnix.F_TEST