Skip to content

Commit

Permalink
Simplify poisoning logic in Lock
Browse files Browse the repository at this point in the history
  • Loading branch information
polytypic committed Jan 25, 2025
1 parent a9cff3f commit cae451a
Showing 1 changed file with 18 additions and 18 deletions.
36 changes: 18 additions & 18 deletions lib/picos_std.sync/lock.ml
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
open Picos_std_awaitable

let poisoned = 1 lsl 0
let no_writers = 1 lsl 1
let locked = 1 lsl 2
let no_awaiters = 1 lsl 0
let locked = 1 lsl 1
let locked_permanently = 1 lsl (Sys.int_size - 2)

type t = int Awaitable.t

exception Poisoned

let poison t =
let rec poison t =
let before = Awaitable.get t in
if before < locked then invalid_arg "not locked";
(* Unfortunately we cannot check ownership at this point. *)
if before land poisoned = 0 then
let _ : int = Awaitable.fetch_and_add t (locked_permanently + poisoned) in
Awaitable.broadcast t
if before < locked_permanently / 2 then
if Awaitable.compare_and_set t before (before + locked_permanently) then
Awaitable.broadcast t
else poison t

(* *)

let rec acquire_awaiting t before =
if before < locked then begin
(* We know [before] is [0] or [no_writers]. *)
let after = locked land lnot no_writers in
(* We know [before] is [0] or [no_awaiters]. *)
let after = locked land lnot no_awaiters in
if not (Awaitable.compare_and_set t before after) then
acquire_awaiting t (Awaitable.get t)
end
else if before land poisoned <> 0 then raise Poisoned
else if locked_permanently / 2 <= before then raise Poisoned
else
let after = before land lnot no_writers in
let after = before land lnot no_awaiters in
if before = after || Awaitable.compare_and_set t before after then
Awaitable.await t after;
acquire_awaiting t (Awaitable.get t)

let rec acquire_contended t before =
if locked * 2 <= before then
let after = (before - locked) land lnot no_writers in
let after = (before - locked) land lnot no_awaiters in
if Awaitable.compare_and_set t before after then acquire_awaiting t after
else acquire_contended t (Awaitable.get t)

Expand All @@ -47,12 +47,12 @@ let acquire t =

let signal_awaiter t =
(* Lock state was [0] after the [fetch_and_add] in [unlock]. *)
if Awaitable.compare_and_set t 0 no_writers then Awaitable.signal t
if Awaitable.compare_and_set t 0 no_awaiters then Awaitable.signal t

let release t =
let prior = Awaitable.fetch_and_add t (-locked) in
if prior < locked lor no_writers then signal_awaiter t
else if locked_permanently / 2 < prior then
if prior < locked lor no_awaiters then signal_awaiter t
else if locked_permanently / 2 <= prior then
(* The lock was poisoned. We need to undo. *)
let _ : int = Awaitable.fetch_and_add t locked in
()
Expand All @@ -69,13 +69,13 @@ let try_acquire t =
prior < locked
||
let prior = Awaitable.fetch_and_add t (-locked) in
prior land poisoned <> 0 && raise Poisoned
locked_permanently / 2 <= prior && raise Poisoned

(* *)

let[@inline] create ?padded () = Awaitable.make ?padded no_writers
let[@inline] create ?padded () = Awaitable.make ?padded no_awaiters
let[@inline] is_locked t = locked <= Awaitable.get t
let[@inline] is_poisoned t = Awaitable.get t land poisoned <> 0
let[@inline] is_poisoned t = locked_permanently / 2 <= Awaitable.get t

module Condition = struct
type lock = t
Expand Down

0 comments on commit cae451a

Please sign in to comment.