Renamed ProgressionTracker to ProgressTracker; renamed most classes with 'Progression' in their name to use 'Waitable' instead; some minor documentation fixes
git-svn-id: file:///srv/devel/repo-conversion/nusu@65 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
7dbfc3c422
commit
432ab888b9
|
@ -145,20 +145,20 @@
|
||||||
<Compile Include="Source\SpatialPartitioning\RTreeNode2.cs" />
|
<Compile Include="Source\SpatialPartitioning\RTreeNode2.cs" />
|
||||||
<Compile Include="Source\SpatialPartitioning\SpatialIndex2.cs" />
|
<Compile Include="Source\SpatialPartitioning\SpatialIndex2.cs" />
|
||||||
<Compile Include="Source\Tracking\IdleStateEventArgs.cs" />
|
<Compile Include="Source\Tracking\IdleStateEventArgs.cs" />
|
||||||
<Compile Include="Source\Tracking\Internal\ObservedWeightedProgression.cs" />
|
<Compile Include="Source\Tracking\Internal\ObservedWeightedWaitable.cs" />
|
||||||
<Compile Include="Source\Tracking\Internal\WeightedProgressionWrapperCollection.cs" />
|
<Compile Include="Source\Tracking\Internal\WeightedWaitableWrapperCollection.cs" />
|
||||||
<Compile Include="Source\Tracking\IProgressReporter.cs" />
|
<Compile Include="Source\Tracking\IProgressReporter.cs" />
|
||||||
<Compile Include="Source\Tracking\IStatusReporter.cs" />
|
<Compile Include="Source\Tracking\IStatusReporter.cs" />
|
||||||
<Compile Include="Source\Tracking\Request.cs" />
|
<Compile Include="Source\Tracking\Request.cs" />
|
||||||
<Compile Include="Source\Tracking\Waitable.cs" />
|
<Compile Include="Source\Tracking\Waitable.cs" />
|
||||||
<Compile Include="Source\Tracking\ProgressionTracker.cs" />
|
<Compile Include="Source\Tracking\ProgressTracker.cs" />
|
||||||
<Compile Include="Source\Tracking\ProgressionTracker.Test.cs">
|
<Compile Include="Source\Tracking\ProgressTracker.Test.cs">
|
||||||
<DependentUpon>ProgressionTracker.cs</DependentUpon>
|
<DependentUpon>ProgressTracker.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Tracking\ProgressUpdateEventArgs.cs" />
|
<Compile Include="Source\Tracking\ProgressUpdateEventArgs.cs" />
|
||||||
<Compile Include="Source\Tracking\SetProgression.cs" />
|
<Compile Include="Source\Tracking\SetProgression.cs" />
|
||||||
<Compile Include="Source\Tracking\SetProgression.Test.cs">
|
<Compile Include="Source\Tracking\SetProgression.Test.cs">
|
||||||
<DependentUpon>ProgressionTracker.cs</DependentUpon>
|
<DependentUpon>SetProgression.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Source\Tracking\StatusReportEventArgs.cs" />
|
<Compile Include="Source\Tracking\StatusReportEventArgs.cs" />
|
||||||
<Compile Include="Source\Tracking\WeightedProgression.cs" />
|
<Compile Include="Source\Tracking\WeightedProgression.cs" />
|
||||||
|
|
|
@ -45,7 +45,7 @@ namespace Nuclex.Support.Scheduling {
|
||||||
// Construct a WeightedProgression with the default weight for each
|
// Construct a WeightedProgression with the default weight for each
|
||||||
// progression and wrap it in an ObservedProgression
|
// progression and wrap it in an ObservedProgression
|
||||||
foreach(OperationType operation in childs)
|
foreach(OperationType operation in childs)
|
||||||
this.children.Add(new WeightedProgression<OperationType>(operation));
|
this.children.Add(new WeightedWaitable<OperationType>(operation));
|
||||||
|
|
||||||
// Since all progressions have a weight of 1.0, the total weight is
|
// Since all progressions have a weight of 1.0, the total weight is
|
||||||
// equal to the number of progressions in our list
|
// equal to the number of progressions in our list
|
||||||
|
@ -55,10 +55,10 @@ namespace Nuclex.Support.Scheduling {
|
||||||
|
|
||||||
/// <summary>Initializes a new queue operation with custom weights</summary>
|
/// <summary>Initializes a new queue operation with custom weights</summary>
|
||||||
/// <param name="childs">Child operations to execute in this operation</param>
|
/// <param name="childs">Child operations to execute in this operation</param>
|
||||||
public QueueOperation(IEnumerable<WeightedProgression<OperationType>> childs) : this() {
|
public QueueOperation(IEnumerable<WeightedWaitable<OperationType>> childs) : this() {
|
||||||
|
|
||||||
// Construct an ObservedProgression around each of the WeightedProgressions
|
// Construct an ObservedProgression around each of the WeightedProgressions
|
||||||
foreach(WeightedProgression<OperationType> operation in childs) {
|
foreach(WeightedWaitable<OperationType> operation in childs) {
|
||||||
this.children.Add(operation);
|
this.children.Add(operation);
|
||||||
|
|
||||||
// Sum up the total weight
|
// Sum up the total weight
|
||||||
|
@ -74,11 +74,11 @@ namespace Nuclex.Support.Scheduling {
|
||||||
asyncOperationProgressChanged
|
asyncOperationProgressChanged
|
||||||
);
|
);
|
||||||
|
|
||||||
this.children = new List<WeightedProgression<OperationType>>();
|
this.children = new List<WeightedWaitable<OperationType>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Provides access to the child operations of this queue</summary>
|
/// <summary>Provides access to the child operations of this queue</summary>
|
||||||
public IList<WeightedProgression<OperationType>> Children {
|
public IList<WeightedWaitable<OperationType>> Children {
|
||||||
get { return this.children; }
|
get { return this.children; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ namespace Nuclex.Support.Scheduling {
|
||||||
/// and launches the operation by calling its Begin() method.
|
/// and launches the operation by calling its Begin() method.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private void startCurrentOperation() {
|
private void startCurrentOperation() {
|
||||||
OperationType operation = this.children[this.currentOperationIndex].Progression;
|
OperationType operation = this.children[this.currentOperationIndex].Waitable;
|
||||||
|
|
||||||
operation.AsyncEnded += this.asyncOperationEndedDelegate;
|
operation.AsyncEnded += this.asyncOperationEndedDelegate;
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ namespace Nuclex.Support.Scheduling {
|
||||||
/// counts up the accumulated progress of the queue.
|
/// counts up the accumulated progress of the queue.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private void endCurrentOperation() {
|
private void endCurrentOperation() {
|
||||||
OperationType operation = this.children[this.currentOperationIndex].Progression;
|
OperationType operation = this.children[this.currentOperationIndex].Waitable;
|
||||||
|
|
||||||
// Disconnect from the operation's events
|
// Disconnect from the operation's events
|
||||||
operation.AsyncEnded -= this.asyncOperationEndedDelegate;
|
operation.AsyncEnded -= this.asyncOperationEndedDelegate;
|
||||||
|
@ -217,7 +217,7 @@ namespace Nuclex.Support.Scheduling {
|
||||||
/// <summary>Delegate to the asyncOperationProgressUpdated() method</summary>
|
/// <summary>Delegate to the asyncOperationProgressUpdated() method</summary>
|
||||||
private EventHandler<ProgressReportEventArgs> asyncOperationProgressChangedDelegate;
|
private EventHandler<ProgressReportEventArgs> asyncOperationProgressChangedDelegate;
|
||||||
/// <summary>Operations being managed in the queue</summary>
|
/// <summary>Operations being managed in the queue</summary>
|
||||||
private List<WeightedProgression<OperationType>> children;
|
private List<WeightedWaitable<OperationType>> children;
|
||||||
/// <summary>Summed weight of all operations in the queue</summary>
|
/// <summary>Summed weight of all operations in the queue</summary>
|
||||||
private float totalWeight;
|
private float totalWeight;
|
||||||
/// <summary>Accumulated weight of the operations already completed</summary>
|
/// <summary>Accumulated weight of the operations already completed</summary>
|
||||||
|
|
|
@ -27,7 +27,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <typeparam name="ProgressionType">
|
/// <typeparam name="ProgressionType">
|
||||||
/// Type of the progression that is being observed
|
/// Type of the progression that is being observed
|
||||||
/// </typeparam>
|
/// </typeparam>
|
||||||
internal class ObservedWeightedProgression<ProgressionType> : IDisposable
|
internal class ObservedWeightedWaitable<ProgressionType> : IDisposable
|
||||||
where ProgressionType : Waitable {
|
where ProgressionType : Waitable {
|
||||||
|
|
||||||
/// <summary>Delegate for reporting progress updates</summary>
|
/// <summary>Delegate for reporting progress updates</summary>
|
||||||
|
@ -41,15 +41,15 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <param name="endedCallback">
|
/// <param name="endedCallback">
|
||||||
/// Callback to invoke when the progression has ended
|
/// Callback to invoke when the progression has ended
|
||||||
/// </param>
|
/// </param>
|
||||||
internal ObservedWeightedProgression(
|
internal ObservedWeightedWaitable(
|
||||||
WeightedProgression<ProgressionType> weightedProgression,
|
WeightedWaitable<ProgressionType> weightedProgression,
|
||||||
ReportDelegate progressUpdateCallback,
|
ReportDelegate progressUpdateCallback,
|
||||||
ReportDelegate endedCallback
|
ReportDelegate endedCallback
|
||||||
) {
|
) {
|
||||||
this.weightedProgression = weightedProgression;
|
this.weightedProgression = weightedProgression;
|
||||||
|
|
||||||
// See if this progression has already ended (initial check for performance)
|
// See if this progression has already ended (initial check for performance)
|
||||||
if(weightedProgression.Progression.Ended) {
|
if(weightedProgression.Waitable.Ended) {
|
||||||
|
|
||||||
this.progress = 1.0f;
|
this.progress = 1.0f;
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
this.endedCallback = endedCallback;
|
this.endedCallback = endedCallback;
|
||||||
this.progressUpdateCallback = progressUpdateCallback;
|
this.progressUpdateCallback = progressUpdateCallback;
|
||||||
|
|
||||||
this.weightedProgression.Progression.AsyncEnded +=
|
this.weightedProgression.Waitable.AsyncEnded +=
|
||||||
new EventHandler(asyncEnded);
|
new EventHandler(asyncEnded);
|
||||||
|
|
||||||
// Check whether this progression might have ended before we were able to
|
// Check whether this progression might have ended before we were able to
|
||||||
|
@ -66,10 +66,10 @@ namespace Nuclex.Support.Tracking {
|
||||||
// other event and (important) set our progress to 1.0 because, since we
|
// other event and (important) set our progress to 1.0 because, since we
|
||||||
// might not have gotten the 'Ended' event, it might otherwise stay at 0.0
|
// might not have gotten the 'Ended' event, it might otherwise stay at 0.0
|
||||||
// even though the progression is in the 'Ended' state.
|
// even though the progression is in the 'Ended' state.
|
||||||
if(weightedProgression.Progression.Ended) {
|
if(weightedProgression.Waitable.Ended) {
|
||||||
this.progress = 1.0f;
|
this.progress = 1.0f;
|
||||||
} else {
|
} else {
|
||||||
this.progressReporter = this.weightedProgression.Progression as IProgressReporter;
|
this.progressReporter = this.weightedProgression.Waitable as IProgressReporter;
|
||||||
|
|
||||||
if(this.progressReporter != null) {
|
if(this.progressReporter != null) {
|
||||||
this.asyncProgressChangedEventHandler = new EventHandler<ProgressReportEventArgs>(
|
this.asyncProgressChangedEventHandler = new EventHandler<ProgressReportEventArgs>(
|
||||||
|
@ -90,7 +90,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Weighted progression being observed</summary>
|
/// <summary>Weighted progression being observed</summary>
|
||||||
public WeightedProgression<ProgressionType> WeightedProgression {
|
public WeightedWaitable<ProgressionType> WeightedWaitable {
|
||||||
get { return this.weightedProgression; }
|
get { return this.weightedProgression; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
// is no risk of deadlock involved, so we don't need a fancy syncRoot!
|
// is no risk of deadlock involved, so we don't need a fancy syncRoot!
|
||||||
lock(this) {
|
lock(this) {
|
||||||
if(this.endedCallback != null) {
|
if(this.endedCallback != null) {
|
||||||
this.weightedProgression.Progression.AsyncEnded -=
|
this.weightedProgression.Waitable.AsyncEnded -=
|
||||||
new EventHandler(asyncEnded);
|
new EventHandler(asyncEnded);
|
||||||
|
|
||||||
if(this.progressReporter != null) {
|
if(this.progressReporter != null) {
|
||||||
|
@ -166,7 +166,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <summary>The observed progression's progress reporting interface</summary>
|
/// <summary>The observed progression's progress reporting interface</summary>
|
||||||
private IProgressReporter progressReporter;
|
private IProgressReporter progressReporter;
|
||||||
/// <summary>The weighted progression that is being observed</summary>
|
/// <summary>The weighted progression that is being observed</summary>
|
||||||
private WeightedProgression<ProgressionType> weightedProgression;
|
private WeightedWaitable<ProgressionType> weightedProgression;
|
||||||
/// <summary>Callback to invoke when the progress updates</summary>
|
/// <summary>Callback to invoke when the progress updates</summary>
|
||||||
private volatile ReportDelegate progressUpdateCallback;
|
private volatile ReportDelegate progressUpdateCallback;
|
||||||
/// <summary>Callback to invoke when the progression ends</summary>
|
/// <summary>Callback to invoke when the progression ends</summary>
|
|
@ -49,14 +49,14 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
internal class WeightedProgressionWrapperCollection<ProgressionType> :
|
internal class WeightedProgressionWrapperCollection<ProgressionType> :
|
||||||
TransformingReadOnlyCollection<
|
TransformingReadOnlyCollection<
|
||||||
ObservedWeightedProgression<ProgressionType>, WeightedProgression<ProgressionType>
|
ObservedWeightedWaitable<ProgressionType>, WeightedWaitable<ProgressionType>
|
||||||
>
|
>
|
||||||
where ProgressionType : Waitable {
|
where ProgressionType : Waitable {
|
||||||
|
|
||||||
/// <summary>Initializes a new weighted progression collection wrapper</summary>
|
/// <summary>Initializes a new weighted progression collection wrapper</summary>
|
||||||
/// <param name="items">Items to be exposed as weighted progressions</param>
|
/// <param name="items">Items to be exposed as weighted progressions</param>
|
||||||
internal WeightedProgressionWrapperCollection(
|
internal WeightedProgressionWrapperCollection(
|
||||||
IList<ObservedWeightedProgression<ProgressionType>> items
|
IList<ObservedWeightedWaitable<ProgressionType>> items
|
||||||
)
|
)
|
||||||
: base(items) { }
|
: base(items) { }
|
||||||
|
|
||||||
|
@ -69,10 +69,10 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// be called frequently, because the TransformingReadOnlyCollection does
|
/// be called frequently, because the TransformingReadOnlyCollection does
|
||||||
/// not cache otherwise store the transformed items.
|
/// not cache otherwise store the transformed items.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected override WeightedProgression<ProgressionType> Transform(
|
protected override WeightedWaitable<ProgressionType> Transform(
|
||||||
ObservedWeightedProgression<ProgressionType> item
|
ObservedWeightedWaitable<ProgressionType> item
|
||||||
) {
|
) {
|
||||||
return item.WeightedProgression;
|
return item.WeightedWaitable;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -148,7 +148,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <summary>Validates that the tracker properly sums the progress</summary>
|
/// <summary>Validates that the tracker properly sums the progress</summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSummedProgress() {
|
public void TestSummedProgress() {
|
||||||
ProgressionTracker tracker = new ProgressionTracker();
|
ProgressTracker tracker = new ProgressTracker();
|
||||||
|
|
||||||
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -174,7 +174,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -194,7 +194,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDelayedRemoval() {
|
public void TestDelayedRemoval() {
|
||||||
ProgressionTracker tracker = new ProgressionTracker();
|
ProgressTracker tracker = new ProgressTracker();
|
||||||
|
|
||||||
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -220,7 +220,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -231,7 +231,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.75f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.75f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -245,7 +245,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(1.0f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(1.0f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -265,7 +265,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSoleEndedProgression() {
|
public void TestSoleEndedProgression() {
|
||||||
ProgressionTracker tracker = new ProgressionTracker();
|
ProgressTracker tracker = new ProgressTracker();
|
||||||
|
|
||||||
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||||
|
|
||||||
|
@ -280,7 +280,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestEndedProgression() {
|
public void TestEndedProgression() {
|
||||||
ProgressionTracker tracker = new ProgressionTracker();
|
ProgressTracker tracker = new ProgressTracker();
|
||||||
|
|
||||||
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.0f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -304,7 +304,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -315,7 +315,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("ProgressUpdated").
|
Method("ProgressUpdated").
|
||||||
With(
|
With(
|
||||||
new Matcher[] {
|
new Matcher[] {
|
||||||
new NMock2.Matchers.TypeMatcher(typeof(ProgressionTracker)),
|
new NMock2.Matchers.TypeMatcher(typeof(ProgressTracker)),
|
||||||
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(1.0f))
|
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(1.0f))
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -335,7 +335,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Test]
|
[Test]
|
||||||
public void TestProvokedDeadlock() {
|
public void TestProvokedDeadlock() {
|
||||||
ProgressionTracker tracker = new ProgressionTracker();
|
ProgressTracker tracker = new ProgressTracker();
|
||||||
|
|
||||||
TestProgression test1 = new TestProgression();
|
TestProgression test1 = new TestProgression();
|
||||||
tracker.Track(test1);
|
tracker.Track(test1);
|
||||||
|
@ -351,14 +351,14 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <summary>Mocks a subscriber for the events of a tracker</summary>
|
/// <summary>Mocks a subscriber for the events of a tracker</summary>
|
||||||
/// <param name="tracker">Tracker to mock an event subscriber for</param>
|
/// <param name="tracker">Tracker to mock an event subscriber for</param>
|
||||||
/// <returns>The mocked event subscriber</returns>
|
/// <returns>The mocked event subscriber</returns>
|
||||||
private IProgressionTrackerSubscriber mockSubscriber(ProgressionTracker tracker) {
|
private IProgressionTrackerSubscriber mockSubscriber(ProgressTracker tracker) {
|
||||||
IProgressionTrackerSubscriber mockedSubscriber =
|
IProgressionTrackerSubscriber mockedSubscriber =
|
||||||
this.mockery.NewMock<IProgressionTrackerSubscriber>();
|
this.mockery.NewMock<IProgressionTrackerSubscriber>();
|
||||||
|
|
||||||
tracker.AsyncIdleStateChanged +=
|
tracker.AsyncIdleStateChanged +=
|
||||||
new EventHandler<IdleStateEventArgs>(mockedSubscriber.IdleStateChanged);
|
new EventHandler<IdleStateEventArgs>(mockedSubscriber.IdleStateChanged);
|
||||||
|
|
||||||
tracker.AsyncProgressUpdated +=
|
tracker.AsyncProgressChanged +=
|
||||||
new EventHandler<ProgressReportEventArgs>(mockedSubscriber.ProgressUpdated);
|
new EventHandler<ProgressReportEventArgs>(mockedSubscriber.ProgressUpdated);
|
||||||
|
|
||||||
return mockedSubscriber;
|
return mockedSubscriber;
|
364
Source/Tracking/ProgressTracker.cs
Normal file
364
Source/Tracking/ProgressTracker.cs
Normal file
|
@ -0,0 +1,364 @@
|
||||||
|
#region CPL License
|
||||||
|
/*
|
||||||
|
Nuclex Framework
|
||||||
|
Copyright (C) 2002-2008 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.Threading;
|
||||||
|
|
||||||
|
namespace Nuclex.Support.Tracking {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helps tracking the progress of one or more waitable background processes
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// This is useful if you want to display a progress bar for multiple
|
||||||
|
/// Waitables but can not guarantee that no additional Waitables
|
||||||
|
/// will appear inmidst of execution.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// This class does not implement the <see cref="Waitable" /> interface itself
|
||||||
|
/// in order to not violate the design principles of Waitables which
|
||||||
|
/// guarantee that a <see cref="Waitable" /> will only finish once (whereas the
|
||||||
|
/// progress tracker might 'finish' any number of times).
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
public class ProgressTracker : IDisposable, IProgressReporter {
|
||||||
|
|
||||||
|
#region class WaitableMatcher
|
||||||
|
|
||||||
|
/// <summary>Matches a direct Waitable to a fully wrapped one</summary>
|
||||||
|
private class WaitableMatcher {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new Waitable matcher that matches against
|
||||||
|
/// the specified Waitable
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="toMatch">Waitable to match against</param>
|
||||||
|
public WaitableMatcher(Waitable toMatch) {
|
||||||
|
this.toMatch = toMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether the provided Waitable matches the comparison
|
||||||
|
/// Waitable of the instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="other">Waitable to match to the comparison Waitable</param>
|
||||||
|
public bool Matches(ObservedWeightedWaitable<Waitable> other) {
|
||||||
|
return ReferenceEquals(other.WeightedWaitable.Waitable, this.toMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Waitable this instance compares against</summary>
|
||||||
|
private Waitable toMatch;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion // class WaitableMatcher
|
||||||
|
|
||||||
|
/// <summary>Triggered when the idle state of the tracker changes</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The tracker is idle when no Waitables are being tracked in it. If you're
|
||||||
|
/// using this class to feed a progress bar, this would be the event to use for
|
||||||
|
/// showing or hiding the progress bar. The tracker starts off as idle because,
|
||||||
|
/// upon construction, its list of Waitables will be empty.
|
||||||
|
/// </remarks>
|
||||||
|
public event EventHandler<IdleStateEventArgs> AsyncIdleStateChanged;
|
||||||
|
|
||||||
|
/// <summary>Triggered when the total progress has changed</summary>
|
||||||
|
public event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
|
||||||
|
|
||||||
|
/// <summary>Initializes a new Waitable tracker</summary>
|
||||||
|
public ProgressTracker() {
|
||||||
|
|
||||||
|
this.trackedWaitables = new List<ObservedWeightedWaitable<Waitable>>();
|
||||||
|
this.idle = true;
|
||||||
|
|
||||||
|
this.asyncEndedDelegate =
|
||||||
|
new ObservedWeightedWaitable<Waitable>.ReportDelegate(asyncEnded);
|
||||||
|
this.asyncProgressUpdatedDelegate =
|
||||||
|
new ObservedWeightedWaitable<Waitable>.ReportDelegate(asyncProgressChanged);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Immediately releases all resources owned by the instance</summary>
|
||||||
|
public void Dispose() {
|
||||||
|
lock(this.trackedWaitables) {
|
||||||
|
|
||||||
|
// Get rid of all Waitables we're tracking. This unsubscribes the
|
||||||
|
// observers from the events of the Waitables and stops us from
|
||||||
|
// being kept alive and receiving any further events if some of the
|
||||||
|
// tracked Waitables are still executing.
|
||||||
|
for(int index = 0; index < this.trackedWaitables.Count; ++index)
|
||||||
|
this.trackedWaitables[index].Dispose();
|
||||||
|
|
||||||
|
// Help the GC a bit by untangling the references :)
|
||||||
|
this.trackedWaitables.Clear();
|
||||||
|
this.trackedWaitables = null;
|
||||||
|
|
||||||
|
} // lock
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Begins tracking the specified waitable background process</summary>
|
||||||
|
/// <param name="waitable">Waitable background process to be tracked</param>
|
||||||
|
public void Track(Waitable waitable) {
|
||||||
|
Track(waitable, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Begins tracking the specified waitable background process</summary>
|
||||||
|
/// <param name="waitable">Waitable background process to be tracked</param>
|
||||||
|
/// <param name="weight">Weight to assign to this waitable background process</param>
|
||||||
|
public void Track(Waitable waitable, float weight) {
|
||||||
|
|
||||||
|
// Add the new Waitable into the tracking list. This has to be done
|
||||||
|
// inside a lock to prevent issues with the progressUpdate callback, which could
|
||||||
|
// access the totalWeight field before it has been updated to reflect the
|
||||||
|
// new Waitable added to the collection.
|
||||||
|
lock(this.trackedWaitables) {
|
||||||
|
|
||||||
|
bool wasEmpty = (this.trackedWaitables.Count == 0);
|
||||||
|
|
||||||
|
// This can be done after we registered the wrapper to our delegates because
|
||||||
|
// any incoming progress updates will be stopped from the danger of a
|
||||||
|
// division-by-zero from the potentially still zeroed totalWeight by the lock.
|
||||||
|
this.totalWeight += weight;
|
||||||
|
|
||||||
|
if(waitable.Ended) {
|
||||||
|
|
||||||
|
// If the ended Waitable would become the only Waitable in the list,
|
||||||
|
// there's no sense in doing anything at all because it would have to be
|
||||||
|
// thrown right out again. Only add the Waitable when there are other
|
||||||
|
// running Waitables to properly sum total progress for consistency.
|
||||||
|
if(!wasEmpty) {
|
||||||
|
|
||||||
|
// Construct a new observation wrapper. This is done inside the lock
|
||||||
|
// because as soon as we are subscribed to the events, we can potentially
|
||||||
|
// receive them. The lock eliminates the risk of processing a progress update
|
||||||
|
// before the Waitable has been added to the tracked Waitables list.
|
||||||
|
this.trackedWaitables.Add(
|
||||||
|
new ObservedWeightedWaitable<Waitable>(
|
||||||
|
new WeightedWaitable<Waitable>(waitable, weight),
|
||||||
|
this.asyncProgressUpdatedDelegate,
|
||||||
|
this.asyncEndedDelegate
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// All done, the total progress is different now, so force a recalculation and
|
||||||
|
// send out the AsyncProgressUpdated event.
|
||||||
|
recalculateProgress();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { // Not ended -- Waitable is still running
|
||||||
|
|
||||||
|
// Construct a new Waitable observer and add the Waitable to our
|
||||||
|
// list of tracked Waitables.
|
||||||
|
ObservedWeightedWaitable<Waitable> observedWaitable =
|
||||||
|
new ObservedWeightedWaitable<Waitable>(
|
||||||
|
new WeightedWaitable<Waitable>(waitable, weight),
|
||||||
|
this.asyncProgressUpdatedDelegate,
|
||||||
|
this.asyncEndedDelegate
|
||||||
|
);
|
||||||
|
|
||||||
|
this.trackedWaitables.Add(observedWaitable);
|
||||||
|
|
||||||
|
// If this is the first Waitable to be added to the list, tell our
|
||||||
|
// owner that we're idle no longer!
|
||||||
|
if(wasEmpty)
|
||||||
|
setIdle(false);
|
||||||
|
|
||||||
|
// All done, the total progress is different now, so force a recalculation and
|
||||||
|
// send out the AsyncProgressUpdated event.
|
||||||
|
recalculateProgress();
|
||||||
|
|
||||||
|
// The Waitable might have ended before we had registered to its AsyncEnded
|
||||||
|
// event, so we have to do this to be on the safe side. This might cause
|
||||||
|
// asyncEnded() to be called twice, but that's not a problem in this
|
||||||
|
// implementation and improves performance and simplicity for the normal path.
|
||||||
|
if(waitable.Ended) {
|
||||||
|
asyncEnded();
|
||||||
|
observedWaitable.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // if Waitable ended
|
||||||
|
|
||||||
|
} // lock
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Stops tracking the specified waitable background process</summary>
|
||||||
|
/// <param name="waitable">Waitable background process to stop tracking of</param>
|
||||||
|
public void Untrack(Waitable waitable) {
|
||||||
|
lock(this.trackedWaitables) {
|
||||||
|
|
||||||
|
// Locate the object to be untracked in our collection
|
||||||
|
int removeIndex = this.trackedWaitables.FindIndex(
|
||||||
|
new Predicate<ObservedWeightedWaitable<Waitable>>(
|
||||||
|
new WaitableMatcher(waitable).Matches
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if(removeIndex == -1)
|
||||||
|
throw new InvalidOperationException("Item is not being tracked");
|
||||||
|
|
||||||
|
// Remove and dispose the Waitable the user wants to untrack
|
||||||
|
{
|
||||||
|
ObservedWeightedWaitable<Waitable> wrappedWaitable =
|
||||||
|
this.trackedWaitables[removeIndex];
|
||||||
|
|
||||||
|
this.trackedWaitables.RemoveAt(removeIndex);
|
||||||
|
wrappedWaitable.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the list is empty, then we're back in the idle state
|
||||||
|
if(this.trackedWaitables.Count == 0) {
|
||||||
|
|
||||||
|
this.totalWeight = 0.0f;
|
||||||
|
|
||||||
|
// If we entered the idle state with this call, report the state change!
|
||||||
|
setIdle(true);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Rebuild the total weight from scratch. Subtracting the removed Waitable's
|
||||||
|
// weight would work, too, but we might accumulate rounding errors making the sum
|
||||||
|
// drift slowly away from the actual value.
|
||||||
|
this.totalWeight = 0.0f;
|
||||||
|
for(int index = 0; index < this.trackedWaitables.Count; ++index)
|
||||||
|
this.totalWeight += this.trackedWaitables[index].WeightedWaitable.Weight;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // lock
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the tracker is currently idle</summary>
|
||||||
|
public bool Idle {
|
||||||
|
get { return this.idle; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Current summed progress of the tracked Waitables</summary>
|
||||||
|
public float Progress {
|
||||||
|
get { return this.progress; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Fires the AsyncIdleStateChanged event</summary>
|
||||||
|
/// <param name="idle">New idle state to report</param>
|
||||||
|
protected virtual void OnAsyncIdleStateChanged(bool idle) {
|
||||||
|
EventHandler<IdleStateEventArgs> copy = AsyncIdleStateChanged;
|
||||||
|
if(copy != null)
|
||||||
|
copy(this, new IdleStateEventArgs(idle));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Fires the AsyncProgressUpdated event</summary>
|
||||||
|
/// <param name="progress">New progress to report</param>
|
||||||
|
protected virtual void OnAsyncProgressUpdated(float progress) {
|
||||||
|
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
|
||||||
|
if(copy != null)
|
||||||
|
copy(this, new ProgressReportEventArgs(progress));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Recalculates the total progress of the tracker</summary>
|
||||||
|
private void recalculateProgress() {
|
||||||
|
float totalProgress = 0.0f;
|
||||||
|
|
||||||
|
// Lock the collection to avoid trouble when someone tries to remove one
|
||||||
|
// of our tracked Waitables while we're just doing a progress update
|
||||||
|
lock(this.trackedWaitables) {
|
||||||
|
|
||||||
|
// This is a safety measure. In theory, even after all Waitables have
|
||||||
|
// ended and the collection of tracked Waitables is cleared, a waiting
|
||||||
|
// thread might deliver another progress update causing this method to
|
||||||
|
// be entered. In this case, the right thing is to do nothing at all.
|
||||||
|
if(this.totalWeight == 0.0f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Sum up the total progress
|
||||||
|
for(int index = 0; index < this.trackedWaitables.Count; ++index) {
|
||||||
|
float weight = this.trackedWaitables[index].WeightedWaitable.Weight;
|
||||||
|
totalProgress += this.trackedWaitables[index].Progress * weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This also needs to be in the lock to guarantee that the totalWeight is
|
||||||
|
// the one for the number of Waitables we just summed -- by design,
|
||||||
|
// the total weight always has to be updated at the same time as the collection.
|
||||||
|
totalProgress /= this.totalWeight;
|
||||||
|
|
||||||
|
// Finally, trigger the event
|
||||||
|
this.progress = totalProgress;
|
||||||
|
OnAsyncProgressUpdated(totalProgress);
|
||||||
|
|
||||||
|
} // lock
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called when one of the tracked Waitables has ended</summary>
|
||||||
|
private void asyncEnded() {
|
||||||
|
lock(this.trackedWaitables) {
|
||||||
|
|
||||||
|
// If any Waitables in the list are still going, keep the entire list.
|
||||||
|
// This behavior is intentional in order to prevent the tracker's progress from
|
||||||
|
// jumping back repeatedly when multiple tracked Waitables come to an end.
|
||||||
|
for(int index = 0; index < this.trackedWaitables.Count; ++index)
|
||||||
|
if(!this.trackedWaitables[index].WeightedWaitable.Waitable.Ended)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// All Waitables have finished, get rid of the wrappers and make a
|
||||||
|
// fresh start for future Waitables to be tracked. No need to call
|
||||||
|
// Dispose() since, as a matter of fact, when the Waitable
|
||||||
|
this.trackedWaitables.Clear();
|
||||||
|
this.totalWeight = 0.0f;
|
||||||
|
|
||||||
|
// Notify our owner that we're idle now. This line is only reached when all
|
||||||
|
// Waitables were finished, so it's safe to trigger this here.
|
||||||
|
setIdle(true);
|
||||||
|
|
||||||
|
} // lock
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Called when one of the tracked Waitables has achieved progress</summary>
|
||||||
|
private void asyncProgressChanged() {
|
||||||
|
recalculateProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Changes the idle state</summary>
|
||||||
|
/// <param name="idle">Whether or not the tracker is currently idle</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method expects to be called during a lock() on trackedWaitables!
|
||||||
|
/// </remarks>
|
||||||
|
private void setIdle(bool idle) {
|
||||||
|
this.idle = idle;
|
||||||
|
|
||||||
|
OnAsyncIdleStateChanged(idle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Whether the tracker is currently idle</summary>
|
||||||
|
private volatile bool idle;
|
||||||
|
/// <summary>Current summed progress of the tracked Waitables</summary>
|
||||||
|
private volatile float progress;
|
||||||
|
/// <summary>Total weight of all Waitables being tracked</summary>
|
||||||
|
private volatile float totalWeight;
|
||||||
|
/// <summary>Waitables being tracked by this tracker</summary>
|
||||||
|
private List<ObservedWeightedWaitable<Waitable>> trackedWaitables;
|
||||||
|
/// <summary>Delegate for the asyncEnded() method</summary>
|
||||||
|
private ObservedWeightedWaitable<Waitable>.ReportDelegate asyncEndedDelegate;
|
||||||
|
/// <summary>Delegate for the asyncProgressUpdated() method</summary>
|
||||||
|
private ObservedWeightedWaitable<Waitable>.ReportDelegate asyncProgressUpdatedDelegate;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Nuclex.Support.Tracking
|
|
@ -1,362 +0,0 @@
|
||||||
#region CPL License
|
|
||||||
/*
|
|
||||||
Nuclex Framework
|
|
||||||
Copyright (C) 2002-2008 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.Threading;
|
|
||||||
|
|
||||||
namespace Nuclex.Support.Tracking {
|
|
||||||
|
|
||||||
/// <summary>Helps tracking the progress of one or more progressions</summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para>
|
|
||||||
/// This is useful if you want to display a progress bar for multiple
|
|
||||||
/// progressions but can not guarantee that no additional progressions
|
|
||||||
/// will appear inmidst of execution.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// This class does not implement the IProgression interface itself in
|
|
||||||
/// order to not violate the design principles of progressions which
|
|
||||||
/// guarantee that a progression will only finish once (whereas the
|
|
||||||
/// progression tracker might 'finish' any number of times).
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public class ProgressionTracker : IDisposable {
|
|
||||||
|
|
||||||
#region class ProgressionMatcher
|
|
||||||
|
|
||||||
/// <summary>Matches a progression to a fully wrapped one</summary>
|
|
||||||
private class ProgressionMatcher {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new progression matcher that matches against
|
|
||||||
/// the specified progression
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="toMatch">Progression to match against</param>
|
|
||||||
public ProgressionMatcher(Waitable toMatch) {
|
|
||||||
this.toMatch = toMatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks whether the provided progression matches the comparison
|
|
||||||
/// progression of the instance
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="other">Progression to match to the comparison progression</param>
|
|
||||||
public bool Matches(ObservedWeightedProgression<Waitable> other) {
|
|
||||||
return ReferenceEquals(other.WeightedProgression.Progression, this.toMatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Progression this instance compares against</summary>
|
|
||||||
private Waitable toMatch;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion // class ProgressionMatcher
|
|
||||||
|
|
||||||
/// <summary>Triggered when the idle state of the tracker changes</summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// The tracker is idle when no progressions are being tracked in it. If you're
|
|
||||||
/// using this class to feed a progress bar, this would be the event to use for
|
|
||||||
/// showing or hiding the progress bar. The tracker starts off as idle because,
|
|
||||||
/// upon construction, its list of progressions will be empty.
|
|
||||||
/// </remarks>
|
|
||||||
public event EventHandler<IdleStateEventArgs> AsyncIdleStateChanged;
|
|
||||||
|
|
||||||
/// <summary>Triggered when the total progress has changed</summary>
|
|
||||||
public event EventHandler<ProgressReportEventArgs> AsyncProgressUpdated;
|
|
||||||
|
|
||||||
/// <summary>Initializes a new progression tracker</summary>
|
|
||||||
public ProgressionTracker() {
|
|
||||||
|
|
||||||
this.trackedProgressions = new List<ObservedWeightedProgression<Waitable>>();
|
|
||||||
this.idle = true;
|
|
||||||
|
|
||||||
this.asyncEndedDelegate =
|
|
||||||
new ObservedWeightedProgression<Waitable>.ReportDelegate(asyncEnded);
|
|
||||||
this.asyncProgressUpdatedDelegate =
|
|
||||||
new ObservedWeightedProgression<Waitable>.ReportDelegate(asyncProgressUpdated);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Immediately releases all resources owned by the instance</summary>
|
|
||||||
public void Dispose() {
|
|
||||||
lock(this.trackedProgressions) {
|
|
||||||
|
|
||||||
// Get rid of all progression we're tracking. This unsubscribes the
|
|
||||||
// observers from the events of the progressions and stops us from
|
|
||||||
// being kept alive and receiving any further events if some of the
|
|
||||||
// tracked progressions are still executing.
|
|
||||||
for(int index = 0; index < this.trackedProgressions.Count; ++index)
|
|
||||||
this.trackedProgressions[index].Dispose();
|
|
||||||
|
|
||||||
// Help the GC a bit by untangling the references :)
|
|
||||||
this.trackedProgressions.Clear();
|
|
||||||
this.trackedProgressions = null;
|
|
||||||
|
|
||||||
} // lock
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Begins tracking the specified progression</summary>
|
|
||||||
/// <param name="progression">Progression to be tracked</param>
|
|
||||||
public void Track(Waitable progression) {
|
|
||||||
Track(progression, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Begins tracking the specified progression</summary>
|
|
||||||
/// <param name="progression">Progression to be tracked</param>
|
|
||||||
/// <param name="weight">Weight to assign to this progression</param>
|
|
||||||
public void Track(Waitable progression, float weight) {
|
|
||||||
|
|
||||||
// Add the new progression into the tracking list. This has to be done
|
|
||||||
// inside a lock to prevent issues with the progressUpdate callback, which could
|
|
||||||
// access the totalWeight field before it has been updated to reflect the
|
|
||||||
// new progression added to the collection.
|
|
||||||
lock(this.trackedProgressions) {
|
|
||||||
|
|
||||||
bool wasEmpty = (this.trackedProgressions.Count == 0);
|
|
||||||
|
|
||||||
// This can be done after we registered the wrapper to our delegates because
|
|
||||||
// any incoming progress updates will be stopped from the danger of a
|
|
||||||
// division-by-zero from the potentially still zeroed totalWeight by the lock.
|
|
||||||
this.totalWeight += weight;
|
|
||||||
|
|
||||||
if(progression.Ended) {
|
|
||||||
|
|
||||||
// If the ended progression would become the only progression in the list,
|
|
||||||
// there's no sense in doing anything at all because it would have to be
|
|
||||||
// thrown right out again. Only add the progression when there are other
|
|
||||||
// running progressions to properly sum total progress for consistency.
|
|
||||||
if(!wasEmpty) {
|
|
||||||
|
|
||||||
// Construct a new observation wrapper. This is done inside the lock
|
|
||||||
// because as soon as we are subscribed to the events, we can potentially
|
|
||||||
// receive them. The lock eliminates the risk of processing a progress update
|
|
||||||
// before the progression has been added to the tracked progressions list.
|
|
||||||
this.trackedProgressions.Add(
|
|
||||||
new ObservedWeightedProgression<Waitable>(
|
|
||||||
new WeightedProgression<Waitable>(progression, weight),
|
|
||||||
this.asyncProgressUpdatedDelegate,
|
|
||||||
this.asyncEndedDelegate
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// All done, the total progress is different now, so force a recalculation and
|
|
||||||
// send out the AsyncProgressUpdated event.
|
|
||||||
recalculateProgress();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} else { // Not ended -- Progression is still running
|
|
||||||
|
|
||||||
// Construct a new progression observer and add the progression to our
|
|
||||||
// list of tracked progressions.
|
|
||||||
ObservedWeightedProgression<Waitable> observedProgression =
|
|
||||||
new ObservedWeightedProgression<Waitable>(
|
|
||||||
new WeightedProgression<Waitable>(progression, weight),
|
|
||||||
this.asyncProgressUpdatedDelegate,
|
|
||||||
this.asyncEndedDelegate
|
|
||||||
);
|
|
||||||
|
|
||||||
this.trackedProgressions.Add(observedProgression);
|
|
||||||
|
|
||||||
// If this is the first progression to be added to the list, tell our
|
|
||||||
// owner that we're idle no longer!
|
|
||||||
if(wasEmpty)
|
|
||||||
setIdle(false);
|
|
||||||
|
|
||||||
// All done, the total progress is different now, so force a recalculation and
|
|
||||||
// send out the AsyncProgressUpdated event.
|
|
||||||
recalculateProgress();
|
|
||||||
|
|
||||||
// The progression might have ended before we had registered to its AsyncEnded
|
|
||||||
// event, so we have to do this to be on the safe side. This might cause
|
|
||||||
// asyncEnded() to be called twice, but that's not a problem in this
|
|
||||||
// implementation and improves performance and simplicity for the normal path.
|
|
||||||
if(progression.Ended) {
|
|
||||||
asyncEnded();
|
|
||||||
observedProgression.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // if progression ended
|
|
||||||
|
|
||||||
} // lock
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Stops tracking the specified progression</summary>
|
|
||||||
/// <param name="progression">Progression to stop tracking of</param>
|
|
||||||
public void Untrack(Waitable progression) {
|
|
||||||
lock(this.trackedProgressions) {
|
|
||||||
|
|
||||||
// Locate the object to be untracked in our collection
|
|
||||||
int removeIndex = this.trackedProgressions.FindIndex(
|
|
||||||
new Predicate<ObservedWeightedProgression<Waitable>>(
|
|
||||||
new ProgressionMatcher(progression).Matches
|
|
||||||
)
|
|
||||||
);
|
|
||||||
if(removeIndex == -1)
|
|
||||||
throw new InvalidOperationException("Item is not being tracked");
|
|
||||||
|
|
||||||
// Remove and dispose the progression the user wants to untrack
|
|
||||||
{
|
|
||||||
ObservedWeightedProgression<Waitable> wrappedProgression =
|
|
||||||
this.trackedProgressions[removeIndex];
|
|
||||||
|
|
||||||
this.trackedProgressions.RemoveAt(removeIndex);
|
|
||||||
wrappedProgression.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the list is empty, then we're back in the idle state
|
|
||||||
if(this.trackedProgressions.Count == 0) {
|
|
||||||
|
|
||||||
this.totalWeight = 0.0f;
|
|
||||||
|
|
||||||
// If we entered the idle state with this call, report the state change!
|
|
||||||
setIdle(true);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// Rebuild the total weight from scratch. Subtracting the removed progression's
|
|
||||||
// weight would work, too, but we might accumulate rounding errors making the sum
|
|
||||||
// drift slowly away from the actual value.
|
|
||||||
this.totalWeight = 0.0f;
|
|
||||||
for(int index = 0; index < this.trackedProgressions.Count; ++index)
|
|
||||||
this.totalWeight += this.trackedProgressions[index].WeightedProgression.Weight;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // lock
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Whether the tracker is currently idle</summary>
|
|
||||||
public bool Idle {
|
|
||||||
get { return this.idle; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Current summed progress of the tracked progressions</summary>
|
|
||||||
public float Progress {
|
|
||||||
get { return this.progress; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Fires the AsyncIdleStateChanged event</summary>
|
|
||||||
/// <param name="idle">New idle state to report</param>
|
|
||||||
protected virtual void OnAsyncIdleStateChanged(bool idle) {
|
|
||||||
EventHandler<IdleStateEventArgs> copy = AsyncIdleStateChanged;
|
|
||||||
if(copy != null)
|
|
||||||
copy(this, new IdleStateEventArgs(idle));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Fires the AsyncProgressUpdated event</summary>
|
|
||||||
/// <param name="progress">New progress to report</param>
|
|
||||||
protected virtual void OnAsyncProgressUpdated(float progress) {
|
|
||||||
EventHandler<ProgressReportEventArgs> copy = AsyncProgressUpdated;
|
|
||||||
if(copy != null)
|
|
||||||
copy(this, new ProgressReportEventArgs(progress));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Recalculates the total progress of the tracker</summary>
|
|
||||||
private void recalculateProgress() {
|
|
||||||
float totalProgress = 0.0f;
|
|
||||||
|
|
||||||
// Lock the collection to avoid trouble when someone tries to remove one
|
|
||||||
// of our tracked progressions while we're just doing a progress update
|
|
||||||
lock(this.trackedProgressions) {
|
|
||||||
|
|
||||||
// This is a safety measure. In theory, even after all progressions have
|
|
||||||
// ended and the collection of tracked progressions is cleared, a waiting
|
|
||||||
// thread might deliver another progress update causing this method to
|
|
||||||
// be entered. In this case, the right thing is to do nothing at all.
|
|
||||||
if(this.totalWeight == 0.0f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Sum up the total progress
|
|
||||||
for(int index = 0; index < this.trackedProgressions.Count; ++index) {
|
|
||||||
float weight = this.trackedProgressions[index].WeightedProgression.Weight;
|
|
||||||
totalProgress += this.trackedProgressions[index].Progress * weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This also needs to be in the lock to guarantee that the totalWeight is
|
|
||||||
// the one for the number of progressions we just summed -- by design,
|
|
||||||
// the total weight always has to be updated at the same time as the collection.
|
|
||||||
totalProgress /= this.totalWeight;
|
|
||||||
|
|
||||||
// Finally, trigger the event
|
|
||||||
this.progress = totalProgress;
|
|
||||||
OnAsyncProgressUpdated(totalProgress);
|
|
||||||
|
|
||||||
} // lock
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Called when one of the tracked progressions has ended</summary>
|
|
||||||
private void asyncEnded() {
|
|
||||||
lock(this.trackedProgressions) {
|
|
||||||
|
|
||||||
// If any progressions in the list are still going, keep the entire list.
|
|
||||||
// This behavior is intentional in order to prevent the tracker's progress from
|
|
||||||
// jumping back repeatedly when multiple tracked progressions come to an end.
|
|
||||||
for(int index = 0; index < this.trackedProgressions.Count; ++index)
|
|
||||||
if(!this.trackedProgressions[index].WeightedProgression.Progression.Ended)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// All progressions have finished, get rid of the wrappers and make a
|
|
||||||
// fresh start for future progressions to be tracked. No need to call
|
|
||||||
// Dispose() since, as a matter of fact, when the progression
|
|
||||||
this.trackedProgressions.Clear();
|
|
||||||
this.totalWeight = 0.0f;
|
|
||||||
|
|
||||||
// Notify our owner that we're idle now. This line is only reached when all
|
|
||||||
// progressions were finished, so it's safe to trigger this here.
|
|
||||||
setIdle(true);
|
|
||||||
|
|
||||||
} // lock
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Called when one of the tracked progression has achieved progress</summary>
|
|
||||||
private void asyncProgressUpdated() {
|
|
||||||
recalculateProgress();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Changes the idle state</summary>
|
|
||||||
/// <param name="idle">Whether or not the tracker is currently idle</param>
|
|
||||||
/// <remarks>
|
|
||||||
/// This method expects to be called during a lock() on trackedProgressions!
|
|
||||||
/// </remarks>
|
|
||||||
private void setIdle(bool idle) {
|
|
||||||
this.idle = idle;
|
|
||||||
|
|
||||||
OnAsyncIdleStateChanged(idle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Whether the tracker is currently idle</summary>
|
|
||||||
private volatile bool idle;
|
|
||||||
/// <summary>Current summed progress of the tracked progressions</summary>
|
|
||||||
private volatile float progress;
|
|
||||||
/// <summary>Total weight of all progressions being tracked</summary>
|
|
||||||
private volatile float totalWeight;
|
|
||||||
/// <summary>Progressions being tracked by this tracker</summary>
|
|
||||||
private List<ObservedWeightedProgression<Waitable>> trackedProgressions;
|
|
||||||
/// <summary>Delegate for the asyncEnded() method</summary>
|
|
||||||
private ObservedWeightedProgression<Waitable>.ReportDelegate asyncEndedDelegate;
|
|
||||||
/// <summary>Delegate for the asyncProgressUpdated() method</summary>
|
|
||||||
private ObservedWeightedProgression<Waitable>.ReportDelegate asyncProgressUpdatedDelegate;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Nuclex.Support.Tracking
|
|
|
@ -164,7 +164,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
testSetProgression.Children[0].Progression.ChangeProgress(0.5f);
|
testSetProgression.Children[0].Waitable.ChangeProgress(0.5f);
|
||||||
|
|
||||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
}
|
}
|
||||||
|
@ -174,9 +174,9 @@ namespace Nuclex.Support.Tracking {
|
||||||
public void TestWeightedSummedProgress() {
|
public void TestWeightedSummedProgress() {
|
||||||
SetProgression<TestWaitable> testSetProgression =
|
SetProgression<TestWaitable> testSetProgression =
|
||||||
new SetProgression<TestWaitable>(
|
new SetProgression<TestWaitable>(
|
||||||
new WeightedProgression<TestWaitable>[] {
|
new WeightedWaitable<TestWaitable>[] {
|
||||||
new WeightedProgression<TestWaitable>(new TestWaitable(), 1.0f),
|
new WeightedWaitable<TestWaitable>(new TestWaitable(), 1.0f),
|
||||||
new WeightedProgression<TestWaitable>(new TestWaitable(), 2.0f)
|
new WeightedWaitable<TestWaitable>(new TestWaitable(), 2.0f)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -191,7 +191,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
testSetProgression.Children[0].Progression.ChangeProgress(0.5f);
|
testSetProgression.Children[0].Waitable.ChangeProgress(0.5f);
|
||||||
|
|
||||||
Expect.Once.On(mockedSubscriber).
|
Expect.Once.On(mockedSubscriber).
|
||||||
Method("ProgressChanged").
|
Method("ProgressChanged").
|
||||||
|
@ -202,7 +202,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
testSetProgression.Children[1].Progression.ChangeProgress(0.5f);
|
testSetProgression.Children[1].Waitable.ChangeProgress(0.5f);
|
||||||
|
|
||||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
}
|
}
|
||||||
|
@ -227,8 +227,8 @@ namespace Nuclex.Support.Tracking {
|
||||||
Method("Ended").
|
Method("Ended").
|
||||||
WithAnyArguments();
|
WithAnyArguments();
|
||||||
|
|
||||||
testSetProgression.Children[0].Progression.End();
|
testSetProgression.Children[0].Waitable.End();
|
||||||
testSetProgression.Children[1].Progression.End();
|
testSetProgression.Children[1].Waitable.End();
|
||||||
|
|
||||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,10 +46,10 @@ namespace Nuclex.Support.Tracking {
|
||||||
// progression and wrap it in an ObservedProgression
|
// progression and wrap it in an ObservedProgression
|
||||||
foreach(ProgressionType progression in childs) {
|
foreach(ProgressionType progression in childs) {
|
||||||
this.children.Add(
|
this.children.Add(
|
||||||
new ObservedWeightedProgression<ProgressionType>(
|
new ObservedWeightedWaitable<ProgressionType>(
|
||||||
new WeightedProgression<ProgressionType>(progression),
|
new WeightedWaitable<ProgressionType>(progression),
|
||||||
new ObservedWeightedProgression<ProgressionType>.ReportDelegate(asyncProgressUpdated),
|
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncProgressUpdated),
|
||||||
new ObservedWeightedProgression<ProgressionType>.ReportDelegate(asyncEnded)
|
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncEnded)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -63,17 +63,17 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <summary>Initializes a new set progression</summary>
|
/// <summary>Initializes a new set progression</summary>
|
||||||
/// <param name="childs">Progressions to track with this set</param>
|
/// <param name="childs">Progressions to track with this set</param>
|
||||||
public SetProgression(
|
public SetProgression(
|
||||||
IEnumerable<WeightedProgression<ProgressionType>> childs
|
IEnumerable<WeightedWaitable<ProgressionType>> childs
|
||||||
)
|
)
|
||||||
: this() {
|
: this() {
|
||||||
|
|
||||||
// Construct an ObservedProgression around each of the WeightedProgressions
|
// Construct an ObservedProgression around each of the WeightedProgressions
|
||||||
foreach(WeightedProgression<ProgressionType> progression in childs) {
|
foreach(WeightedWaitable<ProgressionType> progression in childs) {
|
||||||
this.children.Add(
|
this.children.Add(
|
||||||
new ObservedWeightedProgression<ProgressionType>(
|
new ObservedWeightedWaitable<ProgressionType>(
|
||||||
progression,
|
progression,
|
||||||
new ObservedWeightedProgression<ProgressionType>.ReportDelegate(asyncProgressUpdated),
|
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncProgressUpdated),
|
||||||
new ObservedWeightedProgression<ProgressionType>.ReportDelegate(asyncEnded)
|
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncEnded)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
|
|
||||||
/// <summary>Performs common initialization for the public constructors</summary>
|
/// <summary>Performs common initialization for the public constructors</summary>
|
||||||
private SetProgression() {
|
private SetProgression() {
|
||||||
this.children = new List<ObservedWeightedProgression<ProgressionType>>();
|
this.children = new List<ObservedWeightedWaitable<ProgressionType>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Immediately releases all resources owned by the object</summary>
|
/// <summary>Immediately releases all resources owned by the object</summary>
|
||||||
|
@ -106,7 +106,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Childs contained in the progression set</summary>
|
/// <summary>Childs contained in the progression set</summary>
|
||||||
public IList<WeightedProgression<ProgressionType>> Children {
|
public IList<WeightedWaitable<ProgressionType>> Children {
|
||||||
get {
|
get {
|
||||||
|
|
||||||
// The wrapper is constructed only when needed. Most of the time, users will
|
// The wrapper is constructed only when needed. Most of the time, users will
|
||||||
|
@ -160,7 +160,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
// scaled to the weight each progression has assigned to it.
|
// scaled to the weight each progression has assigned to it.
|
||||||
for(int index = 0; index < this.children.Count; ++index) {
|
for(int index = 0; index < this.children.Count; ++index) {
|
||||||
totalProgress +=
|
totalProgress +=
|
||||||
this.children[index].Progress * this.children[index].WeightedProgression.Weight;
|
this.children[index].Progress * this.children[index].WeightedWaitable.Weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the actual combined progress
|
// Calculate the actual combined progress
|
||||||
|
@ -179,7 +179,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
// If there's still at least one progression going, don't report that
|
// If there's still at least one progression going, don't report that
|
||||||
// the SetProgression has finished yet.
|
// the SetProgression has finished yet.
|
||||||
for(int index = 0; index < this.children.Count; ++index)
|
for(int index = 0; index < this.children.Count; ++index)
|
||||||
if(!this.children[index].WeightedProgression.Progression.Ended)
|
if(!this.children[index].WeightedWaitable.Waitable.Ended)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// All child progressions have ended, so the set has now ended as well
|
// All child progressions have ended, so the set has now ended as well
|
||||||
|
@ -188,7 +188,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Progressions being managed in the set</summary>
|
/// <summary>Progressions being managed in the set</summary>
|
||||||
private List<ObservedWeightedProgression<ProgressionType>> children;
|
private List<ObservedWeightedWaitable<ProgressionType>> children;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wrapper collection for exposing the child progressions under the
|
/// Wrapper collection for exposing the child progressions under the
|
||||||
/// WeightedProgression interface
|
/// WeightedProgression interface
|
||||||
|
|
|
@ -24,24 +24,24 @@ using System.Collections.Generic;
|
||||||
namespace Nuclex.Support.Tracking {
|
namespace Nuclex.Support.Tracking {
|
||||||
|
|
||||||
/// <summary>Progression with an associated weight for the total progress</summary>
|
/// <summary>Progression with an associated weight for the total progress</summary>
|
||||||
public class WeightedProgression<ProgressionType> where ProgressionType : Waitable {
|
public class WeightedWaitable<ProgressionType> where ProgressionType : Waitable {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new weighted progression with a default weight of 1.0
|
/// Initializes a new weighted progression with a default weight of 1.0
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="progression">Progression whose progress to monitor</param>
|
/// <param name="progression">Progression whose progress to monitor</param>
|
||||||
public WeightedProgression(ProgressionType progression) : this(progression, 1.0f) { }
|
public WeightedWaitable(ProgressionType progression) : this(progression, 1.0f) { }
|
||||||
|
|
||||||
/// <summary>Initializes a new weighted progression</summary>
|
/// <summary>Initializes a new weighted progression</summary>
|
||||||
/// <param name="progression">Progression whose progress to monitor</param>
|
/// <param name="progression">Progression whose progress to monitor</param>
|
||||||
/// <param name="weight">Weighting of the progression's progress</param>
|
/// <param name="weight">Weighting of the progression's progress</param>
|
||||||
public WeightedProgression(ProgressionType progression, float weight) {
|
public WeightedWaitable(ProgressionType progression, float weight) {
|
||||||
this.progression = progression;
|
this.progression = progression;
|
||||||
this.weight = weight;
|
this.weight = weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Progression being wrapped by this weighted progression</summary>
|
/// <summary>Progression being wrapped by this weighted progression</summary>
|
||||||
public ProgressionType Progression {
|
public ProgressionType Waitable {
|
||||||
get { return this.progression; }
|
get { return this.progression; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user