Timer not stopping when form closed - c#

I am having an extremely bizarre bug that I cant seem to figure out. I will lay this out as best as I can.
Context: I have an application that will continually ping about a group of IP's that live in a different location. (We need to see if each device is up at a given time)
Bug: When pinging a host, and their internet is down, the application shows all devices are down (as it should). But when closing the app, the process (main app exe) is still running and our router traffic shows that the continuous ping is still running.(Same interval). So its like the timer keeps going.
But when the application is closed, and the group is up, the process closes as it should and all pings stop.
I can provide any code that you guys want to see. Ill give the basics first:
I have a class called EquipmentObj. Inside the constructor, a contiguous ping gets started (async)
*Note this is all done on a seperate form, not the mainform.
Constructor and property:
System.Timers.Timer TimerObj { get; set; }
public EquipmentObj(string ipAddress)
{
this.IP_Address = ipAddress;
this.TimerObj = new System.Timers.Timer();
this.TimerObj.Interval = 2000;
this.TimerObj.Elapsed += TimerObj_Elapsed;
this.TimerObj.AutoReset = true;
this.start();
}
Start and Elapsed Method:
private void start()
{
this._requestStop = false;
this.TimerObj.Start();
}
private void TimerObj_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if(!_requestStop)
base.start_Ping(this.Ip_Address);
}
Base Class:
public void start_Ping(string ipAddress)
{
try
{
Ping pingSender = new Ping();
// Create the event Handler
pingSender.PingCompleted += pingSender_PingCompleted;
// Create the buffer
// Reference buff size: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
byte[] packetData = Encoding.ASCII.GetBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
// Create the pingOptions object
PingOptions packetOptions = new PingOptions();
// Parse the passed ip address to a proper ipaddress object
IPAddress ip = System.Net.IPAddress.Parse(ipAddress);
// Send async
pingSender.SendAsync(ip, 1050, packetData, ip);
}
catch (Exception ex)
{
Error_Log.write_log(ex);
}
}
public virtual void pingSender_PingCompleted(object sender, PingCompletedEventArgs e)
{
throw new NotImplementedException();
}
Implemented Ping Complete:
public override void pingSender_PingCompleted(object sender, PingCompletedEventArgs e)
{
if (e.Reply.Status == IPStatus.Success)
this.status = "On Line";
else
{
this.status = "Off Line";
this.setBounced = ++this.setBounced ?? 1;
}
this.setResponse = e.Reply.RoundtripTime.ToString();
}
Finally my cleanup which is called on the form closing:
foreach (EquipmentObj obj in this.objectListView1.Objects)
{
obj.Stop();
}
Here is the stop:
public void Stop()
{
this._requestStop = true;
this.TimerObj.Stop();
}
What I've tried:
Adding the cleanup (Above) into the closing event.
Moved my timer from a system.threading timer, to system.timer timer.
I've tried implementing idisposable.
Setting Timers to null on form closing event.
Calling dispose on the timer it self.
None of these have worked.
I hope I didn't over whelm you all with too much code and writing. Any help would be appreciated.

My best bet is you have an exception being thrown prior to the Stop being called. I will put some more try { } catch statements in there so you can handle errors.
You may have some threading issues where you are trying to access objects not created on the same thread.
I would check if you need to Invoke anytime you mess with the timer object.
void Stop()
{
if (objectListView1.InvokeRequired)
{
objectListView1.BeginInvoke(new Action(Stop));
return;
}
foreach (EquipmentObj obj in this.objectListView1.Objects)
{
obj.Stop();
}
}

Related

Start multiple instances of the window service

I have created a windows service that reads an IBM MQ messages and processes them. My Win Service is currently designed OnStart it triggers a timer interval which calls the function that calls the class which does all the work (See code below).
What I am trying to accomplish is to scale (if that is the right word) the application, if there are a lot of messages on the queue to process we would like to run multiple instances/threads of the service. Ideal situation would be to have some type of configuration in the app.config that indicates how many threads or worker processes to have running. Is threading the right approach? Is there a better or preferred way? Right now what we are doing is installing a new instance of the service with a different name and it is getting quiet tedious.
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("Service Started", EventLogEntryType.Information);
_myTimer.Interval = 500; // half a second
_myTimer.Elapsed += OnTimer;
_myTimer.AutoReset = false;
_myTimer.Enabled = true;
}
public void OnTimer(object sender, ElapsedEventArgs args)
{
//If you want the main program to go ahead and shut down after some period of time, regardless of whether it's obtained the lock, use Monitor.TryEnter. For example, this will wait 15 seconds.
//bool gotLock = Monitor.TryEnter(_timerLock, TimeSpan.FromSeconds(15));
if (!Monitor.TryEnter(_timerLock))
{
// something has the lock. Probably shutting down.
return;
}
try
{
MqSyncJob mqSyncJob = new MqSyncJob(eventLog1);
mqSyncJob.ProcessSyncJobQueue();
}
catch (Exception ex)
{
eventLog1.WriteEntry(ex.ToString(), EventLogEntryType.Error);
}
finally
{
_myTimer.Start(); // re-enables the timer
Monitor.Exit(_timerLock);
}
}

Timer issue in C# with threads

I am developing a skype-like application, I have an external DLL that do most of the work and fires events handled in my class ip2ip, one of this events is incoming_call fired when there is an incoming call as the name suggest. I'm trying to manage missed calls.
Now this is the relevant part of the code in this class:
private void ics_IncomingCall(object sender, string authenticationData, int socketHandle, string callbackid, string callbackipaddress, int callbackvideoport, int callbackaudiotcpport, int callbackaudiudpport)
{
if (Calling)
{
ics.RejectCall("The contact have another call", (IntPtr)socketHandle);
Message = "An incoming call from [" + callbackipaddress + "] has rejected.";
}
else
{
AcceptIncomingCall = null;
UserCaller = FindUserName(callbackipaddress);
IncomingCall = true;
//waiting for the call to be accepted from outside of this class
while (AcceptIncomingCall.HasValue == false) Thread.Sleep(100);
if(AcceptIncomingCall.Value == true)
{
//call back to have a 1 on one video conference
icc.Parent.BeginInvoke(new MethodInvoker(delegate
{
//accept the incoming call
ics.AcceptCall("n/a", socketHandle);
icc.Call(callbackipaddress, callbackvideoport, 0, 0,
"n/a", callbackid,
ics.GetLocalIp()[0].ToString(), 0, 0, 0, "");
Calling = true;
}));
}
else
{
ics.RejectCall("Call not accepted", (IntPtr)socketHandle);
Log = "Incoming call not accepted";
Calling = false;
}
AcceptIncomingCall = null;
IncomingCall = false;
}
}
IncomingCall is a property generating a PropertyChangedEvent, wich is captured in my main class where I have this code:
private void ip2ip_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e != null && string.IsNullOrEmpty(e.PropertyName) == false)
{
..............
if (e.PropertyName.Equals("IncomingCall") && ip2ip.IncomingCall == true)
{
Invoke(new MethodInvoker(delegate
{
pnlCalling.Visible = true;
aTimer.Start();
}));
}
................
}
}
public Form1()
{
.......
aTimer = new System.Windows.Forms.Timer();
aTimer.Interval = 10000;
aTimer.Tick += aTimer_Tick;
}
void aTimer_Tick(object sender, EventArgs e)
{
aTimer.Stop();
btnNo.PerformClick();
}
private void btnNo_Click(object sender, EventArgs e)
{
aTimer.Stop();
ip2ip.AcceptIncomingCall = false;
}
private void btnOk_Click(object sender, EventArgs e)
{
aTimer.Stop();
ip2ip.AcceptIncomingCall = true;
}
I need the timer to manage the missed call, when there is an incoming call a panel appears, with buttons to accept/reject the call. If the user waits too much the call is considered rejected (missed).
In this way it doesn't work, probably I'm doing something wrong with the timer, as without any timer everything works. I also tried the timer of the class System.Timers with same results. Any Idea?
EDIT
This is my expectation, there is an incoming call so the event ics_IncomingCall is fired, IncomingCall=true cause the execution to go to the main class (we are still in same thread, I see it debugging step by step in VS) where is invoked in the GUI thread the panel to be visible and started the timer, now we have one thread where a while loop block the execution until in the other thread user do something (accept/reject).
The problem exist when the user accept the call, the code after the while loop is always executed, the caller has no problem at all and receive the stream, but in the receiver (who receive the stream as I verified in wireshark) the DLL (who is responsible to show the incoming video) fails to do its job for some reason unknown to me but caused by the timer.
It is unfortunate your question does not include a good, minimal, complete code example that reliably reproduces the problem. Having such a code example would make it much more practical for someone to provide a useful answer.
That said, as explained by commenter varocarbas, your fundamental problem appears to be that you have blocked the UI thread (with the while loop), while at the same time hoping for the UI thread to handle other activity (such as the timer's tick event). In fact, you are also preventing the button click from having an effect. The button Click event handlers can't execute either, while the UI thread is blocked.
One possible way to fix this would be to use a TaskCompletionSource<T> to provide the ics_IncomingCall() with a waitable object, which the buttons and timer can use to signal. For example:
// Change from "bool?" to this:
private TaskCompletionSource<bool> AcceptIncomingCall;
public void HandleCall(bool accept)
{
AcceptIncomingCall.SetResult(accept);
}
private async Task ics_IncomingCall(object sender, string authenticationData, int socketHandle, string callbackid, string callbackipaddress, int callbackvideoport, int callbackaudiotcpport, int callbackaudiudpport)
{
if (Calling)
{
ics.RejectCall("The contact have another call", (IntPtr)socketHandle);
Message = "An incoming call from [" + callbackipaddress + "] has rejected.";
}
else
{
AcceptIncomingCall = new TaskCompletionSource<bool>();
UserCaller = FindUserName(callbackipaddress);
IncomingCall = true;
//waiting for the call to be accepted from outside of this class
if (await AcceptIncomingCall.Task)
{
//call back to have a 1 on one video conference
icc.Parent.BeginInvoke(new MethodInvoker(delegate
{
//accept the incoming call
ics.AcceptCall("n/a", socketHandle);
icc.Call(callbackipaddress, callbackvideoport, 0, 0,
"n/a", callbackid,
ics.GetLocalIp()[0].ToString(), 0, 0, 0, "");
Calling = true;
}));
}
else
{
ics.RejectCall("Call not accepted", (IntPtr)socketHandle);
Log = "Incoming call not accepted";
Calling = false;
}
AcceptIncomingCall.Dispose();
IncomingCall = false;
}
}
and:
void aTimer_Tick(object sender, EventArgs e)
{
aTimer.Stop();
btnNo.PerformClick();
}
private void btnNo_Click(object sender, EventArgs e)
{
aTimer.Stop();
genericServerClient.HandleCall(false);
}
private void btnOk_Click(object sender, EventArgs e)
{
aTimer.Stop();
genericServerClient.HandleCall(false);
}
This causes the ics_IncomingCall() method to return when it reaches the await statement, allowing its thread to continue executing. The button Click event handlers will call back to the public method that encapsulates your field (public fields are very dangerous and should be avoided in almost all situations), setting the result value for the TaskCompletionSource object that is being awaited.
Once the result value has been set, this will cause the framework to resume executing your ics_IncomingCall() method where it left off, but now with the value returned from the button Click event handlers. I.e. true if the user clicked the btnOk and false if they clicked btnNo or the timer interval elapsed.
Note that this changes the signature of your ics_IncomingCall() method, which will force a change to the caller. The best way to handle that will be to change the caller as well, to be async and to use await ics_IncomingCall(...). That will of course force a change in its caller, and its caller's caller, and so on. But you need to release the UI thread, and this is the best way to do it. Hopefully you don't have a lot of callers to change, but even if you do, this is the way to go.
If the above does not seem to address your problem, please provide a good MCVE. Note that a good MCVE is both complete and minimal. You will want to remove from the example any code that is not strictly required to reproduce the problem. At the same time, make sure someone can copy and paste the code into an empty project and have it run with at most very minimal effort, and preferably none at all.

How To: stagger SignalR Clients.Others.[function] calls in C#

I have a basic function that looks like this:
public void AllDataUpdated()
{
Clients.Others.allDataUpdated();
}
Now, I want to add a half-second delay between each of these calls. But, I don't want to just lock my web-server up in doing so.
My first instinct was to do the following:
async Task SendWithDelay(var other, var timeout)
{
await Task.Delay(timeout);
other.allDataUpdated();
}
and iterate over each other in my public void AllDataUpdated() function and increment the timeout for each iteration. Is this the correct approach? How should I do this in a manner that will not lock-up my webserver with this process, but will stagger the SignalR emits?
Thanks!
EDIT: My desired result is that client_0 gets this message at 0ms, then client_1 gets the message at 500ms, etc. All from the same call to AllDataUpdated().
// synchronization primitive
private readonly object syncRoot = new object();
// the timer for 500 miliseconds delay
private Timer notificator;
// public function used for notification with delay
public void NotifyAllDataUpdatedWithDelay() {
// first, we need claim lock, because of accessing from multiple threads
lock(this.syncRoot) {
if (null == notificator) {
// notification timer is lazy-loaded
notificator = new Timer(500);
notificator.Elapse += notificator_Elapsed;
}
if (false == notificator.Enabled) {
// timer is not enabled (=no notification is in delay)
// enabling = starting the timer
notificator.Enabled = true;
}
}
}
private void notificator_Elapsed(object sender, ElapsedEventArgs e) {
// first, we need claim lock, because of accessing from multiple threads
lock(this.syncRoot) {
// stop the notificator
notificator.Enabled = false;
}
// notify clients
Clients.Others.allDataUpdated();
}

Reset System.Timers.Timer to prevent Elapsed event

I am trying to use the Timer to trigger an event to send data across the network. I created a simple class to debug. Basically I have a List<string> I'd like to send. I want the following to happen:
Add string to List
Start Timer for 10 seconds
Add second string to List before Timer.Elapsed
Restart Timer back at 10 seconds.
So far I have this:
public static List<string> list;
public static Timer timer;
public static bool isWiredUp = false;
public static void Log(string value) {
if (list == null) list = new List<string>();
list.Add(value);
//this does not reset the timer, elapsed still happens 10s after #1
if (timer != null) {
timer = null;
}
timer = new Timer(10000);
timer.Start();
timer.Enabled = true;
timer.AutoReset = false;
if (!isWiredUp) {
timer.Elapsed += new ElapsedEventHandler(SendToServer);
isWiredUp = true;
}
}
static void SendToServer(object sender, ElapsedEventArgs e) {
timer.Enabled = false;
timer.Stop();
}
Any ideas?
You can use the Stop function followed immediately by the Start function to "restart" the timer. Using that you can create the Timer when the class is first created, wire up the Elapsed event at that time, and then do nothing but call those two methods when an item is added. It will either start, or restart, the timer. Note that calling Stop on a timer that hasn't yet been started just does nothing, it doesn't throw an exception or cause any other problems.
public class Foo
{
public static List<string> list;
public static Timer timer;
static Foo()
{
list = new List<string>();
timer = new Timer(10000);
timer.Enabled = true;
timer.AutoReset = false;
timer.Elapsed += SendToServer;
}
public static void Log(string value)
{
list.Add(value);
timer.Stop();
timer.Start();
}
static void SendToServer(object sender, ElapsedEventArgs e)
{
//TODO send data to server
//AutoReset is false, so neither of these are needed
//timer.Enabled = false;
//timer.Stop();
}
}
Note that rather than using a List it's very possible that you want to use a BlockingCollection<string> instead. This has several advantages. First, the Log methods will work if called at the same time from multiple threads; as is multiple concurrent logs could break the list. It also means that SendToServer can be taking items out of the queue at the same time that new items are added. If you use a List you'll need to lock all access to the list (which might not be a problem, but isn't as straightforward).
This kind of thing is very easy to achieve with IObservable (Rx).
Let us simplify matters by declaring a Subject<string> as your list to push onto using .OnNext. Once you have your subject, an observable, you can do what you want with a single 'line' of System.Reactive.Linq. This is illustrated in the following pseudo-c#
subject
.Buffer(<your timespan>,1) //buffer until either a value is added or the timeout expires
.Subscribe(x =>
{
if (x.Count == 0) //the timeout expired so send on
{
SendAccumulatedListToServer(<your list>);
<clear your list>
}
else
{
<your list>.Add(x);
}
});
What you are implementing is totally the wrong way to go about doing this. Have a look at the consumer producer model:
http://msdn.microsoft.com/en-us/library/hh228601.aspx
What you are trying to do is very commonly called the Consumer/Producer dataflow model. Essentially you have something generating a list of data that is to be sent somewhere, rather than sending it each time an item is added to the list you would like to send them in groups.. So you have a producer (the code putting data to be sent) and a consumer (the code sending the data).
Generally this problem is solved by spawning a thread that watches the list (usually a queue) and sends the data at regulary intervals, the best way to do this is using an EventWaitHandle.
Here is some very simplified code as an example
class ServerStuff
{
public void Init()
{
datatosend = new List<string>();
exitrequest = new EventWaitHandle(false, EventResetMode.ManualReset); //This wait handle will signal the consumer thread to exit
Thread t = new Thread(new ThreadStart(_RunThread));
t.Start(); // Start the consumer thread...
}
public void Stop()
{
exitrequest.Set();
}
List<string> datatosend;
EventWaitHandle exitrequest;
public void AddItem(string item)
{
lock (((ICollection)datatosend).SyncRoot)
datatosend.Add(item);
}
private void RunThread()
{
while (exitrequest.WaitOne(10 * 1000)) //wait 10 seconds between sending data, or wake up immediatly to exit request
{
string[] tosend;
lock (((ICollection)datatosend).SyncRoot)
{
tosend = datatosend.ToArray();
datatosend.Clear();
}
//Send the data to Sever here...
}
}
}

C# event not being handled

I'm learning C# event handling by writing an app that uses the iTunes COM API. I have a method that should run when iTunes stops playing a song, but the method is never getting called when I trigger the event in the app by hitting the "stop/pause" button.
EDIT: Based on dboarman's reply, I deleted the while loop. Now the event does get handled, but only if I restart iTunes prior to running PlayPlaylist(). If I run PlayPlaylist() a second time, the stop event no longer gets fired/handled.
void trayIcon_Click(object sender, EventArgs e)
{
PlayPlaylist();
}
public static void PlayPlaylist()
{
itapp = new iTunesApp();
itapp.OnPlayerStopEvent +=
new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);
lastPlaylist = itapp.LibraryPlaylist;
itapp.Play();
}
static void itapp_OnPlayerStopEvent(object iTrack)
{
Debug.WriteLine("Stop Event fired");
//...
}
Updated source in Pastebin here (lines 59-68 are the relevant ones).
Spec: My app is supposed to play the songs in a Genius recommendations playlist from first to last (iTunes by default doesn't play Genius recommendations consecutively). The StopEvent should trigger the next song in the list to play.
Here is the complete code that is in question:
public static void PlayPlaylist()
{
itapp = new iTunesApp();
itapp.OnPlayerStopEvent += new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);
lastPlaylistID = itapp.LibraryPlaylist.playlistID;
Debug.WriteLine("Last playlist:");
Debug.WriteLine(lastPlaylistID);
itapp.Play();
while (true)
{
System.Threading.Thread.Sleep(1000);
}
}
I suspect that the while loop is causing the event to never fire because the thread will sleep for a second and because true is, well...always true.
I would put your playlist in into a list. Something like:
static List<myTrack> Tracks;
public static void PlayPlaylist()
{
itapp = new iTunesApp();
itapp.OnPlayerStopEvent += new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);
foreach (myTrack track in Tracks)
{
// perform play
}
}
See how that works for you.
When your itapp goes out of scope, be sure to release it with
System.Runtime.InteropServices.Marshal.ReleaseComObject(itapp);
or you'll have to restart iTunes for it to work again. Unregistering the event handlers with -= probably wouldn't hurt either.
If you want the thread to block and wait for the event you can use the ManualResetEvent class.
private ManualResetEvent _resetEvent;
public void PlayPlaylist()
{
_resetEvent = new ManualResetEvent(false);
itapp = new iTunesApp();
itapp.OnPlayerStopEvent += new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);
// Block until the resetEvent has been Set() or
// give up waiting after 5 minutes
_resetEvent.WaitOne(1000*5*60);
}
Inside itapp_OnPlayerStopEvent() you must call:
_resetEvent.Set();
To answer your original question, I'm pretty sure the while loop is not giving the thread any time to respond to the stop event, hence you are not seeing it being handled.
I'm wondering if the fact that the event handler doesn't unhook is causing an issue somewhere along the line (i.e. iTunes holds a singular reference to the initial instance of your app). This may solve it? I don't have the iTunes API so I'm flying a little blind; apologize if it's a waste of time.
private bool stopIt;
private void trayIcon_Click(object sender, EventArgs e)
{
if (!stopIt)
{
PlayPlaylist();
stopIt = true;
}
else
{
StopPlaylist();
stopIt = false;
}
}
public static void PlayPlaylist()
{
itapp = new iTunesApp();
itapp.OnPlayerStopEvent +=
new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);
lastPlaylist = itapp.LibraryPlaylist;
itapp.Play();
}
public static void StopPlaylist()
{
itapp.Stop(); // don't know if this is the right method name
// unhook the event so the reference object can free if necessary.
itapp.OnPlayerStopEvent -=
new _IiTunesEvents_OnPlayerStopEventEventHandler(itapp_OnPlayerStopEvent);
}
private static void itapp_OnPlayerStopEvent(object iTrack)
{
Debug.WriteLine("Stop Event fired");
// ...
}

Categories

Resources