using System; using System.Collections.Generic; namespace Contralto { /// /// The SchedulerEventCallback describes a delegate that is invoked whenever a scheduled event has /// reached its due-date and is fired. /// /// The current Alto time (in nsec) /// The delta between the requested exec time and the actual exec time (in nsec) /// An object containing context useful to the scheduler of the event public delegate void SchedulerEventCallback(ulong timeNsec, ulong skewNsec, object context); /// /// /// public class Event { public Event(ulong timestampNsec, object context, SchedulerEventCallback callback) { _timestampNsec = timestampNsec; _context = context; _callback = callback; } /// /// The absolute time (in nsec) to raise the event. /// public ulong TimestampNsec { get { return _timestampNsec; } set { _timestampNsec = value; } } /// /// An object containing context to be passed to the /// event callback. /// public object Context { get { return _context; } set { _context = value; } } /// /// A delegate to be executed when the callback fires. /// public SchedulerEventCallback EventCallback { get { return _callback; } } private ulong _timestampNsec; private object _context; private SchedulerEventCallback _callback; } /// /// The Scheduler class provides infrastructure for scheduling Alto time-based hardware events /// (for example, sector marks, or video task wakeups). /// public class Scheduler { public Scheduler() { Reset(); } public ulong CurrentTimeNsec { get { return _currentTimeNsec; } } public void Reset() { _schedule = new SchedulerQueue(); _currentTimeNsec = 0; } public void Clock() { // // Move one system clock forward in time. // _currentTimeNsec += _timeStepNsec; // // See if we have any events waiting to fire at this timestep. // while (true) { if (_schedule.Top != null && _currentTimeNsec >= _schedule.Top.TimestampNsec) { // Pop the top event and fire the callback. Event e = _schedule.Pop(); e.EventCallback(_currentTimeNsec, _currentTimeNsec - e.TimestampNsec, e.Context); } else { // All done. break; } } } public Event Schedule(Event e) { e.TimestampNsec += _currentTimeNsec; _schedule.Push(e); return e; } public void CancelEvent(Event e) { _schedule.Remove(e); } private ulong _currentTimeNsec; private SchedulerQueue _schedule; // 170nsec is approximately one Alto system clock cycle and is the time-base for // the scheduler. private const ulong _timeStepNsec = 170; } /// /// Provides an "ordered" queue based on timestamp -- the top of the queue is always the /// next event to be fired; a "push" places a new event in order on the current queue. /// public class SchedulerQueue { public SchedulerQueue() { _queue = new LinkedList(); } public Event Top { get { if (_queue.Count > 0) { return _queue.First.Value; } else { return null; } } } public void Push(Event e) { // Degenerate case: list is empty or new entry is earlier than the head of the list. if (_queue.Count == 0 || _queue.First.Value.TimestampNsec >= e.TimestampNsec) { _queue.AddFirst(e); return; } // // Do a linear search to find the place to put this in. // Since we maintain a sorted list with every insertion we only need to find the first entry // that the new entry is earlier (or equal) to. // This will likely be adequate as the queue should never get incredibly deep; a binary // search may be more performant if this is not the case. // TODO: profile this and ensure this is the case. // LinkedListNode current = _queue.First; while (current != null) { if (current.Value.TimestampNsec >= e.TimestampNsec) { _queue.AddBefore(current, e); return; } current = current.Next; } // Add at end _queue.AddLast(e); } public Event Pop() { Event e = _queue.First.Value; _queue.RemoveFirst(); return e; } public void Remove(Event e) { _queue.Remove(e); } /// /// TODO: provide more optimal data structure here once profiling can be done. /// private LinkedList _queue; } }