Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid refetch in RealmLive when operating strictly on the update thread #16618

Merged
merged 4 commits into from
Jan 26, 2022
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
56 changes: 52 additions & 4 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 @@ -13,7 +15,7 @@ namespace osu.Game.Database
/// Provides a method of working with realm objects over longer application lifetimes.
/// </summary>
/// <typeparam name="T">The underlying object type.</typeparam>
public class RealmLive<T> : ILive<T> where T : RealmObject, IHasGuidPrimaryKey
public class RealmLive<T> : RealmLive, ILive<T> where T : RealmObject, IHasGuidPrimaryKey
{
public Guid ID { get; }

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));
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));
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();
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)
{
USAGE_UPDATE_IMMEDIATE.Value++;
return;
}

dataIsFromUpdateThread = true;
data = retrieveFromID(realm.Realm, ID);
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());
}

public class RealmLive
smoogipoo marked this conversation as resolved.
Show resolved Hide resolved
{
protected static readonly GlobalStatistic<int> WRITES = GlobalStatistics.Get<int>(@"Realm", @"Live writes");
protected static readonly GlobalStatistic<int> USAGE_UPDATE_IMMEDIATE = GlobalStatistics.Get<int>(@"Realm", @"Live update read (fast)");
protected static readonly GlobalStatistic<int> USAGE_UPDATE_REFETCH = GlobalStatistics.Get<int>(@"Realm", @"Live update read (slow)");
protected static readonly GlobalStatistic<int> USAGE_ASYNC = GlobalStatistics.Get<int>(@"Realm", @"Live async read");
}
}