#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(¤t);
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