singrdk/base/Services/NetStack/Runtime/Dispatcher.cs

351 lines
12 KiB
C#
Raw Normal View History

2008-03-05 09:52:00 -05:00
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
2008-11-17 18:29:00 -05:00
// #define DEBUG_TCP
2008-03-05 09:52:00 -05:00
using System;
2008-11-17 18:29:00 -05:00
using System.Net.IP;
2008-03-05 09:52:00 -05:00
using System.Threading;
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
using RJBlack;
using Drivers.Net;
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
using NetStack.NetDrivers;
namespace NetStack.Runtime
{
// This provides the basic dispatching of three types of events
// 1. Internal events. These are when one piece of code requests
// that another piece of code is called as soon as it is done, but
// it cannot be called immediately because of a re-entrancy issue.
// 2. External events. These events occur when some component
// external to the stack (such as a client or a device driver)
// indicate that some action is pending.
// 3. Timers. These events occur as a result of an external
// activity, but once triggered are handled as internal events
//
// Internal events have priority over external events. The
// rationale for this may be found in the design documents.
// XXX This code needs refactoring into platform specific
// and generic components.
public class Dispatcher
{
// Public interfaces by which we interact with components
public delegate NetStatus Callback(CallbackArgs ca);
public interface CallbackArgs {}
private uint swapCount = 0;
private bool stopped = false;
// Linked list of internal events
private class Internal
{
public Callback fun;
public CallbackArgs arg;
public Internal next;
}
2008-11-17 18:29:00 -05:00
private Internal internalEventsHead;
private Internal internalEventsTail;
2008-03-05 09:52:00 -05:00
// This event is used to wake up the main dispatcher
// thread, typically because we have changed the
// list of events or timeouts to be serviced.
// It lives in the first slot of eHandles.
private AutoResetEvent dispatcherStrobe;
// Timers have arguments too
private Timer.TimerWheel! timers;
private class Timer : RJBlack.Timer
{
public Callback fun;
public CallbackArgs arg;
public Timer(Callback fun, CallbackArgs arg, ulong time) : base(time)
{
this.fun = fun;
this.arg = arg;
}
}
// External events. eHandles[0] is special and
// holds our internal strobe (dispatcherStrobe).
private Callback [] eFuns;
private CallbackArgs [] eArgs;
private WaitHandle []! eHandles;
// Public Methods
public void AddCallback(Callback fun, CallbackArgs arg)
{
2008-11-17 18:29:00 -05:00
Internal internalEvent = new Internal();
internalEvent.fun = fun;
internalEvent.arg = arg;
if (internalEventsHead == null) {
internalEventsHead = internalEvent;
}
else {
internalEventsTail.next = internalEvent;
}
internalEventsTail = internalEvent;
2008-03-05 09:52:00 -05:00
// Make sure to wake up and notice this
dispatcherStrobe.Set();
}
internal RJBlack.Timer! AddCallback(Callback fun, CallbackArgs args, ulong time)
{
Timer t = new Timer(fun, args, time);
2008-11-17 18:29:00 -05:00
lock (timers) {
timers.Add(t);
}
2008-03-05 09:52:00 -05:00
// Make sure to wake up and notice this
dispatcherStrobe.Set();
return t;
}
public void AddCallback(Callback fun, CallbackArgs arg, WaitHandle wh)
{
int n = eHandles.Length;
WaitHandle [] newHandles = new WaitHandle[n + 1];
Callback [] newFuns = new Callback[n + 1];
CallbackArgs [] newArgs = new CallbackArgs[n + 1];
2008-11-17 18:29:00 -05:00
if (n > 0) {
2008-03-05 09:52:00 -05:00
// Lock to defend against the main loop shuffling the array
// while we are copying it
2008-11-17 18:29:00 -05:00
lock (eHandles.SyncRoot) {
2008-03-05 09:52:00 -05:00
eHandles.CopyTo(newHandles, 0);
eFuns.CopyTo(newFuns, 0);
eArgs.CopyTo(newArgs, 0);
}
}
// Add the new callback to the end...
newHandles[n] = wh;
newFuns[n] = fun;
newArgs[n] = arg;
// Swap in the new arrays
eHandles = newHandles;
eFuns = newFuns;
eArgs = newArgs;
// Make sure to wake up and notice this
dispatcherStrobe.Set();
}
// CALLER MUST HOLD A LOCK that ensures that
// the eHandles, eFuns and eArgs array won't
// change during this function call!
private void RemoveWaitHandleCallback(int index)
{
int newLength = eHandles.Length - 1;
2008-11-17 18:29:00 -05:00
if (newLength == 0) {
2008-03-05 09:52:00 -05:00
eHandles = new WaitHandle[0];
eFuns = null;
eArgs = null;
return;
}
int lhLength = index;
int rhLength = newLength - index;
// Allocate the replacement arrays
WaitHandle [] newHandles = new WaitHandle[newLength];
Callback [] newFuns = new Callback[newLength];
CallbackArgs [] newArgs = new CallbackArgs[newLength];
// Copy before the removed item...
2008-11-17 18:29:00 -05:00
if (lhLength > 0) {
2008-03-05 09:52:00 -05:00
Array.Copy(eHandles, 0, newHandles, 0, lhLength);
Array.Copy(eFuns, 0, newFuns, 0, lhLength);
Array.Copy(eArgs, 0, newArgs, 0, lhLength);
}
// Copy after the removed item...
2008-11-17 18:29:00 -05:00
if (rhLength > 0) {
2008-03-05 09:52:00 -05:00
Array.Copy(eHandles, index + 1, newHandles, index, rhLength);
Array.Copy(eFuns, index + 1, newFuns, index, rhLength);
Array.Copy(eArgs, index + 1, newArgs, index, rhLength);
}
// Swap in the replacement array
eHandles = newHandles;
eFuns = newFuns;
eArgs = newArgs;
// Make sure to wake up and notice this
dispatcherStrobe.Set();
}
public void RemoveCallback(WaitHandle wh)
{
2008-11-17 18:29:00 -05:00
lock (eHandles.SyncRoot) {
for (int i = 0; i < eHandles.Length; i++) {
if (((!)eHandles[i]).Equals(wh)) {
2008-03-05 09:52:00 -05:00
RemoveWaitHandleCallback(i);
return;
}
}
}
}
public bool RemoveTimeoutCallback(RJBlack.Timer! fun)
{
2008-11-17 18:29:00 -05:00
lock (timers) {
2008-03-05 09:52:00 -05:00
return timers.Remove(fun);
}
}
public void Stop()
{
stopped = true;
// Make sure to wake up and notice this
dispatcherStrobe.Set();
}
// Execute forever
public void Execute()
{
2008-11-17 18:29:00 -05:00
while (stopped == false) {
2008-03-05 09:52:00 -05:00
// Internal events first
2008-11-17 18:29:00 -05:00
if (internalEventsHead != null) {
Internal internalEvent = internalEventsHead;
internalEventsHead = internalEvent.next;
#if DEBUG_TCP
Core.Log("Dispatching Internal Event at " +
DateTime.UtcNow.ToString() +
" calling '" + internalEvent.fun.ToString() +
"'.");
#endif
internalEvent.fun(internalEvent.arg);
2008-03-05 09:52:00 -05:00
continue;
}
// One read of NOW
ulong now = (ulong)DateTime.UtcNow.Ticks;
// Deal with timers
TimeSpan waitTime = TimeSpan.MaxValue;
2008-11-17 18:29:00 -05:00
lock (timers) {
Timer timer = (Timer)timers.Advance(now);
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
if (timer != null) {
2008-03-05 09:52:00 -05:00
// This timer has expired; service
// it immediately and start over
2008-11-17 18:29:00 -05:00
#if DEBUG_TCP
Core.Log("Timer Expiration at " +
(new DateTime((long) now)).ToString() +
" calling '" + timer.fun.ToString() +
"'.");
#endif
timer.fun(timer.arg);
2008-03-05 09:52:00 -05:00
continue;
}
2008-11-17 18:29:00 -05:00
timer = (Timer)timers.GetSoonest();
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
if (timer != null) {
ulong soonest = timer.time;
if (soonest < now) {
Core.Log("Negative WaitTime");
waitTime = new TimeSpan(0L);
}
else {
waitTime = new TimeSpan((long)(soonest - now));
}
2008-03-05 09:52:00 -05:00
}
}
// Grab a copy of the external-event pointers so
// we're not affected by changes to them on other
// threads
Callback[] funs = eFuns;
CallbackArgs[] args = eArgs;
WaitHandle[] handles = eHandles;
// Shuffle the array values so the same tasks aren't
// always preferred to others (WaitAny() will unblock
// against the lowest-index WaitHandle in the array)
2008-11-17 18:29:00 -05:00
lock (handles.SyncRoot) {
2008-03-05 09:52:00 -05:00
int n = handles.Length;
2008-11-17 18:29:00 -05:00
for (int i = 0; i < n - 1; ++i) {
2008-03-05 09:52:00 -05:00
// Swap the current element of the list with
// an entry that follow it. Instead of an
// expensive real random-number generator,
// generate a pseudo-random number by multiplying
// a largish prime with an increasing count
uint swapWith = (uint)(i + ((unchecked(53533511 * swapCount++)) % (n - i)));
if (swapWith != i) {
Callback swapFun = funs[swapWith];
CallbackArgs swapArgs = args[swapWith];
WaitHandle swapHandle = handles[swapWith];
funs[swapWith] = funs[i];
args[swapWith] = args[i];
handles[swapWith] = handles[i];
funs[i] = swapFun;
args[i] = swapArgs;
handles[i] = swapHandle;
}
}
}
int rc = WaitHandle.WaitAny(handles, waitTime);
if (rc == WaitHandle.WaitTimeout)
continue;
if (rc < 0 || rc >= handles.Length)
throw new ApplicationException("Invalid WaitAny");
Callback cb = funs[rc];
2008-11-17 18:29:00 -05:00
if (cb != null) {
2008-03-05 09:52:00 -05:00
NetStatus res = cb(args[rc]);
if (!NetStatus.SUCCEEDED(res))
Core.Panic("Error handling internal event.");
}
}
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
Core.Log("NetStack dispatcher exiting...\n");
}
// Constructor
public Dispatcher()
{
this.timers = new RJBlack.Timer.TimerWheel(10000, 16, (ulong)DateTime.UtcNow.Ticks);
CallbackArgs[]! eArgs = this.eArgs = new CallbackArgs[1];
Callback[]! eFuns = this.eFuns = new Callback[1];
AutoResetEvent! dispatcherStrobe = this.dispatcherStrobe = new AutoResetEvent(false);
WaitHandle[]! eHandles = this.eHandles = new WaitHandle[1];
base();
// The first eHandles element is always our private strobe
eHandles[0] = dispatcherStrobe;
eArgs[0] = null;
eFuns[0] = null;
}
} // Dispatcher
} // NetStack
// End