Created
November 5, 2021 11:46
-
-
Save sadiqj/0a8b68a89beec96758f39808f068f710 to your computer and use it in GitHub Desktop.
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
external phys_equal : 'a -> 'a -> bool = "%eq" | |
(* A [Ring.t] is represented as a cyclic doubly-linked list. The same representation is | |
used for the ring itself, and for it elements; the ring being represented as a sentinel | |
element containing a dummy value which is never used. | |
The implementation supports calls to [detach] which preempt a running [iter] or [add], | |
by maintaining the invariant that by following [next] pointers from any element, even | |
an element which has been detached, we will reach the [sentinel] element of the [ring] | |
in which the element was originally added. | |
Two places, marked "ATOMIC" in the code below, require a block of code to be executed | |
atomically. We claim this atomicity is guaranteed by the (current) Ocaml implementation | |
because the statements do no allocation and so can not be preempted. *) | |
type 'a t = {mutable prev: 'a t; mutable value: 'a; mutable next: 'a t} | |
type 'a elem = 'a t | |
(* An empty ring is represented by self-referencing [sentinel] element. The never-used | |
[value] is created using [Obj.magic] since there are no values of type ['a] to hand. *) | |
let create () = | |
let rec t = {prev = t; next = t; value = Obj.magic None} in | |
t | |
let keep_alive t parent = | |
(* Note that this only works on type [t] and not [elem], because no function ever looks | |
at the [value] inside a [t]. *) | |
t.value <- Obj.magic parent | |
let[@poll error] add_new_elem elem t = | |
elem.prev <- t.prev; | |
t.prev.next <- elem; | |
t.prev <- elem | |
(* New elements are added at the end of the ring. The new element becomes the [next] of | |
the previously added element. The new element's [next] points to the [sentinel]. *) | |
let add_calling t value ~f = | |
let elem = {prev = t.prev; value; next = t} in (* may cause GC *) | |
f(); | |
begin (* ATOMIC block *) | |
(* [elem.prev] must be assigned in case the original [t.prev] was detached *) | |
add_new_elem elem t | |
end; | |
elem | |
let add t value = add_calling t value ~f:(fun () -> ()) | |
(* An element is detached from a ring by updating the [next] and [prev] pointers of the | |
elements found before and after the elements being detached. | |
The detached element is marked by setting its [prev] pointer to self-reference. This | |
allows a check to ensure subsequent calls to detach have no effect. | |
It is critical that the [next] pointer of the detached element continues to reference | |
back into the linked list from which the [elem] itself is no longer a part. *) | |
let[@poll error] detach elem = | |
begin | |
if not (phys_equal elem elem.prev) then (* if not detached.. *) | |
begin | |
elem.next.prev <- elem.prev; | |
elem.prev.next <- elem.next; | |
elem.prev <- elem; (* mark as detached *) | |
end | |
end | |
(* Iteration over a ring is achieved by following [next] pointers until the [sentinel] | |
element which represents the start of the ring is reached. We do not call [f] with the | |
(dummy) value of the [sentinel]. *) | |
let iter = | |
let rec loop ~sentinel ~elem ~f = | |
if phys_equal sentinel elem | |
then () | |
else (f elem.value; loop ~sentinel ~elem:elem.next ~f) | |
in | |
fun t ~f -> | |
loop ~sentinel:t ~elem:t.next ~f | |
let size t = | |
let n = ref 0 in | |
iter t ~f:(fun _ -> incr n); | |
!n |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment