/* ----------------------------------------------------------------------------- Copyright 2021 Kevin P. Barry Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ----------------------------------------------------------------------------- */ // Author: Kevin P. Barry [ta0kira@gmail.com] // A blocking mutex. // // Notes: // - Most use-cases should use MutexLock instead of explicit calls to lock() and // unlock(), to mitigate the risk of deadlocks. @value interface Mutex { // Lock the mutex. lock () -> (#self) // Unlock the mutex. unlock () -> (#self) } // A simple mutex with no deadlock prevention. concrete SimpleMutex { refines Mutex // Create a new mutex. @type new () -> (Mutex) } // An automatic mutex lock. // // Notes: // - MutexLock is a bit slower than just calling lock() and unlock() directly, // but it provides error checking, if you don't need optimal code: // - Makes sure that lock() is called exactly once. // - Makes sure that unlock() is called exactly once before the last reference // to the MutexLock is released. // - Provides a trace back to where the MutexLock was originally created, in // addition to where you failed to call freeResource(). // - Allows you to mark the mutex as hidden (see example below) so that // subsequent code can't mistakenly lock()/unlock() it. // - MutexLock should never be stored as a @value member or @category member; it // should always be scoped to a specific set of operations in a function. // - freeResource() must be called; otherwise, there will be a crash. This is to // catch code that could cause a deadlock. // - freeResource() must only be called once per MutexLock. // // Example: // // scoped { // MutexLock lock <- MutexLock.lock(myMutex) // $Hidden[myMutex]$ // Hide the mutex to prevent further locking attempts. // } cleanup { // \ lock.freeResource() // } in { // $Hidden[lock]$ // Nothing else needs to see the lock. // // your code // } concrete MutexLock { refines PersistentResource // Create a new lock and lock the Mutex. @type lock (Mutex) -> (MutexLock) }