//
// 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);
}
}
}
}
}