1

I am developing a multi-threaded Serial Port communication software, and I noticed that a ContextSwitchDeadlock is thrown while using the below provided code.

Note that, appending GetPortNames() to its own function and then writing out the IDs of each thread (Start and End) to the Console shows that every started thread is also correctly, as seen in the Explicit Example.

Simple Example:

while (true)
{
    Task.Run(() => SerialPort.GetPortNames());
    Thread.Sleep(100);
}

Explicit Example:

[STAThread]
private static void Main(string[] args)
{
    while (true)
    {
        Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
        Task.Run(() => Example());
        Thread.Sleep(100);
        Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
    }
}

private static void Example()
{
    Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
    SerialPort.GetPortNames();
    Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}

String Compare Example (NO EXCEPTION):

[STAThread]
private static void Main(string[] args)
{
    while (true)
    {
        Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
        Task.Run(() => Example());
        Thread.Sleep(100);
        Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
    }
}

private static void Example()
{
    Console.WriteLine("Start " + Thread.CurrentThread.ManagedThreadId);
    string.Compare("a", "b");
    Console.WriteLine("End " + Thread.CurrentThread.ManagedThreadId);
}

As I do not think that any threads are being deadlocked, this error should not happen. The full error message:

Managed Debugging Assistant 'ContextSwitchDeadlock' : 'The CLR has been unable to transition from COM context 0xb434c0 to COM context 0xb43408 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

I am curious to understand why is it happening.

The simple example provided shows the simplest way to achieve this, while the explicit example shows a way to track all threads, the output of which is:

Start 1
Start 3
End 3
End 1
Start 1
Start 3
End 3
End 1
Start 1
Start 4
End 4
End 1
Start 1
Start 3
End 3
End 1

And as such it can be seen that no Thread is deadlocked, as they all start and end correctly (although I have no idea where Thread No.2 is).

Eduard G
  • 443
  • 5
  • 21
  • Your entry point is marked `[STAThread]`. Why? Is this a console application, or a GUI application? – canton7 Aug 01 '19 at 11:22
  • Possible duplicate of [Visual Studio: ContextSwitchDeadlock](https://stackoverflow.com/questions/578357/visual-studio-contextswitchdeadlock) – Sinatr Aug 01 '19 at 11:43
  • Static methods are usually thread-safe, I don't see anything special in [source](https://referencesource.microsoft.com/#System/sys/system/io/ports/SerialPort.cs,670), so it's nothing to do with serial port. – Sinatr Aug 01 '19 at 11:46
  • (It's probably because OP is putting an STA thread into an infinite loop - but more context is needed to figure out *why* they're doing this, and so what the solution is) – canton7 Aug 01 '19 at 11:47
  • It is a Console Application, I believe it is how the project was created when I started it. Removing [STAThread] also gets rid of the exception (as I just found out). The exception states that there is a thread that did not transition from the COM context for 60 seconds and I was wondering what thread is that since it does not seem to exist. – Eduard G Aug 01 '19 at 11:48
  • If you replace the SerialPort call with something else the Exception is not thrown, which is what made me think that its the cause. – Eduard G Aug 01 '19 at 11:49
  • 4
    I think it's your main thread. STA threads are expected to pump a message queue, but yours isn't doing this because 1) it's a console application which doesn't have access to a message queue, and 2) it's doing an infinite loop. – canton7 Aug 01 '19 at 11:49
  • It's likely that something inside SerialPort was calling into the winapi, which was doing a check to see whether the thread was pumping its message queue correctly. Telling the debugger to break on the ContextSwitchException (as described in the proposed duplicate) will tell you exactly where that's coming from. – canton7 Aug 01 '19 at 11:51
  • 1
    @canton7 that makes sense. I have ContextSwitchException enabled but it is not giving me much information as it stops on "Application is in break mode" page. If you post your comment as an Answer I can select it as the correct answer for the post, so other people can see the answer more quickly. – Eduard G Aug 01 '19 at 11:58
  • Have a look at the Threads window and the Call Stack window – canton7 Aug 01 '19 at 12:01

1 Answers1

2

Your main thread has an [STAThread] attribute on it.

As this answer explains, your thread is therefore expected to be pumping a COM message queue. However, your main thread doesn't have access to a message queue (as it's just in a simple console application), and it's stuck in an infinite loop so it wouldn't be pumping anything anyway!

It's very unusual for console applications to use [STAThread]. If you're not explicitly doing anything with COM, (i.e. there's no specific reason why you added it), you can probably safely remove it. If you do need to use [STAThread] for some reason, then you will need to re-think your threading model a bit.

As for why the ContextSwitchException only occurs when you call into SerialPort, my guess is that SerialPort is calling into the winapi, which ends up calling something which checks whether your thread is pumping a message queue. You could use the debugger to dig further into this if you're curious.

canton7
  • 37,633
  • 3
  • 64
  • 77