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
					
				
					 4 changed files with 44 additions and 19 deletions
				
			
		| 
						 | 
				
			
			@ -40,7 +40,7 @@ namespace Nuclex.Support.Tracking {
 | 
			
		|||
 | 
			
		||||
    /// <summary>Immediately releases all resources owned by the object</summary>
 | 
			
		||||
    public void Dispose() {
 | 
			
		||||
      disconnectEvents();
 | 
			
		||||
      asyncDisconnectEvents();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>Weighted progression being observed</summary>
 | 
			
		||||
| 
						 | 
				
			
			@ -57,11 +57,15 @@ namespace Nuclex.Support.Tracking {
 | 
			
		|||
    /// <param name="sender">Progression that has ended</param>
 | 
			
		||||
    /// <param name="e">Not used</param>
 | 
			
		||||
    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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <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>
 | 
			
		||||
    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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,14 +114,17 @@ namespace Nuclex.Support.Tracking {
 | 
			
		|||
    /// </remarks>
 | 
			
		||||
    protected virtual void OnAsyncEnded() {
 | 
			
		||||
 
 | 
			
		||||
      // 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");
 | 
			
		||||
      // 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) {
 | 
			
		||||
 | 
			
		||||
      this.ended = true;
 | 
			
		||||
        if(this.ended)
 | 
			
		||||
          throw new InvalidOperationException("The progression has already been ended");
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,9 +104,7 @@ namespace Nuclex.Support.Tracking {
 | 
			
		|||
    public void TestSummedProgress() {
 | 
			
		||||
      SetProgression<TestProgression> testSetProgression =
 | 
			
		||||
        new SetProgression<TestProgression>(
 | 
			
		||||
          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<TestProgression> testSetProgression =
 | 
			
		||||
        new SetProgression<TestProgression>(
 | 
			
		||||
          new TestProgression[] {
 | 
			
		||||
            new TestProgression(), new TestProgression()
 | 
			
		||||
          }
 | 
			
		||||
          new TestProgression[] { new TestProgression(), new TestProgression() }
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
      ISetProgressionSubscriber mockedSubscriber = mockSubscriber(testSetProgression);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,14 +8,16 @@ 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> : Progression
 | 
			
		||||
  public class SetProgression<ProgressionType> : Progression, IDisposable
 | 
			
		||||
    where ProgressionType : Progression {
 | 
			
		||||
 | 
			
		||||
    /// <summary>Performs common initialization for the public constructors</summary>
 | 
			
		||||
    private SetProgression() {
 | 
			
		||||
      this.childs = new List<ObservedProgression<ProgressionType>>();
 | 
			
		||||
 | 
			
		||||
      this.asyncProgressUpdatedDelegate =
 | 
			
		||||
        new ObservedProgression<ProgressionType>.ReportDelegate(asyncProgressUpdated);
 | 
			
		||||
 | 
			
		||||
      this.asyncEndedDelegate =
 | 
			
		||||
        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>
 | 
			
		||||
    public IList<WeightedProgression<ProgressionType>> Childs {
 | 
			
		||||
      get {
 | 
			
		||||
| 
						 | 
				
			
			@ -120,10 +139,13 @@ namespace Nuclex.Support.Tracking {
 | 
			
		|||
    /// </summary>
 | 
			
		||||
    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();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue