1
0
mirror of https://github.com/livingcomputermuseum/ContrAlto.git synced 2026-01-17 08:34:15 +00:00

209 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
namespace Contralto
{
/// <summary>
/// The SchedulerEventCallback describes a delegate that is invoked whenever a scheduled event has
/// reached its due-date and is fired.
/// </summary>
/// <param name="timeNsec">The current Alto time (in nsec)</param>
/// <param name="skew">The delta between the requested exec time and the actual exec time (in nsec)</param>
/// <param name="context">An object containing context useful to the scheduler of the event</param>
public delegate void SchedulerEventCallback(ulong timeNsec, ulong skewNsec, object context);
/// <summary>
///
/// </summary>
public class Event
{
public Event(ulong timestampNsec, object context, SchedulerEventCallback callback)
{
_timestampNsec = timestampNsec;
_context = context;
_callback = callback;
}
/// <summary>
/// The absolute time (in nsec) to raise the event.
/// </summary>
public ulong TimestampNsec
{
get { return _timestampNsec; }
set { _timestampNsec = value; }
}
/// <summary>
/// An object containing context to be passed to the
/// event callback.
/// </summary>
public object Context
{
get { return _context; }
set { _context = value; }
}
/// <summary>
/// A delegate to be executed when the callback fires.
/// </summary>
public SchedulerEventCallback EventCallback
{
get { return _callback; }
}
private ulong _timestampNsec;
private object _context;
private SchedulerEventCallback _callback;
}
/// <summary>
/// The Scheduler class provides infrastructure for scheduling Alto time-based hardware events
/// (for example, sector marks, or video task wakeups).
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
public class SchedulerQueue
{
public SchedulerQueue()
{
_queue = new LinkedList<Event>();
}
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<Event> 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);
}
/// <summary>
/// TODO: provide more optimal data structure here once profiling can be done.
/// </summary>
private LinkedList<Event> _queue;
}
}