diff --git a/Source/Scheduling/AbortedException.Test.cs b/Source/Scheduling/AbortedException.Test.cs
deleted file mode 100644
index 0881b15..0000000
--- a/Source/Scheduling/AbortedException.Test.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.Serialization.Formatters.Binary;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Unit Test for the AbortedException class
- [TestFixture]
- public class AbortedExceptionTest {
-
- ///
- /// Verifies that the exception's default constructor is working
- ///
- [Test]
- public void TestDefaultConstructor() {
- AbortedException testException = new AbortedException();
-
- string testExceptionString = testException.ToString();
- Assert.IsNotNull(testExceptionString);
- }
-
- ///
- /// Checks whether the exception correctly stores its inner exception
- ///
- [Test]
- public void TestInnerException() {
- Exception inner = new Exception("This is a test");
- AbortedException testException = new AbortedException(
- "Hello World", inner
- );
-
- Assert.AreSame(inner, testException.InnerException);
- }
-
- ///
- /// Test whether the exception can be serialized
- ///
- [Test]
- public void TestSerialization() {
- BinaryFormatter formatter = new BinaryFormatter();
-
- using(MemoryStream memory = new MemoryStream()) {
- AbortedException exception1 = new AbortedException("Hello World");
-
- formatter.Serialize(memory, exception1);
- memory.Position = 0;
- object exception2 = formatter.Deserialize(memory);
-
- Assert.IsInstanceOf(exception2);
- Assert.AreEqual(exception1.Message, ((AbortedException)exception2).Message);
- }
- }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
-
-#endif // UNITTEST
diff --git a/Source/Scheduling/AbortedException.cs b/Source/Scheduling/AbortedException.cs
deleted file mode 100644
index 7e5f6ac..0000000
--- a/Source/Scheduling/AbortedException.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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.Scheduling {
-
- /// Indicates that an operation has been forcefully aborted
- ///
- /// This exception is the typical result of using AsyncAbort() on a running
- /// background process.
- ///
-#if !NO_SERIALIZATION
- [Serializable]
-#endif
- public class AbortedException : Exception {
-
- /// Initializes the exception
- public AbortedException() { }
-
- /// Initializes the exception with an error message
- /// Error message describing the cause of the exception
- public AbortedException(string message) : base(message) { }
-
- /// Initializes the exception as a followup exception
- /// Error message describing the cause of the exception
- /// Preceding exception that has caused this exception
- public AbortedException(string message, Exception inner) : base(message, inner) { }
-
-#if !NO_SERIALIZATION
-
- /// Initializes the exception from its serialized state
- /// Contains the serialized fields of the exception
- /// Additional environmental informations
- protected AbortedException(
- System.Runtime.Serialization.SerializationInfo info,
- System.Runtime.Serialization.StreamingContext context
- ) :
- base(info, context) { }
-
-#endif // !NO_SERIALIZATION
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/GenericTimeSource.Test.cs b/Source/Scheduling/GenericTimeSource.Test.cs
deleted file mode 100644
index 97ceb23..0000000
--- a/Source/Scheduling/GenericTimeSource.Test.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.Threading;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Unit Test for the generic scheduler time source
- [TestFixture]
- public class GenericTimeSourceTest {
-
- ///
- /// Verifies that the time source's default constructor is working
- ///
- [Test]
- public void TestDefaultConstructor() {
- GenericTimeSource timeSource = new GenericTimeSource();
- }
-
- ///
- /// Verifies that the time source can provide the current UTC time
- ///
- [Test]
- public void TestCurrentUtcTime() {
- GenericTimeSource timeSource = new GenericTimeSource();
-
- Assert.That(
- timeSource.CurrentUtcTime, Is.EqualTo(DateTime.UtcNow).Within(10).Seconds
- );
- }
-
- ///
- /// Verifies that the default time source's tick property is working if
- /// the Stopwatch class is used to measure time
- ///
- [Test]
- public void TestTicksWithStopwatch() {
- GenericTimeSource timeSource = new GenericTimeSource(true);
- long ticks1 = timeSource.Ticks;
- long ticks2 = timeSource.Ticks;
-
- Assert.That(ticks2, Is.GreaterThanOrEqualTo(ticks1));
- }
-
- ///
- /// Verifies that the default time source's tick property is working if
- /// Environment.TickCount is used to measure time
- ///
- [Test]
- public void TestTicksWithTickCount() {
- GenericTimeSource timeSource = new GenericTimeSource(false);
- long ticks1 = timeSource.Ticks;
- long ticks2 = timeSource.Ticks;
-
- Assert.That(ticks2, Is.GreaterThanOrEqualTo(ticks1));
- }
-
- ///
- /// Verifies that the default time source's WaitOne() method works correctly
- ///
- [Test]
- public void TestWaitOne() {
- GenericTimeSource timeSource = new GenericTimeSource();
- AutoResetEvent waitEvent = new AutoResetEvent(true);
-
- Assert.IsTrue(timeSource.WaitOne(waitEvent, TimeSpan.FromMilliseconds(1).Ticks));
- Assert.IsFalse(timeSource.WaitOne(waitEvent, TimeSpan.FromMilliseconds(1).Ticks));
- }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
-
-#endif // UNITTEST
diff --git a/Source/Scheduling/GenericTimeSource.cs b/Source/Scheduling/GenericTimeSource.cs
deleted file mode 100644
index cc54cfe..0000000
--- a/Source/Scheduling/GenericTimeSource.cs
+++ /dev/null
@@ -1,189 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Diagnostics;
-using System.Threading;
-
-namespace Nuclex.Support.Scheduling {
-
- ///
- /// Generic time source implementation using the Stopwatch or Environment.TickCount
- ///
- public class GenericTimeSource : ITimeSource {
-
- /// Number of ticks (100 ns intervals) in a millisecond
- private const long TicksPerMillisecond = 10000;
-
- /// Tolerance for the detection of a date/time adjustment
- ///
- /// If the current system date/time jumps by more than this tolerance into any
- /// direction, the default time source will trigger the DateTimeAdjusted event.
- ///
- private const long TimeAdjustmentToleranceTicks = 75 * TicksPerMillisecond;
-
- /// Called when the system date/time are adjusted
- ///
- /// An adjustment is a change out of the ordinary, eg. when a time synchronization
- /// alters the current system time, when daylight saving time takes effect or
- /// when the user manually adjusts the system date/time.
- ///
- public event EventHandler DateTimeAdjusted;
-
- /// Initializes the static fields of the default time source
- static GenericTimeSource() {
- tickFrequency = 10000000.0;
- tickFrequency /= (double)Stopwatch.Frequency;
- }
-
- /// Initializes the default time source
- public GenericTimeSource() : this(Stopwatch.IsHighResolution) { }
-
- /// Initializes the default time source
- ///
- /// Whether to use the Stopwatch class for measuring time
- ///
- ///
- ///
- /// Normally it's a good idea to use the default constructor. If the Stopwatch
- /// is unable to use the high-resolution timer, it will fall back to
- /// DateTime.Now (as stated on MSDN). This is bad because then the tick count
- /// will jump whenever the system time changes (eg. when the system synchronizes
- /// its time with a time server).
- ///
- ///
- /// Your can safely use this constructor if you always set its arugment to 'false',
- /// but then your won't profit from the high-resolution timer if one is available.
- ///
- ///
- public GenericTimeSource(bool useStopwatch) {
- this.useStopwatch = useStopwatch;
-
- // Update the lastCheckedTime and lastCheckedTicks fields
- checkForTimeAdjustment();
- }
-
- /// Waits for an AutoResetEvent to become signalled
- /// WaitHandle the method will wait for
- /// Number of ticks to wait
- ///
- /// True if the WaitHandle was signalled, false if the timeout was reached
- ///
- public virtual bool WaitOne(AutoResetEvent waitHandle, long ticks) {
-
- // Force a timeout at least each second so the caller can check the system time
- // since we're not able to provide the DateTimeAdjusted notification
- int milliseconds = (int)(ticks / TicksPerMillisecond);
-#if WINDOWS
- bool signalled = waitHandle.WaitOne(Math.Min(1000, milliseconds), false);
-#else
- bool signalled = waitHandle.WaitOne(Math.Min(1000, milliseconds));
-#endif
- // See whether the system date/time have been adjusted while we were asleep.
- checkForTimeAdjustment();
-
- // Now tell the caller whether his event was signalled
- return signalled;
-
- }
-
- /// Current system time in UTC format
- public DateTime CurrentUtcTime {
- get { return DateTime.UtcNow; }
- }
-
- /// How long the time source has been running
- ///
- /// There is no guarantee this value starts at zero (or anywhere near it) when
- /// the time source is created. The only requirement for this value is that it
- /// keeps increasing with the passing of time and that it stays unaffected
- /// (eg. doesn't skip or jump back) when the system date/time are changed.
- ///
- public long Ticks {
- get {
-
- // The docs say if Stopwatch.IsHighResolution is false, it will return
- // DateTime.Now (actually DateTime.UtcNow). This means that the Stopwatch is
- // prone to skips and jumps during DST crossings and NTP synchronizations,
- // so we cannot use it in that case.
- if(this.useStopwatch) {
- double timestamp = (double)Stopwatch.GetTimestamp();
- return (long)(timestamp * tickFrequency);
- }
-
- // Fallback: Use Environment.TickCount instead. Not as accurate, but at least
- // it will not jump around when the date or time are adjusted.
- return Environment.TickCount * TicksPerMillisecond;
-
- }
- }
-
- /// Called when the system time is changed
- /// Not used
- /// Not used
- protected virtual void OnDateTimeAdjusted(object sender, EventArgs arguments) {
- EventHandler copy = DateTimeAdjusted;
- if(copy != null) {
- copy(sender, arguments);
- }
- }
-
- ///
- /// Checks whether the system/date time have been adjusted since the last call
- ///
- private void checkForTimeAdjustment() {
-
- // Grab the current date/time and timer ticks in one go
- long currentDateTimeTicks = DateTime.UtcNow.Ticks;
- long currentStopwatchTicks = Ticks;
-
- // Calculate the number of timer ticks that have passed since the last check and
- // extrapolate the local date/time we should be expecting to see
- long ticksSinceLastCheck = currentStopwatchTicks - lastCheckedStopwatchTicks;
- long expectedLocalTimeTicks = this.lastCheckedDateTimeTicks + ticksSinceLastCheck;
-
- // Find out by what amount the actual local date/time deviates from
- // the extrapolated date/time and trigger the date/time adjustment event if
- // we can reasonably assume that the system date/time have been adjusted.
- long deviationTicks = Math.Abs(expectedLocalTimeTicks - currentDateTimeTicks);
- if(deviationTicks > TimeAdjustmentToleranceTicks) {
- OnDateTimeAdjusted(this, EventArgs.Empty);
- }
-
- // Remember the current local date/time and timer ticks for the next run
- this.lastCheckedDateTimeTicks = currentDateTimeTicks;
- this.lastCheckedStopwatchTicks = currentStopwatchTicks;
-
- }
-
- /// Last local time we checked for a date/time adjustment
- private long lastCheckedDateTimeTicks;
- /// Timer ticks at which we last checked the local time
- private long lastCheckedStopwatchTicks;
-
- /// Number of ticks per Stopwatch time unit
- private static double tickFrequency;
- /// Whether ot use the Stopwatch class for measuring time
- private bool useStopwatch;
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/IAbortable.cs b/Source/Scheduling/IAbortable.cs
deleted file mode 100644
index b031703..0000000
--- a/Source/Scheduling/IAbortable.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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.Scheduling {
-
- /// Interface for abortable processes
- public interface IAbortable {
-
- /// Aborts the running process. Can be called from any thread.
- ///
- /// The receive should honor the abort request and stop whatever it is
- /// doing as soon as possible. The method does not impose any requirement
- /// on the timeliness of the reaction of the running process, but implementers
- /// are advised to not ignore the abort request and urged to try and design
- /// their code in such a way that it can be stopped in a reasonable time
- /// (eg. within 1 second of the abort request).
- ///
- void AsyncAbort();
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/ISchedulerService.cs b/Source/Scheduling/ISchedulerService.cs
deleted file mode 100644
index e5e3c0f..0000000
--- a/Source/Scheduling/ISchedulerService.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-
-namespace Nuclex.Support.Scheduling {
-
-#if SCHEDULER_USE_CUSTOM_CALLBACK
- /// Signature for a timed callback from the scheduler service
- public delegate void SchedulerCallback();
-#endif // SCHEDULER_USE_CUSTOM_CALLBACK
-
- /// Service that allows the scheduled invocation of tasks
- public interface ISchedulerService {
-
- /// Time source being used by the scheduler
- ITimeSource TimeSource { get; }
-
- /// Schedules a notification at the specified absolute time
- ///
- /// Absolute time at which the notification will occur
- ///
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- ///
- /// The notification is scheduled for the indicated absolute time. If the system
- /// enters/leaves daylight saving time or the date/time is changed (for example
- /// when the system synchronizes with an NTP server), this will affect
- /// the notification. So if you need to be notified after a fixed time, use
- /// the NotifyIn() method instead.
- ///
- object NotifyAt(DateTime notificationTime, WaitCallback callback);
-
- ///
- /// Schedules a recurring notification after the specified amount of milliseconds
- ///
- ///
- /// Milliseconds after which the first notification will occur
- ///
- ///
- /// Interval in milliseconds at which the notification will be repeated
- ///
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- object NotifyEach(
- int delayMilliseconds, int intervalMilliseconds, WaitCallback callback
- );
-
- ///
- /// Schedules a recurring notification after the specified time span
- ///
- /// Delay after which the first notification will occur
- /// Interval at which the notification will be repeated
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- object NotifyEach(TimeSpan delay, TimeSpan interval, WaitCallback callback);
-
- ///
- /// Schedules a notification after the specified amount of milliseconds
- ///
- ///
- /// Number of milliseconds after which the notification will occur
- ///
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- object NotifyIn(int delayMilliseconds, WaitCallback callback);
-
- /// Schedules a notification after the specified time span
- /// Delay after which the notification will occur
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- object NotifyIn(TimeSpan delay, WaitCallback callback);
-
- /// Cancels a scheduled notification
- ///
- /// Handle of the notification that will be cancelled
- ///
- void Cancel(object notificationHandle);
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/ITimeSource.cs b/Source/Scheduling/ITimeSource.cs
deleted file mode 100644
index 25087d0..0000000
--- a/Source/Scheduling/ITimeSource.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Provides time measurement and change notification services
- public interface ITimeSource {
-
- /// Called when the system date/time are adjusted
- ///
- /// An adjustment is a change out of the ordinary, eg. when a time synchronization
- /// alters the current system time, when daylight saving time takes effect or
- /// when the user manually adjusts the system date/time.
- ///
- event EventHandler DateTimeAdjusted;
-
- /// Waits for an AutoResetEvent to become signalled
- /// WaitHandle the method will wait for
- /// Number of ticks to wait
- ///
- /// True if the WaitHandle was signalled, false if the timeout was reached
- /// or the time source thinks its time to recheck the system date/time.
- ///
- ///
- /// Depending on whether the system will provide notifications when date/time
- /// is adjusted, the time source will be forced to let this method block for
- /// less than the indicated time before returning a timeout in order to give
- /// the caller a chance to recheck the system time.
- ///
- bool WaitOne(AutoResetEvent waitHandle, long ticks);
-
- /// Current system time in UTC format
- DateTime CurrentUtcTime { get; }
-
- /// How long the time source has been running
- ///
- /// There is no guarantee this value starts at zero (or anywhere near it) when
- /// the time source is created. The only requirement for this value is that it
- /// keeps increasing with the passing of time and that it stays unaffected
- /// (eg. doesn't skip or jump back) when the system date/time are changed.
- ///
- long Ticks { get; }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/Operation.Test.cs b/Source/Scheduling/Operation.Test.cs
deleted file mode 100644
index e493bbf..0000000
--- a/Source/Scheduling/Operation.Test.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.Serialization.Formatters.Binary;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Unit Test for the operation class
- [TestFixture]
- public class OperationTest {
-
- #region class TestOperation
-
- /// Dummy operation used to run the unit tests
- private class TestOperation : Operation {
-
- /// Launches the background operation
- public override void Start() {
- // This could become a race condition of this code would be used in a fashion
- // different than what current unit tests do with it
- if(!base.Ended) {
- OnAsyncEnded();
- }
- }
-
- }
-
- #endregion // class TestOperation
-
- /// Tests whether operations can be started
- [Test]
- public void TestOperationStarting() {
- TestOperation myOperation = new TestOperation();
-
- Assert.IsFalse(myOperation.Ended);
- myOperation.Start();
- Assert.IsTrue(myOperation.Ended);
- }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
-
-#endif // UNITTEST
diff --git a/Source/Scheduling/Operation.cs b/Source/Scheduling/Operation.cs
deleted file mode 100644
index c62581b..0000000
--- a/Source/Scheduling/Operation.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-
-using Nuclex.Support.Tracking;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Base class for observable operations running in the background
- public abstract class Operation : Request {
-
- /// Launches the background operation
- public abstract void Start();
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/OperationQueue.Test.cs b/Source/Scheduling/OperationQueue.Test.cs
deleted file mode 100644
index 15e0b6b..0000000
--- a/Source/Scheduling/OperationQueue.Test.cs
+++ /dev/null
@@ -1,350 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-
-using NUnit.Framework;
-using NMock;
-
-using Nuclex.Support.Tracking;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Unit Test for the operation queue class
- [TestFixture]
- public class OperationQueueTest {
-
- #region interface IOperationQueueSubscriber
-
- /// Interface used to test the operation queue
- public interface IOperationQueueSubscriber {
-
- /// Called when the operations queue's progress changes
- /// Operation queue whose progress has changed
- /// Contains the new progress achieved
- void ProgressChanged(object sender, ProgressReportEventArgs arguments);
-
- /// Called when the operation queue has ended
- /// Operation queue that as ended
- /// Not used
- void Ended(object sender, EventArgs arguments);
-
- }
-
- #endregion // interface IOperationQueueSubscriber
-
- #region class ProgressUpdateEventArgsMatcher
-
- /// Compares two ProgressUpdateEventArgsInstances for NMock validation
- private class ProgressUpdateEventArgsMatcher : Matcher {
-
- /// Initializes a new ProgressUpdateEventArgsMatcher
- /// Expected progress update event arguments
- public ProgressUpdateEventArgsMatcher(ProgressReportEventArgs expected) {
- this.expected = expected;
- }
-
- ///
- /// Called by NMock to verfiy the ProgressUpdateEventArgs match the expected value
- ///
- /// Actual value to compare to the expected value
- ///
- /// True if the actual value matches the expected value; otherwise false
- ///
- public override bool Matches(object actualAsObject) {
- ProgressReportEventArgs actual = (actualAsObject as ProgressReportEventArgs);
- if(actual == null)
- return false;
-
- return (actual.Progress == this.expected.Progress);
- }
-
- /// Creates a string representation of the expected value
- /// Writer to write the string representation into
- public override void DescribeTo(TextWriter writer) {
- writer.Write(this.expected.Progress.ToString());
- }
-
- /// Expected progress update event args value
- private ProgressReportEventArgs expected;
-
- }
-
- #endregion // class ProgressUpdateEventArgsMatcher
-
- #region class TestOperation
-
- /// Operation used for testing in this unit test
- private class TestOperation : Operation, IProgressReporter {
-
- /// will be triggered to report when progress has been achieved
- public event EventHandler AsyncProgressChanged;
-
- /// Begins executing the operation. Yeah, sure :)
- public override void Start() { }
-
- /// Moves the operation into the ended state
- public void SetEnded() {
- SetEnded(null);
- }
-
- /// Moves the operation into the ended state with an exception
- /// Exception
- public void SetEnded(Exception exception) {
- this.exception = exception;
- OnAsyncEnded();
- }
-
- /// Changes the testing operation's indicated progress
- ///
- /// New progress to be reported by the testing operation
- ///
- public void ChangeProgress(float progress) {
- OnAsyncProgressChanged(progress);
- }
-
- ///
- /// Allows the specific request implementation to re-throw an exception if
- /// the background process finished unsuccessfully
- ///
- protected override void ReraiseExceptions() {
- if(this.exception != null)
- throw this.exception;
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this operation about the achieved progress.
- ///
- protected virtual void OnAsyncProgressChanged(float progress) {
- OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// 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
- /// operation's ProgressUpdateEventArgs class.
- ///
- protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
- EventHandler copy = AsyncProgressChanged;
- if(copy != null)
- copy(this, eventArguments);
- }
-
- /// Exception that has occured in the background process
- private volatile Exception exception;
-
- }
-
- #endregion // class TestOperation
-
- /// Initialization routine executed before each test is run
- [SetUp]
- public void Setup() {
- this.mockery = new MockFactory();
- }
-
- /// Validates that the queue executes operations sequentially
- [Test]
- public void TestSequentialExecution() {
- TestOperation operation1 = new TestOperation();
- TestOperation operation2 = new TestOperation();
-
- OperationQueue testQueueOperation =
- new OperationQueue(
- new TestOperation[] { operation1, operation2 }
- );
-
- Mock mockedSubscriber = mockSubscriber(testQueueOperation);
-
- testQueueOperation.Start();
-
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new Matcher[] {
- new NMock.Matchers.TypeMatcher(typeof(OperationQueue)),
- new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
- }
- );
-
- operation1.ChangeProgress(0.5f);
-
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new Matcher[] {
- new NMock.Matchers.TypeMatcher(typeof(OperationQueue)),
- new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
- }
- );
-
- operation1.SetEnded();
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
-
- ///
- /// Validates that the queue executes operations sequentially and honors the weights
- /// assigned to the individual operations
- ///
- [Test]
- public void TestWeightedSequentialExecution() {
- TestOperation operation1 = new TestOperation();
- TestOperation operation2 = new TestOperation();
-
- OperationQueue testQueueOperation =
- new OperationQueue(
- new WeightedTransaction[] {
- new WeightedTransaction(operation1, 0.5f),
- new WeightedTransaction(operation2, 2.0f)
- }
- );
-
- Mock mockedSubscriber = mockSubscriber(testQueueOperation);
-
- testQueueOperation.Start();
-
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(OperationQueue)),
- new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.1f))
- );
-
- operation1.ChangeProgress(0.5f);
-
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(OperationQueue)),
- new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.2f))
- );
-
- operation1.SetEnded();
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
-
- ///
- /// Validates that the operation queue propagates the ended event once all contained
- /// operations have completed
- ///
- [Test]
- public void TestEndPropagation() {
- TestOperation operation1 = new TestOperation();
- TestOperation operation2 = new TestOperation();
-
- OperationQueue testQueueOperation =
- new OperationQueue(
- new TestOperation[] {
- operation1,
- operation2
- }
- );
-
- testQueueOperation.Start();
-
- Assert.IsFalse(testQueueOperation.Ended);
- operation1.SetEnded();
- Assert.IsFalse(testQueueOperation.Ended);
- operation2.SetEnded();
- Assert.IsTrue(testQueueOperation.Ended);
-
- testQueueOperation.Join();
- }
-
- ///
- /// Validates that the operation queue delivers an exception occuring in one of the
- /// contained operations to the operation queue joiner
- ///
- [Test]
- public void TestExceptionPropagation() {
- TestOperation operation1 = new TestOperation();
- TestOperation operation2 = new TestOperation();
-
- OperationQueue testQueueOperation =
- new OperationQueue(
- new TestOperation[] {
- operation1,
- operation2
- }
- );
-
- testQueueOperation.Start();
-
- Assert.IsFalse(testQueueOperation.Ended);
- operation1.SetEnded();
- Assert.IsFalse(testQueueOperation.Ended);
- operation2.SetEnded(new AbortedException("Hello World"));
-
- Assert.Throws(
- delegate() { testQueueOperation.Join(); }
- );
- }
-
- ///
- /// Ensures that the operation queue transparently wraps the child operations in
- /// an observation wrapper that is not visible to an outside user
- ///
- [Test]
- public void TestTransparentWrapping() {
- WeightedTransaction operation1 = new WeightedTransaction(
- new TestOperation()
- );
- WeightedTransaction operation2 = new WeightedTransaction(
- new TestOperation()
- );
-
- OperationQueue testQueueOperation =
- new OperationQueue(
- new WeightedTransaction[] {
- operation1,
- operation2
- }
- );
-
- // Order is important due to sequential execution!
- Assert.AreSame(operation1, testQueueOperation.Children[0]);
- Assert.AreSame(operation2, testQueueOperation.Children[1]);
- }
-
- /// Mocks a subscriber for the events of an operation
- /// Operation to mock an event subscriber for
- /// The mocked event subscriber
- private Mock mockSubscriber(Operation operation) {
- Mock mockedSubscriber =
- this.mockery.CreateMock();
-
- operation.AsyncEnded += new EventHandler(mockedSubscriber.MockObject.Ended);
- (operation as IProgressReporter).AsyncProgressChanged +=
- new EventHandler(mockedSubscriber.MockObject.ProgressChanged);
-
- return mockedSubscriber;
- }
-
- /// Mock object factory
- private MockFactory mockery;
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Scheduling/OperationQueue.cs b/Source/Scheduling/OperationQueue.cs
deleted file mode 100644
index 760c57c..0000000
--- a/Source/Scheduling/OperationQueue.cs
+++ /dev/null
@@ -1,242 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-
-using Nuclex.Support.Tracking;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Operation that sequentially executes a series of operations
- ///
- /// Type of the child operations the QueueOperation will contain
- ///
- public class OperationQueue : Operation, IProgressReporter
- where OperationType : Operation {
-
- /// will be triggered to report when progress has been achieved
- public event EventHandler AsyncProgressChanged;
-
- /// Initializes a new queue operation with default weights
- /// Child operations to execute in this operation
- ///
- /// All child operations will have a default weight of 1.0
- ///
- public OperationQueue(IEnumerable childs) : this() {
-
- // Construct a WeightedTransaction with the default weight for each
- // transaction and wrap it in an ObservedTransaction
- foreach(OperationType operation in childs)
- this.children.Add(new WeightedTransaction(operation));
-
- // Since all transactions have a weight of 1.0, the total weight is
- // equal to the number of transactions in our list
- this.totalWeight = (float)this.children.Count;
-
- }
-
- /// Initializes a new queue operation with custom weights
- /// Child operations to execute in this operation
- public OperationQueue(IEnumerable> childs) : this() {
-
- // Construct an ObservedTransactionn around each of the WeightedTransactions
- foreach(WeightedTransaction operation in childs) {
- this.children.Add(operation);
-
- // Sum up the total weight
- this.totalWeight += operation.Weight;
- }
-
- }
-
- /// Initializes a new queue operation
- private OperationQueue() {
- this.asyncOperationEndedDelegate = new EventHandler(asyncOperationEnded);
- this.asyncOperationProgressChangedDelegate = new EventHandler(
- asyncOperationProgressChanged
- );
-
- this.children = new List>();
- }
-
- /// Provides access to the child operations of this queue
- public IList> Children {
- get { return this.children; }
- }
-
- /// Launches the background operation
- public override void Start() {
- startCurrentOperation();
- }
-
- ///
- /// Allows the specific request implementation to re-throw an exception if
- /// the background process finished unsuccessfully
- ///
- protected override void ReraiseExceptions() {
- if(this.exception != null)
- throw this.exception;
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this transaction about the achieved progress.
- ///
- protected virtual void OnAsyncProgressChanged(float progress) {
- OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this transaction about the achieved progress.
- /// Allows for classes derived from the transaction class to easily provide
- /// a custom event arguments class that has been derived from the
- /// transaction's ProgressUpdateEventArgs class.
- ///
- protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
- EventHandler copy = AsyncProgressChanged;
- if(copy != null)
- copy(this, eventArguments);
- }
-
- /// Prepares the current operation and calls its Start() method
- ///
- /// This subscribes the queue to the events of to the current operation
- /// and launches the operation by calling its Start() method.
- ///
- private void startCurrentOperation() {
- do {
- Thread.MemoryBarrier();
- OperationType operation = this.children[this.currentOperationIndex].Transaction;
-
- operation.AsyncEnded += this.asyncOperationEndedDelegate;
-
- IProgressReporter progressReporter = operation as IProgressReporter;
- if(progressReporter != null)
- progressReporter.AsyncProgressChanged += this.asyncOperationProgressChangedDelegate;
-
- Interlocked.Exchange(ref this.completionStatus, 1);
- operation.Start();
- } while(Interlocked.Decrement(ref this.completionStatus) > 0);
- }
-
- /// Disconnects from the current operation and calls its End() method
- ///
- /// This unsubscribes the queue from the current operation's events, calls End()
- /// on the operation and, if the operation didn't have an exception to report,
- /// counts up the accumulated progress of th e queue.
- ///
- private void endCurrentOperation() {
- Thread.MemoryBarrier();
- OperationType operation = this.children[this.currentOperationIndex].Transaction;
-
- // Disconnect from the operation's events
- operation.AsyncEnded -= this.asyncOperationEndedDelegate;
-
- IProgressReporter progressReporter = operation as IProgressReporter;
- if(progressReporter != null)
- progressReporter.AsyncProgressChanged -= this.asyncOperationProgressChangedDelegate;
-
- try {
- operation.Join();
-
- // Add the operations weight to the total amount of completed weight in the queue
- this.completedWeight += this.children[this.currentOperationIndex].Weight;
-
- // Trigger another progress update
- OnAsyncProgressChanged(this.completedWeight / this.totalWeight);
- }
- catch(Exception exception) {
- this.exception = exception;
- }
- }
-
- /// Called when the current executing operation ends
- /// Operation that ended
- /// Not used
- private void asyncOperationEnded(object sender, EventArgs arguments) {
-
- // Unsubscribe from the current operation's events and update the
- // accumulating progress counter
- endCurrentOperation();
-
- // Only jump to the next operation if no exception occured
- if(this.exception == null) {
- int newIndex = Interlocked.Increment(ref this.currentOperationIndex);
- Thread.MemoryBarrier();
-
- // Execute the next operation unless we reached the end of the queue
- if(newIndex < this.children.Count) {
- if(Interlocked.Increment(ref this.completionStatus) == 1) {
- startCurrentOperation();
- }
- return;
- }
- }
-
- // Either an exception has occured or we reached the end of the operation
- // queue. In any case, we need to report that the operation is over.
- OnAsyncEnded();
-
- }
-
- /// Called when currently executing operation makes progress
- /// Operation that has achieved progress
- /// Not used
- private void asyncOperationProgressChanged(
- object sender, ProgressReportEventArgs arguments
- ) {
-
- // Determine the completed weight of the currently executing operation
- float operationWeight = this.children[this.currentOperationIndex].Weight;
- float operationCompletedWeight = arguments.Progress * operationWeight;
-
- // Build the total normalized amount of progress for the queue
- float progress = (this.completedWeight + operationCompletedWeight) / this.totalWeight;
-
- // Done, we can send the actual progress to any event subscribers
- OnAsyncProgressChanged(progress);
-
- }
-
- /// Delegate to the asyncOperationEnded() method
- private EventHandler asyncOperationEndedDelegate;
- /// Delegate to the asyncOperationProgressUpdated() method
- private EventHandler asyncOperationProgressChangedDelegate;
- /// Operations being managed in the queue
- private List> children;
- /// Summed weight of all operations in the queue
- private float totalWeight;
- /// Accumulated weight of the operations already completed
- private float completedWeight;
- /// Index of the operation currently executing
- private int currentOperationIndex;
- /// Used to detect when an operation completes synchronously
- private int completionStatus;
- /// Exception that has occured in the background process
- private volatile Exception exception;
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/Scheduler.Test.cs b/Source/Scheduling/Scheduler.Test.cs
deleted file mode 100644
index d71a714..0000000
--- a/Source/Scheduling/Scheduler.Test.cs
+++ /dev/null
@@ -1,489 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-using Microsoft.Win32;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Unit Test for the scheduler
- [TestFixture]
- public class SchedulerTest {
-
- #region class MockTimeSource
-
- /// Mocked time source
- private class MockTimeSource : ITimeSource {
-
- /// Called when the system date/time are adjusted
- public event EventHandler DateTimeAdjusted;
-
- /// Initializes a new mocked time source
- /// Start time in UTC format
- public MockTimeSource(DateTime utcStartTime) {
- this.currentTime = utcStartTime;
- this.currentTicks = 1000000000;
- }
-
- /// Waits for an AutoResetEvent to become signalled
- /// WaitHandle the method will wait for
- /// Number of ticks to wait
- ///
- /// True if the WaitHandle was signalled, false if the timeout was reached
- /// or the time source thinks its time to recheck the system date/time.
- ///
- public bool WaitOne(AutoResetEvent waitHandle, long ticks) {
- long currentTicks;
- long eventDueTicks;
- lock(this) {
- this.autoResetEvent = waitHandle;
- this.eventDueTicks += ticks;
-
- currentTicks = this.currentTicks;
- eventDueTicks = this.eventDueTicks;
- }
-
- // If we need to wait, use the wait handle. We do not use the wait handle's
- // return value (or even its timeout) because we might trigger it ourselves
- // to simulate the passing of time.
- if(eventDueTicks > 0) {
- this.autoResetEvent = waitHandle;
- waitHandle.WaitOne();
- this.autoResetEvent = null;
- }
-
- // Do not use the cached values here -- we might have used the WaitHandle and
- // the simulation time could have been advanced while we were waiting.
- lock(this) {
- return (this.eventDueTicks > 0); // True = signalled, false = timed out
- }
- }
-
- /// Current system time in UTC format
- public DateTime CurrentUtcTime {
- get { lock(this) { return this.currentTime; } }
- }
-
- /// How long the time source has been running
- public long Ticks {
- get {
- lock(this) {
- this.eventDueTicks = 0;
- return this.currentTicks;
- }
- }
- }
-
- /// Advances the time of the time source
- ///
- /// Time span by which to advance the time source's time
- ///
- public void AdvanceTime(TimeSpan timeSpan) {
- lock(this) {
- this.currentTicks += timeSpan.Ticks;
- this.currentTime += timeSpan;
-
- // Problem: The Scheduler has just calculated the remaining ticks until
- // notification occurs. Next, another thread advances simulation time
- // and then the scheduler calls this. It will wait, even though
- // the simultion time has progressed.
- // To compensate this, we track the remaining time until the event is due
- // and allow for a negative time budget if AdvanceTime() is called after
- // the scheduler has just queried the current tick count.
- this.eventDueTicks -= timeSpan.Ticks;
-
- if(this.eventDueTicks <= 0) {
- AutoResetEvent copy = this.autoResetEvent;
- if(copy != null) {
- copy.Set();
- }
- }
- }
- }
-
- /// Manually triggers the date time adjusted event
- /// New simulation time to jump to
- public void AdjustTime(DateTime newUtcTime) {
- lock(this) {
- this.currentTime = newUtcTime;
- }
- EventHandler copy = DateTimeAdjusted;
- if(copy != null) {
- copy(this, EventArgs.Empty);
- }
- }
-
- /// Auto reset event the time source is currently waiting on
- private volatile AutoResetEvent autoResetEvent;
- /// Ticks at which the auto reset event will be due
- private long eventDueTicks;
- /// Current time source tick counter
- private long currentTicks;
- /// Current system time and date
- private DateTime currentTime;
-
- }
-
- #endregion // class MockTimeSource
-
- #region class TestSubscriber
-
- /// Subscriber used to test the scheduler notifications
- private class TestSubscriber : IDisposable {
-
- /// Initializes a new test subscriber
- public TestSubscriber() {
- this.waitHandle = new AutoResetEvent(false);
- }
-
- /// Immediately releases all resources owned by the instance
- public void Dispose() {
- if(this.waitHandle != null) {
- this.waitHandle.Close();
- this.waitHandle = null;
- }
- }
-
- /// Callback method that can be subscribed to the scheduler
- /// Not used
- public void Callback(object state) {
- Interlocked.Increment(ref this.callbackCount);
- this.waitHandle.Set();
- }
-
- /// Blocks ther caller until the callback is invoked
- ///
- /// Maximum number of milliseconds to wait for the callback
- ///
- /// True if the callback was invoked, false if the call timed out
- public bool WaitForCallback(int milliseconds) {
- return this.waitHandle.WaitOne(milliseconds);
- }
-
- /// Number of times the callback has been invoked
- public int CallbackCount {
- get { return Thread.VolatileRead(ref this.callbackCount); }
- }
-
- /// Callback invocation count
- private int callbackCount;
- /// WaitHandle used to wait for the callback
- private AutoResetEvent waitHandle;
-
- }
-
- #endregion // class TestSubscriber
-
- ///
- /// Test whether the Scheduler can explicitely create a windows time source
- ///
- [Test]
- public void TestCreateWindowsTimeSource() {
- ITimeSource timeSource = Scheduler.CreateTimeSource(true);
- try {
- Assert.That(timeSource is WindowsTimeSource);
- } finally {
- IDisposable disposableTimeSource = timeSource as IDisposable;
- if(disposableTimeSource != null) {
- disposableTimeSource.Dispose();
- }
- }
- }
-
- ///
- /// Test whether the Scheduler can explicitely create a generic time source
- ///
- [Test]
- public void TestCreateGenericTimeSource() {
- ITimeSource timeSource = Scheduler.CreateTimeSource(false);
- try {
- Assert.That(timeSource is GenericTimeSource);
- } finally {
- IDisposable disposableTimeSource = timeSource as IDisposable;
- if(disposableTimeSource != null) {
- disposableTimeSource.Dispose();
- }
- }
- }
-
- ///
- /// Test whether the Scheduler can automatically choose the right time source
- ///
- [Test]
- public void TestCreateDefaultTimeSource() {
- ITimeSource timeSource = Scheduler.CreateDefaultTimeSource();
- try {
- Assert.IsNotNull(timeSource);
- } finally {
- IDisposable disposableTimeSource = timeSource as IDisposable;
- if(disposableTimeSource != null) {
- disposableTimeSource.Dispose();
- }
- }
- }
-
- ///
- /// Verifies that the default constructor of the scheduler is working
- ///
- [Test]
- public void TestDefaultConstructor() {
- using(Scheduler scheduler = new Scheduler()) { }
- }
-
- ///
- /// Verifies that the default constructor of the scheduler is working
- ///
- [Test]
- public void TestThrowOnNotifyAtWithUnspecifiedDateTimeKind() {
- using(TestSubscriber subscriber = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler()) {
- Assert.Throws(
- delegate() {
- scheduler.NotifyAt(new DateTime(2000, 1, 1), subscriber.Callback);
- }
- );
- }
- }
- }
-
- ///
- /// Tests whether the NotifyAt() method invokes the callback at the right time
- ///
- [Test]
- public void TestNotifyAt() {
- MockTimeSource mockTimeSource = new MockTimeSource(new DateTime(2010, 1, 1));
- using(TestSubscriber subscriber = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler(mockTimeSource)) {
- scheduler.NotifyAt(makeUtc(new DateTime(2010, 1, 2)), subscriber.Callback);
-
- mockTimeSource.AdvanceTime(TimeSpan.FromDays(1));
-
- Assert.IsTrue(subscriber.WaitForCallback(1000));
- }
- }
- }
-
- ///
- /// Verifies that a notification at an absolute time is processed correctly
- /// if a time synchronization occurs during the wait.
- ///
- [Test]
- public void TestNotifyAtWithDateTimeAdjustment() {
- MockTimeSource mockTimeSource = new MockTimeSource(new DateTime(2010, 1, 1));
- using(TestSubscriber subscriber = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler(mockTimeSource)) {
- scheduler.NotifyAt(makeUtc(new DateTime(2010, 1, 2)), subscriber.Callback);
-
- // Let 12 hours pass, after that, we simulate a time synchronization
- // that puts the system 12 hours ahead of the original time.
- mockTimeSource.AdvanceTime(TimeSpan.FromHours(12));
- mockTimeSource.AdjustTime(new DateTime(2010, 1, 2));
-
- Assert.IsTrue(subscriber.WaitForCallback(1000));
- }
- }
- }
-
- /// Tests whether the scheduler's Cancel() method is working
- [Test]
- public void TestCancelNotification() {
- MockTimeSource mockTimeSource = new MockTimeSource(new DateTime(2010, 1, 1));
- using(TestSubscriber subscriber1 = new TestSubscriber()) {
- using(TestSubscriber subscriber2 = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler(mockTimeSource)) {
- object handle = scheduler.NotifyIn(
- TimeSpan.FromHours(24), subscriber1.Callback
- );
- scheduler.NotifyIn(TimeSpan.FromHours(36), subscriber2.Callback);
-
- mockTimeSource.AdvanceTime(TimeSpan.FromHours(12));
- scheduler.Cancel(handle);
- mockTimeSource.AdvanceTime(TimeSpan.FromHours(24));
-
- // Wait for the second subscriber to be notified. This is still a race
- // condition (there's no guarantee the thread pool will run tasks in
- // the order they were added), but it's the best we can do without
- // relying on an ugly Thread.Sleep() in this test.
- Assert.IsTrue(subscriber2.WaitForCallback(1000));
- Assert.AreEqual(0, subscriber1.CallbackCount);
- }
- }
- }
- }
-
- ///
- /// Tests the scheduler with two notifications that are scheduled in inverse
- /// order of their due time.
- ///
- [Test]
- public void TestInverseOrderNotification() {
- MockTimeSource mockTimeSource = new MockTimeSource(new DateTime(2010, 1, 1));
- using(TestSubscriber subscriber1 = new TestSubscriber()) {
- using(TestSubscriber subscriber2 = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler(mockTimeSource)) {
- scheduler.NotifyIn(TimeSpan.FromHours(24), subscriber1.Callback);
- scheduler.NotifyIn(TimeSpan.FromHours(12), subscriber2.Callback);
-
- mockTimeSource.AdvanceTime(TimeSpan.FromHours(18));
-
- Assert.IsTrue(subscriber2.WaitForCallback(1000));
- Assert.AreEqual(0, subscriber1.CallbackCount);
-
- mockTimeSource.AdvanceTime(TimeSpan.FromHours(18));
-
- Assert.IsTrue(subscriber1.WaitForCallback(1000));
- }
- }
- }
- }
-
- ///
- /// Tests the scheduler with two notifications that are scheduled to
- /// occur at the exact same time
- ///
- [Test]
- public void TestTwoNotificationsAtSameTime() {
- MockTimeSource mockTimeSource = new MockTimeSource(new DateTime(2010, 1, 1));
- using(TestSubscriber subscriber1 = new TestSubscriber()) {
- using(TestSubscriber subscriber2 = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler(mockTimeSource)) {
- scheduler.NotifyIn(60000, subscriber1.Callback);
- scheduler.NotifyIn(60000, subscriber2.Callback);
-
- mockTimeSource.AdvanceTime(TimeSpan.FromMilliseconds(60000));
-
- Assert.IsTrue(subscriber1.WaitForCallback(1000));
- Assert.IsTrue(subscriber2.WaitForCallback(1000));
- }
- }
- }
- }
-
- ///
- /// Verifies that the scheduler's NotifyEach() method is working correctly
- ///
- [Test]
- public void TestNotifyEachWithMilliseconds() {
- MockTimeSource mockTimeSource = new MockTimeSource(new DateTime(2010, 1, 1));
- using(TestSubscriber subscriber = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler(mockTimeSource)) {
- scheduler.NotifyEach(1000, 1000, subscriber.Callback);
-
- mockTimeSource.AdvanceTime(TimeSpan.FromMilliseconds(4000));
-
- // Wait for 4 invocations of the callback. We might not catch each trigger
- // of the AutoResetEvent, but we know that it will be 4 at most.
- for(int invocation = 0; invocation < 4; ++invocation) {
- Assert.IsTrue(subscriber.WaitForCallback(1000));
-
- if(subscriber.CallbackCount == 4) {
- break;
- }
- }
- }
- }
- }
-
- ///
- /// Verifies that the scheduler's NotifyEach() method is working correctly
- ///
- [Test]
- public void TestNotifyEachWithTimespan() {
- MockTimeSource mockTimeSource = new MockTimeSource(new DateTime(2010, 1, 1));
- using(TestSubscriber subscriber = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler(mockTimeSource)) {
- scheduler.NotifyEach(
- TimeSpan.FromHours(12), TimeSpan.FromHours(1), subscriber.Callback
- );
-
- mockTimeSource.AdvanceTime(TimeSpan.FromHours(14));
-
- // Wait for 3 invocations of the callback. We might not catch each trigger
- // of the AutoResetEvent, but we know that it will be 3 at most.
- for(int invocation = 0; invocation < 3; ++invocation) {
- Assert.IsTrue(subscriber.WaitForCallback(1000));
-
- if(subscriber.CallbackCount == 3) {
- break;
- }
- }
- }
- }
- }
-
- ///
- /// Reproduction case for a bug that occurred when the final notification in
- /// the scheduler was cancelled (call to PriorityQueue.Peek() on empty queue)
- ///
- [Test]
- public void TestCancelFinalNotification() {
- MockTimeSource mockTimeSource = new MockTimeSource(new DateTime(2010, 1, 1));
- using(TestSubscriber subscriber = new TestSubscriber()) {
- using(Scheduler scheduler = new Scheduler(mockTimeSource)) {
- scheduler.Cancel(
- scheduler.NotifyIn(TimeSpan.FromHours(12), subscriber.Callback)
- );
-
- mockTimeSource.AdvanceTime(TimeSpan.FromHours(14));
- Thread.Sleep(1);
- }
- }
- }
-
- // TODO: Unit testing caused this exception
- //
- // Nuclex.Support.Scheduling.SchedulerTest.TestThrowOnNotifyAtWithUnspecifiedDateTimeKind :
- // System.NullReferenceException: Der Objektverweis wurde nicht auf
- // eine Objektinstanz festgelegt.
- // bei Nuclex.Support.Scheduling.SchedulerTest.TestSubscriber.Callback(Object state)
- // in D:\Devel\framework\Nuclex.Support\Source\Scheduling\Scheduler.Test.cs:Zeile 177.
- // bei System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
- // bei System.Threading.ExecutionContext.Run(
- // ExecutionContext executionContext, ContextCallback callback, Object state
- // )
- // bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(
- // _ThreadPoolWaitCallback tpWaitCallBack
- // )
- // bei System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)
-
- /// Returns the provided date/time value as a utc time value
- /// Date/time value that will be returned as UTC
- /// The provided date/time value as UTC
- ///
- /// This doesn't convert the time, it just returns the exact same date and time
- /// but tags it as UTC by setting the DateTimeKind to UTC.
- ///
- private static DateTime makeUtc(DateTime dateTime) {
- return new DateTime(dateTime.Ticks, DateTimeKind.Utc);
- }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
-
-#endif // UNITTEST
diff --git a/Source/Scheduling/Scheduler.TimeSource.cs b/Source/Scheduling/Scheduler.TimeSource.cs
deleted file mode 100644
index 3ea4571..0000000
--- a/Source/Scheduling/Scheduler.TimeSource.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-using System.Diagnostics;
-
-using Nuclex.Support.Collections;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Schedules actions for execution at a future point in time
- partial class Scheduler {
-
- #region class TimeSourceSingleton
-
- ///
- /// Manages the singleton instance of the scheduler's default time source
- ///
- private class TimeSourceSingleton {
-
- ///
- /// Explicit static constructor to guarantee the singleton is initialized only
- /// when a static member of this class is accessed.
- ///
- static TimeSourceSingleton() { } // Do not remove!
-
- /// The singleton instance of the default time source
- internal static readonly ITimeSource Instance = Scheduler.CreateDefaultTimeSource();
-
- }
-
- #endregion // class TimeSourceSingleton
-
- /// Returns the default time source for the scheduler
- public static ITimeSource DefaultTimeSource {
- get { return TimeSourceSingleton.Instance; }
- }
-
- /// Creates a new default time source for the scheduler
- ///
- /// Whether the specialized windows time source should be used
- ///
- /// The newly created time source
- internal static ITimeSource CreateTimeSource(bool useWindowsTimeSource) {
- if(useWindowsTimeSource) {
- return new WindowsTimeSource();
- } else {
- return new GenericTimeSource();
- }
- }
-
- /// Creates a new default time source for the scheduler
- /// The newly created time source
- internal static ITimeSource CreateDefaultTimeSource() {
- return CreateTimeSource(WindowsTimeSource.Available);
- }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/Scheduler.cs b/Source/Scheduling/Scheduler.cs
deleted file mode 100644
index 5fb30c3..0000000
--- a/Source/Scheduling/Scheduler.cs
+++ /dev/null
@@ -1,460 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-using System.Diagnostics;
-
-using Nuclex.Support.Collections;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Schedules actions for execution at a future point in time
- public partial class Scheduler : ISchedulerService, IDisposable {
-
- /// One tick is 100 ns, meaning 10000 ticks equal 1 ms
- private const long TicksPerMillisecond = 10000;
-
- #region class Notification
-
- /// Scheduled notification
- private class Notification {
-
- /// Initializes a new notification
- ///
- /// Interval in which the notification will re-executed
- ///
- ///
- /// Time source ticks the notification is next due at
- ///
- ///
- /// Absolute time in UTC at which the notification is due
- ///
- ///
- /// Callback to be invoked when the notification is due
- ///
- public Notification(
- long intervalTicks,
- long nextDueTicks,
- DateTime absoluteUtcTime,
- WaitCallback callback
- ) {
- this.IntervalTicks = intervalTicks;
- this.NextDueTicks = nextDueTicks;
- this.AbsoluteUtcTime = absoluteUtcTime;
- this.Callback = callback;
- this.Cancelled = false;
- }
-
- ///
- /// Ticks specifying the interval in which the notification will be re-executed
- ///
- public long IntervalTicks;
-
- /// Next due time for this notification
- public long NextDueTicks;
- /// Absolute time in UTC at which the notification is due
- ///
- /// Only stored for notifications scheduled in absolute time, meaning they
- /// have to be adjusted if the system date/time changes
- ///
- public DateTime AbsoluteUtcTime;
- /// Callback that will be invoked when the notification is due
- public WaitCallback Callback;
- /// Whether the notification has been cancelled
- public bool Cancelled;
-
- }
-
- #endregion // class Notification
-
- #region class NotificationComparer
-
- /// Compares two notifications to each other
- private class NotificationComparer : IComparer {
-
- /// The default instance of the notification comparer
- public static readonly NotificationComparer Default = new NotificationComparer();
-
- /// Compares two notifications to each other based on their time
- /// Notification that will be compared on the left side
- /// Notification that will be comapred on the right side
- /// The relation of the two notification's times to each other
- public int Compare(Notification left, Notification right) {
- if(left.NextDueTicks > right.NextDueTicks) {
- return -1;
- } else if(left.NextDueTicks < right.NextDueTicks) {
- return 1;
- } else {
- return 0;
- }
- }
-
- }
-
- #endregion // class NotificationComparer
-
- /// Initializes a new scheduler using the default time source
- public Scheduler() : this(DefaultTimeSource) { }
-
- /// Initializes a new scheduler using the specified time source
- /// Time source the scheduler will use
- public Scheduler(ITimeSource timeSource) {
- this.dateTimeAdjustedDelegate = new EventHandler(dateTimeAdjusted);
-
- this.timeSource = timeSource;
- this.timeSource.DateTimeAdjusted += this.dateTimeAdjustedDelegate;
-
- this.notifications = new PriorityQueue(NotificationComparer.Default);
- this.notificationWaitEvent = new AutoResetEvent(false);
-
- this.timerThread = new Thread(new ThreadStart(runTimerThread));
- this.timerThread.Name = "Nuclex.Support.Scheduling.Scheduler";
-#if WINDOWS
- this.timerThread.Priority = ThreadPriority.Highest;
-#endif
- this.timerThread.IsBackground = true;
- this.timerThread.Start();
- }
-
- /// Immediately releases all resources owned by the instance
- public void Dispose() {
- if(this.timerThread != null) {
- this.endRequested = true;
- this.notificationWaitEvent.Set();
-
- // Wait for the timer thread to exit. If it doesn't exit in 10 seconds (which is
- // a lot of time given that it doesn't do any real work), forcefully abort
- // the thread. This may risk some leaks, but it's the only thing we can do.
- bool success = this.timerThread.Join(2500);
-#if WINDOWS
- Trace.Assert(success, "Scheduler timer thread did not exit in time");
-#endif
- // Unsubscribe from the time source to avoid surprise events during or
- // after shutdown
- if(this.timeSource != null) {
- this.timeSource.DateTimeAdjusted -= this.dateTimeAdjustedDelegate;
- this.timeSource = null;
- }
-
- // Get rid of the notification wait event now that we've made sure that
- // the timer thread is down.
- this.notificationWaitEvent.Close();
-
- // Help the GC a bit
- this.notificationWaitEvent = null;
- this.notifications = null;
-
- // Set to null so we don't attempt to end the thread again if Dispose() is
- // called multiple times.
- this.timerThread = null;
- }
- }
-
- /// Time source being used by the scheduler
- public ITimeSource TimeSource {
- get { return this.timeSource; }
- }
-
- /// Schedules a notification at the specified absolute time
- ///
- /// Absolute time at which the notification will occur
- ///
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- ///
- /// The notification is scheduled for the indicated absolute time. If the system
- /// enters/leaves daylight saving time or the date/time is changed (for example
- /// when the system synchronizes with an NTP server), this will affect
- /// the notification. So if you need to be notified after a fixed time, use
- /// the NotifyIn() method instead.
- ///
- public object NotifyAt(DateTime notificationTime, WaitCallback callback) {
- if(notificationTime.Kind == DateTimeKind.Unspecified) {
- throw new ArgumentException(
- "Notification time is neither UTC or local", "notificationTime"
- );
- }
-
- DateTime notificationTimeUtc = notificationTime.ToUniversalTime();
- DateTime now = this.timeSource.CurrentUtcTime;
-
- long remainingTicks = notificationTimeUtc.Ticks - now.Ticks;
- long nextDueTicks = this.timeSource.Ticks + remainingTicks;
-
- return scheduleNotification(
- new Notification(
- 0,
- nextDueTicks,
- notificationTimeUtc,
- callback
- )
- );
- }
-
- /// Schedules a notification after the specified time span
- /// Delay after which the notification will occur
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- public object NotifyIn(TimeSpan delay, WaitCallback callback) {
- return scheduleNotification(
- new Notification(
- 0,
- this.timeSource.Ticks + delay.Ticks,
- DateTime.MinValue,
- callback
- )
- );
- }
-
- ///
- /// Schedules a notification after the specified amount of milliseconds
- ///
- ///
- /// Number of milliseconds after which the notification will occur
- ///
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- public object NotifyIn(int delayMilliseconds, WaitCallback callback) {
- return scheduleNotification(
- new Notification(
- 0,
- this.timeSource.Ticks + ((long)delayMilliseconds * TicksPerMillisecond),
- DateTime.MinValue,
- callback
- )
- );
- }
-
- ///
- /// Schedules a recurring notification after the specified time span
- ///
- /// Delay after which the first notification will occur
- /// Interval at which the notification will be repeated
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- public object NotifyEach(TimeSpan delay, TimeSpan interval, WaitCallback callback) {
- return scheduleNotification(
- new Notification(
- interval.Ticks,
- this.timeSource.Ticks + delay.Ticks,
- DateTime.MinValue,
- callback
- )
- );
- }
-
- ///
- /// Schedules a recurring notification after the specified amount of milliseconds
- ///
- ///
- /// Milliseconds after which the first notification will occur
- ///
- ///
- /// Interval in milliseconds at which the notification will be repeated
- ///
- ///
- /// Callback that will be invoked when the notification is due
- ///
- /// A handle that can be used to cancel the notification
- public object NotifyEach(
- int delayMilliseconds, int intervalMilliseconds, WaitCallback callback
- ) {
- return scheduleNotification(
- new Notification(
- (long)intervalMilliseconds * TicksPerMillisecond,
- this.timeSource.Ticks + ((long)delayMilliseconds * TicksPerMillisecond),
- DateTime.MinValue,
- callback
- )
- );
- }
-
- /// Cancels a scheduled notification
- ///
- /// Handle of the notification that will be cancelled
- ///
- public void Cancel(object notificationHandle) {
- Notification notification = notificationHandle as Notification;
- if(notification != null) {
- notification.Cancelled = true;
- }
- }
-
- /// Called when the system date/time have been adjusted
- /// Time source which detected the adjustment
- /// Not used
- private void dateTimeAdjusted(object sender, EventArgs arguments) {
- lock(this.timerThread) {
- long currentTicks = this.timeSource.Ticks;
- DateTime currentTime = this.timeSource.CurrentUtcTime;
-
- PriorityQueue updatedQueue = new PriorityQueue(
- NotificationComparer.Default
- );
-
- // Copy all notifications from the original queue to a new one, adjusting
- // those with an absolute notification time along the way to a new due tick
- while(this.notifications.Count > 0) {
- Notification notification = this.notifications.Dequeue();
- if(!notification.Cancelled) {
-
- // If this notification has an absolute due time, adjust its due tick
- if(notification.AbsoluteUtcTime != DateTime.MinValue) {
-
- // Combining recurrent notifications with absolute time isn't allowed
- Debug.Assert(notification.IntervalTicks == 0);
-
- // Make the adjustment
- long remainingTicks = (notification.AbsoluteUtcTime - currentTime).Ticks;
- notification.NextDueTicks = currentTicks + remainingTicks;
-
- }
-
- // Notification processed, move it over to the new priority queue
- updatedQueue.Enqueue(notification);
-
- }
- }
-
- // Replace the working queue with the updated queue
- this.notifications = updatedQueue;
- }
-
- this.notificationWaitEvent.Set();
- }
-
- /// Schedules a notification for processing by the timer thread
- /// Notification that will be scheduled
- /// The scheduled notification
- private object scheduleNotification(Notification notification) {
- lock(this.timerThread) {
- this.notifications.Enqueue(notification);
-
- // If this notification has become the next due notification, wake up
- // the timer thread so it can adjust its sleep period.
- if(ReferenceEquals(this.notifications.Peek(), notification)) {
- this.notificationWaitEvent.Set();
- }
- }
-
- return notification;
- }
-
- /// Executes the timer thread
- private void runTimerThread() {
- Notification nextDueNotification;
- lock(this.timerThread) {
- nextDueNotification = getNextDueNotification();
- }
-
- // Keep processing notifications until we're told to quit
- for(; ; ) {
-
- // Wait until the nextmost notification is due or something else wakes us up
- if(nextDueNotification == null) {
- this.notificationWaitEvent.WaitOne();
- } else {
- long remainingTicks = nextDueNotification.NextDueTicks - this.timeSource.Ticks;
- if(remainingTicks > 0) {
- this.timeSource.WaitOne(this.notificationWaitEvent, remainingTicks);
- }
- }
-
- // Have we been woken up because the Scheduler is being disposed?
- if(this.endRequested) {
- break;
- }
-
- // Process all notifications that are due by handing them over to the thread pool.
- // The notification queue might have been updated while we were sleeping, so
- // look for the notification that is due next again
- long ticks = this.timeSource.Ticks;
- lock(this.timerThread) {
- for(; ; ) {
- nextDueNotification = getNextDueNotification();
- if(nextDueNotification == null) {
- break;
- }
-
- // If the next notification is more than a millisecond away, we've reached
- // the end of the notifications we need to process.
- long remainingTicks = (nextDueNotification.NextDueTicks - ticks);
- if(remainingTicks >= TicksPerMillisecond) {
- break;
- }
-
- if(!nextDueNotification.Cancelled) {
- ThreadPool.QueueUserWorkItem(nextDueNotification.Callback);
- }
-
- this.notifications.Dequeue();
- if(nextDueNotification.IntervalTicks != 0) {
- nextDueNotification.NextDueTicks += nextDueNotification.IntervalTicks;
- this.notifications.Enqueue(nextDueNotification);
- }
- } // for
- } // lock
-
- } // for
- }
-
- /// Retrieves the notification that is due next
- /// The notification that is due next
- private Notification getNextDueNotification() {
- while(this.notifications.Count > 0) {
- Notification nextDueNotification = this.notifications.Peek();
- if(nextDueNotification.Cancelled) {
- this.notifications.Dequeue();
- } else {
- return nextDueNotification;
- }
- }
-
- return null;
- }
-
- /// Time source used by the scheduler
- private ITimeSource timeSource;
- /// Thread that will wait for the next scheduled event
- private Thread timerThread;
- /// Notifications in the scheduler's queue
- private PriorityQueue notifications;
-
- /// Event used by the timer thread to wait for the next notification
- private AutoResetEvent notificationWaitEvent;
- /// Whether the timer thread should end
- private volatile bool endRequested;
-
- /// Delegate for the dateTimeAdjusted() method
- private EventHandler dateTimeAdjustedDelegate;
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/ThreadCallbackOperation.Test.cs b/Source/Scheduling/ThreadCallbackOperation.Test.cs
deleted file mode 100644
index 44ecf1e..0000000
--- a/Source/Scheduling/ThreadCallbackOperation.Test.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.Serialization.Formatters.Binary;
-using System.Threading;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Unit Test for the thread callback operation class
- [TestFixture]
- public class ThreadCallbackOperationTest {
-
- #region class TestThreadOperation
-
- ///
- /// Provides a test callback for unit testing the thread callback operation
- ///
- private class TestCallbackProvider {
-
- /// Method that can be invoked as a callback
- public void Callback() {
- this.called = true;
- }
-
- /// Whether the callback has been called
- public bool Called {
- get { return this.called; }
- }
-
- /// Set to true when the callback has been called
- private bool called;
-
- }
-
- #endregion // class TestOperation
-
- /// Verifies that the default constructor for a thread operation works
- [Test]
- public void TestDefaultConstructor() {
- ThreadCallbackOperation test = new ThreadCallbackOperation(
- new ThreadStart(errorCallback)
- );
- Assert.IsFalse(test.Ended);
- }
-
- ///
- /// Verifies that the threaded operation can execute in a thread pool thread
- ///
- [Test]
- public void TestExecutionInThreadPool() {
- TestCallbackProvider callbackProvider = new TestCallbackProvider();
-
- ThreadCallbackOperation test = new ThreadCallbackOperation(
- new ThreadStart(callbackProvider.Callback), true
- );
-
- Assert.IsFalse(test.Ended);
- Assert.IsFalse(callbackProvider.Called);
- test.Start();
- test.Join();
- Assert.IsTrue(test.Ended);
- Assert.IsTrue(callbackProvider.Called);
- }
-
- ///
- /// Verifies that the threaded operation can execute in an explicit thread
- ///
- [Test]
- public void TestExecutionInExplicitThread() {
- TestCallbackProvider callbackProvider = new TestCallbackProvider();
-
- ThreadCallbackOperation test = new ThreadCallbackOperation(
- new ThreadStart(callbackProvider.Callback), false
- );
-
- Assert.IsFalse(test.Ended);
- Assert.IsFalse(callbackProvider.Called);
- test.Start();
- test.Join();
- Assert.IsTrue(test.Ended);
- Assert.IsTrue(callbackProvider.Called);
- }
-
- ///
- /// Verifies that the threaded operation forwards an exception that occurred in
- /// a thread pool thread.
- ///
- [Test]
- public void TestForwardExceptionFromThreadPool() {
- ThreadCallbackOperation test = new ThreadCallbackOperation(
- new ThreadStart(errorCallback), false
- );
-
- Assert.IsFalse(test.Ended);
- test.Start();
- Assert.Throws(
- delegate() { test.Join(); }
- );
- }
-
- ///
- /// Verifies that the threaded operation forwards an exception that occurred in
- /// an explicit thread.
- ///
- [Test]
- public void TestForwardExceptionFromExplicitThread() {
- ThreadCallbackOperation test = new ThreadCallbackOperation(
- new ThreadStart(errorCallback), false
- );
-
- Assert.IsFalse(test.Ended);
- test.Start();
- Assert.Throws(
- delegate() { test.Join(); }
- );
- }
-
- ///
- /// Callback which throws an exception to simulate an error during callback execution
- ///
- private static void errorCallback() {
- throw new AbortedException("Hello World");
- }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
-
-#endif // UNITTEST
diff --git a/Source/Scheduling/ThreadCallbackOperation.cs b/Source/Scheduling/ThreadCallbackOperation.cs
deleted file mode 100644
index 15fc980..0000000
--- a/Source/Scheduling/ThreadCallbackOperation.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Operation that executes a method in a background thread
- public class ThreadCallbackOperation : ThreadOperation {
-
- ///
- /// Initializes a new threaded method operation that will call back a
- /// parameterless method from the background thread.
- ///
- /// Method to be invoked in a background thread
- ///
- /// Uses a ThreadPool thread to execute the method in
- ///
- public ThreadCallbackOperation(ThreadStart method)
- : this(method, true) { }
-
- ///
- /// Initializes a new threaded method operation that will call back a
- /// parameterless method from the background thread and use the
- /// thread pool optionally.
- ///
- /// Method to be invoked in a background thread
- /// Whether to use a ThreadPool thread
- ///
- /// If useThreadPool is false, a new thread will be created. This guarantees
- /// that the method will be executed immediately but has an impact on
- /// performance since the creation of new threads is not a cheap operation.
- ///
- public ThreadCallbackOperation(ThreadStart method, bool useThreadPool)
- : base(useThreadPool) {
-
- this.method = method;
- }
-
- /// Executes the thread callback in the background thread
- protected override void Execute() {
- this.method();
- }
-
- /// Method to be invoked in a background thread
- private ThreadStart method;
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/ThreadOperation.Test.cs b/Source/Scheduling/ThreadOperation.Test.cs
deleted file mode 100644
index 0558509..0000000
--- a/Source/Scheduling/ThreadOperation.Test.cs
+++ /dev/null
@@ -1,139 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Runtime.Serialization.Formatters.Binary;
-
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Unit Test for the thread operation class
- [TestFixture]
- public class ThreadOperationTest {
-
- #region class TestThreadOperation
-
- /// Dummy operation used to run the unit tests
- private class TestThreadOperation : ThreadOperation {
-
- /// Initializes a dummy operation
- public TestThreadOperation() { }
-
- /// Initializes a dummy operation
- /// Whether to use a ThreadPool thread.
- public TestThreadOperation(bool useThreadPool) : base(useThreadPool) { }
-
- /// Contains the payload to be executed in the background thread
- protected override void Execute() { }
-
- }
-
- #endregion // class TestOperation
-
- #region class FailingThreadOperation
-
- /// Dummy operation used to run the unit tests
- private class FailingThreadOperation : ThreadOperation {
-
- /// Initializes a dummy operation
- /// Whether to use a ThreadPool thread.
- public FailingThreadOperation(bool useThreadPool) : base(useThreadPool) { }
-
- /// Contains the payload to be executed in the background thread
- protected override void Execute() {
- throw new AbortedException("Hello World");
- }
-
- }
-
- #endregion // class FailingThreadOperation
-
- /// Verifies that the default constructor for a thread operation works
- [Test]
- public void TestDefaultConstructor() {
- TestThreadOperation test = new TestThreadOperation();
- Assert.IsFalse(test.Ended);
- }
-
- ///
- /// Verifies that the threaded operation can execute in a thread pool thread
- ///
- [Test]
- public void TestExecutionInThreadPool() {
- TestThreadOperation test = new TestThreadOperation(true);
- Assert.IsFalse(test.Ended);
- test.Start();
- test.Join();
- Assert.IsTrue(test.Ended);
- }
-
- ///
- /// Verifies that the threaded operation can execute in an explicit thread
- ///
- [Test]
- public void TestExecutionInExplicitThread() {
- TestThreadOperation test = new TestThreadOperation(false);
- Assert.IsFalse(test.Ended);
- test.Start();
- test.Join();
- Assert.IsTrue(test.Ended);
- }
-
- ///
- /// Verifies that the threaded operation forwards an exception that occurred in
- /// a thread pool thread.
- ///
- [Test]
- public void TestForwardExceptionFromThreadPool() {
- FailingThreadOperation test = new FailingThreadOperation(true);
-
- Assert.IsFalse(test.Ended);
- test.Start();
- Assert.Throws(
- delegate() { test.Join(); }
- );
- }
-
- ///
- /// Verifies that the threaded operation forwards an exception that occurred in
- /// an explicit thread.
- ///
- [Test]
- public void TestForwardExceptionFromExplicitThread() {
- FailingThreadOperation test = new FailingThreadOperation(false);
-
- Assert.IsFalse(test.Ended);
- test.Start();
- Assert.Throws(
- delegate() { test.Join(); }
- );
- }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
-
-#endif // UNITTEST
diff --git a/Source/Scheduling/ThreadOperation.cs b/Source/Scheduling/ThreadOperation.cs
deleted file mode 100644
index 67351b6..0000000
--- a/Source/Scheduling/ThreadOperation.cs
+++ /dev/null
@@ -1,112 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Diagnostics;
-using System.Threading;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Operation that executes a method in a background thread
- public abstract class ThreadOperation : Operation {
-
- ///
- /// Initializes a new threaded operation.
- ///
- ///
- /// Uses a ThreadPool thread to execute the method in a background thread.
- ///
- public ThreadOperation() : this(true) { }
-
- ///
- /// Initializes a new threaded operation which optionally uses the ThreadPool.
- ///
- /// Whether to use a ThreadPool thread.
- ///
- /// If useThreadPool is false, a new thread will be created. This guarantees
- /// that the method will be executed immediately but has an impact on
- /// performance since the creation of new threads is not a cheap operation.
- ///
- public ThreadOperation(bool useThreadPool) {
- this.useThreadPool = useThreadPool;
- }
-
- /// Launches the background operation
- public override void Start() {
- Debug.Assert(
- !Ended,
- "Tried to Start an Operation again that has already ended",
- "Operations cannot be re-run"
- );
- if(useThreadPool) {
- ThreadPool.QueueUserWorkItem(new WaitCallback(callMethod));
- } else {
- Thread thread = new Thread(new ThreadStart(callMethod));
- thread.Name = "Nuclex.Support.Scheduling.ThreadOperation";
- thread.IsBackground = true;
- thread.Start();
- }
- }
-
- /// Contains the payload to be executed in the background thread
- protected abstract void Execute();
-
- /// Invokes the delegate passed as an argument
- /// Not used
- private void callMethod(object state) {
- callMethod();
- }
-
- /// Invokes the delegate passed as an argument
- private void callMethod() {
- try {
- Execute();
- Debug.Assert(
- !Ended,
- "Operation unexpectedly ended during Execute()",
- "Do not call OnAsyncEnded() yourself when deriving from ThreadOperation"
- );
- }
- catch(Exception exception) {
- this.exception = exception;
- }
- finally {
- OnAsyncEnded();
- }
- }
-
- ///
- /// Allows the specific request implementation to re-throw an exception if
- /// the background process finished unsuccessfully
- ///
- protected override void ReraiseExceptions() {
- if(this.exception != null)
- throw this.exception;
- }
-
- /// Whether to use the ThreadPool for obtaining a background thread
- private bool useThreadPool;
- /// Exception that has occured in the background process
- private volatile Exception exception;
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Scheduling/WindowsTimeSource.Test.cs b/Source/Scheduling/WindowsTimeSource.Test.cs
deleted file mode 100644
index 7ef272e..0000000
--- a/Source/Scheduling/WindowsTimeSource.Test.cs
+++ /dev/null
@@ -1,164 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-#if WINDOWS
-
-using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-using Microsoft.Win32;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Scheduling {
-
- /// Unit Test for the windows time source
- [TestFixture]
- public class WindowsTimeSourceTest {
-
- #region class TestWindowsTimeSource
-
- /// Windows time source used for testing
- private class TestWindowsTimeSource : WindowsTimeSource {
-
- ///
- /// Forces a time change notification even if the system time was not adjusted
- ///
- public void ForceTimeChange() {
- OnDateTimeAdjusted(this, EventArgs.Empty);
- }
- }
-
- #endregion // class TestWindowsTimeSource
-
- #region class TestTimeChangedSubscriber
-
- /// Dummy subscriber used to test the time changed event
- private class TestTimeChangedSubscriber {
-
- /// Callback subscribed to the TimeChanged event
- /// Not used
- /// Not used
- public void TimeChanged(object sender, EventArgs arguments) {
- ++this.CallCount;
- }
-
- /// Number of times the callback was invoked
- public int CallCount;
-
- }
-
- #endregion // class TestTimeChangedSubscriber
-
- ///
- /// Verifies that the time source's default constructor is working
- ///
- [Test]
- public void TestDefaultConstructor() {
- using(WindowsTimeSource timeSource = new WindowsTimeSource()) { }
- }
-
- ///
- /// Verifies that the time source can provide the current UTC time
- ///
- [Test]
- public void TestCurrentUtcTime() {
- using(WindowsTimeSource timeSource = new WindowsTimeSource()) {
-
- Assert.That(
- timeSource.CurrentUtcTime, Is.EqualTo(DateTime.UtcNow).Within(10).Seconds
- );
- }
- }
-
- ///
- /// Verifies that the time source's tick property is working if
- /// the Stopwatch class is used to measure time
- ///
- [Test]
- public void TestTicks() {
- using(WindowsTimeSource timeSource = new WindowsTimeSource()) {
- long ticks1 = timeSource.Ticks;
- long ticks2 = timeSource.Ticks;
-
- Assert.That(ticks2, Is.GreaterThanOrEqualTo(ticks1));
- }
- }
-
- ///
- /// Verifies that the time source's WaitOne() method works correctly
- ///
- [Test]
- public void TestWaitOne() {
- using(WindowsTimeSource timeSource = new WindowsTimeSource()) {
- AutoResetEvent waitEvent = new AutoResetEvent(true);
-
- Assert.IsTrue(timeSource.WaitOne(waitEvent, TimeSpan.FromMilliseconds(1).Ticks));
- Assert.IsFalse(timeSource.WaitOne(waitEvent, TimeSpan.FromMilliseconds(1).Ticks));
- }
- }
-
- ///
- /// Verifies that the default time source's WaitOne() method works correctly
- ///
- [Test]
- public void TestTimeChange() {
- using(TestWindowsTimeSource timeSource = new TestWindowsTimeSource()) {
- TestTimeChangedSubscriber subscriber = new TestTimeChangedSubscriber();
-
- EventHandler callbackDelegate = new EventHandler(subscriber.TimeChanged);
- timeSource.DateTimeAdjusted += callbackDelegate;
- try {
- timeSource.ForceTimeChange();
- }
- finally {
- timeSource.DateTimeAdjusted -= callbackDelegate;
- }
-
- // Using greater than because during the test run a real time change notification
- // might have happened, increasing the counter to 2 or more.
- Assert.That(subscriber.CallCount, Is.GreaterThanOrEqualTo(1));
- }
- }
-
- ///
- /// Tests whether the Windows-specific time source can reports its availability on
- /// the current platform
- ///
- [Test]
- public void TestAvailability() {
- bool isAvailable = WindowsTimeSource.Available;
- Assert.IsTrue(
- (isAvailable == true) ||
- (isAvailable == false)
- );
- }
-
- }
-
-} // namespace Nuclex.Support.Scheduling
-
-#endif // WINDOWS
-
-#endif // UNITTEST
diff --git a/Source/Scheduling/WindowsTimeSource.cs b/Source/Scheduling/WindowsTimeSource.cs
deleted file mode 100644
index e40b5c1..0000000
--- a/Source/Scheduling/WindowsTimeSource.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-
-#if !NO_SYSTEMEVENTS
-using Microsoft.Win32;
-#endif
-
-namespace Nuclex.Support.Scheduling {
-
- ///
- /// Time source that makes use of additional features only available on Windows
- ///
- public class WindowsTimeSource : GenericTimeSource, IDisposable {
-
- /// Number of ticks (100 ns intervals) in a millisecond
- private const long TicksPerMillisecond = 10000;
-
- /// Initializes a new Windows time source
- public WindowsTimeSource() {
-#if NO_SYSTEMEVENTS
- throw new InvalidOperationException(
- "Windows time source is not available without the SystemEvents class"
- );
-#else
- this.onDateTimeAdjustedDelegate = new EventHandler(OnDateTimeAdjusted);
- SystemEvents.TimeChanged += this.onDateTimeAdjustedDelegate;
-#endif
- }
-
- /// Immediately releases all resources owned by the instance
- public void Dispose() {
-#if !NO_SYSTEMEVENTS
- if (this.onDateTimeAdjustedDelegate != null) {
- SystemEvents.TimeChanged -= this.onDateTimeAdjustedDelegate;
- this.onDateTimeAdjustedDelegate = null;
- }
-#endif
- }
-
- /// Waits for an AutoResetEvent to become signalled
- /// WaitHandle the method will wait for
- /// Number of ticks to wait
- ///
- /// True if the WaitHandle was signalled, false if the timeout was reached
- ///
- public override bool WaitOne(AutoResetEvent waitHandle, long ticks) {
-#if WINDOWS
- return waitHandle.WaitOne((int)(ticks / TicksPerMillisecond), false);
-#else
- return waitHandle.WaitOne((int)(ticks / TicksPerMillisecond));
-#endif
- }
-
- ///
- /// Whether the Windows time source can be used on the current platform
- ///
- public static bool Available {
- get { return Environment.OSVersion.Platform == PlatformID.Win32NT; }
- }
-
-#if !NO_SYSTEMEVENTS
-
- /// Delegate for the timeChanged() callback method
- private EventHandler onDateTimeAdjustedDelegate;
-
-#endif // !NO_SYSTEMEVENTS
-
- }
-
-} // namespace Nuclex.Support.Scheduling
diff --git a/Source/Tracking/IProgressReporter.cs b/Source/Tracking/IProgressReporter.cs
deleted file mode 100644
index f9523e4..0000000
--- a/Source/Tracking/IProgressReporter.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 {
-
- /// Interface for processes that report their progress
- public interface IProgressReporter {
-
- /// Triggered when the status of the process changes
- event EventHandler AsyncProgressChanged;
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/IStatusReporter.cs b/Source/Tracking/IStatusReporter.cs
deleted file mode 100644
index 91bd1cc..0000000
--- a/Source/Tracking/IStatusReporter.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 {
-
- /// Interface for processes that report their status
- public interface IStatusReporter {
-
- /// Triggered when the status of the process changes
- event EventHandler AsyncStatusChanged;
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/IdleStateEventArgs.Test.cs b/Source/Tracking/IdleStateEventArgs.Test.cs
deleted file mode 100644
index 6eefc8c..0000000
--- a/Source/Tracking/IdleStateEventArgs.Test.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.IO;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the "idle state" event argument container
- [TestFixture]
- public class IdleStateEventArgsTest {
-
- ///
- /// Tests whether the idle state event arguments correctly report a non-idle state
- ///
- [Test]
- public void TestIdleStateChangedToFalse() {
- IdleStateEventArgs idleStateFalse = new IdleStateEventArgs(false);
-
- Assert.IsFalse(idleStateFalse.Idle);
- }
-
- ///
- /// Tests whether the idle state event arguments correctly report an idle state
- ///
- [Test]
- public void TestIdleStateChangedToTrue() {
- IdleStateEventArgs idleStateFalse = new IdleStateEventArgs(true);
-
- Assert.IsTrue(idleStateFalse.Idle);
- }
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/IdleStateEventArgs.cs b/Source/Tracking/IdleStateEventArgs.cs
deleted file mode 100644
index 1143f74..0000000
--- a/Source/Tracking/IdleStateEventArgs.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 {
-
- /// Event arguments for an idle state change notification
- public class IdleStateEventArgs : EventArgs {
-
- /// Initializes the idle state change notification
- /// The new idle state
- public IdleStateEventArgs(bool idle) {
- this.idle = idle;
- }
-
- /// Current idle state
- public bool Idle {
- get { return this.idle; }
- }
-
- /// Current idle state
- private bool idle;
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/Internal/ObservedWeightedTransaction.Test.cs b/Source/Tracking/Internal/ObservedWeightedTransaction.Test.cs
deleted file mode 100644
index 338ef2b..0000000
--- a/Source/Tracking/Internal/ObservedWeightedTransaction.Test.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 NMock;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the observation wrapper of weighted transactions
- [TestFixture]
- public class ObservedWeightedTransactionTest {
-
- #region interface IObservationSubscriber
-
- ///
- /// Interface used to test the observation wrapper of weighted transactions
- ///
- public interface IObservationSubscriber {
-
- /// Will be invoked when an observed transaction's progress changes
- void ProgressUpdated();
-
- /// Will be invoked when an observed transaction completes
- void Ended();
-
- }
-
- #endregion // interface IObservationSubscriber
-
- #region class FunkyTransaction
-
- ///
- /// Transaction that goes into the 'ended' state as soon as someone registers for
- /// state change notifications
- ///
- private class FunkyTransaction : Transaction {
-
- /// Manages registrations to the AsyncEnded event
- 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;
- }
- }
-
- /// Whether the transaction has already been ended
- private int alreadyEnded;
-
- }
-
- #endregion // class FunkyTransaction
-
- /// Initialization routine executed before each test is run
- [SetUp]
- public void Setup() {
- this.mockery = new MockFactory();
- }
-
- /// Verifies that the constructor of the observation wrapper works
- [Test]
- public void TestConstructorWithAlreadyEndedTransaction() {
- WeightedTransaction testTransaction = new WeightedTransaction(
- Transaction.EndedDummy
- );
-
- Mock subscriber = this.mockery.CreateMock();
-
- subscriber.Expects.AtLeast(0).Method(m => m.ProgressUpdated());
- // This should no be called because otherwise, the 'Ended' event would be raised
- // to the transaction group before all transactions have been added into
- // the internal list, leading to an early ending or even multiple endings.
- subscriber.Expects.No.Method(m => m.Ended());
-
- using(
- ObservedWeightedTransaction test =
- new ObservedWeightedTransaction(
- testTransaction,
- new ObservedWeightedTransaction.ReportDelegate(
- subscriber.MockObject.ProgressUpdated
- ),
- new ObservedWeightedTransaction.ReportDelegate(
- subscriber.MockObject.Ended
- )
- )
- ) {
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
- }
-
- ///
- /// Verifies that the constructor of the observation wrapper can handle a transaction
- /// entering the 'ended' state right on subscription
- ///
- [Test]
- public void TestConstructorWithEndingTransaction() {
- WeightedTransaction testTransaction = new WeightedTransaction(
- new FunkyTransaction()
- );
-
- Mock subscriber = this.mockery.CreateMock();
-
- subscriber.Expects.AtLeast(0).Method(m => m.ProgressUpdated());
- subscriber.Expects.One.Method(m => m.Ended());
-
- using(
- ObservedWeightedTransaction test =
- new ObservedWeightedTransaction(
- testTransaction,
- new ObservedWeightedTransaction.ReportDelegate(
- subscriber.MockObject.ProgressUpdated
- ),
- new ObservedWeightedTransaction.ReportDelegate(
- subscriber.MockObject.Ended
- )
- )
- ) {
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
- }
-
- /// Mock object factory
- private MockFactory mockery;
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/Internal/ObservedWeightedTransaction.cs b/Source/Tracking/Internal/ObservedWeightedTransaction.cs
deleted file mode 100644
index e4259b1..0000000
--- a/Source/Tracking/Internal/ObservedWeightedTransaction.cs
+++ /dev/null
@@ -1,186 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 {
-
- /// Transaction being observed by another object
- ///
- /// Type of the transaction that is being observed
- ///
- internal class ObservedWeightedTransaction : IDisposable
- where TransactionType : Transaction {
-
- /// Delegate for reporting progress updates
- public delegate void ReportDelegate();
-
- /// Initializes a new observed transaction
- /// Weighted transaction being observed
- ///
- /// Callback to invoke when the transaction's progress changes
- ///
- ///
- /// Callback to invoke when the transaction has ended
- ///
- internal ObservedWeightedTransaction(
- WeightedTransaction 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();
-
- // Do not call the ended callback here. This constructor is called when the
- // TransactionGroup constructs its list of transactions. If this is called and
- // the first transaction to be added to the group happens to be in the ended
- // state, the transactionGroup will immediately think it has ended!
- //!DONT!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(
- asyncProgressChanged
- );
- this.progressReporter.AsyncProgressChanged += this.asyncProgressChangedEventHandler;
- }
- }
-
- /// Immediately releases all resources owned by the object
- public void Dispose() {
- asyncDisconnectEvents();
- }
-
- /// Weighted transaction being observed
- public WeightedTransaction WeightedTransaction {
- get { return this.weightedTransaction; }
- }
-
- /// Amount of progress this transaction has achieved so far
- public float Progress {
- get { return this.progress; }
- }
-
- /// Called when the observed transaction has ended
- /// Transaction that has ended
- /// Not used
- 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();
- }
-
- /// Called when the progress of the observed transaction changes
- /// Transaction whose progress has changed
- /// Contains the updated progress
- private void asyncProgressChanged(object sender, ProgressReportEventArgs arguments) {
- this.progress = arguments.Progress;
-
- ReportDelegate savedProgressUpdateCallback = this.progressUpdateCallback;
- if(savedProgressUpdateCallback != null) {
- savedProgressUpdateCallback();
- }
- }
-
- /// Unsubscribes from all events of the observed transaction
- 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 asyncProgressChangedEventHandler;
- /// The observed transaction's progress reporting interface
- private IProgressReporter progressReporter;
- /// The weighted wable that is being observed
- private WeightedTransaction weightedTransaction;
- /// Callback to invoke when the progress updates
- private volatile ReportDelegate progressUpdateCallback;
- /// Callback to invoke when the transaction ends
- private volatile ReportDelegate endedCallback;
- /// Progress achieved so far
- private volatile float progress;
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/Internal/WeightedTransactionWrapperCollection.Test.cs b/Source/Tracking/Internal/WeightedTransactionWrapperCollection.Test.cs
deleted file mode 100644
index 30e441b..0000000
--- a/Source/Tracking/Internal/WeightedTransactionWrapperCollection.Test.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-
-namespace Nuclex.Support.Tracking {
-
- ///
- /// Unit Test for the observation wrapper collection of weighted transactions
- ///
- [TestFixture]
- public class WeightedTransactionWrapperCollectionTest {
-
- ///
- /// Tests whether the wrapper collection is handing out the unwrapped transactions
- ///
- [Test]
- public void TestWrapperCollection() {
- WeightedTransaction transaction = new WeightedTransaction(
- Transaction.EndedDummy
- );
-
- ObservedWeightedTransaction observed =
- new ObservedWeightedTransaction(
- transaction,
- endedCallback,
- progressUpdatedCallback
- );
-
- WeightedTransactionWrapperCollection wrapper =
- new WeightedTransactionWrapperCollection(
- new ObservedWeightedTransaction[] { observed }
- );
-
- Assert.AreSame(transaction, wrapper[0]);
- }
-
- /// Dummy callback used as event subscriber in the tests
- private void endedCallback() { }
-
- /// Dummy callback used as event subscriber in the tests
- private void progressUpdatedCallback() { }
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/Internal/WeightedTransactionWrapperCollection.cs b/Source/Tracking/Internal/WeightedTransactionWrapperCollection.cs
deleted file mode 100644
index ab13b7c..0000000
--- a/Source/Tracking/Internal/WeightedTransactionWrapperCollection.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-
-using Nuclex.Support.Collections;
-
-namespace Nuclex.Support.Tracking {
-
- /// Collection of transactions with a weighting value
- /// Type of transactions to manage
- ///
- ///
- /// This collection is exposed as a read-only collection to the user that
- /// 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.
- ///
- ///
- /// 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.
- ///
- ///
- internal class WeightedTransactionWrapperCollection :
- TransformingReadOnlyCollection<
- ObservedWeightedTransaction, WeightedTransaction
- >
- where TransactionType : Transaction {
-
- /// Initializes a new weighted transaction collection wrapper
- /// Items to be exposed as weighted transactions
- internal WeightedTransactionWrapperCollection(
- IList> items
- )
- : base(items) { }
-
- /// Transforms an item into the exposed type
- /// Item to be transformed
- /// The transformed item
- ///
- /// This method is used to transform an item in the wrapped collection into
- /// the exposed item type whenever the user accesses an item. Expect it to
- /// be called frequently, because the TransformingReadOnlyCollection does
- /// not cache otherwise store the transformed items.
- ///
- protected override WeightedTransaction Transform(
- ObservedWeightedTransaction item
- ) {
- return item.WeightedTransaction;
- }
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/ProgressReportEventArgs.Test.cs b/Source/Tracking/ProgressReportEventArgs.Test.cs
deleted file mode 100644
index c3bf98d..0000000
--- a/Source/Tracking/ProgressReportEventArgs.Test.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.IO;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the progress report event argument container
- [TestFixture]
- public class ProgressReportEventArgsTest {
-
- ///
- /// Tests whether the progress report event arguments correctly report zero progress
- ///
- [Test]
- public void TestZeroProgress() {
- ProgressReportEventArgs zeroProgress = new ProgressReportEventArgs(0.0f);
-
- Assert.AreEqual(0.0f, zeroProgress.Progress);
- }
-
- ///
- /// Tests whether the progress report event arguments correctly report complete progress
- ///
- [Test]
- public void TestCompleteProgress() {
- ProgressReportEventArgs zeroProgress = new ProgressReportEventArgs(1.0f);
-
- Assert.AreEqual(1.0f, zeroProgress.Progress);
- }
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/ProgressReportEventArgs.cs b/Source/Tracking/ProgressReportEventArgs.cs
deleted file mode 100644
index ea6fefb..0000000
--- a/Source/Tracking/ProgressReportEventArgs.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 {
-
- /// Event arguments for a progress update notification
- public class ProgressReportEventArgs : EventArgs {
-
- /// Initializes the progress update informations
- /// Achieved progress ranging from 0.0 to 1.0
- public ProgressReportEventArgs(float progress) {
- this.progress = progress;
- }
-
- /// Currently achieved progress
- public float Progress {
- get { return this.progress; }
- }
-
- /// Achieved progress
- private float progress;
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/ProgressTracker.Test.cs b/Source/Tracking/ProgressTracker.Test.cs
deleted file mode 100644
index 49d4be9..0000000
--- a/Source/Tracking/ProgressTracker.Test.cs
+++ /dev/null
@@ -1,509 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-
-using NUnit.Framework;
-using NMock;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the progress tracker class
- [TestFixture]
- public class ProgressTrackerTest {
-
- #region interface IProgressTrackerSubscriber
-
- /// Interface used to test the progress tracker
- public interface IProgressTrackerSubscriber {
-
- /// Called when the tracked progress changes
- /// Progress tracker whose progress has changed
- /// Contains the new progress achieved
- void ProgressChanged(object sender, ProgressReportEventArgs arguments);
-
- /// Called when the progress tracker's idle state changes
- /// Progress tracker whose idle state has changed
- /// Contains the new idle state of the tracker
- void IdleStateChanged(object sender, IdleStateEventArgs arguments);
-
- }
-
- #endregion // interface IProgressTrackerSubscriber
-
- #region class ProgressUpdateEventArgsMatcher
-
- /// Compares two ProgressUpdateEventArgs instances
- private class ProgressReportEventArgsMatcher : Matcher {
-
- /// Initializes a new ProgressUpdateEventArgsMatcher
- /// Expected progress update event arguments
- public ProgressReportEventArgsMatcher(ProgressReportEventArgs expected) {
- this.expected = expected;
- }
-
- ///
- /// Called by NMock to verfiy the ProgressUpdateEventArgs match the expected value
- ///
- /// Actual value to compare to the expected value
- ///
- /// True if the actual value matches the expected value; otherwise false
- ///
- public override bool Matches(object actualAsObject) {
- ProgressReportEventArgs actual = (actualAsObject as ProgressReportEventArgs);
- if(actual == null)
- return false;
-
- return (actual.Progress == this.expected.Progress);
- }
-
- /// Creates a string representation of the expected value
- /// Writer to write the string representation into
- public override void DescribeTo(TextWriter writer) {
- writer.Write(this.expected.Progress.ToString());
- }
-
- /// Expected progress update event args value
- private ProgressReportEventArgs expected;
-
- }
-
- #endregion // class ProgressUpdateEventArgsMatcher
-
- #region class TestTransaction
-
- /// Transaction used for testing in this unit test
- private class TestTransaction : Transaction, IProgressReporter {
-
- /// will be triggered to report when progress has been achieved
- public event EventHandler AsyncProgressChanged;
-
- /// Changes the testing transaction's indicated progress
- /// New progress to be reported by the testing transaction
- public void ChangeProgress(float progress) {
- OnAsyncProgressChanged(progress);
- }
-
- /// Transitions the transaction into the ended state
- public void End() {
- OnAsyncEnded();
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this transaction about the achieved progress.
- ///
- protected virtual void OnAsyncProgressChanged(float progress) {
- OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this transaction about the achieved progress.
- /// Allows for classes derived from the transaction class to easily provide
- /// a custom event arguments class that has been derived from the
- /// transaction's ProgressUpdateEventArgs class.
- ///
- protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
- EventHandler copy = AsyncProgressChanged;
- if(copy != null)
- copy(this, eventArguments);
- }
-
- }
-
- #endregion // class TestTransaction
-
- #region class EvilTransaction
-
- ///
- /// Transaction that tries to emulate a thread giving a progress report at
- /// a very inconvenient time ;)
- ///
- private class EvilTransaction : Transaction, IProgressReporter {
-
- /// will be triggered to report when progress has been achieved
- public event EventHandler AsyncProgressChanged {
- add { }
- remove {
- // Send a progress update right when the subscriber is trying to unsubscribe
- value(this, new ProgressReportEventArgs(0.5f));
- }
- }
-
- }
-
- #endregion // class EvilTransaction
-
- /// Initialization routine executed before each test is run
- [SetUp]
- public void Setup() {
- this.mockery = new MockFactory();
- }
-
- /// Validates that the tracker properly sums the progress
- [Test]
- public void TestSummedProgress() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- Mock mockedSubscriber = mockSubscriber(tracker);
-
- TestTransaction test1 = new TestTransaction();
- TestTransaction test2 = new TestTransaction();
-
- // Step 1
- {
- mockedSubscriber.Expects.One.Method(
- m => m.IdleStateChanged(null, null)
- ).WithAnyArguments();
-
- // Since the progress is already at 0, these redundant reports are optional
- mockedSubscriber.Expects.Between(0, 2).Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(0.0f))
- );
-
- tracker.Track(test1);
- tracker.Track(test2);
- }
-
- // Step 2
- {
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(0.25f))
- );
-
- test1.ChangeProgress(0.5f);
- }
- }
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
-
- ///
- /// Validates that the tracker only removes transactions when the whole
- /// tracking list has reached the 'ended' state.
- ///
- ///
- /// If the tracker would remove ended transactions right when they finished,
- /// the total progress would jump back each time. This is unwanted, of course.
- ///
- [Test]
- public void TestDelayedRemoval() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- Mock mockedSubscriber = mockSubscriber(tracker);
-
- TestTransaction test1 = new TestTransaction();
- TestTransaction test2 = new TestTransaction();
-
- // Step 1
- {
- mockedSubscriber.Expects.One.Method(
- m => m.IdleStateChanged(null, null)
- ).WithAnyArguments();
-
- // This is optional. The tracker's progress is currently 0, so there's no need
- // to send out superfluous progress reports.
- mockedSubscriber.Expects.Between(0, 2).Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(0.0f))
- );
-
- tracker.Track(test1);
- tracker.Track(test2);
- }
-
- // Step 2
- {
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(0.25f))
- );
-
- // Total progress should be 0.25 after this call (two transactions, one with
- // 0% progress and one with 50% progress)
- test1.ChangeProgress(0.5f);
- }
-
- // Step 3
- {
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(0.75f))
- );
-
- // Total progress should be 0.75 after this call (one transaction at 100%,
- // the other one at 50%). If the second transaction would be removed by the tracker,
- // (which would be inappropriate!) the progress would falsely jump to 0.5 instead.
- test2.End();
- }
-
- // Step 4
- {
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(1.0f))
- );
-
- mockedSubscriber.Expects.One.Method(
- m => m.IdleStateChanged(null, null)
- ).WithAnyArguments();
-
- test1.End();
- }
- }
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
-
- ///
- /// Validates that the tracker behaves correctly if it is fed with transactions
- /// that have already ended.
- ///
- [Test]
- public void TestSoleEndedTransaction() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- Mock mockedSubscriber = mockSubscriber(tracker);
-
- mockedSubscriber.Expects.No.Method(
- m => m.IdleStateChanged(null, null)
- ).WithAnyArguments();
- mockedSubscriber.Expects.No.Method(
- m => m.ProgressChanged(null, null)
- ).WithAnyArguments();
-
- tracker.Track(Transaction.EndedDummy);
- }
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
-
- ///
- /// Validates that the tracker behaves correctly if it is fed with transactions
- /// that have already ended in addition to transactions that are actively executing.
- ///
- [Test]
- public void TestEndedTransaction() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- Mock mockedSubscriber = mockSubscriber(tracker);
-
- TestTransaction test1 = new TestTransaction();
-
- // Step 1
- {
- mockedSubscriber.Expects.One.Method(
- m => m.IdleStateChanged(null, null)
- ).WithAnyArguments();
-
- mockedSubscriber.Expects.AtMost(1).Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(0.0f))
- );
-
- tracker.Track(test1);
- }
-
- // Step 2
- {
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(0.5f))
- );
-
- tracker.Track(Transaction.EndedDummy);
- }
-
- // Step 3
- {
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(ProgressTracker)),
- new ProgressReportEventArgsMatcher(new ProgressReportEventArgs(1.0f))
- );
-
- mockedSubscriber.Expects.One.Method(
- m => m.IdleStateChanged(null, null)
- ).WithAnyArguments();
-
- test1.End();
- }
- }
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
-
- ///
- /// Tries to provoke a deadlock by re-entering the tracker from one of its own events
- ///
- [Test]
- public void TestProvokedDeadlock() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- TestTransaction test1 = new TestTransaction();
- tracker.Track(test1);
-
- tracker.AsyncIdleStateChanged +=
- (EventHandler)delegate(object sender, IdleStateEventArgs arguments) {
- tracker.Track(Transaction.EndedDummy);
- };
-
- test1.End();
- }
- }
-
- ///
- /// Tests whether the progress tracker enters and leaves the idle state correctly
- /// when a transaction is removed via Untrack()
- ///
- [Test]
- public void TestIdleWithUntrack() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- TestTransaction test1 = new TestTransaction();
-
- Assert.IsTrue(tracker.Idle);
-
- tracker.Track(test1);
-
- Assert.IsFalse(tracker.Idle);
-
- tracker.Untrack(test1);
-
- Assert.IsTrue(tracker.Idle);
- }
- }
-
- ///
- /// Tests whether the progress tracker enters and leaves the idle state correctly
- /// when a transaction is removed the transaction finishing
- ///
- [Test]
- public void TestIdleWithAutoRemoval() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- TestTransaction test1 = new TestTransaction();
-
- Assert.IsTrue(tracker.Idle);
-
- tracker.Track(test1);
-
- Assert.IsFalse(tracker.Idle);
-
- test1.End();
-
- Assert.IsTrue(tracker.Idle);
- }
- }
-
- ///
- /// Tests whether the progress tracker enters and leaves the idle state correctly
- /// when a transaction is removed via Untrack()
- ///
- [Test]
- public void TestProgressWithUntrack() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- TestTransaction test1 = new TestTransaction();
- TestTransaction test2 = new TestTransaction();
- tracker.Track(test1);
- tracker.Track(test2);
-
- Assert.AreEqual(0.0f, tracker.Progress);
-
- test1.ChangeProgress(0.5f);
-
- Assert.AreEqual(0.25f, tracker.Progress);
-
- tracker.Untrack(test2);
-
- Assert.AreEqual(0.5f, tracker.Progress);
- }
- }
-
- ///
- /// Verifies that the progress tracker throws an exception if it is instructed
- /// to untrack a transaction it doesn't know about
- ///
- [Test]
- public void TestThrowOnUntrackNonTrackedTransaction() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- TestTransaction test1 = new TestTransaction();
-
- Assert.Throws(
- delegate() { tracker.Untrack(test1); }
- );
- }
- }
-
- ///
- /// Verifies that the progress tracker throws an exception if it is instructed
- /// to untrack a transaction it doesn't know about
- ///
- [Test]
- public void TestProgressReportDuringUnsubscribe() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- EvilTransaction evil = new EvilTransaction();
- tracker.Track(evil);
- tracker.Untrack(evil);
- }
- }
-
- ///
- /// Verifies that the progress tracker doesn't choke on a transaction being tracked
- /// multiple times.
- ///
- [Test]
- public void TestMultiTrackedTransaction() {
- using(ProgressTracker tracker = new ProgressTracker()) {
- TestTransaction test = new TestTransaction();
- tracker.Track(test);
- tracker.Track(test);
- tracker.Track(test);
- tracker.Untrack(test);
- tracker.Untrack(test);
- tracker.Untrack(test);
- }
- }
-
- /// Mocks a subscriber for the events of a tracker
- /// Tracker to mock an event subscriber for
- /// The mocked event subscriber
- private Mock mockSubscriber(ProgressTracker tracker) {
- Mock mockedSubscriber =
- this.mockery.CreateMock();
-
- tracker.AsyncIdleStateChanged +=
- new EventHandler(mockedSubscriber.MockObject.IdleStateChanged);
-
- tracker.AsyncProgressChanged +=
- new EventHandler(mockedSubscriber.MockObject.ProgressChanged);
-
- return mockedSubscriber;
- }
-
- /// Mock object factory
- private MockFactory mockery;
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/ProgressTracker.cs b/Source/Tracking/ProgressTracker.cs
deleted file mode 100644
index a0a49f3..0000000
--- a/Source/Tracking/ProgressTracker.cs
+++ /dev/null
@@ -1,369 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-
-namespace Nuclex.Support.Tracking {
-
- ///
- /// Helps tracking the progress of one or more background transactions
- ///
- ///
- ///
- /// This is useful if you want to display a progress bar for multiple
- /// transactions but can not guarantee that no additional transactions
- /// will appear inmidst of execution.
- ///
- ///
- /// This class does not implement the interface itself
- /// in order to not violate the design principles of transactions which
- /// guarantee that a will only finish once (whereas the
- /// progress tracker might 'finish' any number of times).
- ///
- ///
- public class ProgressTracker : IDisposable, IProgressReporter {
-
- #region class TransactionMatcher
-
- /// Matches a direct transaction to a fully wrapped one
- private class TransactionMatcher {
-
- ///
- /// Initializes a new transaction matcher that matches against
- /// the specified transaction
- ///
- /// Transaction to match against
- public TransactionMatcher(Transaction toMatch) {
- this.toMatch = toMatch;
- }
-
- ///
- /// Checks whether the provided transaction matches the comparison
- /// transaction of the instance
- ///
- /// Transaction to match to the comparison transaction
- public bool Matches(ObservedWeightedTransaction other) {
- return ReferenceEquals(other.WeightedTransaction.Transaction, this.toMatch);
- }
-
- /// Transaction this instance compares against
- private Transaction toMatch;
-
- }
-
- #endregion // class TransactionMatcher
-
- /// Triggered when the idle state of the tracker changes
- ///
- /// The tracker is idle when no transactions are being tracked in it. If you're
- /// using this class to feed a progress bar, this would be the event to use for
- /// showing or hiding the progress bar. The tracker starts off as idle because,
- /// upon construction, its list of transactions will be empty.
- ///
- public event EventHandler AsyncIdleStateChanged;
-
- /// Triggered when the total progress has changed
- public event EventHandler AsyncProgressChanged;
-
- /// Initializes a new transaction tracker
- public ProgressTracker() {
-
- this.trackedTransactions = new List>();
- this.idle = true;
-
- this.asyncEndedDelegate =
- new ObservedWeightedTransaction.ReportDelegate(asyncEnded);
- this.asyncProgressUpdatedDelegate =
- new ObservedWeightedTransaction.ReportDelegate(asyncProgressChanged);
-
- }
-
- /// Immediately releases all resources owned by the instance
- public void Dispose() {
- lock(this.trackedTransactions) {
-
- // Get rid of all transactions we're tracking. This unsubscribes the
- // observers from the events of the transactions and stops us from
- // being kept alive and receiving any further events if some of the
- // tracked transactions are still executing.
- for(int index = 0; index < this.trackedTransactions.Count; ++index)
- this.trackedTransactions[index].Dispose();
-
- // Help the GC a bit by untangling the references :)
- this.trackedTransactions.Clear();
- this.trackedTransactions = null;
-
- } // lock
- }
-
- /// Begins tracking the specified background transactions
- /// Background transaction to be tracked
- public void Track(Transaction transaction) {
- Track(transaction, 1.0f);
- }
-
- /// Begins tracking the specified background transaction
- /// Background transaction to be tracked
- /// Weight to assign to this background transaction
- public void Track(Transaction transaction, float weight) {
-
- // Add the new transaction into the tracking list. This has to be done
- // inside a lock to prevent issues with the progressUpdate callback, which could
- // access the totalWeight field before it has been updated to reflect the
- // new transaction added to the collection.
- lock(this.trackedTransactions) {
-
- bool wasEmpty = (this.trackedTransactions.Count == 0);
-
- if(transaction.Ended) {
-
- // If the ended transaction would become the only transaction in the list,
- // there's no sense in doing anything at all because it would have to be
- // thrown right out again. Only add the transaction when there are other
- // running transactions to properly sum total progress for consistency.
- if(!wasEmpty) {
-
- // Construct a new observation wrapper. This is done inside the lock
- // because as soon as we are subscribed to the events, we can potentially
- // receive them. The lock eliminates the risk of processing a progress update
- // before the transaction has been added to the tracked transactions list.
- this.trackedTransactions.Add(
- new ObservedWeightedTransaction(
- new WeightedTransaction(transaction, weight),
- this.asyncProgressUpdatedDelegate,
- this.asyncEndedDelegate
- )
- );
-
- }
-
- } else { // Not ended -- Transaction is still running
-
- // Construct a new transation observer and add the transaction to our
- // list of tracked transactions.
- ObservedWeightedTransaction observedTransaction =
- new ObservedWeightedTransaction(
- new WeightedTransaction(transaction, weight),
- this.asyncProgressUpdatedDelegate,
- this.asyncEndedDelegate
- );
-
- this.trackedTransactions.Add(observedTransaction);
-
- // If this is the first transaction to be added to the list, tell our
- // owner that we're idle no longer!
- if(wasEmpty) {
- setIdle(false);
- }
-
- } // if transaction ended
-
- // This can be done after we registered the wrapper to our delegates because
- // any incoming progress updates will be stopped from the danger of a
- // division-by-zero from the potentially still zeroed totalWeight by the lock.
- this.totalWeight += weight;
-
- // All done, the total progress is different now, so force a recalculation and
- // send out the AsyncProgressUpdated event.
- recalculateProgress();
-
- } // lock
-
- }
-
- /// Stops tracking the specified background transaction
- /// Background transaction to stop tracking of
- public void Untrack(Transaction transaction) {
- lock(this.trackedTransactions) {
-
- // Locate the object to be untracked in our collection
- int index;
- for(index = 0; index < this.trackedTransactions.Count; ++index) {
- bool same = ReferenceEquals(
- transaction,
- this.trackedTransactions[index].WeightedTransaction.Transaction
- );
- if(same) {
- break;
- }
- }
- if(index == this.trackedTransactions.Count) {
- throw new ArgumentException("Specified transaction is not being tracked");
- }
-
- // Remove and dispose the transaction the user wants to untrack
- {
- ObservedWeightedTransaction wrappedTransaction =
- this.trackedTransactions[index];
-
- this.trackedTransactions.RemoveAt(index);
- wrappedTransaction.Dispose();
- }
-
- // If the list is empty, then we're back in the idle state
- if(this.trackedTransactions.Count == 0) {
-
- this.totalWeight = 0.0f;
-
- // If we entered the idle state with this call, report the state change!
- setIdle(true);
-
- } else {
-
- // Rebuild the total weight from scratch. Subtracting the removed transaction's
- // weight would work, too, but we might accumulate rounding errors making the sum
- // drift slowly away from the actual value.
- float newTotalWeight = 0.0f;
- for(index = 0; index < this.trackedTransactions.Count; ++index)
- newTotalWeight += this.trackedTransactions[index].WeightedTransaction.Weight;
-
- this.totalWeight = newTotalWeight;
-
- recalculateProgress();
-
- }
-
- } // lock
- }
-
- /// Whether the tracker is currently idle
- public bool Idle {
- get { return this.idle; }
- }
-
- /// Current summed progress of the tracked transactions
- public float Progress {
- get { return this.progress; }
- }
-
- /// Fires the AsyncIdleStateChanged event
- /// New idle state to report
- protected virtual void OnAsyncIdleStateChanged(bool idle) {
- EventHandler copy = AsyncIdleStateChanged;
- if(copy != null)
- copy(this, new IdleStateEventArgs(idle));
- }
-
- /// Fires the AsyncProgressUpdated event
- /// New progress to report
- protected virtual void OnAsyncProgressUpdated(float progress) {
- EventHandler copy = AsyncProgressChanged;
- if(copy != null)
- copy(this, new ProgressReportEventArgs(progress));
- }
-
- /// Recalculates the total progress of the tracker
- private void recalculateProgress() {
- bool progressChanged = false;
-
- // Lock the collection to avoid trouble when someone tries to remove one
- // of our tracked transactions while we're just doing a progress update
- lock(this.trackedTransactions) {
-
- // This is a safety measure. In theory, even after all transactions have
- // ended and the collection of tracked transactions is cleared, a waiting
- // thread might deliver another progress update causing this method to
- // be entered. In this case, the right thing is to do nothing at all.
- if(this.totalWeight != 0.0f) {
- float totalProgress = 0.0f;
-
- // Sum up the total progress
- for(int index = 0; index < this.trackedTransactions.Count; ++index) {
- float weight = this.trackedTransactions[index].WeightedTransaction.Weight;
- totalProgress += this.trackedTransactions[index].Progress * weight;
- }
-
- // This also needs to be in the lock to guarantee that the total weight
- // corresponds to the number of transactions we just summed -- by design,
- // the total weight always has to be updated at the same time as the collection.
- totalProgress /= this.totalWeight;
-
- if(totalProgress != this.progress) {
- this.progress = totalProgress;
- progressChanged = true;
- }
- }
-
- } // lock
-
- // Finally, trigger the event if the progress has changed
- if(progressChanged) {
- OnAsyncProgressUpdated(this.progress);
- }
- }
-
- /// Called when one of the tracked transactions has ended
- private void asyncEnded() {
- lock(this.trackedTransactions) {
-
- // If any transactions in the list are still going, keep the entire list.
- // This behavior is intentional in order to prevent the tracker's progress from
- // jumping back repeatedly when multiple tracked transactions come to an end.
- for(int index = 0; index < this.trackedTransactions.Count; ++index)
- if(!this.trackedTransactions[index].WeightedTransaction.Transaction.Ended)
- return;
-
- // All transactions have finished, get rid of the wrappers and make a
- // fresh start for future transactions to be tracked. No need to call
- // Dispose() since, as a matter of fact, when the transaction
- this.trackedTransactions.Clear();
- this.totalWeight = 0.0f;
-
- // Notify our owner that we're idle now. This line is only reached when all
- // transactions were finished, so it's safe to trigger this here.
- setIdle(true);
-
- } // lock
- }
-
- /// Called when one of the tracked transactions has achieved progress
- private void asyncProgressChanged() {
- recalculateProgress();
- }
-
- /// Changes the idle state
- /// Whether or not the tracker is currently idle
- ///
- /// This method expects to be called during a lock() on trackedTransactions!
- ///
- private void setIdle(bool idle) {
- this.idle = idle;
-
- OnAsyncIdleStateChanged(idle);
- }
-
- /// Whether the tracker is currently idle
- private volatile bool idle;
- /// Current summed progress of the tracked transactions
- private volatile float progress;
- /// Total weight of all transactions being tracked
- private volatile float totalWeight;
- /// Transactions being tracked by this tracker
- private List> trackedTransactions;
- /// Delegate for the asyncEnded() method
- private ObservedWeightedTransaction.ReportDelegate asyncEndedDelegate;
- /// Delegate for the asyncProgressUpdated() method
- private ObservedWeightedTransaction.ReportDelegate asyncProgressUpdatedDelegate;
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/Request.Test.cs b/Source/Tracking/Request.Test.cs
deleted file mode 100644
index f97f642..0000000
--- a/Source/Tracking/Request.Test.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-
-using Nuclex.Support.Scheduling;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the request class
- [TestFixture]
- public class RequestTest {
-
- #region class CustomWaitRequest
-
- ///
- /// Request with a custom wait implementation that completes the request instead
- /// of waiting for it complete by outside means
- ///
- private class CustomWaitRequest : Request {
-
- /// Waits until the background process finishes
- public override void Wait() {
- // This could be a race condition if this was used for anything but this simple
- // unit test. Might be neccessary to refactor this when writing advanced tests.
- if(!base.Ended) {
- OnAsyncEnded();
- }
- }
-
- }
-
- #endregion // class CustomWaitRequest
-
- ///
- /// Verifies that the SucceededDummy request is in the ended state
- ///
- [Test]
- public void TestSucceededDummy() {
- Request dummy = Request.SucceededDummy;
-
- Assert.IsTrue(dummy.Ended);
- dummy.Join(); // should not throw
- }
-
- ///
- /// Verifies that the FailedDummy request is in the ended state and throws
- /// an exception when Join()ing
- ///
- [Test]
- public void TestFailedDummy() {
- Request failedDummy = Request.CreateFailedDummy(
- new AbortedException("Hello World")
- );
-
- Assert.IsTrue(failedDummy.Ended);
- Assert.Throws(
- delegate() { failedDummy.Join(); }
- );
- }
-
- ///
- /// Verifies that the Request's Wait() method is invoked if the request is joined
- /// before the request has finished.
- ///
- [Test]
- public void TestJoinWithWaiting() {
- CustomWaitRequest waitRequest = new CustomWaitRequest();
- waitRequest.Join();
- Assert.IsTrue(waitRequest.Ended);
- }
-
-
- }
-
- /// Unit Test for the generic request class
- [TestFixture]
- public class GenericRequestTest {
-
- ///
- /// Verifies that the SucceededDummy request is in the ended state
- ///
- [Test]
- public void TestSucceededDummy() {
- Request dummy = Request.CreateSucceededDummy(12345);
-
- Assert.IsTrue(dummy.Ended);
- Assert.AreEqual(12345, dummy.Join()); // should not throw
- }
-
- ///
- /// Verifies that the FailedDummy request is in the ended state and throws
- /// an exception when Join()ing
- ///
- [Test]
- public void TestFailedDummy() {
- Request failedDummy = Request.CreateFailedDummy(
- new AbortedException("Hello World")
- );
-
- Assert.IsTrue(failedDummy.Ended);
- Assert.Throws(
- delegate() { failedDummy.Join(); }
- );
- }
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/Request.cs b/Source/Tracking/Request.cs
deleted file mode 100644
index 47d481a..0000000
--- a/Source/Tracking/Request.cs
+++ /dev/null
@@ -1,217 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 {
-
- /// Asynchronous request running in the background
- ///
- ///
- /// If the background process fails, the exception that caused it to fail is
- /// 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()
- ///
- ///
- /// Like in the transaction class, the contract requires you to always call
- /// OnAsyncEnded(), no matter what the outcome of your operation is.
- ///
- ///
- public abstract class Request : Transaction {
-
- #region class EndedDummyRequest
-
- /// Dummy request that is always in the ended state
- private class EndedDummyRequest : Request {
- /// Creates a new successfully completed dummy request
- public EndedDummyRequest() : this(null) { }
- /// Creates a new failed dummy request
- /// Exception that caused the dummy to fail
- public EndedDummyRequest(Exception exception) {
- this.exception = exception;
- OnAsyncEnded();
- }
- ///
- /// Allows the specific request implementation to re-throw an exception if
- /// the background process finished unsuccessfully
- ///
- protected override void ReraiseExceptions() {
- if(this.exception != null)
- throw this.exception;
- }
- /// Exception that supposedly caused the request to fail
- private Exception exception;
- }
-
- #endregion // EndedDummyRequest
-
- /// Succeeded dummy request
- ///
- /// Use to indicate success if the request has already been completed at
- /// the time you are asked to perform it.
- ///
- public static readonly Request SucceededDummy = new EndedDummyRequest();
-
- /// Creates a new failed dummy request
- /// Exception that supposedly caused the request to fail
- ///
- /// A failed request that reports the provided exception as cause for its failure
- ///
- public static Request CreateFailedDummy(Exception exception) {
- return new EndedDummyRequest(exception);
- }
-
- /// Waits for the background operation to end
- ///
- /// Any exceptions raised in the background operation will be thrown
- /// in this method. If you decide to override this method, you should
- /// call Wait() first (and let any possible exception through to your
- /// caller).
- ///
- public virtual void Join() {
-
- // 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!
- if(!Ended)
- Wait();
-
- // Allow the implementer to throw an exception in case an error has occured
- ReraiseExceptions();
-
- }
-
- ///
- /// Allows the specific request implementation to re-throw an exception if
- /// the background process finished unsuccessfully
- ///
- protected virtual void ReraiseExceptions() { }
-
- }
-
- /// Request providing a result that can be passed to the caller
- ///
- /// Type of the result being provided by the request
- ///
- public abstract class Request : Request {
-
- #region class SucceededDummyRequest
-
- /// Succeeded dummy request that is always in the ended state
- private class SucceededDummyRequest : Request {
- /// Creates a new failed dummy request
- /// Result to return to the request's caller
- public SucceededDummyRequest(ResultType result) {
- this.result = result;
- OnAsyncEnded();
- }
- ///
- /// Allows the specific request implementation to re-throw an exception if
- /// the background process finished unsuccessfully
- ///
- protected override ResultType GatherResults() {
- return this.result;
- }
- /// Results the succeede dummy request will provide to the caller
- private ResultType result;
- }
-
- #endregion // SucceededDummyRequest
-
- #region class FailedDummyRequest
-
- /// Failed dummy request that is always in the ended state
- private class FailedDummyRequest : Request {
- /// Creates a new failed dummy request
- /// Exception that caused the dummy to fail
- public FailedDummyRequest(Exception exception) {
- this.exception = exception;
- OnAsyncEnded();
- }
- ///
- /// Allows the specific request implementation to re-throw an exception if
- /// the background process finished unsuccessfully
- ///
- protected override ResultType GatherResults() {
- throw this.exception;
- }
- /// Exception that supposedly caused the request to fail
- private Exception exception;
- }
-
- #endregion // FailedDummyRequest
-
- /// Creates a new failed dummy request
- /// Result to provide to the caller
- ///
- /// A succeeded request that returns the provided result to the caller
- ///
- public static Request CreateSucceededDummy(ResultType result) {
- return new SucceededDummyRequest(result);
- }
-
- /// Creates a new failed dummy request
- /// Exception that supposedly caused the request to fail
- ///
- /// A failed request that reports the provided exception as cause for its failure
- ///
- public static new Request CreateFailedDummy(Exception exception) {
- return new FailedDummyRequest(exception);
- }
-
- /// Waits for the background operation to end
- ///
- /// Any exceptions raised in the background operation will be thrown
- /// in this method. If you decide to override this method, you should
- /// call End() first (and let any possible exception through to your
- /// caller).
- ///
- public new ResultType Join() {
- base.Join();
-
- // Return the results of the request
- return GatherResults();
- }
-
- ///
- /// Allows the specific request implementation to re-throw an exception if
- /// the background process finished unsuccessfully
- ///
- protected override void ReraiseExceptions() {
- // Request and discard the result, so the implementor can do all error handling
- // in the GatherResults() method. This is a good default implementation as long
- // as the returned object does not require IDispose. It if does, this method
- // needs to be overridden.
- GatherResults();
- }
-
- ///
- /// Allows the specific request to return the results of the Request to the
- /// caller of the Join() method
- ///
- protected abstract ResultType GatherResults();
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/StatusReportEventArgs.Test.cs b/Source/Tracking/StatusReportEventArgs.Test.cs
deleted file mode 100644
index a66fbed..0000000
--- a/Source/Tracking/StatusReportEventArgs.Test.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.IO;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the status report event argument container
- [TestFixture]
- public class StatusReportEventArgsTest {
-
- ///
- /// Tests whether the status report event arguments correctly reports an empty status
- ///
- [Test]
- public void TestEmptyStatus() {
- StatusReportEventArgs emptyStatus = new StatusReportEventArgs(string.Empty);
-
- Assert.AreEqual(string.Empty, emptyStatus.Status);
- }
-
- ///
- /// Tests whether the status report event arguments correctly reports simple
- /// status indications
- ///
- [Test]
- public void TestSimpleStatus() {
- StatusReportEventArgs emptyStatus = new StatusReportEventArgs("hello world");
-
- Assert.AreEqual("hello world", emptyStatus.Status);
- }
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/StatusReportEventArgs.cs b/Source/Tracking/StatusReportEventArgs.cs
deleted file mode 100644
index 0de4ac3..0000000
--- a/Source/Tracking/StatusReportEventArgs.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 {
-
- /// Event arguments for reporting a status to the subscriber
- public class StatusReportEventArgs : EventArgs {
-
- /// Initializes a new status report event arguments container
- /// Status to report to the event's subscribers
- public StatusReportEventArgs(string status) {
- this.status = status;
- }
-
- /// The currently reported status
- ///
- /// The contents of this string are up to the publisher of the event to
- /// define. Though it is recommended to report the status as a human-readable
- /// string, these strings might not in all cases be properly localized or
- /// suitable for display in a GUI.
- ///
- public string Status {
- get { return this.status; }
- }
-
- /// Reported status
- private string status;
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/Transaction.Test.cs b/Source/Tracking/Transaction.Test.cs
deleted file mode 100644
index abc2f63..0000000
--- a/Source/Tracking/Transaction.Test.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-
-using NUnit.Framework;
-using NMock;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the transaction class
- [TestFixture]
- public class TransactionTest {
-
- #region interface ITransactionSubscriber
-
- /// Interface used to test the transaction
- public interface ITransactionSubscriber {
-
- /// Called when the set transaction has ended
- /// Transaction group that as ended
- /// Not used
- void Ended(object sender, EventArgs arguments);
-
- }
-
- #endregion // interface ITransactionGroupSubscriber
-
- #region class TestTransaction
-
- /// Transaction used for testing in this unit test
- private class TestTransaction : Transaction {
-
- /// Transitions the transaction into the ended state
- public void End() {
- OnAsyncEnded();
- }
-
- }
-
- #endregion // class TestWiatable
-
- #region class UnsubscribingTransaction
-
- /// Transaction that unsubscribes during an event callback
- private class UnsubscribingTransaction : Transaction {
-
- /// Initializes a new unsubscribing transaction
- ///
- /// Transaction whose AsyncEnded event will be monitored to trigger
- /// the this transaction unsubscribing from the event.
- ///
- public UnsubscribingTransaction(Transaction transactionToMonitor) {
- this.transactionToMonitor = transactionToMonitor;
- this.monitoredTransactionEndedDelegate = new EventHandler(
- monitoredTransactionEnded
- );
-
- this.transactionToMonitor.AsyncEnded += this.monitoredTransactionEndedDelegate;
- }
-
- /// Called when the monitored transaction has ended
- /// Monitored transaction that has ended
- /// Not used
- private void monitoredTransactionEnded(object sender, EventArgs arguments) {
- this.transactionToMonitor.AsyncEnded -= this.monitoredTransactionEndedDelegate;
- }
-
- /// Transitions the transaction into the ended state
- public void End() {
- OnAsyncEnded();
- }
-
- /// Transaction whose ending in being monitored
- private Transaction transactionToMonitor;
- /// Delegate to the monitoredTransactionEnded() method
- private EventHandler monitoredTransactionEndedDelegate;
-
- }
-
- #endregion // class TestWiatable
-
- /// Initialization routine executed before each test is run
- [SetUp]
- public void Setup() {
- this.mockery = new MockFactory();
- }
-
- ///
- /// Verifies that the transaction throws an exception when it is ended multiple times
- ///
- [Test]
- public void TestThrowOnRepeatedlyEndedTransaction() {
- TestTransaction test = new TestTransaction();
- test.End();
- Assert.Throws(
- delegate() { test.End(); }
- );
- }
-
- ///
- /// Tests whether the Ended event of the transaction is correctly delivered if
- /// the transaction ends after the subscription already took place
- ///
- [Test]
- public void TestEndedEventAfterSubscription() {
- TestTransaction test = new TestTransaction();
-
- Mock mockedSubscriber = mockSubscriber(test);
- mockedSubscriber.Expects.One.Method(m => m.Ended(null, null)).WithAnyArguments();
-
- test.End();
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
-
- ///
- /// Tests whether the Ended event of the transaction is correctly delivered if
- /// the transaction is already done when the subscription takes place
- ///
- [Test]
- public void TestEndedEventDuingSubscription() {
- TestTransaction test = new TestTransaction();
- test.End();
-
- Mock mockedSubscriber =
- this.mockery.CreateMock();
-
- mockedSubscriber.Expects.One.Method(m => m.Ended(null, null)).WithAnyArguments();
-
- test.AsyncEnded += new EventHandler(mockedSubscriber.MockObject.Ended);
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
-
- ///
- /// Verifies that the Wait() method of the transaction works as expected
- ///
- [Test]
- public void TestWaitUnlimited() {
- TestTransaction test = new TestTransaction();
-
- // We can only do a positive test here without slowing down the unit test
- ThreadPool.QueueUserWorkItem(
- (WaitCallback)delegate(object state) { Thread.Sleep(1); test.End(); }
- );
-
- test.Wait();
- }
-
- ///
- /// Verifies that the Wait() method of the transaction works as expected using
- /// a millisecond count as its argument
- ///
- [Test]
- public void TestWaitMilliseconds() {
- TestTransaction test = new TestTransaction();
-
- // Wait 0 milliseconds for the transaction to end. Of course, this will not happen,
- // so a timeout occurs and false is returned
- Assert.IsFalse(test.Wait(0));
-
- test.End();
-
- // Wait another 0 milliseconds for the transaction to end. Now it has already ended
- // and no timeout will occur, even with a wait time of 0 milliseconds.
- Assert.IsTrue(test.Wait(0));
- }
-
- ///
- /// Verifies that the Wait() method of the transaction works as expected using
- /// a TimeSpan as its argument
- ///
- [Test]
- public void TestWaitTimeSpan() {
- TestTransaction test = new TestTransaction();
-
- // Wait 0 milliseconds for the transaction to end. Of course, this will not happen,
- // so a timeout occurs and false is returned
- Assert.IsFalse(test.Wait(TimeSpan.Zero));
-
- test.End();
-
- // Wait another 0 milliseconds for the transaction to end. Now it has already ended
- // and no timeout will occur, even with a wait time of 0 milliseconds.
- Assert.IsTrue(test.Wait(TimeSpan.Zero));
- }
-
- ///
- /// Verifies that no error occurs when an even subscriber to the AsyncEnded event
- /// unsubscribes in the event callback handler
- ///
- [Test]
- public void TestUnsubscribeInEndedCallback() {
- TestTransaction monitored = new TestTransaction();
- UnsubscribingTransaction test = new UnsubscribingTransaction(monitored);
-
- Mock mockedSubscriber = mockSubscriber(monitored);
-
- try {
- mockedSubscriber.Expects.One.Method(m => m.Ended(null, null)).WithAnyArguments();
- monitored.End();
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
- finally {
- test.End();
- }
- }
-
- /// Mocks a subscriber for the events of a transaction
- /// Transaction to mock an event subscriber for
- /// The mocked event subscriber
- private Mock mockSubscriber(Transaction transaction) {
- Mock mockedSubscriber =
- this.mockery.CreateMock();
-
- transaction.AsyncEnded += new EventHandler(mockedSubscriber.MockObject.Ended);
-
- return mockedSubscriber;
- }
-
- /// Mock object factory
- private MockFactory mockery;
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/Transaction.cs b/Source/Tracking/Transaction.cs
deleted file mode 100644
index 2802bc1..0000000
--- a/Source/Tracking/Transaction.cs
+++ /dev/null
@@ -1,269 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Threading;
-
-namespace Nuclex.Support.Tracking {
-
- /// Base class for background processes the user can wait on
- ///
- ///
- /// By encapsulating long-running operations which will ideally be running in
- /// a background thread in a class that's derived from
- /// you can wait for the completion of the operation and optionally even receive
- /// feedback on the achieved progress. This is useful for displaying a progress
- /// bar, loading screen or some other means of entertaining the user while he
- /// waits for the task to complete.
- ///
- ///
- /// You can register callbacks which will be fired once the
- /// task has completed. This class deliberately does not provide an Execute()
- /// method or anything similar to clearly seperate the initiation of an operation
- /// from just monitoring it. By omitting an Execute() method, it also becomes
- /// possible to construct a transaction just-in-time when it is explicitely being
- /// asked for.
- ///
- ///
- public abstract class Transaction {
-
- #region class EndedDummyTransaction
-
- /// Dummy transaction which always is in the 'ended' state
- private class EndedDummyTransaction : Transaction {
-
- /// Initializes a new ended dummy transaction
- public EndedDummyTransaction() {
- OnAsyncEnded();
- }
-
- }
-
- #endregion // class EndedDummyTransaction
-
- /// A dummy transaction that's always in the 'ended' state
- ///
- /// Useful if an operation is already complete when it's being asked for or
- /// when a transaction that's lazily created is accessed after the original
- /// operation has ended already.
- ///
- public static readonly Transaction EndedDummy = new EndedDummyTransaction();
-
- /// Will be triggered when the transaction has ended
- ///
- /// If the process is already finished when a client registers to this event,
- /// the registered callback will be invoked synchronously right when the
- /// registration takes place.
- ///
- public virtual event EventHandler AsyncEnded {
- add {
-
- // If the background process has not yet ended, add the delegate to the
- // list of subscribers. This uses the double-checked locking idiom to
- // avoid taking the lock when the background process has already ended.
- if(!this.ended) {
- lock(this) {
- if(!this.ended) {
-
- // The subscriber list is also created lazily ;-)
- if(ReferenceEquals(this.endedEventSubscribers, null)) {
- this.endedEventSubscribers = new List();
- }
-
- // Subscribe the event handler to the list
- this.endedEventSubscribers.Add(value);
- return;
-
- }
- }
- }
-
- // If this point is reached, the background process was already finished
- // and we have to invoke the subscriber manually as promised.
- value(this, EventArgs.Empty);
-
- }
- remove {
-
- if(!this.ended) {
- lock(this) {
- if(!this.ended) {
-
- // Only try to remove the event handler if the subscriber list was created,
- // otherwise, we can be sure that no actual subscribers exist.
- if(!ReferenceEquals(this.endedEventSubscribers, null)) {
- int eventHandlerIndex = this.endedEventSubscribers.IndexOf(value);
-
- // Unsubscribing a non-subscribed delegate from an event is allowed and
- // should not throw an exception.
- if(eventHandlerIndex != -1) {
- this.endedEventSubscribers.RemoveAt(eventHandlerIndex);
- }
- }
-
- }
- }
- }
-
- }
- }
-
- /// Waits until the background process finishes
- public virtual void Wait() {
- if(!this.ended) {
- WaitHandle.WaitOne();
- }
- }
-
-#if WINDOWS
-
- /// Waits until the background process finishes or a timeout occurs
- ///
- /// Time span after which to stop waiting and return immediately
- ///
- ///
- /// True if the background process completed, false if the timeout was reached
- ///
- public virtual bool Wait(TimeSpan timeout) {
- if(this.ended) {
- return true;
- }
-
- return WaitHandle.WaitOne(timeout, false);
- }
-
-#endif // WINDOWS
-
- /// Waits until the background process finishes or a timeout occurs
- ///
- /// Number of milliseconds after which to stop waiting and return immediately
- ///
- ///
- /// True if the background process completed, false if the timeout was reached
- ///
- public virtual bool Wait(int timeoutMilliseconds) {
- if(this.ended) {
- return true;
- }
-
-#if WINDOWS
- return WaitHandle.WaitOne(timeoutMilliseconds, false);
-#else
- return WaitHandle.WaitOne(timeoutMilliseconds);
-#endif
- }
-
- /// Whether the transaction has ended already
- public virtual bool Ended {
- get { return this.ended; }
- }
-
- /// WaitHandle that can be used to wait for the transaction to end
- public virtual WaitHandle WaitHandle {
- get {
-
- // The WaitHandle will only be created when someone asks for it!
- // We can *not* optimize this lock away since we absolutely must not create
- // two doneEvents -- someone might call .WaitOne() on the first one when only
- // the second one is referenced by this.doneEvent and thus gets set in the end.
- if(this.doneEvent == null) {
- lock(this) {
- if(this.doneEvent == null) {
- this.doneEvent = new ManualResetEvent(this.ended);
- }
- }
- }
-
- // We can be sure the doneEvent has been created now!
- return this.doneEvent;
-
- }
- }
-
- /// Fires the AsyncEnded event
- ///
- ///
- /// This event should be fired by the implementing class when its work is completed.
- /// It's of no interest to this class whether the outcome of the process was
- /// successfull or not, the outcome and results of the process taking place both
- /// need to be communicated seperately.
- ///
- ///
- /// Calling this method is mandatory. Implementers need to take care that
- /// the OnAsyncEnded() method is called on any instance of transaction that's
- /// being created. This method also must not be called more than once.
- ///
- ///
- protected virtual void OnAsyncEnded() {
-
- // Make sure the transaction is not ended more than once. By guaranteeing that
- // a transaction can only be ended once, we allow users of this class to
- // skip some safeguards against notifications arriving twice.
- lock(this) {
-
- // No double lock here, this is an exception that indicates an implementation
- // error that will not be triggered under normal circumstances. We don't need
- // to waste any effort optimizing the speed at which an implementation fault
- // will be reported ;-)
- if(this.ended)
- throw new InvalidOperationException("The transaction has already been ended");
-
- this.ended = true;
-
- // Doesn't really 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 state due to the ended flag (see above) being set to true beforehand!
- // But since we've got a lock ready, we can even avoid that 1 in a million
- // performance loss and prevent the doneEvent from being signalled needlessly.
- if(this.doneEvent != null)
- this.doneEvent.Set();
-
- }
-
- // Fire the ended events to all event subscribers. We can freely use the list
- // without synchronization at this point on since once this.ended is set to true,
- // the subscribers list will not be accessed any longer
- if(!ReferenceEquals(this.endedEventSubscribers, null)) {
- for(int index = 0; index < this.endedEventSubscribers.Count; ++index) {
- this.endedEventSubscribers[index](this, EventArgs.Empty);
- }
- this.endedEventSubscribers = null;
- }
-
- }
-
- /// Event handlers which have subscribed to the ended event
- ///
- /// Does not need to be volatile since it's only accessed inside
- ///
- protected volatile List endedEventSubscribers;
- /// Whether the operation has completed yet
- protected volatile bool ended;
- /// Event that will be set when the transaction is completed
- ///
- /// This event is will only be created when it is specifically asked for using
- /// the WaitHandle property.
- ///
- protected volatile ManualResetEvent doneEvent;
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/TransactionGroup.Test.cs b/Source/Tracking/TransactionGroup.Test.cs
deleted file mode 100644
index d77a8e9..0000000
--- a/Source/Tracking/TransactionGroup.Test.cs
+++ /dev/null
@@ -1,386 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.IO;
-using System.Threading;
-
-#if UNITTEST
-
-using NUnit.Framework;
-using NMock;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the transaction group class
- [TestFixture]
- public class TransactionGroupTest {
-
- #region interface ITransactionGroupSubscriber
-
- /// Interface used to test the transaction group
- public interface ITransactionGroupSubscriber {
-
- /// Called when the transaction group's progress changes
- /// Transaction group whose progress has changed
- /// Contains the new progress achieved
- void ProgressChanged(object sender, ProgressReportEventArgs arguments);
-
- /// Called when the transaction group has ended
- /// Transaction group that as ended
- /// Not used
- void Ended(object sender, EventArgs arguments);
-
- }
-
- #endregion // interface ITransactionGroupSubscriber
-
- #region class ProgressUpdateEventArgsMatcher
-
- /// Compares two ProgressUpdateEventArgsInstances for NMock validation
- private class ProgressUpdateEventArgsMatcher : Matcher {
-
- /// Initializes a new ProgressUpdateEventArgsMatcher
- /// Expected progress update event arguments
- public ProgressUpdateEventArgsMatcher(ProgressReportEventArgs expected) {
- this.expected = expected;
- }
-
- ///
- /// Called by NMock to verfiy the ProgressUpdateEventArgs match the expected value
- ///
- /// Actual value to compare to the expected value
- ///
- /// True if the actual value matches the expected value; otherwise false
- ///
- public override bool Matches(object actualAsObject) {
- ProgressReportEventArgs actual = (actualAsObject as ProgressReportEventArgs);
- if(actual == null)
- return false;
-
- return (actual.Progress == this.expected.Progress);
- }
-
- /// Creates a string representation of the expected value
- /// Writer to write the string representation into
- public override void DescribeTo(TextWriter writer) {
- writer.Write(this.expected.Progress.ToString());
- }
-
- /// Expected progress update event args value
- private ProgressReportEventArgs expected;
-
- }
-
- #endregion // class ProgressUpdateEventArgsMatcher
-
- #region class TestTransaction
-
- /// Transaction used for testing in this unit test
- private class TestTransaction : Transaction, IProgressReporter {
-
- /// will be triggered to report when progress has been achieved
- public event EventHandler AsyncProgressChanged;
-
- /// Changes the testing transaction's indicated progress
- ///
- /// New progress to be reported by the testing transaction
- ///
- public void ChangeProgress(float progress) {
- OnAsyncProgressChanged(progress);
- }
-
- /// Transitions the transaction into the ended state
- public void End() {
- OnAsyncEnded();
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this transaction about the achieved progress.
- ///
- protected virtual void OnAsyncProgressChanged(float progress) {
- OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this transaction about the achieved progress.
- /// Allows for classes derived from the transaction class to easily provide
- /// a custom event arguments class that has been derived from the
- /// transaction's ProgressUpdateEventArgs class.
- ///
- protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
- EventHandler copy = AsyncProgressChanged;
- if(copy != null)
- copy(this, eventArguments);
- }
-
- }
-
- #endregion // class TestTransaction
-
- #region class ChainEndingTransaction
-
- ///
- /// Transaction that ends another transaction when its Ended property is called
- ///
- private class ChainEndingTransaction : Transaction {
-
- /// Initializes a new chain ending transaction
- public ChainEndingTransaction() {
- this.chainedTransaction = new TestTransaction();
- }
-
- /// Transitions the transaction into the ended state
- public void End() {
- OnAsyncEnded();
- }
-
- ///
- /// Transaction that will end when this transaction's ended property is accessed
- ///
- public TestTransaction ChainedTransaction {
- get { return this.chainedTransaction; }
- }
-
- /// Whether the transaction has ended already
- public override bool Ended {
- get {
- if(Interlocked.Exchange(ref this.endedCalled, 1) == 0) {
- this.chainedTransaction.End();
- }
-
- return base.Ended;
- }
- }
-
- ///
- /// Transaction that will end when this transaction's ended property is accessed
- ///
- private TestTransaction chainedTransaction;
-
- /// Whether we already ended the chained transaction and ourselves
- private int endedCalled;
-
- }
-
- #endregion // class ChainEndingTransaction
-
- /// Initialization routine executed before each test is run
- [SetUp]
- public void Setup() {
- this.mockery = new MockFactory();
- }
-
- /// Validates that the transaction group correctly sums the progress
- [Test]
- public void TestSummedProgress() {
- using(
- TransactionGroup testTransactionGroup =
- new TransactionGroup(
- new TestTransaction[] { new TestTransaction(), new TestTransaction() }
- )
- ) {
- Mock mockedSubscriber = mockSubscriber(testTransactionGroup);
-
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(TransactionGroup)),
- new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.25f))
- );
-
- testTransactionGroup.Children[0].Transaction.ChangeProgress(0.5f);
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
- }
-
- /// Validates that the transaction group respects the weights
- [Test]
- public void TestWeightedSummedProgress() {
- using(
- TransactionGroup testTransactionGroup =
- new TransactionGroup(
- new WeightedTransaction[] {
- new WeightedTransaction(new TestTransaction(), 1.0f),
- new WeightedTransaction(new TestTransaction(), 2.0f)
- }
- )
- ) {
- Mock mockedSubscriber = mockSubscriber(testTransactionGroup);
-
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(TransactionGroup)),
- new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f / 3.0f))
- );
-
- testTransactionGroup.Children[0].Transaction.ChangeProgress(0.5f);
-
- mockedSubscriber.Expects.One.Method(m => m.ProgressChanged(null, null)).With(
- new NMock.Matchers.TypeMatcher(typeof(TransactionGroup)),
- new ProgressUpdateEventArgsMatcher(new ProgressReportEventArgs(0.5f))
- );
-
- testTransactionGroup.Children[1].Transaction.ChangeProgress(0.5f);
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
- }
-
- ///
- /// Validates that the ended event is triggered when the last transaction out of
- /// multiple transactions in the group ends.
- ///
- [Test]
- public void TestEndedEventWithTwoTransactions() {
- using(
- TransactionGroup testTransactionGroup =
- new TransactionGroup(
- new TestTransaction[] { new TestTransaction(), new TestTransaction() }
- )
- ) {
- Mock mockedSubscriber = mockSubscriber(testTransactionGroup);
-
- mockedSubscriber.Expects.Exactly(2).Method(
- m => m.ProgressChanged(null, null)
- ).WithAnyArguments();
-
- mockedSubscriber.Expects.One.Method(
- m => m.Ended(null, null)
- ).WithAnyArguments();
-
- testTransactionGroup.Children[0].Transaction.End();
- testTransactionGroup.Children[1].Transaction.End();
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
- }
-
- ///
- /// Validates that the ended event is triggered when a single transaction contained
- /// in the group ends.
- ///
- [Test]
- public void TestEndedEventWithSingleTransaction() {
- using(
- TransactionGroup testTransactionGroup =
- new TransactionGroup(
- new TestTransaction[] { new TestTransaction() }
- )
- ) {
- Mock mockedSubscriber = mockSubscriber(testTransactionGroup);
-
- mockedSubscriber.Expects.One.Method(
- m => m.ProgressChanged(null, null)
- ).WithAnyArguments();
-
- mockedSubscriber.Expects.One.Method(
- m => m.Ended(null, null)
- ).WithAnyArguments();
-
-
- testTransactionGroup.Children[0].Transaction.End();
-
- this.mockery.VerifyAllExpectationsHaveBeenMet();
- }
- }
-
- ///
- /// Verifies that the transaction group immediately enters the ended state when
- /// the contained transactions have already ended before the constructor
- ///
- ///
- /// This was a bug at one time and should prevent a regression
- ///
- [Test]
- public void TestAlreadyEndedTransactions() {
- using(
- TransactionGroup testTransactionGroup =
- new TransactionGroup(
- new Transaction[] { Transaction.EndedDummy, Transaction.EndedDummy }
- )
- ) {
- Assert.IsTrue(testTransactionGroup.Wait(1000));
- }
- }
-
- ///
- /// Verifies that the transaction group doesn't think it's already ended when
- /// the first transaction being added is in the ended state
- ///
- ///
- /// This was a bug at one time and should prevent a regression
- ///
- [Test]
- public void TestAlreadyEndedTransactionAsFirstTransaction() {
- using(
- TransactionGroup testTransactionGroup =
- new TransactionGroup(
- new Transaction[] { Transaction.EndedDummy, new TestTransaction() }
- )
- ) {
- Assert.IsFalse(testTransactionGroup.Ended);
- }
- }
-
- ///
- /// Verifies that a transaction ending while the constructor is running doesn't
- /// wreak havoc on the transaction group
- ///
- [Test]
- public void TestTransactionEndingDuringConstructor() {
- ChainEndingTransaction chainTransaction = new ChainEndingTransaction();
- using(
- TransactionGroup testTransactionGroup =
- new TransactionGroup(
- new Transaction[] { chainTransaction.ChainedTransaction, chainTransaction }
- )
- ) {
- Assert.IsFalse(testTransactionGroup.Ended);
- chainTransaction.End();
- Assert.IsTrue(testTransactionGroup.Ended);
- }
- }
-
- /// Mocks a subscriber for the events of a transaction
- /// Transaction to mock an event subscriber for
- /// The mocked event subscriber
- private Mock mockSubscriber(Transaction transaction) {
- Mock mockedSubscriber =
- this.mockery.CreateMock();
-
- transaction.AsyncEnded += new EventHandler(mockedSubscriber.MockObject.Ended);
- (transaction as IProgressReporter).AsyncProgressChanged +=
- new EventHandler(mockedSubscriber.MockObject.ProgressChanged);
-
- return mockedSubscriber;
- }
-
- /// Mock object factory
- private MockFactory mockery;
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/TransactionGroup.cs b/Source/Tracking/TransactionGroup.cs
deleted file mode 100644
index e5eb7fd..0000000
--- a/Source/Tracking/TransactionGroup.cs
+++ /dev/null
@@ -1,235 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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;
-using System.Collections.ObjectModel;
-using System.Threading;
-
-using Nuclex.Support.Collections;
-
-namespace Nuclex.Support.Tracking {
-
- /// Forms a single transaction from a group of transactions
- /// Type of transactions to manage as a set
- public class TransactionGroup : Transaction, IDisposable, IProgressReporter
- where TransactionType : Transaction {
-
- /// will be triggered to report when progress has been achieved
- public event EventHandler AsyncProgressChanged;
-
- /// Initializes a new transaction group
- /// Transactions to track with this group
- ///
- /// Uses a default weighting factor of 1.0 for all transactions.
- ///
- public TransactionGroup(IEnumerable children) {
- List> childrenList =
- new List>();
-
- // Construct a WeightedTransaction with the default weight for each
- // transaction and wrap it in an ObservedTransaction
- foreach(TransactionType transaction in children) {
- childrenList.Add(
- new ObservedWeightedTransaction(
- new WeightedTransaction(transaction),
- new ObservedWeightedTransaction.ReportDelegate(
- asyncProgressUpdated
- ),
- new ObservedWeightedTransaction.ReportDelegate(
- asyncChildEnded
- )
- )
- );
- }
-
- // Since all transactions have a weight of 1.0, the total weight is
- // equal to the number of transactions in our list
- this.totalWeight = (float)childrenList.Count;
- // Thread.MemoryBarrier(); // not needed because children is volatile
- this.children = childrenList;
-
- // Any asyncEnded events being receiving from the transactions until now
- // would have been ignored, so we need to check again here
- asyncChildEnded();
- }
-
- /// Initializes a new transaction group
- /// Transactions to track with this group
- public TransactionGroup(
- IEnumerable> children
- ) {
- List> childrenList =
- new List>();
-
- // Construct an ObservedTransaction around each of the WeightedTransactions
- foreach(WeightedTransaction transaction in children) {
- childrenList.Add(
- new ObservedWeightedTransaction(
- transaction,
- new ObservedWeightedTransaction.ReportDelegate(
- asyncProgressUpdated
- ),
- new ObservedWeightedTransaction.ReportDelegate(
- asyncChildEnded
- )
- )
- );
-
- // Sum up the total weight
- this.totalWeight += transaction.Weight;
- }
-
- this.children = childrenList;
-
- // Any asyncEnded events being receiving from the transactions until now
- // would have been ignored, so we need to check again here
- asyncChildEnded();
- }
-
- /// Immediately releases all resources owned by the object
- public void Dispose() {
-
- if(this.children != null) {
-
- // Dispose all the observed transactions, disconnecting the events from the
- // actual transactions so the GC can more easily collect this class
- for(int index = 0; index < this.children.Count; ++index)
- this.children[index].Dispose();
-
- this.children = null;
- this.wrapper = null;
-
- }
-
- }
-
- /// Childs contained in the transaction set
- public IList> Children {
- get {
-
- // The wrapper is constructed only when needed. Most of the time, users will
- // just create a transaction 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 WeightedTransactionWrapperCollection(
- this.children
- );
-
- }
-
- return this.wrapper;
-
- }
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this transaction about the achieved progress.
- ///
- protected virtual void OnAsyncProgressChanged(float progress) {
- OnAsyncProgressChanged(new ProgressReportEventArgs(progress));
- }
-
- /// Fires the progress update event
- /// Progress to report (ranging from 0.0 to 1.0)
- ///
- /// Informs the observers of this transaction about the achieved progress.
- /// Allows for classes derived from the transaction class to easily provide
- /// a custom event arguments class that has been derived from the
- /// transaction's ProgressUpdateEventArgs class.
- ///
- protected virtual void OnAsyncProgressChanged(ProgressReportEventArgs eventArguments) {
- EventHandler copy = AsyncProgressChanged;
- if(copy != null)
- copy(this, eventArguments);
- }
-
- ///
- /// Called when the progress of one of the observed transactions changes
- ///
- private void asyncProgressUpdated() {
- if(this.children == null) {
- return;
- }
-
- float totalProgress = 0.0f;
-
- // Calculate the sum of the progress reported by our child transactions,
- // scaled to the weight each transaction has assigned to it.
- for(int index = 0; index < this.children.Count; ++index) {
- totalProgress +=
- this.children[index].Progress * this.children[index].WeightedTransaction.Weight;
- }
-
- // Calculate the actual combined progress
- if(this.totalWeight > 0.0f)
- totalProgress /= this.totalWeight;
-
- // Send out the progress update
- OnAsyncProgressChanged(totalProgress);
- }
-
- ///
- /// Called when an observed transaction ends
- ///
- private void asyncChildEnded() {
-
- // If a transaction reports its end durign the constructor, it will end up here
- // where the collection has not been assigned yet, allowing us to skip the
- // check until all transactions are there (otherwise, we might invoke
- // OnAsyncended() early, because all transactions in the list seem to have ended
- // despite the fact that the constructor hasn't finished adding transactions yet)
- if(this.children == null) {
- return;
- }
-
- // If there's still at least one transaction going, don't report that
- // the transaction group has finished yet.
- for(int index = 0; index < this.children.Count; ++index)
- if(!this.children[index].WeightedTransaction.Transaction.Ended)
- return;
-
- // All child transactions have ended, so the set has now ended as well
- if(Interlocked.Exchange(ref this.endedCalled, 1) == 0) {
- OnAsyncEnded();
- }
-
- }
-
- /// Transactions being managed in the set
- private volatile List> children;
- ///
- /// Wrapper collection for exposing the child transactions under the
- /// WeightedTransaction interface
- ///
- private volatile WeightedTransactionWrapperCollection wrapper;
- /// Summed weight of all transactions in the set
- private float totalWeight;
- /// Whether we already called OnAsyncEnded
- private int endedCalled;
-
- }
-
-} // namespace Nuclex.Support.Tracking
diff --git a/Source/Tracking/WeightedTransaction.Test.cs b/Source/Tracking/WeightedTransaction.Test.cs
deleted file mode 100644
index 316b5b7..0000000
--- a/Source/Tracking/WeightedTransaction.Test.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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
-
-#if UNITTEST
-
-using System;
-using System.IO;
-
-using NUnit.Framework;
-
-namespace Nuclex.Support.Tracking {
-
- /// Unit Test for the weighted transaction wrapper
- [TestFixture]
- public class WeightedTransactionTest {
-
- #region class TestTransaction
-
- /// Transaction used for testing in this unit test
- private class TestTransaction : Transaction { }
-
- #endregion // class TestTransaction
-
- ///
- /// Tests whether the weighted transaction wrapper correctly stores the transaction
- /// it was given in the constructor
- ///
- [Test]
- public void TestTransactionStorage() {
- TestTransaction transaction = new TestTransaction();
- WeightedTransaction testWrapper = new WeightedTransaction(
- transaction
- );
-
- Assert.AreSame(transaction, testWrapper.Transaction);
- }
-
- ///
- /// Tests whether the weighted transaction wrapper correctly applies the default
- /// unit weight to the transaction if no explicit weight was specified
- ///
- [Test]
- public void TestDefaultWeight() {
- TestTransaction transaction = new TestTransaction();
- WeightedTransaction testWrapper = new WeightedTransaction(
- transaction
- );
-
- Assert.AreEqual(1.0f, testWrapper.Weight);
- }
-
- ///
- /// Tests whether the weighted transaction wrapper correctly stores the weight
- /// it was given in the constructor
- ///
- [Test]
- public void TestWeightStorage() {
- TestTransaction transaction = new TestTransaction();
- WeightedTransaction testWrapper = new WeightedTransaction(
- transaction, 12.0f
- );
-
- Assert.AreEqual(12.0f, testWrapper.Weight);
- }
-
- }
-
-} // namespace Nuclex.Support.Tracking
-
-#endif // UNITTEST
diff --git a/Source/Tracking/WeightedTransaction.cs b/Source/Tracking/WeightedTransaction.cs
deleted file mode 100644
index eb0b8c6..0000000
--- a/Source/Tracking/WeightedTransaction.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-#region CPL License
-/*
-Nuclex Framework
-Copyright (C) 2002-2010 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 {
-
- /// Transaction with an associated weight for the total progress
- public class WeightedTransaction where TransactionType : Transaction {
-
- ///
- /// Initializes a new weighted transaction with a default weight of 1.0
- ///
- /// Transaction whose progress to monitor
- public WeightedTransaction(TransactionType transaction) : this(transaction, 1.0f) { }
-
- /// Initializes a new weighted transaction
- /// transaction whose progress to monitor
- /// Weighting of the transaction's progress
- public WeightedTransaction(TransactionType transaction, float weight) {
- this.transaction = transaction;
- this.weight = weight;
- }
-
- /// Transaction being wrapped by this weighted transaction
- public TransactionType Transaction {
- get { return this.transaction; }
- }
-
- /// The contribution of this transaction to the total progress
- public float Weight {
- get { return this.weight; }
- }
-
- /// Transaction whose progress we're tracking
- private TransactionType transaction;
- /// Weighting of this transaction in the total progress
- private float weight;
-
- }
-
-} // namespace Nuclex.Support.Tracking