How to handle COM events from a console application? - c#

I'm using a COM object from a third party library that generates periodic events. When I use the library from a Winforms app, having the object as a class member and creating it in the main form thread, everything works. However, if I create the object from another thread, I don't receive any event.
My guess is that I need to have some kind of event loop in the same thread used to create the object.
I need to use this object from a console application. I guess I could use Application.DoEvents, but I'd rather not include the Winforms namespace in a console App.
How can I solve this problem?
Update 3 (2011-06-15): The vendor has answered at last. In short, they say there is some difference between the message pump created by Application.Run and the one created by Thread.Join, but they don't know what that difference is.
I agree with them; any light shed on this matter would be very appreciated.
Update:
From Richard comment to mdm answer:
if there other component is single threaded and instantiated from an MTA then Windows will create the worker thread + window + message pump and do the necessary marshalling.
Trying to follow his advice, I'm doing the following:
Update 2:
I'm changed the code following João Angelo answer.
using System;
namespace ConsoleApplication2
{
class Program
{
[STAThread]
static void Main(string[] args)
{
MyComObjectWrapper wrapper = new MyComObjectWrapper();
}
}
class MyComObjectWrapper
{
MyComObject m_Object;
AutoResetEvent m_Event;
public MyComObjectWrapper()
{
m_Event = new System.Threading.AutoResetEvent(false);
System.Threading.Thread t = new System.Threading.Thread(() => CreateObject());
t.SetApartmentState (System.Threading.ApartmentState.STA);
t.Start();
Wait();
}
void ObjectEvt(/*...*/)
{
// ...
}
void Wait()
{
m_Event.WaitOne();
}
void CreateObject()
{
m_Object = new MyComObject();
m_Object.OnEvent += ObjectEvt;
System.Threading.Thread.CurrentThread.Join();
}
}
}
I have also tried the following instead:
public MyComObjectWrapper()
{
CreateObject();
}

If you're using STA, then you're going to need a message loop one way or another. If you don't otherwise need a message loop, MTA is perhaps the simplest way to go, and is also the best for for a console-style application.
One thing to be aware of is that with MTA, it doesn't matter which thread created the object; all objects created by an MTA thread belong equally to all MTA threads. (Or, in COM speak, a process has exactly one Multi-Threaded Apartment, in which all MTA threads live.) What this means is that if you're taking the MTA approach, there's no need to create a separate thread at all - just create the object from the main thread. But you also need to be aware that incoming events will be delivered on a 'random' thread, so you'll have to take separate steps to communicate back to the main thread.
using System;
using System.Threading;
class Program
{
static MyComObject m_Object;
static AutoResetEvent m_Event;
[MTAThread]
static void Main(string[] args)
{
m_Event = new AutoResetEvent(false);
m_Object = new MyComObject();
m_Object.OnEvent += ObjectEvt;
Console.WriteLine("Main thread waiting...");
m_Event.WaitOne();
Console.WriteLine("Main thread got event, exiting.");
// This exits after just one event; add loop or other logic to exit properly when appropriate.
}
void ObjectEvt(/*...*/)
{
Console.WriteLine("Received event, doing work...");
// ... note that this could be on any random COM thread.
Console.WriteLine("Done work, signalling event to notify main thread...");
m_Event.Set();
}
}
Couple of comments on the previous version of the code you had: you had calls to Wait() in both CreateObject and in the MycomObjectWrapper constructor; seems you should only have one - if you have two of them, only one of them will get released when m_Event.Set() is called, and the other will still be waiting. Also, suggest adding in some debugging code so you know how far you are getting. That way you can at least tell if you are getting the event from COM, and separately, whether you are successfully communicating that back to the main thread. If the objects are marked neutral or both in the registry, then there should be no problem creating them from a MTA.

As already stated in other answers STA COM components require a message loop to be run in order for calls happening in other threads be correctly marshaled to the STA thread that owns the component.
In Windows Forms you get the message loop for free, but in a console application you must do it explicitly by calling Thread.CurrentThread.Join on the thread that owns the COM component and that is probably also the main thread for the application. This thread must be STA.
From the MSDN entry of Thread.Join you can see that this is what you want:
Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping.
If you don't want to do anything else in the main console thread you just wait indefinitely, otherwise you can do other stuff while periodically calling Thread.CurrentThread.Join to pump messages.
Side-note: This assumes you're dealing with a STA COM component.
A simplified example:
class Program
{
[STAThread]
static void Main(string[] args)
{
var myComObj = new MyComObject();
myComObj.OnEvent += ObjectEvt;
Thread.CurrentThread.Join(); // Waits forever
}
static void ObjectEvt(object sender, EventArgs e) { }
}
In this example the console application will be in a never ending loop that should do nothing more then respond to events from the COM component. If this does not work you should try to get support from the COM component vendor.

IIRC, COM events require an event loop to work, something that pumps messages and calls the Win32 GetMessage function.
Winforms does this for you, or you can emulate it with Win32 calls. This question/answer has a good example you can build on.

I think the following should work:
[STAThread]
Main(...)
{
var comObject = new YourComObject();
comObject.Event += EventHandler;
Console.WriteLine("Press enter to exit.");
Console.ReadLine();
}
void EventHandler(...)
{
// Handle the event
}

Have you defined the thread apartment model?
[STAThread]
static void Main(string[] args)
{
// Create the thread that will manage the COM component
Thread th = new Thread(...);
// Before starting the thread
th.SetApartmentState (ApartmentState.STA);
}
In the thread, just wait for an Event to signal its termination. While the thread is waiting on the event, I think that it should process messages on the thread loop.

Could you try this:
static class Program
{
MyComObject m_Object;
[STAThread]
static void Main()
{
m_Object = new MyComObject();
m_Object.OnEvent += ObjectEvt;
System.Windows.Forms.Application.Run();
}
void ObjectEvt(/*...*/)
{
// ...
}
}

Related

Releasing a named mutex created in WPF Application.OnStartUp(): Which thread owns it?

I create a mutex within the OnStartup Method of a WPF app. The mutex is not used anywhere else in the program, its only purpose is to prevent certain programs from running concurrently. How can I release this mutex when the application closes?
According to the documentation, mutex.ReleaseMutex() must be called from the same thread that created the mutex. However this presents a problem, since I do not control the thread that calls OnStartup().
Suppose my OnStartup method looks like this:
public partial class App : Application
{
private Mutex mutex;
private bool hasHandle = false;
protected override void OnStartup(StartupEventArgs e)
{
bool createdNew;
mutex = new Mutex(false, #"Global\XYZ", out createdNew);
try
{
hasHandle = mutex.WaitOne(5000, false);
if (!hasHandle)
{/*do stuff*/};
}
catch (AbandonedMutexException)
{
hasHandle = true;
// do stuff
}
base.OnStartup(e);
}
private void releaseMutex()
{
if (mutex!=null)
{
if (hasHandle) mutex.ReleaseMutex();
mutex.Dispose();
}
}
}
Is it save to call releaseMutex() ...
in the OnExit() method?
protected override void OnExit(){releaseMutex();}
in the ProcessExit event handler?
AppDomain.CurrentDomain.ProcessExit += (sender,e)=> releaseMutex();
in a finalizer?
~App(){releaseMutex();}
in the unhandled exception event handler?
AppDomain.CurrentDomain.UnhandledException += (sender,e)=> releaseMutex();
It seems like the OnExit method has the best chance to be in the same thread, but even that seems a sketchy assumption. Is there a way to ignore the same-thread requirement? Or should I create and store a separate thread in conjunction with my mutex?
I personally wouldn't bother releasing it at all, especially since you handle AbandonedMutexException.
If a mutex is not used to synchronize threads of the same process there is no need to explicitly release it. When a process terminates OS automatically closes all handles created by the process, such as files, sockets, mutexes, semaphores and event handles .
If you still prefer to release it consider using Application.OnExit() since it is called from the main thread, just like the Startup().
According to my research, every GUI WPF application has a UI thread which can be accessed via Application.Current.Dispatcher (see for example this answer). This UI thread should always remain active for the lifetime of the application.
You can use Dispatcher.CheckAccess to see whether you are running in the UI thread, and if you are not you can use Dispatcher.Invoke to execute an action in the context of the UI thread.
The description of Application.Run implies that Application.OnStartup is always run on the UI thread, but it should not be harmful to check and, if necessary, use the UI thread dispatcher to invoke the action that creates the mutex.
It seems a reasonable guess that Application.OnExit is also always run on the UI thread, but since this does not appear to be documented, you should check and, if necessary, use the UI thread dispatcher to invoke the action that releases the mutex.
As Alexm correctly points out, you do not in fact need to explicitly release the mutex provided that the application is running in its own process (which will usually be the case) but you do need to ensure that the thread the mutex is created on will remain active until you are ready to free it. I believe using the UI thread is the simplest way to ensure this.

Thread in SxS C# COM DLL makes calling application unresponsive

I have written a Side-By-Side COM DLL in C#.
I am using this DLL in VB6.
The COM DLL contains a thread.
As soon as I start this thread, the calling application (in my case VB6) blocks (meaning I can not do anything in it anymore).
I am starting the thread like this:
private Thread _startMasterThread;
public void Init()
{
if (_startMasterThread == null)
{
_startMasterThread = new Thread(new ThreadStart(pMasterThread));
_startMasterThread.Priority = ThreadPriority.Highest;
_startMasterThread.Start();
}
}
private void pMasterThread()
{
while (!_bAbortAll)
{
//do something
}
}
ThreadStart comes from this:
namespace System.Threading
{
[ComVisible(true)]
public delegate void ThreadStart();
}
Is it normal that the calling application becomes unresponsive?
I thought that since it is a different thread, it would not do this.
It looks like you create a STA thread and then neglect to serve the message pump. Callers will block because nobody responds to their messages. Hence, the UI (a caller) freezes (block).
Some old, but highly relevant articles:
Understanding and Using COM Threading Models
Single-Threaded Apartments
Apartments and Pumping in the CLR
After you read these, and understand the issue, the solution(s) will be obvious. Switch to an MTA or run a message pump in your thread.

C# STAThread COMException

I have an external component (C++), which I want to call from my C# code.
The code is something like this:
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace dgTEST
{
class Program
{
[STAThread]
static void Main(string[] args)
{
ExtComponentCaller extCompCaller = new ExtComponentCaller();
result = extCompCaller.Call(input);
Thread t = new Thread(new ThreadStart(() =>
{
try
{
result = extCompCaller.Call(input);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
}
}
So the problem is that, at the first call it's working well, the external component called, I got back result.
But when I try to call it in an other thread, I got an exception:
System.InvalidCastException: Unable to cast COM object of type 'System.__ComObject' ... .
I'm sure this exception throwed, because of the STAThread. Because if I remove the [STAThread] attribute from the Main function, the same occurs with the first call of the external component, which was worked fine.
How can I call this external component from an other thread to get rid of this exception?
UPDATE-------------
Other crazy thing occurs now. When I start the program from Visual Studio with F5, the problem occurs in the first call as well, but when I execute directly the binary .exe file, it's working (from the other thread it isn't :( ).
If I switch the build from Debug to Release and starting it from Visual Studio with F5, the first call working again.
Why does it happen?
Thanks for you help in advance!
Best Regards,
Zoli
Threading is never a small detail. If code isn't explicitly documented to support threading then the 99% odds are that it doesn't support it.
And clearly this component doesn't support threading. Creating another STA thread is not the magic solution, it is still a different thread. The InvalidCastException tells you that it also is missing the proxy/stub support that's required to marshal calls from a worker thread, like the one that you are trying to create. Required to make thread-safe calls to code that isn't thread-safe. Albeit that you did break the contract for an [STAThread], it must pump a message loop. It is the message loop that allows making calls from a worker thread to a component that isn't thread safe. You get a message loop from Application.Run().
This is where the buck stops. It isn't thread-safe, period. Even if fix your main thread or ask the vendor or author to supply you with the proxy/stub, you still haven't accomplished what you set out to do, it won't actually run on that worker thread you created. So it must look like this:
static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(() =>
{
ExtComponentCaller extCompCaller = new ExtComponentCaller();
result = extCompCaller.Call(input);
}));
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
}
Which creates the object on the same thread that you make the calls from so it is thread-safe. There's still the problem that this worker thread doesn't pump a message loop, COM components tend to rely on that. You'll find out whether that's a problem or not from deadlock or events that don't run. If it already worked okay in your test program when you called it from the main thread then you are probably okay with not pumping.

Worker thread and Dipose()

I have some utility class with worker thread with simple exit condition. I use this class in my application. Worker thread is created and started in class constructor.
class MyClass
{
Thread _thread;
// unrelevant details are omitted
void WorkerThreadRoutine
{
while(_running)
{
// do some useful background work here
}
}
}
My question is WHEN do I have to set _running=false. In C++ with deterministic
resource deallocation life is easy - I use object destructors and don't care.
I would write something like
~MyClass()
{
_running = false;
}
In C# there no destructors in C++ sense. Do I have to write some Dispose() function here
and use IDisposable? I can of course provide a Stop() function. But when do I have to call it? Is there a way to automatically have my Stop function called?
What is right pattern here? I have lots of MyClass intances across my application.
Right now my application hangs on exit.
The reason your application hangs is that new threads are created as foreground threads per default. The CLR will keep your process alive as long as you have any running foreground threads.
To get rid of the thread, just exit the code it is running. This will make the thread available for cleanup and once it has shut down the process will be able to close as well (assuming you have no other foreground threads running).
In C++, you'd normally explicitly call the delete operator. That's not different from explicitly calling a Stop() method in C#.
The C++ compiler can auto-generate the delete operator call if your object is a local variable of a method. That maps well to IDisposible in C# with the using statement:
void SomethingSlow() {
using (var obj = new MyClass()) {
// etc..
}
}
class MyClass : IDisposable {
private ManualResetEvent mStop = new ManualResetEvent(false);
public Dispose() {
mStop.Set();
}
// etc...
}
Your thread probably doesn't stop right now because you forgot to declare the _running field as volatile. Using an event can help avoid problems like that. You can set the thread's IsBackground property to true to prevent the thread from hanging your program termination. That's a band-aid, not a fix.

Deadlock in WinForms that is prevented by right click on the taskbar

I encountered a strange problem with our Windows C# / .NET application. Actually it is a GUI application, my job is the included network component, encapsulated in an assembly. I do not know the code of the main/GUI application, I could contact it's developer though.
Now the application's UI has buttons to "Start" and "Stop" the network engine. Both buttons work.
To make my component threadsafe I am using a lock around three methods. I dont't want a client to be able to call Stop() before Start() finished. Additinally there is a Polling Timer.
I tried to show you as few lines as possible and simpified the problem:
private Timer actionTimer = new Timer(new
TimerCallback(actionTimer_TimerCallback),
null, Timeout.Infinite, Timeout.Infinite);
public void Start()
{
lock (driverLock)
{
active = true;
// Trigger the first timer event in 500ms
actionTimer.Change(500, Timeout.Infinite);
}
}
private void actionTimer_TimerCallback(object state)
{
lock (driverLock)
{
if (!active) return;
log.Debug("Before event");
StatusEvent(this, new StatusEventArgs()); // it hangs here
log.Debug("After event");
// Now restart timer
actionTimer.Change(500, Timeout.Infinite);
}
}
public void Stop()
{
lock (driverLock)
{
active = false;
}
}
Here is how to reproduce my problem. As I said, the Start and Stop buttons both work, but if you press Start(), and during the execution of the TimerCallback press Stop(), this prevents the TimerCallback to return. It hangs exactly at the same position, the StatusEvent. So the lock is never released and the GUI also hangs, because it's call of the Stop() method cannot proceed.
Now I observed the following: If the application hangs because of this "deadlock" and I click on the application in the task bar with the right mouse button, it continues. It just works as expected then. Anybody has an explanation or better a solution for this?
By the way, I also tried it with InvokeIfRequired as I don't know the internas of the GUI application. This is neccesary if my StatusEvent would change something in the GUI.
Since I have no reference to the GUI controls, I used (assuming only one target):
Delegate firstTarget = StatusEvent.GetInocationList()[0];
ISynchronizeInvoke syncInvoke = firstTarget.Target as ISynchronizeInvoke;
if (syncInvoke.InvokeRequired)
{
syncInvoke.Invoke(firstTarget, new object[] { this, new StatusEventArgs() });
}
else
{
firstTarget.Method.Invoke(firstTarget.Target, new object[] { this, new StatusEventArgs() });
}
This approach didn't change the problem. I think this is because I am Invoking on the main application's event handlers, not on the GUI controls. So the main app is responsible for Invoking? But anyway, AFAIK not using Invoke although needed would not result in a deadlock like this but (hopefully) in an exception.
As for why right-click "unlocks" your application, my "educated guess" of events that lead to this behaviour is as follows:
(when your component was created) GUI registered a subscriber to the status notification event
Your component acquires lock (in a worker thread, not GUI thread), then fires status notification event
The GUI callback for status notification event is called and it starts updating GUI; the updates are causing events to be sent to the event loop
While the update is going on, "Start" button gets clicked
Win32 sends a click message to the GUI thread and tries to handle it synchronously
Handler for the "Start" button gets called, it then calls "Start" method on your component (on GUI thread)
Note that the status update has not finished yet; start button handler "cut in front of"
the remaining GUI updates in status update (this actually happens quite a bit in Win32)
"Start" method tries to acquire your component's lock (on GUI thread), blocks
GUI thread is now hung (waits for start handler to finish; start handler waits for lock; the lock is held by worker thread that marshalled a GUI update call to GUI thread and waits for the update call to finish; the GUI update call marshalled from worker thread is waiting for start handler that cut in front of it to finish; ...)
If you now right-click on taskbar, my guess is that taskbar manager (somehow) starts a "sub-event-loop" (much like modal dialogs start their own "sub-event-loops", see Raymond Chen's blog for details) and processes queued events for the application
The extra event loop triggered by the right-click can now process the GUI updates that were marshalled from the worker thread; this unblocks the worker thread; this in turn releases the lock; this in turn unblocks application's GUI thread so it can finish handling start button click (because it can now acquire the lock)
You could test this theory by causing your application to "bite", then breaking into debugger and looking at the stack trace of the worker thread for your component. It should be blocked in some transition to GUI thread. The GUI thread itself should be blocked in the lock statement, but down the stack you should be able to see some "cut in front of the line" calls...
I think the first recommendation to be able to track this issue down would be to turn on the flag Control.CheckForIllegalCrossThreadCalls = true;.
Next, I would recommend firing the notification event outside of the lock. What I usually do is gather information needed by an event inside a lock, then release the lock and use the information I gathered to fire the event. Something along the lines:
string status;
lock (driverLock) {
if (!active) { return; }
status = ...
actionTimer.Change(500, Timeout.Infinite);
}
StatusEvent(this, new StatusEventArgs(status));
But most importantly, I would review who are the intended clients of your component. From the method names and your description I suspect GUI is the only one (it tells you when to start and stop; you tell it when your status changes). In that case you should not be using a lock. Start & stop methods could simply be setting and resetting a manual-reset event to indicate whether your component is active (a semaphore, really).
[update]
In trying to reproduce your scenario I wrote the following simple program. You should be able to copy the code, compile and run it without problems (I built it as a console application that starts a form :-) )
using System;
using System.Threading;
using System.Windows.Forms;
using Timer=System.Threading.Timer;
namespace LockTest
{
public static class Program
{
// Used by component's notification event
private sealed class MyEventArgs : EventArgs
{
public string NotificationText { get; set; }
}
// Simple component implementation; fires notification event 500 msecs after previous notification event finished
private sealed class MyComponent
{
public MyComponent()
{
this._timer = new Timer(this.Notify, null, -1, -1); // not started yet
}
public void Start()
{
lock (this._lock)
{
if (!this._active)
{
this._active = true;
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d));
}
}
}
public void Stop()
{
lock (this._lock)
{
this._active = false;
}
}
public event EventHandler<MyEventArgs> Notification;
private void Notify(object ignore) // this will be invoked invoked in the context of a threadpool worker thread
{
lock (this._lock)
{
if (!this._active) { return; }
var notification = this.Notification; // make a local copy
if (notification != null)
{
notification(this, new MyEventArgs { NotificationText = "Now is " + DateTime.Now.ToString("o") });
}
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d)); // rinse and repeat
}
}
private bool _active;
private readonly object _lock = new object();
private readonly Timer _timer;
}
// Simple form to excercise our component
private sealed class MyForm : Form
{
public MyForm()
{
this.Text = "UI Lock Demo";
this.AutoSize = true;
this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
var container = new FlowLayoutPanel { FlowDirection = FlowDirection.TopDown, Dock = DockStyle.Fill, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink };
this.Controls.Add(container);
this._status = new Label { Width = 300, Text = "Ready, press Start" };
container.Controls.Add(this._status);
this._component.Notification += this.UpdateStatus;
var button = new Button { Text = "Start" };
button.Click += (sender, args) => this._component.Start();
container.Controls.Add(button);
button = new Button { Text = "Stop" };
button.Click += (sender, args) => this._component.Stop();
container.Controls.Add(button);
}
private void UpdateStatus(object sender, MyEventArgs args)
{
if (this.InvokeRequired)
{
Thread.Sleep(2000);
this.Invoke(new EventHandler<MyEventArgs>(this.UpdateStatus), sender, args);
}
else
{
this._status.Text = args.NotificationText;
}
}
private readonly Label _status;
private readonly MyComponent _component = new MyComponent();
}
// Program entry point, runs event loop for the form that excercises out component
public static void Main(string[] args)
{
Control.CheckForIllegalCrossThreadCalls = true;
Application.EnableVisualStyles();
using (var form = new MyForm())
{
Application.Run(form);
}
}
}
}
As you can see, the code has 3 parts - first, the component that is using timer to call notification method every 500 milliseconds; second, a simple form with label and start/stop buttons; and finally main function to run the even loop.
You can deadlock the application by clicking start button and then within 2 seconds clicking stop button. However, the application is not "unfrozen" when I right-click on taskbar, sigh.
When I break into the deadlocked application, this is what I see when switched to the worker (timer) thread:
And this is what I see when switched to the main thread:
I would appreciate if you could try compiling and running this example; if it works the same for you as me, you could try updating the code to be more similar to what you have in your application and perhaps we can reproduce your exact issue. Once we reproduce it in a test application like this, it shouldn't be a problem to refactor it to make the problem go away (we would isolate essence of the problem).
[update 2]
I guess we agree that we can't easily reproduce your behaviour with the example I provided. I'm still pretty sure the deadlock in your scenario is broken by an extra even loop being introduced on right-click and this event loop processing messages pending from the notification callback. However, how this is achieved is beyond me.
That said I would like to make the following recommendation. Could you try these changes in your application and let me know if they solved the deadlock problem? Essentially, you would move ALL component code to worker threads (i.e. nothing that has to do with your component will be running on GUI thread any more except code to delegate to worker threads :-) )...
public void Start()
{
ThreadPool.QueueUserWorkItem(delegate // added
{
lock (this._lock)
{
if (!this._active)
{
this._active = true;
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d));
}
}
});
}
public void Stop()
{
ThreadPool.QueueUserWorkItem(delegate // added
{
lock (this._lock)
{
this._active = false;
}
});
}
I moved body of Start and Stop methods into a thread-pool worker thread (much like your timers call your callback regularly in context of a thread-pool worker). This means GUI thread will never own the lock, the lock will only be acquired in context of (probably different for each call) thread-pool worker threads.
Note that with the change above, my sample program doesn't deadlock any more (even with "Invoke" instead of "BeginInvoke").
[update 3]
As per your comment, queueing Start method is not acceptable because it needs to indicate whether the component was able to start. In this case I would recommend treating the "active" flag differently. You would switch to "int" (0 stopped, 1 running)and use "Interlocked" static methods to manipulate it (I assume that your component has more state it exposes - you would guard access to anything other than "active" flag with your lock):
public bool Start()
{
if (0 == Interlocked.CompareExchange(ref this._active, 0, 0)) // will evaluate to true if we're not started; this is a variation on the double-checked locking pattern, without the problems associated with lack of memory barriers (see http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)
{
lock (this._lock) // serialize all Start calls that are invoked on an un-started component from different threads
{
if (this._active == 0) // make sure only the first Start call gets through to actual start, 2nd part of double-checked locking pattern
{
// run component startup
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d));
Interlocked.Exchange(ref this._active, 1); // now mark the component as successfully started
}
}
}
return true;
}
public void Stop()
{
Interlocked.Exchange(ref this._active, 0);
}
private void Notify(object ignore) // this will be invoked invoked in the context of a threadpool worker thread
{
if (0 != Interlocked.CompareExchange(ref this._active, 0, 0)) // only handle the timer event in started components (notice the pattern is the same as in Start method except for the return value comparison)
{
lock (this._lock) // protect internal state
{
if (this._active != 0)
{
var notification = this.Notification; // make a local copy
if (notification != null)
{
notification(this, new MyEventArgs { NotificationText = "Now is " + DateTime.Now.ToString("o") });
}
this._timer.Change(TimeSpan.FromMilliseconds(500d), TimeSpan.FromMilliseconds(-1d)); // rinse and repeat
}
}
}
}
private int _active;
A couple things come to mind when reviewing your code. The first thing is that you are not checking for a null delegate before firing the status event. If no listeners are bound to the event, then this will cause an exception, which if not caught or handled, might cause strange issues in threaded code.
So the first thing I'd so is this:
if(StatusEvent != null)
{
StatusEvent(this, new StatusEventArgs());
}
The other thing that comes to mind is that perhaps your lock is failing you in some manner. What type of object are you using for the lock? The simplest thing to use is just a plain ole "object", but you must ensure you are not using a value type (e.g. int, float, etc.) that would be boxed for locking, thus never really establishing a lock since each lock statement would box and create a new object instance. You should also keep in mind that a lock only keeps "other" threads out. If called on the same thread, then it will sail through the lock statement.
If you don't have the source for the GUI (which you probably should) you can use Reflector to disassemble it. There is even a plugin to generate source files so you could run the app in your VS IDE and set breakpoints.
Not having access to the GUI source makes this harder, but a general tip here... The WinForm GUI is not managed code, and doesn't mix well with .NET threading. The recommended solution for this is to use a BackgroundWorker to spawn a thread that is independent of the WinForm. Once you're running in the thread started by the BackgroundWorker, you're in pure managed code and you can use .NET's timers and threading for pretty much anything. The restriction is that you have to use the BackgroundWorker's events to pass information back to the GUI, and your thread started by the BackgroundWorker can't access the Winform controls.
Also, you'd be well off to disable the "Stop" button while the "Start" task is running, and vice versa. But a BackgroundWorker is still the way to go; that way the WinForm doesn't hang while the background thread is running.
Yes, this is a classic deadlock scenario. The StatusEvent cannot proceed because it needs the UI thread to update the controls. The UI thread is however stuck, trying to acquire the driverLock. Held by the code that calls StatusEvent. Neither thread can proceed.
Two ways to break the lock:
the StatusEvent code might not necessarily need to run synchronously. Use BeginInvoke instead of Invoke.
the UI thread might not necessarily need to wait for the thread to stop. Your thread could notify it later.
There is not enough context in your snippets to decide which one is better.
Note that you might have a potential race on the timer too, it isn't visible in your snippet. But the callback might run a microsecond after the timer was stopped. Avoid this kind of headache by using a real thread instead of a timer callback. It can do things periodically by calling WaitOne() on a ManualResetEvent, passing a timeout value. That ManualResetEvent is good to signal the thread to stop.
A wild guess here: Could the status message somehow be causing the other app to call your Stop task?
I would put debug stuff at the start of all three methods, see if you're deadlocking on yourself.

Categories