Source file test_locks.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
open Core
open File_path.Operators
module Unix = Core_unix

let%expect_test "Procfs.get_locks parses sample /proc/locks content" =
  let content =
    {|
1: POSIX  ADVISORY  READ 5433 08:01:7864448 128 128
2: FLOCK  ADVISORY  WRITE 2001 08:01:7864554 0 EOF
3: FLOCK  ADVISORY  WRITE 1568 00:2f:32388 0 EOF
4: -> FLOCK  ADVISORY  WRITE 807744 00:6f:793922 0 EOF
5: POSIX  ADVISORY  WRITE 699 00:16:28457 0 EOF
6: POSIX  ADVISORY  WRITE 764 00:16:21448 0 0
7: POSIX  ADVISORY  READ 3548 08:01:7867240 1 1
8: POSIX  ADVISORY  READ 3548 08:01:7865567 1826 2335
9: OFDLCK ADVISORY  WRITE -1 08:01:8713209 128 191
46: POSIX  ADVISORY  WRITE 163033 00:10e:11 0 EOF
|}
    |> String.lstrip
  in
  content
  |> String.split_lines
  |> Procfs.For_testing.parse_locks
  |> [%sexp_of: Procfs.Lock.t list]
  |> print_s;
  [%expect
    {|
    (((mechanism (Posix (5433) ((start 128) (end_ (At 128))))) (type_ Read)
      (inode ((((major 8) (minor 1)) 7864448))))
     ((mechanism (Flock (2001))) (type_ Write)
      (inode ((((major 8) (minor 1)) 7864554))))
     ((mechanism (Flock (1568))) (type_ Write)
      (inode ((((major 0) (minor 47)) 32388))))
     ((mechanism (Posix (699) ((start 0) (end_ Eof)))) (type_ Write)
      (inode ((((major 0) (minor 22)) 28457))))
     ((mechanism (Posix (764) ((start 0) (end_ (At 0))))) (type_ Write)
      (inode ((((major 0) (minor 22)) 21448))))
     ((mechanism (Posix (3548) ((start 1) (end_ (At 1))))) (type_ Read)
      (inode ((((major 8) (minor 1)) 7867240))))
     ((mechanism (Posix (3548) ((start 1826) (end_ (At 2335))))) (type_ Read)
      (inode ((((major 8) (minor 1)) 7865567))))
     ((mechanism (Ofd ((start 128) (end_ (At 191))))) (type_ Write)
      (inode ((((major 8) (minor 1)) 8713209))))
     ((mechanism (Posix (163033) ((start 0) (end_ Eof)))) (type_ Write)
      (inode ((((major 0) (minor 270)) 11)))))
    |}]
;;

let%test_unit "[get_pids_holding_flock]" =
  Filesystem_core.within_temp_dir (fun () ->
    let lockfile = ?/"lockfile" in
    (* Test exclusive lock. *)
    let flock = Filesystem_core.flock_create lockfile in
    let pids = Procfs.get_pids_holding_flock lockfile in
    assert ([%equal: Pid.t list] pids [ Unix.getpid () ]);
    (* Test unlocked. *)
    Filesystem_core.funlock flock;
    assert (List.is_empty (Procfs.get_pids_holding_flock lockfile));
    (* Test shared lock. *)
    let parent_sock, child_sock =
      Unix.socketpair ~domain:PF_UNIX ~kind:SOCK_DGRAM ~protocol:0 ()
    in
    let send fd msg =
      let bytes_sent =
        Unix.send_substring fd ~buf:msg ~pos:0 ~len:(String.length msg) ~mode:[]
      in
      assert (bytes_sent = String.length msg)
    in
    let await fd msg =
      let buf = Bytes.create (String.length msg) in
      let bytes_received = Unix.recv fd ~buf ~pos:0 ~len:(String.length msg) ~mode:[] in
      assert (bytes_received = String.length msg);
      assert (String.equal (Bytes.to_string buf) msg)
    in
    match Unix.fork () with
    | `In_the_parent child_pid ->
      Unix.close child_sock;
      let (_ : Filesystem_core.Flock.t) = Filesystem_core.flock ~shared:true lockfile in
      await parent_sock "locked";
      let pids = Procfs.get_pids_holding_flock lockfile |> Pid.Set.of_list in
      send parent_sock "exit";
      Unix.close parent_sock;
      let child_exit_status = Unix.waitpid child_pid in
      assert (Result.is_ok child_exit_status);
      let expected_pids = Pid.Set.of_list [ Unix.getpid (); child_pid ] in
      assert ([%equal: Pid.Set.t] pids expected_pids)
    | `In_the_child ->
      Unix.close parent_sock;
      let (_ : Filesystem_core.Flock.t) = Filesystem_core.flock ~shared:true lockfile in
      send child_sock "locked";
      await child_sock "exit")
;;