Skip to content

Commit

Permalink
Merge pull request #16618 from peppy/realm-live-update-optimisations
Browse files Browse the repository at this point in the history
Avoid refetch in `RealmLive` when operating strictly on the update thread
  • Loading branch information
smoogipoo committed Jan 26, 2022
2 parents cdd63e4 + d37c3c4 commit 7544aa4
Showing 1 changed file with 51 additions and 3 deletions.
54 changes: 51 additions & 3 deletions osu.Game/Database/RealmLive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Diagnostics;
using osu.Framework.Development;
using osu.Framework.Statistics;
using Realms;

#nullable enable
Expand All @@ -22,7 +24,9 @@ public class RealmLive<T> : ILive<T> where T : RealmObject, IHasGuidPrimaryKey
/// <summary>
/// The original live data used to create this instance.
/// </summary>
private readonly T data;
private T data;

private bool dataIsFromUpdateThread;

private readonly RealmAccess realm;

Expand All @@ -37,6 +41,7 @@ public RealmLive(T data, RealmAccess realm)
this.realm = realm;

ID = data.ID;
dataIsFromUpdateThread = ThreadSafety.IsUpdateThread;
}

/// <summary>
Expand All @@ -51,7 +56,18 @@ public void PerformRead(Action<T> perform)
return;
}

realm.Run(r => perform(retrieveFromID(r, ID)));
realm.Run(r =>
{
if (ThreadSafety.IsUpdateThread)
{
ensureDataIsFromUpdateThread();
perform(data);
return;
}
perform(retrieveFromID(r, ID));
RealmLiveStatistics.USAGE_ASYNC.Value++;
});
}

/// <summary>
Expand All @@ -63,9 +79,16 @@ public TReturn PerformRead<TReturn>(Func<T, TReturn> perform)
if (!IsManaged)
return perform(data);

if (ThreadSafety.IsUpdateThread)
{
ensureDataIsFromUpdateThread();
return perform(data);
}

return realm.Run(r =>
{
var returnData = perform(retrieveFromID(r, ID));
RealmLiveStatistics.USAGE_ASYNC.Value++;
if (returnData is RealmObjectBase realmObject && realmObject.IsManaged)
throw new InvalidOperationException(@$"Managed realm objects should not exit the scope of {nameof(PerformRead)}.");
Expand All @@ -88,6 +111,7 @@ public void PerformWrite(Action<T> perform)
var transaction = t.Realm.BeginWrite();
perform(t);
transaction.Commit();
RealmLiveStatistics.WRITES.Value++;
});
}

Expand All @@ -101,8 +125,24 @@ public T Value
if (!ThreadSafety.IsUpdateThread)
throw new InvalidOperationException($"Can't use {nameof(Value)} on managed objects from non-update threads");

return realm.Realm.Find<T>(ID);
ensureDataIsFromUpdateThread();
return data;
}
}

private void ensureDataIsFromUpdateThread()
{
Debug.Assert(ThreadSafety.IsUpdateThread);

if (dataIsFromUpdateThread && !data.Realm.IsClosed)
{
RealmLiveStatistics.USAGE_UPDATE_IMMEDIATE.Value++;
return;
}

dataIsFromUpdateThread = true;
data = retrieveFromID(realm.Realm, ID);
RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++;
}

private T retrieveFromID(Realm realm, Guid id)
Expand All @@ -125,4 +165,12 @@ private T retrieveFromID(Realm realm, Guid id)

public override string ToString() => PerformRead(i => i.ToString());
}

internal static class RealmLiveStatistics
{
public static readonly GlobalStatistic<int> WRITES = GlobalStatistics.Get<int>(@"Realm", @"Live writes");
public static readonly GlobalStatistic<int> USAGE_UPDATE_IMMEDIATE = GlobalStatistics.Get<int>(@"Realm", @"Live update read (fast)");
public static readonly GlobalStatistic<int> USAGE_UPDATE_REFETCH = GlobalStatistics.Get<int>(@"Realm", @"Live update read (slow)");
public static readonly GlobalStatistic<int> USAGE_ASYNC = GlobalStatistics.Get<int>(@"Realm", @"Live async read");
}
}

0 comments on commit 7544aa4

Please sign in to comment.