namespace AP.Threading open System open System.Collections.Generic open System.Threading.Tasks type OneManyMode = Exclusive | Shared [] type AsyncOneManyLock() = let _lock = new SpinLock(true) let _noContentionAccessGranter = Task.FromResult(null) let _qWaitingWriters = new Queue>() let mutable _waitingReadersSignal = new TaskCompletionSource() let mutable _numWaitingReaders = 0 let mutable _state = 0 let lock() = let mutable taken = false _lock.Enter(&taken) let unlock() = _lock.Exit() let isFree() = _state = 0 let isOwnedByWriter() = _state = -1 let isOwnedByReader() = _state > 0 let addReaders(count: Int32) = _state <- _state + count let subtractReader() = _state <- _state - 1 let makeWriter() = _state <- -1 let makeFree() = _state <- 0 member this.WaitAsync(mode: OneManyMode) = let mutable accessGranter = _noContentionAccessGranter lock() match mode with | Exclusive -> if isFree() then makeWriter() else let tcs = new TaskCompletionSource() _qWaitingWriters.Enqueue(tcs) accessGranter <- tcs.Task | Shared -> if isFree() || (isOwnedByReader() && _qWaitingWriters.Count = 0) then addReaders(1) else _numWaitingReaders <- _numWaitingReaders + 1 accessGranter <- _waitingReadersSignal.Task.ContinueWith( fun (t: Task) -> t.Result ) unlock() accessGranter member this.Release() = let mutable accessGranted: TaskCompletionSource option = None lock() if isOwnedByWriter() then makeFree() else subtractReader() if isFree() then if _qWaitingWriters.Count > 0 then makeWriter() accessGranted <- Some <| _qWaitingWriters.Dequeue() elif _numWaitingReaders > 0 then addReaders(_numWaitingReaders) _numWaitingReaders <- 0 accessGranted <- Some <| _waitingReadersSignal _waitingReadersSignal <- new TaskCompletionSource() unlock() if accessGranted.IsSome then accessGranted.Value.SetResult(null)