From 4d02eaabdca94835153736a69d67864559adf3ba Mon Sep 17 00:00:00 2001 From: Timothy Baldridge Date: Mon, 6 May 2024 16:14:25 -0600 Subject: [PATCH] Fix a strange race condition with interop delegates --- src/NexusMods.MnemonicDB.Storage/DatomStore.cs | 11 +---------- .../RocksDbBackend/IndexStore.cs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/NexusMods.MnemonicDB.Storage/DatomStore.cs b/src/NexusMods.MnemonicDB.Storage/DatomStore.cs index fb8cdfd5..1b8ae4ce 100644 --- a/src/NexusMods.MnemonicDB.Storage/DatomStore.cs +++ b/src/NexusMods.MnemonicDB.Storage/DatomStore.cs @@ -136,16 +136,7 @@ public async Task Transact(IndexSegment datoms, HashSet public async Task Sync() { - var pending = new PendingTransaction - { - Data = new IndexSegment(), - TxFunctions = null, - DatabaseFactory = null - }; - if (!_txChannel.Writer.TryWrite(pending)) - throw new InvalidOperationException("Failed to write to the transaction channel"); - - return await pending.CompletionSource.Task; + return await Transact(new IndexSegment()); } public IObservable<(TxId TxId, ISnapshot Snapshot)> TxLog => _updatesSubject; diff --git a/src/NexusMods.MnemonicDB.Storage/RocksDbBackend/IndexStore.cs b/src/NexusMods.MnemonicDB.Storage/RocksDbBackend/IndexStore.cs index 00799ea1..004502b4 100644 --- a/src/NexusMods.MnemonicDB.Storage/RocksDbBackend/IndexStore.cs +++ b/src/NexusMods.MnemonicDB.Storage/RocksDbBackend/IndexStore.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Runtime.InteropServices; +using DynamicData; using NexusMods.MnemonicDB.Abstractions; using NexusMods.MnemonicDB.Abstractions.DatomComparators; using NexusMods.MnemonicDB.Abstractions.DatomIterators; @@ -21,6 +23,15 @@ public class IndexStore : IRocksDBIndexStore private IntPtr _namePtr; private ColumnFamilyOptions _options = null!; + /// + /// This is a bit of a hack, but we throw all our interop delegates in here, and then they + /// live for the entire life of the application. It seems that RocksDB will occasionally call + /// delegates after we think we've disposed of the handles. It really doesn't matter as these + /// things will never amount to more than a few dozen objects. + /// + /// + private static List _roots = new (); + public IndexStore(string handleName, IndexType type, AttributeRegistry registry) { Type = type; @@ -47,6 +58,9 @@ public void SetupColumnFamily(IIndex index, ColumnFamilies columnFamilies) } }; + // Save these as roots so they never get GC'd + _roots.Add((_nameDelegate, _destructorDelegate, _comparatorDelegate)); + _comparator = Native.Instance.rocksdb_comparator_create(IntPtr.Zero, _destructorDelegate, _comparatorDelegate, _nameDelegate);