Pull out thread local contexts and have main realm refresh in update loop

This commit is contained in:
Dean Herbert
2021-01-13 17:34:44 +09:00
parent 78707c3b06
commit 542f535247
3 changed files with 53 additions and 53 deletions

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System.Threading;
using osu.Framework.Graphics;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Statistics;
@ -9,16 +10,13 @@ using Realms;
namespace osu.Game.Database
{
public class RealmContextFactory : IRealmFactory
public class RealmContextFactory : Component, IRealmFactory
{
private readonly Storage storage;
private const string database_name = @"client";
private const int schema_version = 5;
private readonly ThreadLocal<Realm> threadContexts;
/// <summary>
/// Lock object which is held for the duration of a write operation (via <see cref="GetForWrite"/>).
/// </summary>
@ -32,54 +30,55 @@ namespace osu.Game.Database
private Transaction currentWriteTransaction;
public RealmContextFactory(Storage storage)
{
this.storage = storage;
threadContexts = new ThreadLocal<Realm>(createContext, true);
// creating a context will ensure our schema is up-to-date and migrated.
var realm = Get();
Logger.Log($"Opened realm \"{realm.Config.DatabasePath}\" at version {realm.Config.SchemaVersion}");
}
private void onMigration(Migration migration, ulong lastSchemaVersion)
{
}
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", "Refreshes");
private static readonly GlobalStatistic<int> commits = GlobalStatistics.Get<int>("Realm", "Commits");
private static readonly GlobalStatistic<int> rollbacks = GlobalStatistics.Get<int>("Realm", "Rollbacks");
private static readonly GlobalStatistic<int> contexts_open = GlobalStatistics.Get<int>("Realm", "Contexts (Open)");
private static readonly GlobalStatistic<int> contexts_created = GlobalStatistics.Get<int>("Realm", "Contexts (Created)");
/// <summary>
/// Get a context for the current thread for read-only usage.
/// If a <see cref="RealmWriteUsage"/> is in progress, the existing write-safe context will be returned.
/// </summary>
public Realm Get()
private Realm context;
public Realm Context
{
reads.Value++;
return getContextForCurrentThread();
get
{
if (context == null)
{
context = createContext();
Logger.Log($"Opened realm \"{context.Config.DatabasePath}\" at version {context.Config.SchemaVersion}");
}
// creating a context will ensure our schema is up-to-date and migrated.
return context;
}
}
public RealmContextFactory(Storage storage)
{
this.storage = storage;
}
public Realm GetForRead()
{
reads.Value++;
return createContext();
}
/// <summary>
/// Request a context for write usage. Can be consumed in a nested fashion (and will return the same underlying context).
/// This method may block if a write is already active on a different thread.
/// </summary>
/// <returns>A usage containing a usable context.</returns>
public RealmWriteUsage GetForWrite()
{
writes.Value++;
Monitor.Enter(writeLock);
Realm context;
Realm realm;
try
{
context = createContext();
realm = createContext();
currentWriteTransaction ??= context.BeginWrite();
currentWriteTransaction ??= realm.BeginWrite();
}
catch
{
@ -90,27 +89,15 @@ namespace osu.Game.Database
Interlocked.Increment(ref currentWriteUsages);
return new RealmWriteUsage(context, usageCompleted) { IsTransactionLeader = currentWriteTransaction != null && currentWriteUsages == 1 };
return new RealmWriteUsage(realm, usageCompleted) { IsTransactionLeader = currentWriteTransaction != null && currentWriteUsages == 1 };
}
private Realm getContextForCurrentThread()
protected override void Update()
{
var context = threadContexts.Value;
base.Update();
if (context?.IsClosed != false)
threadContexts.Value = context = createContext();
contexts_open.Value = threadContexts.Values.Count;
if (!refreshCompleted.Value)
{
// to keep things simple, realm refreshes are currently performed per thread context at the point of retrieval.
// in the future this should likely be run as part of the update loop for the main (update thread) context.
context.Refresh();
refreshCompleted.Value = true;
}
return context;
if (Context.Refresh())
refreshes.Value++;
}
private Realm createContext()
@ -156,5 +143,9 @@ namespace osu.Game.Database
Monitor.Exit(writeLock);
}
}
private void onMigration(Migration migration, ulong lastSchemaVersion)
{
}
}
}