Skip to content

[BUG] The read lock is being released without being held #2017

@laurentiucocanu

Description

@laurentiucocanu

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()

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions