/- Copyright (c) 2022 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Gabriel Ebner -/ prelude import Init.System.IO import Init.Control.StateRef namespace IO private opaque BaseMutexImpl : NonemptyType.{0} /-- Mutual exclusion primitive (a lock). If you want to guard shared state, use `Mutex α` instead. -/ def BaseMutex : Type := BaseMutexImpl.type instance : Nonempty BaseMutex := BaseMutexImpl.property /-- Creates a new `BaseMutex`. -/ @[extern "lean_io_basemutex_new"] opaque BaseMutex.new : BaseIO BaseMutex /-- Locks a `BaseMutex`. Waits until no other thread has locked the mutex. The current thread must not have already locked the mutex. Reentrant locking is undefined behavior (inherited from the C++ implementation). -/ @[extern "lean_io_basemutex_lock"] opaque BaseMutex.lock (mutex : @& BaseMutex) : BaseIO Unit /-- Unlocks a `BaseMutex`. The current thread must have already locked the mutex. Unlocking an unlocked mutex is undefined behavior (inherited from the C++ implementation). -/ @[extern "lean_io_basemutex_unlock"] opaque BaseMutex.unlock (mutex : @& BaseMutex) : BaseIO Unit private opaque CondvarImpl : NonemptyType.{0} /-- Condition variable. -/ def Condvar : Type := CondvarImpl.type instance : Nonempty Condvar := CondvarImpl.property /-- Creates a new condition variable. -/ @[extern "lean_io_condvar_new"] opaque Condvar.new : BaseIO Condvar /-- Waits until another thread calls `notifyOne` or `notifyAll`. -/ @[extern "lean_io_condvar_wait"] opaque Condvar.wait (condvar : @& Condvar) (mutex : @& BaseMutex) : BaseIO Unit /-- Wakes up a single other thread executing `wait`. -/ @[extern "lean_io_condvar_notify_one"] opaque Condvar.notifyOne (condvar : @& Condvar) : BaseIO Unit /-- Wakes up all other threads executing `wait`. -/ @[extern "lean_io_condvar_notify_all"] opaque Condvar.notifyAll (condvar : @& Condvar) : BaseIO Unit /-- Waits on the condition variable until the predicate is true. -/ def Condvar.waitUntil [Monad m] [MonadLift BaseIO m] (condvar : Condvar) (mutex : BaseMutex) (pred : m Bool) : m Unit := do while !(← pred) do condvar.wait mutex /-- Mutual exclusion primitive (lock) guarding shared state of type `α`. The type `Mutex α` is similar to `IO.Ref α`, except that concurrent accesses are guarded by a mutex instead of atomic pointer operations and busy-waiting. -/ structure Mutex (α : Type) where private mk :: private ref : IO.Ref α mutex : BaseMutex deriving Nonempty instance : CoeOut (Mutex α) BaseMutex where coe := Mutex.mutex /-- Creates a new mutex. -/ def Mutex.new (a : α) : BaseIO (Mutex α) := return { ref := ← mkRef a, mutex := ← BaseMutex.new } /-- `AtomicT α m` is the monad that can be atomically executed inside a `Mutex α`, with outside monad `m`. The action has access to the state `α` of the mutex (via `get` and `set`). -/ abbrev AtomicT := StateRefT' IO.RealWorld /-- `mutex.atomically k` runs `k` with access to the mutex's state while locking the mutex. -/ def Mutex.atomically [Monad m] [MonadLiftT BaseIO m] [MonadFinally m] (mutex : Mutex α) (k : AtomicT α m β) : m β := do try mutex.mutex.lock k mutex.ref finally mutex.mutex.unlock /-- `mutex.atomicallyOnce condvar pred k` runs `k`, waiting on `condvar` until `pred` returns true. Both `k` and `pred` have access to the mutex's state. -/ def Mutex.atomicallyOnce [Monad m] [MonadLiftT BaseIO m] [MonadFinally m] (mutex : Mutex α) (condvar : Condvar) (pred : AtomicT α m Bool) (k : AtomicT α m β) : m β := let _ : MonadLift BaseIO (AtomicT α m) := ⟨liftM⟩ mutex.atomically do condvar.waitUntil mutex pred k