package eio
Effect-based direct-style IO API for OCaml
Install
Dune Dependency
Authors
Maintainers
Sources
eio-0.6.tbz
sha256=ead3eea352dd3d7d11a81ffdbeee6ca94d5e6b3f46de264b4e59689360b3ef38
sha512=0543643da7861f533f9b7ebee8aa30a6868b48ae1e19211666a9b860e9ff8d8a9e135f214a4603d0329f2027277701f6ffd900b6fba3405a538eebc301edaf29
doc/src/eio.core/switch.ml.html
Source file switch.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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
type t = { id : Ctf.id; mutable fibers : int; (* Total, including daemon_fibers and the main function *) mutable daemon_fibers : int; mutable exs : (exn * Printexc.raw_backtrace) option; on_release : (unit -> unit) Lwt_dllist.t; waiter : unit Waiters.t; (* The main [top]/[sub] function may wait here for fibers to finish. *) cancel : Cancel.t; } type hook = | Null | Hook : Domain.id * 'a Lwt_dllist.node -> hook let null_hook = Null let remove_hook = function | Null -> () | Hook (id, n) -> if Domain.self () <> id then invalid_arg "Switch hook removed from wrong domain!"; Lwt_dllist.remove n let dump f t = Fmt.pf f "@[<v2>Switch %d (%d extra fibers):@,%a@]" (t.id :> int) t.fibers Cancel.dump t.cancel let is_finished t = Cancel.is_finished t.cancel (* Check switch belongs to this domain (and isn't finished). It's OK if it's cancelling. *) let check_our_domain t = if is_finished t then invalid_arg "Switch finished!"; if Domain.self () <> t.cancel.domain then invalid_arg "Switch accessed from wrong domain!" (* Check isn't cancelled (or finished). *) let check t = if is_finished t then invalid_arg "Switch finished!"; Cancel.check t.cancel let get_error t = Cancel.get_error t.cancel let combine_exn ex = function | None -> ex | Some ex1 -> Exn.combine ex1 ex (* Note: raises if [t] is finished or called from wrong domain. *) let fail ?(bt=Printexc.get_raw_backtrace ()) t ex = check_our_domain t; if t.exs = None then Ctf.note_resolved t.id ~ex:(Some ex); t.exs <- Some (combine_exn (ex, bt) t.exs); try Cancel.cancel t.cancel ex with Exn.Cancel_hook_failed _ as ex -> let bt = Printexc.get_raw_backtrace () in t.exs <- Some (combine_exn (ex, bt) t.exs) let inc_fibers t = check t; t.fibers <- t.fibers + 1 let dec_fibers t = t.fibers <- t.fibers - 1; if t.daemon_fibers > 0 && t.fibers = t.daemon_fibers then Cancel.cancel t.cancel Exit; if t.fibers = 0 then Waiters.wake_all t.waiter () let with_op t fn = inc_fibers t; Fun.protect fn ~finally:(fun () -> dec_fibers t) let with_daemon t fn = inc_fibers t; t.daemon_fibers <- t.daemon_fibers + 1; Fun.protect fn ~finally:(fun () -> t.daemon_fibers <- t.daemon_fibers - 1; dec_fibers t ) let or_raise = function | Ok x -> x | Error ex -> raise ex let rec await_idle t = (* Wait for fibers to finish: *) while t.fibers > 0 do Ctf.note_try_read t.id; Waiters.await ~mutex:None t.waiter t.id done; (* Call on_release handlers: *) let queue = Lwt_dllist.create () in Lwt_dllist.transfer_l t.on_release queue; let rec release () = match Lwt_dllist.take_opt_r queue with | None when t.fibers = 0 && Lwt_dllist.is_empty t.on_release -> () | None -> await_idle t | Some fn -> begin try fn () with | ex -> fail t ex end; release () in release () let await_idle t = Cancel.protect (fun _ -> await_idle t) let maybe_raise_exs t = match t.exs with | None -> () | Some (ex, bt) -> Printexc.raise_with_backtrace ex bt let create cancel = let id = Ctf.mint_id () in Ctf.note_created id Ctf.Switch; { id; fibers = 1; (* The main function counts as a fiber *) daemon_fibers = 0; exs = None; waiter = Waiters.create (); on_release = Lwt_dllist.create (); cancel; } let run_internal t fn = match fn t with | v -> dec_fibers t; await_idle t; Ctf.note_read t.id; maybe_raise_exs t; (* Check for failure while finishing *) (* Success. *) v | exception ex -> (* Main function failed. Turn the switch off to cancel any running fibers, if it's not off already. *) dec_fibers t; fail t ex; await_idle t; Ctf.note_read t.id; maybe_raise_exs t; assert false let run fn = Cancel.sub (fun cc -> run_internal (create cc) fn) let run_protected fn = let ctx = Effect.perform Cancel.Get_context in Cancel.with_cc ~ctx ~parent:ctx.cancel_context ~protected:true @@ fun cancel -> run_internal (create cancel) fn (* Run [fn ()] in [t]'s cancellation context. This prevents [t] from finishing until [fn] is done, and means that cancelling [t] will cancel [fn]. *) let run_in t fn = with_op t @@ fun () -> let ctx = Effect.perform Cancel.Get_context in let old_cc = ctx.cancel_context in Cancel.move_fiber_to t.cancel ctx; match fn () with | () -> Cancel.move_fiber_to old_cc ctx; | exception ex -> Cancel.move_fiber_to old_cc ctx; raise ex let on_release_full t fn = if Domain.self () = t.cancel.domain then ( match t.cancel.state with | On | Cancelling _ -> Lwt_dllist.add_r fn t.on_release | Finished -> match Cancel.protect fn with | () -> invalid_arg "Switch finished!" | exception ex -> raise (Exn.Multiple [ex; Invalid_argument "Switch finished!"]) ) else ( match Cancel.protect fn with | () -> invalid_arg "Switch accessed from wrong domain!" | exception ex -> raise (Exn.Multiple [ex; Invalid_argument "Switch accessed from wrong domain!"]) ) let on_release t fn = ignore (on_release_full t fn : _ Lwt_dllist.node) let on_release_cancellable t fn = Hook (t.cancel.domain, on_release_full t fn)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>