Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Reactive.Disposables;
namespace System.Reactive.Concurrency
{
/// <summary>
/// Base class for historical schedulers, which are virtual time schedulers that use DateTimeOffset for absolute time and TimeSpan for relative time.
/// </summary>
public abstract class HistoricalSchedulerBase : VirtualTimeSchedulerBase<DateTimeOffset, TimeSpan>
{
/// <summary>
/// Creates a new historical scheduler with the minimum value of DateTimeOffset as the initial clock value.
/// </summary>
protected HistoricalSchedulerBase()
: base(DateTimeOffset.MinValue, Comparer<DateTimeOffset>.Default)
{
}
/// <summary>
/// Creates a new historical scheduler with the specified initial clock value.
/// </summary>
/// <param name="initialClock">Initial clock value.</param>
protected HistoricalSchedulerBase(DateTimeOffset initialClock)
: base(initialClock, Comparer<DateTimeOffset>.Default)
{
}
/// <summary>
/// Creates a new historical scheduler with the specified initial clock value and absolute time comparer.
/// </summary>
/// <param name="initialClock">Initial value for the clock.</param>
/// <param name="comparer">Comparer to determine causality of events based on absolute time.</param>
protected HistoricalSchedulerBase(DateTimeOffset initialClock, IComparer<DateTimeOffset> comparer)
: base(initialClock, comparer)
{
}
/// <summary>
/// Adds a relative time value to an absolute time value.
/// </summary>
/// <param name="absolute">Absolute time value.</param>
/// <param name="relative">Relative time value to add.</param>
/// <returns>The resulting absolute time sum value.</returns>
protected override DateTimeOffset Add(DateTimeOffset absolute, TimeSpan relative)
{
return absolute.Add(relative);
}
/// <summary>
/// Converts the absolute time value to a DateTimeOffset value.
/// </summary>
/// <param name="absolute">Absolute time value to convert.</param>
/// <returns>The corresponding DateTimeOffset value.</returns>
protected override DateTimeOffset ToDateTimeOffset(DateTimeOffset absolute)
{
return absolute;
}
/// <summary>
/// Converts the TimeSpan value to a relative time value.
/// </summary>
/// <param name="timeSpan">TimeSpan value to convert.</param>
/// <returns>The corresponding relative time value.</returns>
protected override TimeSpan ToRelative(TimeSpan timeSpan)
{
return timeSpan;
}
}
/// <summary>
/// Provides a virtual time scheduler that uses DateTimeOffset for absolute time and TimeSpan for relative time.
/// </summary>
public class HistoricalScheduler : HistoricalSchedulerBase
{
private readonly SchedulerQueue<DateTimeOffset> queue = new SchedulerQueue<DateTimeOffset>();
/// <summary>
/// Creates a new historical scheduler with the minimum value of DateTimeOffset as the initial clock value.
/// </summary>
public HistoricalScheduler()
: base()
{
}
/// <summary>
/// Creates a new historical scheduler with the specified initial clock value.
/// </summary>
/// <param name="initialClock">Initial value for the clock.</param>
public HistoricalScheduler(DateTimeOffset initialClock)
: base(initialClock)
{
}
/// <summary>
/// Creates a new historical scheduler with the specified initial clock value.
/// </summary>
/// <param name="initialClock">Initial value for the clock.</param>
/// <param name="comparer">Comparer to determine causality of events based on absolute time.</param>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>
public HistoricalScheduler(DateTimeOffset initialClock, IComparer<DateTimeOffset> comparer)
: base(initialClock, comparer)
{
}
/// <summary>
/// Gets the next scheduled item to be executed.
/// </summary>
/// <returns>The next scheduled item.</returns>
protected override IScheduledItem<DateTimeOffset> GetNext()
{
while (queue.Count > 0)
{
var next = queue.Peek();
if (next.IsCanceled)
queue.Dequeue();
else
return next;
}
return null;
}
/// <summary>
/// Schedules an action to be executed at dueTime.
/// </summary>
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
/// <param name="state">State passed to the action to be executed.</param>
/// <param name="action">Action to be executed.</param>
/// <param name="dueTime">Absolute time at which to execute the action.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
/// <exception cref="ArgumentNullException"><paramref name="action"/> is null.</exception>
public override IDisposable ScheduleAbsolute<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
{
if (action == null)
throw new ArgumentNullException("action");
var si = default(ScheduledItem<DateTimeOffset, TState>);
var run = new Func<IScheduler, TState, IDisposable>((scheduler, state1) =>
{
queue.Remove(si);
return action(scheduler, state1);
});
si = new ScheduledItem<DateTimeOffset, TState>(this, state, run, dueTime, Comparer);
queue.Enqueue(si);
return Disposable.Create(si.Cancel);
}
}
}

View File

@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Reactive.Disposables;
namespace System.Reactive.Concurrency
{
/// <summary>
/// Provides a set of extension methods for virtual time scheduling.
/// </summary>
public static class VirtualTimeSchedulerExtensions
{
/// <summary>
/// Schedules an action to be executed at dueTime.
/// </summary>
/// <typeparam name="TAbsolute">Absolute time representation type.</typeparam>
/// <typeparam name="TRelative">Relative time representation type.</typeparam>
/// <param name="scheduler">Scheduler to execute the action on.</param>
/// <param name="dueTime">Relative time after which to execute the action.</param>
/// <param name="action">Action to be executed.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
/// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is null.</exception>
public static IDisposable ScheduleRelative<TAbsolute, TRelative>(this VirtualTimeSchedulerBase<TAbsolute, TRelative> scheduler, TRelative dueTime, Action action)
where TAbsolute : IComparable<TAbsolute>
{
if (scheduler == null)
throw new ArgumentNullException("scheduler");
if (action == null)
throw new ArgumentNullException("action");
return scheduler.ScheduleRelative(action, dueTime, Invoke);
}
/// <summary>
/// Schedules an action to be executed at dueTime.
/// </summary>
/// <typeparam name="TAbsolute">Absolute time representation type.</typeparam>
/// <typeparam name="TRelative">Relative time representation type.</typeparam>
/// <param name="scheduler">Scheduler to execute the action on.</param>
/// <param name="dueTime">Absolute time at which to execute the action.</param>
/// <param name="action">Action to be executed.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
/// <exception cref="ArgumentNullException"><paramref name="scheduler"/> or <paramref name="action"/> is null.</exception>
public static IDisposable ScheduleAbsolute<TAbsolute, TRelative>(this VirtualTimeSchedulerBase<TAbsolute, TRelative> scheduler, TAbsolute dueTime, Action action)
where TAbsolute : IComparable<TAbsolute>
{
if (scheduler == null)
throw new ArgumentNullException("scheduler");
if (action == null)
throw new ArgumentNullException("action");
return scheduler.ScheduleAbsolute(action, dueTime, Invoke);
}
static IDisposable Invoke(IScheduler scheduler, Action action)
{
action();
return Disposable.Empty;
}
}
}

View File

@@ -0,0 +1,415 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Globalization;
using System.Reactive.Disposables;
namespace System.Reactive.Concurrency
{
/// <summary>
/// Base class for virtual time schedulers.
/// </summary>
/// <typeparam name="TAbsolute">Absolute time representation type.</typeparam>
/// <typeparam name="TRelative">Relative time representation type.</typeparam>
public abstract class VirtualTimeSchedulerBase<TAbsolute, TRelative> : IScheduler, IServiceProvider, IStopwatchProvider
where TAbsolute : IComparable<TAbsolute>
{
/// <summary>
/// Creates a new virtual time scheduler with the default value of TAbsolute as the initial clock value.
/// </summary>
protected VirtualTimeSchedulerBase()
: this(default(TAbsolute), Comparer<TAbsolute>.Default)
{
}
/// <summary>
/// Creates a new virtual time scheduler with the specified initial clock value and absolute time comparer.
/// </summary>
/// <param name="initialClock">Initial value for the clock.</param>
/// <param name="comparer">Comparer to determine causality of events based on absolute time.</param>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>
protected VirtualTimeSchedulerBase(TAbsolute initialClock, IComparer<TAbsolute> comparer)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
Clock = initialClock;
Comparer = comparer;
}
/// <summary>
/// Adds a relative time value to an absolute time value.
/// </summary>
/// <param name="absolute">Absolute time value.</param>
/// <param name="relative">Relative time value to add.</param>
/// <returns>The resulting absolute time sum value.</returns>
protected abstract TAbsolute Add(TAbsolute absolute, TRelative relative);
/// <summary>
/// Converts the absolute time value to a DateTimeOffset value.
/// </summary>
/// <param name="absolute">Absolute time value to convert.</param>
/// <returns>The corresponding DateTimeOffset value.</returns>
protected abstract DateTimeOffset ToDateTimeOffset(TAbsolute absolute);
/// <summary>
/// Converts the TimeSpan value to a relative time value.
/// </summary>
/// <param name="timeSpan">TimeSpan value to convert.</param>
/// <returns>The corresponding relative time value.</returns>
protected abstract TRelative ToRelative(TimeSpan timeSpan);
/// <summary>
/// Gets whether the scheduler is enabled to run work.
/// </summary>
public bool IsEnabled
{
get;
private set;
}
/// <summary>
/// Gets the comparer used to compare absolute time values.
/// </summary>
protected IComparer<TAbsolute> Comparer
{
get;
private set;
}
/// <summary>
/// Schedules an action to be executed at dueTime.
/// </summary>
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
/// <param name="state">State passed to the action to be executed.</param>
/// <param name="dueTime">Absolute time at which to execute the action.</param>
/// <param name="action">Action to be executed.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
public abstract IDisposable ScheduleAbsolute<TState>(TState state, TAbsolute dueTime, Func<IScheduler, TState, IDisposable> action);
/// <summary>
/// Schedules an action to be executed at dueTime.
/// </summary>
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
/// <param name="state">State passed to the action to be executed.</param>
/// <param name="dueTime">Relative time after which to execute the action.</param>
/// <param name="action">Action to be executed.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
public IDisposable ScheduleRelative<TState>(TState state, TRelative dueTime, Func<IScheduler, TState, IDisposable> action)
{
if (action == null)
throw new ArgumentNullException("action");
var runAt = Add(Clock, dueTime);
return ScheduleAbsolute(state, runAt, action);
}
/// <summary>
/// Schedules an action to be executed.
/// </summary>
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
/// <param name="state">State passed to the action to be executed.</param>
/// <param name="action">Action to be executed.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
/// <exception cref="ArgumentNullException"><paramref name="action"/> is null.</exception>
public IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
{
if (action == null)
throw new ArgumentNullException("action");
return ScheduleAbsolute(state, Clock, action);
}
/// <summary>
/// Schedules an action to be executed after dueTime.
/// </summary>
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
/// <param name="state">State passed to the action to be executed.</param>
/// <param name="dueTime">Relative time after which to execute the action.</param>
/// <param name="action">Action to be executed.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
/// <exception cref="ArgumentNullException"><paramref name="action"/> is null.</exception>
public IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
if (action == null)
throw new ArgumentNullException("action");
return ScheduleRelative(state, ToRelative(dueTime), action);
}
/// <summary>
/// Schedules an action to be executed at dueTime.
/// </summary>
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
/// <param name="state">State passed to the action to be executed.</param>
/// <param name="dueTime">Absolute time at which to execute the action.</param>
/// <param name="action">Action to be executed.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
/// <exception cref="ArgumentNullException"><paramref name="action"/> is null.</exception>
public IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
{
if (action == null)
throw new ArgumentNullException("action");
return ScheduleRelative(state, ToRelative(dueTime - Now), action);
}
/// <summary>
/// Starts the virtual time scheduler.
/// </summary>
public void Start()
{
if (!IsEnabled)
{
IsEnabled = true;
do
{
var next = GetNext();
if (next != null)
{
if (Comparer.Compare(next.DueTime, Clock) > 0)
Clock = next.DueTime;
next.Invoke();
}
else
IsEnabled = false;
} while (IsEnabled);
}
}
/// <summary>
/// Stops the virtual time scheduler.
/// </summary>
public void Stop()
{
IsEnabled = false;
}
/// <summary>
/// Advances the scheduler's clock to the specified time, running all work till that point.
/// </summary>
/// <param name="time">Absolute time to advance the scheduler's clock to.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="time"/> is in the past.</exception>
/// <exception cref="InvalidOperationException">The scheduler is already running. VirtualTimeScheduler doesn't support running nested work dispatch loops. To simulate time slippage while running work on the scheduler, use <see cref="Sleep"/>.</exception>
public void AdvanceTo(TAbsolute time)
{
var dueToClock = Comparer.Compare(time, Clock);
if (dueToClock < 0)
throw new ArgumentOutOfRangeException("time");
if (dueToClock == 0)
return;
if (!IsEnabled)
{
IsEnabled = true;
do
{
var next = GetNext();
if (next != null && Comparer.Compare(next.DueTime, time) <= 0)
{
if (Comparer.Compare(next.DueTime, Clock) > 0)
Clock = next.DueTime;
next.Invoke();
}
else
IsEnabled = false;
} while (IsEnabled);
Clock = time;
}
else
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.CANT_ADVANCE_WHILE_RUNNING, "AdvanceTo"));
}
}
/// <summary>
/// Advances the scheduler's clock by the specified relative time, running all work scheduled for that timespan.
/// </summary>
/// <param name="time">Relative time to advance the scheduler's clock by.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="time"/> is negative.</exception>
/// <exception cref="InvalidOperationException">The scheduler is already running. VirtualTimeScheduler doesn't support running nested work dispatch loops. To simulate time slippage while running work on the scheduler, use <see cref="Sleep"/>.</exception>
public void AdvanceBy(TRelative time)
{
var dt = Add(Clock, time);
var dueToClock = Comparer.Compare(dt, Clock);
if (dueToClock < 0)
throw new ArgumentOutOfRangeException("time");
if (dueToClock == 0)
return;
if (!IsEnabled)
{
AdvanceTo(dt);
}
else
{
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.CANT_ADVANCE_WHILE_RUNNING, "AdvanceBy"));
}
}
/// <summary>
/// Advances the scheduler's clock by the specified relative time.
/// </summary>
/// <param name="time">Relative time to advance the scheduler's clock by.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="time"/> is negative.</exception>
public void Sleep(TRelative time)
{
var dt = Add(Clock, time);
var dueToClock = Comparer.Compare(dt, Clock);
if (dueToClock < 0)
throw new ArgumentOutOfRangeException("time");
Clock = dt;
}
/// <summary>
/// Gets the scheduler's absolute time clock value.
/// </summary>
public TAbsolute Clock
{
get;
protected set;
}
/// <summary>
/// Gets the scheduler's notion of current time.
/// </summary>
public DateTimeOffset Now
{
get { return ToDateTimeOffset(Clock); }
}
/// <summary>
/// Gets the next scheduled item to be executed.
/// </summary>
/// <returns>The next scheduled item.</returns>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "By design. Side-effecting operation to retrieve the next element.")]
protected abstract IScheduledItem<TAbsolute> GetNext();
object IServiceProvider.GetService(Type serviceType)
{
return GetService(serviceType);
}
/// <summary>
/// Discovers scheduler services by interface type. The base class implementation supports
/// only the IStopwatchProvider service. To influence service discovery - such as adding
/// support for other scheduler services - derived types can override this method.
/// </summary>
/// <param name="serviceType">Scheduler service interface type to discover.</param>
/// <returns>Object implementing the requested service, if available; null otherwise.</returns>
protected virtual object GetService(Type serviceType)
{
if (serviceType == typeof(IStopwatchProvider))
return this as IStopwatchProvider;
return null;
}
/// <summary>
/// Starts a new stopwatch object.
/// </summary>
/// <returns>New stopwatch object; started at the time of the request.</returns>
public IStopwatch StartStopwatch()
{
var start = ToDateTimeOffset(Clock);
return new VirtualTimeStopwatch(() => ToDateTimeOffset(Clock) - start);
}
class VirtualTimeStopwatch : IStopwatch
{
private readonly Func<TimeSpan> _getElapsed;
public VirtualTimeStopwatch(Func<TimeSpan> getElapsed)
{
_getElapsed = getElapsed;
}
public TimeSpan Elapsed
{
get { return _getElapsed(); }
}
}
}
/// <summary>
/// Base class for virtual time schedulers using a priority queue for scheduled items.
/// </summary>
/// <typeparam name="TAbsolute">Absolute time representation type.</typeparam>
/// <typeparam name="TRelative">Relative time representation type.</typeparam>
public abstract class VirtualTimeScheduler<TAbsolute, TRelative> : VirtualTimeSchedulerBase<TAbsolute, TRelative>
where TAbsolute : IComparable<TAbsolute>
{
private readonly SchedulerQueue<TAbsolute> queue = new SchedulerQueue<TAbsolute>();
/// <summary>
/// Creates a new virtual time scheduler with the default value of TAbsolute as the initial clock value.
/// </summary>
protected VirtualTimeScheduler()
: base()
{
}
/// <summary>
/// Creates a new virtual time scheduler.
/// </summary>
/// <param name="initialClock">Initial value for the clock.</param>
/// <param name="comparer">Comparer to determine causality of events based on absolute time.</param>
/// <exception cref="ArgumentNullException"><paramref name="comparer"/> is null.</exception>
protected VirtualTimeScheduler(TAbsolute initialClock, IComparer<TAbsolute> comparer)
: base(initialClock, comparer)
{
}
/// <summary>
/// Gets the next scheduled item to be executed.
/// </summary>
/// <returns>The next scheduled item.</returns>
protected override IScheduledItem<TAbsolute> GetNext()
{
while (queue.Count > 0)
{
var next = queue.Peek();
if (next.IsCanceled)
queue.Dequeue();
else
return next;
}
return null;
}
/// <summary>
/// Schedules an action to be executed at dueTime.
/// </summary>
/// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
/// <param name="state">State passed to the action to be executed.</param>
/// <param name="action">Action to be executed.</param>
/// <param name="dueTime">Absolute time at which to execute the action.</param>
/// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
/// <exception cref="ArgumentNullException"><paramref name="action"/> is null.</exception>
public override IDisposable ScheduleAbsolute<TState>(TState state, TAbsolute dueTime, Func<IScheduler, TState, IDisposable> action)
{
if (action == null)
throw new ArgumentNullException("action");
var si = default(ScheduledItem<TAbsolute, TState>);
var run = new Func<IScheduler, TState, IDisposable>((scheduler, state1) =>
{
queue.Remove(si);
return action(scheduler, state1);
});
si = new ScheduledItem<TAbsolute, TState>(this, state, run, dueTime, Comparer);
queue.Enqueue(si);
return Disposable.Create(si.Cancel);
}
}
}

View File

@@ -0,0 +1,116 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Reactive
{
/// <summary>
/// Represents a .NET event invocation consisting of the weakly typed object that raised the event and the data that was generated by the event.
/// </summary>
/// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
public class EventPattern<TEventArgs> : EventPattern<object, TEventArgs>
#if !NO_EVENTARGS_CONSTRAINT
where TEventArgs : EventArgs
#endif
{
/// <summary>
/// Creates a new data representation instance of a .NET event invocation with the given sender and event data.
/// </summary>
/// <param name="sender">The sender object that raised the event.</param>
/// <param name="e">The event data that was generated by the event.</param>
public EventPattern(object sender, TEventArgs e)
: base(sender, e)
{
}
}
/// <summary>
/// Represents a .NET event invocation consisting of the strongly typed object that raised the event and the data that was generated by the event.
/// </summary>
/// <typeparam name="TSender">The type of the sender that raised the event.</typeparam>
/// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
public class EventPattern<TSender, TEventArgs> : IEquatable<EventPattern<TSender, TEventArgs>>, IEventPattern<TSender, TEventArgs>
#if !NO_EVENTARGS_CONSTRAINT
where TEventArgs : EventArgs
#endif
{
/// <summary>
/// Creates a new data representation instance of a .NET event invocation with the given sender and event data.
/// </summary>
/// <param name="sender">The sender object that raised the event.</param>
/// <param name="e">The event data that was generated by the event.</param>
public EventPattern(TSender sender, TEventArgs e)
{
Sender = sender;
EventArgs = e;
}
/// <summary>
/// Gets the sender object that raised the event.
/// </summary>
public TSender Sender { get; private set; }
/// <summary>
/// Gets the event data that was generated by the event.
/// </summary>
public TEventArgs EventArgs { get; private set; }
/// <summary>
/// Determines whether the current EventPattern&lt;TSender, TEventArgs&gt; object represents the same event as a specified EventPattern&lt;TSender, TEventArgs&gt; object.
/// </summary>
/// <param name="other">An object to compare to the current EventPattern&lt;TSender, TEventArgs&gt; object.</param>
/// <returns>true if both EventPattern&lt;TSender, TEventArgs&gt; objects represent the same event; otherwise, false.</returns>
public bool Equals(EventPattern<TSender, TEventArgs> other)
{
if (object.ReferenceEquals(null, other))
return false;
if (object.ReferenceEquals(this, other))
return true;
return EqualityComparer<TSender>.Default.Equals(Sender, other.Sender) && EqualityComparer<TEventArgs>.Default.Equals(EventArgs, other.EventArgs);
}
/// <summary>
/// Determines whether the specified System.Object is equal to the current EventPattern&lt;TSender, TEventArgs&gt;.
/// </summary>
/// <param name="obj">The System.Object to compare with the current EventPattern&lt;TSender, TEventArgs&gt;.</param>
/// <returns>true if the specified System.Object is equal to the current EventPattern&lt;TSender, TEventArgs&gt;; otherwise, false.</returns>
public override bool Equals(object obj)
{
return Equals(obj as EventPattern<TSender, TEventArgs>);
}
/// <summary>
/// Returns the hash code for the current EventPattern&lt;TSender, TEventArgs&gt; instance.
/// </summary>
/// <returns>A hash code for the current EventPattern&lt;TSender, TEventArgs&gt; instance.</returns>
public override int GetHashCode()
{
var x = EqualityComparer<TSender>.Default.GetHashCode(Sender);
var y = EqualityComparer<TEventArgs>.Default.GetHashCode(EventArgs);
return (x << 5) + (x ^ y);
}
/// <summary>
/// Determines whether two specified EventPattern&lt;TSender, TEventArgs&gt; objects represent the same event.
/// </summary>
/// <param name="first">The first EventPattern&lt;TSender, TEventArgs&gt; to compare, or null.</param>
/// <param name="second">The second EventPattern&lt;TSender, TEventArgs&gt; to compare, or null.</param>
/// <returns>true if both EventPattern&lt;TSender, TEventArgs&gt; objects represent the same event; otherwise, false.</returns>
public static bool operator ==(EventPattern<TSender, TEventArgs> first, EventPattern<TSender, TEventArgs> second)
{
return object.Equals(first, second);
}
/// <summary>
/// Determines whether two specified EventPattern&lt;TSender, TEventArgs&gt; objects represent a different event.
/// </summary>
/// <param name="first">The first EventPattern&lt;TSender, TEventArgs&gt; to compare, or null.</param>
/// <param name="second">The second EventPattern&lt;TSender, TEventArgs&gt; to compare, or null.</param>
/// <returns>true if both EventPattern&lt;TSender, TEventArgs&gt; objects don't represent the same event; otherwise, false.</returns>
public static bool operator !=(EventPattern<TSender, TEventArgs> first, EventPattern<TSender, TEventArgs> second)
{
return !object.Equals(first, second);
}
}
}

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Reactive
{
class EventPatternSource<TEventArgs> : EventPatternSourceBase<object, TEventArgs>, IEventPatternSource<TEventArgs>
#if !NO_EVENTARGS_CONSTRAINT
where TEventArgs : EventArgs
#endif
{
public EventPatternSource(IObservable<EventPattern<object, TEventArgs>> source, Action<Action<object, TEventArgs>, /*object,*/ EventPattern<object, TEventArgs>> invokeHandler)
: base(source, invokeHandler)
{
}
event EventHandler<TEventArgs> IEventPatternSource<TEventArgs>.OnNext
{
add
{
Add(value, (o, e) => value(o, e));
}
remove
{
Remove(value);
}
}
}
}

View File

@@ -0,0 +1,127 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Reactive
{
/// <summary>
/// Base class for classes that expose an observable sequence as a well-known event pattern (sender, event arguments).
/// Contains functionality to maintain a map of event handler delegates to observable sequence subscriptions. Subclasses
/// should only add an event with custom add and remove methods calling into the base class's operations.
/// </summary>
/// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
/// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
public abstract class EventPatternSourceBase<TSender, TEventArgs>
#if !NO_EVENTARGS_CONSTRAINT
where TEventArgs : EventArgs
#endif
{
private readonly IObservable<EventPattern<TSender, TEventArgs>> _source;
private readonly Dictionary<Delegate, Stack<IDisposable>> _subscriptions;
private readonly Action<Action<TSender, TEventArgs>, /*object,*/ EventPattern<TSender, TEventArgs>> _invokeHandler;
/// <summary>
/// Creates a new event pattern source.
/// </summary>
/// <param name="source">Source sequence to expose as an event.</param>
/// <param name="invokeHandler">Delegate used to invoke the event for each element of the sequence.</param>
/// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="invokeHandler"/> is null.</exception>
protected EventPatternSourceBase(IObservable<EventPattern<TSender, TEventArgs>> source, Action<Action<TSender, TEventArgs>, /*object,*/ EventPattern<TSender, TEventArgs>> invokeHandler)
{
if (source == null)
throw new ArgumentNullException("source");
if (invokeHandler == null)
throw new ArgumentNullException("invokeHandler");
_source = source;
_invokeHandler = invokeHandler;
_subscriptions = new Dictionary<Delegate, Stack<IDisposable>>();
}
/// <summary>
/// Adds the specified event handler, causing a subscription to the underlying source.
/// </summary>
/// <param name="handler">Event handler to add. The same delegate should be passed to the Remove operation in order to remove the event handler.</param>
/// <param name="invoke">Invocation delegate to raise the event in the derived class.</param>
/// <exception cref="ArgumentNullException"><paramref name="handler"/> or <paramref name="invoke"/> is null.</exception>
protected void Add(Delegate handler, Action<TSender, TEventArgs> invoke)
{
if (handler == null)
throw new ArgumentNullException("handler");
if (invoke == null)
throw new ArgumentNullException("invoke");
var gate = new object();
var isAdded = false;
var isDone = false;
var remove = new Action(() =>
{
lock (gate)
{
if (isAdded)
Remove(handler);
else
isDone = true;
}
});
//
// [OK] Use of unsafe Subscribe: non-pretentious wrapper of an observable in an event; exceptions can occur during +=.
//
var d = _source.Subscribe/*Unsafe*/(
x => _invokeHandler(invoke, /*this,*/ x),
ex => { remove(); ex.Throw(); },
() => remove()
);
lock (gate)
{
if (!isDone)
{
Add(handler, d);
isAdded = true;
}
}
}
private void Add(Delegate handler, IDisposable disposable)
{
lock (_subscriptions)
{
var l = new Stack<IDisposable>();
if (!_subscriptions.TryGetValue(handler, out l))
_subscriptions[handler] = l = new Stack<IDisposable>();
l.Push(disposable);
}
}
/// <summary>
/// Removes the specified event handler, causing a disposal of the corresponding subscription to the underlying source that was created during the Add operation.
/// </summary>
/// <param name="handler">Event handler to remove. This should be the same delegate as one that was passed to the Add operation.</param>
/// <exception cref="ArgumentNullException"><paramref name="handler"/> is null.</exception>
protected void Remove(Delegate handler)
{
if (handler == null)
throw new ArgumentNullException("handler");
var d = default(IDisposable);
lock (_subscriptions)
{
var l = new Stack<IDisposable>();
if (_subscriptions.TryGetValue(handler, out l))
{
d = l.Pop();
if (l.Count == 0)
_subscriptions.Remove(handler);
}
}
if (d != null)
d.Dispose();
}
}
}

View File

@@ -0,0 +1,95 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Reactive
{
class EventSource<T> : IEventSource<T>
{
private readonly IObservable<T> _source;
private readonly Dictionary<Delegate, Stack<IDisposable>> _subscriptions;
private readonly Action<Action<T>, /*object,*/ T> _invokeHandler;
public EventSource(IObservable<T> source, Action<Action<T>, /*object,*/ T> invokeHandler)
{
_source = source;
_invokeHandler = invokeHandler;
_subscriptions = new Dictionary<Delegate, Stack<IDisposable>>();
}
public event Action<T> OnNext
{
add
{
var gate = new object();
var isAdded = false;
var isDone = false;
var remove = new Action(() =>
{
lock (gate)
{
if (isAdded)
Remove(value);
else
isDone = true;
}
});
//
// [OK] Use of unsafe Subscribe: non-pretentious wrapper of an observable in an event; exceptions can occur during +=.
//
var d = _source.Subscribe/*Unsafe*/(
x => _invokeHandler(value, /*this,*/ x),
ex => { remove(); ex.Throw(); },
() => remove()
);
lock (gate)
{
if (!isDone)
{
Add(value, d);
isAdded = true;
}
}
}
remove
{
Remove(value);
}
}
private void Add(Delegate handler, IDisposable disposable)
{
lock (_subscriptions)
{
var l = new Stack<IDisposable>();
if (!_subscriptions.TryGetValue(handler, out l))
_subscriptions[handler] = l = new Stack<IDisposable>();
l.Push(disposable);
}
}
private void Remove(Delegate handler)
{
var d = default(IDisposable);
lock (_subscriptions)
{
var l = new Stack<IDisposable>();
if (_subscriptions.TryGetValue(handler, out l))
{
d = l.Pop();
if (l.Count == 0)
_subscriptions.Remove(handler);
}
}
if (d != null)
d.Dispose();
}
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace System.Reactive
{
internal sealed class AnonymousEnumerable<T> : IEnumerable<T>
{
private readonly Func<IEnumerator<T>> getEnumerator;
public AnonymousEnumerable(Func<IEnumerator<T>> getEnumerator)
{
this.getEnumerator = getEnumerator;
}
public IEnumerator<T> GetEnumerator()
{
return getEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Reactive
{
#if NO_PERF
class BinaryObserver<TLeft, TRight> : IObserver<Either<Notification<TLeft>, Notification<TRight>>>
{
public BinaryObserver(IObserver<TLeft> leftObserver, IObserver<TRight> rightObserver)
{
LeftObserver = leftObserver;
RightObserver = rightObserver;
}
public BinaryObserver(Action<Notification<TLeft>> left, Action<Notification<TRight>> right)
: this(left.ToObserver(), right.ToObserver())
{
}
public IObserver<TLeft> LeftObserver { get; private set; }
public IObserver<TRight> RightObserver { get; private set; }
void IObserver<Either<Notification<TLeft>, Notification<TRight>>>.OnNext(Either<Notification<TLeft>, Notification<TRight>> value)
{
value.Switch(left => left.Accept(LeftObserver), right => right.Accept(RightObserver));
}
void IObserver<Either<Notification<TLeft>, Notification<TRight>>>.OnError(Exception exception)
{
}
void IObserver<Either<Notification<TLeft>, Notification<TRight>>>.OnCompleted()
{
}
}
#endif
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace System.Reactive
{
abstract class ConcatSink<TSource> : TailRecursiveSink<TSource>
{
public ConcatSink(IObserver<TSource> observer, IDisposable cancel)
: base(observer, cancel)
{
}
protected override IEnumerable<IObservable<TSource>> Extract(IObservable<TSource> source)
{
var concat = source as IConcatenatable<TSource>;
if (concat != null)
return concat.GetSources();
return null;
}
public override void OnCompleted()
{
_recurse();
}
}
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
//
// NOTE: Identical copies of this file are kept in System.Reactive.Linq and System.Reactive.Providers.
//
namespace System.Reactive
{
// We can't make those based on the Strings_*.resx file, because the ObsoleteAttribute needs a compile-time constant.
class Constants_Linq
{
#if PREFER_ASYNC
public const string USE_ASYNC = "This blocking operation is no longer supported. Instead, use the async version in combination with C# and Visual Basic async/await support. In case you need a blocking operation, use Wait or convert the resulting observable sequence to a Task object and block. See http://go.microsoft.com/fwlink/?LinkID=260866 for more information.";
public const string USE_TASK_FROMASYNCPATTERN = "This conversion is no longer supported. Replace use of the Begin/End asynchronous method pair with a new Task-based async method, and convert the result using ToObservable. If no Task-based async method is available, use Task.Factory.FromAsync to obtain a Task object. See http://go.microsoft.com/fwlink/?LinkID=260866 for more information.";
#endif
}
}

View File

@@ -0,0 +1,115 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Globalization;
namespace System.Reactive
{
abstract class Either<TLeft, TRight>
{
Either()
{
}
public static Either<TLeft, TRight> CreateLeft(TLeft value)
{
return new Either<TLeft, TRight>.Left(value);
}
public static Either<TLeft, TRight> CreateRight(TRight value)
{
return new Either<TLeft, TRight>.Right(value);
}
public abstract TResult Switch<TResult>(Func<TLeft, TResult> caseLeft, Func<TRight, TResult> caseRight);
public abstract void Switch(Action<TLeft> caseLeft, Action<TRight> caseRight);
public sealed class Left : Either<TLeft, TRight>, IEquatable<Left>
{
public TLeft Value { get; private set; }
public Left(TLeft value)
{
Value = value;
}
public override TResult Switch<TResult>(Func<TLeft, TResult> caseLeft, Func<TRight, TResult> caseRight)
{
return caseLeft(Value);
}
public override void Switch(Action<TLeft> caseLeft, Action<TRight> caseRight)
{
caseLeft(Value);
}
public bool Equals(Left other)
{
if (other == this)
return true;
if (other == null)
return false;
return EqualityComparer<TLeft>.Default.Equals(Value, other.Value);
}
public override bool Equals(object obj)
{
return Equals(obj as Left);
}
public override int GetHashCode()
{
return EqualityComparer<TLeft>.Default.GetHashCode(Value);
}
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "Left({0})", Value);
}
}
public sealed class Right : Either<TLeft, TRight>, IEquatable<Right>
{
public TRight Value { get; private set; }
public Right(TRight value)
{
Value = value;
}
public override TResult Switch<TResult>(Func<TLeft, TResult> caseLeft, Func<TRight, TResult> caseRight)
{
return caseRight(Value);
}
public override void Switch(Action<TLeft> caseLeft, Action<TRight> caseRight)
{
caseRight(Value);
}
public bool Equals(Right other)
{
if (other == this)
return true;
if (other == null)
return false;
return EqualityComparer<TRight>.Default.Equals(Value, other.Value);
}
public override bool Equals(object obj)
{
return Equals(obj as Right);
}
public override int GetHashCode()
{
return EqualityComparer<TRight>.Default.GetHashCode(Value);
}
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "Right({0})", Value);
}
}
}
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#if NO_HASHSET
using System;
using System.Collections.Generic;
namespace System.Reactive
{
class HashSet<T>
{
private readonly Dictionary<T, object> _set;
private bool _hasNull;
public HashSet(IEqualityComparer<T> comparer)
{
_set = new Dictionary<T, object>(comparer);
_hasNull = false;
}
public bool Add(T value)
{
//
// Note: The box instruction in the IL will be erased by the JIT in case T is
// a value type. See GroupBy for more information.
//
if (value == null)
{
if (_hasNull)
return false;
_hasNull = true;
return true;
}
else
{
if (_set.ContainsKey(value))
return false;
_set[value] = null;
return true;
}
}
}
}
#endif

View File

@@ -0,0 +1,45 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#if !NO_PERF
using System;
using System.Collections.Generic;
using System.Reactive.Linq.ObservableImpl;
namespace System.Reactive
{
internal static class Helpers
{
public static int? GetLength<T>(IEnumerable<T> source)
{
var array = source as T[];
if (array != null)
return array.Length;
var list = source as IList<T>;
if (list != null)
return list.Count;
return null;
}
public static IObservable<T> Unpack<T>(IObservable<T> source)
{
var hasOpt = default(bool);
do
{
hasOpt = false;
var eval = source as IEvaluatableObservable<T>;
if (eval != null)
{
source = eval.Eval();
hasOpt = true;
}
} while (hasOpt);
return source;
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
namespace System.Reactive
{
interface IConcatenatable<TSource>
{
IEnumerable<IObservable<TSource>> GetSources();
}
}

View File

@@ -0,0 +1,9 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Reactive
{
interface IEvaluatableObservable<T>
{
IObservable<T> Eval();
}
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Reactive
{
class ImmutableList<T>
{
T[] data;
public ImmutableList()
{
data = new T[0];
}
public ImmutableList(T[] data)
{
this.data = data;
}
public ImmutableList<T> Add(T value)
{
var newData = new T[data.Length + 1];
Array.Copy(data, newData, data.Length);
newData[data.Length] = value;
return new ImmutableList<T>(newData);
}
public ImmutableList<T> Remove(T value)
{
var i = IndexOf(value);
if (i < 0)
return this;
var newData = new T[data.Length - 1];
Array.Copy(data, 0, newData, 0, i);
Array.Copy(data, i + 1, newData, i, data.Length - i - 1);
return new ImmutableList<T>(newData);
}
public int IndexOf(T value)
{
for (var i = 0; i < data.Length; ++i)
if (data[i].Equals(value))
return i;
return -1;
}
public T[] Data
{
get { return data; }
}
}
}

View File

@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace System.Reactive
{
class Lookup<K, E> : ILookup<K, E>
{
Dictionary<K, List<E>> d;
public Lookup(IEqualityComparer<K> comparer)
{
d = new Dictionary<K, List<E>>(comparer);
}
public void Add(K key, E element)
{
var list = default(List<E>);
if (!d.TryGetValue(key, out list))
d[key] = list = new List<E>();
list.Add(element);
}
public bool Contains(K key)
{
return d.ContainsKey(key);
}
public int Count
{
get { return d.Count; }
}
public IEnumerable<E> this[K key]
{
get { return Hide(d[key]); }
}
private IEnumerable<E> Hide(List<E> elements)
{
foreach (var x in elements)
yield return x;
}
public IEnumerator<IGrouping<K, E>> GetEnumerator()
{
foreach (var kv in d)
yield return new Grouping(kv);
}
class Grouping : IGrouping<K, E>
{
KeyValuePair<K, List<E>> kv;
public Grouping(KeyValuePair<K, List<E>> kv)
{
this.kv = kv;
}
public K Key
{
get { return kv.Key; }
}
public IEnumerator<E> GetEnumerator()
{
return kv.Value.GetEnumerator();
}
Collections.IEnumerator Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Collections.IEnumerator Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -0,0 +1,109 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
namespace System.Reactive
{
class NopObserver<T> : IObserver<T>
{
public static readonly IObserver<T> Instance = new NopObserver<T>();
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(T value)
{
}
}
class DoneObserver<T> : IObserver<T>
{
public static readonly IObserver<T> Completed = new DoneObserver<T>();
public Exception Exception { get; set; }
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(T value)
{
}
}
class DisposedObserver<T> : IObserver<T>
{
public static readonly IObserver<T> Instance = new DisposedObserver<T>();
public void OnCompleted()
{
throw new ObjectDisposedException("");
}
public void OnError(Exception error)
{
throw new ObjectDisposedException("");
}
public void OnNext(T value)
{
throw new ObjectDisposedException("");
}
}
class Observer<T> : IObserver<T>
{
private readonly ImmutableList<IObserver<T>> _observers;
public Observer(ImmutableList<IObserver<T>> observers)
{
_observers = observers;
}
public void OnCompleted()
{
foreach (var observer in _observers.Data)
observer.OnCompleted();
}
public void OnError(Exception error)
{
foreach (var observer in _observers.Data)
observer.OnError(error);
}
public void OnNext(T value)
{
foreach (var observer in _observers.Data)
observer.OnNext(value);
}
internal IObserver<T> Add(IObserver<T> observer)
{
return new Observer<T>(_observers.Add(observer));
}
internal IObserver<T> Remove(IObserver<T> observer)
{
var i = Array.IndexOf(_observers.Data, observer);
if (i < 0)
return this;
if (_observers.Data.Length == 2)
{
return _observers.Data[1 - i];
}
else
{
return new Observer<T>(_observers.Remove(observer));
}
}
}
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
#if !NO_PERF
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
namespace System.Reactive
{
/// <summary>
/// Base class for implementation of query operators, providing performance benefits over the use of <see cref="System.Reactive.Linq.Observable.Create">Observable.Create</see>.
/// </summary>
/// <typeparam name="TSource">Type of the resulting sequence's elements.</typeparam>
abstract class Producer<TSource> : IObservable<TSource>
{
/// <summary>
/// Publicly visible Subscribe method.
/// </summary>
/// <param name="observer">Observer to send notifications on. The implementation of a producer must ensure the correct message grammar on the observer.</param>
/// <returns>IDisposable to cancel the subscription. This causes the underlying sink to be notified of unsubscription, causing it to prevent further messages from being sent to the observer.</returns>
public IDisposable Subscribe(IObserver<TSource> observer)
{
if (observer == null)
throw new ArgumentNullException("observer");
var sink = new SingleAssignmentDisposable();
var subscription = new SingleAssignmentDisposable();
if (CurrentThreadScheduler.Instance.ScheduleRequired)
{
CurrentThreadScheduler.Instance.Schedule(this, (_, me) => subscription.Disposable = me.Run(observer, subscription, s => sink.Disposable = s));
}
else
{
subscription.Disposable = this.Run(observer, subscription, s => sink.Disposable = s);
}
return new CompositeDisposable(2) { sink, subscription };
}
/// <summary>
/// Core implementation of the query operator, called upon a new subscription to the producer object.
/// </summary>
/// <param name="observer">Observer to send notifications on. The implementation of a producer must ensure the correct message grammar on the observer.</param>
/// <param name="cancel">The subscription disposable object returned from the Run call, passed in such that it can be forwarded to the sink, allowing it to dispose the subscription upon sending a final message (or prematurely for other reasons).</param>
/// <param name="setSink">Callback to communicate the sink object to the subscriber, allowing consumers to tunnel a Dispose call into the sink, which can stop the processing.</param>
/// <returns>Disposable representing all the resources and/or subscriptions the operator uses to process events.</returns>
/// <remarks>The <paramref name="observer">observer</paramref> passed in to this method is not protected using auto-detach behavior upon an OnError or OnCompleted call. The implementation must ensure proper resource disposal and enforce the message grammar.</remarks>
protected abstract IDisposable Run(IObserver<TSource> observer, IDisposable cancel, Action<IDisposable> setSink);
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More