package affect
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Streamlined and natural concurrency model for OCaml
Install
dune-project
Dependency
Authors
Maintainers
Sources
affect-0.0.0.tbz
sha512=b328f6696e60c489da8cc2e8fad0537ca6634a39fc0c5de5840d4f98561f110617ef79ba8285ee8427dd99908c6b3d39114973598cddc5ded91abcce3b91b9a6
doc/src/affect.unix/affect_unix__signal.ml.html
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(*--------------------------------------------------------------------------- Copyright (c) 2026 The affect programmers. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) open Affect open Affect__base open Affect.Action.Private open Affect_unix__fd (* Signals *) 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) (* Unblocking signal waiters *) module Unblocker = struct type blocked_wait_signal = Action.Blocked.Value.t type t = { gc_count : int Atomic.t; (* See [handle_waiters] to understand why we have this [ready] field. *) 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 (* Domain local signal unblocker Each domain may know about a signal unblocker via domain local storage. This allows APIs calls in Unix.Singal to register signal waits on on the appropriate unblocker. *) 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 (* Due to the global nature of signal handling we maintain a list of all existing signal unblockers. This list is updated when Unix.unblocker values install and deinstall. The list is consluted by signal handlers with [Waiters] handlers. *) 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) (* Handling *) type handler = Default | Ignore | Fun of (Sys.signal -> unit) | Waiters (* We use a socket in non-blocking mode (see Flagfd) to unblock the system call used to unblock fds since EINTR is only triggered on the thread that handles the signal. Things we would also like to avoid are a dedicated thread for signals and complex signal masking setups. Hopefully this works regardless of all that. This should work if multiple domains are blocked at the same time. *) 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 = (* Note we cannot run the unblockers in the signal handler because it may be executed by one of our sleeping domain which is holding it's [Affect__scheduler.Local_domain.state_mutex] lock. Since running the unblockers schedules work with [Affect__scheduler.schedule_ready] and that the latter tries to wakeup a sleeping domain and thus take the state lock, if it's the same domain that is woken up we get a deadlock and a nice Sys_error("Mutex.lock: Resource deadlock avoided") from OCaml. *) 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
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>