-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Version
5.0.10/Windows 10/NET 5
Describe the bug
Trying to read and update records in a parallel foreach throws an exception with the following message: "The read lock is being released without being held."
It seems to run fine in 5.0.3, but not in 5.0.10.
Code to Reproduce
public class Target
{
[BsonId]
public ObjectId Id { get; set; }
public string Name { get; set; }
public DateTime LastUpdateCheck { get; set; }
}
[Fact]
public void InsertReadTest()
{
var dbFullPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
try
{
using (var db = new LiteDatabase(dbFullPath))
{
var table = db.GetCollection<Target>("targets");
for (int i = 0; i < 10000; i++)
{
table.Insert(new Target
{
Name = $"Name_{i}",
LastUpdateCheck = DateTime.UtcNow
});
}
var targetIds = table.FindAll().Select(t => t.Id);
Parallel.ForEach(targetIds, targetId =>
{
var target = table.FindOne(ur => ur.Id == targetId);
target.LastUpdateCheck = DateTime.UtcNow;
table.Update(target);
});
}
}
finally
{
File.Delete(dbFullPath);
}
}
Expected behavior
No exception thrown.
Screenshots/Stacktrace
The read lock is being released without being held.
at System.Threading.Tasks.TaskReplicator.Run[TState](ReplicatableUserAction1 action, ParallelOptions options, Boolean stopOnFirstFailure) at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](Partitioner1 source, ParallelOptions parallelOptions, Action1 simpleBody, Action2 bodyWithState, Action3 bodyWithStateAndIndex, Func4 bodyWithStateAndLocal, Func5 bodyWithEverything, Func1 localInit, Action1 localFinally) --- End of stack trace from previous location --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw(Exception source) at System.Threading.Tasks.Parallel.ThrowSingleCancellationExceptionOrOtherException(ICollection exceptions, CancellationToken cancelToken, Exception otherException) at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](Partitioner1 source, ParallelOptions parallelOptions, Action1 simpleBody, Action2 bodyWithState, Action3 bodyWithStateAndIndex, Func4 bodyWithStateAndLocal, Func5 bodyWithEverything, Func1 localInit, Action1 localFinally) at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable1 source, ParallelOptions parallelOptions, Action1 body, Action2 bodyWithState, Action3 bodyWithStateAndIndex, Func4 bodyWithStateAndLocal, Func5 bodyWithEverything, Func1 localInit, Action1 localFinally) at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable1 source, Action`1 body)
Inner exception:
at System.Threading.ReaderWriterLockSlim.ExitReadLock()
at LiteDB.Engine.TransactionMonitor.ReleaseTransaction(TransactionService transaction)
at LiteDB.Engine.QueryExecutor.<>c__DisplayClass10_0.<g__RunQuery|0>d.MoveNext()
at LiteDB.BsonDataReader.Read()
at LiteDB.LiteQueryable1.<ToDocuments>d__26.MoveNext() at System.Linq.Enumerable.SelectEnumerableIterator2.MoveNext()
at System.Collections.Concurrent.Partitioner.DynamicPartitionerForIEnumerable1.InternalPartitionEnumerable.GrabChunk_Buffered(KeyValuePair2[] destArray, Int32 requestedChunkSize, Int32& actualNumElementsGrabbed)
at System.Collections.Concurrent.Partitioner.DynamicPartitionEnumerator_Abstract2.MoveNext() at System.Threading.Tasks.Parallel.<>c__DisplayClass44_02.b__1(IEnumerator& partitionState, Int32 timeout, Boolean& replicationDelegateYieldedBeforeCompletion)
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw(Exception source)
at System.Threading.Tasks.Parallel.<>c__DisplayClass44_02.<PartitionerForEachWorker>b__1(IEnumerator& partitionState, Int32 timeout, Boolean& replicationDelegateYieldedBeforeCompletion) at System.Threading.Tasks.TaskReplicator.Replica1.ExecuteAction(Boolean& yieldedBeforeCompletion)
at System.Threading.Tasks.TaskReplicator.Replica.Execute()