mirror of
https://github.com/ppy/osu.git
synced 2025-02-06 07:43:24 +08:00
Merge branch 'realm-nested-context-creation-deadlock-fix' into realm-integration/compiled
This commit is contained in:
commit
c411a755c7
@ -5,6 +5,9 @@ using System;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Database;
|
||||||
|
using osu.Game.Models;
|
||||||
|
using Realms;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -33,6 +36,37 @@ namespace osu.Game.Tests.Database
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNestedContextCreation()
|
||||||
|
{
|
||||||
|
RunTestWithRealm((realmFactory, _) =>
|
||||||
|
{
|
||||||
|
var mainContext = realmFactory.Context;
|
||||||
|
bool callbackRan = false;
|
||||||
|
|
||||||
|
var subscription = mainContext.All<RealmBeatmap>().SubscribeForNotifications((sender, changes, error) =>
|
||||||
|
{
|
||||||
|
realmFactory.CreateContext();
|
||||||
|
callbackRan = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
using (var threadContext = realmFactory.CreateContext())
|
||||||
|
{
|
||||||
|
threadContext.Write(r => r.Add(new RealmBeatmap(CreateRuleset(), new RealmBeatmapDifficulty(), new RealmBeatmapMetadata())));
|
||||||
|
}
|
||||||
|
}, TaskCreationOptions.LongRunning | TaskCreationOptions.HideScheduler).Wait();
|
||||||
|
|
||||||
|
// will create a context but also run the callback above (Refresh is implicitly run when getting a new context).
|
||||||
|
realmFactory.CreateContext();
|
||||||
|
|
||||||
|
Assert.IsTrue(callbackRan);
|
||||||
|
|
||||||
|
subscription.Dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestBlockOperationsWithContention()
|
public void TestBlockOperationsWithContention()
|
||||||
{
|
{
|
||||||
|
@ -52,6 +52,8 @@ namespace osu.Game.Database
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly SemaphoreSlim contextCreationLock = new SemaphoreSlim(1);
|
private readonly SemaphoreSlim contextCreationLock = new SemaphoreSlim(1);
|
||||||
|
|
||||||
|
private readonly ThreadLocal<bool> currentThreadCanCreateContexts = new ThreadLocal<bool>();
|
||||||
|
|
||||||
private static readonly GlobalStatistic<int> refreshes = GlobalStatistics.Get<int>(@"Realm", @"Dirty Refreshes");
|
private static readonly GlobalStatistic<int> refreshes = GlobalStatistics.Get<int>(@"Realm", @"Dirty Refreshes");
|
||||||
private static readonly GlobalStatistic<int> contexts_created = GlobalStatistics.Get<int>(@"Realm", @"Contexts (Created)");
|
private static readonly GlobalStatistic<int> contexts_created = GlobalStatistics.Get<int>(@"Realm", @"Contexts (Created)");
|
||||||
|
|
||||||
@ -151,9 +153,22 @@ namespace osu.Game.Database
|
|||||||
if (isDisposed)
|
if (isDisposed)
|
||||||
throw new ObjectDisposedException(nameof(RealmContextFactory));
|
throw new ObjectDisposedException(nameof(RealmContextFactory));
|
||||||
|
|
||||||
|
bool tookSemaphoreLock = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
contextCreationLock.Wait();
|
if (!currentThreadCanCreateContexts.Value)
|
||||||
|
{
|
||||||
|
contextCreationLock.Wait();
|
||||||
|
tookSemaphoreLock = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// the semaphore is used to handle blocking of all context creation during certain periods.
|
||||||
|
// once the semaphore has been taken by this code section, it is safe to create further contexts on the same thread.
|
||||||
|
// this can happen if a realm subscription is active and triggers a callback which has user code that calls `CreateContext`.
|
||||||
|
currentThreadCanCreateContexts.Value = true;
|
||||||
|
}
|
||||||
|
|
||||||
contexts_created.Value++;
|
contexts_created.Value++;
|
||||||
|
|
||||||
@ -161,7 +176,11 @@ namespace osu.Game.Database
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
contextCreationLock.Release();
|
if (tookSemaphoreLock)
|
||||||
|
{
|
||||||
|
contextCreationLock.Release();
|
||||||
|
currentThreadCanCreateContexts.Value = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user