SetProgression fully working and tested, yay!; remedied a little imperfection that would theoretically allow progressions to trigger the end even twice
git-svn-id: file:///srv/devel/repo-conversion/nusu@13 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
71b6dc90a2
commit
0008304d64
|
@ -40,7 +40,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
|
|
||||||
/// <summary>Immediately releases all resources owned by the object</summary>
|
/// <summary>Immediately releases all resources owned by the object</summary>
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
disconnectEvents();
|
asyncDisconnectEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Weighted progression being observed</summary>
|
/// <summary>Weighted progression being observed</summary>
|
||||||
|
@ -57,11 +57,15 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// <param name="sender">Progression that has ended</param>
|
/// <param name="sender">Progression that has ended</param>
|
||||||
/// <param name="e">Not used</param>
|
/// <param name="e">Not used</param>
|
||||||
private void asyncEnded(object sender, EventArgs e) {
|
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;
|
this.progress = 1.0f;
|
||||||
|
progressUpdateCallback();
|
||||||
|
|
||||||
disconnectEvents(); // We don't need those anymore!
|
endedCallback();
|
||||||
|
|
||||||
this.progressUpdateCallback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Called when the progress of the observed progression changes</summary>
|
/// <summary>Called when the progress of the observed progression changes</summary>
|
||||||
|
@ -74,7 +78,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Unscribes from all events of the observed progression</summary>
|
/// <summary>Unscribes from all events of the observed progression</summary>
|
||||||
private void disconnectEvents() {
|
private void asyncDisconnectEvents() {
|
||||||
|
|
||||||
// Make use of the double check locking idiom to avoid the costly lock when
|
// Make use of the double check locking idiom to avoid the costly lock when
|
||||||
// the events have already been unsubscribed
|
// the events have already been unsubscribed
|
||||||
|
|
|
@ -113,15 +113,18 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// seperately.
|
/// seperately.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected virtual void OnAsyncEnded() {
|
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!
|
if(this.ended)
|
||||||
// We do not lock here since we require this method to be called only once
|
throw new InvalidOperationException("The progression has already been ended");
|
||||||
// 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");
|
|
||||||
|
|
||||||
this.ended = true;
|
this.ended = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Doesn't need a lock. If another thread wins the race and creates the event
|
// 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
|
// after we just saw it being null, it would be created in an already set
|
||||||
|
|
|
@ -104,9 +104,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
public void TestSummedProgress() {
|
public void TestSummedProgress() {
|
||||||
SetProgression<TestProgression> testSetProgression =
|
SetProgression<TestProgression> testSetProgression =
|
||||||
new SetProgression<TestProgression>(
|
new SetProgression<TestProgression>(
|
||||||
new TestProgression[] {
|
new TestProgression[] { new TestProgression(), new TestProgression() }
|
||||||
new TestProgression(), new TestProgression()
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
|
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
|
||||||
|
@ -184,9 +182,7 @@ namespace Nuclex.Support.Tracking {
|
||||||
public void TestEndedEvent() {
|
public void TestEndedEvent() {
|
||||||
SetProgression<TestProgression> testSetProgression =
|
SetProgression<TestProgression> testSetProgression =
|
||||||
new SetProgression<TestProgression>(
|
new SetProgression<TestProgression>(
|
||||||
new TestProgression[] {
|
new TestProgression[] { new TestProgression(), new TestProgression() }
|
||||||
new TestProgression(), new TestProgression()
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
|
ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
|
||||||
|
|
|
@ -8,14 +8,16 @@ namespace Nuclex.Support.Tracking {
|
||||||
|
|
||||||
/// <summary>Forms a single progression from a set of progressions</summary>
|
/// <summary>Forms a single progression from a set of progressions</summary>
|
||||||
/// <typeparam name="ProgressionType">Type of progressions to manage as a set</typeparam>
|
/// <typeparam name="ProgressionType">Type of progressions to manage as a set</typeparam>
|
||||||
public class SetProgression<ProgressionType> : Progression
|
public class SetProgression<ProgressionType> : Progression, IDisposable
|
||||||
where ProgressionType : Progression {
|
where ProgressionType : Progression {
|
||||||
|
|
||||||
/// <summary>Performs common initialization for the public constructors</summary>
|
/// <summary>Performs common initialization for the public constructors</summary>
|
||||||
private SetProgression() {
|
private SetProgression() {
|
||||||
this.childs = new List<ObservedProgression<ProgressionType>>();
|
this.childs = new List<ObservedProgression<ProgressionType>>();
|
||||||
|
|
||||||
this.asyncProgressUpdatedDelegate =
|
this.asyncProgressUpdatedDelegate =
|
||||||
new ObservedProgression<ProgressionType>.ReportDelegate(asyncProgressUpdated);
|
new ObservedProgression<ProgressionType>.ReportDelegate(asyncProgressUpdated);
|
||||||
|
|
||||||
this.asyncEndedDelegate =
|
this.asyncEndedDelegate =
|
||||||
new ObservedProgression<ProgressionType>.ReportDelegate(asyncEnded);
|
new ObservedProgression<ProgressionType>.ReportDelegate(asyncEnded);
|
||||||
}
|
}
|
||||||
|
@ -71,6 +73,23 @@ namespace Nuclex.Support.Tracking {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Immediately releases all resources owned by the object</summary>
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Childs contained in the progression set</summary>
|
/// <summary>Childs contained in the progression set</summary>
|
||||||
public IList<WeightedProgression<ProgressionType>> Childs {
|
public IList<WeightedProgression<ProgressionType>> Childs {
|
||||||
get {
|
get {
|
||||||
|
@ -120,10 +139,13 @@ namespace Nuclex.Support.Tracking {
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void asyncEnded() {
|
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)
|
for(int index = 0; index < this.childs.Count; ++index)
|
||||||
if(!this.childs[index].WeightedProgression.Progression.Ended)
|
if(!this.childs[index].WeightedProgression.Progression.Ended)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// All child progressions have ended, so the set has now ended as well
|
||||||
OnAsyncEnded();
|
OnAsyncEnded();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user