One way is to call t.Abort() where t is the Thread in question. This will cause the victim to experience a ThreadAbortException. (Which, by the way, is a funky little exception -- see note 1)
However, I consider it kind of dirty, because it makes it that much harder to analyze the correctness of your program. If you use it, you have to consider that the target code might be getting a ThreadAbortException at some arbitrary and unplanned-for point, and it's not hard to imagine scenarios where this could mess up your program's global state.
Unfortunately, in the webserver program I don't see any other way to abort the Accept. One reason I don't like calling Abort() is that you might get in a little race and the webserver might receive a new connection just as you've decided to abort it, so maybe what really gets aborted is not the Accept() itself but rather some other point while you're halfway through the "requestReceived" delegate and whatever it was trying to do. At the very least this is ugly.
In general the approach I like to use in my threads is some variant of the below:
while(I still have more work to do) {
...do some work...
if(killRequested) {
//maybe do some cleanup here
break;
}
}
In code like this, you
request that it die (by setting some shared killRequested flag), but it dies at a time of its own choosing.
Now, if I absolutely
had to support this in the case of the webserver, my first instinct is the following (maybe someone out there has a simpler idea). Make a new thread and dedicate it solely to calling Accept and stashing its result somewhere, like this:
while(true) {
try {
var nextSocket=listeningSocket.Accept();
lock(sync) {
queue.Add(nextSocket);
}
autoResetEvent.Set(); //notify my listener that there is a new item
} catch(ThreadAbortException) { //consider maybe doing this on *any* exception
lock(sync) {
queue=null;
}
autoResetEvent.Set(); //notify my listener one last time
}
}
The nice thing about this is that it's not doing very much, so it's easier to analyze its behavior upon thread abort. Upon thread abort, I even throw away my queue (because maybe the thread abort happened at the worst possible time and corrupted my queue data structure. I don't know whether that's possible or not given the thread model of the Netduino but I'm erring on the side of caution)
Then on the queue consuming side you have this thread:
while(true) {
var killRequested=false;
Socket nextItem=null;
lock(sync) {
if(queue==null) {
killRequested=true;
} else if(queue.Length()>0) {
nextItem=(Socket)queue.Dequeue();
}
}
if(killRequested) {
...nice kill of self here
return;
}
if(nextItem==null) {
//wait for a signal and then restart the logic all over again
autoResetEvent.WaitOne();
continue;
}
...otherwise we have something to work on
}
The nice thing about this thread is that nobody aborts it. It hears about the death of the acceptor thread and then kills itself at a controlled time, not while it's in the middle of something.
Is this post helpful? Too long and wanky? You decide!
Note 1: The trippy thing about ThreadAbortException is that you can catch it but it gets rethrown at the end of every catch. You may want to run this little program and puzzle over all the "wow"'s that get printed and why it doesn't print "Why am I not printed?", and how this is different from normal exception handling.
using System;
using System.Threading;
using Microsoft.SPOT;
namespace NetduinoApplication1 {
public class Program {
public static void Main() {
var t=new Thread(Doit1);
t.Start();
Thread.Sleep(5*1000);
t.Abort();
}
private static void Doit1() {
try {
Doit2();
} catch(Exception) {
Debug.Print("wow 1");
}
}
private static void Doit2() {
try {
Doit3();
} catch(Exception) {
Debug.Print("wow 2");
}
}
private static void Doit3() {
try {
while(true) {
Thread.Sleep(100);
}
} catch(Exception) {
Debug.Print("wow 3");
}
Debug.Print("Why am I not printed?");
}
}
}