1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 07:33:20 +08:00

Add helper method for safer realm Find<T>

This commit is contained in:
Dean Herbert 2023-08-16 15:36:31 +09:00
parent 88295a49aa
commit 6e11162ab1
3 changed files with 37 additions and 41 deletions

View File

@ -3,6 +3,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using osu.Framework.Platform; using osu.Framework.Platform;
@ -47,13 +48,16 @@ namespace osu.Game.Database
// This method should be removed as soon as all the surrounding pieces support non-detached operations. // This method should be removed as soon as all the surrounding pieces support non-detached operations.
if (!item.IsManaged) if (!item.IsManaged)
{ {
// We use RealmLive here as it handled re-retrieval and refreshing of realm if required. // Importantly, begin the realm write *before* re-fetching, else the update realm may not be in a consistent state
new RealmLive<TModel>(item.ID, Realm).PerformWrite(i => // (ie. if an async import finished very recently).
Realm.Realm.Write(realm =>
{ {
operation(i); var managed = realm.FindWithRefresh<TModel>(item.ID);
Debug.Assert(managed != null);
operation(managed);
item.Files.Clear(); item.Files.Clear();
item.Files.AddRange(i.Files.Detach()); item.Files.AddRange(managed.Files.Detach());
}); });
} }
else else

View File

@ -8,6 +8,31 @@ namespace osu.Game.Database
{ {
public static class RealmExtensions public static class RealmExtensions
{ {
/// <summary>
/// Performs a <see cref="Realm.Find{T}(System.Nullable{long})"/>.
/// If a match was not found, a <see cref="Realm.Refresh"/> is performed before trying a second time.
/// This ensures that an instance is found even if the realm requested against was not in a consistent state.
/// </summary>
/// <param name="realm"></param>
/// <param name="id"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T? FindWithRefresh<T>(this Realm realm, Guid id) where T : IRealmObject
{
var found = realm.Find<T>(id);
if (found == null)
{
// It may be that we access this from the update thread before a refresh has taken place.
// To ensure that behaviour matches what we'd expect (the object *is* available), force
// a refresh to bring in any off-thread changes immediately.
realm.Refresh();
found = realm.Find<T>(id);
}
return found;
}
/// <summary> /// <summary>
/// Perform a write operation against the provided realm instance. /// Perform a write operation against the provided realm instance.
/// </summary> /// </summary>

View File

@ -41,24 +41,6 @@ namespace osu.Game.Database
dataIsFromUpdateThread = ThreadSafety.IsUpdateThread; dataIsFromUpdateThread = ThreadSafety.IsUpdateThread;
} }
/// <summary>
/// Construct a new instance of live realm data from an ID.
/// </summary>
/// <param name="id">The ID of an already-persisting realm instance.</param>
/// <param name="realm">The realm factory the data was sourced from. May be null for an unmanaged object.</param>
public RealmLive(Guid id, RealmAccess realm)
: base(id)
{
data = retrieveFromID(realm.Realm);
if (data.IsNull())
throw new ArgumentException("Realm instance for provided ID could not be found.", nameof(id));
this.realm = realm;
dataIsFromUpdateThread = ThreadSafety.IsUpdateThread;
}
/// <summary> /// <summary>
/// Perform a read operation on this live object. /// Perform a read operation on this live object.
/// </summary> /// </summary>
@ -80,7 +62,7 @@ namespace osu.Game.Database
return; return;
} }
perform(retrieveFromID(r)); perform(r.FindWithRefresh<T>(ID)!);
RealmLiveStatistics.USAGE_ASYNC.Value++; RealmLiveStatistics.USAGE_ASYNC.Value++;
}); });
} }
@ -102,7 +84,7 @@ namespace osu.Game.Database
return realm.Run(r => return realm.Run(r =>
{ {
var returnData = perform(retrieveFromID(r)); var returnData = perform(r.FindWithRefresh<T>(ID)!);
RealmLiveStatistics.USAGE_ASYNC.Value++; RealmLiveStatistics.USAGE_ASYNC.Value++;
if (returnData is RealmObjectBase realmObject && realmObject.IsManaged) if (returnData is RealmObjectBase realmObject && realmObject.IsManaged)
@ -159,25 +141,10 @@ namespace osu.Game.Database
} }
dataIsFromUpdateThread = true; dataIsFromUpdateThread = true;
data = retrieveFromID(realm.Realm); data = realm.Realm.FindWithRefresh<T>(ID)!;
RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++; RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++;
} }
private T retrieveFromID(Realm realm)
{
var found = realm.Find<T>(ID);
if (found == null)
{
// It may be that we access this from the update thread before a refresh has taken place.
// To ensure that behaviour matches what we'd expect (the object *is* available), force
// a refresh to bring in any off-thread changes immediately.
realm.Refresh();
found = realm.Find<T>(ID)!;
}
return found;
}
} }
internal static class RealmLiveStatistics internal static class RealmLiveStatistics