All remaining references to the old name of the waitable class, 'progression' changed to 'waitable'; QueueOperation renamed to OperationQueue; SetProgression renamed to WaitableGroup; improved documentation in various places

git-svn-id: file:///srv/devel/repo-conversion/nusu@78 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2008-06-11 20:28:08 +00:00
parent df860b8e57
commit 7bff8c5d52
12 changed files with 307 additions and 271 deletions

View File

@ -151,9 +151,9 @@
<Compile Include="Source\Scheduling\AbortedException.cs" />
<Compile Include="Source\Scheduling\IAbortable.cs" />
<Compile Include="Source\Scheduling\Operation.cs" />
<Compile Include="Source\Scheduling\QueueOperation.cs" />
<Compile Include="Source\Scheduling\QueueOperation.Test.cs">
<DependentUpon>QueueOperation.cs</DependentUpon>
<Compile Include="Source\Scheduling\OperationQueue.cs" />
<Compile Include="Source\Scheduling\OperationQueue.Test.cs">
<DependentUpon>OperationQueue.cs</DependentUpon>
</Compile>
<Compile Include="Source\Scheduling\ThreadCallbackOperation.cs" />
<Compile Include="Source\Scheduling\ThreadOperation.cs" />
@ -178,9 +178,9 @@
<DependentUpon>ProgressTracker.cs</DependentUpon>
</Compile>
<Compile Include="Source\Tracking\Request.cs" />
<Compile Include="Source\Tracking\SetProgression.cs" />
<Compile Include="Source\Tracking\SetProgression.Test.cs">
<DependentUpon>SetProgression.cs</DependentUpon>
<Compile Include="Source\Tracking\WaitableGroup.cs" />
<Compile Include="Source\Tracking\WaitableGroup.Test.cs">
<DependentUpon>WaitableGroup.cs</DependentUpon>
</Compile>
<Compile Include="Source\Tracking\StatusReportEventArgs.cs" />
<Compile Include="Source\Tracking\Waitable.cs" />

View File

@ -57,7 +57,7 @@ namespace Nuclex.Support.Collections {
}
/// <summary>
/// Inserts an element into the ProgressionCollection at the specified index
/// Inserts an element into the ObservableCollection at the specified index
/// </summary>
/// <param name="index">
/// The object to insert. The value can be null for reference types.
@ -70,7 +70,7 @@ namespace Nuclex.Support.Collections {
}
/// <summary>
/// Removes the element at the specified index of the ProgressionCollection
/// Removes the element at the specified index of the ObservableCollection
/// </summary>
/// <param name="index">The zero-based index of the element to remove</param>
protected override void RemoveItem(int index) {

View File

@ -33,11 +33,11 @@ namespace Nuclex.Support.Scheduling {
/// <summary>Unit Test for the queue operation class</summary>
[TestFixture]
public class QueueOperationTest {
public class OperationQueueTest {
#region interface IQueueOperationSubscriber
/// <summary>Interface used to test the set progression.</summary>
/// <summary>Interface used to test the set waitable.</summary>
public interface IQueueOperationSubscriber {
/// <summary>Called when the queue operations's progress changes</summary>
@ -95,7 +95,7 @@ namespace Nuclex.Support.Scheduling {
#region class TestOperation
/// <summary>Progression used for testing in this unit test</summary>
/// <summary>Operation used for testing in this unit test</summary>
private class TestOperation : Operation, IProgressReporter {
/// <summary>will be triggered to report when progress has been achieved</summary>
@ -116,9 +116,9 @@ namespace Nuclex.Support.Scheduling {
OnAsyncEnded();
}
/// <summary>Changes the testing progression's indicated progress</summary>
/// <summary>Changes the testing operation's indicated progress</summary>
/// <param name="progress">
/// New progress to be reported by the testing progression
/// New progress to be reported by the testing operation
/// </param>
public void ChangeProgress(float progress) {
OnAsyncProgressChanged(progress);
@ -136,7 +136,7 @@ namespace Nuclex.Support.Scheduling {
/// <summary>Fires the progress update event</summary>
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Informs the observers of this operation about the achieved progress.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
@ -145,10 +145,10 @@ namespace Nuclex.Support.Scheduling {
/// <summary>Fires the progress update event</summary>
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Allows for classes derived from the Progression class to easily provide
/// Informs the observers of this operation about the achieved progress.
/// Allows for classes derived from the Operation class to easily provide
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// operation's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
@ -175,8 +175,8 @@ namespace Nuclex.Support.Scheduling {
TestOperation operation1 = new TestOperation();
TestOperation operation2 = new TestOperation();
QueueOperation<TestOperation> testQueueOperation =
new QueueOperation<TestOperation>(
OperationQueue<TestOperation> testQueueOperation =
new OperationQueue<TestOperation>(
new TestOperation[] { operation1, operation2 }
);
@ -188,7 +188,7 @@ namespace Nuclex.Support.Scheduling {
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(QueueOperation<TestOperation>)),
new NMock2.Matchers.TypeMatcher(typeof(OperationQueue<TestOperation>)),
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
}
);
@ -199,7 +199,7 @@ namespace Nuclex.Support.Scheduling {
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(QueueOperation<TestOperation>)),
new NMock2.Matchers.TypeMatcher(typeof(OperationQueue<TestOperation>)),
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
}
);

View File

@ -29,7 +29,7 @@ namespace Nuclex.Support.Scheduling {
/// <typeparam name="OperationType">
/// Type of the child operations the QueueOperation will contain
/// </typeparam>
public class QueueOperation<OperationType> : Operation, IProgressReporter
public class OperationQueue<OperationType> : Operation, IProgressReporter
where OperationType : Operation {
/// <summary>will be triggered to report when progress has been achieved</summary>
@ -40,24 +40,24 @@ namespace Nuclex.Support.Scheduling {
/// <remarks>
/// All child operations will have a default weight of 1.0
/// </remarks>
public QueueOperation(IEnumerable<OperationType> childs) : this() {
public OperationQueue(IEnumerable<OperationType> childs) : this() {
// Construct a WeightedProgression with the default weight for each
// progression and wrap it in an ObservedProgression
// Construct a WeightedWaitable with the default weight for each
// waitable and wrap it in an ObservedWaitable
foreach(OperationType operation in childs)
this.children.Add(new WeightedWaitable<OperationType>(operation));
// Since all progressions have a weight of 1.0, the total weight is
// equal to the number of progressions in our list
// Since all waitables have a weight of 1.0, the total weight is
// equal to the number of waitables in our list
this.totalWeight = (float)this.children.Count;
}
/// <summary>Initializes a new queue operation with custom weights</summary>
/// <param name="childs">Child operations to execute in this operation</param>
public QueueOperation(IEnumerable<WeightedWaitable<OperationType>> childs) : this() {
public OperationQueue(IEnumerable<WeightedWaitable<OperationType>> childs) : this() {
// Construct an ObservedProgression around each of the WeightedProgressions
// Construct an ObservedWaitablen around each of the WeightedWaitables
foreach(WeightedWaitable<OperationType> operation in childs) {
this.children.Add(operation);
@ -68,7 +68,7 @@ namespace Nuclex.Support.Scheduling {
}
/// <summary>Initializes a new queue operation</summary>
private QueueOperation() {
private OperationQueue() {
this.asyncOperationEndedDelegate = new EventHandler(asyncOperationEnded);
this.asyncOperationProgressChangedDelegate = new EventHandler<ProgressReportEventArgs>(
asyncOperationProgressChanged
@ -99,7 +99,7 @@ namespace Nuclex.Support.Scheduling {
/// <summary>Fires the progress update event</summary>
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Informs the observers of this waitable about the achieved progress.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
@ -108,10 +108,10 @@ namespace Nuclex.Support.Scheduling {
/// <summary>Fires the progress update event</summary>
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Allows for classes derived from the Progression class to easily provide
/// Informs the observers of this waitable about the achieved progress.
/// Allows for classes derived from the Waitable class to easily provide
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// waitable's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;

View File

@ -23,33 +23,33 @@ using System.Collections.Generic;
namespace Nuclex.Support.Tracking {
/// <summary>Progression being observed by another class</summary>
/// <typeparam name="ProgressionType">
/// Type of the progression that is being observed
/// <summary>Waitable being observed by another object</summary>
/// <typeparam name="WaitableType">
/// Type of the waitable that is being observed
/// </typeparam>
internal class ObservedWeightedWaitable<ProgressionType> : IDisposable
where ProgressionType : Waitable {
internal class ObservedWeightedWaitable<WaitableType> : IDisposable
where WaitableType : Waitable {
/// <summary>Delegate for reporting progress updates</summary>
public delegate void ReportDelegate();
/// <summary>Initializes a new observed progression</summary>
/// <param name="weightedProgression">Weighted progression being observed</param>
/// <summary>Initializes a new observed waitable</summary>
/// <param name="weightedWaitable">Weighted waitable being observed</param>
/// <param name="progressUpdateCallback">
/// Callback to invoke when the progression's progress changes
/// Callback to invoke when the waitable's progress changes
/// </param>
/// <param name="endedCallback">
/// Callback to invoke when the progression has ended
/// Callback to invoke when the waitable has ended
/// </param>
internal ObservedWeightedWaitable(
WeightedWaitable<ProgressionType> weightedProgression,
WeightedWaitable<WaitableType> weightedWaitable,
ReportDelegate progressUpdateCallback,
ReportDelegate endedCallback
) {
this.weightedProgression = weightedProgression;
this.weightedWaitable = weightedWaitable;
// See if this progression has already ended (initial check for performance)
if(weightedProgression.Waitable.Ended) {
// See if this waitable has already ended (initial check for performance)
if(weightedWaitable.Waitable.Ended) {
this.progress = 1.0f;
@ -58,18 +58,18 @@ namespace Nuclex.Support.Tracking {
this.endedCallback = endedCallback;
this.progressUpdateCallback = progressUpdateCallback;
this.weightedProgression.Waitable.AsyncEnded +=
this.weightedWaitable.Waitable.AsyncEnded +=
new EventHandler(asyncEnded);
// Check whether this progression might have ended before we were able to
// Check whether this waitable might have ended before we were able to
// attach ourselfes to its event. If so, don't bother registering to the
// 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
// even though the progression is in the 'Ended' state.
if(weightedProgression.Waitable.Ended) {
// even though the waitable is in the 'Ended' state.
if(weightedWaitable.Waitable.Ended) {
this.progress = 1.0f;
} else {
this.progressReporter = this.weightedProgression.Waitable as IProgressReporter;
this.progressReporter = this.weightedWaitable.Waitable as IProgressReporter;
if(this.progressReporter != null) {
this.asyncProgressChangedEventHandler = new EventHandler<ProgressReportEventArgs>(
@ -89,18 +89,18 @@ namespace Nuclex.Support.Tracking {
asyncDisconnectEvents();
}
/// <summary>Weighted progression being observed</summary>
public WeightedWaitable<ProgressionType> WeightedWaitable {
get { return this.weightedProgression; }
/// <summary>Weighted waitable being observed</summary>
public WeightedWaitable<WaitableType> WeightedWaitable {
get { return this.weightedWaitable; }
}
/// <summary>Amount of progress this progression has achieved so far</summary>
/// <summary>Amount of progress this waitable has achieved so far</summary>
public float Progress {
get { return this.progress; }
}
/// <summary>Called when the observed progression has ended</summary>
/// <param name="sender">Progression that has ended</param>
/// <summary>Called when the observed waitable has ended</summary>
/// <param name="sender">Waitable that has ended</param>
/// <param name="e">Not used</param>
private void asyncEnded(object sender, EventArgs e) {
ReportDelegate endedCallback = this.endedCallback;
@ -109,11 +109,11 @@ namespace Nuclex.Support.Tracking {
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.
// when a waitable doesn't report any progress at all, the set or queue
// owning us will have a percentage of waitables 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
// report could have been generated by a thread running the waitable
// 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) {
@ -124,15 +124,15 @@ namespace Nuclex.Support.Tracking {
endedCallback();
}
/// <summary>Called when the progress of the observed progression changes</summary>
/// <param name="sender">Progression whose progress has changed</param>
/// <summary>Called when the progress of the observed waitable changes</summary>
/// <param name="sender">Waitable whose progress has changed</param>
/// <param name="e">Contains the updated progress</param>
private void asyncProgressChanged(object sender, ProgressReportEventArgs e) {
this.progress = e.Progress;
this.progressUpdateCallback();
}
/// <summary>Unsubscribes from all events of the observed progression</summary>
/// <summary>Unsubscribes from all events of the observed waitable</summary>
private void asyncDisconnectEvents() {
// Make use of the double check locking idiom to avoid the costly lock when
@ -143,8 +143,7 @@ namespace Nuclex.Support.Tracking {
// is no risk of deadlock involved, so we don't need a fancy syncRoot!
lock(this) {
if(this.endedCallback != null) {
this.weightedProgression.Waitable.AsyncEnded -=
new EventHandler(asyncEnded);
this.weightedWaitable.Waitable.AsyncEnded -= new EventHandler(asyncEnded);
if(this.progressReporter != null) {
this.progressReporter.AsyncProgressChanged -=
@ -163,13 +162,13 @@ namespace Nuclex.Support.Tracking {
}
private EventHandler<ProgressReportEventArgs> asyncProgressChangedEventHandler;
/// <summary>The observed progression's progress reporting interface</summary>
/// <summary>The observed waitable's progress reporting interface</summary>
private IProgressReporter progressReporter;
/// <summary>The weighted progression that is being observed</summary>
private WeightedWaitable<ProgressionType> weightedProgression;
/// <summary>The weighted wable that is being observed</summary>
private WeightedWaitable<WaitableType> weightedWaitable;
/// <summary>Callback to invoke when the progress updates</summary>
private volatile ReportDelegate progressUpdateCallback;
/// <summary>Callback to invoke when the progression ends</summary>
/// <summary>Callback to invoke when the waitable ends</summary>
private volatile ReportDelegate endedCallback;
/// <summary>Progress achieved so far</summary>
private volatile float progress;

View File

@ -27,36 +27,36 @@ using Nuclex.Support.Collections;
namespace Nuclex.Support.Tracking {
/// <summary>Collection of progressions with a weighting value</summary>
/// <typeparam name="ProgressionType">Type of progressions to manage</typeparam>
/// <summary>Collection of waitables with a weighting value</summary>
/// <typeparam name="WaitableType">Type of waitables to manage</typeparam>
/// <remarks>
/// <para>
/// This collection is exposed as a read-only collection to the user that
/// stores WeightedProgressions. Internally, it merely wraps a collection of
/// an internal type used to keep track of the individual progression's
/// progress in the SetProgression and QueueOperation classes.
/// stores WeightedWaitables. Internally, it merely wraps a collection of
/// an internal type used to keep track of the individual waitable's
/// progress in the WaitableSet and OperationQueue classes.
/// </para>
/// <para>
/// It is read-only because the design requires a progression to only ever
/// finish once. If it was possible eg. to add items after a SetProgression
/// It is read-only because the design requires a waitable to only ever
/// finish once. If it was possible eg. to add items after a WaitableSet
/// had signalled itself as being finished, it would be moved into an
/// unfinished state again. Also, an empty SetProgression is, by definition,
/// unfinished state again. Also, an empty WaitableSet is, by definition,
/// finished (simply because there is no work to do) - unless the contents
/// of set are passed to the SetProgression's constructor and never modified
/// of set are passed to the WaitableSet's constructor and never modified
/// at all, the design would be violated as soon as ab instance of the
/// SetProgression or QueueOperation classes was created.
/// WaitableSet or OperationQueue classes was created.
/// </para>
/// </remarks>
internal class WeightedProgressionWrapperCollection<ProgressionType> :
internal class WeightedWaitableWrapperCollection<WaitableType> :
TransformingReadOnlyCollection<
ObservedWeightedWaitable<ProgressionType>, WeightedWaitable<ProgressionType>
ObservedWeightedWaitable<WaitableType>, WeightedWaitable<WaitableType>
>
where ProgressionType : Waitable {
where WaitableType : Waitable {
/// <summary>Initializes a new weighted progression collection wrapper</summary>
/// <param name="items">Items to be exposed as weighted progressions</param>
internal WeightedProgressionWrapperCollection(
IList<ObservedWeightedWaitable<ProgressionType>> items
/// <summary>Initializes a new weighted waitable collection wrapper</summary>
/// <param name="items">Items to be exposed as weighted waitables</param>
internal WeightedWaitableWrapperCollection(
IList<ObservedWeightedWaitable<WaitableType>> items
)
: base(items) { }
@ -69,8 +69,8 @@ namespace Nuclex.Support.Tracking {
/// be called frequently, because the TransformingReadOnlyCollection does
/// not cache otherwise store the transformed items.
/// </remarks>
protected override WeightedWaitable<ProgressionType> Transform(
ObservedWeightedWaitable<ProgressionType> item
protected override WeightedWaitable<WaitableType> Transform(
ObservedWeightedWaitable<WaitableType> item
) {
return item.WeightedWaitable;
}

View File

@ -29,28 +29,28 @@ using NMock2;
namespace Nuclex.Support.Tracking {
/// <summary>Unit Test for the progression tracker class</summary>
/// <summary>Unit Test for the progress tracker class</summary>
[TestFixture]
public class ProgressionTrackerTest {
public class ProgressTrackerTest {
#region interface IProgressionTrackerSubscriber
#region interface IProgressTrackerSubscriber
/// <summary>Interface used to test the progression tracker.</summary>
public interface IProgressionTrackerSubscriber {
/// <summary>Interface used to test the progress tracker</summary>
public interface IProgressTrackerSubscriber {
/// <summary>Called when the progression tracker's progress changes</summary>
/// <param name="sender">Progression tracker whose progress has changed</param>
/// <summary>Called when the tracked progress changes</summary>
/// <param name="sender">Progress tracker whose progress has changed</param>
/// <param name="e">Contains the new progress achieved</param>
void ProgressChanged(object sender, ProgressReportEventArgs e);
/// <summary>Called when the progression tracker's idle state changes</summary>
/// <param name="sender">Progression tracker whose idle state has changed</param>
/// <summary>Called when the progress tracker's idle state changes</summary>
/// <param name="sender">Progress tracker whose idle state has changed</param>
/// <param name="e">Contains the new idle state of the tracker</param>
void IdleStateChanged(object sender, IdleStateEventArgs e);
}
#endregion // interface IProgressionTrackerSubscriber
#endregion // interface IProgressTrackerSubscriber
#region class ProgressUpdateEventArgsMatcher
@ -91,23 +91,21 @@ namespace Nuclex.Support.Tracking {
#endregion // class ProgressUpdateEventArgsMatcher
#region class TestProgression
#region class TestWaitable
/// <summary>Progression used for testing in this unit test</summary>
private class TestProgression : Waitable, IProgressReporter {
/// <summary>Waitable used for testing in this unit test</summary>
private class TestWaitable : Waitable, IProgressReporter {
/// <summary>will be triggered to report when progress has been achieved</summary>
public event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
/// <summary>Changes the testing progression's indicated progress</summary>
/// <param name="progress">
/// New progress to be reported by the testing progression
/// </param>
/// <summary>Changes the testing waitable's indicated progress</summary>
/// <param name="progress">New progress to be reported by the testing waitable</param>
public void ChangeProgress(float progress) {
OnAsyncProgressChanged(progress);
}
/// <summary>Transitions the progression into the ended state</summary>
/// <summary>Transitions the waitable into the ended state</summary>
public void End() {
OnAsyncEnded();
}
@ -115,7 +113,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>Fires the progress update event</summary>
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Informs the observers of this waitable about the achieved progress.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
@ -124,10 +122,10 @@ namespace Nuclex.Support.Tracking {
/// <summary>Fires the progress update event</summary>
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Allows for classes derived from the Progression class to easily provide
/// Informs the observers of this waitable about the achieved progress.
/// Allows for classes derived from the Waitable class to easily provide
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// waitable's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
@ -137,7 +135,7 @@ namespace Nuclex.Support.Tracking {
}
#endregion // class TestProgression
#endregion // class TestWiatable
/// <summary>Initialization routine executed before each test is run</summary>
[SetUp]
@ -150,7 +148,7 @@ namespace Nuclex.Support.Tracking {
public void TestSummedProgress() {
ProgressTracker tracker = new ProgressTracker();
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
IProgressTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
Expect.Once.On(mockedSubscriber).
Method("IdleStateChanged").
@ -165,9 +163,9 @@ namespace Nuclex.Support.Tracking {
}
);
TestProgression test1 = new TestProgression();
TestWaitable test1 = new TestWaitable();
tracker.Track(test1);
TestProgression test2 = new TestProgression();
TestWaitable test2 = new TestWaitable();
tracker.Track(test2);
Expect.Once.On(mockedSubscriber).
@ -185,18 +183,18 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>
/// Validates that the tracker only removes progressions when the whole
/// Validates that the tracker only removes waitables when the whole
/// tracking list has reached the 'ended' state.
/// </summary>
/// <remarks>
/// If the tracker would remove ended progressions right when they finished,
/// If the tracker would remove ended waitables right when they finished,
/// the total progress would jump back each time. This is unwanted, of course.
/// </remarks>
[Test]
public void TestDelayedRemoval() {
ProgressTracker tracker = new ProgressTracker();
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
IProgressTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
Expect.Once.On(mockedSubscriber).
Method("IdleStateChanged").
@ -211,9 +209,9 @@ namespace Nuclex.Support.Tracking {
}
);
TestProgression test1 = new TestProgression();
TestWaitable test1 = new TestWaitable();
tracker.Track(test1);
TestProgression test2 = new TestProgression();
TestWaitable test2 = new TestWaitable();
tracker.Track(test2);
Expect.Once.On(mockedSubscriber).
@ -236,8 +234,8 @@ namespace Nuclex.Support.Tracking {
}
);
// Total progress should be 0.75 after this call (one progression at 1.0,
// the other one at 0.5). If the second progression would be removed,
// Total progress should be 0.75 after this call (one waitable at 1.0,
// the other one at 0.5). If the second waitable would be removed,
// the progress would jump to 0.5 instead.
test2.End();
@ -260,14 +258,14 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>
/// Validates that the tracker behaves correctly if it is fed with progressions
/// Validates that the tracker behaves correctly if it is fed with waitables
/// that have already ended.
/// </summary>
[Test]
public void TestSoleEndedProgression() {
public void TestSoleEndedWaitable() {
ProgressTracker tracker = new ProgressTracker();
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
IProgressTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
tracker.Track(Waitable.EndedDummy);
@ -275,14 +273,14 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>
/// Validates that the tracker behaves correctly if it is fed with progressions
/// that have already ended in addition to progressions that are actively executing.
/// Validates that the tracker behaves correctly if it is fed with waitables
/// that have already ended in addition to waitables that are actively executing.
/// </summary>
[Test]
public void TestEndedProgression() {
public void TestEndedWaitable() {
ProgressTracker tracker = new ProgressTracker();
IProgressionTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
IProgressTrackerSubscriber mockedSubscriber = mockSubscriber(tracker);
Expect.Once.On(mockedSubscriber).
Method("IdleStateChanged").
@ -297,7 +295,7 @@ namespace Nuclex.Support.Tracking {
}
);
TestProgression test1 = new TestProgression();
TestWaitable test1 = new TestWaitable();
tracker.Track(test1);
Expect.Once.On(mockedSubscriber).
@ -337,7 +335,7 @@ namespace Nuclex.Support.Tracking {
public void TestProvokedDeadlock() {
ProgressTracker tracker = new ProgressTracker();
TestProgression test1 = new TestProgression();
TestWaitable test1 = new TestWaitable();
tracker.Track(test1);
tracker.AsyncIdleStateChanged +=
@ -351,9 +349,9 @@ namespace Nuclex.Support.Tracking {
/// <summary>Mocks a subscriber for the events of a tracker</summary>
/// <param name="tracker">Tracker to mock an event subscriber for</param>
/// <returns>The mocked event subscriber</returns>
private IProgressionTrackerSubscriber mockSubscriber(ProgressTracker tracker) {
IProgressionTrackerSubscriber mockedSubscriber =
this.mockery.NewMock<IProgressionTrackerSubscriber>();
private IProgressTrackerSubscriber mockSubscriber(ProgressTracker tracker) {
IProgressTrackerSubscriber mockedSubscriber =
this.mockery.NewMock<IProgressTrackerSubscriber>();
tracker.AsyncIdleStateChanged +=
new EventHandler<IdleStateEventArgs>(mockedSubscriber.IdleStateChanged);

View File

@ -23,18 +23,18 @@ using System.Collections.Generic;
namespace Nuclex.Support.Tracking {
/// <summary>Extended type of progression that is able to fail</summary>
/// <summary>Asynchronous request running in the background</summary>
/// <remarks>
/// <para>
/// If the background process fails, the exception that caused it to fail is
/// communicated to all parties waiting on the progression through the
/// Exception property. Implementers should place their code in try..catch
/// blocks and call SetException() to temporarily store the exception for
/// retrieval by the caller(s).
/// communicated to all parties waiting on the Request through the Join()
/// method. Implementers should store any errors occuring in the asynchronous
/// parts of their code in a try..catch block (or avoid throwing and just
/// store a new exception) and re-throw them when in ReraiseExceptions()
/// </para>
/// <para>
/// As with all progressions, the interface contract still requires you to call
/// OnAsyncEnded(), no matter what the outcome of your background operation is.
/// Like in the Waitable class, the contract requires you to always call
/// OnAsyncEnded(), no matter what the outcome of your operation is.
/// </para>
/// </remarks>
public abstract class Request : Waitable {
@ -65,6 +65,13 @@ namespace Nuclex.Support.Tracking {
#endregion // EndedDummyRequest
/// <summary>Succeeded dummy request</summary>
/// <remarks>
/// Use to indicate success if the request has already been completed at
/// the time you are asked to perform it.
/// </remarks>
public static readonly Request SucceededDummy = new EndedDummyRequest();
/// <summary>Creates a new failed dummy request</summary>
/// <param name="exception">Exception that supposedly caused the request to fail</param>
/// <returns>
@ -83,7 +90,7 @@ namespace Nuclex.Support.Tracking {
/// </remarks>
public virtual void Join() {
// If the progression itself hasn't ended yet, block the caller until it has.
// If the request itself hasn't ended yet, block the caller until it has.
// We could just use WaitHandle.WaitOne() here, but since the WaitHandle is created
// on-the-fly only when it is requested, we can avoid the WaitHandle creation in
// case the request is already finished!
@ -109,13 +116,36 @@ namespace Nuclex.Support.Tracking {
/// </typeparam>
public abstract class Request<ResultType> : Request {
#region class EndedDummyRequest
#region class SucceededDummyRequest
/// <summary>Dummy request that is always in the ended state</summary>
private class EndedDummyRequest : Request<ResultType> {
/// <summary>Succeeded dummy request that is always in the ended state</summary>
private class SucceededDummyRequest : Request<ResultType> {
/// <summary>Creates a new failed dummy request</summary>
/// <param name="result">Result to return to the request's caller</param>
public SucceededDummyRequest(ResultType result) {
this.result = result;
OnAsyncEnded();
}
/// <summary>
/// Allows the specific request implementation to re-throw an exception if
/// the background process finished unsuccessfully
/// </summary>
protected override ResultType GatherResults() {
return this.result;
}
/// <summary>Results the succeede dummy request will provide to the caller</summary>
private ResultType result;
}
#endregion // SucceededDummyRequest
#region class FailedDummyRequest
/// <summary>Failed dummy request that is always in the ended state</summary>
private class FailedDummyRequest : Request<ResultType> {
/// <summary>Creates a new failed dummy request</summary>
/// <param name="exception">Exception that caused the dummy to fail</param>
public EndedDummyRequest(Exception exception) {
public FailedDummyRequest(Exception exception) {
this.exception = exception;
OnAsyncEnded();
}
@ -130,15 +160,24 @@ namespace Nuclex.Support.Tracking {
private Exception exception;
}
#endregion // EndedDummyRequest
#endregion // FailedDummyRequest
/// <summary>Creates a new failed dummy request</summary>
/// <param name="result">Result to provide to the caller</param>
/// <returns>
/// A succeeded request that returns the provided result to the caller
/// </returns>
public static Request<ResultType> CreateSucceededDummy(ResultType result) {
return new SucceededDummyRequest(result);
}
/// <summary>Creates a new failed dummy request</summary>
/// <param name="exception">Exception that supposedly caused the request to fail</param>
/// <returns>
/// A failed request that reports the provided exception as cause for its failure
/// </returns>
public static new Request CreateFailedDummy(Exception exception) {
return new EndedDummyRequest(exception);
public static new Request<ResultType> CreateFailedDummy(Exception exception) {
return new FailedDummyRequest(exception);
}
/// <summary>Waits for the background operation to end</summary>

View File

@ -62,7 +62,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>A dummy waitable that's always in the 'ended' state</summary>
/// <remarks>
/// Useful if an operation is already complete when it's being asked for or
/// when a progression that's lazily created is accessed after the original
/// when a waitable that's lazily created is accessed after the original
/// operation has ended already.
/// </remarks>
public static readonly Waitable EndedDummy = new EndedDummyWaitable();
@ -186,14 +186,14 @@ namespace Nuclex.Support.Tracking {
/// </para>
/// <para>
/// Calling this method is mandatory. Implementers need to take care that
/// the OnAsyncEnded() method is called on any instance of Progression that's
/// the OnAsyncEnded() method is called on any instance of Waitable that's
/// being created. This method also must not be called more than once.
/// </para>
/// </remarks>
protected virtual void OnAsyncEnded() {
// Make sure the progression is not ended more than once. By guaranteeing that
// a progression can only be ended once, we allow users of this class to
// Make sure the waitable is not ended more than once. By guaranteeing that
// a waitable can only be ended once, we allow users of this class to
// skip some safeguards against notifications arriving twice.
lock(this) {
@ -202,7 +202,7 @@ namespace Nuclex.Support.Tracking {
// to waste any effort optimizing the speed at which an implementation fault
// will be noticed.
if(this.ended)
throw new InvalidOperationException("The progression has already been ended");
throw new InvalidOperationException("The Waitable has already been ended");
this.ended = true;
@ -236,7 +236,7 @@ namespace Nuclex.Support.Tracking {
protected volatile List<EventHandler> endedEventSubscribers;
/// <summary>Whether the operation has completed yet</summary>
protected volatile bool ended;
/// <summary>Event that will be set when the progression is completed</summary>
/// <summary>Event that will be set when the waitable is completed</summary>
/// <remarks>
/// This event is will only be created when it is specifically asked for using
/// the WaitHandle property.

View File

@ -29,28 +29,28 @@ using NMock2;
namespace Nuclex.Support.Tracking {
/// <summary>Unit Test for the progression set class</summary>
/// <summary>Unit Test for the waitable group class</summary>
[TestFixture]
public class SetProgressionTest {
public class WaitableGroupTest {
#region interface ISetProgressionSubscriber
#region interface IWaitableGroupSubscriber
/// <summary>Interface used to test the set progression.</summary>
public interface ISetProgressionSubscriber {
/// <summary>Interface used to test the set waitable.</summary>
public interface IWaitableGroupSubscriber {
/// <summary>Called when the set progression's progress changes</summary>
/// <param name="sender">Set progression whose progress has changed</param>
/// <summary>Called when the set waitable's progress changes</summary>
/// <param name="sender">Waitable group whose progress has changed</param>
/// <param name="e">Contains the new progress achieved</param>
void ProgressChanged(object sender, ProgressReportEventArgs e);
/// <summary>Called when the set progression has ended</summary>
/// <param name="sender">Set progression that as ended</param>
/// <summary>Called when the set waitable has ended</summary>
/// <param name="sender">Waitable group that as ended</param>
/// <param name="e">Not used</param>
void Ended(object sender, EventArgs e);
}
#endregion // interface ISetProgressionSubscriber
#endregion // interface IWaitableGroupSubscriber
#region class ProgressUpdateEventArgsMatcher
@ -93,21 +93,21 @@ namespace Nuclex.Support.Tracking {
#region class TestWaitable
/// <summary>Progression used for testing in this unit test</summary>
/// <summary>Waitable used for testing in this unit test</summary>
private class TestWaitable : Waitable, IProgressReporter {
/// <summary>will be triggered to report when progress has been achieved</summary>
public event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
/// <summary>Changes the testing progression's indicated progress</summary>
/// <summary>Changes the testing waitable's indicated progress</summary>
/// <param name="progress">
/// New progress to be reported by the testing progression
/// New progress to be reported by the testing waitable
/// </param>
public void ChangeProgress(float progress) {
OnAsyncProgressChanged(progress);
}
/// <summary>Transitions the progression into the ended state</summary>
/// <summary>Transitions the waitable into the ended state</summary>
public void End() {
OnAsyncEnded();
}
@ -115,7 +115,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>Fires the progress update event</summary>
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Informs the observers of this waitable about the achieved progress.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
@ -124,10 +124,10 @@ namespace Nuclex.Support.Tracking {
/// <summary>Fires the progress update event</summary>
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Allows for classes derived from the Progression class to easily provide
/// Informs the observers of this waitable about the achieved progress.
/// Allows for classes derived from the Waitable class to easily provide
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// Waitable's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
@ -145,79 +145,79 @@ namespace Nuclex.Support.Tracking {
this.mockery = new Mockery();
}
/// <summary>Validates that the set progression properly sums the progress</summary>
/// <summary>Validates that the set waitable properly sums the progress</summary>
[Test]
public void TestSummedProgress() {
SetProgression<TestWaitable> testSetProgression =
new SetProgression<TestWaitable>(
WaitableGroup<TestWaitable> testWaitableGroup =
new WaitableGroup<TestWaitable>(
new TestWaitable[] { new TestWaitable(), new TestWaitable() }
);
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
IWaitableGroupSubscriber mockedSubscriber = mockSubscriber(testWaitableGroup);
Expect.Once.On(mockedSubscriber).
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
new NMock2.Matchers.TypeMatcher(typeof(WaitableGroup<TestWaitable>)),
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
}
);
testSetProgression.Children[0].Waitable.ChangeProgress(0.5f);
testWaitableGroup.Children[0].Waitable.ChangeProgress(0.5f);
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Validates that the set progression respects the weights</summary>
/// <summary>Validates that the waitable group respects the weights</summary>
[Test]
public void TestWeightedSummedProgress() {
SetProgression<TestWaitable> testSetProgression =
new SetProgression<TestWaitable>(
WaitableGroup<TestWaitable> testWaitableGroup =
new WaitableGroup<TestWaitable>(
new WeightedWaitable<TestWaitable>[] {
new WeightedWaitable<TestWaitable>(new TestWaitable(), 1.0f),
new WeightedWaitable<TestWaitable>(new TestWaitable(), 2.0f)
}
);
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
IWaitableGroupSubscriber mockedSubscriber = mockSubscriber(testWaitableGroup);
Expect.Once.On(mockedSubscriber).
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
new NMock2.Matchers.TypeMatcher(typeof(WaitableGroup<TestWaitable>)),
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f / 3.0f))
}
);
testSetProgression.Children[0].Waitable.ChangeProgress(0.5f);
testWaitableGroup.Children[0].Waitable.ChangeProgress(0.5f);
Expect.Once.On(mockedSubscriber).
Method("ProgressChanged").
With(
new Matcher[] {
new NMock2.Matchers.TypeMatcher(typeof(SetProgression<TestWaitable>)),
new NMock2.Matchers.TypeMatcher(typeof(WaitableGroup<TestWaitable>)),
new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
}
);
testSetProgression.Children[1].Waitable.ChangeProgress(0.5f);
testWaitableGroup.Children[1].Waitable.ChangeProgress(0.5f);
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>
/// Validates that the ended event is triggered when the last progression ends
/// Validates that the ended event is triggered when the last waitable ends
/// </summary>
[Test]
public void TestEndedEvent() {
SetProgression<TestWaitable> testSetProgression =
new SetProgression<TestWaitable>(
WaitableGroup<TestWaitable> testWaitableGroup =
new WaitableGroup<TestWaitable>(
new TestWaitable[] { new TestWaitable(), new TestWaitable() }
);
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
IWaitableGroupSubscriber mockedSubscriber = mockSubscriber(testWaitableGroup);
Expect.Exactly(2).On(mockedSubscriber).
Method("ProgressChanged").
@ -227,21 +227,21 @@ namespace Nuclex.Support.Tracking {
Method("Ended").
WithAnyArguments();
testSetProgression.Children[0].Waitable.End();
testSetProgression.Children[1].Waitable.End();
testWaitableGroup.Children[0].Waitable.End();
testWaitableGroup.Children[1].Waitable.End();
this.mockery.VerifyAllExpectationsHaveBeenMet();
}
/// <summary>Mocks a subscriber for the events of a progression</summary>
/// <param name="progression">Progression to mock an event subscriber for</param>
/// <summary>Mocks a subscriber for the events of a waitable</summary>
/// <param name="waitable">Waitable to mock an event subscriber for</param>
/// <returns>The mocked event subscriber</returns>
private ISetProgressionSubscriber mockSubscriber(Waitable progression) {
ISetProgressionSubscriber mockedSubscriber =
this.mockery.NewMock<ISetProgressionSubscriber>();
private IWaitableGroupSubscriber mockSubscriber(Waitable waitable) {
IWaitableGroupSubscriber mockedSubscriber =
this.mockery.NewMock<IWaitableGroupSubscriber>();
progression.AsyncEnded += new EventHandler(mockedSubscriber.Ended);
(progression as IProgressReporter).AsyncProgressChanged +=
waitable.AsyncEnded += new EventHandler(mockedSubscriber.Ended);
(waitable as IProgressReporter).AsyncProgressChanged +=
new EventHandler<ProgressReportEventArgs>(mockedSubscriber.ProgressChanged);
return mockedSubscriber;

View File

@ -26,66 +26,66 @@ using Nuclex.Support.Collections;
namespace Nuclex.Support.Tracking {
/// <summary>Forms a single progression from a set of progressions</summary>
/// <typeparam name="ProgressionType">Type of progressions to manage as a set</typeparam>
public class SetProgression<ProgressionType> : Waitable, IDisposable, IProgressReporter
where ProgressionType : Waitable {
/// <summary>Forms a single waitable from a group of waitables</summary>
/// <typeparam name="WaitableType">Type of waitables to manage as a set</typeparam>
public class WaitableGroup<WaitableType> : Waitable, IDisposable, IProgressReporter
where WaitableType : Waitable {
/// <summary>will be triggered to report when progress has been achieved</summary>
public event EventHandler<ProgressReportEventArgs> AsyncProgressChanged;
/// <summary>Initializes a new set progression</summary>
/// <param name="childs">Progressions to track with this set</param>
/// <summary>Initializes a new waitable group</summary>
/// <param name="childs">Waitables to track with this group</param>
/// <remarks>
/// Uses a default weighting factor of 1.0 for all progressions.
/// Uses a default weighting factor of 1.0 for all waitables.
/// </remarks>
public SetProgression(IEnumerable<ProgressionType> childs)
public WaitableGroup(IEnumerable<WaitableType> childs)
: this() {
// Construct a WeightedProgression with the default weight for each
// progression and wrap it in an ObservedProgression
foreach(ProgressionType progression in childs) {
// Construct a WeightedWaitable with the default weight for each
// waitable and wrap it in an ObservedWaitable
foreach(WaitableType waitable in childs) {
this.children.Add(
new ObservedWeightedWaitable<ProgressionType>(
new WeightedWaitable<ProgressionType>(progression),
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncProgressUpdated),
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncEnded)
new ObservedWeightedWaitable<WaitableType>(
new WeightedWaitable<WaitableType>(waitable),
new ObservedWeightedWaitable<WaitableType>.ReportDelegate(asyncProgressUpdated),
new ObservedWeightedWaitable<WaitableType>.ReportDelegate(asyncChildEnded)
)
);
}
// Since all progressions have a weight of 1.0, the total weight is
// equal to the number of progressions in our list
// Since all waitables have a weight of 1.0, the total weight is
// equal to the number of waitables in our list
this.totalWeight = (float)this.children.Count;
}
/// <summary>Initializes a new set progression</summary>
/// <param name="childs">Progressions to track with this set</param>
public SetProgression(
IEnumerable<WeightedWaitable<ProgressionType>> childs
/// <summary>Initializes a new waitable group</summary>
/// <param name="childs">Waitables to track with this group</param>
public WaitableGroup(
IEnumerable<WeightedWaitable<WaitableType>> childs
)
: this() {
// Construct an ObservedProgression around each of the WeightedProgressions
foreach(WeightedWaitable<ProgressionType> progression in childs) {
// Construct an ObservedWaitable around each of the WeightedWaitables
foreach(WeightedWaitable<WaitableType> waitable in childs) {
this.children.Add(
new ObservedWeightedWaitable<ProgressionType>(
progression,
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncProgressUpdated),
new ObservedWeightedWaitable<ProgressionType>.ReportDelegate(asyncEnded)
new ObservedWeightedWaitable<WaitableType>(
waitable,
new ObservedWeightedWaitable<WaitableType>.ReportDelegate(asyncProgressUpdated),
new ObservedWeightedWaitable<WaitableType>.ReportDelegate(asyncChildEnded)
)
);
// Sum up the total weight
this.totalWeight += progression.Weight;
this.totalWeight += waitable.Weight;
}
}
/// <summary>Performs common initialization for the public constructors</summary>
private SetProgression() {
this.children = new List<ObservedWeightedWaitable<ProgressionType>>();
private WaitableGroup() {
this.children = new List<ObservedWeightedWaitable<WaitableType>>();
}
/// <summary>Immediately releases all resources owned by the object</summary>
@ -93,8 +93,8 @@ namespace Nuclex.Support.Tracking {
if(this.children != null) {
// Dispose all the observed progressions, disconnecting the events from the
// actual progressions so the GC can more easily collect this class
// Dispose all the observed waitables, disconnecting the events from the
// actual waitables so the GC can more easily collect this class
for(int index = 0; index < this.children.Count; ++index)
this.children[index].Dispose();
@ -105,18 +105,18 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>Childs contained in the progression set</summary>
public IList<WeightedWaitable<ProgressionType>> Children {
/// <summary>Childs contained in the waitable set</summary>
public IList<WeightedWaitable<WaitableType>> Children {
get {
// The wrapper is constructed only when needed. Most of the time, users will
// just create a SetProgression and monitor its progress without ever using
// just create a waitable group and monitor its progress without ever using
// the Childs collection.
if(this.wrapper == null) {
// This doesn't need a lock because it's a stateless wrapper.
// If it is constructed twice, then so be it, no problem at all.
this.wrapper = new WeightedProgressionWrapperCollection<ProgressionType>(
this.wrapper = new WeightedWaitableWrapperCollection<WaitableType>(
this.children
);
@ -130,7 +130,7 @@ namespace Nuclex.Support.Tracking {
/// <summary>Fires the progress update event</summary>
/// <param name="progress">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Informs the observers of this waitables about the achieved progress.
/// </remarks>
protected virtual void OnAsyncProgressChanged(float progress) {
OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
@ -139,10 +139,10 @@ namespace Nuclex.Support.Tracking {
/// <summary>Fires the progress update event</summary>
/// <param name="eventArguments">Progress to report (ranging from 0.0 to 1.0)</param>
/// <remarks>
/// Informs the observers of this progression about the achieved progress.
/// Allows for classes derived from the Progression class to easily provide
/// Informs the observers of this waitable about the achieved progress.
/// Allows for classes derived from the Waitable class to easily provide
/// a custom event arguments class that has been derived from the
/// Progression's ProgressUpdateEventArgs class.
/// waitable's ProgressUpdateEventArgs class.
/// </remarks>
protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
EventHandler<ProgressReportEventArgs> copy = AsyncProgressChanged;
@ -151,13 +151,13 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>
/// Called when the progress of one of the observed progressions changes
/// Called when the progress of one of the observed waitables changes
/// </summary>
private void asyncProgressUpdated() {
float totalProgress = 0.0f;
// Calculate the sum of the progress reported by our child progressions,
// scaled to the weight each progression has assigned to it.
// Calculate the sum of the progress reported by our child waitables,
// scaled to the weight each waitable has assigned to it.
for(int index = 0; index < this.children.Count; ++index) {
totalProgress +=
this.children[index].Progress * this.children[index].WeightedWaitable.Weight;
@ -172,29 +172,29 @@ namespace Nuclex.Support.Tracking {
}
/// <summary>
/// Called when an observed progression ends
/// Called when an observed waitable ends
/// </summary>
private void asyncEnded() {
private void asyncChildEnded() {
// If there's still at least one progression going, don't report that
// the SetProgression has finished yet.
// If there's still at least one waitable going, don't report that
// the waitable group has finished yet.
for(int index = 0; index < this.children.Count; ++index)
if(!this.children[index].WeightedWaitable.Waitable.Ended)
return;
// All child progressions have ended, so the set has now ended as well
// All child waitables have ended, so the set has now ended as well
OnAsyncEnded();
}
/// <summary>Progressions being managed in the set</summary>
private List<ObservedWeightedWaitable<ProgressionType>> children;
/// <summary>Waitables being managed in the set</summary>
private List<ObservedWeightedWaitable<WaitableType>> children;
/// <summary>
/// Wrapper collection for exposing the child progressions under the
/// WeightedProgression interface
/// Wrapper collection for exposing the child waitables under the
/// WeightedWaitable interface
/// </summary>
private volatile WeightedProgressionWrapperCollection<ProgressionType> wrapper;
/// <summary>Summed weight of all progressions in the set</summary>
private volatile WeightedWaitableWrapperCollection<WaitableType> wrapper;
/// <summary>Summed weight of all waitables in the set</summary>
private float totalWeight;
}

View File

@ -23,36 +23,36 @@ using System.Collections.Generic;
namespace Nuclex.Support.Tracking {
/// <summary>Progression with an associated weight for the total progress</summary>
public class WeightedWaitable<ProgressionType> where ProgressionType : Waitable {
/// <summary>Waitable with an associated weight for the total progress</summary>
public class WeightedWaitable<WaitableType> where WaitableType : Waitable {
/// <summary>
/// Initializes a new weighted progression with a default weight of 1.0
/// Initializes a new weighted waitable with a default weight of 1.0
/// </summary>
/// <param name="progression">Progression whose progress to monitor</param>
public WeightedWaitable(ProgressionType progression) : this(progression, 1.0f) { }
/// <param name="waitable">Waitable whose progress to monitor</param>
public WeightedWaitable(WaitableType waitable) : this(waitable, 1.0f) { }
/// <summary>Initializes a new weighted progression</summary>
/// <param name="progression">Progression whose progress to monitor</param>
/// <param name="weight">Weighting of the progression's progress</param>
public WeightedWaitable(ProgressionType progression, float weight) {
this.progression = progression;
/// <summary>Initializes a new weighted waitable</summary>
/// <param name="waitable">Waitable whose progress to monitor</param>
/// <param name="weight">Weighting of the waitable's progress</param>
public WeightedWaitable(WaitableType waitable, float weight) {
this.waitable = waitable;
this.weight = weight;
}
/// <summary>Progression being wrapped by this weighted progression</summary>
public ProgressionType Waitable {
get { return this.progression; }
/// <summary>Waitable being wrapped by this weighted waitable</summary>
public WaitableType Waitable {
get { return this.waitable; }
}
/// <summary>The contribution of this progression to the total progress</summary>
/// <summary>The contribution of this waitable to the total progress</summary>
public float Weight {
get { return this.weight; }
}
/// <summary>Progression whose progress we're tracking</summary>
private ProgressionType progression;
/// <summary>Weighting of this progression in the total progress</summary>
/// <summary>Waitable whose progress we're tracking</summary>
private WaitableType waitable;
/// <summary>Weighting of this waitable in the total progress</summary>
private float weight;
}