mirror of
https://github.com/evopro-ag/Sharp7Reactive.git
synced 2025-12-16 11:42:52 +00:00
Use file scoped namespaces
This commit is contained in:
@@ -1,128 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace Sharp7.Rx.Basics
|
||||
namespace Sharp7.Rx.Basics;
|
||||
|
||||
internal class ConcurrentSubjectDictionary<TKey, TValue> : IDisposable
|
||||
{
|
||||
internal class ConcurrentSubjectDictionary<TKey, TValue> : IDisposable
|
||||
private readonly object dictionaryLock = new object();
|
||||
private readonly Func<TKey, TValue> valueFactory;
|
||||
private ConcurrentDictionary<TKey, SubjectWithRefCounter> dictionary;
|
||||
|
||||
public ConcurrentSubjectDictionary()
|
||||
{
|
||||
private readonly object dictionaryLock = new object();
|
||||
private readonly Func<TKey, TValue> valueFactory;
|
||||
private ConcurrentDictionary<TKey, SubjectWithRefCounter> dictionary;
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>();
|
||||
}
|
||||
|
||||
public ConcurrentSubjectDictionary()
|
||||
public ConcurrentSubjectDictionary(IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>(comparer);
|
||||
}
|
||||
|
||||
public ConcurrentSubjectDictionary(TValue initialValue, IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
valueFactory = _ => initialValue;
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>(comparer);
|
||||
}
|
||||
|
||||
public ConcurrentSubjectDictionary(TValue initialValue)
|
||||
{
|
||||
valueFactory = _ => initialValue;
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>();
|
||||
}
|
||||
|
||||
public ConcurrentSubjectDictionary(Func<TKey, TValue> valueFactory = null)
|
||||
{
|
||||
this.valueFactory = valueFactory;
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>();
|
||||
}
|
||||
|
||||
public IEnumerable<TKey> ExistingKeys => dictionary.Keys;
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public DisposableItem<TValue> GetOrCreateObservable(TKey key)
|
||||
{
|
||||
lock (dictionaryLock)
|
||||
{
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>();
|
||||
}
|
||||
|
||||
public ConcurrentSubjectDictionary(IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>(comparer);
|
||||
}
|
||||
|
||||
public ConcurrentSubjectDictionary(TValue initialValue, IEqualityComparer<TKey> comparer)
|
||||
{
|
||||
valueFactory = _ => initialValue;
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>(comparer);
|
||||
}
|
||||
|
||||
public ConcurrentSubjectDictionary(TValue initialValue)
|
||||
{
|
||||
valueFactory = _ => initialValue;
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>();
|
||||
}
|
||||
|
||||
public ConcurrentSubjectDictionary(Func<TKey, TValue> valueFactory = null)
|
||||
{
|
||||
this.valueFactory = valueFactory;
|
||||
dictionary = new ConcurrentDictionary<TKey, SubjectWithRefCounter>();
|
||||
}
|
||||
|
||||
public IEnumerable<TKey> ExistingKeys => dictionary.Keys;
|
||||
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public DisposableItem<TValue> GetOrCreateObservable(TKey key)
|
||||
{
|
||||
lock (dictionaryLock)
|
||||
var subject = dictionary.AddOrUpdate(key, k => new SubjectWithRefCounter {Counter = 1, Subject = CreateSubject(k)}, (key1, counter) =>
|
||||
{
|
||||
var subject = dictionary.AddOrUpdate(key, k => new SubjectWithRefCounter {Counter = 1, Subject = CreateSubject(k)}, (key1, counter) =>
|
||||
{
|
||||
counter.Counter = counter.Counter + 1;
|
||||
return counter;
|
||||
});
|
||||
counter.Counter = counter.Counter + 1;
|
||||
return counter;
|
||||
});
|
||||
|
||||
return new DisposableItem<TValue>(subject.Subject.AsObservable(), () => RemoveIfNoLongerInUse(key));
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetObserver(TKey key, out IObserver<TValue> subject)
|
||||
{
|
||||
SubjectWithRefCounter subjectWithRefCount;
|
||||
if (dictionary.TryGetValue(key, out subjectWithRefCount))
|
||||
{
|
||||
subject = subjectWithRefCount.Subject.AsObserver();
|
||||
return true;
|
||||
}
|
||||
|
||||
subject = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
if (disposing && dictionary != null)
|
||||
{
|
||||
foreach (var subjectWithRefCounter in dictionary)
|
||||
subjectWithRefCounter.Value.Subject.OnCompleted();
|
||||
dictionary.Clear();
|
||||
dictionary = null;
|
||||
}
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
private ISubject<TValue> CreateSubject(TKey key)
|
||||
{
|
||||
if (valueFactory == null)
|
||||
return new Subject<TValue>();
|
||||
return new BehaviorSubject<TValue>(valueFactory(key));
|
||||
}
|
||||
|
||||
private void RemoveIfNoLongerInUse(TKey variableName)
|
||||
{
|
||||
lock (dictionaryLock)
|
||||
{
|
||||
SubjectWithRefCounter subjectWithRefCount;
|
||||
if (dictionary.TryGetValue(variableName, out subjectWithRefCount))
|
||||
{
|
||||
if (subjectWithRefCount.Counter == 1)
|
||||
dictionary.TryRemove(variableName, out subjectWithRefCount);
|
||||
else subjectWithRefCount.Counter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~ConcurrentSubjectDictionary()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
class SubjectWithRefCounter
|
||||
{
|
||||
public int Counter { get; set; }
|
||||
public ISubject<TValue> Subject { get; set; }
|
||||
return new DisposableItem<TValue>(subject.Subject.AsObservable(), () => RemoveIfNoLongerInUse(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetObserver(TKey key, out IObserver<TValue> subject)
|
||||
{
|
||||
SubjectWithRefCounter subjectWithRefCount;
|
||||
if (dictionary.TryGetValue(key, out subjectWithRefCount))
|
||||
{
|
||||
subject = subjectWithRefCount.Subject.AsObserver();
|
||||
return true;
|
||||
}
|
||||
|
||||
subject = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
if (disposing && dictionary != null)
|
||||
{
|
||||
foreach (var subjectWithRefCounter in dictionary)
|
||||
subjectWithRefCounter.Value.Subject.OnCompleted();
|
||||
dictionary.Clear();
|
||||
dictionary = null;
|
||||
}
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
private ISubject<TValue> CreateSubject(TKey key)
|
||||
{
|
||||
if (valueFactory == null)
|
||||
return new Subject<TValue>();
|
||||
return new BehaviorSubject<TValue>(valueFactory(key));
|
||||
}
|
||||
|
||||
private void RemoveIfNoLongerInUse(TKey variableName)
|
||||
{
|
||||
lock (dictionaryLock)
|
||||
{
|
||||
SubjectWithRefCounter subjectWithRefCount;
|
||||
if (dictionary.TryGetValue(variableName, out subjectWithRefCount))
|
||||
{
|
||||
if (subjectWithRefCount.Counter == 1)
|
||||
dictionary.TryRemove(variableName, out subjectWithRefCount);
|
||||
else subjectWithRefCount.Counter--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~ConcurrentSubjectDictionary()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
class SubjectWithRefCounter
|
||||
{
|
||||
public int Counter { get; set; }
|
||||
public ISubject<TValue> Subject { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,34 @@
|
||||
using System;
|
||||
namespace Sharp7.Rx.Basics;
|
||||
|
||||
namespace Sharp7.Rx.Basics
|
||||
internal class DisposableItem<TValue> : IDisposable
|
||||
{
|
||||
internal class DisposableItem<TValue> : IDisposable
|
||||
private readonly Action disposeAction;
|
||||
|
||||
bool disposed;
|
||||
|
||||
public DisposableItem(IObservable<TValue> observable, Action disposeAction)
|
||||
{
|
||||
private readonly Action disposeAction;
|
||||
|
||||
bool disposed;
|
||||
|
||||
public DisposableItem(IObservable<TValue> observable, Action disposeAction)
|
||||
{
|
||||
this.disposeAction = disposeAction;
|
||||
Observable = observable;
|
||||
}
|
||||
|
||||
public IObservable<TValue> Observable { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
disposeAction();
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
this.disposeAction = disposeAction;
|
||||
Observable = observable;
|
||||
}
|
||||
}
|
||||
|
||||
public IObservable<TValue> Observable { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
disposeAction();
|
||||
}
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,146 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
namespace Sharp7.Rx.Basics;
|
||||
|
||||
namespace Sharp7.Rx.Basics
|
||||
/// <summary>
|
||||
/// Provides a task scheduler that ensures a maximum concurrency level while
|
||||
/// running on top of the ThreadPool.
|
||||
/// from http://msdn.microsoft.com/en-us/library/ee789351.aspx
|
||||
/// </summary>
|
||||
internal class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
|
||||
{
|
||||
/// <summary>Whether the current thread is processing work items.</summary>
|
||||
[ThreadStatic] private static bool currentThreadIsProcessingItems;
|
||||
|
||||
/// <summary>The maximum concurrency level allowed by this scheduler.</summary>
|
||||
private readonly int maxDegreeOfParallelism;
|
||||
|
||||
/// <summary>The list of tasks to be executed.</summary>
|
||||
private readonly LinkedList<Task> tasks = new LinkedList<Task>(); // protected by lock(_tasks)
|
||||
|
||||
/// <summary>Whether the scheduler is currently processing work items.</summary>
|
||||
private int delegatesQueuedOrRunning; // protected by lock(_tasks)
|
||||
|
||||
/// <summary>
|
||||
/// Provides a task scheduler that ensures a maximum concurrency level while
|
||||
/// running on top of the ThreadPool.
|
||||
/// from http://msdn.microsoft.com/en-us/library/ee789351.aspx
|
||||
/// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
|
||||
/// specified degree of parallelism.
|
||||
/// </summary>
|
||||
internal class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
|
||||
/// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param>
|
||||
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
|
||||
{
|
||||
/// <summary>Whether the current thread is processing work items.</summary>
|
||||
[ThreadStatic] private static bool currentThreadIsProcessingItems;
|
||||
if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
|
||||
this.maxDegreeOfParallelism = maxDegreeOfParallelism;
|
||||
}
|
||||
|
||||
/// <summary>The maximum concurrency level allowed by this scheduler.</summary>
|
||||
private readonly int maxDegreeOfParallelism;
|
||||
/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
|
||||
public sealed override int MaximumConcurrencyLevel => maxDegreeOfParallelism;
|
||||
|
||||
/// <summary>The list of tasks to be executed.</summary>
|
||||
private readonly LinkedList<Task> tasks = new LinkedList<Task>(); // protected by lock(_tasks)
|
||||
|
||||
/// <summary>Whether the scheduler is currently processing work items.</summary>
|
||||
private int delegatesQueuedOrRunning; // protected by lock(_tasks)
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the
|
||||
/// specified degree of parallelism.
|
||||
/// </summary>
|
||||
/// <param name="maxDegreeOfParallelism">The maximum degree of parallelism provided by this scheduler.</param>
|
||||
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
|
||||
/// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary>
|
||||
/// <returns>An enumerable of the tasks currently scheduled.</returns>
|
||||
protected sealed override IEnumerable<Task> GetScheduledTasks()
|
||||
{
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism");
|
||||
this.maxDegreeOfParallelism = maxDegreeOfParallelism;
|
||||
Monitor.TryEnter(tasks, ref lockTaken);
|
||||
if (lockTaken) return tasks.ToArray();
|
||||
else throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
|
||||
public sealed override int MaximumConcurrencyLevel => maxDegreeOfParallelism;
|
||||
|
||||
/// <summary>Gets an enumerable of the tasks currently scheduled on this scheduler.</summary>
|
||||
/// <returns>An enumerable of the tasks currently scheduled.</returns>
|
||||
protected sealed override IEnumerable<Task> GetScheduledTasks()
|
||||
finally
|
||||
{
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
Monitor.TryEnter(tasks, ref lockTaken);
|
||||
if (lockTaken) return tasks.ToArray();
|
||||
else throw new NotSupportedException();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) Monitor.Exit(tasks);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Queues a task to the scheduler.</summary>
|
||||
/// <param name="task">The task to be queued.</param>
|
||||
protected sealed override void QueueTask(Task task)
|
||||
{
|
||||
// Add the task to the list of tasks to be processed. If there aren't enough
|
||||
// delegates currently queued or running to process tasks, schedule another.
|
||||
lock (tasks)
|
||||
{
|
||||
tasks.AddLast(task);
|
||||
if (delegatesQueuedOrRunning < maxDegreeOfParallelism)
|
||||
{
|
||||
++delegatesQueuedOrRunning;
|
||||
NotifyThreadPoolOfPendingWork();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary>
|
||||
/// <param name="task">The task to be removed.</param>
|
||||
/// <returns>Whether the task could be found and removed.</returns>
|
||||
protected sealed override bool TryDequeue(Task task)
|
||||
{
|
||||
lock (tasks)
|
||||
{
|
||||
return tasks.Remove(task);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Attempts to execute the specified task on the current thread.</summary>
|
||||
/// <param name="task">The task to be executed.</param>
|
||||
/// <param name="taskWasPreviouslyQueued"></param>
|
||||
/// <returns>Whether the task could be executed on the current thread.</returns>
|
||||
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
|
||||
{
|
||||
// If this thread isn't already processing a task, we don't support inlining
|
||||
if (!currentThreadIsProcessingItems) return false;
|
||||
|
||||
// If the task was previously queued, remove it from the queue
|
||||
if (taskWasPreviouslyQueued) TryDequeue(task);
|
||||
|
||||
// Try to run the task.
|
||||
return TryExecuteTask(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Informs the ThreadPool that there's work to be executed for this scheduler.
|
||||
/// </summary>
|
||||
private void NotifyThreadPoolOfPendingWork()
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(_ =>
|
||||
{
|
||||
// Note that the current thread is now processing work items.
|
||||
// This is necessary to enable inlining of tasks into this thread.
|
||||
currentThreadIsProcessingItems = true;
|
||||
try
|
||||
{
|
||||
// Process all available items in the queue.
|
||||
while (true)
|
||||
{
|
||||
Task item;
|
||||
lock (tasks)
|
||||
{
|
||||
// When there are no more items to be processed,
|
||||
// note that we're done processing, and get out.
|
||||
if (tasks.Count == 0)
|
||||
{
|
||||
--delegatesQueuedOrRunning;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the next item from the queue
|
||||
item = tasks.First.Value;
|
||||
tasks.RemoveFirst();
|
||||
}
|
||||
|
||||
// Execute the task we pulled out of the queue
|
||||
TryExecuteTask(item);
|
||||
}
|
||||
}
|
||||
// We're done processing items on the current thread
|
||||
finally
|
||||
{
|
||||
currentThreadIsProcessingItems = false;
|
||||
}
|
||||
}, null);
|
||||
if (lockTaken) Monitor.Exit(tasks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Queues a task to the scheduler.</summary>
|
||||
/// <param name="task">The task to be queued.</param>
|
||||
protected sealed override void QueueTask(Task task)
|
||||
{
|
||||
// Add the task to the list of tasks to be processed. If there aren't enough
|
||||
// delegates currently queued or running to process tasks, schedule another.
|
||||
lock (tasks)
|
||||
{
|
||||
tasks.AddLast(task);
|
||||
if (delegatesQueuedOrRunning < maxDegreeOfParallelism)
|
||||
{
|
||||
++delegatesQueuedOrRunning;
|
||||
NotifyThreadPoolOfPendingWork();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Attempts to remove a previously scheduled task from the scheduler.</summary>
|
||||
/// <param name="task">The task to be removed.</param>
|
||||
/// <returns>Whether the task could be found and removed.</returns>
|
||||
protected sealed override bool TryDequeue(Task task)
|
||||
{
|
||||
lock (tasks)
|
||||
{
|
||||
return tasks.Remove(task);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Attempts to execute the specified task on the current thread.</summary>
|
||||
/// <param name="task">The task to be executed.</param>
|
||||
/// <param name="taskWasPreviouslyQueued"></param>
|
||||
/// <returns>Whether the task could be executed on the current thread.</returns>
|
||||
protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
|
||||
{
|
||||
// If this thread isn't already processing a task, we don't support inlining
|
||||
if (!currentThreadIsProcessingItems) return false;
|
||||
|
||||
// If the task was previously queued, remove it from the queue
|
||||
if (taskWasPreviouslyQueued) TryDequeue(task);
|
||||
|
||||
// Try to run the task.
|
||||
return TryExecuteTask(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Informs the ThreadPool that there's work to be executed for this scheduler.
|
||||
/// </summary>
|
||||
private void NotifyThreadPoolOfPendingWork()
|
||||
{
|
||||
ThreadPool.UnsafeQueueUserWorkItem(_ =>
|
||||
{
|
||||
// Note that the current thread is now processing work items.
|
||||
// This is necessary to enable inlining of tasks into this thread.
|
||||
currentThreadIsProcessingItems = true;
|
||||
try
|
||||
{
|
||||
// Process all available items in the queue.
|
||||
while (true)
|
||||
{
|
||||
Task item;
|
||||
lock (tasks)
|
||||
{
|
||||
// When there are no more items to be processed,
|
||||
// note that we're done processing, and get out.
|
||||
if (tasks.Count == 0)
|
||||
{
|
||||
--delegatesQueuedOrRunning;
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the next item from the queue
|
||||
item = tasks.First.Value;
|
||||
tasks.RemoveFirst();
|
||||
}
|
||||
|
||||
// Execute the task we pulled out of the queue
|
||||
TryExecuteTask(item);
|
||||
}
|
||||
}
|
||||
// We're done processing items on the current thread
|
||||
finally
|
||||
{
|
||||
currentThreadIsProcessingItems = false;
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user