diff --git a/src/dune b/src/dune index 921de611..c83fc96c 100644 --- a/src/dune +++ b/src/dune @@ -42,5 +42,5 @@ let () = (<> %{os_type} Win32) (>= %{ocaml_version} 5.0.0))) (libraries saturn) - (files treiber_stack.mli bounded_stack.mli ws_deque.mli mpsc_queue.mli)) + (files treiber_stack.mli bounded_stack.mli ws_deque.mli mpsc_queue.mli skiplist.mli)) |} diff --git a/src/skiplist.ml b/src/skiplist.ml index bde2ff48..074bc1bc 100644 --- a/src/skiplist.ml +++ b/src/skiplist.ml @@ -234,6 +234,9 @@ let max_height_of t = Array.length t.root let find_opt t key = match find_node t key with Null -> None | Node r -> Some r.value +let find_exn t key = + match find_node t key with Null -> raise Not_found | Node r -> r.value + (* *) let mem t key = match find_node t key with Null -> false | Node _ -> true diff --git a/src/skiplist.mli b/src/skiplist.mli index 174b781a..2716ffdb 100644 --- a/src/skiplist.mli +++ b/src/skiplist.mli @@ -1,5 +1,7 @@ (** A lock-free skiplist. *) +(** {1 API}*) + type (!'k, !'v) t (** The type of a lock-free skiplist containing bindings of keys of type ['k] to values of type ['v]. *) @@ -14,29 +16,100 @@ val create : ?max_height:int -> compare:('k -> 'k -> int) -> unit -> ('k, 'v) t The optional [max_height] argument determines the maximum height of nodes in the skiplist and directly affects the performance of the skiplist. The - current implementation does not adjust height automatically. *) + current implementation does not adjust height automatically. + [max_height] can be more than 30. *) val max_height_of : ('k, 'v) t -> int (** [max_height_of s] returns the maximum height of nodes of the skiplist [s] as specified to {!create}. *) +val length : ('k, 'v) t -> int +(** [length s] computes the number of bindings in the skiplist [s]. *) + +(** {2 Looking up bindings} *) + val find_opt : ('k, 'v) t -> 'k -> 'v option -(** [find_opt s k] tries to find a binding of [k] to [v] from the skiplist [s] - and returns [Some v] in case such a binding was found or return [None] in - case no such binding was found. *) +(** [find_opt sl key] returns [Some] of the current binding of [key] in the + skiplist [sl] or [None] if it does not exist. *) + +val find_exn : ('k, 'v) t -> 'k -> 'v +(** [find_exn sl key] returns the current binding of [key] in the skiplist + [sl] or raises {!Not_found} if no such binding exists. + + @raise Not_found if no binding of [key] exists in the skiplist [sl]. *) val mem : ('k, 'v) t -> 'k -> bool -(** [mem s k] determines whether the skiplist [s] contained a binding of [k]. *) +(** [mem sl k] determines whether the skiplist [sl] contained a binding of [k]. *) + +(** {2 Adding bindings} *) val try_add : ('k, 'v) t -> 'k -> 'v -> bool -(** [try_add s k v] tries to add a new binding of [k] to [v] into the skiplist - [s] and returns [true] on success. Otherwise the skiplist already contained - a binding of [k] and [false] is returned. *) +(** [try_add sk key value] tries to add a new binding of [key] to [value] to + the skiplist [sl]. Returns [true] on success and [false] if the skiplist + already contains a binding for [key]. *) val try_remove : ('k, 'v) t -> 'k -> bool -(** [try_remove s k] tries to remove a binding of [k] from the skiplist and - returns [true] on success. Otherwise the skiplist did not contain a binding - of [k] and [false] is returned. *) +(** [try_remove sl key] tries to remove a binding of [key] from the skiplist [sl]. + Returns [true] on success and [false] if the skiplist does not contain a + binding for [key]. *) -val length : ('k, 'v) t -> int -(** [length s] computes the number of bindings in the skiplist [s]. *) +(** {1 Examples} *) + +(** {2 Sequential example} + +{[ + # open Saturn.Skiplist + # let t = create ~compare:Int.compare () + val t : (int, '_weak1) t = + # try_add t 42 "The answer" + - : bool = true + + # try_add t 101 "Basics" + - : bool = true + + # find_opt t 42 + - : string option = Some "The answer" + + # try_add t 101 "The basics" + - : bool = false + + # try_remove t 101 + - : bool = true +]} +*) + +(** {2 Multicore example} + + **Note**: The barrier is used in this example solely to make the results more + interesting by increasing the likelihood of parallelism. Spawning a domain is + a costly operation, especially compared to the relatively small amount of work + being performed here. In practice, using a barrier in this manner is unnecessary. + +{[ + # open Saturn.Skiplist + # let t : (int, int) t= create ~compare:Int.compare () + val t : (int, int) t = + # Random.self_init () + - : unit = () + # let barrier = Atomic.make 2 + val barrier : int Atomic.t = + + # let work () = + Atomic.decr barrier; + while Atomic.get barrier > 0 do () done; + for i = 0 to 10 do + if Random.bool () then + try_add t i i |> ignore + else + try_remove t i |> ignore + done + val work : unit -> unit = + + # let d1 = Domain.spawn work + val d1 : unit Domain.t = + # let d2 = Domain.spawn work + val d2 : unit Domain.t = + # Domain.join d1; Domain.join d2 + - : unit = () +]} +*)