diff --git a/osu.Game/Database/ModelManager.cs b/osu.Game/Database/ModelManager.cs index 56aa0843a0..39dae61d36 100644 --- a/osu.Game/Database/ModelManager.cs +++ b/osu.Game/Database/ModelManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; 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. if (!item.IsManaged) { - // We use RealmLive here as it handled re-retrieval and refreshing of realm if required. - new RealmLive(item.ID, Realm).PerformWrite(i => + // Importantly, begin the realm write *before* re-fetching, else the update realm may not be in a consistent state + // (ie. if an async import finished very recently). + Realm.Realm.Write(realm => { - operation(i); + var managed = realm.FindWithRefresh(item.ID); + Debug.Assert(managed != null); + operation(managed); item.Files.Clear(); - item.Files.AddRange(i.Files.Detach()); + item.Files.AddRange(managed.Files.Detach()); }); } else diff --git a/osu.Game/Database/RealmExtensions.cs b/osu.Game/Database/RealmExtensions.cs index 13c4defb83..ee76f1aa79 100644 --- a/osu.Game/Database/RealmExtensions.cs +++ b/osu.Game/Database/RealmExtensions.cs @@ -8,6 +8,31 @@ namespace osu.Game.Database { public static class RealmExtensions { + /// + /// Performs a . + /// If a match was not found, a 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. + /// + /// + /// + /// + /// + public static T? FindWithRefresh(this Realm realm, Guid id) where T : IRealmObject + { + var found = realm.Find(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(id); + } + + return found; + } + /// /// Perform a write operation against the provided realm instance. /// diff --git a/osu.Game/Database/RealmLive.cs b/osu.Game/Database/RealmLive.cs index bfb755c42a..9e99cba45c 100644 --- a/osu.Game/Database/RealmLive.cs +++ b/osu.Game/Database/RealmLive.cs @@ -41,24 +41,6 @@ namespace osu.Game.Database dataIsFromUpdateThread = ThreadSafety.IsUpdateThread; } - /// - /// Construct a new instance of live realm data from an ID. - /// - /// The ID of an already-persisting realm instance. - /// The realm factory the data was sourced from. May be null for an unmanaged object. - 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; - } - /// /// Perform a read operation on this live object. /// @@ -80,7 +62,7 @@ namespace osu.Game.Database return; } - perform(retrieveFromID(r)); + perform(r.FindWithRefresh(ID)!); RealmLiveStatistics.USAGE_ASYNC.Value++; }); } @@ -102,7 +84,7 @@ namespace osu.Game.Database return realm.Run(r => { - var returnData = perform(retrieveFromID(r)); + var returnData = perform(r.FindWithRefresh(ID)!); RealmLiveStatistics.USAGE_ASYNC.Value++; if (returnData is RealmObjectBase realmObject && realmObject.IsManaged) @@ -159,25 +141,10 @@ namespace osu.Game.Database } dataIsFromUpdateThread = true; - data = retrieveFromID(realm.Realm); + data = realm.Realm.FindWithRefresh(ID)!; + RealmLiveStatistics.USAGE_UPDATE_REFETCH.Value++; } - - private T retrieveFromID(Realm realm) - { - var found = realm.Find(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(ID)!; - } - - return found; - } } internal static class RealmLiveStatistics