blob: d90f41234b0a149fe23eca0bf73a3b5a5e7fc0bf [file] [log] [blame]
// Source: https://github.com/PrismLibrary/Prism/blob/7f0b1680bbe754da790274f80851265f808d9bbf
#region Copyright .NET Foundation, Licensed under the MIT License (MIT)
// The MIT License (MIT)
//
// Copyright(c).NET Foundation
//
// All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
// is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#endregion
#if !FEATURE_CONDITIONALWEAKTABLE_ENUMERATOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace Lucene.Net.Util.Events
{
/// <summary>
/// Defines a class that manages publication and subscription to events.
/// </summary>
internal class PubSubEvent : EventBase
{
/// <summary>
/// Subscribes a delegate to an event that will be published on the <see cref="ThreadOption.PublisherThread"/>.
/// <see cref="PubSubEvent"/> will maintain a <see cref="WeakReference"/> to the target of the supplied <paramref name="action"/> delegate.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is published.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public SubscriptionToken Subscribe(Action action)
{
return Subscribe(action, ThreadOption.PublisherThread);
}
/// <summary>
/// Subscribes a delegate to an event.
/// PubSubEvent will maintain a <see cref="WeakReference"/> to the Target of the supplied <paramref name="action"/> delegate.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is raised.</param>
/// <param name="threadOption">Specifies on which thread to receive the delegate callback.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public SubscriptionToken Subscribe(Action action, ThreadOption threadOption)
{
return Subscribe(action, threadOption, false);
}
/// <summary>
/// Subscribes a delegate to an event that will be published on the <see cref="ThreadOption.PublisherThread"/>.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is published.</param>
/// <param name="keepSubscriberReferenceAlive">When <see langword="true"/>, the <see cref="PubSubEvent"/> keeps a reference to the subscriber so it does not get garbage collected.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// If <paramref name="keepSubscriberReferenceAlive"/> is set to <see langword="false" />, <see cref="PubSubEvent"/> will maintain a <see cref="WeakReference"/> to the Target of the supplied <paramref name="action"/> delegate.
/// If not using a WeakReference (<paramref name="keepSubscriberReferenceAlive"/> is <see langword="true" />), the user must explicitly call Unsubscribe for the event when disposing the subscriber in order to avoid memory leaks or unexpected behavior.
/// <para/>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public SubscriptionToken Subscribe(Action action, bool keepSubscriberReferenceAlive)
{
return Subscribe(action, ThreadOption.PublisherThread, keepSubscriberReferenceAlive);
}
/// <summary>
/// Subscribes a delegate to an event.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is published.</param>
/// <param name="threadOption">Specifies on which thread to receive the delegate callback.</param>
/// <param name="keepSubscriberReferenceAlive">When <see langword="true"/>, the <see cref="PubSubEvent"/> keeps a reference to the subscriber so it does not get garbage collected.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// If <paramref name="keepSubscriberReferenceAlive"/> is set to <see langword="false" />, <see cref="PubSubEvent"/> will maintain a <see cref="WeakReference"/> to the Target of the supplied <paramref name="action"/> delegate.
/// If not using a WeakReference (<paramref name="keepSubscriberReferenceAlive"/> is <see langword="true" />), the user must explicitly call Unsubscribe for the event when disposing the subscriber in order to avoid memory leaks or unexpected behavior.
/// <para/>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public virtual SubscriptionToken Subscribe(Action action, ThreadOption threadOption, bool keepSubscriberReferenceAlive)
{
IDelegateReference actionReference = new DelegateReference(action, keepSubscriberReferenceAlive);
EventSubscription subscription;
switch (threadOption)
{
case ThreadOption.PublisherThread:
subscription = new EventSubscription(actionReference);
break;
case ThreadOption.BackgroundThread:
subscription = new BackgroundEventSubscription(actionReference);
break;
case ThreadOption.UIThread:
if (SynchronizationContext == null) throw new InvalidOperationException(Resources.EventAggregatorNotConstructedOnUIThread);
subscription = new DispatcherEventSubscription(actionReference, SynchronizationContext);
break;
default:
subscription = new EventSubscription(actionReference);
break;
}
return InternalSubscribe(subscription);
}
/// <summary>
/// Publishes the <see cref="PubSubEvent"/>.
/// </summary>
public virtual void Publish()
{
InternalPublish();
}
/// <summary>
/// Removes the first subscriber matching <see cref="Action"/> from the subscribers' list.
/// </summary>
/// <param name="subscriber">The <see cref="Action"/> used when subscribing to the event.</param>
public virtual void Unsubscribe(Action subscriber)
{
lock (Subscriptions)
{
IEventSubscription eventSubscription = Subscriptions.Cast<EventSubscription>().FirstOrDefault(evt => evt.Action == subscriber);
if (eventSubscription != null)
{
Subscriptions.Remove(eventSubscription);
}
}
}
/// <summary>
/// Returns <see langword="true"/> if there is a subscriber matching <see cref="Action"/>.
/// </summary>
/// <param name="subscriber">The <see cref="Action"/> used when subscribing to the event.</param>
/// <returns><see langword="true"/> if there is an <see cref="Action"/> that matches; otherwise <see langword="false"/>.</returns>
public virtual bool Contains(Action subscriber)
{
IEventSubscription eventSubscription;
lock (Subscriptions)
{
eventSubscription = Subscriptions.Cast<EventSubscription>().FirstOrDefault(evt => evt.Action == subscriber);
}
return eventSubscription != null;
}
}
/// <summary>
/// Defines a class that manages publication and subscription to events.
/// </summary>
/// <typeparam name="TPayload">The type of message that will be passed to the subscribers.</typeparam>
internal class PubSubEvent<TPayload> : EventBase
{
/// <summary>
/// Subscribes a delegate to an event that will be published on the <see cref="ThreadOption.PublisherThread"/>.
/// <see cref="PubSubEvent{TPayload}"/> will maintain a <see cref="WeakReference"/> to the target of the supplied <paramref name="action"/> delegate.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is published.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public SubscriptionToken Subscribe(Action<TPayload> action)
{
return Subscribe(action, ThreadOption.PublisherThread);
}
/// <summary>
/// Subscribes a delegate to an event that will be published on the <see cref="ThreadOption.PublisherThread"/>
/// </summary>
/// <param name="action">The delegate that gets executed when the event is raised.</param>
/// <param name="filter">Filter to evaluate if the subscriber should receive the event.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
public virtual SubscriptionToken Subscribe(Action<TPayload> action, Predicate<TPayload> filter)
{
return Subscribe(action, ThreadOption.PublisherThread, false, filter);
}
/// <summary>
/// Subscribes a delegate to an event.
/// PubSubEvent will maintain a <see cref="WeakReference"/> to the Target of the supplied <paramref name="action"/> delegate.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is raised.</param>
/// <param name="threadOption">Specifies on which thread to receive the delegate callback.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption)
{
return Subscribe(action, threadOption, false);
}
/// <summary>
/// Subscribes a delegate to an event that will be published on the <see cref="ThreadOption.PublisherThread"/>.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is published.</param>
/// <param name="keepSubscriberReferenceAlive">When <see langword="true"/>, the <see cref="PubSubEvent{TPayload}"/> keeps a reference to the subscriber so it does not get garbage collected.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// If <paramref name="keepSubscriberReferenceAlive"/> is set to <see langword="false" />, <see cref="PubSubEvent{TPayload}"/> will maintain a <see cref="WeakReference"/> to the Target of the supplied <paramref name="action"/> delegate.
/// If not using a WeakReference (<paramref name="keepSubscriberReferenceAlive"/> is <see langword="true" />), the user must explicitly call Unsubscribe for the event when disposing the subscriber in order to avoid memory leaks or unexpected behavior.
/// <para/>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public SubscriptionToken Subscribe(Action<TPayload> action, bool keepSubscriberReferenceAlive)
{
return Subscribe(action, ThreadOption.PublisherThread, keepSubscriberReferenceAlive);
}
/// <summary>
/// Subscribes a delegate to an event.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is published.</param>
/// <param name="threadOption">Specifies on which thread to receive the delegate callback.</param>
/// <param name="keepSubscriberReferenceAlive">When <see langword="true"/>, the <see cref="PubSubEvent{TPayload}"/> keeps a reference to the subscriber so it does not get garbage collected.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// If <paramref name="keepSubscriberReferenceAlive"/> is set to <see langword="false" />, <see cref="PubSubEvent{TPayload}"/> will maintain a <see cref="WeakReference"/> to the Target of the supplied <paramref name="action"/> delegate.
/// If not using a WeakReference (<paramref name="keepSubscriberReferenceAlive"/> is <see langword="true" />), the user must explicitly call Unsubscribe for the event when disposing the subscriber in order to avoid memory leaks or unexpected behavior.
/// <para/>
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive)
{
return Subscribe(action, threadOption, keepSubscriberReferenceAlive, null);
}
/// <summary>
/// Subscribes a delegate to an event.
/// </summary>
/// <param name="action">The delegate that gets executed when the event is published.</param>
/// <param name="threadOption">Specifies on which thread to receive the delegate callback.</param>
/// <param name="keepSubscriberReferenceAlive">When <see langword="true"/>, the <see cref="PubSubEvent{TPayload}"/> keeps a reference to the subscriber so it does not get garbage collected.</param>
/// <param name="filter">Filter to evaluate if the subscriber should receive the event.</param>
/// <returns>A <see cref="SubscriptionToken"/> that uniquely identifies the added subscription.</returns>
/// <remarks>
/// If <paramref name="keepSubscriberReferenceAlive"/> is set to <see langword="false" />, <see cref="PubSubEvent{TPayload}"/> will maintain a <see cref="WeakReference"/> to the Target of the supplied <paramref name="action"/> delegate.
/// If not using a WeakReference (<paramref name="keepSubscriberReferenceAlive"/> is <see langword="true" />), the user must explicitly call Unsubscribe for the event when disposing the subscriber in order to avoid memory leaks or unexpected behavior.
///
/// The PubSubEvent collection is thread-safe.
/// </remarks>
public virtual SubscriptionToken Subscribe(Action<TPayload> action, ThreadOption threadOption, bool keepSubscriberReferenceAlive, Predicate<TPayload> filter)
{
IDelegateReference actionReference = new DelegateReference(action, keepSubscriberReferenceAlive);
IDelegateReference filterReference;
if (filter != null)
{
filterReference = new DelegateReference(filter, keepSubscriberReferenceAlive);
}
else
{
filterReference = new DelegateReference(new Predicate<TPayload>(delegate { return true; }), true);
}
EventSubscription<TPayload> subscription;
switch (threadOption)
{
case ThreadOption.PublisherThread:
subscription = new EventSubscription<TPayload>(actionReference, filterReference);
break;
case ThreadOption.BackgroundThread:
subscription = new BackgroundEventSubscription<TPayload>(actionReference, filterReference);
break;
case ThreadOption.UIThread:
if (SynchronizationContext == null) throw new InvalidOperationException(Resources.EventAggregatorNotConstructedOnUIThread);
subscription = new DispatcherEventSubscription<TPayload>(actionReference, filterReference, SynchronizationContext);
break;
default:
subscription = new EventSubscription<TPayload>(actionReference, filterReference);
break;
}
return InternalSubscribe(subscription);
}
/// <summary>
/// Publishes the <see cref="PubSubEvent{TPayload}"/>.
/// </summary>
/// <param name="payload">Message to pass to the subscribers.</param>
public virtual void Publish(TPayload payload)
{
InternalPublish(payload);
}
/// <summary>
/// Removes the first subscriber matching <see cref="Action{TPayload}"/> from the subscribers' list.
/// </summary>
/// <param name="subscriber">The <see cref="Action{TPayload}"/> used when subscribing to the event.</param>
public virtual void Unsubscribe(Action<TPayload> subscriber)
{
lock (Subscriptions)
{
IEventSubscription eventSubscription = Subscriptions.Cast<EventSubscription<TPayload>>().FirstOrDefault(evt => evt.Action == subscriber);
if (eventSubscription != null)
{
Subscriptions.Remove(eventSubscription);
}
}
}
/// <summary>
/// Returns <see langword="true"/> if there is a subscriber matching <see cref="Action{TPayload}"/>.
/// </summary>
/// <param name="subscriber">The <see cref="Action{TPayload}"/> used when subscribing to the event.</param>
/// <returns><see langword="true"/> if there is an <see cref="Action{TPayload}"/> that matches; otherwise <see langword="false"/>.</returns>
public virtual bool Contains(Action<TPayload> subscriber)
{
IEventSubscription eventSubscription;
lock (Subscriptions)
{
eventSubscription = Subscriptions.Cast<EventSubscription<TPayload>>().FirstOrDefault(evt => evt.Action == subscriber);
}
return eventSubscription != null;
}
}
}
#endif