// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using System.Threading; using Realms; #nullable enable namespace osu.Game.Database { /// /// Provides a method of passing realm live objects across threads in a safe fashion. /// /// /// To consume this as a live instance, the live object should be stored and accessed via each time. /// To consume this as a detached instance, assign to a variable of type . The implicit conversion will handle detaching an instance. /// /// The underlying object type. Should be a with a primary key provided via . public class Live : IEquatable>, IHasGuidPrimaryKey where T : RealmObject, IHasGuidPrimaryKey { /// /// The primary key of the object. /// public Guid Guid { get; } public string ID { get => Guid.ToString(); set => throw new NotImplementedException(); } private readonly ThreadLocal threadValues; private readonly IRealmFactory contextFactory; public Live(T original, IRealmFactory contextFactory) { this.contextFactory = contextFactory; Guid = original.Guid; var originalContext = original.Realm; threadValues = new ThreadLocal(() => { var context = this.contextFactory.Get(); if (context == null || originalContext?.IsSameInstance(context) != false) return original; return context.Find(Guid); }); } /// /// Retrieve a live reference to the data. /// public T Get() => threadValues.Value; /// /// Wrap a property of this instance as its own live access object. /// /// The child to return. /// The underlying child object type. Should be a with a primary key provided via . /// A wrapped instance of the child. public Live WrapChild(Func lookup) where TChild : RealmObject, IHasGuidPrimaryKey => new Live(lookup(Get()), contextFactory); /// /// Perform a write operation on this live object. /// /// The action to perform. public void PerformUpdate(Action perform) { using (contextFactory.GetForWrite()) perform(Get()); } public static implicit operator T?(Live? wrapper) => wrapper?.Get().Detach(); public static implicit operator Live(T obj) => obj.WrapAsUnmanaged(); public bool Equals(Live? other) => other != null && other.Guid == Guid; public override string ToString() => Get().ToString(); } }