I have a producer/consumer queue as following but I am getting ArgumentWException.
Following is the code:
public class ProducerConsumer<T> where T : class
{
#region Private Variables
private Thread _workerThread;
private readonly Queue<T> _workQueue;
private object _enqueueItemLocker = new object();
private object _processRecordLocker = new object();
private readonly Action<T> _workCallbackAction;
private AutoResetEvent _workerWaitSignal;
#endregion
#region Constructor
public ProducerConsumer(Action<T> action)
{
_workQueue = new Queue<T>();
_workCallbackAction = action;
}
#endregion
#region Private Methods
private void ProcessRecord()
{
while (true)
{
T workItemToBeProcessed = default(T);
bool hasSomeWorkItem = false;
lock (_processRecordLocker)
{
hasSomeWorkItem = _workQueue.Count > 0;
if (hasSomeWorkItem)
{
workItemToBeProcessed = _workQueue.Dequeue();
if (workItemToBeProcessed == null)
{
return;
}
}
}
if (hasSomeWorkItem)
{
if (_workCallbackAction != null)
{
_workCallbackAction(workItemToBeProcessed);
}
}
else
{
_workerWaitSignal.WaitOne();
}
}
}
#endregion
#region Public Methods
/// <summary>
/// Enqueues work item in the queue.
/// </summary>
/// <param name="workItem">The work item.</param>
public void EnQueueWorkItem(T workItem)
{
lock (_enqueueItemLocker)
{
_workQueue.Enqueue(workItem);
if (_workerWaitSignal == null)
{
_workerWaitSignal = new AutoResetEvent(false);
}
_workerWaitSignal.Set();
}
}
/// <summary>
/// Stops the processer, releases wait handles.
/// </summary>
/// <param name="stopSignal">The stop signal.</param>
public void StopProcesser(AutoResetEvent stopSignal)
{
EnQueueWorkItem(null);
_workerThread.Join();
_workerWaitSignal.Close();
_workerWaitSignal = null;
if (stopSignal != null)
{
stopSignal.Set();
}
}
/// <summary>
/// Starts the processer, starts a new worker thread.
/// </summary>
public void StartProcesser()
{
if (_workerWaitSignal == null)
{
_workerWaitSignal = new AutoResetEvent(false);
}
_workerThread = new Thread(ProcessRecord) { IsBackground = true };
_workerThread.Start();
}
#endregion
}
Another class is:
public class Tester
{
private readonly ProducerConsumer<byte[]> _proConsumer;
public Tester()
{
_proConsumer = new ProducerConsumer<byte[]>(Display);
}
public void AddData(byte[] data)
{
try
{
_proConsumer.EnQueueWorkItem(recordData);
}
catch (NullReferenceException nre)
{
}
}
public void Start()
{
_proConsumer.StartProcesser();
}
private static object _recordLocker = new object();
private void Display(byte[] recordByteStream)
{
try
{
lock (_recordLocker)
{
Console.WriteLine("Done with data:" + BitConverter.ToInt32(recordByteStream, 0));
}
}
catch (Exception ex)
{
}
}
}
And my main function:
class Program
{
private static Tester _recorder;
static void Main(string[] args)
{
_recorder = new Tester();
_recorder.StartRecording();
for (int i = 0; i < 100000; i++)
{
_recorder.AddRecordData(BitConverter.GetBytes(i));
}
Console.Read();
}
}
Any idea why do I get the exception and what should I do to avoid that ?
Your class, in its current implementation, is not thread-safe. You're using two different objects for your Enqueue (lock (_enqueueItemLocker)) and Dequeue (lock (_processRecordLocker)) calls, which creates a race condition in your Queue<T>.
You need to lock the same object instance on both calls in order to safely use the queue.
If you're using .NET 4, I'd recommend either using ConcurrentQueue<T> or BlockingCollection<T> instead, as these would eliminate the need for the locks in your code, since they're thread-safe.
Related
I'm not finding the bug, but this thread is stopping after some time of execution, it returns a message "Thread was being aborted."
Sometimes it takes a hour, in another takes four, I can't find a patern, in some cases it never stops, but I can't find anything relate to it
using System.Threading;
namespace NewMVC.infraestructure
{
public class Thread_iFood
{
private static bool inicializado = false;
static Thread_iFood instance = null;
static readonly object padlock = new object();
private const int timer = 30000;
static Timer thProcessaRet = new Timer(PollingIfood, null, Timeout.Infinite, Timeout.Infinite);
private Thread_iFood()
{
}
public static void Reiniciar()
{
inicializado = false;
instance = null;
}
public static Thread_iFood GetInstance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Thread_iFood();
}
}
}
return instance;
}
}
internal static async void PollingIfood(Object obj)
{
try
{
thProcessaRet.Change(Timeout.Infinite, Timeout.Infinite);
MetodosAuxiliares.BuscaPedidosIFood();
}
catch (Exception e)
{
LogErros.GravaLog(e, "Thread_iFood/PollingIfood");
//here is where is stopping (Thread was being aborted.)
}
finally
{
thProcessaRet.Change(timer, Timeout.Infinite);
}
}
public void Inicializa()
{
if (!inicializado)
{
inicializado = true;
thProcessaRet.Change(timer, timer);
}
}
}
}
I have SinglaR hub class "MyHub" and a .net client class that connects to it successfully in runtime. however when i'm testing the client with nunit test it doesn't connect, I've debugged the test and find that hub instance doesn't created by SinglaR's hub activator class.
I'm using Resharper nunit runner for running test.
Here is my hub class structure, i've remove method's body for simplicity.
[HubName("myHub")]
public class MyHub : Hub
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(MyHub));
#region MyRegion
#region Connection Methods
public MyHub()
{
}
private static int c;
public override Task OnConnected()
{
}
private string UserNameConnectionId
{
}
public override Task OnReconnected()
{
lock (LockObject)
{
}
}
public override Task OnDisconnected(bool b)
{
lock (LockObject)
{
}
}
private void LogUserActivity(string userName, string activity)
{
}
#endregion
//Live controls invoke this Method to send their required data and also their ConnectionId
/// <summary>
/// </summary>
/// <param name="liveControlsInfo"></param>
public Task UpdateLiveControlsInfo(LiveControlInfo[] liveControlsInfo)
{
lock (LockObject)
{
}
}
public string GetValue()
{
return "SignalR Rox!";
}
public void Acknowledge()
{
lock (LockObject)
{
string connectionId = Context.ConnectionId;
}
}
}
#endregion
}
}
here is HubWinClient class
public class HubWinClient
{
private readonly string _userName;
public int ServerPort { get; set; }
private static readonly ILog logger = LogManager.GetLogger(typeof (HubWinClient));
protected int HubLogCounter = 0;
private HubConnection connection;
private bool moduleStarted;
private IHubProxy myHub;
public HubWinClient(int serverPort, string userName= "Windows Hub Client", bool tryConnect=false)
{
_userName = userName;
ServerPort = serverPort;
}
public void Start()
{
ConnectToHub();
moduleStarted = true;
}
public void Stop()
{
DisconnectFromHub();
moduleStarted = false;
}
public void ConnectToHub()
{
ConnectToHub(false);
}
public event EventHandler<EventArgs> ConnectingToHub = delegate { };
public event EventHandler<EventArgs> ConnectedToHub = delegate { };
/// <summary>
/// callback event called by hubhost
/// </summary>
public event EventHandler<int> LogSentToHub = delegate { };
public event EventHandler<EventArgs> DisonnectedFromHub = delegate { };
public event EventHandler<EventArgs> HubConnectionError = delegate { };
public event EventHandler<EventArgs> HubInvokeError = delegate { };
public event EventHandler<EventArgs> HubInvokeSuccess = delegate { };
public event EventHandler<EventArgs> NotConnectedToSentLog = delegate { };
public event EventHandler<LogMessage> DataRecivedFromHub = delegate { };
/// <summary>
/// connects HunNotifier to Hun in order to ListenerMessage passing can be done
/// </summary>
/// <param name="reconnect"></param>
private void ConnectToHub(bool reconnect)
{
//This url will not point to a specific connection. But will instead point to the root of your site=root of hub server.
string hubAddress = "http://localhost:" + ServerPort;
connection = new HubConnection(hubAddress, new Dictionary<string, string>{{"UserName", _userName } });
ConnectingToHub(this, new EventArgs());
if (reconnect)
logger.InfoFormat("HubWinClient reconnecting to HubHost # {0}...", hubAddress);
else
logger.InfoFormat("HubWinClient connecting to HubHost # {0}...", hubAddress);
myHub = connection.CreateHubProxy("myHub");
myHub.On("joined", joined);
myHub.On("rejoined", rejoined);
myHub.On("leaved", leaved);
myHub.On<LogMessage>("addData", addData);
myHub.On<int>("LogSentToWeb", LogSentToWeb);
connection.StateChanged += change =>
{
var a = change;
var b = 10;
var a1 = connection.LastError;
var b2 = connection.Proxy;
};
try
{
connection.Start();
}
catch (AggregateException aggregateException)
{
Exception exception = aggregateException.InnerException;
logger.Error("HubWinClient connection to HubHost exception...", exception);
HubConnectionError(this, new MyEventArgs {Exception = exception});
}
catch (Exception exception)
{
logger.Error("HubWinClient connection to HubHost exception...", exception);
HubConnectionError(this, new MyEventArgs {Exception = exception});
}
}
public void DisconnectFromHub()
{
connection.Stop();
}
/// <summary>
/// when hub notifier connected to hub, hub invoke this method to inform connection success.
/// </summary>
public void joined()
{
var liveControlInfo = new LiveControlInfo
{
LiveControlType = LiveControlType.mainClient
};
// myHub.Invoke("AddLiveControl", liveControlInfo).Wait();
// logger.Info("HubWinClient joined");
ConnectedToHub(this, new EventArgs());
}
/// <summary>
/// when hub notifier reconnected to hub, hub invoke this method to inform reconnection success.
/// </summary>
public void rejoined()
{
logger.Info("HubWinClient rejoined");
}
/// <summary>
/// when hub notifier disconnect from hub, hub invoke this method to inform disconnection.
/// </summary>
public void leaved()
{
logger.Info("HubWinClient disconnected");
DisonnectedFromHub(this, new EventArgs());
}
public void addData(LogMessage logMessage)
{
DataRecivedFromHub(this, logMessage);
}
public event EventHandler<LogMessage> OnSendingLogToHub = delegate { };
/// <summary>
/// called by HubClientMoudle when it's ApplyLogReceived are invoked by WCF service
/// </summary>
/// <param name="logMessage"></param>
public void SendToHub(LogMessage logMessage)
{
OnSendingLogToHub(this, logMessage);
if (!moduleStarted) return;
if (connection.State != ConnectionState.Connected)
ConnectToHub(true);
if (connection.State == ConnectionState.Connected)
{
if (System.Diagnostics.Debugger.IsAttached ||
Math.Abs((logMessage.Log.ArrivalDateTime - DateTime.Now).Ticks) < TimeSpan.FromMinutes(10).Ticks)
{
try
{
Task task = myHub.Invoke("send", logMessage);
task.Wait(5000);
logger.Debug("Send log success");
HubInvokeSuccess(this, new EventArgs());
}
catch (AggregateException aggregateException)
{
Exception exception = aggregateException.InnerException;
logger.Error("Send log Exceotion!", exception);
HubInvokeError(this, new MyEventArgs {Exception = exception});
}
catch (Exception exception)
{
logger.Error("Send log Exceotion!", exception);
HubInvokeError(this, new MyEventArgs {Exception = exception});
}
}
else
{
logger.Warn("Log arrival date time is not less than DateTime.Now+30 minutues");
}
}
else
{
logger.Warn("HubWinClient not connected to send log");
NotConnectedToSentLog(this, new EventArgs());
}
}
/// <summary>
/// when Hub receives log from HubWinClient, calls this method to inform send success
/// </summary>
/// <param name="log"></param>
public void LogSentToWeb(int logId)
{
logger.Debug("Call back from Hub: send success");
LogSentToHub(this, logId);
}
}
Here is my test:
[Category("Integration")]
public class HubWinClientTests
{
[Test]
public void TestConnection()
{
//arrange
const int port = 4000;
HubHost hubHost = new HubHost(port);
HubWinClient hubWinClient = new HubWinClient(port,"mvc app client");
bool clientConnected = false;
bool clientDisconnected = false;
bool hubInvokeError = false;
hubWinClient.ConnectedToHub += (sender, args) => { clientConnected = true; };
hubWinClient.DisonnectedFromHub += (sender, args) => { clientDisconnected = true; };
hubWinClient.HubInvokeError += (sender, args) => { hubInvokeError = true; };
//act
hubHost.Start();
hubWinClient.Start();
//assert
var constrain = Is.True.After(10000, 100);
Assert.That(() => clientConnected, constrain);
//act
hubWinClient.SendToHub(new LogMessage());
//assert
var constrain2 = Is.True.After(2000, 100);
Assert.That(() => hubInvokeError == false, constrain2);
// act
hubWinClient.DisconnectFromHub();
// assert
var constrain1 = Is.True.After(2000, 100);
Assert.That(() => clientDisconnected, constrain1);
hubHost.Stop();
}
}
For some reason when i run my the test by resharper nunit runner, hub instance does created and my test fails. i think it's related to Resharper's and how it create test environment (i'e process creation and such stuff).
Any ideas.
Thanks.
I have a C# project working with input audio Stream from Kinect 1, Kinect 2, Microphone or anything else.
waveIn.DataAvailable += (object sender, WaveInEventArgs e) => {
lock(buffer){
var pos = buffer.Position;
buffer.Write(e.Buffer, 0, e.BytesRecorded);
buffer.Position = pos;
}
};
The buffer variable is a Stream from component A that will be processed by a SpeechRecognition component B working on Streams.
I will add new components C, D, E, working on Streams to compute pitch, detect sound, do finger printing, or anything else ...
How can I duplicate that Stream for components C, D, E ?
Component A send an Event "I have a Stream do what you want" I don't want to reverse the logic by an Event "Give me your streams"
I'm looking for a "MultiStream" that could give me a Stream instance and will handle the job
Component A
var MultiStream buffer = new MultiStream()
...
SendMyEventWith(buffer)
Component B, C, D, E
public void HandleMyEvent(MultiStream buffer){
var stream = buffer.GetNewStream();
var engine = new EngineComponentB()
engine.SetStream(stream);
}
The MultiStream must be a Stream to wrap Write() method (because Stream do not have data available mechanics) ?
If a Stream is Dispose() by Component B the MultiStream should remove it from it's array ?
The MultiStream must throw an exception on Read() to require use of GetNewStream()
EDIT: Kinect 1 provide a Stream itself ... :-( should I use a Thread to pumpit into the MultiStream ?
Did anybody have that kind of MultiStream Class ?
Thanks
I'm not sure if this is the best way to do it or that it's better than the previous answer, and I'm not guaranteeing that this code is perfect, but I coded something that is literally what you asked for because it was fun - a MultiStream class.
You can find the code for the class here: http://pastie.org/10289142
Usage Example:
MultiStream ms = new MultiStream();
Stream copy1 = ms.CloneStream();
ms.Read( ... );
Stream copy2 = ms.CloneStream();
ms.Read( ... );
copy1 and copy2 will contain identical data after the example is ran, and they will continue to get updated as the MultiStream is written to. You can read, update position, and dispose of the cloned streams individually. If disposed the cloned streams will get removed from MultiStream, and disposing of Multistream will close all related and cloned streams (you can change this if it's not the behavior you want). Trying to write to the cloned streams will throw a not supported exception.
Somehow I don't think streams really fit what you're trying to do. You're setting up a situation where a long run of the program is going to continually expand the data requirements for no apparent reason.
I'd suggest a pub/sub model that publishes the received audio data to subscribers, preferably using a multi-threaded approach to minimize the impact of a bad subscriber. Some ideas can be found here.
I've done this before with a processor class that implements IObserver<byte[]> and uses a Queue<byte[]> to store the sample blocks until the process thread is ready for them. Here's are the base classes:
public abstract class BufferedObserver<T> : IObserver<T>, IDisposable
{
private object _lck = new object();
private IDisposable _subscription = null;
public bool Subscribed { get { return _subscription != null; } }
private bool _completed = false;
public bool Completed { get { return _completed; } }
protected readonly Queue<T> _queue = new Queue<T>();
protected bool DataAvailable { get { lock(_lck) { return _queue.Any(); } } }
protected int AvailableCount { get { lock (_lck) { return _queue.Count; } } }
protected BufferedObserver()
{
}
protected BufferedObserver(IObservable<T> observable)
{
SubscribeTo(observable);
}
public virtual void Dispose()
{
if (_subscription != null)
{
_subscription.Dispose();
_subscription = null;
}
}
public void SubscribeTo(IObservable<T> observable)
{
if (_subscription != null)
_subscription.Dispose();
_subscription = observable.Subscribe(this);
_completed = false;
}
public virtual void OnCompleted()
{
_completed = true;
}
public virtual void OnError(Exception error)
{ }
public virtual void OnNext(T value)
{
lock (_lck)
_queue.Enqueue(value);
}
protected bool GetNext(ref T buffer)
{
lock (_lck)
{
if (!_queue.Any())
return false;
buffer = _queue.Dequeue();
return true;
}
}
protected T NextOrDefault()
{
T buffer = default(T);
GetNext(ref buffer);
return buffer;
}
}
public abstract class Processor<T> : BufferedObserver<T>
{
private object _lck = new object();
private Thread _thread = null;
private object _cancel_lck = new object();
private bool _cancel_requested = false;
private bool CancelRequested
{
get { lock(_cancel_lck) return _cancel_requested; }
set { lock(_cancel_lck) _cancel_requested = value; }
}
public bool Running { get { return _thread == null ? false : _thread.IsAlive; } }
public bool Finished { get { return _thread == null ? false : !_thread.IsAlive; } }
protected Processor(IObservable<T> observable)
: base(observable)
{ }
public override void Dispose()
{
if (_thread != null && _thread.IsAlive)
{
//CancelRequested = true;
_thread.Join(5000);
}
base.Dispose();
}
public bool Start()
{
if (_thread != null)
return false;
_thread = new Thread(threadfunc);
_thread.Start();
return true;
}
private void threadfunc()
{
while (!CancelRequested && (!Completed || _queue.Any()))
{
if (DataAvailable)
{
T data = NextOrDefault();
if (data != null && !data.Equals(default(T)))
ProcessData(data);
}
else
Thread.Sleep(10);
}
}
// implement this in a sub-class to process the blocks
protected abstract void ProcessData(T data);
}
This way you're only keeping the data as long as you need it, and you can attach as many process threads as you need to the same observable data source.
And for the sake of completeness, here's a generic class that implements IObservable<T> so you can see how it all fits together. This one even has comments:
/// <summary>Generic IObservable implementation</summary>
/// <typeparam name="T">Type of messages being observed</typeparam>
public class Observable<T> : IObservable<T>
{
/// <summary>Subscription class to manage unsubscription of observers.</summary>
private class Subscription : IDisposable
{
/// <summary>Observer list that this subscription relates to</summary>
public readonly ConcurrentBag<IObserver<T>> _observers;
/// <summary>Observer to manage</summary>
public readonly IObserver<T> _observer;
/// <summary>Initialize subscription</summary>
/// <param name="observers">List of subscribed observers to unsubscribe from</param>
/// <param name="observer">Observer to manage</param>
public Subscription(ConcurrentBag<IObserver<T>> observers, IObserver<T> observer)
{
_observers = observers;
_observer = observer;
}
/// <summary>On disposal remove the subscriber from the subscription list</summary>
public void Dispose()
{
IObserver<T> observer;
if (_observers != null && _observers.Contains(_observer))
_observers.TryTake(out observer);
}
}
// list of subscribed observers
private readonly ConcurrentBag<IObserver<T>> _observers = new ConcurrentBag<IObserver<T>>();
/// <summary>Subscribe an observer to this observable</summary>
/// <param name="observer">Observer instance to subscribe</param>
/// <returns>A subscription object that unsubscribes on destruction</returns>
/// <remarks>Always returns a subscription. Ensure that previous subscriptions are disposed
/// before re-subscribing.</remarks>
public IDisposable Subscribe(IObserver<T> observer)
{
// only add observer if it doesn't already exist:
if (!_observers.Contains(observer))
_observers.Add(observer);
// ...but always return a new subscription.
return new Subscription(_observers, observer);
}
// delegate type for threaded invocation of IObserver.OnNext method
private delegate void delNext(T value);
/// <summary>Send <paramref name="data"/> to the OnNext methods of each subscriber</summary>
/// <param name="data">Data object to send to subscribers</param>
/// <remarks>Uses delegate.BeginInvoke to send out notifications asynchronously.</remarks>
public void Notify(T data)
{
foreach (var observer in _observers)
{
delNext handler = observer.OnNext;
handler.BeginInvoke(data, null, null);
}
}
// delegate type for asynchronous invocation of IObserver.OnComplete method
private delegate void delComplete();
/// <summary>Notify all subscribers that the observable has completed</summary>
/// <remarks>Uses delegate.BeginInvoke to send out notifications asynchronously.</remarks>
public void NotifyComplete()
{
foreach (var observer in _observers)
{
delComplete handler = observer.OnCompleted;
handler.BeginInvoke(null, null);
}
}
}
Now you can create an Observable<byte[]> to use as your transmitter for Process<byte[]> instances that are interested. Pull data blocks out of the input stream, audio reader, etc. and pass them to the Notify method. Just make sure that you clone the arrays beforehand...
I have often run into situations where I need some sort of valve construct to control the flow of a reactive pipeline. Typically, in a network-based application I have had the requirement to open/close a request stream according to the connection state.
This valve subject should support opening/closing the stream, and output delivery in FIFO order. Input values should be buffered when the valve is closed.
A ConcurrentQueue or BlockingCollection are typically used in such scenarios, but that immediately introduces threading into the picture. I was looking for a purely reactive solution to this problem.
Here's an implementation mainly based on Buffer() and BehaviorSubject. The behavior subject tracks the open/close state of the valve. Openings of the valve start buffering windows, and closings of the valve close those windows. Output of the buffer operator is "re-injected" onto the input (so that even observers themselves can close the valve):
/// <summary>
/// Subject offering Open() and Close() methods, with built-in buffering.
/// Note that closing the valve in the observer is supported.
/// </summary>
/// <remarks>As is the case with other Rx subjects, this class is not thread-safe, in that
/// order of elements in the output is indeterministic in the case of concurrent operation
/// of Open()/Close()/OnNext()/OnError(). To guarantee strict order of delivery even in the
/// case of concurrent access, <see cref="ValveSubjectExtensions.Synchronize{T}(NEXThink.Finder.Utils.Rx.IValveSubject{T})"/> can be used.</remarks>
/// <typeparam name="T">Elements type</typeparam>
public class ValveSubject<T> : IValveSubject<T>
{
private enum Valve
{
Open,
Closed
}
private readonly Subject<T> input = new Subject<T>();
private readonly BehaviorSubject<Valve> valveSubject = new BehaviorSubject<Valve>(Valve.Open);
private readonly Subject<T> output = new Subject<T>();
public ValveSubject()
{
var valveOperations = valveSubject.DistinctUntilChanged();
input.Buffer(
bufferOpenings: valveOperations.Where(v => v == Valve.Closed),
bufferClosingSelector: _ => valveOperations.Where(v => v == Valve.Open))
.SelectMany(t => t).Subscribe(input);
input.Where(t => valveSubject.Value == Valve.Open).Subscribe(output);
}
public bool IsOpen
{
get { return valveSubject.Value == Valve.Open; }
}
public bool IsClosed
{
get { return valveSubject.Value == Valve.Closed; }
}
public void OnNext(T value)
{
input.OnNext(value);
}
public void OnError(Exception error)
{
input.OnError(error);
}
public void OnCompleted()
{
output.OnCompleted();
input.OnCompleted();
valveSubject.OnCompleted();
}
public IDisposable Subscribe(IObserver<T> observer)
{
return output.Subscribe(observer);
}
public void Open()
{
valveSubject.OnNext(Valve.Open);
}
public void Close()
{
valveSubject.OnNext(Valve.Closed);
}
}
public interface IValveSubject<T>:ISubject<T>
{
void Open();
void Close();
}
An additional method for flushing out the valve can be useful at times, e.g. to eliminate remaining requests when they are no longer relevant. Here is an implementation that builds upon the precedent, adapter-style:
/// <summary>
/// Subject with same semantics as <see cref="ValveSubject{T}"/>, but adding flushing out capability
/// which allows clearing the valve of any remaining elements before closing.
/// </summary>
/// <typeparam name="T">Elements type</typeparam>
public class FlushableValveSubject<T> : IFlushableValveSubject<T>
{
private readonly BehaviorSubject<ValveSubject<T>> valvesSubject = new BehaviorSubject<ValveSubject<T>>(new ValveSubject<T>());
private ValveSubject<T> CurrentValve
{
get { return valvesSubject.Value; }
}
public bool IsOpen
{
get { return CurrentValve.IsOpen; }
}
public bool IsClosed
{
get { return CurrentValve.IsClosed; }
}
public void OnNext(T value)
{
CurrentValve.OnNext(value);
}
public void OnError(Exception error)
{
CurrentValve.OnError(error);
}
public void OnCompleted()
{
CurrentValve.OnCompleted();
valvesSubject.OnCompleted();
}
public IDisposable Subscribe(IObserver<T> observer)
{
return valvesSubject.Switch().Subscribe(observer);
}
public void Open()
{
CurrentValve.Open();
}
public void Close()
{
CurrentValve.Close();
}
/// <summary>
/// Discards remaining elements in the valve and reset the valve into a closed state
/// </summary>
/// <returns>Replayable observable with any remaining elements</returns>
public IObservable<T> FlushAndClose()
{
var previousValve = CurrentValve;
valvesSubject.OnNext(CreateClosedValve());
var remainingElements = new ReplaySubject<T>();
previousValve.Subscribe(remainingElements);
previousValve.Open();
return remainingElements;
}
private static ValveSubject<T> CreateClosedValve()
{
var valve = new ValveSubject<T>();
valve.Close();
return valve;
}
}
public interface IFlushableValveSubject<T> : IValveSubject<T>
{
IObservable<T> FlushAndClose();
}
As mentioned in the comment, these subjects are not "thread-safe" in the sense that order of delivery is no longer guaranteed in the case of concurrent operation. In a similar fashion as what exists for the standard Rx Subject, Subject.Synchronize() (https://msdn.microsoft.com/en-us/library/hh211643%28v=vs.103%29.aspx) we can introduce some extensions which provide locking around the valve:
public static class ValveSubjectExtensions
{
public static IValveSubject<T> Synchronize<T>(this IValveSubject<T> valve)
{
return Synchronize(valve, new object());
}
public static IValveSubject<T> Synchronize<T>(this IValveSubject<T> valve, object gate)
{
return new SynchronizedValveAdapter<T>(valve, gate);
}
public static IFlushableValveSubject<T> Synchronize<T>(this IFlushableValveSubject<T> valve)
{
return Synchronize(valve, new object());
}
public static IFlushableValveSubject<T> Synchronize<T>(this IFlushableValveSubject<T> valve, object gate)
{
return new SynchronizedFlushableValveAdapter<T>(valve, gate);
}
}
internal class SynchronizedValveAdapter<T> : IValveSubject<T>
{
private readonly object gate;
private readonly IValveSubject<T> valve;
public SynchronizedValveAdapter(IValveSubject<T> valve, object gate)
{
this.valve = valve;
this.gate = gate;
}
public void OnNext(T value)
{
lock (gate)
{
valve.OnNext(value);
}
}
public void OnError(Exception error)
{
lock (gate)
{
valve.OnError(error);
}
}
public void OnCompleted()
{
lock (gate)
{
valve.OnCompleted();
}
}
public IDisposable Subscribe(IObserver<T> observer)
{
return valve.Subscribe(observer);
}
public void Open()
{
lock (gate)
{
valve.Open();
}
}
public void Close()
{
lock (gate)
{
valve.Close();
}
}
}
internal class SynchronizedFlushableValveAdapter<T> : SynchronizedValveAdapter<T>, IFlushableValveSubject<T>
{
private readonly object gate;
private readonly IFlushableValveSubject<T> valve;
public SynchronizedFlushableValveAdapter(IFlushableValveSubject<T> valve, object gate)
: base(valve, gate)
{
this.valve = valve;
this.gate = gate;
}
public IObservable<T> FlushAndClose()
{
lock (gate)
{
return valve.FlushAndClose();
}
}
}
Here is my implementation with delay operator:
source.delay(new Func1<Integer, Observable<Boolean>>() {
#Override
public Observable<Boolean> call(Integer integer) {
return valve.filter(new Func1<Boolean, Boolean>() {
#Override
public Boolean call(Boolean aBoolean) {
return aBoolean;
}
});
}
})
.toBlocking()
.subscribe(new Action1<Integer>() {
#Override
public void call(Integer integer) {
System.out.println("out: " + integer);
}
});
The idea is to delay all source emissions until "valve opens". If valve is already opened, there will be no delay in emission of item.
Rx valve gist
The System.Threading.EventWaitHandle type does not contain a definition for 'dispose'.
How am I supposed to dispose of the EventWaitHandle when I want to kill the object in which it is contained? I've been having a serious memory leak problem and I've tried setting the EventWaitHandle to null and the memory leak persists.
Here's my code (I'm using Bob Craven's library, I added only the dispose() method):
using System;
using System.Threading;
namespace Rlc.Cron
{
public class CronObject
{
public delegate void CronEvent(CronObject cronObject);
public event CronEvent OnCronTrigger;
public event CronEvent OnStarted;
public event CronEvent OnStopped;
public event CronEvent OnThreadAbort;
private CronObjectDataContext _cronObjectDataContext;
private Guid _id = Guid.NewGuid();
private object _startStopLock = new object();
private EventWaitHandle _wh = new AutoResetEvent(false);
private Thread _thread;
public bool _isStarted;
private bool _isStopRequested;
private DateTime _nextCronTrigger;
public Guid Id { get { return _id; } }
public object Object { get { return _cronObjectDataContext.Object; } }
public DateTime LastTigger { get { return _cronObjectDataContext.LastTrigger; } }
/// <summary>
/// Initializes a new instance of the <see cref="CronObject"/> class.
/// </summary>
/// <param name="cronObjectDataContext">The cron object data context.</param>
public CronObject(CronObjectDataContext cronObjectDataContext)
{
if (cronObjectDataContext == null)
{
throw new ArgumentNullException("cronObjectDataContext");
}
if (cronObjectDataContext.Object == null)
{
throw new ArgumentException("cronObjectDataContext.Object");
}
if (cronObjectDataContext.CronSchedules == null || cronObjectDataContext.CronSchedules.Count == 0)
{
throw new ArgumentException("cronObjectDataContext.CronSchedules");
}
_cronObjectDataContext = cronObjectDataContext;
}
/// <summary>
/// Starts this instance.
/// </summary>
/// <returns></returns>
public bool Start()
{
lock (_startStopLock)
{
// Can't start if already started.
//
if (_isStarted)
{
return false;
}
_isStarted = true;
_isStopRequested = false;
// This is a long running process. Need to run on a thread
// outside the thread pool.
//
_thread = new Thread(ThreadRoutine);
_thread.Start();
}
// Raise the started event.
//
if(OnStarted != null)
{
OnStarted(this);
}
return true;
}
/// <summary>
/// Stops this instance.
/// </summary>
/// <returns></returns>
public bool Stop()
{
lock (_startStopLock)
{
// Can't stop if not started.
//
if (!_isStarted)
{
return false;
}
_isStarted = false;
_isStopRequested = true;
// Signal the thread to wake up early
//
_wh.Set();
// Wait for the thread to join.
//
if(!_thread.Join(5000))
{
_thread.Abort();
// Raise the thread abort event.
//
if(OnThreadAbort != null)
{
OnThreadAbort(this);
}
}
}
// Raise the stopped event.
//
if(OnStopped != null)
{
OnStopped(this);
}
return true;
}
public void dispose(){
this.Stop ();
this.OnCronTrigger = null;
this.OnStarted=null;
this.OnStopped=null;
this.OnThreadAbort=null;
this._cronObjectDataContext=null;
this._startStopLock = null;
this._wh = null;
this._thread=null;
}
/// <summary>
/// Cron object thread routine.
/// </summary>
private void ThreadRoutine()
{
// Continue until stop is requested.
//
while(!_isStopRequested)
{
// Determine the next cron trigger
//
DetermineNextCronTrigger(out _nextCronTrigger);
TimeSpan sleepSpan = _nextCronTrigger - DateTime.Now;
if(sleepSpan.TotalMilliseconds < 0)
{
// Next trigger is in the past. Trigger the right away.
//
sleepSpan = new TimeSpan(0, 0, 0, 0, 50);
}
// Wait here for the timespan or until I am triggered
// to wake up.
//
if(!_wh.WaitOne(sleepSpan))
{
// Timespan is up...raise the trigger event
//
if(OnCronTrigger != null)
{
OnCronTrigger(this);
}
// Update the last trigger time.
//
_cronObjectDataContext.LastTrigger = DateTime.Now;
}
}
}
/// <summary>
/// Determines the next cron trigger.
/// </summary>
/// <param name="nextTrigger">The next trigger.</param>
private void DetermineNextCronTrigger(out DateTime nextTrigger)
{
nextTrigger = DateTime.MaxValue;
foreach (CronSchedule cronSchedule in _cronObjectDataContext.CronSchedules)
{
DateTime thisTrigger;
if(cronSchedule.GetNext(LastTigger, out thisTrigger))
{
if (thisTrigger < nextTrigger)
{
nextTrigger = thisTrigger;
}
}
}
}
~CronObject(){
Console.WriteLine ("===================CRONOBJECT DESTROYED!!===============");
}
}
}