-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
68e7c70
commit ce0b6b4
Showing
20 changed files
with
664 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
(lang dune 2.9) | ||
(name ec) | ||
(name meio) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# This file is generated by dune, edit dune-project instead | ||
opam-version: "2.0" | ||
synopsis: "Monitor live Eio programs" | ||
description: | ||
"Eio-console provides an executable that allows you to monitor OCaml programs using Eventring." | ||
maintainer: ["[email protected]"] | ||
authors: ["Patrick Ferris"] | ||
license: "MIT" | ||
homepage: "https://github.com/patricoferris/eio-console" | ||
bug-reports: "https://github.com/patricoferris/eio-console/issues" | ||
depends: [ | ||
"dune" {>= "2.9"} | ||
"odoc" {with-doc} | ||
] | ||
build: [ | ||
["dune" "subst"] {dev} | ||
[ | ||
"dune" | ||
"build" | ||
"-p" | ||
name | ||
"-j" | ||
jobs | ||
"--promote-install-files=false" | ||
"@install" | ||
"@runtest" {with-test} | ||
"@doc" {with-doc} | ||
] | ||
["dune" "install" "-p" name "--create-install-files" name] | ||
] | ||
dev-repo: "git+https://github.com/patricoferris/eio-console.git" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
(executable | ||
(name meio) | ||
(public_name meio) | ||
(package meio) | ||
(libraries meio)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
(* Copyright (C) 2021 Anil Madhavapeddy | ||
Copyright (C) 2022 Thomas Leonard | ||
Permission to use, copy, modify, and distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) | ||
|
||
(* A lock-free multi-producer, single-consumer, thread-safe queue without support for cancellation. | ||
This makes a good data structure for a scheduler's run queue. | ||
See: "Implementing lock-free queues" | ||
https://people.cs.pitt.edu/~jacklange/teaching/cs2510-f12/papers/implementing_lock_free.pdf | ||
It is simplified slightly because we don't need multiple consumers. | ||
Therefore [head] is not atomic. *) | ||
|
||
exception Closed | ||
|
||
module Node : sig | ||
type 'a t = { next : 'a opt Atomic.t; mutable value : 'a } | ||
and +'a opt | ||
|
||
val make : next:'a opt -> 'a -> 'a t | ||
|
||
val none : 'a opt | ||
(** [t.next = none] means that [t] is currently the last node. *) | ||
|
||
val closed : 'a opt | ||
(** [t.next = closed] means that [t] will always be the last node. *) | ||
|
||
val some : 'a t -> 'a opt | ||
val fold : 'a opt -> none:(unit -> 'b) -> some:('a t -> 'b) -> 'b | ||
end = struct | ||
(* https://github.com/ocaml/RFCs/pull/14 should remove the need for magic here *) | ||
|
||
type +'a opt (* special | 'a t *) | ||
type 'a t = { next : 'a opt Atomic.t; mutable value : 'a } | ||
type special = Nothing | Closed | ||
|
||
let none : 'a. 'a opt = Obj.magic Nothing | ||
let closed : 'a. 'a opt = Obj.magic Closed | ||
let some (t : 'a t) : 'a opt = Obj.magic t | ||
|
||
let fold (opt : 'a opt) ~none:n ~some = | ||
if opt == none then n () | ||
else if opt == closed then raise Closed | ||
else some (Obj.magic opt : 'a t) | ||
|
||
let make ~next value = { value; next = Atomic.make next } | ||
end | ||
|
||
type 'a t = { tail : 'a Node.t Atomic.t; mutable head : 'a Node.t } | ||
(* [head] is the last node dequeued (or a dummy node, initially). | ||
[head.next] gives the real first node, if not [Node.none]. | ||
If [tail.next] is [none] then it is the last node in the queue. | ||
Otherwise, [tail.next] is a node that is closer to the tail. *) | ||
|
||
let push t x = | ||
let node = Node.(make ~next:none) x in | ||
let rec aux () = | ||
let p = Atomic.get t.tail in | ||
(* While [p.next == none], [p] is the last node in the queue. *) | ||
if Atomic.compare_and_set p.next Node.none (Node.some node) then | ||
(* [node] has now been added to the queue (and possibly even consumed). | ||
Update [tail], unless someone else already did it for us. *) | ||
ignore (Atomic.compare_and_set t.tail p node : bool) | ||
else | ||
(* Someone else added a different node first ([p.next] is not [none]). | ||
Make [t.tail] more up-to-date, if it hasn't already changed, and try again. *) | ||
Node.fold (Atomic.get p.next) | ||
~none:(fun () -> assert false) | ||
~some:(fun p_next -> | ||
ignore (Atomic.compare_and_set t.tail p p_next : bool); | ||
aux ()) | ||
in | ||
aux () | ||
|
||
let rec push_head t x = | ||
let p = t.head in | ||
let next = Atomic.get p.next in | ||
if next == Node.closed then raise Closed; | ||
let node = Node.make ~next x in | ||
if Atomic.compare_and_set p.next next (Node.some node) then | ||
if | ||
(* We don't want to let [tail] get too far behind, so if the queue was empty, move it to the new node. *) | ||
next == Node.none | ||
then ignore (Atomic.compare_and_set t.tail p node : bool) | ||
else | ||
( (* If the queue wasn't empty, there's nothing to do. | ||
Either tail isn't at head or there is some [push] thread working to update it. | ||
Either [push] will update it directly to the new tail, or will update it to [node] | ||
and then retry. Either way, it ends up at the real tail. *) ) | ||
else ( | ||
(* Someone else changed it first. This can only happen if the queue was empty. *) | ||
assert (next == Node.none); | ||
push_head t x) | ||
|
||
let rec close (t : 'a t) = | ||
(* Mark the tail node as final. *) | ||
let p = Atomic.get t.tail in | ||
if not (Atomic.compare_and_set p.next Node.none Node.closed) then | ||
(* CAS failed because [p] is no longer the tail (or is already closed). *) | ||
Node.fold (Atomic.get p.next) | ||
~none:(fun () -> assert false) | ||
(* Can't switch from another state to [none] *) | ||
~some:(fun p_next -> | ||
(* Make [tail] more up-to-date if it hasn't changed already *) | ||
ignore (Atomic.compare_and_set t.tail p p_next : bool); | ||
(* Retry *) | ||
close t) | ||
|
||
let pop t = | ||
let p = t.head in | ||
(* [p] is the previously-popped item. *) | ||
let node = Atomic.get p.next in | ||
Node.fold node | ||
~none:(fun () -> None) | ||
~some:(fun node -> | ||
t.head <- node; | ||
let v = node.value in | ||
node.value <- Obj.magic (); | ||
(* So it can be GC'd *) | ||
Some v) | ||
|
||
let is_empty t = | ||
Node.fold (Atomic.get t.head.next) | ||
~none:(fun () -> true) | ||
~some:(fun _ -> false) | ||
|
||
let create () = | ||
let dummy = { Node.value = Obj.magic (); next = Atomic.make Node.none } in | ||
{ tail = Atomic.make dummy; head = dummy } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.