I've written a small windows service, and I'm having a problem that I'm
spending a lot more time on than I'd like.
If anyone has experienced this problem and has any hints or solutions; they
would be appreciated.
Simply leaving the threadMain() method if something unexpected occurs during
thread execution leaves the thread in a ThreadState.Running state. This
(apparently??) causes the Thread.Join() in service.OnStop() to hang
indefinitely, resulting in a service that can't be stopped. A
Thread.Sleep(0) in threadMain() yields the same result. So I thought I'd ask
the service to stop from the thread and then just hang around, waiting for a
regular service shutdown. ServiceController.Stop() does seem to run
asynchronously, so no obvious problems to this, except that it always hangs
on the instruction setting the field ThreadCodeClass.dieNow.
As for dieNow, I've tried using a lock(this) section in a property instead
of the volatile field. Didn't work either.
Tried compiling against v1.0.3705 and v1.1.4322, same result.
===== Source code =====
using System;
using System.IO;
using System.ServiceProcess;
using System.Threading;
namespace Dummy {
public class Debug {
private static StreamWriter sw;
public static void log(string s) {
if (sw == null) {
sw = File.AppendText("c:\\lagkage.txt");
sw.AutoFlush = true;
sw.Write("\r\n\r\n--- Test run at " + DateTime.Now + " ---\r\n\r\n");
}
// no thread safety information in the .net docs on System.IO.File;
locking just to be sure
lock(sw) {
sw.Write("" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff "));
sw.WriteLine(s);
}
}
}
public class ThreadCodeClass {
public volatile bool dieNow = false;
public void threadMain() {
Debug.log("Thread: started.");
try {
while (! dieNow) {
Debug.log("Thread: running.");
// code that does one thing or the other would be here
throw new Exception("wak wak simulated failure");
Thread.Sleep(5000);
}
} catch (Exception e) {
Debug.log("Thread: Unexpected exception occured; I don't know how to
handle this. This is fatal; attempting automated service shutdown. Exception
was: " + e.Message);
try {
ServiceController sc = new ServiceController(TestService.serviceName);
// Assuming this is done asynchronously - the .net docs doesn't mention
sc.Stop();
Debug.log("Thread: Service stop initiated.");
} catch { }
}
while (! dieNow) {
Debug.log("Thread: Waiting for termination request. dieNow is " +
dieNow + ", sleeping.");
Thread.Sleep(5000);
}
Debug.log("Thread: Dying.");
}
}
public class TestService : System.ServiceProcess.ServiceBase {
public const string serviceName = "TestService";
private Thread wT = null;
private ThreadCodeClass wT2 = null;
public TestService() {
ServiceName = serviceName;
}
static void Main() {
System.ServiceProcess.ServiceBase[] ServicesToRun;
ServicesToRun = new System.ServiceProcess.ServiceBase[] { new
TestService() };
System.ServiceProcess.ServiceBase.Run(ServicesToRu n);
}
protected override void OnStart(string[] args) {
Debug.log("Service: Starting service.");
ThreadCodeClass wT2 = new ThreadCodeClass();
wT = new Thread(new ThreadStart(wT2.threadMain));
wT.Start();
Debug.log("Service: Service started.");
}
protected override void OnStop() {
Debug.log("Service: Stop signal received. Stopping and joining thread
(thread state is: " + wT.ThreadState + ")");//, dieNow is " + wT2.dieNow +
")...");
// Now why doesn't this work:
wT2.dieNow = true;
Debug.log("Service: Volatile dieNow set to true. Reading dieNow yields: "
+ wT2.dieNow + ". Joining thread...");
wT.Join();
Debug.log("Service: Service stopped.");
}
}
}
===== End source code =====
Copy-paste into a windows service project, compile and install as service.
Upon starting the service, it should generate a log file, which should
contain something like this:
===== Log file =====
Service: Starting service.
Service: Service started.
Thread: started.
Thread: running.
Thread: Unexpected exception occured; I don't know how to handle this. This
is fatal; attempting automated service shutdown. Exception was: wak wak
simulated failure
Service: Stop signal received. Stopping and joining thread (thread state is:
Running)
Thread: Service stop initiated.
Thread: Waiting for termination request. dieNow is False, sleeping.
===== End log file =====
The "Service: Stop signal received" doesn't always occur, atm. I believe
it's a thread context switch issue, but that this isn't the real problem.
Run the service again (kill it with "kill testservice" first) if it fails to
produce this line :-)...
On this system, the line is produced almost every time.
Ciao!