diff --git a/RocksDbSharp/RocksDb.cs b/RocksDbSharp/RocksDb.cs index 770898b..af91c26 100644 --- a/RocksDbSharp/RocksDb.cs +++ b/RocksDbSharp/RocksDb.cs @@ -5,11 +5,14 @@ using System.Runtime.InteropServices; using System.Text; using Transitional; +// ReSharper disable EmptyGeneralCatchClause namespace RocksDbSharp { public class RocksDb : IDisposable { + bool disposed; + internal static ReadOptions DefaultReadOptions { get; } = new ReadOptions(); internal static OptionsHandle DefaultOptions { get; } = new DbOptions(); internal static WriteOptions DefaultWriteOptions { get; } = new WriteOptions(); @@ -29,13 +32,36 @@ private RocksDb(IntPtr handle, dynamic optionsReferences, dynamic cfOptionsRefs, this.columnFamilies = columnFamilies; } + ~RocksDb() + { + ReleaseUnmanagedResources(); + } + public void Dispose() + { + if (disposed) return; + + try + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + finally + { + disposed = true; + } + } + + void ReleaseUnmanagedResources() { if (columnFamilies != null) { foreach (var cfh in columnFamilies.Values) + { cfh.Dispose(); + } } + Native.Instance.rocksdb_close(Handle); } @@ -168,7 +194,7 @@ public long Get(byte[] key, long keyLength, byte[] buffer, long offset, long len } } - public KeyValuePair[] MultiGet(byte[][] keys, ColumnFamilyHandle[] cf = null, ReadOptions readOptions = null) + public KeyValuePair[] MultiGet(byte[][] keys, ColumnFamilyHandle[] cf = null, ReadOptions readOptions = null) { return Native.Instance.rocksdb_multi_get(Handle, (readOptions ?? DefaultReadOptions).Handle, keys); } @@ -261,7 +287,7 @@ public void DropColumnFamily(string name) Native.Instance.rocksdb_drop_column_family(Handle, cf.Handle); columnFamilies.Remove(name); } - + public ColumnFamilyHandle GetDefaultColumnFamily() { return GetColumnFamily(ColumnFamilies.DefaultName); diff --git a/tests/RocksDbSharpTest/LifestyleTest.cs b/tests/RocksDbSharpTest/LifestyleTest.cs new file mode 100644 index 0000000..0e8d78f --- /dev/null +++ b/tests/RocksDbSharpTest/LifestyleTest.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using RocksDbSharp; +using Xunit; +// ReSharper disable RedundantArgumentDefaultValue +// ReSharper disable ArgumentsStyleLiteral + +namespace RocksDbSharpTest +{ + public class LifestyleTest + { + [Fact] + public void DoubleDisposableDoesNotThrow() + { + var testdir = Path.Combine(Path.GetTempPath(), "lifestyle_test"); + var testdb = Path.Combine(testdir, "main"); + var path = Environment.ExpandEnvironmentVariables(testdb); + + if (Directory.Exists(testdir)) + { + Directory.Delete(testdir, recursive: true); + } + + Directory.CreateDirectory(testdir); + + var options = new DbOptions().SetCreateIfMissing(true).EnableStatistics(); + + var db = RocksDb.Open(options, path); + + db.Dispose(); + + // throws AccessViolationException, which on my machine crashed the process so hard that XUnit coulnd't cope... + // + db.Dispose(); + // + // got this in Event Viewer though: + // + // Application: dotnet.exe + // CoreCLR Version: 4.6.28619.1 + // Description: The process was terminated due to an internal error in the .NET Runtime at IP 00007FFF39BC5AA3 (00007FFF39A20000) with exit code c0000005. + // + } + } +} \ No newline at end of file