You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
8.0 KiB

  1. #nullable enable
  2. using IPA.Logging;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Reflection;
  6. using System.Threading;
  7. using System.Runtime.CompilerServices;
  8. using System.ComponentModel;
  9. using IPA.Config.Data;
  10. #if NET3
  11. using Net3_Proxy;
  12. using Array = Net3_Proxy.Array;
  13. #endif
  14. [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)]
  15. namespace IPA.Config.Stores
  16. {
  17. internal static partial class GeneratedStoreImpl
  18. {
  19. internal interface IGeneratedStore
  20. {
  21. Type Type { get; }
  22. IGeneratedStore Parent { get; }
  23. Impl Impl { get; }
  24. void OnReload();
  25. void Changed();
  26. IDisposable ChangeTransaction();
  27. Value Serialize();
  28. void Deserialize(Value val);
  29. }
  30. internal interface IGeneratedStore<T> : IGeneratedStore where T : class
  31. {
  32. void CopyFrom(T source, bool useLock);
  33. }
  34. internal interface IGeneratedPropertyChanged : INotifyPropertyChanged
  35. {
  36. PropertyChangedEventHandler PropertyChangedEvent { get; }
  37. }
  38. internal class Impl : IConfigStore
  39. {
  40. private readonly IGeneratedStore generated;
  41. private long enteredTransactions;
  42. internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
  43. public Impl(IGeneratedStore store) => generated = store;
  44. public WaitHandle? SyncObject => null;
  45. public static WaitHandle? ImplGetSyncObject(IGeneratedStore s) => FindImpl(s)?.SyncObject;
  46. internal static MethodInfo ImplGetSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetSyncObject));
  47. public ReaderWriterLockSlim WriteSyncObject { get; } = new();
  48. public static ReaderWriterLockSlim? ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject;
  49. internal static MethodInfo ImplGetWriteSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetWriteSyncObject));
  50. internal static MethodInfo ImplSignalChangedMethod = typeof(Impl).GetMethod(nameof(ImplSignalChanged));
  51. public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s)?.SignalChanged();
  52. public void SignalChanged()
  53. {
  54. ConfigRuntime.AddRequiresSave(this);
  55. }
  56. internal static MethodInfo ImplInvokeChangedMethod = typeof(Impl).GetMethod(nameof(ImplInvokeChanged));
  57. public static void ImplInvokeChanged(IGeneratedStore s) => FindImpl(s)?.InvokeChanged();
  58. public void InvokeChanged() => generated.Changed();
  59. internal static MethodInfo ImplTakeReadMethod = typeof(Impl).GetMethod(nameof(ImplTakeRead));
  60. public static void ImplTakeRead(IGeneratedStore s) => FindImpl(s)?.TakeRead();
  61. public void TakeRead()
  62. {
  63. if (!WriteSyncObject.IsWriteLockHeld)
  64. WriteSyncObject.EnterReadLock();
  65. }
  66. internal static MethodInfo ImplReleaseReadMethod = typeof(Impl).GetMethod(nameof(ImplReleaseRead));
  67. public static void ImplReleaseRead(IGeneratedStore s) => FindImpl(s)?.ReleaseRead();
  68. public void ReleaseRead()
  69. {
  70. if (!WriteSyncObject.IsWriteLockHeld)
  71. WriteSyncObject.ExitReadLock();
  72. }
  73. internal static MethodInfo ImplTakeWriteMethod = typeof(Impl).GetMethod(nameof(ImplTakeWrite));
  74. public static void ImplTakeWrite(IGeneratedStore s) => FindImpl(s)?.TakeWrite();
  75. public void TakeWrite() => WriteSyncObject.EnterWriteLock();
  76. internal static MethodInfo ImplReleaseWriteMethod = typeof(Impl).GetMethod(nameof(ImplReleaseWrite));
  77. public static void ImplReleaseWrite(IGeneratedStore s) => FindImpl(s)?.ReleaseWrite();
  78. public void ReleaseWrite() => WriteSyncObject.ExitWriteLock();
  79. internal static MethodInfo ImplChangeTransactionMethod = typeof(Impl).GetMethod(nameof(ImplChangeTransaction));
  80. public static IDisposable? ImplChangeTransaction(IGeneratedStore s, IDisposable nest) => FindImpl(s)?.ChangeTransaction(nest);
  81. // TODO: improve trasactionals so they don't always save in every case
  82. public IDisposable ChangeTransaction(IDisposable nest, bool takeWrite = true)
  83. => GetFreeTransaction().InitWith(this, nest, takeWrite && !WriteSyncObject.IsWriteLockHeld);
  84. private static ChangeTransactionObj GetFreeTransaction()
  85. => freeTransactionObjs.Count > 0 ? freeTransactionObjs.Pop()
  86. : new ChangeTransactionObj();
  87. // TODO: maybe sometimes clean this?
  88. private static readonly Stack<ChangeTransactionObj> freeTransactionObjs = new();
  89. private sealed class ChangeTransactionObj : IDisposable
  90. {
  91. private struct Data
  92. {
  93. public readonly Impl impl;
  94. public readonly bool ownsWrite;
  95. public readonly IDisposable nested;
  96. public Data(Impl impl, bool takeWrite, IDisposable nest)
  97. {
  98. this.impl = impl; ownsWrite = takeWrite; nested = nest;
  99. }
  100. }
  101. private Data data;
  102. public ChangeTransactionObj InitWith(Impl impl, IDisposable nest, bool takeWrite)
  103. {
  104. data = new Data(impl, takeWrite, nest);
  105. _ = Interlocked.Increment(ref impl.enteredTransactions);
  106. if (data.ownsWrite)
  107. impl.TakeWrite();
  108. return this;
  109. }
  110. public void Dispose() => Dispose(true);
  111. private void Dispose(bool addToStore)
  112. {
  113. if (data.impl != null && Interlocked.Decrement(ref data.impl.enteredTransactions) == 0)
  114. {
  115. data.impl.InvokeChanged();
  116. }
  117. data.nested?.Dispose();
  118. try
  119. {
  120. if (data.ownsWrite)
  121. data.impl?.ReleaseWrite();
  122. }
  123. catch
  124. {
  125. }
  126. data = default;
  127. if (addToStore)
  128. freeTransactionObjs.Push(this);
  129. }
  130. ~ChangeTransactionObj() => Dispose(false);
  131. }
  132. public static Impl? FindImpl(IGeneratedStore store)
  133. {
  134. while (store?.Parent != null) store = store.Parent; // walk to the top of the tree
  135. return store?.Impl;
  136. }
  137. internal static MethodInfo ImplReadFromMethod = typeof(Impl).GetMethod(nameof(ImplReadFrom));
  138. public static void ImplReadFrom(IGeneratedStore s, ConfigProvider provider) => FindImpl(s)?.ReadFrom(provider);
  139. public void ReadFrom(ConfigProvider provider)
  140. {
  141. Logger.Config.Debug($"Generated impl ReadFrom {generated.GetType()}");
  142. var values = provider.Load();
  143. //Logger.config.Debug($"Read {values}");
  144. generated.Deserialize(values);
  145. using var transaction = generated.ChangeTransaction();
  146. generated.OnReload();
  147. }
  148. internal static MethodInfo ImplWriteToMethod = typeof(Impl).GetMethod(nameof(ImplWriteTo));
  149. public static void ImplWriteTo(IGeneratedStore s, ConfigProvider provider) => FindImpl(s)?.WriteTo(provider);
  150. public void WriteTo(ConfigProvider provider)
  151. {
  152. Logger.Config.Debug($"Generated impl WriteTo {generated.GetType()}");
  153. var values = generated.Serialize();
  154. //Logger.config.Debug($"Serialized {values}");
  155. provider.Store(values);
  156. }
  157. }
  158. }
  159. }