/////////////////////////////////////////////////////////////////////////////
// process.cc
//
// SIMLIB version: 2.18
// Date: 2004-01-25
//
// Copyright (c) 1991-2004 Petr Peringer
//
// This library is licensed under GNU Library GPL. See the file COPYING.
//
//
// implementation of interruptable functions (non-preemptive
// threads/coroutines)
// this module is the only nonportable module in SIMLIB
// we need to save/restore process stack and working setjmp/longjmp
// NOTE: on Sparc it is not enough (probably due register windows)
// NOTE2: on X86_64 it is not working (unknown problems)
//
// TODO: add implementation with stack switching
// TODO: add implementation with preemptive threads
////////////////////////////////////////////////////////////////////////////
// interface
//
#include "simlib.h"
#include "internal.h"
#include <csetjmp>
#include <cstring>
// operating system test
#if !(defined(__MSDOS__)||defined(__linux__)||defined(__WIN32__)||defined(__FreeBSD__)||defined(__i386))
# error "process.cc is not implemented in this system"
#endif /* !__<OS>__ */
////////////////////////////////////////////////////////////////////////////
// implementation
//
SIMLIB_IMPLEMENTATION
////////////////////////////////////////////////////////////////////////////
// machine dependent macros:
// this works on i386+ platforms
/////////////////////////////////////////////////////////////////////////////
#if defined(__BCPLUSPLUS__) || defined(__TURBOC__) // Borland compilers
/////////////////////////////////////////////////////////////////////////////
# if defined(__WIN32__) // 32 bit
# define _GET_STACK_PTR(v) { v = (char*)_ESP; }
# define _SHIFT_STACK_PTR(v) { (char*)_ESP -= v; }
# else // 16 bit (OLD, not supported)
# if defined(__LARGE__) || defined(__COMPACT__) || defined(__HUGE__)
# define _GET_STACK_PTR(v) { v = (char far *)MK_FP(_SS,_SP); }
# else
# define _GET_STACK_PTR(v) { v = (char*)_SP; }
# endif
# define _SHIFT_STACK_PTR(v) { _SP -= v; }
# endif
/////////////////////////////////////////////////////////////////////////////
#elif defined(__GNUC__) // GNU C++ compiler
/////////////////////////////////////////////////////////////////////////////
# ifdef __sun__ // Sparc
# error "SPARC implementation of Process::Behavior not ready! "
// this code doesn't work --- the problem: register windows not saved?
# define _GET_STACK_PTR(v) { asm("st %%sp, %0" : "=m" (v)); }
static volatile int _THREAD_TMP;
# define _SHIFT_STACK_PTR(v) { _THREAD_TMP = v; \
asm("sub %%sp, %0, %%sp" : : "r" (_THREAD_TMP)); \
}
# elif defined(__i386__) // i386+ expected...
# define _GET_STACK_PTR(v) { asm("movl %%esp,%0":"=r" (v)); }
static volatile int _THREAD_TMP;
# define _SHIFT_STACK_PTR(v) { _THREAD_TMP=v; \
asm("movl %0,%%eax": : "m" (_THREAD_TMP)); \
asm("subl %eax,%esp"); \
}
# else // other...
# error "process.cc: this platform is not supported"
# endif
/////////////////////////////////////////////////////////////////////////////
#else // other compilers not supported
/////////////////////////////////////////////////////////////////////////////
# error "process.cc: this compiler is not supported"
/////////////////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
// machine independent code:
////////////////////////////////////////////////////////////////////////////
// memory allocation:
//
# define _THREAD_ALLOC(p,size) { \
p = new char[size]; \
if(!p) SIMLIB_error(MemoryError); /* for old compilers */ \
}
# define _THREAD_FREE(p) { delete [] p; p = 0; }
////////////////////////////////////////////////////////////////////////////
// stack manipulations:
//
// save the stack state of thread
static volatile char * volatile _THREAD_sp;
#define THREAD_SAVE_STACK() \
{ \
_GET_STACK_PTR(_THREAD_sp); \
_size = (unsigned)(_StackBase-_THREAD_sp); \
_THREAD_ALLOC(_p, _size); \
DEBUG(DBG_THREAD,("THREAD_SAVE_STACK(%p,%uB)", _StackBase, _size)); \
memcpy(_p, (void*)(_StackBase-_size), _size); \
}
// restore the stack state of thread
#define THREAD_RESTORE_STACK() \
{ \
DEBUG(DBG_THREAD,("THREAD_RESTORE_STACK(%p,%uB)",_StackBase,_size));\
_SHIFT_STACK_PTR(_size); \
DEBUG(DBG_THREAD,("THREAD_RESTORE_STACK -- after shift")); \
memcpy((void*)(_StackBase-_size), _p, _size); \
DEBUG(DBG_THREAD,("THREAD_RESTORE_STACK -- after memcpy")); \
_THREAD_FREE(_p); \
}
// mark the base of stack
#define THREAD_MARK_STACK() \
{ \
_GET_STACK_PTR(_StackBase); \
DEBUG(DBG_THREAD,("THREAD_MARK_STACK(%p)", _StackBase)); \
}
////////////////////////////////////////////////////////////////////////////
// THREADS implementation:
// prepare thread for running
#define THREAD_INIT() \
{ \
_p = 0; \
_status = _PREPARED; \
}
// destroy thread
#define THREAD_DONE() \
{ \
if (_p) { _THREAD_FREE(_p); } \
_status = _TERMINATED; \
}
// interrupt thread (must be current)
#define THREAD_INTERRUPT() \
{ \
/*if(!isCurrent()) SIMLIB_error("Can't interrupt...");*/ \
_status = _INTERRUPTED; \
THREAD_SAVE_STACK(); \
if(!setjmp(_ProcStatus)) longjmp(_StateBuf,1); \
}
// end of any thread
#define THREAD_TERMINATE() \
{ \
_status = _TERMINATED; \
if(isCurrent()) longjmp(_StateBuf,2); \
else if(IsAllocated()) delete this; /* remove passive process */ \
}
////////////////////////////////////////////////////////////////////////////
// global variables
//
static jmp_buf _StateBuf; // stack state before dispatch
static volatile char * volatile _StackBase=0; // start of stack data area
////////////////////////////////////////////////////////////////////////////
// constructor
//
Process::Process(Priority_t p) : Entity(p) {
#ifdef NEW_WULIST
_wait_until = false;
#endif
dprintf(("Process::Process(%d)", p));
THREAD_INIT(); // initialize
}
////////////////////////////////////////////////////////////////////////////
/// Process destructor
/// removes process from queue/calendar/waituntil list
//
Process::~Process()
{
dprintf(("Process::~Process()"));
THREAD_DONE(); // free memory
#ifdef NEW_WULIST
if (_wait_until) // process is in WaitUntil list
_WaitUntilRemove();
#endif
if (where() != 0) { // Entity linked in queue
// no warning
Out(); // remove from queue
}
if (!Idle()) { // process is scheduled
SIMLIB_warning("Destructing active process");
SQS::Get(this); // remove from calendar
}
}
////////////////////////////////////////////////////////////////////////////
// Interrupt -- ensures WaitUntil tests
//
void Process::Interrupt()
{
dprintf(("%s.Interrupt()", Name()));
SQS::ScheduleFirst(this); // continue after other processes WaitUntil checks
THREAD_INTERRUPT();
}
////////////////////////////////////////////////////////////////////////////
// Activate -- activate now
//
void Process::Activate()
{
Entity::Activate();
if (!isCurrent()) return;
THREAD_INTERRUPT();
}
void Process::Activate(double t) // Activate in time t
{
Entity::Activate(t);
if (!isCurrent()) return;
THREAD_INTERRUPT();
}
////////////////////////////////////////////////////////////////////////////
// Wait -- wait fo dtime
//
void Process::Wait(double dtime)
{
dprintf(("%s.Wait(%g)",Name(),dtime));
Activate(double(Time)+dtime); // scheduling
if (!isCurrent()) return;
THREAD_INTERRUPT();
}
////////////////////////////////////////////////////////////////////////////
// Seize -- seize facility
//
void Process::Seize(Facility &f, ServicePriority_t sp /* = 0 */)
{
(&f)->Seize(this,sp); // use polymorphic interface
}
////////////////////////////////////////////////////////////////////////////
// Release -- release facility
//
void Process::Release(Facility &f)
{
(&f)->Release(this);
}
////////////////////////////////////////////////////////////////////////////
// Enter --
//
void Process::Enter(Store &s, unsigned long ReqCap)
{
(&s)->Enter(this,ReqCap); // polymorphic
}
////////////////////////////////////////////////////////////////////////////
// Leave --
//
void Process::Leave(Store &s, unsigned long ReqCap)
{
(&s)->Leave(ReqCap);
}
////////////////////////////////////////////////////////////////////////////
// Into --- inserting into queue
//
void Process::Into(Queue &q)
{
if(where()!=0) Out(); // if already in queue then remove ### +warning ???
(&q)->Insert(this); // polymorphic insert
}
////////////////////////////////////////////////////////////////////////////
// Passivate - deactivation of process
//
void Process::Passivate() // ***** process interrupt *****
{
Entity::Passivate();
if (!isCurrent()) return; // can be passivated by other process
THREAD_INTERRUPT();
}
////////////////////////////////////////////////////////////////////////////
// Terminate -- end of process
//
void Process::Terminate()
{
dprintf(("%s.Terminate()", Name()));
if(!Idle()) SQS::Get(this); // remove from calendar
THREAD_TERMINATE(); // ukonceni aktivniho procesu
}
////////////////////////////////////////////////////////////////////////////
// _Run - process (re)activation
//
void Process::_Run()
{
dprintf(("%s._Run()", Name()));
if(_status==_INTERRUPTED || _status==_PREPARED)
{
THREAD_MARK_STACK(); // mark the stack base address
if(!setjmp(_StateBuf)) // save processor status
{ // return after process interrupt
_status = _RUNNING;
if(_p)
{ // restart - process was run
// ... have saved status
DEBUG(DBG_THREAD,(" - Thread CONTINUE "));
THREAD_RESTORE_STACK(); // restore status
longjmp(_ProcStatus,1); // continue - reactivation
// never reach this point // ??
SIMLIB_internal_error();
}
else
{
DEBUG(DBG_THREAD,(" - Thread START "));
Behavior(); // start of behavior description
DEBUG(DBG_THREAD,(" - Thread END "));
_status = _TERMINATED;
if (!Idle()) SQS::Get(this); // remove from calendar
if (IsAllocated()) delete this; // destroy
}
}
else // return from Behavior() Interrupt|Terminate
if (Terminated() && IsAllocated()) delete this; // destroy
}
else
SIMLIB_error(ProcessNotInitialized);
}
////////////////////////////////////////////////////////////////////////////
// Process::Name --- name of the process
//
const char *Process::Name()
{
const char *name = SimObject::Name();
if(*name) return name; // has explicit name
else return SIMLIB_create_name("Process#%lu", _Ident);
}
// end
syntax highlighted by Code2HTML, v. 0.9.1