/// <summary>Estimate child ops</summary> /// <exception cref="Exception">On error</exception> protected override void InnerEstimate() { StructList2 <Exception> errors = new StructList2 <Exception>(); foreach (IOperation op in Ops) { try { // Assert session is not cancelled if (session.CancelSrc.IsCancellationRequested) { SetState(OperationState.Cancelled); return; } op.Estimate(); if (op.TotalLength > 0L) { this.TotalLength += op.TotalLength; } if (op.Progress > 0L) { this.Progress += op.Progress; } this.CanRollback &= op.CanRollback | op.EffectivePolicy.HasFlag(OperationPolicy.EstimateOnRun); } catch (Exception e) when(session.Policy.HasFlag(OperationPolicy.BatchContinueOnError)) { errors.Add(e); } } // Throw captured exceptions if (errors.Count > 0) { throw new AggregateException(errors.ToArray()); } }
/// <summary> /// Dispose enumerable and capture errors /// </summary> /// <param name="disposableObjects">list of disposables</param> /// <param name="disposeErrors">list to be created if errors occur</param> public static void DisposeAndCapture(ref StructList2 <IDisposable> disposableObjects, ref StructList4 <Exception> disposeErrors) { // Dispose disposables for (int i = 0; i < disposableObjects.Count; i++) { IDisposable disposable = disposableObjects[i]; if (disposable != null) { try { disposable.Dispose(); } catch (AggregateException ae) { foreach (Exception e in ae.InnerExceptions) { disposeErrors.Add(e); } } catch (Exception e) { // Capture error disposeErrors.Add(e); } } } }
/// <summary> /// Process the non-dispose. Used when <see cref="nonDisposable"/> is true (singleton instances). /// /// This may be called from <see cref="Dispose"/> or from the dispose of the last /// belate handle (After <see cref="Dispose"/> has been called aswell). /// /// Only one thread may process the dispose. Returns state back to 0. /// /// Unattaches all disposables, disposes them, and calls <see cref="InnerDispose(ref StructList4{Exception})"/>. /// Does not set state /// </summary> /// <exception cref="AggregateException">thrown if disposing threw errors</exception> protected virtual void ProcessNonDispose() { // Revert state Interlocked.CompareExchange(ref disposing, 0L, 1L); // Extract snapshot, clear array StructList2 <IDisposable> toDispose = default; lock (m_disposelist_lock) { toDispose = disposeList; disposeList = default; } // Captured errors StructList4 <Exception> disposeErrors = new StructList4 <Exception>(); // Dispose disposables DisposeList.DisposeAndCapture(ref toDispose, ref disposeErrors); // Call InnerDispose(). Capture errors to compose it with others. try { InnerDispose(ref disposeErrors); } catch (Exception e) { // Capture error disposeErrors.Add(e); } // Throw captured errors if (disposeErrors.Count > 0) { throw new AggregateException(disposeErrors); } }
/// <summary> /// Process the actual dispose. This may be called from Dispose() or from the dispose of the last /// belate handle (After Dispose() has been called aswell). /// /// Disposes all attached diposables and call <see cref="InnerDispose(ref StructList4{Exception})"/>. /// /// Only one thread may process the dispose. /// Sets state to 2, and then 3. /// /// Unattaches all disposables, disposes them, and calls <see cref="InnerDispose(ref StructList4{Exception})"/>. /// </summary> /// <exception cref="AggregateException">thrown if disposing threw errors</exception> protected virtual void ProcessDispose() { // Set state IsDisposing=2, but let only one thread continue. bool thisThreadChangedStateToDispose = (Interlocked.CompareExchange(ref disposing, 2L, 0L) == 0L) || (Interlocked.CompareExchange(ref disposing, 2L, 1L) == 1L); // Not for this thread. if (!thisThreadChangedStateToDispose) { return; } // Extract snapshot, clear array StructList2 <IDisposable> toDispose = default; lock (m_disposelist_lock) { toDispose = disposeList; disposeList = default; } // Captured errors StructList4 <Exception> disposeErrors = new StructList4 <Exception>(); // Dispose disposables DisposeAndCapture(ref toDispose, ref disposeErrors); // Call InnerDispose(). Capture errors to compose it with others. try { InnerDispose(ref disposeErrors); } catch (Exception e) { // Capture error disposeErrors.Add(e); } // Call InnerDisposeUnmanaged(). Capture errors to compose it with others. try { InnerDisposeUnmanaged(ref disposeErrors); } catch (Exception e) { // Capture error disposeErrors.Add(e); } // Is disposed Interlocked.CompareExchange(ref disposing, 3L, 2L); // Throw captured errors if (disposeErrors.Count > 0) { throw new AggregateException(disposeErrors); } }
/// <summary> /// Create a token that combins a pair of tokens. /// Removes null values. /// </summary> /// <param name="token1"></param> /// <param name="token2"></param> public TokenList(IToken token1, IToken token2) { StructList2 <IToken> list = new StructList2 <IToken>(); if (token1 != null) { list.Add(token1); } if (token2 != null) { list.Add(token2); } this.tokens = list.ToArray(); }
/// <summary>Run child ops</summary> protected override void InnerRun() { StructList2 <Exception> errors = new StructList2 <Exception>(); long progressReminder = this.Progress; foreach (OperationBase op in Ops) { if (op.CurrentState == OperationState.Completed) { continue; } if (op.CurrentState == OperationState.Skipped) { continue; } try { // Assert session is not cancelled if (session.CancelSrc.IsCancellationRequested) { SetState(OperationState.Cancelled); return; } // Run op op.Run(); // if (!EffectivePolicy.HasFlag(OperationPolicy.BatchContinueOnError) && op.CurrentState == OperationState.Error) { throw new AggregateException(op.Errors); } // Move progress if (op.Progress > 0L) { this.Progress += op.Progress; } if (op.TotalLength > 0L) { this.TotalLength += op.TotalLength; } if (op.Progress > 0L || op.TotalLength > 0L) { // Update progress position progressReminder += op.Progress; // Time to send progress event if (session.ProgressInterval > 0L && progressReminder > session.ProgressInterval && session.HasObservers) { progressReminder %= session.ProgressInterval; session.DispatchEvent(new OperationProgressEvent(this, Progress, TotalLength)); } } } catch (Exception e) when(session.Policy.HasFlag(OperationPolicy.BatchContinueOnError)) { errors.Add(e); } } // Throw captured exceptions if (errors.Count > 0) { throw new AggregateException(errors.ToArray()); } }