1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-28 23:32:59 +08:00

Refactor naming and add more comments to help understanding in RealmContextFactory subscription logic

This commit is contained in:
Dean Herbert 2022-01-24 14:48:55 +09:00
parent 40aa873190
commit cb319cebdb

View File

@ -63,8 +63,22 @@ namespace osu.Game.Database
private readonly ThreadLocal<bool> currentThreadCanCreateContexts = new ThreadLocal<bool>(); private readonly ThreadLocal<bool> currentThreadCanCreateContexts = new ThreadLocal<bool>();
private readonly Dictionary<Func<Realm, IDisposable?>, IDisposable?> customSubscriptionActions = new Dictionary<Func<Realm, IDisposable?>, IDisposable?>(); /// <summary>
/// Holds a map of functions registered via <see cref="RegisterCustomSubscription"/> and <see cref="RegisterForNotifications{T}"/> and a coinciding action which when triggered,
/// will unregister the subscription from realm.
///
/// Put another way, the key is an action which registers the subscription with realm. The returned <see cref="IDisposable"/> from the action is stored as the value and only
/// used internally.
///
/// Entries in this dictionary are only removed when a consumer signals that the subscription should be permanently ceased (via their own <see cref="IDisposable"/>).
/// </summary>
private readonly Dictionary<Func<Realm, IDisposable?>, IDisposable?> customSubscriptionsResetMap = new Dictionary<Func<Realm, IDisposable?>, IDisposable?>();
/// <summary>
/// Holds a map of functions registered via <see cref="RegisterForNotifications{T}"/> and a coinciding action which when triggered,
/// fires a change set event with an empty collection. This is used to inform subscribers when a realm context goes away, and ensure they don't use invalidated
/// managed realm objects from a previous firing.
/// </summary>
private readonly Dictionary<Func<Realm, IDisposable?>, Action> realmSubscriptionsResetMap = new Dictionary<Func<Realm, IDisposable?>, Action>(); private readonly Dictionary<Func<Realm, IDisposable?>, Action> realmSubscriptionsResetMap = new Dictionary<Func<Realm, IDisposable?>, Action>();
private static readonly GlobalStatistic<int> contexts_created = GlobalStatistics.Get<int>(@"Realm", @"Contexts (Created)"); private static readonly GlobalStatistic<int> contexts_created = GlobalStatistics.Get<int>(@"Realm", @"Contexts (Created)");
@ -88,7 +102,7 @@ namespace osu.Game.Database
Logger.Log(@$"Opened realm ""{context.Config.DatabasePath}"" at version {context.Config.SchemaVersion}"); Logger.Log(@$"Opened realm ""{context.Config.DatabasePath}"" at version {context.Config.SchemaVersion}");
// Resubscribe any subscriptions // Resubscribe any subscriptions
foreach (var action in customSubscriptionActions.Keys) foreach (var action in customSubscriptionsResetMap.Keys)
registerSubscription(action); registerSubscription(action);
} }
@ -245,11 +259,12 @@ namespace osu.Game.Database
lock (contextLock) lock (contextLock)
{ {
Func<Realm, IDisposable?> action = realm => query(realm).QueryAsyncWithNotifications(onChanged);
// Store an action which is used when blocking to ensure consumers don't use results of a stale changeset firing.
realmSubscriptionsResetMap.Add(action, () => onChanged(new EmptyRealmSet<T>(), null, null)); realmSubscriptionsResetMap.Add(action, () => onChanged(new EmptyRealmSet<T>(), null, null));
return RegisterCustomSubscription(action); return RegisterCustomSubscription(action);
} }
IDisposable? action(Realm realm) => query(realm).QueryAsyncWithNotifications(onChanged);
} }
/// <summary> /// <summary>
@ -266,8 +281,8 @@ namespace osu.Game.Database
registerSubscription(action); registerSubscription(action);
// This token is returned to the consumer only. // This token is returned to the consumer.
// It will cause the registration to be permanently removed. // When disposed, it will cause the registration to be permanently ceased (unsubscribed with realm and unregistered by this class).
return new InvokeOnDisposal(() => return new InvokeOnDisposal(() =>
{ {
if (ThreadSafety.IsUpdateThread) if (ThreadSafety.IsUpdateThread)
@ -279,10 +294,10 @@ namespace osu.Game.Database
{ {
lock (contextLock) lock (contextLock)
{ {
if (customSubscriptionActions.TryGetValue(action, out var unsubscriptionAction)) if (customSubscriptionsResetMap.TryGetValue(action, out var unsubscriptionAction))
{ {
unsubscriptionAction?.Dispose(); unsubscriptionAction?.Dispose();
customSubscriptionActions.Remove(action); customSubscriptionsResetMap.Remove(action);
realmSubscriptionsResetMap.Remove(action); realmSubscriptionsResetMap.Remove(action);
} }
} }
@ -301,10 +316,10 @@ namespace osu.Game.Database
lock (contextLock) lock (contextLock)
{ {
Debug.Assert(!customSubscriptionActions.TryGetValue(action, out var found) || found == null); Debug.Assert(!customSubscriptionsResetMap.TryGetValue(action, out var found) || found == null);
current_thread_subscriptions_allowed.Value = true; current_thread_subscriptions_allowed.Value = true;
customSubscriptionActions[action] = action(realm); customSubscriptionsResetMap[action] = action(realm);
current_thread_subscriptions_allowed.Value = false; current_thread_subscriptions_allowed.Value = false;
} }
} }
@ -318,10 +333,10 @@ namespace osu.Game.Database
foreach (var action in realmSubscriptionsResetMap.Values) foreach (var action in realmSubscriptionsResetMap.Values)
action(); action();
foreach (var action in customSubscriptionActions) foreach (var action in customSubscriptionsResetMap)
{ {
action.Value?.Dispose(); action.Value?.Dispose();
customSubscriptionActions[action.Key] = null; customSubscriptionsResetMap[action.Key] = null;
} }
} }