public interface ICommand {
void DoWork();
}
public class CommandRunner {
readonly ThreadCounter threadCounter = new ThreadCounter();
public List<ICommand> workItems = new List<ICommand>();
private bool isStopping;
public void StartWorkItems() {
while(workItems.Count>0) {
if (!isStopping) {
var item = workItems.First();
workItems.RemoveAt(0);
ThreadPool.QueueUserWorkItem(DoWorkItem, item);
} else {
Thread.Sleep(250);
}
}
}
private void DoWorkItem(object state) {
using(threadCounter.CountableThread()) {
((ICommand)state).DoWork();
}
}
public void WaitForWorkToFinish() {
isStopping = true;
threadCounter.WaitForPendingThreadsToFinish();
isStopping = false;
}
}
public class ThreadCounter {
private readonly object lockobject = 0;
private int _count;
public sealed class WorkingThread:IDisposable {
private readonly ThreadCounter _reference;
public WorkingThread(ThreadCounter reference) {
_reference = reference;
}
void IDisposable.Dispose() {
lock (_reference.lockobject) {
_reference._count--;
}
}
}
public void WaitForPendingThreadsToFinish() {
int count;
lock (lockobject) {
count = _count;
}
while (count > 0) {
Thread.Sleep(250);
lock (lockobject) {
count = _count;
}
}
}
public WorkingThread CountableThread() {
lock (lockobject) {
_count++;
}
return new WorkingThread(this);
}
}
internal class Program {
private static void Main(string[] args) {
var server = new Server(8181, "/asdf", @"C:\TestSite\");
server.Start();
bool TurningOff = false;
while (!TurningOff) {
if (Console.KeyAvailable) {
TurningOff = true;
}
Thread.Sleep(500);
}
server.Stop();
}
}
It is also very simple to encapsulate that logic in the SetUpFixture and
TearDownFixture of my test library; which you can see being done with
Cassini.dll in
https://svn.castleproject.org/svn/castle/trunk/MonoRail/Castle.MonoRail.TestSupport/WebServer.cs
The issue is more that I am seeking an understanding of how this works,
not that I merely want to replace cassini (and after I posted the
previous email, I went and reread the cassini eula and couldn't find the
part saying you can't redistribute it in compiled form). In coming to
such an understanding I will have a replacement for Cassini.dll simply
because I will have written one. I don't particularly care that Cassini
or xsp exist, and would probably be writing my own toy app even still.
I've already learned several things about ASP.NET running from a command
line that I didn't already know:
I've learned ASP.NET has to run in a different app domain (or at least
the domain needs to be modified, I am not sure exactly what is happening
there, only that I am doing it correctly) from the server and sockets
running from a console app
I now know why exactly you do need MarshalByRefObject (previously I just
noticed it was the base object for all collections and had no idea what
it really did, now I have learned that it is used to proxy classes
across remoting connections, including across app domains)
I've learned a lot about rfc2616 (http/1.1)
I've discovered System.Web.HttpRequest.PathInfo
I've learned that even though System.Net.HttpListener can be used to
create a basic web server and can even be used to process asp.net files,
I can't seem to figure out how to get it to correctly handle postbacks.
And those are just some of the notable things; Also I am getting even
better with my R# commands.