Pol  Revision:4b29d2b
script_internals.cpp
Go to the documentation of this file.
1 #include "script_internals.h"
2 
3 #include <string.h>
4 
5 #include "../../clib/logfacility.h"
6 #include "../../clib/passert.h"
7 #include "../../plib/systemstate.h"
8 #include "../module/osmod.h"
9 #include "../polsig.h"
10 #include "../uoexec.h"
11 #include "state.h"
12 
13 namespace Pol
14 {
15 namespace Core
16 {
18 
19 // This number is intended so that PID and custom GUMPIDS will never clash together
20 // and to avoid breaking the old assumption that gumpid == pid when gumpid has been
21 // automatically generated (for backward compatibility).
22 // Custom gumpids must always be < PID_MIN.
23 const unsigned int ScriptScheduler::PID_MIN = 0x01000000;
24 
26  : priority_divide( 1 ),
27  scrstore(),
28  runlist(),
29  ranlist(),
30  holdlist(),
31  notimeoutholdlist(),
32  debuggerholdlist(),
33  pidlist(),
34  next_pid( PID_MIN )
35 {
36 }
37 
39 
40 // Note, when the program exits, each executor in these queues
41 // will be deleted by cleanup_scripts()
42 // Therefore, any object that owns an executor must be destroyed
43 // before cleanup_scripts() is called.
45 {
46  scrstore.clear();
48  while ( !holdlist.empty() )
49  {
50  delete ( ( *holdlist.begin() ).second );
51  holdlist.erase( holdlist.begin() );
52  }
53  while ( !notimeoutholdlist.empty() )
54  {
55  delete ( *notimeoutholdlist.begin() );
56  notimeoutholdlist.erase( notimeoutholdlist.begin() );
57  }
58  while ( !debuggerholdlist.empty() )
59  {
60  delete ( *debuggerholdlist.begin() );
61  debuggerholdlist.erase( debuggerholdlist.begin() );
62  }
63 }
64 
66 {
67  Memory usage;
68  memset( &usage, 0, sizeof( usage ) );
69 
70  usage.script_size =
71  sizeof( int ) /*priority_divide*/
72  + sizeof( unsigned int ) /*next_pid*/
73  + ( sizeof( unsigned int ) + sizeof( UOExecutor* ) + ( sizeof( void* ) * 3 + 1 ) / 2 ) *
74  pidlist.size();
75 
76  for ( const auto& script : scrstore )
77  {
78  usage.scriptstorage_size += ( sizeof( void* ) * 3 + 1 ) / 2;
79  usage.scriptstorage_size += script.first.capacity();
80  if ( script.second.get() != nullptr )
81  usage.scriptstorage_size += script.second->sizeEstimate();
82  }
83  usage.scriptstorage_count = scrstore.size();
84 
85 
86  usage.script_size += 3 * sizeof( UOExecutor** ) + runlist.size() * sizeof( UOExecutor* );
87  for ( const auto& exec : runlist )
88  {
89  if ( exec != nullptr )
90  usage.script_size += exec->sizeEstimate();
91  }
92  usage.script_count += runlist.size();
93 
94  usage.script_size += 3 * sizeof( UOExecutor** ) + ranlist.size() * sizeof( UOExecutor* );
95  for ( const auto& exec : ranlist )
96  {
97  if ( exec != nullptr )
98  usage.script_size += exec->sizeEstimate();
99  }
100  usage.script_count += ranlist.size();
101 
102  for ( const auto& hold : holdlist )
103  {
104  if ( hold.second != nullptr )
105  usage.script_size += hold.second->sizeEstimate();
106  usage.script_size += sizeof( Core::polclock_t ) + ( sizeof( void* ) * 3 + 1 ) / 2;
107  }
108  usage.script_count += holdlist.size();
109 
110  usage.script_size += 3 * sizeof( void* );
111  for ( const auto& hold : notimeoutholdlist )
112  {
113  if ( hold != nullptr )
114  usage.script_size += hold->sizeEstimate() + 3 * sizeof( void* );
115  }
116  usage.script_count += notimeoutholdlist.size();
117 
118  usage.script_size += 3 * sizeof( void* );
119  for ( const auto& hold : debuggerholdlist )
120  {
121  if ( hold != nullptr )
122  usage.script_size += hold->sizeEstimate() + 3 * sizeof( void* );
123  }
124  usage.script_count += debuggerholdlist.size();
125 
126 
127  return usage;
128 }
129 
130 
132 {
133  THREAD_CHECKPOINT( scripts, 110 );
134  while ( !runlist.empty() )
135  {
136  ExecList::iterator itr = runlist.begin();
137  UOExecutor* ex = *itr;
138  passert_paranoid( ex != nullptr );
139  runlist.pop_front(); // remove it directly, since itr can get invalid during execution
140 
141  Module::OSExecutorModule* os_module = ex->os_module;
143 
144  int inscount = 0;
145  int totcount = 0;
146  int insleft = os_module->priority / priority_divide;
147  if ( insleft == 0 )
148  insleft = 1;
149 
150  THREAD_CHECKPOINT( scripts, 111 );
151 
152  while ( ex->runnable() )
153  {
154  ++ex->instr_cycles;
155  THREAD_CHECKPOINT( scripts, 112 );
157  ex->execInstr();
158 
159  THREAD_CHECKPOINT( scripts, 113 );
160 
161  if ( os_module->blocked() )
162  {
165  ex->runaway_cycles = 0;
166  break;
167  }
168 
169  if ( ex->instr_cycles == ex->warn_runaway_on_cycle )
170  {
172  if ( os_module->warn_on_runaway )
173  {
174  fmt::Writer tmp;
175  tmp << "Runaway script[" << os_module->pid() << "]: " << ex->scriptname() << " ("
176  << ex->runaway_cycles << " cycles)\n";
177  ex->show_context( tmp, ex->PC );
178  SCRIPTLOG << tmp.str();
179  }
181  }
182 
183  if ( os_module->critical )
184  {
185  ++inscount;
186  ++totcount;
187  if ( inscount > 1000 )
188  {
189  inscount = 0;
190  if ( Plib::systemstate.config.report_critical_scripts )
191  {
192  fmt::Writer tmp;
193  tmp << "Critical script " << ex->scriptname() << " has run for " << totcount
194  << " instructions\n";
195  ex->show_context( tmp, ex->PC );
196  ERROR_PRINT << tmp.str();
197  }
198  }
199  continue;
200  }
201 
202  if ( !--insleft )
203  {
204  break;
205  }
206  }
207 
208  // hmm, this new terminology (runnable()) is confusing
209  // in this case. Technically, something that is blocked
210  // isn't runnable.
211  if ( !ex->runnable() )
212  {
213  if ( ex->error() || ex->done )
214  {
215  THREAD_CHECKPOINT( scripts, 114 );
216 
217  if ( ( ex->pParent != nullptr ) && ex->pParent->runnable() )
218  {
219  ranlist.push_back( ex );
220  // ranlist.splice( ranlist.end(), runlist, itr );
221  ex->pParent->os_module->revive();
222  }
223  else
224  {
225  // runlist.erase( itr );
226  // Check if the script has a child script running
227  // Set the parent of the child script nullptr to stop crashing when trying to return to
228  // parent script
229  if ( ex->pChild != nullptr )
230  ex->pChild->pParent = nullptr;
231 
232  delete ex;
233  }
234  continue;
235  }
236  else if ( !ex->os_module->blocked() )
237  {
238  THREAD_CHECKPOINT( scripts, 115 );
239 
240  // runlist.erase( itr );
242  debuggerholdlist.insert( ex );
243  continue;
244  }
245  }
246 
247  if ( ex->os_module->blocked() )
248  {
249  THREAD_CHECKPOINT( scripts, 116 );
250 
251  if ( ex->os_module->sleep_until_clock_ )
252  {
254  ex->os_module->hold_itr_ =
255  holdlist.insert( HoldList::value_type( ex->os_module->sleep_until_clock_, ex ) );
256  }
257  else
258  {
260  notimeoutholdlist.insert( ex );
261  }
262 
263  // runlist.erase( itr );
264  --ex->sleep_cycles; // it'd get counted twice otherwise
266 
267  THREAD_CHECKPOINT( scripts, 117 );
268  }
269  else
270  {
271  ranlist.push_back( ex );
272  // ranlist.splice( ranlist.end(), runlist, itr );
273  }
274  }
275  THREAD_CHECKPOINT( scripts, 118 );
276 
277  runlist.swap( ranlist );
278  THREAD_CHECKPOINT( scripts, 119 );
279 }
280 
282 {
284  enqueue( exec );
285 }
286 
288 {
289  for ( ;; )
290  {
291  unsigned int newpid = next_pid++;
292  if ( newpid < PID_MIN )
293  newpid = PID_MIN;
294 
295  // NOTE: The code below is pessimistic, is there a way to avoid checking the pidlist every time?
296  // (Nando, 06-Nov-2016)
297 
298  // newpid=0 should now never happen but leaving this
299  // check in place for extra code robustness
300  if ( newpid != 0 && ( pidlist.find( newpid ) == pidlist.end() ) )
301  {
302  pidlist[newpid] = exec;
303  return newpid;
304  }
305  }
306 }
307 
308 bool ScriptScheduler::find_exec( unsigned int pid, UOExecutor** exec )
309 {
310  auto itr = pidlist.find( pid );
311  if ( itr != pidlist.end() )
312  {
313  *exec = ( *itr ).second;
314  return true;
315  }
316  else
317  {
318  *exec = nullptr;
319  return false;
320  }
321 }
322 }
323 }
void setDebugLevel(DEBUG_LEVEL level)
Definition: executor.h:369
void delete_all(T &coll)
Definition: stlutil.h:24
SystemState systemstate
Definition: systemstate.cpp:12
std::string scripts_thread_script
Definition: passert.cpp:36
Core::PolConfig config
Definition: systemstate.h:43
unsigned int runaway_script_threshold
Definition: polcfg.h:55
void enqueue(UOExecutor *exec)
bool error() const
Definition: executor.h:442
bool find_exec(unsigned int pid, UOExecutor **exec)
#define THREAD_CHECKPOINT(thread, check)
Definition: polsig.h:48
u64 pid
Definition: osmod.cpp:945
unsigned int pid() const
Definition: osmod.cpp:129
#define SCRIPTLOG
Definition: logfacility.h:234
ProfileVars profilevars
Definition: state.h:47
enum Pol::Module::OSExecutorModule::@9 in_hold_list_
NoTimeoutHoldList debuggerholdlist
NoTimeoutHoldList notimeoutholdlist
Core::TimeoutHandle hold_itr_
Definition: osmod.h:121
u64 warn_runaway_on_cycle
Definition: uoexec.h:53
UOExecutor * pChild
Definition: uoexec.h:64
int polclock_t
Definition: polclock.h:26
Core::polclock_t sleep_until_clock_
Definition: osmod.h:112
UOExecutor * pParent
Definition: uoexec.h:64
bool runnable() const
Definition: executor.h:428
unsigned char priority
Definition: osmod.h:63
StateManager stateManager
Definition: state.cpp:8
ScriptScheduler scriptScheduler
unsigned int get_new_pid(UOExecutor *exec)
const std::string & scriptname() const
Definition: executor.h:413
void schedule(UOExecutor *exec)
#define ERROR_PRINT
Definition: logfacility.h:230
void show_context(unsigned atPC)
Definition: executor.cpp:2914
#define passert_paranoid(exp)
Definition: passert.h:95
Definition: berror.cpp:12
Module::OSExecutorModule * os_module
Definition: uoexec.h:37
bool blocked() const
Definition: osmod.h:158
static const unsigned int PID_MIN
unsigned scripts_thread_scriptPC
Definition: passert.cpp:37