|
|
- #nullable enable
- using IPA.Logging;
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using System.Threading;
- using System.Runtime.CompilerServices;
- using System.ComponentModel;
- using IPA.Config.Data;
- #if NET3
- using Net3_Proxy;
- using Array = Net3_Proxy.Array;
- #endif
-
- [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)]
-
- namespace IPA.Config.Stores
- {
- internal static partial class GeneratedStoreImpl
- {
- internal interface IGeneratedStore
- {
- Type Type { get; }
- IGeneratedStore Parent { get; }
- Impl Impl { get; }
- void OnReload();
-
- void Changed();
- IDisposable ChangeTransaction();
-
- Value Serialize();
- void Deserialize(Value val);
- }
- internal interface IGeneratedStore<T> : IGeneratedStore where T : class
- {
- void CopyFrom(T source, bool useLock);
- }
- internal interface IGeneratedPropertyChanged : INotifyPropertyChanged
- {
- PropertyChangedEventHandler PropertyChangedEvent { get; }
- }
-
- internal class Impl : IConfigStore
- {
- private readonly IGeneratedStore generated;
- private long enteredTransactions;
-
- internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
- public Impl(IGeneratedStore store) => generated = store;
- public WaitHandle? SyncObject => null;
- public static WaitHandle? ImplGetSyncObject(IGeneratedStore s) => FindImpl(s)?.SyncObject;
- internal static MethodInfo ImplGetSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetSyncObject));
-
- public ReaderWriterLockSlim WriteSyncObject { get; } = new();
- public static ReaderWriterLockSlim? ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject;
- internal static MethodInfo ImplGetWriteSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetWriteSyncObject));
-
- internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
- public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s)?.SignalChanged();
- public void SignalChanged()
- {
- ConfigRuntime.RequiresSave.Add(this);
- }
-
- internal static MethodInfo ImplInvokeChangedMethod = typeof(Impl).GetMethod(nameof(ImplInvokeChanged));
- public static void ImplInvokeChanged(IGeneratedStore s) => FindImpl(s)?.InvokeChanged();
- public void InvokeChanged() => generated.Changed();
-
- internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
- public static void ImplTakeRead(IGeneratedStore s) => FindImpl(s)?.TakeRead();
- public void TakeRead()
- {
- if (!WriteSyncObject.IsWriteLockHeld)
- WriteSyncObject.EnterReadLock();
- }
-
- internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
- public static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s)?.ReleaseRead();
- public void ReleaseRead()
- {
- if (!WriteSyncObject.IsWriteLockHeld)
- WriteSyncObject.ExitReadLock();
- }
-
- internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
- public static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s)?.TakeWrite();
- public void TakeWrite() => WriteSyncObject.EnterWriteLock();
-
- internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite));
- public static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s)?.ReleaseWrite();
- public void ReleaseWrite() => WriteSyncObject.ExitWriteLock();
-
- internal static MethodInfo ImplChangeTransactionMethod = typeof(Impl).GetMethod(nameof(ImplChangeTransaction));
- public static IDisposable? ImplChangeTransaction(IGeneratedStore s, IDisposable nest) => FindImpl(s)?.ChangeTransaction(nest);
- // TODO: improve trasactionals so they don't always save in every case
- public IDisposable ChangeTransaction(IDisposable nest, bool takeWrite = true)
- => GetFreeTransaction().InitWith(this, nest, takeWrite && !WriteSyncObject.IsWriteLockHeld);
-
- private static ChangeTransactionObj GetFreeTransaction()
- => freeTransactionObjs.Count > 0 ? freeTransactionObjs.Pop()
- : new ChangeTransactionObj();
- // TODO: maybe sometimes clean this?
- private static readonly Stack<ChangeTransactionObj> freeTransactionObjs = new();
-
- private sealed class ChangeTransactionObj : IDisposable
- {
- private struct Data
- {
- public readonly Impl impl;
- public readonly bool ownsWrite;
- public readonly IDisposable nested;
-
- public Data(Impl impl, bool takeWrite, IDisposable nest)
- {
- this.impl = impl; ownsWrite = takeWrite; nested = nest;
- }
- }
- private Data data;
-
- public ChangeTransactionObj InitWith(Impl impl, IDisposable nest, bool takeWrite)
- {
- data = new Data(impl, takeWrite, nest);
-
- _ = Interlocked.Increment(ref impl.enteredTransactions);
- if (data.ownsWrite)
- impl.TakeWrite();
-
- return this;
- }
-
- public void Dispose() => Dispose(true);
- private void Dispose(bool addToStore)
- {
- if (data.impl != null && Interlocked.Decrement(ref data.impl.enteredTransactions) == 0)
- {
- data.impl.InvokeChanged();
- }
- data.nested?.Dispose();
- try
- {
- if (data.ownsWrite)
- data.impl?.ReleaseWrite();
- }
- catch
- {
- }
-
- data = default;
-
- if (addToStore)
- freeTransactionObjs.Push(this);
- }
-
- ~ChangeTransactionObj() => Dispose(false);
- }
-
- public static Impl? FindImpl(IGeneratedStore store)
- {
- while (store?.Parent != null) store = store.Parent; // walk to the top of the tree
- return store?.Impl;
- }
-
- internal static MethodInfo ImplReadFromMethod = typeof(Impl).GetMethod(nameof(ImplReadFrom));
- public static void ImplReadFrom(IGeneratedStore s, ConfigProvider provider) => FindImpl(s)?.ReadFrom(provider);
- public void ReadFrom(ConfigProvider provider)
- {
- Logger.Config.Debug($"Generated impl ReadFrom {generated.GetType()}");
- var values = provider.Load();
- //Logger.config.Debug($"Read {values}");
- generated.Deserialize(values);
-
- using var transaction = generated.ChangeTransaction();
- generated.OnReload();
- }
-
- internal static MethodInfo ImplWriteToMethod = typeof(Impl).GetMethod(nameof(ImplWriteTo));
- public static void ImplWriteTo(IGeneratedStore s, ConfigProvider provider) => FindImpl(s)?.WriteTo(provider);
- public void WriteTo(ConfigProvider provider)
- {
- Logger.Config.Debug($"Generated impl WriteTo {generated.GetType()}");
- var values = generated.Serialize();
- //Logger.config.Debug($"Serialized {values}");
- provider.Store(values);
- }
- }
- }
- }
|