@@ -3,7 +3,6 @@ open Picos
3
3
let intr_sig = Sys. sigusr2
4
4
let is_intr_sig s = s == intr_sig
5
5
let intr_sigs = [ intr_sig ]
6
- let intr_pending = Atomic. make 0
7
6
8
7
type cancel_at =
9
8
| Cancel_at : {
@@ -49,6 +48,21 @@ type state = {
49
48
new_ex : return_on list ref ;
50
49
}
51
50
51
+ type intr_status = Cleared | Signaled
52
+
53
+ type _ tdt =
54
+ | Nothing : [> `Nothing ] tdt
55
+ | Req : {
56
+ state : state ;
57
+ mutable unused : bool ;
58
+ computation : intr_status Computation .t ;
59
+ }
60
+ -> [> `Req ] tdt
61
+
62
+ type req = R : [< `Nothing | `Req ] tdt -> req [@@ unboxed]
63
+ type counter_state = { value : int ; req : req }
64
+
65
+ let intr_pending = Atomic. make { value = 0 ; req = R Nothing }
52
66
let exit_exn_bt = Exn_bt. get_callstack 0 Exit
53
67
54
68
let key =
@@ -180,9 +194,10 @@ and select_thread_continue s rd wr ex (rd_fds, wr_fds, ex_fds) =
180
194
let ex = process_fds ex_fds ex (Picos_thread_atomic. exchange s.new_ex [] ) in
181
195
let tos = process_timeouts s in
182
196
let tos =
183
- let n = Atomic. get intr_pending in
184
- if n = 0 then tos
197
+ let state = Atomic. get intr_pending in
198
+ if state.value = 0 then tos
185
199
else begin
200
+ assert (0 < state.value);
186
201
Unix. kill (Unix. getpid () ) intr_sig;
187
202
let idle = 0.000_001 (* 1μs *) in
188
203
if tos < 0.0 || idle < = tos then idle else tos
@@ -302,17 +317,7 @@ let await_on file_descr op =
302
317
(* *)
303
318
304
319
module Intr = struct
305
- type intr_status = Cleared | Signaled
306
-
307
- type _ tdt =
308
- | Nothing : [> `Nothing ] tdt
309
- | Req : {
310
- state : state ;
311
- mutable computation : intr_status Computation .t ;
312
- }
313
- -> [> `Req ] tdt
314
-
315
- type t = T : [< `Nothing | `Req ] tdt -> t [@@ unboxed]
320
+ type t = req
316
321
317
322
let cleared =
318
323
let computation = Computation. create () in
@@ -321,29 +326,42 @@ module Intr = struct
321
326
322
327
let intr_key =
323
328
Picos_tls. new_key @@ fun () : [ `Req ] tdt ->
324
- Req { state = get () ; computation = cleared }
329
+ Req { state = get () ; unused = true ; computation = cleared }
330
+
331
+ let [@ inline] use = function R Nothing -> () | R (Req r ) -> r.unused < - false
325
332
326
333
let handle _ =
327
334
let (Req r) = Picos_tls. get intr_key in
328
335
Computation. return r.computation Signaled
329
336
330
- let intr_action trigger (Req r : [ `Req ] tdt ) id =
337
+ let rec finish (Req r as req : [ `Req ] tdt ) backoff =
338
+ let before = Atomic. get intr_pending in
339
+ r.unused && before.req != R req
340
+ && begin
341
+ use before.req;
342
+ let after = { value = before.value + 1 ; req = R req } in
343
+ if Atomic. compare_and_set intr_pending before after then
344
+ after.value = 1
345
+ else finish req (Backoff. once backoff)
346
+ end
347
+
348
+ let intr_action trigger (Req r as req : [ `Req ] tdt ) id =
331
349
match Computation. await r.computation with
332
350
| Cleared ->
333
351
(* No signal needs to be delivered. *)
334
352
remove_action trigger r.state id
335
353
| Signaled ->
336
354
(* Signal was delivered before timeout. *)
337
355
remove_action trigger r.state id;
338
- if Atomic. fetch_and_add intr_pending 1 = 0 then begin
356
+ if finish req Backoff. default then
339
357
(* We need to make sure at least one select thread will keep on
340
358
triggering interrupts. *)
341
359
wakeup r.state `Alive
342
- end
343
360
| exception Exit ->
344
361
(* The timeout was triggered. This must have been called from the
345
362
select thread, which will soon trigger an interrupt. *)
346
- Atomic. incr intr_pending
363
+ let _ : bool = finish req Backoff. default in
364
+ ()
347
365
348
366
let () =
349
367
if not Sys. win32 then begin
@@ -353,37 +371,47 @@ module Intr = struct
353
371
assert (old_behavior == Signal_default )
354
372
end
355
373
356
- let nothing = T Nothing
374
+ let nothing = R Nothing
357
375
358
376
let [@ alert " -handler" ] req ~seconds =
359
377
if Sys. win32 then
360
378
invalid_arg " Picos_select.Intr is not supported on Windows"
361
379
else begin
362
380
let time = to_deadline ~seconds in
363
- let (Req r as req) = Picos_tls. get intr_key in
364
- assert (not (Computation. is_running r.computation));
365
- let id = next_id r.state in
366
- let computation = Computation. with_action req id intr_action in
367
- r.computation < - computation;
381
+ (* assert (not (Computation.is_running r.computation)); *)
382
+ let state = get () in
383
+ let id = next_id state in
384
+ let computation = Computation. create () in
385
+ let (Req _ as req : [ `Req ] tdt ) =
386
+ Req { state; unused = true ; computation }
387
+ in
388
+ let _ : bool =
389
+ Computation. try_attach computation
390
+ (Trigger. from_action req id intr_action)
391
+ in
392
+ Picos_tls. set intr_key req;
368
393
let entry = Cancel_at { time; exn_bt = exit_exn_bt; computation } in
369
- add_timeout r. state id entry;
370
- let was_blocked : int list = Thread. sigmask SIG_UNBLOCK intr_sigs in
371
- assert (List. exists is_intr_sig was_blocked);
372
- T req
394
+ add_timeout state id entry;
395
+ let _was_blocked : int list = Thread. sigmask SIG_UNBLOCK intr_sigs in
396
+ (* assert (List.exists is_intr_sig was_blocked); *)
397
+ R req
373
398
end
374
399
400
+ let rec decr backoff =
401
+ let before = Atomic. get intr_pending in
402
+ use before.req;
403
+ let after = { value = before.value - 1 ; req = R Nothing } in
404
+ assert (0 < = after.value);
405
+ if not (Atomic. compare_and_set intr_pending before after) then
406
+ decr (Backoff. once backoff)
407
+
375
408
let clr = function
376
- | T Nothing -> ()
377
- | T (Req r ) ->
378
- let was_blocked : int list = Thread. sigmask SIG_BLOCK intr_sigs in
379
- assert (not (List. exists is_intr_sig was_blocked));
409
+ | R Nothing -> ()
410
+ | R (Req r as req ) ->
411
+ let _was_blocked : int list = Thread. sigmask SIG_BLOCK intr_sigs in
412
+ (* assert (not (List.exists is_intr_sig was_blocked)); *)
380
413
if not (Computation. try_return r.computation Cleared ) then begin
381
- while
382
- let count = Atomic. get intr_pending in
383
- count < = 0
384
- || not (Atomic. compare_and_set intr_pending count (count - 1 ))
385
- do
386
- Thread. yield ()
387
- done
414
+ let _ : bool = finish req Backoff. default in
415
+ decr Backoff. default
388
416
end
389
417
end
0 commit comments