351 lines
12 KiB
C#
351 lines
12 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
|
|
// #define DEBUG_TCP
|
|
|
|
using System;
|
|
using System.Net.IP;
|
|
using System.Threading;
|
|
|
|
using RJBlack;
|
|
|
|
using Drivers.Net;
|
|
|
|
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;
|
|
}
|
|
|
|
private Internal internalEventsHead;
|
|
private Internal internalEventsTail;
|
|
|
|
// 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)
|
|
{
|
|
Internal internalEvent = new Internal();
|
|
internalEvent.fun = fun;
|
|
internalEvent.arg = arg;
|
|
if (internalEventsHead == null) {
|
|
internalEventsHead = internalEvent;
|
|
}
|
|
else {
|
|
internalEventsTail.next = internalEvent;
|
|
}
|
|
internalEventsTail = internalEvent;
|
|
|
|
// 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);
|
|
|
|
lock (timers) {
|
|
timers.Add(t);
|
|
}
|
|
|
|
// 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];
|
|
|
|
if (n > 0) {
|
|
// Lock to defend against the main loop shuffling the array
|
|
// while we are copying it
|
|
lock (eHandles.SyncRoot) {
|
|
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;
|
|
|
|
if (newLength == 0) {
|
|
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...
|
|
if (lhLength > 0) {
|
|
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...
|
|
if (rhLength > 0) {
|
|
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)
|
|
{
|
|
lock (eHandles.SyncRoot) {
|
|
for (int i = 0; i < eHandles.Length; i++) {
|
|
if (((!)eHandles[i]).Equals(wh)) {
|
|
RemoveWaitHandleCallback(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool RemoveTimeoutCallback(RJBlack.Timer! fun)
|
|
{
|
|
lock (timers) {
|
|
return timers.Remove(fun);
|
|
}
|
|
}
|
|
|
|
|
|
public void Stop()
|
|
{
|
|
stopped = true;
|
|
|
|
// Make sure to wake up and notice this
|
|
dispatcherStrobe.Set();
|
|
}
|
|
|
|
// Execute forever
|
|
public void Execute()
|
|
{
|
|
while (stopped == false) {
|
|
// Internal events first
|
|
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);
|
|
continue;
|
|
}
|
|
|
|
// One read of NOW
|
|
ulong now = (ulong)DateTime.UtcNow.Ticks;
|
|
|
|
// Deal with timers
|
|
TimeSpan waitTime = TimeSpan.MaxValue;
|
|
|
|
lock (timers) {
|
|
Timer timer = (Timer)timers.Advance(now);
|
|
|
|
if (timer != null) {
|
|
// This timer has expired; service
|
|
// it immediately and start over
|
|
#if DEBUG_TCP
|
|
Core.Log("Timer Expiration at " +
|
|
(new DateTime((long) now)).ToString() +
|
|
" calling '" + timer.fun.ToString() +
|
|
"'.");
|
|
#endif
|
|
timer.fun(timer.arg);
|
|
continue;
|
|
}
|
|
|
|
timer = (Timer)timers.GetSoonest();
|
|
|
|
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));
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
lock (handles.SyncRoot) {
|
|
int n = handles.Length;
|
|
|
|
for (int i = 0; i < n - 1; ++i) {
|
|
// 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];
|
|
if (cb != null) {
|
|
NetStatus res = cb(args[rc]);
|
|
|
|
if (!NetStatus.SUCCEEDED(res))
|
|
Core.Panic("Error handling internal event.");
|
|
}
|
|
}
|
|
|
|
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
|