1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-16 00:13:12 +08:00
osu-lazer/osu.Game/Database/Live.cs

105 lines
3.8 KiB
C#
Raw Normal View History

2021-01-11 15:22:52 +08:00
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Threading;
using Realms;
2021-01-11 15:26:45 +08:00
#nullable enable
2021-01-11 15:22:52 +08:00
namespace osu.Game.Database
{
2021-01-11 15:50:14 +08:00
/// <summary>
/// Provides a method of passing realm live objects across threads in a safe fashion.
/// </summary>
/// <remarks>
/// To consume this as a live instance, the live object should be stored and accessed via <see cref="Get"/> each time.
/// To consume this as a detached instance, assign to a variable of type <typeparam ref="T"/>. The implicit conversion will handle detaching an instance.
2021-01-11 15:50:14 +08:00
/// </remarks>
/// <typeparam name="T">The underlying object type. Should be a <see cref="RealmObject"/> with a primary key provided via <see cref="IHasGuidPrimaryKey"/>.</typeparam>
2021-01-11 15:57:40 +08:00
public class Live<T> : IEquatable<Live<T>>, IHasGuidPrimaryKey
2021-01-11 15:22:52 +08:00
where T : RealmObject, IHasGuidPrimaryKey
{
2021-01-11 15:50:14 +08:00
/// <summary>
/// The primary key of the object.
/// </summary>
2021-01-11 15:57:40 +08:00
public Guid Guid { get; }
public string ID
{
get => Guid.ToString();
set => throw new NotImplementedException();
}
2021-01-11 15:22:52 +08:00
private readonly ThreadLocal<T> threadValues;
private readonly T original;
2021-01-11 15:50:14 +08:00
private readonly IRealmFactory contextFactory;
2021-01-11 15:22:52 +08:00
public Live(T item, IRealmFactory contextFactory)
2021-01-11 15:22:52 +08:00
{
2021-01-11 15:50:14 +08:00
this.contextFactory = contextFactory;
2021-01-11 15:22:52 +08:00
original = item;
Guid = item.Guid;
threadValues = new ThreadLocal<T>(getThreadLocalValue);
2021-01-11 15:22:52 +08:00
// the instance passed in may not be in a managed state.
// for now let's immediately retrieve a managed object on the current thread.
// in the future we may want to delay this until the first access (only populating the Guid at construction time).
if (!item.IsManaged)
original = Get();
}
private T getThreadLocalValue()
{
var context = contextFactory.Context;
2021-01-11 15:22:52 +08:00
// only use the original if no context is available or the source realm is the same.
if (context == null || original.Realm?.IsSameInstance(context) == true) return original;
2021-01-11 15:22:52 +08:00
return context.Find<T>(ID);
2021-01-11 15:22:52 +08:00
}
2021-01-11 15:50:14 +08:00
/// <summary>
/// Retrieve a live reference to the data.
/// </summary>
2021-01-11 15:22:52 +08:00
public T Get() => threadValues.Value;
/// <summary>
/// Retrieve a detached copy of the data.
/// </summary>
public T Detach() => Get().Detach();
2021-01-11 15:50:14 +08:00
/// <summary>
/// Wrap a property of this instance as its own live access object.
/// </summary>
/// <param name="lookup">The child to return.</param>
/// <typeparam name="TChild">The underlying child object type. Should be a <see cref="RealmObject"/> with a primary key provided via <see cref="IHasGuidPrimaryKey"/>.</typeparam>
/// <returns>A wrapped instance of the child.</returns>
2021-01-11 15:30:55 +08:00
public Live<TChild> WrapChild<TChild>(Func<T, TChild> lookup)
2021-01-11 15:50:14 +08:00
where TChild : RealmObject, IHasGuidPrimaryKey => new Live<TChild>(lookup(Get()), contextFactory);
2021-01-11 15:22:52 +08:00
2021-01-11 15:50:14 +08:00
/// <summary>
/// Perform a write operation on this live object.
/// </summary>
/// <param name="perform">The action to perform.</param>
2021-01-11 15:22:52 +08:00
public void PerformUpdate(Action<T> perform)
{
2021-01-11 15:50:14 +08:00
using (contextFactory.GetForWrite())
2021-01-11 15:26:45 +08:00
perform(Get());
2021-01-11 15:22:52 +08:00
}
2021-01-11 15:30:55 +08:00
public static implicit operator T?(Live<T>? wrapper)
=> wrapper?.Detach() ?? null;
2021-01-11 15:22:52 +08:00
2021-01-11 15:30:55 +08:00
public static implicit operator Live<T>(T obj) => obj.WrapAsUnmanaged();
2021-01-11 15:22:52 +08:00
2021-01-11 15:57:40 +08:00
public bool Equals(Live<T>? other) => other != null && other.Guid == Guid;
2021-01-11 15:22:52 +08:00
public override string ToString() => Get().ToString();
}
}