home | Richards Bench

MS C#.NET #20

Values are restored to registers, scheduler functions use register values and packet values.

Each implementation of the scheduler function is stored in a Tcb data member, as a delegate type.

C# language MS C#.NET version 1.0.3705

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

internal class Scheduler
{
   const bool TraceOn = false; 

   enum Kind {Device, Work}
   enum Task {Idle, Worker, HandlerA, HandlerB, DeviceA, DeviceB}
   const int MaxTasks = 10;

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



   class Packet
   {
      internal const int DATA_SIZE = 4;

      internal Packet link;    
      internal Task id;  
      internal Kind kind;      
      internal byte a1 = 0;    
      internal byte[] a2 = new byte[DATA_SIZE];  
                  
      internal Packet(Packet aLink, Task anId, Kind 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;
      }
   }


   class Tcb 
   {
      internal Tcb link;             
      internal Task id;  
      internal int pri;              
      internal Packet wkq;           
      internal int state;           
      internal object v1;
      internal object v2;
      internal Scheduler.Fn fn;  

      internal Tcb(Tcb aTcb, Task anId, int aPriority, 
         Packet aWorkQueue, object aV1, object aV2, Scheduler.Fn aFn
         )
      {
         link = aTcb;
         id = anId;
         pri = aPriority;
         wkq = aWorkQueue;   
         v1 = aV1;   
         v2 = aV2;       
         fn = aFn;
         if (wkq == null) 
            state = SUSPENDED;
         else 
            state = SUSPENDED_RUNNABLE;
      }
   }

   static Tcb[] table = new Tcb[MaxTasks];
   static Tcb list = null;
   static Tcb currentTcb;
   static Task currentId;
   static object v1;
   static object v2;

   static int queueCount = 0;
   static int holdCount = 0;

   // 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);
      
      Packet wkq;

      AddIdleTask(Task.Idle, 0, null, count);

      wkq = new Packet(null, Task.Worker, Kind.Work);
      wkq = new Packet(wkq, Task.Worker,  Kind.Work);
      AddWorkerTask(Task.Worker, 1000, wkq);

      wkq = new Packet(null, Task.DeviceA, Kind.Device);
      wkq = new Packet(wkq, Task.DeviceA, Kind.Device);
      wkq = new Packet(wkq, Task.DeviceA, Kind.Device);
      AddHandlerTask(Task.HandlerA, 2000, wkq);

      wkq = new Packet(null, Task.DeviceB, Kind.Device);
      wkq = new Packet(wkq, Task.DeviceB, Kind.Device);
      wkq = new Packet(wkq, Task.DeviceB, Kind.Device);
      AddHandlerTask(Task.HandlerB, 3000, wkq);

      AddDeviceTask(Task.DeviceA, 4000, null);
      AddDeviceTask(Task.DeviceB, 5000, null);

      Schedule();

      // Timing
      QueryPerformanceCounter(out stopTicks);
      QueryPerformanceFrequency(out frequency);
      // Timing
      FileInfo f = new FileInfo("testrun-times.xml");
      StreamWriter sw = f.AppendText();
      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}", queueCount);
      Console.WriteLine("HoldCount = {0}", holdCount);         
   }


   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]);
   }


   static void AddDeviceTask(Task anId, int aPriority, 
      Packet aQueue)
   {
      AddTask(anId, aPriority, aQueue, null, null, new Fn(DeviceFn) );
   }

   static void AddHandlerTask(Task anId, int aPriority, 
      Packet aQueue)
   {
      AddTask(anId, aPriority, aQueue, null, null, new Fn(HandlerFn) );
   }

   static void AddIdleTask(Task anId, int aPriority, 
      Packet aQueue, int aCount)
   {
      AddRunningTask(anId, aPriority, aQueue, 1, aCount, new Fn(IdleFn) );
   }

   static void AddWorkerTask(Task anId, int aPriority, 
      Packet aWorkQueue)
   {
      AddTask(anId, aPriority, aWorkQueue, 
         Task.HandlerA, 0, new Fn(WorkerFn) );
   }

   static void AddTask(Task anId, int aPriority,
      Packet aWorkQueue, object aV1, object aV2, Fn aFn)
   {
      currentTcb = new Tcb(list, anId, aPriority, 
         aWorkQueue, aV1, aV2, aFn);
      list = currentTcb;
      table[(int)anId] = currentTcb;
   }

   static void AddRunningTask(Task anId, int aPriority,
      Packet aWorkQueue, object aV1, object aV2, Fn aFn)
   {
      AddTask(anId, aPriority, aWorkQueue, aV1, aV2, aFn);
      currentTcb.state = RUNNING;
   }



   delegate Tcb Fn(Packet aPacket);

   static Tcb DeviceFn(Packet aPacket)
   {
      if (aPacket == null) 
      {
         if (v1 == null) 
            return SuspendCurrent();
         Packet v = (Packet)v1;
         v1 = null;
         return Queue(v);
      }
      else 
      {
         v1 = aPacket;
         if (TraceOn) Trace(aPacket.a1);
         return HoldCurrent();
      }
   }

   static Tcb HandlerFn(Packet aPacket)
   {
      if (aPacket != null) 
      {
         if (aPacket.kind == Kind.Work)
            v1 = aPacket.AddTo((Packet)v1);
         else 
            v2 = aPacket.AddTo((Packet)v2);
      }
      if (v1 != null) 
      {
         byte count = ((Packet)v1).a1;
         Packet v;
         if (count < Packet.DATA_SIZE )
         {
            if (v2 != null ) 
            {
               v = (Packet)v2;
               v2 = v.link;
               v.a1 = ((Packet)v1).a2[count];
               ((Packet)v1).a1 = (byte)(count + 1);
               return Queue(v);
            }
         }
         else
         {
            v = (Packet)v1;
            v1 = v.link;
            return Queue(v);
         }
      }
      return SuspendCurrent(); 
   }

   static Tcb IdleFn(Packet aPacket)
   {
      v2 = (int)v2 - 1;
      if ((int)v2 == 0) 
         return HoldCurrent();

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

   static Tcb WorkerFn(Packet aPacket)
   {
      if (aPacket == null) 
      {   return SuspendCurrent(); }
      else 
      {
         if ((Task)v1 == Task.HandlerA)
            v1 = Task.HandlerB;
         else
            v1 = Task.HandlerA;
         aPacket.id  = (Task)v1;
         aPacket.a1 = 0;
         for (int i=0; i < Packet.DATA_SIZE; i++)
         {
            v2 = (int)v2 + 1;
            if ((int)v2 > 26) v2 = 1;
            aPacket.a2[i] = (byte)('A' + (int)v2 - 1);               
         } 
         return Queue(aPacket);
      }
   }

   static Tcb HoldCurrent()
   {
      ++holdCount;
      currentTcb.state = currentTcb.state | HELD;
      return currentTcb.link;
   }

   static Tcb Queue(Packet aPacket)
   {
      Tcb t = table[(int)aPacket.id];
      if (t == null) return t;
      queueCount++;
      aPacket.link = null;
      aPacket.id = currentId;

      if (t.wkq == null )
      {
         t.wkq = aPacket;
         t.state = t.state | RUNNABLE;
         if (t.pri > currentTcb.pri) { return t; }
      }
      else 
      {
         aPacket.AddTo(t.wkq);
      }
      return currentTcb;
   }


   static Tcb Release(Task anId)
   {
      Tcb t = table[(int)anId];
      if (t == null) return t;
      t.state = t.state & NOT_HELD;
      if (t.pri > currentTcb.pri) 
         return t;
      else 
         return currentTcb;
   }


   static void Schedule()
   {
      Packet packet;
      currentTcb = list;
      while (currentTcb != null)
      {
         if (currentTcb.state == SUSPENDED_RUNNABLE)
         {
            packet = currentTcb.wkq;
            currentTcb.wkq = packet.link;
            if (currentTcb.wkq == null) 
               currentTcb.state = RUNNING; 
            else 
               currentTcb.state = RUNNABLE; 
         }
         else
         {
            packet = null;
         }

         if ((currentTcb.state & HELD) != 0 || 
            (currentTcb.state == SUSPENDED))
         { 
            currentTcb = currentTcb.link;
         }
         else
         {
            currentId = currentTcb.id;

            v1 = currentTcb.v1;
            v2 = currentTcb.v2;
            if (TraceOn) Trace((byte)((int)'0' + currentId + 1));
            Tcb nextTcb = currentTcb.fn(packet);
            currentTcb.v1 = v1;
            currentTcb.v2 = v2;

            currentTcb = nextTcb;
         }
      }
   }

   static Tcb SuspendCurrent()
   {
      currentTcb.state = currentTcb.state | SUSPENDED;
      return currentTcb;
   }
}

Valid XHTML 1.0!