diff --git a/src/LightningDB.Tests/CursorTests.cs b/src/LightningDB.Tests/CursorTests.cs
index 1ced1af..c8bc79a 100644
--- a/src/LightningDB.Tests/CursorTests.cs
+++ b/src/LightningDB.Tests/CursorTests.cs
@@ -60,8 +60,6 @@ public void CursorShouldPutValues()
_env.RunCursorScenario((tx, _, c) =>
{
PopulateCursorValues(c);
- c.Dispose();
- //TODO evaluate how not to require this Dispose likely due to #155
var result = tx.Commit();
Assert.Equal(MDBResultCode.Success, result);
});
@@ -362,4 +360,41 @@ public void ShouldDeleteDuplicates()
Assert.Equal(MDBResultCode.NotFound, result);
}, DatabaseOpenFlags.DuplicatesFixed | DatabaseOpenFlags.Create);
}
+
+ [Fact]
+ public void CanPutBatchesViaCursorIssue155()
+ {
+ static LightningDatabase OpenDatabase(LightningEnvironment environment)
+ {
+ using var tx = environment.BeginTransaction();
+ var db = tx.OpenDatabase(configuration: new DatabaseConfiguration { Flags = DatabaseOpenFlags.Create });
+ tx.Commit();
+ return db;
+ }
+
+ void ReproduceCoreIteration(LightningEnvironment environment, LightningDatabase db)
+ {
+ using var tx = environment.BeginTransaction(); //auto-disposed at end of scope
+ using var cursor = tx.CreateCursor(db); //auto-disposed at end of scope
+
+ var guid = Guid.NewGuid().ToString();
+ var guidBytes = UTF8.GetBytes(guid);
+
+ _ = cursor.Put(
+ guidBytes,
+ guidBytes,
+ CursorPutOptions.None
+ );
+
+ tx.Commit().ThrowOnError();
+ }
+
+ using var db = OpenDatabase(_env);
+
+ for (var i = 0; i < 5000; i++)
+ {
+ ReproduceCoreIteration(_env, db);
+ }
+ Assert.True(true, "Code would be unreachable otherwise.");
+ }
}
\ No newline at end of file
diff --git a/src/LightningDB/LightningCursor.cs b/src/LightningDB/LightningCursor.cs
index d5c5dda..e409572 100644
--- a/src/LightningDB/LightningCursor.cs
+++ b/src/LightningDB/LightningCursor.cs
@@ -29,6 +29,7 @@ internal LightningCursor(LightningDatabase db, LightningTransaction txn)
mdb_cursor_open(txn.Handle(), db.Handle(), out _handle).ThrowOnError();
Transaction = txn;
+ Transaction.ShouldCloseCursor = true;
Transaction.Disposing += Dispose;
}
@@ -503,14 +504,17 @@ public MDBResultCode Renew(LightningTransaction txn)
/// True if called from Dispose.
private void Dispose(bool disposing)
{
- if (_handle == 0)
+ if (_handle == default)
return;
if (!disposing)
throw new InvalidOperationException("The LightningCursor was not disposed and cannot be reliably dealt with from the finalizer");
- mdb_cursor_close(_handle);
- _handle = 0;
+ if (Transaction.ShouldCloseCursor)
+ {
+ mdb_cursor_close(_handle);
+ }
+ _handle = default;
Transaction.Disposing -= Dispose;
diff --git a/src/LightningDB/LightningDatabase.cs b/src/LightningDB/LightningDatabase.cs
index d5b49c1..0f4f2a4 100644
--- a/src/LightningDB/LightningDatabase.cs
+++ b/src/LightningDB/LightningDatabase.cs
@@ -86,6 +86,7 @@ public Stats DatabaseStats
public MDBResultCode Drop(LightningTransaction transaction)
{
var result = mdb_drop(transaction.Handle(), _handle, true);
+ _transaction.ShouldCloseCursor = false;
IsOpened = false;
_handle = default;
return result;
diff --git a/src/LightningDB/LightningTransaction.cs b/src/LightningDB/LightningTransaction.cs
index 7e4ee43..cf08a70 100644
--- a/src/LightningDB/LightningTransaction.cs
+++ b/src/LightningDB/LightningTransaction.cs
@@ -59,6 +59,8 @@ private void OnParentStateChanging(LightningTransactionState state)
public event Action Disposing;
private event Action StateChanging;
+
+ internal bool ShouldCloseCursor { get; set; }
///
/// Current transaction state.
@@ -295,6 +297,7 @@ public MDBResultCode Renew()
public MDBResultCode Commit()
{
State = LightningTransactionState.Committed;
+ ShouldCloseCursor = false;
StateChanging?.Invoke(State);
return mdb_txn_commit(_handle);
}