home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 8
/
CDASC08.ISO
/
VRAC
/
CCL110JE.ZIP
/
COROUTIN.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1993-06-19
|
10KB
|
341 lines
//--------------------------------------------------------------------------
//
// COROUTINE.CPP: body of DOS coroutine library.
// Copyright (c) J.English 1993.
// Author's address: je@unix.brighton.ac.uk
//
// Permission is granted to use copy and distribute the
// information contained in this file provided that this
// copyright notice is retained intact and that any software
// or other document incorporating this file or parts thereof
// makes the source code for the library of which this file
// is a part freely available.
//
//--------------------------------------------------------------------------
//
// Note: this library is highly DOS specific and hence non-portable.
// It also involves the use of assembly language and interrupt
// functions, so it is also very compiler-specific and will need
// modification for use with compilers other than Borland C++.
//
// Revision history:
// 1.0 April 1993 Initial coding
// 1.1 June 1993 Added wait, terminate; tidied private data
// Minor changes to schedule.
//
//--------------------------------------------------------------------------
#include "coroutine.h"
#include <dos.h>
//--------------------------------------------------------------------------
//
// Constants.
//
const unsigned MIN_STACK = 512; // minimum stack size
//--------------------------------------------------------------------------
//
// Global (static) variables.
//
static CoroutineManager* current = 0; // current coroutine
static CoroutineManager* nextentry = 0; // current coroutine
static Coroutine* mainitem = 0; // coroutine for execution of "main"
static unsigned mainsetup = 0; // flag set when constructing "main"
static volatile unsigned count = 0; // number of active coroutines
//--------------------------------------------------------------------------
//
// Class MainCoroutine.
//
// A minimal derivation of Coroutine used for the main task.
//
class MainCoroutine : public Coroutine
{
public:
MainCoroutine () : Coroutine (MIN_STACK) { }
protected:
virtual void main () { }
};
//--------------------------------------------------------------------------
//
// Class CoroutineManager.
//
// This is a support class used for maintaining the coroutine queue.
// The queue is represented as a circular list of CoroutineManagers.
//
class CoroutineManager
{
public:
CoroutineManager (Coroutine* t);
~CoroutineManager () { delete stack; }
CoroutineManager* next; // next entry in list
CoroutineManager* prev; // previous entry in list
Coroutine* coroutine; // coroutine for this entry
char* stack; // coroutine stack area
unsigned sp,ss; // coroutine stack pointer
static void far start (Coroutine* t); // execute coroutine and then die
static void create (); // register coroutine creation
static void destroy (); // register coroutine destruction
static void interrupt schedule (); // schedule next coroutine
};
//--------------------------------------------------------------------------
//
// CoroutineManager::CoroutineManager.
//
// Constructor for class CoroutineManager.
//
inline CoroutineManager::CoroutineManager (Coroutine* t)
{
next = prev = this;
coroutine = t;
stack = 0;
}
//--------------------------------------------------------------------------
//
// CoroutineManager::start.
//
// This is used to execute a coroutine by executing "main" and
// then terminating it.
//
void far CoroutineManager::start (Coroutine* t)
{
t->main ();
t->terminate ();
}
//--------------------------------------------------------------------------
//
// CoroutineManager::create.
//
// Register the creation of a coroutine and initialise the system
// if it is the first coroutine to be created.
//
void CoroutineManager::create ()
{
//--- increment the number of coroutines & initialise if necessary
if (count++ == 0)
{
//--- create the main coroutine (bodged using "mainsetup")
mainsetup = 1;
mainitem = new MainCoroutine;
mainsetup = 0;
mainitem->state = Coroutine::RUNNING;
//--- select main coroutine as the current one
current = mainitem->entry;
}
}
//--------------------------------------------------------------------------
//
// CoroutineManager::destroy.
//
// Register the destruction of a coroutine and stop scheduling if
// the last coroutine is being destroyed.
//
void CoroutineManager::destroy ()
{
//--- decrement coroutine count
count--;
//--- terminate main coroutine when no others left
if (count == 0)
{ current = 0;
mainitem->state = Coroutine::TERMINATED;
delete mainitem;
}
}
//--------------------------------------------------------------------------
//
// CoroutineManager::schedule.
//
// Save the current coroutine and restore another one.
//
void interrupt CoroutineManager::schedule ()
{
//--- don't schedule if no coroutines present
if (current == 0)
return;
//--- switch context to "nextentry"
asm { cli; }
_SP = _BP;
current->sp = _SP;
current->ss = _SS;
current = nextentry;
_SS = current->ss;
_SP = current->sp;
_BP = _SP;
asm { sti; }
}
//--------------------------------------------------------------------------
//
// Coroutine::Coroutine.
//
// Construct a new coroutine. All new coroutines are kept in limbo
// until they are explicitly started using "run".
//
Coroutine::Coroutine (unsigned stacksize)
: entry (new CoroutineManager (this)),
state (TERMINATED)
{
//--- leave coroutine terminated if coroutine manager not allocated
if (entry == 0)
return;
//--- set up new coroutine (for all but main coroutine)
entry->stack = 0;
if (!mainsetup)
{
//--- allocate coroutine stack area
if (stacksize < MIN_STACK)
stacksize = MIN_STACK;
entry->stack = new char [stacksize];
if (entry->stack == 0)
return;
//--- register coroutine creation
CoroutineManager::create ();
//--- create coroutine stack
unsigned* stk = (unsigned*)(entry->stack + stacksize);
*--(Coroutine**)stk = this; // "this" pointer for call to "start"
stk -= 2; // dummy return address from "start"
*--stk = _FLAGS; // flags
*--stk = FP_SEG (&CoroutineManager::start);
*--stk = FP_OFF (&CoroutineManager::start);
stk -= 5; // ax, bx, cx, dx, es
*--stk = _DS; // ds
stk -= 2; // si, di
*--stk = _BP; // bp
entry->ss = FP_SEG (stk);
entry->sp = FP_OFF (stk);
//--- set coroutine state
state = CREATED;
}
}
//--------------------------------------------------------------------------
//
// Coroutine::~Coroutine.
//
// Wait for current coroutine to terminate, and then destroy it.
//
Coroutine::~Coroutine ()
{
//--- wait for coroutine to terminate
wait ();
//--- register coroutine destruction (normal coroutines only)
if (this != mainitem)
CoroutineManager::destroy ();
//--- delete associated structures
delete entry;
}
//--------------------------------------------------------------------------
//
// Coroutine::run.
//
// Start a new coroutine running.
//
int Coroutine::run ()
{
//--- don't start coroutine if not newly created
if (state != CREATED)
return 0;
//--- link coroutine into queue
entry->next = current->next;
entry->prev = current;
entry->prev->next = entry;
entry->next->prev = entry;
//--- start coroutine running
state = RUNNING;
nextentry = entry;
CoroutineManager::schedule ();
return 1;
}
//--------------------------------------------------------------------------
//
// Coroutine::terminate.
//
// This terminates a coroutine by removing it from the queue. If
// a coroutine calls it to terminate itself, the next coroutine
// is selected and then scheduled.
//
void Coroutine::terminate ()
{
//--- check if coroutine is running and reset its state
int running = (state == RUNNING);
state = TERMINATED;
if (!running)
return;
//--- select next coroutine to run if necessary
if (entry == current)
nextentry = current->next;
//--- detach coroutine from the queue
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
//--- schedule next coroutine if necessary
if (entry == current)
CoroutineManager::schedule ();
}
//--------------------------------------------------------------------------
//
// Coroutine::wait.
//
// Wait for a coroutine to terminate. If it's the current coroutine
// waiting for itself to terminate, grant its wish by terminating it.
// If it hasn't been started yet, terminate it immediately.
//
void Coroutine::wait ()
{
if (current == entry)
terminate ();
if (state == CREATED)
state = TERMINATED;
while (state != TERMINATED)
pause ();
}
//--------------------------------------------------------------------------
//
// Coroutine::pause.
//
// Schedule the next available coroutine.
//
void Coroutine::pause ()
{
nextentry = current->next;
CoroutineManager::schedule ();
}