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

340 lines
11 KiB
C#

// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Threading;
using RJBlack;
using System.Net.IP;
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 iHead, iTail;
// 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 i = new Internal();
i.fun = fun;
i.arg = arg;
if (iHead == null)
iHead = i;
else
iTail.next = i;
iTail = i;
// 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 (iHead != null)
{
Internal i = iHead;
iHead = i.next;
i.fun(i.arg);
continue;
}
// One read of NOW
ulong now = (ulong)DateTime.UtcNow.Ticks;
// Deal with timers
TimeSpan waitTime = TimeSpan.MaxValue;
lock (timers)
{
Timer t = (Timer)timers.Advance(now);
if (t != null)
{
// This timer has expired; service
// it immediately and start over
t.fun(t.arg);
continue;
}
t = (Timer)timers.GetSoonest();
if (t != null)
{
ulong soonest = t.time;
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