Renamed Waitable to Transaction (Progression -> Waitable -> Transaction -- might it be that this concept is no crispy enough and should be revisited? I think so!); increased test coverage for lots of classes in the tracking namespace, all but 3 are now 100% covered; eliminated redundant ProgressUpdate events from the ProgressTracker and adjusted unit tests so progress update events that re-report the current progress are optional; added a new idea for a replacement of the broken (quality-wise, at least) command line parser
git-svn-id: file:///srv/devel/repo-conversion/nusu@100 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
b39f8de155
commit
8c5f2d45f7
25 changed files with 1867 additions and 1079 deletions
158
Source/Tracking/Internal/ObservedWeightedTransaction.Test.cs
Normal file
158
Source/Tracking/Internal/ObservedWeightedTransaction.Test.cs
Normal file
|
@ -0,0 +1,158 @@
|
|||
#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.IO;
|
||||
using System.Threading;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.SyntaxHelpers;
|
||||
using NMock2;
|
||||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Unit Test for the observation wrapper of weighted transactions</summary>
|
||||
[TestFixture]
|
||||
public class ObservedWeightedTransactionTest {
|
||||
|
||||
#region interface IObservationSubscriber
|
||||
|
||||
/// <summary>
|
||||
/// Interface used to test the observation wrapper of weighted transactions
|
||||
/// </summary>
|
||||
public interface IObservationSubscriber {
|
||||
|
||||
/// <summary>Will be invoked when an observed transaction's progress changes</summary>
|
||||
void ProgressUpdated();
|
||||
|
||||
/// <summary>Will be invoked when an observed transaction completes</summary>
|
||||
void Ended();
|
||||
|
||||
}
|
||||
|
||||
#endregion // interface IObservationSubscriber
|
||||
|
||||
#region class FunkyTransaction
|
||||
|
||||
/// <summary>
|
||||
/// Transaction that goes into the 'ended' state as soon as someone registers for
|
||||
/// state change notifications
|
||||
/// </summary>
|
||||
private class FunkyTransaction : Transaction {
|
||||
|
||||
/// <summary>Manages registrations to the AsyncEnded event</summary>
|
||||
public override event EventHandler AsyncEnded {
|
||||
add {
|
||||
base.AsyncEnded += value;
|
||||
|
||||
// To deterministically provoke an 'Ended' event just after registration we
|
||||
// will switch the transaction into the 'ended' state right here
|
||||
int oldValue = Interlocked.Exchange(ref this.alreadyEnded, 1);
|
||||
if(oldValue != 1) {
|
||||
OnAsyncEnded();
|
||||
}
|
||||
}
|
||||
remove {
|
||||
base.AsyncEnded -= value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Whether the transaction has already been ended</summary>
|
||||
private int alreadyEnded;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class FunkyTransaction
|
||||
|
||||
/// <summary>Initialization routine executed before each test is run</summary>
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
this.mockery = new Mockery();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the constructor of the observation wrapper works</summary>
|
||||
[Test]
|
||||
public void TestConstructorWithAlreadyEndedTransaction() {
|
||||
WeightedTransaction<Transaction> testTransaction = new WeightedTransaction<Transaction>(
|
||||
Transaction.EndedDummy
|
||||
);
|
||||
|
||||
IObservationSubscriber subscriber = this.mockery.NewMock<IObservationSubscriber>();
|
||||
|
||||
Expect.AtLeast(0).On(subscriber).Method("ProgressUpdated");
|
||||
Expect.Once.On(subscriber).Method("Ended");
|
||||
|
||||
using(
|
||||
ObservedWeightedTransaction<Transaction> test =
|
||||
new ObservedWeightedTransaction<Transaction>(
|
||||
testTransaction,
|
||||
new ObservedWeightedTransaction<Transaction>.ReportDelegate(
|
||||
subscriber.ProgressUpdated
|
||||
),
|
||||
new ObservedWeightedTransaction<Transaction>.ReportDelegate(
|
||||
subscriber.Ended
|
||||
)
|
||||
)
|
||||
) {
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the constructor of the observation wrapper can handle a transaction
|
||||
/// entering the 'ended' state right on subscription
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestConstructorWithEndingTransaction() {
|
||||
WeightedTransaction<Transaction> testTransaction = new WeightedTransaction<Transaction>(
|
||||
new FunkyTransaction()
|
||||
);
|
||||
|
||||
IObservationSubscriber subscriber = this.mockery.NewMock<IObservationSubscriber>();
|
||||
|
||||
Expect.AtLeast(0).On(subscriber).Method("ProgressUpdated");
|
||||
Expect.Once.On(subscriber).Method("Ended");
|
||||
|
||||
using(
|
||||
ObservedWeightedTransaction<Transaction> test =
|
||||
new ObservedWeightedTransaction<Transaction>(
|
||||
testTransaction,
|
||||
new ObservedWeightedTransaction<Transaction>.ReportDelegate(
|
||||
subscriber.ProgressUpdated
|
||||
),
|
||||
new ObservedWeightedTransaction<Transaction>.ReportDelegate(
|
||||
subscriber.Ended
|
||||
)
|
||||
)
|
||||
) {
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Mock object factory</summary>
|
||||
private Mockery mockery;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Tracking
|
||||
|
||||
#endif // UNITTEST
|
177
Source/Tracking/Internal/ObservedWeightedTransaction.cs
Normal file
177
Source/Tracking/Internal/ObservedWeightedTransaction.cs
Normal file
|
@ -0,0 +1,177 @@
|
|||
#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;
|
||||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Transaction being observed by another object</summary>
|
||||
/// <typeparam name="TransactionType">
|
||||
/// Type of the transaction that is being observed
|
||||
/// </typeparam>
|
||||
internal class ObservedWeightedTransaction<TransactionType> : IDisposable
|
||||
where TransactionType : Transaction {
|
||||
|
||||
/// <summary>Delegate for reporting progress updates</summary>
|
||||
public delegate void ReportDelegate();
|
||||
|
||||
/// <summary>Initializes a new observed transaction</summary>
|
||||
/// <param name="weightedTransaction">Weighted transaction being observed</param>
|
||||
/// <param name="progressUpdateCallback">
|
||||
/// Callback to invoke when the transaction's progress changes
|
||||
/// </param>
|
||||
/// <param name="endedCallback">
|
||||
/// Callback to invoke when the transaction has ended
|
||||
/// </param>
|
||||
internal ObservedWeightedTransaction(
|
||||
WeightedTransaction<TransactionType> weightedTransaction,
|
||||
ReportDelegate progressUpdateCallback,
|
||||
ReportDelegate endedCallback
|
||||
) {
|
||||
this.weightedTransaction = weightedTransaction;
|
||||
|
||||
// See if this transaction has already ended (initial check for performance)
|
||||
if(weightedTransaction.Transaction.Ended) {
|
||||
|
||||
// Since we don't subscribe to the .Ended event (which would be fired immediately on
|
||||
// subscription if the transaction was already finished), we will emulate this
|
||||
// behavior here. There is no race condition here: The transition to .Ended occurs
|
||||
// only once and will never happen in reverse. This is just a minor optimization to
|
||||
// prevent object coupling where none is neccessary and to save some processing time.
|
||||
this.progress = 1.0f;
|
||||
progressUpdateCallback();
|
||||
endedCallback();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.endedCallback = endedCallback;
|
||||
this.progressUpdateCallback = progressUpdateCallback;
|
||||
|
||||
// This might trigger the event handler to be invoked right here if the transaction
|
||||
// ended between our initial optimization attempt and this line. It's unlikely,
|
||||
// however, so we'll not waste time with another optimization attempt.
|
||||
this.weightedTransaction.Transaction.AsyncEnded += new EventHandler(asyncEnded);
|
||||
|
||||
// See whether this transaction implements the IProgressReporter interface and if
|
||||
// so, connect to its progress report event in order to pass these reports on
|
||||
// to whomever created ourselfes.
|
||||
this.progressReporter = this.weightedTransaction.Transaction as IProgressReporter;
|
||||
if(this.progressReporter != null) {
|
||||
this.asyncProgressChangedEventHandler = new EventHandler<ProgressReportEventArgs>(
|
||||
asyncProgressChanged
|
||||
);
|
||||
this.progressReporter.AsyncProgressChanged += this.asyncProgressChangedEventHandler;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Immediately releases all resources owned by the object</summary>
|
||||
public void Dispose() {
|
||||
asyncDisconnectEvents();
|
||||
}
|
||||
|
||||
/// <summary>Weighted transaction being observed</summary>
|
||||
public WeightedTransaction<TransactionType> WeightedTransaction {
|
||||
get { return this.weightedTransaction; }
|
||||
}
|
||||
|
||||
/// <summary>Amount of progress this transaction has achieved so far</summary>
|
||||
public float Progress {
|
||||
get { return this.progress; }
|
||||
}
|
||||
|
||||
/// <summary>Called when the observed transaction has ended</summary>
|
||||
/// <param name="sender">Transaction that has ended</param>
|
||||
/// <param name="e">Not used</param>
|
||||
private void asyncEnded(object sender, EventArgs e) {
|
||||
ReportDelegate savedEndedCallback = this.endedCallback;
|
||||
ReportDelegate savedProgressUpdateCallback = this.progressUpdateCallback;
|
||||
|
||||
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 transaction doesn't report any progress at all, the set or queue
|
||||
// owning us will have a percentage of transactions completed.
|
||||
//
|
||||
// There is the possibility of a race condition here, as a final progress
|
||||
// report could have been generated by a thread running the transaction
|
||||
// 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) {
|
||||
this.progress = 1.0f;
|
||||
savedProgressUpdateCallback();
|
||||
}
|
||||
|
||||
savedEndedCallback();
|
||||
}
|
||||
|
||||
/// <summary>Called when the progress of the observed transaction changes</summary>
|
||||
/// <param name="sender">Transaction 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 transaction</summary>
|
||||
private void asyncDisconnectEvents() {
|
||||
|
||||
// Make use of the double check locking idiom to avoid the costly lock when
|
||||
// the events have already been unsubscribed
|
||||
if(this.endedCallback != null) {
|
||||
|
||||
// This is an internal class with special knowledge that there
|
||||
// is no risk of deadlock involved, so we don't need a fancy syncRoot!
|
||||
lock(this) {
|
||||
if(this.endedCallback != null) {
|
||||
this.weightedTransaction.Transaction.AsyncEnded -= new EventHandler(asyncEnded);
|
||||
|
||||
if(this.progressReporter != null) {
|
||||
this.progressReporter.AsyncProgressChanged -=
|
||||
this.asyncProgressChangedEventHandler;
|
||||
|
||||
this.asyncProgressChangedEventHandler = null;
|
||||
}
|
||||
|
||||
this.endedCallback = null;
|
||||
this.progressUpdateCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
} // endedCallback != null
|
||||
|
||||
}
|
||||
|
||||
private EventHandler<ProgressReportEventArgs> asyncProgressChangedEventHandler;
|
||||
/// <summary>The observed transaction's progress reporting interface</summary>
|
||||
private IProgressReporter progressReporter;
|
||||
/// <summary>The weighted wable that is being observed</summary>
|
||||
private WeightedTransaction<TransactionType> weightedTransaction;
|
||||
/// <summary>Callback to invoke when the progress updates</summary>
|
||||
private volatile ReportDelegate progressUpdateCallback;
|
||||
/// <summary>Callback to invoke when the transaction ends</summary>
|
||||
private volatile ReportDelegate endedCallback;
|
||||
/// <summary>Progress achieved so far</summary>
|
||||
private volatile float progress;
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Tracking
|
|
@ -1,177 +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;
|
||||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Waitable being observed by another object</summary>
|
||||
/// <typeparam name="WaitableType">
|
||||
/// Type of the waitable that is being observed
|
||||
/// </typeparam>
|
||||
internal class ObservedWeightedWaitable<WaitableType> : IDisposable
|
||||
where WaitableType : Waitable {
|
||||
|
||||
/// <summary>Delegate for reporting progress updates</summary>
|
||||
public delegate void ReportDelegate();
|
||||
|
||||
/// <summary>Initializes a new observed waitable</summary>
|
||||
/// <param name="weightedWaitable">Weighted waitable being observed</param>
|
||||
/// <param name="progressUpdateCallback">
|
||||
/// Callback to invoke when the waitable's progress changes
|
||||
/// </param>
|
||||
/// <param name="endedCallback">
|
||||
/// Callback to invoke when the waitable has ended
|
||||
/// </param>
|
||||
internal ObservedWeightedWaitable(
|
||||
WeightedWaitable<WaitableType> weightedWaitable,
|
||||
ReportDelegate progressUpdateCallback,
|
||||
ReportDelegate endedCallback
|
||||
) {
|
||||
this.weightedWaitable = weightedWaitable;
|
||||
|
||||
// See if this waitable has already ended (initial check for performance)
|
||||
if(weightedWaitable.Waitable.Ended) {
|
||||
|
||||
this.progress = 1.0f;
|
||||
|
||||
} else {
|
||||
|
||||
this.endedCallback = endedCallback;
|
||||
this.progressUpdateCallback = progressUpdateCallback;
|
||||
|
||||
this.weightedWaitable.Waitable.AsyncEnded +=
|
||||
new EventHandler(asyncEnded);
|
||||
|
||||
// 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 waitable is in the 'Ended' state.
|
||||
if(weightedWaitable.Waitable.Ended) {
|
||||
this.progress = 1.0f;
|
||||
} else {
|
||||
this.progressReporter = this.weightedWaitable.Waitable as IProgressReporter;
|
||||
|
||||
if(this.progressReporter != null) {
|
||||
this.asyncProgressChangedEventHandler = new EventHandler<ProgressReportEventArgs>(
|
||||
asyncProgressChanged
|
||||
);
|
||||
|
||||
this.progressReporter.AsyncProgressChanged +=
|
||||
this.asyncProgressChangedEventHandler;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Immediately releases all resources owned by the object</summary>
|
||||
public void Dispose() {
|
||||
asyncDisconnectEvents();
|
||||
}
|
||||
|
||||
/// <summary>Weighted waitable being observed</summary>
|
||||
public WeightedWaitable<WaitableType> WeightedWaitable {
|
||||
get { return this.weightedWaitable; }
|
||||
}
|
||||
|
||||
/// <summary>Amount of progress this waitable has achieved so far</summary>
|
||||
public float Progress {
|
||||
get { return this.progress; }
|
||||
}
|
||||
|
||||
/// <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;
|
||||
ReportDelegate progressUpdateCallback = this.progressUpdateCallback;
|
||||
|
||||
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 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 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) {
|
||||
this.progress = 1.0f;
|
||||
progressUpdateCallback();
|
||||
}
|
||||
|
||||
endedCallback();
|
||||
}
|
||||
|
||||
/// <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 waitable</summary>
|
||||
private void asyncDisconnectEvents() {
|
||||
|
||||
// Make use of the double check locking idiom to avoid the costly lock when
|
||||
// the events have already been unsubscribed
|
||||
if(this.endedCallback != null) {
|
||||
|
||||
// This is an internal class with special knowledge that there
|
||||
// is no risk of deadlock involved, so we don't need a fancy syncRoot!
|
||||
lock(this) {
|
||||
if(this.endedCallback != null) {
|
||||
this.weightedWaitable.Waitable.AsyncEnded -= new EventHandler(asyncEnded);
|
||||
|
||||
if(this.progressReporter != null) {
|
||||
this.progressReporter.AsyncProgressChanged -=
|
||||
this.asyncProgressChangedEventHandler;
|
||||
|
||||
this.asyncProgressChangedEventHandler = null;
|
||||
}
|
||||
|
||||
this.endedCallback = null;
|
||||
this.progressUpdateCallback = null;
|
||||
}
|
||||
}
|
||||
|
||||
} // endedCallback != null
|
||||
|
||||
}
|
||||
|
||||
private EventHandler<ProgressReportEventArgs> asyncProgressChangedEventHandler;
|
||||
/// <summary>The observed waitable's progress reporting interface</summary>
|
||||
private IProgressReporter progressReporter;
|
||||
/// <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 waitable ends</summary>
|
||||
private volatile ReportDelegate endedCallback;
|
||||
/// <summary>Progress achieved so far</summary>
|
||||
private volatile float progress;
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Tracking
|
|
@ -0,0 +1,70 @@
|
|||
#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.IO;
|
||||
using System.Threading;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.SyntaxHelpers;
|
||||
using NMock2;
|
||||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Unit Test for the observation wrapper collection of weighted transactions</summary>
|
||||
[TestFixture]
|
||||
public class WeightedTransactionWrapperCollectionTest {
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the wrapper collection is handing out the unwrapped transactions
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWrapperCollection() {
|
||||
WeightedTransaction<Transaction> transaction = new WeightedTransaction<Transaction>(
|
||||
Transaction.EndedDummy
|
||||
);
|
||||
|
||||
ObservedWeightedTransaction<Transaction> observed = new ObservedWeightedTransaction<Transaction>(
|
||||
transaction,
|
||||
endedCallback,
|
||||
progressUpdatedCallback
|
||||
);
|
||||
|
||||
WeightedTransactionWrapperCollection<Transaction> wrapper =
|
||||
new WeightedTransactionWrapperCollection<Transaction>(
|
||||
new ObservedWeightedTransaction<Transaction>[] { observed }
|
||||
);
|
||||
|
||||
Assert.AreSame(transaction, wrapper[0]);
|
||||
}
|
||||
|
||||
/// <summary>Dummy callback used as event subscriber in the tests</summary>
|
||||
private void endedCallback() { }
|
||||
|
||||
/// <summary>Dummy callback used as event subscriber in the tests</summary>
|
||||
private void progressUpdatedCallback() { }
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Tracking
|
||||
|
||||
#endif // UNITTEST
|
|
@ -27,36 +27,36 @@ using Nuclex.Support.Collections;
|
|||
|
||||
namespace Nuclex.Support.Tracking {
|
||||
|
||||
/// <summary>Collection of waitables with a weighting value</summary>
|
||||
/// <typeparam name="WaitableType">Type of waitables to manage</typeparam>
|
||||
/// <summary>Collection of transactions with a weighting value</summary>
|
||||
/// <typeparam name="TransactionType">Type of transactions to manage</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This collection is exposed as a read-only collection to the user that
|
||||
/// 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.
|
||||
/// stores WeightedTransactions. Internally, it merely wraps a collection of
|
||||
/// an internal type used to keep track of the individual transaction's
|
||||
/// progress in the TransactionGroup and OperationQueue classes.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// 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 WaitableSet is, by definition,
|
||||
/// finished (simply because there is no work to do) - unless the contents
|
||||
/// 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
|
||||
/// WaitableSet or OperationQueue classes was created.
|
||||
/// It is read-only because the design requires a transaction to only ever finish
|
||||
/// once. If it was possible eg. to add items after a TransactionGroup had signalled
|
||||
/// itself as being finished, it would be moved into an unfinished state again.
|
||||
/// Also, an empty TransactionGroup is, by definition, finished (simply because
|
||||
/// there is no work to do) - unless the contents of the group are passed to the
|
||||
/// TransactionGroup's constructor and never modified at all, the design would be
|
||||
/// violated as soon as an instance of the TransactionGroup or OperationQueue
|
||||
/// classes was created.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal class WeightedWaitableWrapperCollection<WaitableType> :
|
||||
internal class WeightedTransactionWrapperCollection<TransactionType> :
|
||||
TransformingReadOnlyCollection<
|
||||
ObservedWeightedWaitable<WaitableType>, WeightedWaitable<WaitableType>
|
||||
ObservedWeightedTransaction<TransactionType>, WeightedTransaction<TransactionType>
|
||||
>
|
||||
where WaitableType : Waitable {
|
||||
where TransactionType : Transaction {
|
||||
|
||||
/// <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
|
||||
/// <summary>Initializes a new weighted transaction collection wrapper</summary>
|
||||
/// <param name="items">Items to be exposed as weighted transactions</param>
|
||||
internal WeightedTransactionWrapperCollection(
|
||||
IList<ObservedWeightedTransaction<TransactionType>> items
|
||||
)
|
||||
: base(items) { }
|
||||
|
||||
|
@ -69,10 +69,10 @@ namespace Nuclex.Support.Tracking {
|
|||
/// be called frequently, because the TransformingReadOnlyCollection does
|
||||
/// not cache otherwise store the transformed items.
|
||||
/// </remarks>
|
||||
protected override WeightedWaitable<WaitableType> Transform(
|
||||
ObservedWeightedWaitable<WaitableType> item
|
||||
protected override WeightedTransaction<TransactionType> Transform(
|
||||
ObservedWeightedTransaction<TransactionType> item
|
||||
) {
|
||||
return item.WeightedWaitable;
|
||||
return item.WeightedTransaction;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue