diff --git a/Source/Tracking/Internal/ObservedProgression.cs b/Source/Tracking/Internal/ObservedProgression.cs
index aab0d8d..2906387 100644
--- a/Source/Tracking/Internal/ObservedProgression.cs
+++ b/Source/Tracking/Internal/ObservedProgression.cs
@@ -40,7 +40,7 @@ namespace Nuclex.Support.Tracking {
/// Immediately releases all resources owned by the object
public void Dispose() {
- disconnectEvents();
+ asyncDisconnectEvents();
}
/// Weighted progression being observed
@@ -57,11 +57,15 @@ namespace Nuclex.Support.Tracking {
/// Progression that has ended
/// Not used
private void asyncEnded(object sender, EventArgs e) {
+ ReportDelegate endedCallback = this.endedCallback;
+ ReportDelegate progressUpdateCallback = this.progressUpdateCallback;
+
+ asyncDisconnectEvents(); // We don't need those anymore!
+
this.progress = 1.0f;
+ progressUpdateCallback();
- disconnectEvents(); // We don't need those anymore!
-
- this.progressUpdateCallback();
+ endedCallback();
}
/// Called when the progress of the observed progression changes
@@ -74,7 +78,7 @@ namespace Nuclex.Support.Tracking {
}
/// Unscribes from all events of the observed progression
- private void disconnectEvents() {
+ private void asyncDisconnectEvents() {
// Make use of the double check locking idiom to avoid the costly lock when
// the events have already been unsubscribed
diff --git a/Source/Tracking/Progression.cs b/Source/Tracking/Progression.cs
index 05a20cd..b8c24c0 100644
--- a/Source/Tracking/Progression.cs
+++ b/Source/Tracking/Progression.cs
@@ -113,15 +113,18 @@ namespace Nuclex.Support.Tracking {
/// seperately.
///
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
+ // skip some safeguards against notifications arriving twice.
+ lock(this.syncRoot) {
- // TODO: Find a way around this. Interlocked.Exchange would be best!
- // We do not lock here since we require this method to be called only once
- // in the object's lifetime. If someone really badly wanted to break this
- // he'd probably have a one-in-a-million chance of getting through.
- if(this.ended)
- throw new InvalidOperationException("The progression has already been ended");
+ if(this.ended)
+ throw new InvalidOperationException("The progression has already been ended");
- this.ended = true;
+ this.ended = true;
+
+ }
// Doesn't need a lock. If another thread wins the race and creates the event
// after we just saw it being null, it would be created in an already set
diff --git a/Source/Tracking/SetProgression.Test.cs b/Source/Tracking/SetProgression.Test.cs
index f39658f..2d014f7 100644
--- a/Source/Tracking/SetProgression.Test.cs
+++ b/Source/Tracking/SetProgression.Test.cs
@@ -104,9 +104,7 @@ namespace Nuclex.Support.Tracking {
public void TestSummedProgress() {
SetProgression testSetProgression =
new SetProgression(
- new TestProgression[] {
- new TestProgression(), new TestProgression()
- }
+ new TestProgression[] { new TestProgression(), new TestProgression() }
);
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
@@ -184,9 +182,7 @@ namespace Nuclex.Support.Tracking {
public void TestEndedEvent() {
SetProgression testSetProgression =
new SetProgression(
- new TestProgression[] {
- new TestProgression(), new TestProgression()
- }
+ new TestProgression[] { new TestProgression(), new TestProgression() }
);
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
diff --git a/Source/Tracking/SetProgression.cs b/Source/Tracking/SetProgression.cs
index c2ce6da..bc0f2c9 100644
--- a/Source/Tracking/SetProgression.cs
+++ b/Source/Tracking/SetProgression.cs
@@ -8,14 +8,16 @@ namespace Nuclex.Support.Tracking {
/// Forms a single progression from a set of progressions
/// Type of progressions to manage as a set
- public class SetProgression : Progression
+ public class SetProgression : Progression, IDisposable
where ProgressionType : Progression {
/// Performs common initialization for the public constructors
private SetProgression() {
this.childs = new List>();
+
this.asyncProgressUpdatedDelegate =
new ObservedProgression.ReportDelegate(asyncProgressUpdated);
+
this.asyncEndedDelegate =
new ObservedProgression.ReportDelegate(asyncEnded);
}
@@ -71,6 +73,23 @@ namespace Nuclex.Support.Tracking {
}
+ /// Immediately releases all resources owned by the object
+ public void Dispose() {
+
+ if(this.childs != null) {
+
+ // Dispose all the observed progressions, disconnecting the events from the
+ // actual progressions so the GC can more easily collect this class
+ for(int index = 0; index < this.childs.Count; ++index)
+ this.childs[index].Dispose();
+
+ this.childs = null;
+ this.wrapper = null;
+
+ }
+
+ }
+
/// Childs contained in the progression set
public IList> Childs {
get {
@@ -120,10 +139,13 @@ namespace Nuclex.Support.Tracking {
///
private void asyncEnded() {
+ // If there's still at least one progression going, don't report that
+ // the SetProgression has finished yet.
for(int index = 0; index < this.childs.Count; ++index)
if(!this.childs[index].WeightedProgression.Progression.Ended)
return;
+ // All child progressions have ended, so the set has now ended as well
OnAsyncEnded();
}