// // Copyright (c) Moonfire Games. // This code is licensed under the MIT license. // See LICENSE.md in the source or https://opensource.org/licenses/MIT // namespace MfGames.Locking { using System; using System.Threading; /// /// Implements the basic pattern for getting an item from a cache using /// the ReaderWriterLockSlim class. This attempts to get it using a read-only /// lock. If that fails, it gets an upgradable lock, tries again, and if it /// still can't find it, upgrades the lock to a write lock to create it. /// public static class TryGetCreate { /// /// Invokes the try/get/create pattern used a condition to test for it /// and a constructor function. /// /// The reader writer lock slim. /// The condition handler. /// The create handler. public static void Invoke( ReaderWriterLockSlim readerWriterLockSlim, Func conditionHandler, Action createHandler) { using (new ReadLock(readerWriterLockSlim)) { // Verify that the condition for creating it is false. if (!conditionHandler()) { return; } } // We failed to get the lock using the read-only. We create an upgradable lock // and try again since it may have been created with a race condition when the // last lock was released and this one was acquired. using (new UpgradableLock(readerWriterLockSlim)) { // Verify that the condition for creating it is false. if (!conditionHandler()) { return; } // We failed to get it in the lock. Upgrade the lock to a write and create it. using (new WriteLock(readerWriterLockSlim)) { createHandler(); } } } /// /// Invokes the try/get/create pattern using a tryget retrieval and a /// creator handler. /// /// The type of the input. /// The type of the output. /// The reader writer lock slim. /// The input. /// The try get handler. /// The create handler. /// The requested output object. public static TOutput Invoke( ReaderWriterLockSlim readerWriterLockSlim, TInput input, TryGetHandler tryGetHandler, Func createHandler) { // First attempt to get the item using a read-only lock. TOutput output; using (new ReadLock(readerWriterLockSlim)) { // Try to get the item using the try/get handler. if (tryGetHandler(input, out output)) { // We successful got the item in the read-only cache, so just return it. return output; } } // We failed to get the lock using the read-only. We create an upgradable lock // and try again since it may have been created with a race condition when the // last lock was released and this one was acquired. using (new UpgradableLock(readerWriterLockSlim)) { // Try to get the item using the try/get handler. if (tryGetHandler(input, out output)) { // We successful got the item in this lock, so return it without // upgrading the lock. return output; } // We failed to get it in the lock. Upgrade the lock to a write and create it. using (new WriteLock(readerWriterLockSlim)) { return createHandler(input); } } } } }