mirror of
https://github.com/ocaml-multicore/eio.git
synced 2025-10-10 00:03:07 -04:00
Keep an explicit tree of cancellation contexts
This is slightly more efficient, and might also be useful to allow dumping out the tree for debugging.
This commit is contained in:
parent
021330b258
commit
9c41d9fdf2
@ -17,14 +17,25 @@ type state =
|
|||||||
|
|
||||||
type t = {
|
type t = {
|
||||||
mutable state : state;
|
mutable state : state;
|
||||||
|
parent : t;
|
||||||
|
children : t Lwt_dllist.t;
|
||||||
|
protected : bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
type fibre_context = {
|
||||||
|
tid : Ctf.id;
|
||||||
|
mutable cancel : t;
|
||||||
}
|
}
|
||||||
|
|
||||||
(* A dummy value for bootstrapping *)
|
(* A dummy value for bootstrapping *)
|
||||||
let boot = {
|
let rec boot = {
|
||||||
state = Finished;
|
state = Finished;
|
||||||
|
parent = boot;
|
||||||
|
children = Lwt_dllist.create ();
|
||||||
|
protected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
type _ eff += Set_cancel : t -> t eff
|
type _ eff += Get_context : fibre_context eff
|
||||||
|
|
||||||
let cancelled t =
|
let cancelled t =
|
||||||
match t.state with
|
match t.state with
|
||||||
@ -49,70 +60,68 @@ let is_finished t =
|
|||||||
| Finished -> true
|
| Finished -> true
|
||||||
| On _ | Cancelling _ -> false
|
| On _ | Cancelling _ -> false
|
||||||
|
|
||||||
(* Runs [fn] with a fresh cancellation context value (but does not install it). *)
|
(* Runs [fn] with a fresh cancellation context. *)
|
||||||
let with_cc fn =
|
let with_cc ~ctx ?parent ~protected fn =
|
||||||
let q = Lwt_dllist.create () in
|
let q = Lwt_dllist.create () in
|
||||||
let t = { state = On q } in
|
let parent = Option.value parent ~default:ctx.cancel in
|
||||||
Fun.protect (fun () -> fn t)
|
let children = Lwt_dllist.create () in
|
||||||
~finally:(fun () -> t.state <- Finished)
|
let t = { state = On q; parent; children; protected } in
|
||||||
|
let node = Lwt_dllist.add_r t parent.children in
|
||||||
|
ctx.cancel <- t;
|
||||||
|
match fn t with
|
||||||
|
| x -> ctx.cancel <- t.parent; t.state <- Finished; Lwt_dllist.remove node; x
|
||||||
|
| exception ex -> ctx.cancel <- t.parent; t.state <- Finished; Lwt_dllist.remove node; raise ex
|
||||||
|
|
||||||
let protect_full fn =
|
let protect fn =
|
||||||
with_cc @@ fun t ->
|
let ctx = perform Get_context in
|
||||||
let x =
|
with_cc ~ctx ?parent:None ~protected:true @@ fun t ->
|
||||||
let old = perform (Set_cancel t) in
|
let x = fn () in
|
||||||
Fun.protect (fun () -> fn t)
|
|
||||||
~finally:(fun () -> ignore (perform (Set_cancel old)))
|
|
||||||
in
|
|
||||||
check t;
|
check t;
|
||||||
x
|
x
|
||||||
|
|
||||||
let protect fn = protect_full (fun (_ : t) -> fn ())
|
let add_hook t hook =
|
||||||
|
|
||||||
let add_hook_unwrapped t hook =
|
|
||||||
match t.state with
|
match t.state with
|
||||||
| Finished -> invalid_arg "Cancellation context finished!"
|
| Finished -> invalid_arg "Cancellation context finished!"
|
||||||
| Cancelling (ex, _) -> protect (fun () -> hook ex); Hook.null
|
| Cancelling (ex, _) -> protect (fun () -> hook (Cancelled ex)); Hook.null
|
||||||
| On q ->
|
| On q ->
|
||||||
let node = Lwt_dllist.add_r hook q in
|
let node = Lwt_dllist.add_r hook q in
|
||||||
(fun () -> Lwt_dllist.remove node)
|
(fun () -> Lwt_dllist.remove node)
|
||||||
|
|
||||||
let add_hook t hook = add_hook_unwrapped t (fun ex -> hook (Cancelled ex))
|
let rec cancel t ex =
|
||||||
|
|
||||||
let cancel t ex =
|
|
||||||
match t.state with
|
match t.state with
|
||||||
| Finished -> invalid_arg "Cancellation context finished!"
|
| Finished -> invalid_arg "Cancellation context finished!"
|
||||||
| Cancelling _ -> ()
|
| Cancelling _ -> ()
|
||||||
| On q ->
|
| On q ->
|
||||||
let bt = Printexc.get_raw_backtrace () in
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
t.state <- Cancelling (ex, bt);
|
t.state <- Cancelling (ex, bt);
|
||||||
|
let cex = Cancelled ex in
|
||||||
let rec aux () =
|
let rec aux () =
|
||||||
match Lwt_dllist.take_opt_r q with
|
match Lwt_dllist.take_opt_r q with
|
||||||
| None -> []
|
| None -> Lwt_dllist.fold_r (cancel_child ex) t.children []
|
||||||
| Some f ->
|
| Some f ->
|
||||||
match f ex with
|
match f cex with
|
||||||
| () -> aux ()
|
| () -> aux ()
|
||||||
| exception ex2 -> ex2 :: aux ()
|
| exception ex2 -> ex2 :: aux ()
|
||||||
in
|
in
|
||||||
match protect aux with
|
match protect aux with
|
||||||
| [] -> ()
|
| [] -> ()
|
||||||
| exns -> raise (Cancel_hook_failed exns)
|
| exns -> raise (Cancel_hook_failed exns)
|
||||||
|
and cancel_child ex t acc =
|
||||||
|
if t.protected then acc
|
||||||
|
else match cancel t ex with
|
||||||
|
| () -> acc
|
||||||
|
| exception ex -> ex :: acc
|
||||||
|
|
||||||
let sub fn =
|
let sub fn =
|
||||||
with_cc @@ fun t ->
|
let ctx = perform Get_context in
|
||||||
|
with_cc ~ctx ?parent:None ~protected:false @@ fun t ->
|
||||||
let x =
|
let x =
|
||||||
(* Can't use Fun.protect here because of [Fun.Finally_raised]. *)
|
match fn t with
|
||||||
let old = perform (Set_cancel t) in
|
|
||||||
match
|
|
||||||
let unhook = add_hook_unwrapped old (cancel t) in
|
|
||||||
Fun.protect (fun () -> fn t) ~finally:unhook
|
|
||||||
with
|
|
||||||
| x ->
|
| x ->
|
||||||
ignore (perform (Set_cancel old));
|
check t.parent;
|
||||||
check old;
|
|
||||||
x
|
x
|
||||||
| exception ex ->
|
| exception ex ->
|
||||||
ignore (perform (Set_cancel old));
|
check t.parent;
|
||||||
check old;
|
|
||||||
raise ex
|
raise ex
|
||||||
in
|
in
|
||||||
match t.state with
|
match t.state with
|
||||||
@ -123,11 +132,7 @@ let sub fn =
|
|||||||
(* Like [sub], but it's OK if the new context is cancelled.
|
(* Like [sub], but it's OK if the new context is cancelled.
|
||||||
(instead, return the parent context on exit so the caller can check that) *)
|
(instead, return the parent context on exit so the caller can check that) *)
|
||||||
let sub_unchecked fn =
|
let sub_unchecked fn =
|
||||||
with_cc @@ fun t ->
|
let ctx = perform Get_context in
|
||||||
let old = perform (Set_cancel t) in
|
with_cc ~ctx ?parent:None ~protected:false @@ fun t ->
|
||||||
Fun.protect (fun () ->
|
fn t;
|
||||||
let unhook = add_hook_unwrapped old (cancel t) in
|
t.parent
|
||||||
Fun.protect (fun () -> fn t) ~finally:unhook
|
|
||||||
)
|
|
||||||
~finally:(fun () -> ignore (perform (Set_cancel old)));
|
|
||||||
old
|
|
||||||
|
@ -257,7 +257,7 @@ module Stdenv = struct
|
|||||||
end
|
end
|
||||||
|
|
||||||
module Private = struct
|
module Private = struct
|
||||||
type context = Suspend.context = {
|
type context = Cancel.fibre_context = {
|
||||||
tid : Ctf.id;
|
tid : Ctf.id;
|
||||||
mutable cancel : Cancel.t;
|
mutable cancel : Cancel.t;
|
||||||
}
|
}
|
||||||
@ -268,8 +268,8 @@ module Private = struct
|
|||||||
| Suspend = Suspend.Suspend
|
| Suspend = Suspend.Suspend
|
||||||
| Fork = Fibre.Fork
|
| Fork = Fibre.Fork
|
||||||
| Fork_ignore = Fibre.Fork_ignore
|
| Fork_ignore = Fibre.Fork_ignore
|
||||||
|
| Get_context = Cancel.Get_context
|
||||||
| Trace = Std.Trace
|
| Trace = Std.Trace
|
||||||
| Set_cancel = Cancel.Set_cancel
|
|
||||||
end
|
end
|
||||||
let boot_cancel = Cancel.boot
|
let boot_cancel = Cancel.boot
|
||||||
end
|
end
|
||||||
|
@ -283,9 +283,6 @@ module Cancel : sig
|
|||||||
This can be used to clean up resources on cancellation.
|
This can be used to clean up resources on cancellation.
|
||||||
However, it is usually better to use {!Switch.on_release} (which calls this for you). *)
|
However, it is usually better to use {!Switch.on_release} (which calls this for you). *)
|
||||||
|
|
||||||
val protect_full : (t -> 'a) -> 'a
|
|
||||||
(** [protect_full fn] is like {!protect}, but also gives access to the new context. *)
|
|
||||||
|
|
||||||
val check : t -> unit
|
val check : t -> unit
|
||||||
(** [check t] checks that [t] hasn't been cancelled.
|
(** [check t] checks that [t] hasn't been cancelled.
|
||||||
@raise Cancelled If the context has been cancelled. *)
|
@raise Cancelled If the context has been cancelled. *)
|
||||||
@ -619,7 +616,7 @@ module Private : sig
|
|||||||
| Fork : (unit -> 'a) -> 'a Promise.t eff
|
| Fork : (unit -> 'a) -> 'a Promise.t eff
|
||||||
(** See {!Fibre.fork} *)
|
(** See {!Fibre.fork} *)
|
||||||
|
|
||||||
| Fork_ignore : (unit -> unit) -> unit eff
|
| Fork_ignore : (context -> unit) -> unit eff
|
||||||
(** See {!Fibre.fork_ignore} *)
|
(** See {!Fibre.fork_ignore} *)
|
||||||
|
|
||||||
| Trace : (?__POS__:(string * int * int * int) -> ('a, Format.formatter, unit, unit) format4 -> 'a) eff
|
| Trace : (?__POS__:(string * int * int * int) -> ('a, Format.formatter, unit, unit) format4 -> 'a) eff
|
||||||
@ -628,8 +625,7 @@ module Private : sig
|
|||||||
If the system is not ready to receive the trace output,
|
If the system is not ready to receive the trace output,
|
||||||
the whole domain must block until it is. *)
|
the whole domain must block until it is. *)
|
||||||
|
|
||||||
| Set_cancel : Cancel.t -> Cancel.t eff
|
| Get_context : context eff
|
||||||
(** [Set_cancel c] sets the running fibre's cancel context to [c] and returns the previous context. *)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
val boot_cancel : Cancel.t
|
val boot_cancel : Cancel.t
|
||||||
|
@ -12,16 +12,14 @@ let fork ~sw ~exn_turn_off f =
|
|||||||
in
|
in
|
||||||
perform (Fork f)
|
perform (Fork f)
|
||||||
|
|
||||||
type _ eff += Fork_ignore : (unit -> unit) -> unit eff
|
type _ eff += Fork_ignore : (Cancel.fibre_context -> unit) -> unit eff
|
||||||
|
|
||||||
let fork_ignore ~sw f =
|
let fork_ignore ~sw f =
|
||||||
let f () =
|
let f child =
|
||||||
Switch.with_op sw @@ fun () ->
|
Switch.with_op sw @@ fun () ->
|
||||||
try
|
try
|
||||||
Cancel.protect_full @@ fun c ->
|
Cancel.with_cc ~ctx:child ~parent:sw.cancel ~protected:false @@ fun _t ->
|
||||||
let hook = Switch.add_cancel_hook_unwrapped sw (Cancel.cancel c) in
|
f ()
|
||||||
Fun.protect f
|
|
||||||
~finally:(fun () -> Hook.remove hook)
|
|
||||||
with ex ->
|
with ex ->
|
||||||
Switch.turn_off sw ex
|
Switch.turn_off sw ex
|
||||||
in
|
in
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
open EffectHandlers
|
open EffectHandlers
|
||||||
|
|
||||||
type context = {
|
|
||||||
tid : Ctf.id;
|
|
||||||
mutable cancel : Cancel.t;
|
|
||||||
}
|
|
||||||
|
|
||||||
type 'a enqueue = ('a, exn) result -> unit
|
type 'a enqueue = ('a, exn) result -> unit
|
||||||
type _ eff += Suspend : (context -> 'a enqueue -> unit) -> 'a eff
|
type _ eff += Suspend : (Cancel.fibre_context -> 'a enqueue -> unit) -> 'a eff
|
||||||
|
|
||||||
let enter_unchecked fn = perform (Suspend fn)
|
let enter_unchecked fn = perform (Suspend fn)
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ let rec turn_off t ex =
|
|||||||
Cancel.cancel t.cancel ex
|
Cancel.cancel t.cancel ex
|
||||||
|
|
||||||
let add_cancel_hook t hook = Cancel.add_hook t.cancel hook
|
let add_cancel_hook t hook = Cancel.add_hook t.cancel hook
|
||||||
let add_cancel_hook_unwrapped t hook = Cancel.add_hook_unwrapped t.cancel hook
|
|
||||||
|
|
||||||
let with_op t fn =
|
let with_op t fn =
|
||||||
check t;
|
check t;
|
||||||
@ -43,7 +42,7 @@ let with_op t fn =
|
|||||||
Waiters.wake_all t.waiter (Ok ())
|
Waiters.wake_all t.waiter (Ok ())
|
||||||
)
|
)
|
||||||
|
|
||||||
let await_internal waiters id (ctx:Suspend.context) enqueue =
|
let await_internal waiters id (ctx:Cancel.fibre_context) enqueue =
|
||||||
let cleanup_hooks = Queue.create () in
|
let cleanup_hooks = Queue.create () in
|
||||||
let when_resolved r =
|
let when_resolved r =
|
||||||
Queue.iter Waiters.remove_waiter cleanup_hooks;
|
Queue.iter Waiters.remove_waiter cleanup_hooks;
|
||||||
|
@ -927,7 +927,7 @@ let run ?(queue_depth=64) ?(block_size=4096) main =
|
|||||||
let rec fork ~tid ~cancel:initial_cancel fn =
|
let rec fork ~tid ~cancel:initial_cancel fn =
|
||||||
Ctf.note_switch tid;
|
Ctf.note_switch tid;
|
||||||
let fibre = { Eio.Private.tid; cancel = initial_cancel } in
|
let fibre = { Eio.Private.tid; cancel = initial_cancel } in
|
||||||
match_with fn ()
|
match_with fn fibre
|
||||||
{ retc = (fun () -> schedule st);
|
{ retc = (fun () -> schedule st);
|
||||||
exnc = raise;
|
exnc = raise;
|
||||||
effc = fun (type a) (e : a eff) ->
|
effc = fun (type a) (e : a eff) ->
|
||||||
@ -972,11 +972,7 @@ let run ?(queue_depth=64) ?(block_size=4096) main =
|
|||||||
);
|
);
|
||||||
schedule st
|
schedule st
|
||||||
)
|
)
|
||||||
| Eio.Private.Effects.Set_cancel cancel -> Some (fun k ->
|
| Eio.Private.Effects.Get_context -> Some (fun k -> continue k fibre)
|
||||||
let old = fibre.cancel in
|
|
||||||
fibre.cancel <- cancel;
|
|
||||||
continue k old
|
|
||||||
)
|
|
||||||
| Eio.Private.Effects.Suspend f -> Some (fun k ->
|
| Eio.Private.Effects.Suspend f -> Some (fun k ->
|
||||||
let k = { Suspended.k; fibre } in
|
let k = { Suspended.k; fibre } in
|
||||||
f fibre (function
|
f fibre (function
|
||||||
@ -994,7 +990,7 @@ let run ?(queue_depth=64) ?(block_size=4096) main =
|
|||||||
fork
|
fork
|
||||||
~tid:id
|
~tid:id
|
||||||
~cancel:fibre.cancel
|
~cancel:fibre.cancel
|
||||||
(fun () ->
|
(fun _fibre ->
|
||||||
match f () with
|
match f () with
|
||||||
| x -> Promise.fulfill resolver x
|
| x -> Promise.fulfill resolver x
|
||||||
| exception ex ->
|
| exception ex ->
|
||||||
@ -1007,8 +1003,8 @@ let run ?(queue_depth=64) ?(block_size=4096) main =
|
|||||||
enqueue_thread st k ();
|
enqueue_thread st k ();
|
||||||
let child = Ctf.note_fork () in
|
let child = Ctf.note_fork () in
|
||||||
Ctf.note_switch child;
|
Ctf.note_switch child;
|
||||||
fork ~tid:child ~cancel:fibre.cancel (fun () ->
|
fork ~tid:child ~cancel:fibre.cancel (fun new_fibre ->
|
||||||
match f () with
|
match f new_fibre with
|
||||||
| () ->
|
| () ->
|
||||||
Ctf.note_resolved child ~ex:None
|
Ctf.note_resolved child ~ex:None
|
||||||
| exception ex ->
|
| exception ex ->
|
||||||
@ -1029,7 +1025,7 @@ let run ?(queue_depth=64) ?(block_size=4096) main =
|
|||||||
in
|
in
|
||||||
let main_done = ref false in
|
let main_done = ref false in
|
||||||
let `Exit_scheduler =
|
let `Exit_scheduler =
|
||||||
fork ~tid:(Ctf.mint_id ()) ~cancel:Eio.Private.boot_cancel (fun () ->
|
fork ~tid:(Ctf.mint_id ()) ~cancel:Eio.Private.boot_cancel (fun _fibre ->
|
||||||
Fun.protect (fun () -> Eio.Cancel.protect (fun () -> main stdenv))
|
Fun.protect (fun () -> Eio.Cancel.protect (fun () -> main stdenv))
|
||||||
~finally:(fun () -> main_done := true)
|
~finally:(fun () -> main_done := true)
|
||||||
) in
|
) in
|
||||||
|
@ -586,7 +586,7 @@ let run main =
|
|||||||
let rec fork ~tid ~cancel:initial_cancel fn =
|
let rec fork ~tid ~cancel:initial_cancel fn =
|
||||||
Ctf.note_switch tid;
|
Ctf.note_switch tid;
|
||||||
let fibre = { Eio.Private.tid; cancel = initial_cancel } in
|
let fibre = { Eio.Private.tid; cancel = initial_cancel } in
|
||||||
match_with fn ()
|
match_with fn fibre
|
||||||
{ retc = (fun () -> ());
|
{ retc = (fun () -> ());
|
||||||
exnc = (fun e -> raise e);
|
exnc = (fun e -> raise e);
|
||||||
effc = fun (type a) (e : a eff) ->
|
effc = fun (type a) (e : a eff) ->
|
||||||
@ -607,7 +607,7 @@ let run main =
|
|||||||
fork
|
fork
|
||||||
~tid:id
|
~tid:id
|
||||||
~cancel:fibre.cancel
|
~cancel:fibre.cancel
|
||||||
(fun () ->
|
(fun _new_fibre ->
|
||||||
match f () with
|
match f () with
|
||||||
| x -> Promise.fulfill resolver x
|
| x -> Promise.fulfill resolver x
|
||||||
| exception ex ->
|
| exception ex ->
|
||||||
@ -620,19 +620,14 @@ let run main =
|
|||||||
enqueue_thread k ();
|
enqueue_thread k ();
|
||||||
let child = Ctf.note_fork () in
|
let child = Ctf.note_fork () in
|
||||||
Ctf.note_switch child;
|
Ctf.note_switch child;
|
||||||
fork ~tid:child ~cancel:fibre.cancel (fun () ->
|
fork ~tid:child ~cancel:fibre.cancel (fun new_fibre ->
|
||||||
match f () with
|
match f new_fibre with
|
||||||
| () ->
|
| () ->
|
||||||
Ctf.note_resolved child ~ex:None
|
Ctf.note_resolved child ~ex:None
|
||||||
| exception ex ->
|
| exception ex ->
|
||||||
Ctf.note_resolved child ~ex:(Some ex)
|
Ctf.note_resolved child ~ex:(Some ex)
|
||||||
))
|
))
|
||||||
| Eio.Private.Effects.Set_cancel cancel ->
|
| Eio.Private.Effects.Get_context -> Some (fun k -> continue k fibre)
|
||||||
Some (fun k ->
|
|
||||||
let old = fibre.cancel in
|
|
||||||
fibre.cancel <- cancel;
|
|
||||||
continue k old
|
|
||||||
)
|
|
||||||
| Enter_unchecked fn -> Some (fun k ->
|
| Enter_unchecked fn -> Some (fun k ->
|
||||||
fn { Suspended.k; fibre }
|
fn { Suspended.k; fibre }
|
||||||
)
|
)
|
||||||
@ -650,7 +645,7 @@ let run main =
|
|||||||
}
|
}
|
||||||
in
|
in
|
||||||
let main_status = ref `Running in
|
let main_status = ref `Running in
|
||||||
fork ~tid:(Ctf.mint_id ()) ~cancel:Eio.Private.boot_cancel (fun () ->
|
fork ~tid:(Ctf.mint_id ()) ~cancel:Eio.Private.boot_cancel (fun _new_fibre ->
|
||||||
match Eio.Cancel.protect (fun () -> main stdenv) with
|
match Eio.Cancel.protect (fun () -> main stdenv) with
|
||||||
| () -> main_status := `Done
|
| () -> main_status := `Done
|
||||||
| exception ex -> main_status := `Ex (ex, Printexc.get_raw_backtrace ())
|
| exception ex -> main_status := `Ex (ex, Printexc.get_raw_backtrace ())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user