views:

52

answers:

2

I've written an application with a plug-in feature, and if a plug in doesn't respond quickly to a stop request, I call Thread.Abort() on its worker thread. This seems to work most of the time, but I recently discovered that it throws an ObjectDisposedException if the thread is aborted while it's writing to the serial port. With some USB-to-serial drivers in Windows XP, it even causes a blue screen of death.

I think I can use a producer/consumer pattern to make it so that the worker thread doesn't directly write to the serial port, but I'd like to know if there's any more detail available about why the serial port freaks out.

Here's a simplified example that reproduces the problem:

using System;
using System.IO.Ports;
using System.Threading;

class Program
{
    private static SerialPort port = new SerialPort();
    static void Main(string[] args)
    {
        port.PortName = "COM1";
        port.Open();
        for (int i = 0; i < 10; i++)
        {
            var workerThread = new Thread(Loop);
            workerThread.Start();

            Thread.Sleep(1000);

            workerThread.Abort();

            Thread.Sleep(1000);
            Console.Out.WriteLine("Finished {0}.", i);
        }
    }

    static void Loop()
    {
        for (int i = 0; i < 1000000; i++)
        {
            port.Write(new byte[] {0, 0, 0, 0, 0, (byte)(i % 256)}, 0, 6);
        }
    }
}
A: 

It doesn't explain exactly what goes wrong, but tip 4 in this post from the base class library team makes it sound like aborting the thread during serial port operations is unsupported:

We’ve had a few bugs reporting sudden shutdown in apps – most often it’s caused by performing unsupported behavior such as interrupting a thread accessing the SerialPort. The specific symptom in this case is an ObjectDisposedException thrown on a separate thread that cannot be caught.

Don Kirkby
Sounds plausible. Last I recall though, `Thread.Abort()` was considered obsolete and you should use a shared flag.
Jesse C. Slicer
A: 

Not sure if this would help (my computer has no serial ports; can't test), but this goes along with my comment.

using System;
using System.IO.Ports;
using System.Threading;

class Program
{
    private static SerialPort port = new SerialPort();
    private static readonly object locker = new object();
    private static bool abort;
    static void Main(string[] args)
    {
        port.PortName = "COM1";
        port.Open();
        for (int i = 0; i < 10; i++)
        {
            var workerThread = new Thread(Loop);
            lock (locker)
            {
                abort = false;
            }

            workerThread.Start();

            Thread.Sleep(1000);

            lock (locker)
            {
                abort = true;
            }

            Thread.Sleep(1000);
            if (workerThread.IsAlive)
            {
                // Last-ditch effort.
                workerThread.Abort();
            }

            Console.Out.WriteLine("Finished {0}.", i);
        }
    }

    static void Loop()
    {
        for (int i = 0; i < 1000000; i++)
        {
            lock (locker)
            {
                if (abort)
                {
                    return; // or break would work here in this case...
                }
            }

            port.Write(new byte[] {0, 0, 0, 0, 0, (byte)(i % 256)}, 0, 6);
        }
    }
}
Jesse C. Slicer
Yes, my application uses a shared flag to communicate with the plug in. I'm getting the problem when the plug in doesn't follow the rules and continues to run after the abort flag is set. At that point, I use Thread.Abort() as a last-ditch effort. If I'm very unlucky and the plug in happens to be accessing the serial port at that moment, it crashes the application.
Don Kirkby