mirror of
https://github.com/ppy/osu
synced 2025-01-10 08:09:40 +00:00
Fix potential deadlocking behaviour (and convert ResetEvent
to Semaphore
)
This commit is contained in:
parent
c85edd2b55
commit
63ab40ec24
@ -26,6 +26,11 @@ namespace osu.Game.Database
|
||||
/// </summary>
|
||||
private readonly object writeLock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Lock object which is held during <see cref="BlockAllOperations"/> sections.
|
||||
/// </summary>
|
||||
private readonly SemaphoreSlim blockingLock = new SemaphoreSlim(1);
|
||||
|
||||
private static readonly GlobalStatistic<int> reads = GlobalStatistics.Get<int>("Realm", "Get (Read)");
|
||||
private static readonly GlobalStatistic<int> writes = GlobalStatistics.Get<int>("Realm", "Get (Write)");
|
||||
private static readonly GlobalStatistic<int> refreshes = GlobalStatistics.Get<int>("Realm", "Dirty Refreshes");
|
||||
@ -33,8 +38,6 @@ namespace osu.Game.Database
|
||||
private static readonly GlobalStatistic<int> pending_writes = GlobalStatistics.Get<int>("Realm", "Pending writes");
|
||||
private static readonly GlobalStatistic<int> active_usages = GlobalStatistics.Get<int>("Realm", "Active usages");
|
||||
|
||||
private readonly ManualResetEventSlim blockingResetEvent = new ManualResetEventSlim(true);
|
||||
|
||||
private Realm context;
|
||||
|
||||
public Realm Context
|
||||
@ -64,7 +67,7 @@ namespace osu.Game.Database
|
||||
public RealmUsage GetForRead()
|
||||
{
|
||||
reads.Value++;
|
||||
return new RealmUsage(this);
|
||||
return new RealmUsage(createContext());
|
||||
}
|
||||
|
||||
public RealmWriteUsage GetForWrite()
|
||||
@ -73,8 +76,13 @@ namespace osu.Game.Database
|
||||
pending_writes.Value++;
|
||||
|
||||
Monitor.Enter(writeLock);
|
||||
return new RealmWriteUsage(createContext(), writeComplete);
|
||||
}
|
||||
|
||||
return new RealmWriteUsage(this);
|
||||
private void writeComplete()
|
||||
{
|
||||
Monitor.Exit(writeLock);
|
||||
pending_writes.Value--;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -87,7 +95,9 @@ namespace osu.Game.Database
|
||||
|
||||
private Realm createContext()
|
||||
{
|
||||
blockingResetEvent.Wait();
|
||||
try
|
||||
{
|
||||
blockingLock.Wait();
|
||||
|
||||
contexts_created.Value++;
|
||||
|
||||
@ -97,6 +107,11 @@ namespace osu.Game.Database
|
||||
MigrationCallback = onMigration,
|
||||
});
|
||||
}
|
||||
finally
|
||||
{
|
||||
blockingLock.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void onMigration(Migration migration, ulong lastSchemaVersion)
|
||||
{
|
||||
@ -113,21 +128,23 @@ namespace osu.Game.Database
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
// In the standard case, operations will already be blocked by the Update thread "pausing" from GameHost exit.
|
||||
// This avoids waiting (forever) on an already entered semaphore.
|
||||
if (context != null || active_usages.Value > 0)
|
||||
BlockAllOperations();
|
||||
|
||||
blockingLock?.Dispose();
|
||||
}
|
||||
|
||||
public IDisposable BlockAllOperations()
|
||||
{
|
||||
blockingResetEvent.Reset();
|
||||
blockingLock.Wait();
|
||||
flushContexts();
|
||||
|
||||
return new InvokeOnDisposal<RealmContextFactory>(this, r => endBlockingSection());
|
||||
return new InvokeOnDisposal<RealmContextFactory>(this, endBlockingSection);
|
||||
}
|
||||
|
||||
private void endBlockingSection()
|
||||
{
|
||||
blockingResetEvent.Set();
|
||||
}
|
||||
private static void endBlockingSection(RealmContextFactory factory) => factory.blockingLock.Release();
|
||||
|
||||
private void flushContexts()
|
||||
{
|
||||
@ -148,13 +165,10 @@ namespace osu.Game.Database
|
||||
{
|
||||
public readonly Realm Realm;
|
||||
|
||||
protected readonly RealmContextFactory Factory;
|
||||
|
||||
internal RealmUsage(RealmContextFactory factory)
|
||||
internal RealmUsage(Realm context)
|
||||
{
|
||||
active_usages.Value++;
|
||||
Factory = factory;
|
||||
Realm = factory.createContext();
|
||||
Realm = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -172,11 +186,13 @@ namespace osu.Game.Database
|
||||
/// </summary>
|
||||
public class RealmWriteUsage : RealmUsage
|
||||
{
|
||||
private readonly Action onWriteComplete;
|
||||
private readonly Transaction transaction;
|
||||
|
||||
internal RealmWriteUsage(RealmContextFactory factory)
|
||||
: base(factory)
|
||||
internal RealmWriteUsage(Realm context, Action onWriteComplete)
|
||||
: base(context)
|
||||
{
|
||||
this.onWriteComplete = onWriteComplete;
|
||||
transaction = Realm.BeginWrite();
|
||||
}
|
||||
|
||||
@ -200,8 +216,7 @@ namespace osu.Game.Database
|
||||
|
||||
base.Dispose();
|
||||
|
||||
Monitor.Exit(Factory.writeLock);
|
||||
pending_writes.Value--;
|
||||
onWriteComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user