staload "libc/sys/SATS/types.sats"
staload "libc/SATS/errno.sats"
staload "libc/SATS/fcntl.sats"
staload "libc/SATS/random.sats"
staload "libc/SATS/unistd.sats"
#define LOCKDIR "/tmp/"
%{^
#define LOCKDIR "/tmp/"
%}
#define MAXTRIES 10
#define NAPLENGTH 2
%{^
static
ats_ptr_type lockpath (ats_ptr_type name) {
static char path[100] ;
if (snprintf (path, sizeof(path), "%s%s", LOCKDIR, name) > sizeof(path))
return (char*)0;
return path ;
} // end of [lockpath]
%}
extern
fun lockpath (name: !READ(string)): Stropt = "lockpath"
extern fun lock (name: !READ(string)): bool
extern fun unlock (name: !READ(string)): bool
macdef errno_is_EAGAIN () = (errno_get () = EAGAIN)
macdef errno_is_EEXIST () = (errno_get () = EEXIST)
implement lock (name) = let
val path = lockpath (name)
val ans = stropt_is_some (path)
in
if ans then let
val path = stropt_unsome (path)
val flag = O_WRONLY lor O_CREAT lor O_EXCL
val err = loop (path, flag, 0) where {
fun loop (
path: !READ(string), flag: flag_t, n: int
) : int = let
val (pf_fdopt | fd) = open_flag_err (path, flag)
in
if (fd >= 0) then let
prval open_v_succ (pf_fd) = pf_fdopt
val () = close_loop_exn (pf_fd | fd)
in
0
end else let
prval open_v_fail () = pf_fdopt
in
if errno_is_EEXIST () then
(if n >= MAXTRIES - 1 then (errno_set EAGAIN; ~1) else loop (path, flag, n+1))
else ~1
end
end } in
if err = 0 then true else false
end else false
end
implement unlock (name) = let
val path = lockpath (name)
val ans = stropt_is_some (path)
in
if ans then let
val path = stropt_unsome (path)
val err = unlink (path)
in
if err <> ~1 then true else false
end else false
end
fn testlock (): void = loop (1) where {
#define N 4
#define NAME "accounts"
fun loop (i: natLte N): void = let
val status = (if lock (NAME) then let
val pid = getpid ()
val () = printf ("Process %ld acquired the lock\n", @(lint_of_pid pid))
val _leftover = sleep (randint 5 + 1); val ans = unlock (NAME)
val () = if ~ans then (prerr "Exit: [testlock] failed"; exit 1)
in
1
end else let
val () = if errno_is_EAGAIN () then let
val pid = getpid ()
val () = printf ("Process %ld tired of busy waiting\n", @(lint_of_pid pid))
val () = errno_reset ()
in
end else (prerr "Exit: [testlock] failed"; exit 1)
in
0
end) : natLte 2 val _leftover = sleep (randint 5 + 5) val i = i + status
in
if i <= N then loop (i) else ()
end }
implement main () = testlock ()