#region CPL License /* Nuclex Framework Copyright (C) 2002-2007 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections.Generic; using System.Text; namespace Nuclex.Support.Tracking { /// Progression being observed by another class /// /// Type of the progression that is being observed /// internal class ObservedWeightedProgression : IDisposable where ProgressionType : Progression { /// Delegate for reporting progress updates public delegate void ReportDelegate(); /// Initializes a new observed progression /// Weighted progression being observed /// /// Callback to invoke when the progression's progress changes /// /// /// Callback to invoke when the progression has ended /// internal ObservedWeightedProgression( WeightedProgression weightedProgression, ReportDelegate progressUpdateCallback, ReportDelegate endedCallback ) { this.weightedProgression = weightedProgression; if(weightedProgression.Progression.Ended) { this.progress = 1.0f; } else { this.endedCallback = endedCallback; this.progressUpdateCallback = progressUpdateCallback; this.weightedProgression.Progression.AsyncEnded += new EventHandler(asyncEnded); this.weightedProgression.Progression.AsyncProgressUpdated += new EventHandler(asyncProgressUpdated); } } /// Immediately releases all resources owned by the object public void Dispose() { asyncDisconnectEvents(); } /// Weighted progression being observed public WeightedProgression WeightedProgression { get { return this.weightedProgression; } } /// Amount of progress this progression has achieved so far public float Progress { get { return this.progress; } } /// Called when the observed progression has ended /// Progression that has ended /// Not used private void asyncEnded(object sender, EventArgs e) { ReportDelegate endedCallback = this.endedCallback; ReportDelegate progressUpdateCallback = this.progressUpdateCallback; asyncDisconnectEvents(); // We don't need those anymore! // If the progress hasn't reached 1.0 yet, make a fake report so that even // when a progression doesn't report any progress at all, the set or queue // owning us will have a percentage of progressions completed. // // There is the possibility of a race condition here, as a final progress // report could have been generated by a thread running the progression // that was preempted by this thread. This would cause the progress to // jump to 1.0 and then back to whatever the waiting thread will report. if(this.progress != 1.0f) { this.progress = 1.0f; progressUpdateCallback(); } endedCallback(); } /// Called when the progress of the observed progression changes /// Progression whose progress has changed /// Contains the updated progress private void asyncProgressUpdated(object sender, ProgressUpdateEventArgs e) { this.progress = e.Progress; this.progressUpdateCallback(); } /// Unsubscribes from all events of the observed progression private void asyncDisconnectEvents() { // Make use of the double check locking idiom to avoid the costly lock when // the events have already been unsubscribed if(this.endedCallback != null) { // This is an internal class with special knowledge that there // is no risk of deadlock involved, so we don't need a fancy syncRoot! lock(this) { if(this.endedCallback != null) { this.weightedProgression.Progression.AsyncEnded -= new EventHandler(asyncEnded); this.weightedProgression.Progression.AsyncProgressUpdated -= new EventHandler(asyncProgressUpdated); this.endedCallback = null; this.progressUpdateCallback = null; } } } // endedCallback != null } /// The weighted progression that is being observed private WeightedProgression weightedProgression; /// Callback to invoke when the progress updates private volatile ReportDelegate progressUpdateCallback; /// Callback to invoke when the progression ends private volatile ReportDelegate endedCallback; /// Progress achieved so far private volatile float progress; } } // namespace Nuclex.Support.Tracking