internal LowLevelTransaction NewLowLevelTransaction(TransactionPersistentContext transactionPersistentContext, TransactionFlags flags, ByteStringContext context = null, TimeSpan?timeout = null) { _cancellationTokenSource.Token.ThrowIfCancellationRequested(); bool txLockTaken = false; bool flushInProgressReadLockTaken = false; try { IncrementUsageOnNewTransaction(); if (flags == TransactionFlags.ReadWrite) { var wait = timeout ?? (Debugger.IsAttached ? TimeSpan.FromMinutes(30) : TimeSpan.FromSeconds(30)); if (FlushInProgressLock.IsWriteLockHeld == false) { flushInProgressReadLockTaken = FlushInProgressLock.TryEnterReadLock(wait); } txLockTaken = _transactionWriter.Wait(wait); if (txLockTaken == false || (flushInProgressReadLockTaken == false && FlushInProgressLock.IsWriteLockHeld == false)) { GlobalFlushingBehavior.GlobalFlusher.Value.MaybeFlushEnvironment(this); ThrowOnTimeoutWaitingForWriteTxLock(wait); } _cancellationTokenSource.Token.ThrowIfCancellationRequested(); _currentTransactionHolder = NativeMemory.ThreadAllocations.Value; WriteTransactionStarted(); if (_endOfDiskSpace != null) { _endOfDiskSpace.AssertCanContinueWriting(); _endOfDiskSpace = null; Task.Run(IdleFlushTimer); GlobalFlushingBehavior.GlobalFlusher.Value.MaybeFlushEnvironment(this); } } LowLevelTransaction tx; _txCommit.EnterReadLock(); try { _cancellationTokenSource.Token.ThrowIfCancellationRequested(); if (_currentTransactionHolder == null) { _currentTransactionHolder = NativeMemory.ThreadAllocations.Value; } long txId = flags == TransactionFlags.ReadWrite ? NextWriteTransactionId : CurrentReadTransactionId; tx = new LowLevelTransaction(this, txId, transactionPersistentContext, flags, _freeSpaceHandling, context) { FlushInProgressLockTaken = flushInProgressReadLockTaken, CurrentTransactionHolder = _currentTransactionHolder }; ActiveTransactions.Add(tx); } finally { _txCommit.ExitReadLock(); } var state = _dataPager.PagerState; tx.EnsurePagerStateReference(state); return(tx); } catch (Exception) { try { if (txLockTaken) { _transactionWriter.Release(); } if (flushInProgressReadLockTaken) { FlushInProgressLock.ExitReadLock(); } } finally { DecrementUsageOnTransactionCreationFailure(); } throw; } }
internal LowLevelTransaction NewLowLevelTransaction(TransactionPersistentContext transactionPersistentContext, TransactionFlags flags, ByteStringContext context = null, TimeSpan?timeout = null) { bool txLockTaken = false; bool flushInProgressReadLockTaken = false; try { if (flags == TransactionFlags.ReadWrite) { var wait = timeout ?? (Debugger.IsAttached ? TimeSpan.FromMinutes(30) : TimeSpan.FromSeconds(30)); if (FlushInProgressLock.IsWriteLockHeld == false) { flushInProgressReadLockTaken = FlushInProgressLock.TryEnterReadLock(wait); } if (Monitor.IsEntered(_txWriter)) { ThrowOnRecursiveWriteTransaction(); } Monitor.TryEnter(_txWriter, wait, ref txLockTaken); if (txLockTaken == false || (flushInProgressReadLockTaken == false && FlushInProgressLock.IsWriteLockHeld == false)) { GlobalFlushingBehavior.GlobalFlusher.Value.MaybeFlushEnvironment(this); ThrowOnTimeoutWaitingForWriteTxLock(wait); } _writeTransactionRunning.SetByAsyncCompletion(); if (_endOfDiskSpace != null) { if (_endOfDiskSpace.CanContinueWriting) { CatastrophicFailure = null; _endOfDiskSpace = null; _cancellationTokenSource = new CancellationTokenSource(); Task.Run(IdleFlushTimer); GlobalFlushingBehavior.GlobalFlusher.Value.MaybeFlushEnvironment(this); } } } LowLevelTransaction tx; _txCommit.EnterReadLock(); try { long txId = flags == TransactionFlags.ReadWrite ? _transactionsCounter + 1 : _transactionsCounter; tx = new LowLevelTransaction(this, txId, transactionPersistentContext, flags, _freeSpaceHandling, context) { FlushInProgressLockTaken = flushInProgressReadLockTaken }; ActiveTransactions.Add(tx); } finally { _txCommit.ExitReadLock(); } var state = _dataPager.PagerState; tx.EnsurePagerStateReference(state); return(tx); } catch (Exception) { if (txLockTaken) { Monitor.Exit(_txWriter); } if (flushInProgressReadLockTaken) { FlushInProgressLock.ExitReadLock(); } throw; } }