|
@ -126,6 +126,7 @@ namespace IPA.Config.Stores |
|
|
{ |
|
|
{ |
|
|
private readonly IGeneratedStore generated; |
|
|
private readonly IGeneratedStore generated; |
|
|
private bool inChangeTransaction = false; |
|
|
private bool inChangeTransaction = false; |
|
|
|
|
|
private bool changedInTransaction = false; |
|
|
|
|
|
|
|
|
internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) }); |
|
|
internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) }); |
|
|
public Impl(IGeneratedStore store) => generated = store; |
|
|
public Impl(IGeneratedStore store) => generated = store; |
|
@ -173,41 +174,61 @@ namespace IPA.Config.Stores |
|
|
|
|
|
|
|
|
internal static MethodInfo ImplChangeTransactionMethod = typeof(Impl).GetMethod(nameof(ImplChangeTransaction)); |
|
|
internal static MethodInfo ImplChangeTransactionMethod = typeof(Impl).GetMethod(nameof(ImplChangeTransaction)); |
|
|
public static IDisposable ImplChangeTransaction(IGeneratedStore s, IDisposable nest) => FindImpl(s).ChangeTransaction(nest); |
|
|
public static IDisposable ImplChangeTransaction(IGeneratedStore s, IDisposable nest) => FindImpl(s).ChangeTransaction(nest); |
|
|
// TODO: use some fixed pool of these, because their lifetimes are hella short
|
|
|
|
|
|
|
|
|
// TODO: improve trasactionals so they don't always save in every case
|
|
|
public IDisposable ChangeTransaction(IDisposable nest, bool takeWrite = true) |
|
|
public IDisposable ChangeTransaction(IDisposable nest, bool takeWrite = true) |
|
|
=> new ChangeTransactionObj(this, !inChangeTransaction, nest, takeWrite && !WriteSyncObject.IsWriteLockHeld); |
|
|
|
|
|
|
|
|
=> GetFreeTransaction().InitWith(this, !inChangeTransaction, nest, takeWrite && !WriteSyncObject.IsWriteLockHeld); |
|
|
|
|
|
|
|
|
|
|
|
private ChangeTransactionObj GetFreeTransaction() |
|
|
|
|
|
=> freeTransactionObjs.Count > 0 ? freeTransactionObjs.Pop() |
|
|
|
|
|
: new ChangeTransactionObj(); |
|
|
|
|
|
// TODO: maybe sometimes clean this?
|
|
|
|
|
|
private static readonly Stack<ChangeTransactionObj> freeTransactionObjs = new Stack<ChangeTransactionObj>(); |
|
|
|
|
|
|
|
|
private sealed class ChangeTransactionObj : IDisposable |
|
|
private sealed class ChangeTransactionObj : IDisposable |
|
|
{ |
|
|
{ |
|
|
private readonly Impl impl; |
|
|
|
|
|
private readonly bool owns; |
|
|
|
|
|
private readonly bool ownsWrite; |
|
|
|
|
|
private readonly IDisposable nested; |
|
|
|
|
|
|
|
|
private struct Data |
|
|
|
|
|
{ |
|
|
|
|
|
public readonly Impl impl; |
|
|
|
|
|
public readonly bool owns; |
|
|
|
|
|
public readonly bool ownsWrite; |
|
|
|
|
|
public readonly IDisposable nested; |
|
|
|
|
|
|
|
|
public ChangeTransactionObj(Impl impl, bool owning, IDisposable nest, bool takeWrite) |
|
|
|
|
|
|
|
|
public Data(Impl impl, bool owning, bool takeWrite, IDisposable nest) |
|
|
|
|
|
{ |
|
|
|
|
|
this.impl = impl; owns = owning; ownsWrite = takeWrite; nested = nest; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
private Data data; |
|
|
|
|
|
|
|
|
|
|
|
public ChangeTransactionObj InitWith(Impl impl, bool owning, IDisposable nest, bool takeWrite) |
|
|
{ |
|
|
{ |
|
|
this.impl = impl; |
|
|
|
|
|
nested = nest; |
|
|
|
|
|
if (owns = owning) |
|
|
|
|
|
|
|
|
data = new Data(impl, owning, takeWrite, nest); |
|
|
|
|
|
|
|
|
|
|
|
if (data.owns) |
|
|
impl.inChangeTransaction = true; |
|
|
impl.inChangeTransaction = true; |
|
|
if (ownsWrite = takeWrite) |
|
|
|
|
|
|
|
|
if (data.ownsWrite) |
|
|
impl.TakeWrite(); |
|
|
impl.TakeWrite(); |
|
|
|
|
|
|
|
|
|
|
|
return this; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void Dispose() |
|
|
|
|
|
|
|
|
public void Dispose() => Dispose(true); |
|
|
|
|
|
private void Dispose(bool addToStore) |
|
|
{ |
|
|
{ |
|
|
if (owns) |
|
|
|
|
|
|
|
|
if (data.owns) |
|
|
{ |
|
|
{ |
|
|
impl.inChangeTransaction = false; |
|
|
|
|
|
impl.InvokeChanged(); |
|
|
|
|
|
|
|
|
data.impl.inChangeTransaction = false; |
|
|
|
|
|
data.impl.InvokeChanged(); |
|
|
} |
|
|
} |
|
|
nested?.Dispose(); |
|
|
|
|
|
if (ownsWrite) |
|
|
|
|
|
impl.ReleaseWrite(); |
|
|
|
|
|
GC.SuppressFinalize(this); |
|
|
|
|
|
|
|
|
data.nested?.Dispose(); |
|
|
|
|
|
if (data.ownsWrite) |
|
|
|
|
|
data.impl.ReleaseWrite(); |
|
|
|
|
|
|
|
|
|
|
|
if (addToStore) |
|
|
|
|
|
freeTransactionObjs.Push(this); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
~ChangeTransactionObj() => Dispose(); |
|
|
|
|
|
|
|
|
~ChangeTransactionObj() => Dispose(false); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public static Impl FindImpl(IGeneratedStore store) |
|
|
public static Impl FindImpl(IGeneratedStore store) |
|
|