Listing 2 Member function definitions for class Cschlr

#include <stdlib.h>
#include <stdio.h>
#include "cschlr.hpp"

const int MAIN = CSC_NO_THREAD;        //main prgram context
const int DUMMY = MAIN + 1;            //temporary context
const int EMPTY =-1;                   //no delayed thread marker

/* The following static varaible is used to facilitate the
   implementation of Kill. This limits the number of Cschlr
   object per Windows application to 1 only */
static Cschlr *schlr;

/* Used for the implicit termination of a thread when
   execution falls through the thread function body */
static void Kill()
{
  schlr->Suicide();
}

/* Used to check if it is time to wake up the sleeping threads. */
void Cschlr::WakeUp()
{
  time_t current;
  schlr_table *ptr;

  /* check time and dispatch thread */
  time(&current);
  while ((head != EMPTY) &&
        (current >= (ptr = (table + head))->wakeuptime))
    {
    readyQ.Enqueue(head);
    head = ptr->next;
    ptr->next = EMPTY;
    }
}

/* Used to perform a task switch. Control is passed
   back to the Windows PeekMessage loop */
void Cschlr::Switch(Csemq *sem)
{
   WakeUp();
   sem->Enqueue(nRunning);
   task[nRunning]->Transfer(*task[MAIN]);
}

/* Constructor to set up the task table */
Cschlr::Cschlr()
{
  nTask = 0;
  nRunning = CSC_NO_THREAD;
  head = EMPTY;
  table = new schlr_table[CSC_NO_THREAD];
  schlr = this;

  //used to resume main program
  task[MAIN] = new Cthread(NULL, 0, NULL, 0);

  //used in Suicide to switch task
  task[DUMMY] = new Cthread(NULL, 0, NULL, 0);
}

/* Empty destructor */
Cschlr::~Cschlr()
{
  //do nothing
}

/* Used to create a thread and pass to it a user variable */
int Cschlr::CreateThread(THDFN func, int stacksize, void *param)
{
  THDFN retaddr;   //these two variables must appear
  void *ptr;       //together for CreateThread to work
  int i;

  int thread = CSC_NO_THREAD;

  if (nTask < CSC_NO_THREAD + 1)
    {
    for (i = 0; i < CSC_NO_THREAD + 1; i++)
      {
      if (task[i] == NULL)
        break;
      }
    retaddr = Kill;     //set return address to point to Kill
    ptr = param;        //set user parameter pointer
    task[i] = new Cthread(func, stacksize, (int *) &retaddr, 4);
    readyQ.Enqueue(i);
    thread = nTask++;
    }

  return (thread);
}

/* Used by a calling thread to commit suicide or self-terminate */
void Cschlr::Suicide()
{
   int current;

   current = nRunning;
   nRunning = readyQ.Dequeue();
   delete task[current];
   task[current] = NULL;
   nTask--;
   task[DUMMY]->Transfer(*task[nRunning]);
}

/* Used to create a semaphore object */
Csemq* Cschlr::CreateSem(long lValue)
{
  return (new Csemq(lValue));
}

/* Used to destroy a semaphore object */
void Cschlr::DestroySem(Csemq* sem)
{
  delete sem;
}

/* Used to signal a semaphore */
void Cschlr::Signal(Csemq *sem, long 1MaxCount)
{
  if (sem->GetType() == CST_COUNT)
    {
    if (sem->GetCount() < lMaxCount)
      sem->UpdateCount(1);
    }
  else
    {
    readyQ.Enqueue(sem->Dequeue());
    }
}

/* Used to wait on a semaphore */
void Cschlr::Wait(Csemq *sem)
{

  if (sem->GetType() == CST_COUNT)
    {
    if (!sem->GetCount())
      {
      //move running thread to semaphore queue and
      //switch to a ready-to-run thread
      Switch(sem);
      }
    else
      //decrement sempahore count and continue execution
      sem->UpdateCount(-1);
    }
  else
    {
    //move running thread to semaphore queue and
    //switch to a ready-to-run thread
    Switch(sem);
    }
}

/* Used to give up the cpu voluntarily */
void Cschlr::Preempt()
{
  Switch(&readyQ);
}
/* Used to put the calling thread to sleep for the
   specified number of seconds */
void Cschlr::Sleep(long lseconds)
{
  time_t current;
  schlr_table *ptr;
  schlr_table *thread;
  int prev;
  int next;

  current = time(0) + lSeconds; //init wakeup time

  if (head == EMPTY)
    {                           //no thread delayed
    head = nRunning;
    next = EMPTY;
    }
  else                          //scan delayed threads
    {
    prev = EMPTY;
    next = head;
    while ((next != EMPTY) && (current >=
       (ptr = (table + next))->wakeuptime))
      {
      prev = next;
      next = ptr->next;
      }
    if (prev == EMPTY)
      head = nRunning;
    else
      (table + prev)->next = nRunning;
    }

  ptr = (table + nRunning);
  ptr->wakeuptime = current;
  ptr->next = next;

  Switch(&waitQ);

}

/* Used to retrieve the status of a semaphore */
void Cschlr::GetSemStates(Csemq *sem, long &lCount, int &fWait)
{
  if (sem->GetType() == CST_COUNT)
    {
    1Count = sem->GetCount();
    fWait = 0;
    }
  else
    {
    1Count = 0;
    fWait = 1;
    }
}

/* Used by the main PeekMessage loop to multiplex the
   cpu among a number of threads */
void Cschlr::Run()
{
  if ((nRunning = readyQ.Dequeue()) != CSC_NO_THREAD)
    task[MAIN]->Transfer(*task[nRunning]);
  else
    WakeUp();
}

// End of File