Source file affect_unix__signal.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
147
148
149
150
151
152
153
154
155
156
157
open Affect
open Affect__base
open Affect.Action.Private
open Affect_unix__fd
module T = struct type t = Sys.signal let compare = Int.compare end
module Map = Map.Make (T)
module Synchronized_map = Synchronized_map.Make (Map)
let pp ppf signal = Format.pp_print_string ppf (Sys.signal_to_string signal)
module Unblocker = struct
type blocked_wait_signal = Action.Blocked.Value.t
type t =
{ gc_count : int Atomic.t;
ready : blocked_wait_signal list list Atomic.t;
blocked : blocked_wait_signal list Synchronized_map.t }
let make () =
let gc_count = Atomic.make 0 and ready = Atomic.make [] in
let blocked = Synchronized_map.make () in
{ gc_count; ready; blocked }
let gc_threshold = 300
let gc_synced u =
let keep_not_synced _sg bs =
let bs = List.filter Action.Blocked.Value.is_not_synced bs in
if List.is_empty bs then None else Some bs
in
Atomic.set u.gc_count 0;
Synchronized_map.update_all keep_not_synced u.blocked
let maybe_gc_synced u =
if Atomic.get u.gc_count > gc_threshold then gc_synced u
let is_empty u =
gc_synced u;
Synchronized_map.is_empty u.blocked && List.is_empty (Atomic.get u.ready)
let has_blocked_after_gc_synced u = not (is_empty u)
let add_wait ~signal ~blocked u =
Atomic.incr u.gc_count;
Synchronized_map.add_to_list signal blocked u.blocked
let ready_to_unblock ~signal u =
match Synchronized_map.find_and_remove signal u.blocked with
| None -> ()
| Some bs -> Atomic.update (List.cons bs) u.ready
let get_ready_and_clear u = Atomic.fold_update (fun l -> l, []) u.ready
let unblock u =
let unblock_list did_unblock b =
Atomic.decr u.gc_count;
Action.Blocked.Value.synced_unblock_is_ours b || did_unblock
in
let unblock_list_list did_unblock l =
List.fold_left unblock_list did_unblock l
in
let did_unblock =
let ready = get_ready_and_clear u in
List.fold_left unblock_list_list false ready
in
maybe_gc_synced u; did_unblock
let nil = make ()
let err_unblocker_not_set () = invalid_arg "Signal 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
let unblockers : t list Atomic.t = Atomic.make []
let list () = Atomic.get unblockers
let register u = Atomic.update (List.cons u) unblockers
let unregister u = Atomic.update (List.filter (fun v -> v != u)) unblockers
end
let wait_block ~signal tag ~blocked =
if Action.Blocked.is_synced blocked then () else
let blocked = Action.Blocked.Value.make tag blocked in
Unblocker.add_wait ~signal ~blocked (Unblocker.get_domain_local ())
let wait_key = Action.Meta.Key.make ~pp_value:pp ()
let wait_meta signal =
let bindings = Action.Meta.[Binding (wait_key, signal)] in
Action.Meta.make ~name:"Unix.Signal.wait" ~bindings ()
let wait' signal tag =
let poll = Action.Primitive.poll_is_none in
let block = wait_block ~signal tag in
Action.Primitive.make ~meta:(wait_meta signal) ~poll ~block
let wait signal = Action.invoke (wait' signal ())
let wait_any signals =
let waits = List.map (fun signal -> wait' signal signal) signals in
Action.invoke (Action.choose waits)
type handler = Default | Ignore | Fun of (Sys.signal -> unit) | Waiters
let syscall_block_bypass = Flagfd.make ()
let clear_syscall_block_bypass () = Flagfd.clear syscall_block_bypass
let syscall_block_bypass_fd = Flagfd.fd syscall_block_bypass
let handle_waiters signal =
List.iter (Unblocker.ready_to_unblock ~signal) (Unblocker.list ());
Flagfd.set syscall_block_bypass
let behaviour_of_handler = function
| Default -> Sys.Signal_default
| Ignore -> Sys.Signal_ignore
| Fun f -> Sys.Signal_handle f
| Waiters -> Sys.Signal_handle handle_waiters
let set signal h = Sys.set_signal signal (behaviour_of_handler h)
let set_and_restore signal h f =
let old = Sys.signal signal (behaviour_of_handler h) in
let finally () = Sys.set_signal signal old in
Fun.protect ~finally f