///////////////////////////////////////////////////////////////////////////// // 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 #include // operating system test #if !(defined(__MSDOS__)||defined(__linux__)||defined(__WIN32__)||defined(__FreeBSD__)||defined(__i386)) # error "process.cc is not implemented in this system" #endif /* !____ */ //////////////////////////////////////////////////////////////////////////// // 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