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.

196 lines
8.4 KiB

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