/// <summary> /// Parallel task that checks if the transaction has been aborted internally /// If so, cause the waiting dequeuer to throw InvalidOperationException /// </summary> private async Task PollTransactionStateAsync( Transaction txn, TaskCompletionSource <ConditionalValue <IListElement <T> > > tcs, CancellationToken cancellationToken) { while (true) { // Ignore the exception thrown by this call // As this polls every 4 secs, The cancellationTokenSource could have been disposed and set to null // causing Task.Delay to throw ObjectDisposedException. // The token would be honoured by the StartTimerAsync task and the tcs would be set to the TaskCanceledException. await Task.Delay(this.DefaultTransactionStatePollingTimeout, cancellationToken).ConfigureAwait(false); if (tcs.Task.IsCompleted) { // If the tcs was already set, no need to wait check the transaction state. // End transaction polling task return; } switch (txn.State) { case TransactionState.Active: case TransactionState.Reading: // Do nothing, the transaction is still active, go back to sleep break; case TransactionState.Aborted: case TransactionState.Aborting: case TransactionState.Faulted: tcs.TrySetException( new InvalidOperationException( string.Format("DataStore.PollTransactionStateAsync : The transaction was aborted or faulted, dispose the transaction and try again, Txn : {0}", txn))); return; case TransactionState.Committed: case TransactionState.Committing: var exc = new InvalidOperationException( string.Format( "DataStore.PollTransactionStateAsync : The transaction was commited but DequeueAsync did not finish. Make sure DequeueAsync was awaited. Txn : {0}", txn)); if (tcs.TrySetException(exc)) { // If we were able to set the result, then DequeueAsync was not awaited. // Do not Assert as this is something that the user is doing incorrectly. // Do not throw as the exception would be thrown by the tcs. FabricEvents.Events.ReliableConcurrentQueue_ExceptionWarning(this.traceType, exc.ToString()); } return; default: // Unknown transaction state : Assert TestableAssertHelper.FailInvalidOperation(this.traceType, "DataStore.PollTransactionStateAsync", "Unknown transaction state"); return; } } }
/// <summary> /// The prepare for remove async. /// </summary> /// <param name="transaction"> /// The transaction. /// </param> /// <param name="timeout"> /// The timeout. /// </param> /// <param name="cancellationToken"> /// The cancellation token. /// </param> /// <returns> /// The <see cref="Task"/>. /// </returns> Task IStateProvider2.PrepareForRemoveAsync( Transaction transaction, TimeSpan timeout, CancellationToken cancellationToken) { Trace.TraceInformation("[" + this.partitionId + "] " + "PrepareForRemoveAsync()"); return(Task.FromResult(0)); }
/// <summary> /// Try and dequeue from the DataStore. /// If there are dequeuers waiting or there is no element to dequeue, this dequeueur would be added to the waiting queue. /// Else, the result would be set for the <paramref name="tcs"/> /// Caller must await on tcs.Task /// </summary> public void TryDequeue( Transaction txn, TaskCompletionSource <ConditionalValue <IListElement <T> > > tcs, TimeSpan timeout, CancellationToken cancellationToken) { lock (this.updateLatch) { switch (this.dequeuersState) { case DequeuerState.Aborting: case DequeuerState.Closing: tcs.TrySetException( new FabricObjectClosedException( string.Format("Queue is closed. Please dispose the current transaction and retry the operation with a new transaction, Cause : {0}", this.dequeuersState))); return; case DequeuerState.None: case DequeuerState.ChangeRoleFromPrimary: tcs.TrySetException( new FabricNotPrimaryException( string.Format("Current role not Primary. Please dispose the current transaction and retry the operation with a new transaction, Cause : {0}", this.dequeuersState))); return; case DequeuerState.OnPrimary: // In the expected state, continue DataStore.TryDequeue break; default: TestableAssertHelper.FailInvalidOperation( this.traceType, "TryDequeue", "DataStore.TryDequeue : Trying to add a dequeuer in an invalid state. Current DequeuerState = {0}", this.dequeuersState); break; // unreachable } var res = this.TryUnlinkHead(); if (res.HasValue) { // There is something to unlink and no waiting dequeuer if (!tcs.TrySetResult(res)) { TestableAssertHelper.FailInvalidOperation( this.traceType, "TryDequeue", "Could not set the tcs result after unlinking from the DataStore. TCS {0}", res); } } else { this.AddDequeuer(txn, tcs, timeout, cancellationToken); } } }
/// <summary> /// Start a timer and add the <paramref name="tcs"/> to the dequeuerQueue /// Should be called under the updateLatch /// </summary> private void AddDequeuer( Transaction txn, TaskCompletionSource <ConditionalValue <IListElement <T> > > tcs, TimeSpan timeSpan, CancellationToken cancellationToken) { this.dequeuerQueue.Enqueue(tcs); // Fire the timer task var t1 = this.StartDequeueTimerAsync(tcs, timeSpan, cancellationToken); var t2 = this.PollTransactionStateAsync(txn, tcs, cancellationToken).IgnoreException(); }