Source file affect_unix__select.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
open Affect__base
open Affect.Action.Private
open Affect_unix__fd
type blocked = Action.Blocked.Value.t
type blocked_state = { readable : blocked list; writable : blocked list; }
let unblock_fd rset wset fd st =
let readable =
if List.mem fd rset
then (List.iter Action.Blocked.Value.synced_unblock st.readable; [])
else List.filter Action.Blocked.Value.is_not_synced st.readable
in
let writable =
if List.mem fd wset
then (List.iter Action.Blocked.Value.synced_unblock st.writable; [])
else List.filter Action.Blocked.Value.is_not_synced st.writable
in
if List.is_empty readable && List.is_empty writable then None else
Some { readable; writable }
type t = { fds : blocked_state Fd.Synchronized_map.t; block_bypass : Flagfd.t }
let make () =
let fds = Fd.Synchronized_map.make () and block_bypass = Flagfd.make () in
{ fds; block_bypass }
let dispose u = Flagfd.dispose u.block_bypass
let gc_fd fd st =
let is_not_synced = Action.Blocked.Value.is_not_synced in
let readable = List.filter is_not_synced st.readable in
let writable = List.filter is_not_synced st.writable in
if List.is_empty readable && List.is_empty writable then None else
Some { readable; writable }
let gc_ebadf fd st =
match gc_fd fd st with
| None -> None
| Some st as keep ->
try Unix.set_nonblock fd ; keep with
| Unix.Unix_error (EBADF, _, _) ->
let bt = Printexc.get_raw_backtrace () in
let exn op = Unix.Unix_error (EBADF, op, "") in
let unblock exn (Action.Blocked.Value.V (_, b)) =
let candidate = Action.Result.Exn (exn, bt) in
Action.Blocked.synced_unblock ~candidate b
in
List.iter (unblock (exn "Unix.wait_readable")) st.readable;
List.iter (unblock (exn "Unix.wait_writable")) st.writable;
None
let gc_synced u = Fd.Synchronized_map.update_all gc_fd u.fds
let gc_ebadf u = Fd.Synchronized_map.update_all gc_ebadf u.fds
let is_empty u = gc_synced u; Fd.Synchronized_map.is_empty u.fds
let fd_sets u =
let add fd st (rset, wset) =
let rset = if List.is_empty st.readable then rset else fd :: rset in
let wset = if List.is_empty st.writable then wset else fd :: wset in
(rset, wset)
in
Fd.Synchronized_map.fold add u.fds ([], [])
let add_wait_readable u fd ~blocked =
let add_read r = function
| None -> Some { readable = [r]; writable = [] }
| Some st -> Some { st with readable = r :: st.readable }
in
Fd.Synchronized_map.update fd (add_read blocked) u.fds
let add_wait_writable u fd ~blocked =
let add_write w = function
| None -> Some { readable = []; writable = [w] }
| Some st -> Some { st with writable = w :: st.writable }
in
Fd.Synchronized_map.update fd (add_write blocked) u.fds
let rec unblock u ~timeout_ns =
let timeout_s = match timeout_ns with
| None -> -1.
| Some dur_s -> Affect_unix__timeline.mtime_span_ns_to_float_s dur_s
in
let blocking = timeout_s <> 0. in
let rset, wset = fd_sets u in
let rset = Affect_unix__signal.syscall_block_bypass_fd :: rset in
let rset = if blocking then Flagfd.fd u.block_bypass :: rset else rset in
let did_unblock = match Unix.select rset wset [] timeout_s with
| exception Unix.Unix_error (EBADF, _, _) ->
gc_ebadf u; unblock u ~timeout_ns
| exception Unix.Unix_error (EINTR, _, _) ->
Affect_unix__signal.clear_syscall_block_bypass ();
if blocking then Flagfd.clear u.block_bypass;
false
| rset, wset, _eset ->
Affect_unix__signal.clear_syscall_block_bypass ();
if blocking then Flagfd.clear u.block_bypass;
Fd.Synchronized_map.update_all (unblock_fd rset wset) u.fds;
not (List.is_empty rset) || not (List.is_empty wset)
in
did_unblock
let set_block_bypass u = Flagfd.set u.block_bypass
let nil = let nil = make () in dispose nil; nil
let err_unblocker_not_set () = invalid_arg "Fd unblocker not set"
let key : t Domain.DLS.key = Domain.DLS.new_key (fun () -> nil)
let set_domain_local u = Domain.DLS.set key u
let clear_domain_local () = set_domain_local nil
let get_domain_local () =
let u = Domain.DLS.get key in
if Repr.phys_equal u nil then err_unblocker_not_set () else u