home | OO Richards Bench

Mono C# #30

Register values v1 v2 are held in Task classes, which implement scheduler function fn to match interface ISchedulerTask. (They could be implemented as nested classes within the Scheduler class.)

C# language Mono C# version 1.0 Mono JIT compiler

using System;
using System.Collections.Specialized;
using System.Text;
using System.IO;
using System.Runtime.InteropServices; 

internal class Bench
{
   const bool TraceOn = false; 

   // Use Encoding to explicitly convert bytes to ascii chars
   static ASCIIEncoding Encoding = new ASCIIEncoding();
   static int Layout = 0;

   // Timing
   [DllImport("Kernel32.dll")]
   private static extern bool QueryPerformanceCounter(
      out long lpPerformanceCount);
   // Timing
   [DllImport("Kernel32.dll")]
   private static extern bool QueryPerformanceFrequency(
      out long lpFrequency);


   static void Main(string[] args)
   {
      int count = 10000;
      if (args.Length > 0)
         count = Int32.Parse(args[0]); 

      // Timing
      long startTicks, stopTicks, frequency;
      QueryPerformanceCounter(out startTicks);
      
      Scheduler s = new Scheduler(TraceOn);
      Packet wkq;

      s.AddIdleTask(Scheduler.TaskId.Idle, 0, null, count);

      wkq = new Packet(null, Scheduler.TaskId.Worker, Packet.Kinds.Work);
      wkq = new Packet(wkq, Scheduler.TaskId.Worker,  Packet.Kinds.Work);
      s.AddWorkerTask(Scheduler.TaskId.Worker, 1000, wkq);

      wkq = new Packet(null, Scheduler.TaskId.DeviceA, Packet.Kinds.Device);
      wkq = new Packet(wkq, Scheduler.TaskId.DeviceA, Packet.Kinds.Device);
      wkq = new Packet(wkq, Scheduler.TaskId.DeviceA, Packet.Kinds.Device);
      s.AddHandlerTask(Scheduler.TaskId.HandlerA, 2000, wkq);

      wkq = new Packet(null, Scheduler.TaskId.DeviceB, Packet.Kinds.Device);
      wkq = new Packet(wkq, Scheduler.TaskId.DeviceB, Packet.Kinds.Device);
      wkq = new Packet(wkq, Scheduler.TaskId.DeviceB, Packet.Kinds.Device);
      s.AddHandlerTask(Scheduler.TaskId.HandlerB, 3000, wkq);

      s.AddDeviceTask(Scheduler.TaskId.DeviceA, 4000, null);

      s.AddDeviceTask(Scheduler.TaskId.DeviceB, 5000, null);

      s.Schedule();

      // Timing
      QueryPerformanceCounter(out stopTicks);
      QueryPerformanceFrequency(out frequency);
      // Timing

      String ss = "";

      FileInfo f = new FileInfo("testrun-times.xml");
      if (f.Exists){
         StreamReader sr = f.OpenText();
         ss = sr.ReadToEnd();
         sr.Close();
      }

      StreamWriter sw = new StreamWriter("testrun-times.xml");
      sw.Write(ss);

      sw.Write("<ExternalStat>");
      sw.Write("<size>{0}</size>", count);
      sw.Write("<ticks>{0}</ticks>", stopTicks - startTicks);
      sw.Write("<ticksPerSecond>{0}</ticksPerSecond>", frequency);  
      sw.WriteLine("</ExternalStat>");

      sw.Flush();   
      sw.Close();

      Console.WriteLine("QueueCount = {0}", s.queueCount);
      Console.WriteLine("HoldCount = {0}", s.holdCount);         
   }


   internal static void Trace(byte aByte)
   {
      --Layout;
      if (Layout <= 0)
      {
         Console.WriteLine();
         Layout = 50;
      }
      // need to explicitly convert byte to ascii char
      byte[] b = new Byte[1];
      b[0] = aByte;
      char[] c = Encoding.GetChars(b);
      Console.Write(c[0]);
   }

}


internal class Scheduler
{
   private Tcb[] table = new Tcb[MaxTasks];
   private Tcb list = null;
   private Tcb currentTcb;
   private TaskId currentId;

   internal int queueCount = 0;
   internal int holdCount = 0;
   internal bool traceOn = false;


   internal enum TaskId 
   {
      Idle, Worker, HandlerA, HandlerB, 
      DeviceA, DeviceB}

   protected const int MaxTasks = 10;


   internal Scheduler(bool trace)
   {
      traceOn = trace;
   }


   internal void AddIdleTask(TaskId anId, int aPriority, 
      Packet aWorkQueue, int aCount)
   {
      AddRunningTask(anId, aPriority, aWorkQueue, 
         new IdleTask(this, 1, aCount) );
   }


   internal void AddWorkerTask(TaskId anId, int aPriority, 
      Packet aWorkQueue)
   {
      AddTask(anId, aPriority, aWorkQueue, 
         new WorkerTask(this, TaskId.HandlerA, 0) );
   }


   internal void AddHandlerTask(TaskId anId, int aPriority,
      Packet aWorkQueue)
   {
      AddTask(anId, aPriority, aWorkQueue,
         new HandlerTask(this) );
   }


   internal void AddDeviceTask(TaskId anId, int aPriority,
      Packet aWorkQueue
      )
   {
      AddTask(anId, aPriority, aWorkQueue,  
         new DeviceTask(this) );
   }


   protected void AddTask(TaskId anId, int aPriority,
      Packet aWorkQueue, ISchedulerTask aTask)
   {
      currentTcb = new Tcb(list, anId, aPriority, aWorkQueue, aTask);
      list = currentTcb;
      table[(int)anId] = currentTcb;
   }


   protected void AddRunningTask(TaskId anId, int aPriority,
      Packet aWorkQueue, ISchedulerTask aTask)
   {
      AddTask(anId, aPriority, aWorkQueue, aTask);
      currentTcb.SetRunning(); 
   }


   internal void Schedule()
   {
      currentTcb = list;
      while (currentTcb != null)
      {
         if (currentTcb.IsHeldOrSuspended())
            currentTcb = currentTcb.Link;
         else
         {
            currentId = currentTcb.Id;
            //if (traceOn) Bench.Trace((byte)((int)'0' + currentId + 1));
            currentTcb = currentTcb.Run();
         }
      }
   }


   internal Tcb Queue(Packet aPacket)
   {
      Tcb t = table[(int)aPacket.Id];
      if (t == null) return t;
      queueCount++;
      aPacket.Link = null;
      aPacket.Id = currentId;
      return t.CheckPriorityAdd(currentTcb, aPacket);
   }


   internal Tcb Release(TaskId anId)
   {
      Tcb t = table[(int)anId];
      if (t == null) return t;
      t.NotHeld();
      if (t.Priority > currentTcb.Priority) 
         return t;
      else 
         return currentTcb;
   }


   internal Tcb HoldCurrent()
   {
      ++holdCount;
      currentTcb.Held();
      return currentTcb.Link;
   }


   internal Tcb SuspendCurrent()
   {
      currentTcb.Suspended();
      return currentTcb;
   }

}



internal interface ISchedulerTask 
{
   Tcb run(Packet aPacket);
}


class DeviceTask : ISchedulerTask
{
   Packet v1;
   Scheduler s;

   public DeviceTask(Scheduler aScheduler)
   {  
      s = aScheduler;
   }

   public Tcb run(Packet aPacket)
   {
      if (aPacket == null) 
      {
         if (v1 == null) 
            return s.SuspendCurrent();
         Packet v = v1;
         v1 = null;
         return s.Queue(v);
      }
      else 
      {
         v1 = aPacket;
         //if (s.traceOn) Bench.Trace(aPacket.A1);
         return s.HoldCurrent();
      }
   }
}



class HandlerTask : ISchedulerTask
{
   Packet v1, v2;
   Scheduler s;

   public HandlerTask(Scheduler aScheduler)
   {  
      s = aScheduler;
   }

   public Tcb run(Packet aPacket)
   {
      if (aPacket != null) 
      {
         if (aPacket.Kind == Packet.Kinds.Work)
            v1 = aPacket.AddTo(v1);
         else 
            v2 = aPacket.AddTo(v2);
      }
      if (v1 != null) 
      {
         byte count = v1.A1;
         Packet v;
         if (count < Packet.dataSize )
         {
            if (v2 != null ) 
            {
               v = v2;
               v2 = v2.Link;
               v.A1 = v1.A2[count];
               v1.A1 = (byte)(count + 1);
               return s.Queue(v);
            }
         }
         else
         {
            v = v1;
            v1 = v1.Link;
            return s.Queue(v);
         }
      }
      return s.SuspendCurrent(); 
   }
}



class IdleTask : ISchedulerTask
{
   int v1, v2;
   Scheduler s;

   public IdleTask(Scheduler aScheduler, int aValue1, int aValue2)
   {
      v1 = aValue1;        
      v2 = aValue2;  
      s = aScheduler;
   }

   public Tcb run(Packet aPacket)
   {
      v2--;
      if (v2 == 0) 
         return s.HoldCurrent();

      if ( (v1 & 1) == 0 )
      {
         v1 = v1 >> 1;
         return s.Release(Scheduler.TaskId.DeviceA);
      }
      else
      {
         v1 = (v1 >> 1) ^ 0xD008;
         return s.Release(Scheduler.TaskId.DeviceB);
      }
   }
}



class WorkerTask : ISchedulerTask
{
   Scheduler.TaskId v1; 
   int v2;
   Scheduler s;

   public WorkerTask(Scheduler aScheduler, Scheduler.TaskId aValue1, int aValue2)
   {
      v1 = aValue1;        
      v2 = aValue2;  
      s = aScheduler;
   }

   public Tcb run(Packet aPacket)
   {
      if (aPacket == null) 
      {   return s.SuspendCurrent(); }
      else 
      {
         if (v1 == Scheduler.TaskId.HandlerA)
            v1 = Scheduler.TaskId.HandlerB;
         else
            v1 = Scheduler.TaskId.HandlerA;
         aPacket.Id  = v1;
         aPacket.A1 = 0;
         for (int i=0; i < Packet.dataSize; i++)
         {
            v2++;
            if (v2 > 26) v2 = 1;
            aPacket.A2[i] = (byte)('A' + v2 - 1);               
         } 
         return s.Queue(aPacket);
      }
   }
}




internal class Tcb 
{
   // Variables from spec
   private Tcb link;       // pointer to another tcb or nil
   private Scheduler.TaskId id;  // identifier (a small integer)
   private int pri;        // priority (a positive integer)
   private Packet wkq;      // list of Packets in the tasks work queue
   private int state;      // a 3 bit value giving the state
   private ISchedulerTask task;  



   internal Tcb(Tcb aTcb, Scheduler.TaskId anId, int aPriority, 
      Packet aWorkQueue, ISchedulerTask aTask
      )
   {
      link = aTcb;
      id = anId;
      pri = aPriority;
      wkq = aWorkQueue;   
      task = aTask;
      if (wkq == null) 
         state = SUSPENDED;
      else 
         state = SUSPENDED_RUNNABLE;
   }

   internal Tcb Link
   {
      get { return link; }
   }


   internal Scheduler.TaskId Id
   {
      get { return id; }
   }


   internal int Priority
   {
      get { return pri; }
   }


   internal Tcb CheckPriorityAdd(Tcb aTask, Packet aPacket)
   {
      if (wkq == null )
      {
         wkq = aPacket;
         state = state | RUNNABLE;
         if (pri > aTask.Priority) { return this; }
      }
      else 
      {
         wkq = aPacket.AddTo(wkq);
      }
      return aTask;
   }


   internal Tcb Run()
   {
      Packet packet;
      if (state == SUSPENDED_RUNNABLE)
      {
         packet = wkq;
         wkq = packet.Link;
         if (wkq == null) 
            state = RUNNING; 
         else 
            state = RUNNABLE; 
      }
      else
      {
         packet = null;
      }
      return task.run(packet);
   }


   // Named bit masks to access 3 bit state value.
   private static readonly int RUNNING = 0;
   private static readonly int RUNNABLE = BitVector32.CreateMask();
   private static readonly int SUSPENDED = BitVector32.CreateMask(RUNNABLE);
   private static readonly int HELD = BitVector32.CreateMask(SUSPENDED);
   private static readonly int SUSPENDED_RUNNABLE = SUSPENDED | RUNNABLE;
   private static readonly int NOT_HELD = ~HELD;

   internal void SetRunning()
   {
      state = RUNNING;
   }

   internal void Suspended()
   {
      state = state | SUSPENDED; 
   }

   internal void Held()
   {
      state = state | HELD;
   }

   internal void NotHeld()
   {
      state = state & NOT_HELD;
   }

   internal bool IsHeldOrSuspended()
   {
      return (state & HELD) != 0 || (state == SUSPENDED); 
   }
}



internal class Packet
{
   internal const int dataSize = 4;
   // Variables from spec
   private Packet link;     // pointer to the next Packet or nil
   private Scheduler.TaskId id;  // task or device that sent the packet
   private Kinds kind;      // part of the message
   private byte a1 = 0;     // part of the message
   private byte[] a2 = new byte[dataSize];  
               

   // Device & Work alias the enum Kinds values.
   internal enum Kinds {Device, Work};
   
   internal Packet(Packet aLink, Scheduler.TaskId anId, Kinds aKind)
   {
      link = aLink;
      id = anId;
      kind = aKind;
   }


   internal Packet AddTo(Packet aQueue)
   {
      Packet next, peek;
      link = null;
      if (aQueue == null) return this;
      next = aQueue;
      while ((peek = next.Link) != null)
         next = peek;
      next.Link = this;
      return aQueue;
   }


   internal Packet Link 
   {
      get { return link; }
      set { link = value; }
   }


   internal Scheduler.TaskId Id
   {
      get { return id; }
      set { id = value; }
   }


   internal Kinds Kind
   {
      get { return kind; }
   }


   internal byte A1
   {
      get { return a1; }
      set { a1 = value; }
   }


   internal byte[] A2
   {
      get { return a2; }
   }
}

Valid XHTML 1.0!