SIMLIB/C++  3.07
process.cc
Go to the documentation of this file.
1 /////////////////////////////////////////////////////////////////////////////
2 //! \file process.cc Implementation of quasiparallel processes (coroutines)
3 //!
4 //! This module contains implementation of cooperative multitasking processes
5 //! for discrete simulation
6 
7 //
8 // Copyright (c) 1991-2018 Petr Peringer
9 //
10 // This library is licensed under GNU Library GPL. See the file COPYING.
11 //
12 
13 //
14 // Implementation of interruptable functions (non-preemptive threads/coroutines)
15 //
16 // This module is the only nonportable module in SIMLIB
17 // We need code to save/restore process stack contents and working setjmp/longjmp
18 // This approach has advantage in small memory requirements.
19 //
20 // Supported CPU architectures: i386+, x86_64
21 //
22 // WARNING: dirty hack inside
23 //
24 
25 // DONE: mark SP, alloc stack [size=runtime-option], change SP, call global function without
26 // params/locals, call Current->Behavior (uses new stack for this)
27 // return: set SP back, ...
28 // Process destructor: free stack
29 // TODO: add implementation with stack switching (not copying)
30 // as compile-time option
31 // TODO: add implementation using C++20 coroutines?
32 
33 
34 ////////////////////////////////////////////////////////////////////////////
35 // interface
36 //
37 
38 #include "simlib.h"
39 #include "internal.h"
40 
41 #include <csetjmp>
42 #include <cstring>
43 
44 // basic operating system test
45 #if !(defined(__MSDOS__)||defined(__linux__)|| \
46  defined(__WIN32__)||defined(__FreeBSD__))
47 # error "module process.cc is not implemented on this operating system"
48 #endif
49 
50 // basic CPU test
51 #if !(defined(__i386__)||defined(__x86_64__))
52 # error "module process.cc is not ported to this processor architecture"
53 #endif
54 
55 
56 ////////////////////////////////////////////////////////////////////////////
57 // implementation
58 //
59 
60 namespace simlib3 {
61 
63 
64 ////////////////////////////////////////////////////////////////////////////
65 // Machine dependent macros for direct stack pointer manipulation:
66 //
67 // GET_STACK_PTR(v) v = <stack pointer> (not used)
68 // SET_STACK_PTR(v) <stack pointer> = v
69 //
70 /////////////////////////////////////////////////////////////////////////////
71 #if defined(__BCPLUSPLUS__) || defined(__TURBOC__) // Borland compilers
72 
73 // this works on i386+ platforms
74 # if defined(__WIN32__) // 32 bit WINDOWS
75 # define GET_STACK_PTR(v) { v = (char*)_ESP; }
76 # define SET_STACK_PTR(v) { _ESP = v; }
77 # else // 16 bit MSDOS (obsolete, not supported)
78 # if defined(__LARGE__) || defined(__COMPACT__) || defined(__HUGE__)
79 # define GET_STACK_PTR(v) { v = (char far *)MK_FP(_SS,_SP); }
80 # else
81 # define GET_STACK_PTR(v) { v = (char*)_SP; }
82 # endif
83 # define SET_STACK_PTR(v) { _SP = v; }
84 # endif
85 
86 #elif defined(__GNUC__) // GNU C++ compiler
87 
88 # if defined(__i386__) // 32bit: i386+ expected...
89 
90 # define GET_STACK_PTR(v) { asm("movl %%esp,%0":"=r" (v)); }
91 # define SET_STACK_PTR(v) { asm("movl %0,%%eax": : "m" (v)); \
92  asm("movl %eax,%esp"); \
93  }
94 
95 # elif defined(__x86_64__) // 64bit: Athlon64, ...
96 
97 # define GET_STACK_PTR(gvar) { asm("movq %%rsp,%0": "=r" (gvar)); }
98 # define SET_STACK_PTR(gvar) { asm("movq %0,%%rsp": : "m" (gvar)); }
99 
100 # else // Other CPU ...
101 # error "process.cc: Unsupported CPU architecture"
102 # endif
103 
104 #else // Other compilers not supported
105 
106 # error "process.cc: Unsupported compiler"
107 
108 #endif
109 /////////////////////////////////////////////////////////////////////////////
110 
111 
112 ////////////////////////////////////////////////////////////////////////////
113 // machine independent code:
114 ////////////////////////////////////////////////////////////////////////////
115 
116 /**
117  * internal structure for storing of process context
118  * @ingroup process
119  */
120 struct P_Context_t {
121  jmp_buf status; //!< stored SP, IP, and other registers
122  size_t size; //!< size of following array (allocated on heap)
123  char stack[1]; //!< stack contents saved
124 };
125 
126 ////////////////////////////////////////////////////////////////////////////
127 // global variables (should be volatile)
128 static jmp_buf P_DispatcherStatusBuffer; //!< setjmp() state before dispatch
129 static char *volatile P_StackBase = 0; //!< global start of stack area
130 static char *volatile P_StackBase2 = 0; //!< for checking start of stack
131 
132 static P_Context_t *volatile P_Context = 0; //!< temporary global process state
133 static volatile size_t P_StackSize = 0; //!< temporary global stack size
134 
135 ////////////////////////////////////////////////////////////////////////////
136 // Support for THREADS implementation debugging:
137 ////////////////////////////////////////////////////////////////////////////
138 
139 #define EXTRA_DEBUG 0 // 0==off, 1==on
140 
141 #if EXTRA_DEBUG
142 
143 /// \def THREAD_DEBUG
144 /// Macro for detailed debugging of Process switching code
145 #define THREAD_DEBUG(n) THREAD_DEBUG_f(n)
146 
147 // FIXME
148 // We use extra debug print macro, because for 0xC we can not print float
149 // format used in DEBUG macro (prints Time using %g) -> segfault
150 // [%d format is O.K.] [tested with GCC version 7, 8, 11]
151 # define P_DEBUG(c,f) \
152  do{ if( SIMLIB_debug_flag & (c) ) { \
153  _Print("EXTRA_PROCESS_DEBUG:"); \
154  _Print f; _Print("\n"); \
155  } }while(0)
156 
157 /// \fn THREAD_DEBUG_f
158 /// We need this non-inline function because optimization of volatile global
159 /// variable access after longjmp -> setjmp transition in 32bit i686 mode of
160 /// GCC 7 and newer versions
161 /// TODO: +string table
162 static void THREAD_DEBUG_f(int n) __attribute__ ((noinline));
163 static void THREAD_DEBUG_f(int n) {
164  switch(n) {
165  case 1:
166  P_DEBUG(DBG_THREAD,("| THREAD_INTERRUPT ***** begin *****"));
167  P_DEBUG(DBG_THREAD,("| %d) - stack size = %p", n, P_StackSize));
168  break;
169  case 2:
170  break;
171  case 3:
172  break;
173  case 4:
174  P_DEBUG(DBG_THREAD,("| %d) THREAD_SAVE_STACK: before setjmp() - context=%p", n, P_Context));
175  break;
176  case 5:
177  P_DEBUG(DBG_THREAD,("| %d) THREAD_SAVE_STACK: after setjmp() - context=%p", n, P_Context));
178  break;
179  case 6:
180  P_DEBUG(DBG_THREAD,("| %d) THREAD_RESTORE: longjmp() back - context=%p", n, P_Context));
181  break;
182  case 7:
183  P_DEBUG(DBG_THREAD,("| THREAD_INTERRUPT ***** end *****"));
184  break;
185 
186  case 0xB:
187  P_DEBUG(DBG_THREAD,("| b) THREAD Shift SP to %p ", P_StackBase2));
188  break;
189  case 0xC:
190  P_DEBUG(DBG_THREAD,("| c) THREAD Before longjmp(%p,1)", P_Context->status));
191  break;
192 
193  default:
194  P_DEBUG(DBG_THREAD,("| %d) - context = %p", n, P_Context));
195  break;
196  }
197 }
198 #else
199 #define THREAD_DEBUG(n)
200 #endif
201 
202 ////////////////////////////////////////////////////////////////////////////
203 // special THREADS implementation:
204 ////////////////////////////////////////////////////////////////////////////
205 
206 static void THREAD_INTERRUPT_f() __attribute__ ((noinline)); // special function
207 
208 /// interrupt process behavior execution, continue after return
209 #define THREAD_INTERRUPT() \
210 { /* This should be MACRO */ \
211  /* if(!isCurrent()) SIMLIB_error("Can't interrupt..."); */ \
212  this->_status = _INTERRUPTED; \
213  THREAD_INTERRUPT_f(); \
214  this->_status = _RUNNING; \
215  this->_context = 0; \
216 }
217 
218 /// does not save context
219 #define THREAD_EXIT() \
220  longjmp(P_DispatcherStatusBuffer, 2) // jump to dispatcher
221 
222 // TODO: allocation/freeing memory is expensive, should be optimized
223 
224 /// \def ALLOC_CONTEXT
225 /// allocate memory for process context, sz = size of stack area to save
226 #define ALLOC_CONTEXT(sz) \
227  P_Context = (P_Context_t *) new char[sizeof(P_Context_t) + sz]; \
228  P_Context->size = sz;
229 
230 // bug for new compilers (2018) -- too aggresive optimization of global var
231 // access in THREAD_INTERRUPT_f causes longjmp register problem
232 #if BUG_IN_32BIT_CODE_USING_GCC_7_plus
233 /// free memory of process context
234 #define FREE_CONTEXT() \
235  delete[] (char *) P_Context; \
236  P_Context = 0;
237 #else
238 static void FREE_CONTEXT() __attribute__ ((noinline)); // special function
239 /// \fn FREE_CONTEXT
240 /// non-inline function for deallocating saved process context
241 static void FREE_CONTEXT() {
242  delete[] (char *) P_Context;
243  P_Context = 0;
244 }
245 #endif
246 
247 ////////////////////////////////////////////////////////////////////////////
248 /// Process constructor
249 /// sets state to PREPARED
251  Dprintf(("Process::Process(%d)", p));
252  _wait_until = false;
253  _context = 0; // pointer to process context
254  _status = _PREPARED; // prepared for running
255 }
256 
257 ////////////////////////////////////////////////////////////////////////////
258 /// Process destructor
259 /// Sets status to TERMINATED and
260 /// removes process from queue/calendar/waituntil list.
261 /// TODO: Warn if this==Current? (e.g. "delete this;" in Behavior())
263 {
264  Dprintf(("Process::~Process()"));
265 
266  //if(this==Current) SIMLIB_warning("Currently running process self-destructed");
267 
268  // destroy context data
269  delete [] (char*)_context;
270  _context = 0;
271 
273 
274  if (_wait_until) {
275  //SIMLIB_warning("Process in Wait-Until destructed");
276  _WaitUntilRemove(); // Remove from wait-until list
277  }
278 
279  if (Where() != 0) { // if waiting in queue
280  //SIMLIB_warning("Process waiting in queue destructed");
281  Out(); // remove from queue, no warning
282  }
283  if (!Idle()) { // if process is scheduled
284  //SIMLIB_warning("Active process destructed");
285  SQS::Get(this); // remove from calendar
286  }
287 }
288 
289 #if 1
290 ////////////////////////////////////////////////////////////////////////////
291 /// Name of the process
292 /// warning: uses static buffer for generic names
293 /// TODO: use std::string
294 std::string Process::Name() const
295 {
296  const std::string name = SimObject::Name();
297  if (!name.empty())
298  return name; // has explicit name
299  else
300  return SIMLIB_create_tmp_name("Process#%lu", _Ident);
301 }
302 #endif
303 
304 ////////////////////////////////////////////////////////////////////////////
305 /// Interrupt process behavior - this ensures WaitUntil tests
306 /// WARNING: use with care - it can run higher (or equal) priority processes
307 /// before continuing
309 {
310  Dprintf(("Process#%lu.Interrupt()", _Ident));
311  if (!isCurrent())
312  return; // quasiparallel, TODO: Error?
313  // continue after other processes WaitUntil checks
314  // TODO: use >highest priority to eliminate problem
315  Entity::Activate(); // schedule now - can run higher priority
316  // processes before this one
318  // TODO: return to previous priority
319 }
320 
321 ////////////////////////////////////////////////////////////////////////////
322 /// Activate process at time t
323 void Process::Activate(double t)
324 {
325  Dprintf(("Process#%lu.Activate(%g)", _Ident, t));
326  Entity::Activate(t);
327  if (!isCurrent())
328  return;
329  // SIMLIB_warning("Process can not activate itself - use Wait");
331 }
332 
333 ////////////////////////////////////////////////////////////////////////////
334 /// Wait for dtime
335 void Process::Wait(double dtime)
336 {
337  Dprintf(("Process#%lu.Wait(%g)", _Ident, dtime));
338  Entity::Activate(double (Time) + dtime); // scheduling
339  if (!isCurrent())
340  return;
342 }
343 
344 ////////////////////////////////////////////////////////////////////////////
345 /// Seize facility f with optional priority of service sp
346 /// possibly waiting in input queue, if it is busy
347 void Process::Seize(Facility & f, ServicePriority_t sp /* = 0 */ )
348 {
349  f.Seize(this, sp); // polymorphic interface
350 }
351 
352 ////////////////////////////////////////////////////////////////////////////
353 /// Release facility f
354 /// possibly activate first waiting entity in queue
356 {
357  f.Release(this); // polymorphic interface
358 }
359 
360 ////////////////////////////////////////////////////////////////////////////
361 /// Enter - use cap capacity of store s
362 /// possibly waiting in input queue, if not enough free capacity
363 void Process::Enter(Store & s, unsigned long cap)
364 {
365  s.Enter(this, cap); // polymorphic interface
366 }
367 
368 ////////////////////////////////////////////////////////////////////////////
369 /// Leave - return cap capacity of store s
370 /// and enter first waiting entity from queue, which can use free capacity
371 //TODO: should be parametrized: use first-only, first-good, all-good
372 void Process::Leave(Store & s, unsigned long cap)
373 {
374  s.Leave(cap); // polymorphic interface
375 }
376 
377 ////////////////////////////////////////////////////////////////////////////
378 /// insert current process into queue
379 /// The process can be at most in single queue.
381 {
382  if (Where() != 0) {
383  SIMLIB_warning("Process already in (other) queue");
384  Out(); // if already in queue then remove
385  }
386  q.Insert(this); // polymorphic interface
387 }
388 
389 ////////////////////////////////////////////////////////////////////////////
390 /// Process deactivation
391 /// To continue the behavior it should be activated again
392 /// Warning: memory leak if not activated/deleted
394 {
395  Dprintf(("Process#%lu.Passivate()", id()));
397  if (!isCurrent())
398  return; // passivated by other process
400 }
401 
402 ////////////////////////////////////////////////////////////////////////////
403 /// Terminate the process
404 /// if called by current process, self-destruct
405 /// free memory of the process
407 {
408  Dprintf(("Process#%lu.Terminate()", _Ident));
409 
410  // remove from all queues TODO: write special method for this
411  if (Where() != 0) { // Entity linked in queue
412  Out(); // remove from queue, no warning
413  }
414  if (!Idle())
415  SQS::Get(this); // remove from calendar
416 
417  // End of thread
418  if (isCurrent()) { // if currently running process
420  THREAD_EXIT(); // Jump back to dispatcher
421  }
422  else {
424  if (isAllocated())
425  delete this; // Remove passive process
426  }
427 }
428 
429 ////////////////////////////////////////////////////////////////////////////
430 #define CANARY1 (reinterpret_cast<long>(this)+1) // unaligned value is better
431 
432 /**
433  * \fn Process::_Run
434  * Process dispatch method
435  *
436  * The dispatcher starts/reactivates process behavior
437  *
438  * IMPORTANT notes:
439  * - Function contains some non-portable code and should be called
440  * from single place in simulation-control algorithm,
441  * because it is sensitive to stack-frame position
442  * (it saves/restores the stack contents).
443  *
444  * This function:
445  * 1) marks current position on stack
446  * 2) marks current CPU context
447  * 3) calls Behavior()
448  * 4) after interruption of Behavior() returns
449  * 5) if called next time, do 1), 2) and move stack pointer (SP)
450  * 6) copy saved stack content back
451  * 7) do longjmp() to restore Behavior() execution
452  *
453  * @ingroup process
454  */
455 void Process::_Run() noexcept // no exceptions
456 {
457  // WARNING: all local variables should be volatile (see setjmp manual)
458  static const char * status_strings[] = {
459  "unknown", "PREPARED", "RUNNING", "INTERRUPTED", "TERMINATED"
460  };
461  Dprintf(("%016p===Process#%lu._Run() status=%s", this, _Ident, status_strings[_status]));
462 
463  if (_status != _INTERRUPTED && _status != _PREPARED)
465 
466  // Mark the stack base address
467  volatile long mylocal = CANARY1; // should be automatic = on stack
468  // Warning: DO NOT USE ANY OTHER LOCAL VARIABLES in this function!
469  P_StackBase = (char*)(&mylocal + 1);
470 
471  //
472  // STACK layout (stack grows down):
473  //
474  // | ... |
475  // | |
476  // P_StackBase +-> +----------+
477  // | | mylocal |
478  // | +----------+
479  // | | |
480  // | | ... |
481  // _size | | Arguments, return addresses, locals, etc.
482  // | | ... |
483  // | | |
484  // | +----------+
485  // | | mylocal2 |
486  // +-> +----------+
487  // | | |
488 # define STACK_RESERVED 0x080 // reserved for "red zone" 128B ?
489  // | | |
490  // P_StackBase2 +-> +----------+
491 
492 
493 #if EXTRA_DEBUG
494  DEBUG(DBG_THREAD,("| THREAD_STACK_BASE=%016p", P_StackBase));
495  // CHECK if the P_StackBase position is the same in each call
496  static char *P_StackBase0=0;
497  if(P_StackBase0==0)
498  P_StackBase0=P_StackBase;
499  else if (P_StackBase!=P_StackBase0)
500  SIMLIB_error("Internal error: P_StackBase not constant");
501 #endif
502 
503  // 2) mark current CPU context (part of context)
504  if (!setjmp(P_DispatcherStatusBuffer))
505  {
506  // setjmp returned after saving current status
507  _status = _RUNNING;
508  if (_context == 0) { // process start
509  DEBUG(DBG_THREAD, ("| --- Process::Behavior() START "));
510  Behavior(); // run behavior description
511  DEBUG(DBG_THREAD, ("| --- Process::Behavior() END "));
512  _status = _TERMINATED;
513  if(mylocal != CANARY1)
514  SIMLIB_error("Process canary1 died after Behavior() return");
515  // Remove from any queue
516  if (Where() != 0) { // Entity linked in queue
517  Out(); // Remove from queue, no warning
518  }
519  if (!Idle())
520  SQS::Get(this); // Remove from calendar
521  //TODO: if(in any facility) error
522  } else { // process was interrupted and has saved context
523  DEBUG(DBG_THREAD, ("| --- Process::Behavior() CONTINUE "));
524  mylocal = 0; // for checking only - previous value should be saved and later restored
525  // RESTORE_CONTEXT
526  // a) Save local variables to global
527  // This is important because of following stack manipulations.
528  P_Context = (P_Context_t*) this->_context;
529  P_StackSize = P_Context->size;
530 
531  // b) Shift stack pointer under the currently restored stack area
532  // This is very important for next memcpy and longjmp
533  // (stack grows down), we reserve some more space
534  P_StackBase2 = P_StackBase - P_StackSize - STACK_RESERVED;
535  THREAD_DEBUG(0xB);
536 
537  // ==========================================================
538  SET_STACK_PTR(P_StackBase2); // === BEGIN inconsistent state!
539  // Warning: We can not use any local variables here!
540 
541  // c) Copy saved stack contents back to stack
542  memcpy((void *) (P_StackBase - P_StackSize), P_Context->stack, P_StackSize);
543  THREAD_DEBUG(0xC);
544 
545  // 4) Restore proces status (SP,IP,...)
546  longjmp(P_Context->status, 1); // === END inconsistent state!
547  // ===========================================================
548  // never reach this point - longjmp never returns
549 
550  }
551  }
552  else
553  { // setjmp: back from Behavior() - interrupted or terminated
554 
555  if(mylocal != CANARY1)
556  SIMLIB_error("Process implementation canary1 died");
557 
558  if(!isTerminated()) {
559  // Interrupted process
560  // Store content in global variables back to attributes
561  P_Context->size = P_StackSize;
562  this->_context = P_Context;
563  DEBUG(DBG_THREAD,("| --- Process::Behavior() INTERRUPT %p.context=%p, size=%d", \
564  this, P_Context, P_StackSize));
565  P_Context = 0; // cleaning
566  }
567  }
568 
569  Dprintf(("%016p===Process#%lu._Run() RETURN status=%s", this, _Ident, status_strings[_status]));
570 
571  //TODO: MOVE to simulation control loop
572  if (isTerminated() && isAllocated()) {
573  // terminated process on heap
574  DEBUG(DBG_THREAD,("| Process %p ends and is deallocated now",this));
575  delete this; // destroy process
576  }
577  // return to simulation control
578 }
579 
580 
581 ////////////////////////////////////////////////////////////////////////////
582 #define CANARY2 0xDEADBEEFUL
583 /**
584  * \fn THREAD_INTERRUPT_f
585  * Special function called from Process::Behavior() directly or indirectly.
586  *
587  * This function:
588  * 1) computes stack content size,
589  * 2) allocates memory for stack contents,
590  * 3) saves stack contents to allocated memory,
591  * 4) saves CPU context using setjmp(), and
592  * 5) interrupts execution of current function using longjmp()
593  * to process dispatcher code,
594  * == (now run dispatcher and other code)
595  * 6) continues execution after longjmp from dispatcher.
596  * 7) frees memory allocated at 2)
597  *
598  * Warning: This function is critical to process switching code and
599  * should not be inlined! It is never called directly by SIMLIB user.
600  *
601  * @ingroup process
602  */
603 static void THREAD_INTERRUPT_f()
604 {
605  // SAVE THE STACK STATE of the thread
606 
607  // 1) compute stack context size (from P_StackBase to local variable)
608  volatile unsigned mylocal2 = CANARY2; // the only on-stack variable
609  P_StackSize = (size_t) (P_StackBase - (char *) (&mylocal2));
610  THREAD_DEBUG(1);
611 
612  // 2) allocate memory for stack contents
613  ALLOC_CONTEXT(P_StackSize);
614 
615  // 3) save stack data (stack grows DOWN)
616  memcpy(P_Context->stack, (P_StackBase - P_StackSize), P_StackSize);
617 
618  mylocal2 = 0; // previous value should be saved now and restored later
619 
620  // STACK CONTENTS SAVED
621 
622  // 4) mark CPU context
623  THREAD_DEBUG(4);
624  if (!setjmp(P_Context->status)) {
625  ////////////////////////////////////////////////////////////////////
626  // 5) Interrupt the execution of Behavior()
627  THREAD_DEBUG(5);
628  longjmp(P_DispatcherStatusBuffer, 1); // --> longjmp back to dispatcher
629  // never return here
630  }
631 
632  // Check canary on stack after restore
633  if (mylocal2 != CANARY2)
634  SIMLIB_error("Process switching canary2 died.");
635 
636  ////////////////////////////////////////////////////////////////////////
637  // 6) Continue execution after longjmp from dispatcher
638  // Data were restored on stack, longjmp restored SP
639  THREAD_DEBUG(6);
640 
641  // 7) free memory of stack contents
642  FREE_CONTEXT();
643  THREAD_DEBUG(7);
644  // return and continue in Process::Behavior() execution
645 }
646 
647 } // namespace
648 
void Leave(Store &s, unsigned long ReqCap=1)
return some capacity
Definition: process.cc:372
(SOL-like) facility Facility with exclusive access and service priority
Definition: simlib.h:753
void Interrupt()
test of WaitUntil list, allow running others
Definition: process.cc:308
virtual void Out() override
remove entity from queue
Definition: entity.cc:90
virtual void Into(Queue &q)
insert process into queue
Definition: process.cc:380
virtual ~Process()
Process destructor Sets status to TERMINATED and removes process from queue/calendar/waituntil list...
Definition: process.cc:262
void SIMLIB_error(const enum _ErrEnum N)
print error message and abort program
Definition: error.cc:38
unsigned char ServicePriority_t
Service priority (see Facility::Seize)
Definition: simlib.h:365
static volatile size_t P_StackSize
temporary global stack size
Definition: process.cc:133
(SOL-like) store store capacity can be changed dynamically
Definition: simlib.h:785
void * _context
process context pointer
Definition: simlib.h:441
#define THREAD_INTERRUPT()
interrupt process behavior execution, continue after return
Definition: process.cc:209
void Release(Facility &f)
release facility
Definition: process.cc:355
bool _wait_until
Definition: simlib.h:454
jmp_buf status
stored SP, IP, and other registers
Definition: process.cc:121
virtual void Passivate()
deactivation
Definition: entity.cc:68
void Get(Entity *e)
remove selected entity activation record from calendar
Definition: calendar.cc:1308
virtual void Seize(Entity *e, ServicePriority_t sp=DEFAULT_PRIORITY)
Definition: facility.cc:111
bool isAllocated() const
Definition: simlib.h:318
bool isTerminated() const
Definition: simlib.h:451
#define CANARY2
Definition: process.cc:582
size_t size
size of following array (allocated on heap)
Definition: process.cc:122
Implementation of class CalendarList interface is static - using global functions in SQS namespace...
Definition: algloop.cc:32
bool isCurrent() const
Definition: simlib.h:449
virtual void Behavior()=0
behavior description
unsigned long _Ident
unique identification number of entity
Definition: simlib.h:378
void SIMLIB_warning(const enum _ErrEnum N)
print warning message and continue
Definition: error.cc:74
std::string SIMLIB_create_tmp_name(const char *fmt,...)
printf-like function to create temporary name (the length of temporary names is limited) used only ...
Definition: name.cc:80
abstract base class for active entities (Process, Event) instances of derived classes provide Behavio...
Definition: simlib.h:375
static char *volatile P_StackBase
global start of stack area
Definition: process.cc:129
#define THREAD_DEBUG(n)
Definition: process.cc:199
const double & Time
model time (is NOT the block)
Definition: run.cc:48
#define ALLOC_CONTEXT(sz)
allocate memory for process context, sz = size of stack area to save
Definition: process.cc:226
static char *volatile P_StackBase2
for checking start of stack
Definition: process.cc:130
virtual void Release(Entity *e)
Definition: facility.cc:151
void Seize(Facility &f, ServicePriority_t sp=0)
seize facility
Definition: process.cc:347
#define THREAD_EXIT()
does not save context
Definition: process.cc:219
enum simlib3::Process::ProcessStatus_t _status
Test t(F)
virtual void Terminate() override
kill process
Definition: process.cc:406
#define STACK_RESERVED
virtual void Wait(double dtime)
wait for dtime interval
Definition: process.cc:335
virtual void Enter(Entity *e, unsigned long rcap)
allocate capacity
Definition: store.cc:128
virtual void Leave(unsigned long rcap)
deallocate capacity
Definition: store.cc:152
Internal header file for SIMLIB/C++.
void _WaitUntilRemove()
Definition: waitunti.cc:142
internal structure for storing of process context
Definition: process.cc:120
virtual void Passivate() override
process deactivation (sleep)
Definition: process.cc:393
Main SIMLIB/C++ interface.
virtual std::string Name() const
get object name
Definition: object.cc:134
SIMLIB_IMPLEMENTATION
Definition: algloop.cc:34
static jmp_buf P_DispatcherStatusBuffer
setjmp() state before dispatch
Definition: process.cc:128
static P_Context_t *volatile P_Context
temporary global process state
Definition: process.cc:132
bool Idle()
entity activation is not scheduled in calendar
Definition: simlib.h:412
#define DEBUG(c, f)
Definition: internal.h:105
void Enter(Store &s, unsigned long ReqCap=1)
acquire some capacity
Definition: process.cc:363
static void FREE_CONTEXT() __attribute__((noinline))
non-inline function for deallocating saved process context
Definition: process.cc:241
void Activate()
activate now
Definition: simlib.h:408
EntityPriority_t Priority_t
Definition: simlib.h:397
#define Dprintf(f)
Definition: internal.h:100
virtual std::string Name() const override
name of object
Definition: process.cc:294
Process(Priority_t p=DEFAULT_PRIORITY)
Process constructor sets state to PREPARED.
Definition: process.cc:250
static void THREAD_INTERRUPT_f() __attribute__((noinline))
Special function called from Process::Behavior() directly or indirectly.
Definition: process.cc:603
priority queue
Definition: simlib.h:685
#define DBG_THREAD
Definition: internal.h:126
#define CANARY1
Definition: process.cc:430
char stack[1]
stack contents saved
Definition: process.cc:123
virtual void Insert(Entity *e)
Definition: queue.cc:50
virtual void _Run() noexcept override
Process dispatch method.
Definition: process.cc:455