• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

polserver / polserver / 12782377031

15 Jan 2025 05:52AM UTC coverage: 57.311% (-0.03%) from 57.34%
12782377031

push

github

web-flow
simplified worldsave commit, do not ignore errors during rename (#744)

15 of 19 new or added lines in 1 file covered. (78.95%)

16 existing lines in 6 files now uncovered.

41074 of 71669 relevant lines covered (57.31%)

372443.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.84
/pol-core/pol/globals/script_internals.cpp
1
#include "script_internals.h"
2

3
#include <iterator>
4
#include <string.h>
5

6
#include "../../clib/logfacility.h"
7
#include "../../clib/passert.h"
8
#include "../../clib/stlutil.h"
9
#include "../../plib/systemstate.h"
10
#include "../polsig.h"
11
#include "../uoexec.h"
12
#include "state.h"
13

14
namespace Pol
15
{
16
namespace Core
17
{
18
ScriptScheduler scriptScheduler;
19

20
// This number is intended so that PID and custom GUMPIDS will never clash together
21
// and to avoid breaking the old assumption that gumpid == pid when gumpid has been
22
// automatically generated (for backward compatibility).
23
// Custom gumpids must always be < PID_MIN.
24
const unsigned int ScriptScheduler::PID_MIN = 0x01000000;
25

26
ScriptScheduler::ScriptScheduler()
3✔
27
    : priority_divide( 1 ),
3✔
28
      scrstore(),
3✔
29
      runlist(),
3✔
30
      ranlist(),
3✔
31
      holdlist(),
3✔
32
      notimeoutholdlist(),
3✔
33
      debuggerholdlist(),
3✔
34
      pidlist(),
3✔
35
      next_pid( PID_MIN )
3✔
36
{
37
}
3✔
38

39
ScriptScheduler::~ScriptScheduler() {}
3✔
40

41
// Note, when the program exits, each executor in these queues
42
// will be deleted by cleanup_scripts()
43
// Therefore, any object that owns an executor must be destroyed
44
// before cleanup_scripts() is called.
45
void ScriptScheduler::deinitialize()
3✔
46
{
47
  scrstore.clear();
3✔
48
  Clib::delete_all( runlist );
3✔
49
  while ( !holdlist.empty() )
9✔
50
  {
51
    delete ( ( *holdlist.begin() ).second );
6✔
52
    holdlist.erase( holdlist.begin() );
6✔
53
  }
54
  while ( !notimeoutholdlist.empty() )
3✔
55
  {
56
    delete ( *notimeoutholdlist.begin() );
×
57
    notimeoutholdlist.erase( notimeoutholdlist.begin() );
×
58
  }
59
  while ( !debuggerholdlist.empty() )
3✔
60
  {
61
    delete ( *debuggerholdlist.begin() );
×
62
    debuggerholdlist.erase( debuggerholdlist.begin() );
×
63
  }
64
}
3✔
65

66
ScriptScheduler::Memory ScriptScheduler::estimateSize( bool verbose ) const
2✔
67
{
68
  Memory usage;
69
  memset( &usage, 0, sizeof( usage ) );
2✔
70

71
  usage.script_size = sizeof( int )            /*priority_divide*/
2✔
72
                      + sizeof( unsigned int ) /*next_pid*/
73
                      + Clib::memsize( pidlist );
2✔
74
  usage.scriptstorage_size = Clib::memsize( scrstore );
2✔
75
  for ( const auto& script : scrstore )
136✔
76
  {
77
    usage.scriptstorage_size += script.first.capacity();
134✔
78
    if ( script.second.get() != nullptr )
134✔
79
      usage.scriptstorage_size += script.second->sizeEstimate();
134✔
80
  }
81
  usage.scriptstorage_count = scrstore.size();
2✔
82

83
  std::string verbose_w;
2✔
84
  if ( verbose )
2✔
85
    verbose_w = GET_LOG_FILESTAMP + "\n";
1✔
86
  usage.script_size += Clib::memsize( runlist );
2✔
87
  if ( verbose )
2✔
88
    verbose_w += "runlist:\n";
1✔
89
  for ( const auto& exec : runlist )
4✔
90
  {
91
    if ( exec != nullptr )
2✔
92
    {
93
      usage.script_size += exec->sizeEstimate();
2✔
94
      if ( verbose )
2✔
95
        fmt::format_to( std::back_inserter( verbose_w ), "{} {} \n", exec->scriptname(),
2✔
96
                        exec->sizeEstimate() );
2✔
97
    }
98
  }
99
  usage.script_count += runlist.size();
2✔
100

101
  usage.script_size += Clib::memsize( ranlist );
2✔
102
  if ( verbose )
2✔
103
    verbose_w += "ranlist:\n";
1✔
104
  for ( const auto& exec : ranlist )
2✔
105
  {
UNCOV
106
    if ( exec != nullptr )
×
107
    {
UNCOV
108
      usage.script_size += exec->sizeEstimate();
×
UNCOV
109
      if ( verbose )
×
110
        fmt::format_to( std::back_inserter( verbose_w ), "{} {}\n", exec->scriptname(),
×
111
                        exec->sizeEstimate() );
×
112
    }
113
  }
114
  usage.script_count += ranlist.size();
2✔
115

116
  if ( verbose )
2✔
117
    verbose_w += "holdlist:\n";
1✔
118
  usage.script_size += Clib::memsize( holdlist );
2✔
119
  for ( const auto& hold : holdlist )
6✔
120
  {
121
    if ( hold.second != nullptr )
4✔
122
    {
123
      usage.script_size += hold.second->sizeEstimate();
4✔
124
      if ( verbose )
4✔
125
        fmt::format_to( std::back_inserter( verbose_w ), "{} {}\n", hold.second->scriptname(),
4✔
126
                        hold.second->sizeEstimate() );
4✔
127
    }
128
  }
129
  usage.script_count += holdlist.size();
2✔
130

131
  usage.script_size += Clib::memsize( notimeoutholdlist );
2✔
132
  if ( verbose )
2✔
133
    verbose_w += "notimeoutholdlist:\n";
1✔
134
  for ( const auto& hold : notimeoutholdlist )
4✔
135
  {
136
    if ( hold != nullptr )
2✔
137
    {
138
      usage.script_size += hold->sizeEstimate();
2✔
139
      if ( verbose )
2✔
140
        fmt::format_to( std::back_inserter( verbose_w ), "{} {}\n", hold->scriptname(),
2✔
141
                        hold->sizeEstimate() );
2✔
142
    }
143
  }
144
  usage.script_count += notimeoutholdlist.size();
2✔
145

146
  usage.script_size += Clib::memsize( debuggerholdlist );
2✔
147
  if ( verbose )
2✔
148
    verbose_w += "debuggerholdlist:\n";
1✔
149
  for ( const auto& hold : debuggerholdlist )
2✔
150
  {
151
    if ( hold != nullptr )
×
152
    {
153
      usage.script_size += hold->sizeEstimate();
×
154
      if ( verbose )
×
155
        fmt::format_to( std::back_inserter( verbose_w ), "{} {}\n", hold->scriptname(),
×
156
                        hold->sizeEstimate() );
×
157
    }
158
  }
159
  usage.script_count += debuggerholdlist.size();
2✔
160
  if ( verbose )
2✔
161
  {
162
    auto log = OPEN_FLEXLOG( "log/memoryusagescripts.log", false );
1✔
163
    FLEXLOGLN( log, verbose_w );  // extra newline at the end,seperates the old from the new entry
1✔
164

165
    CLOSE_FLEXLOG( log );
1✔
166
  }
1✔
167

168
  return usage;
4✔
169
}
2✔
170

171

172
void ScriptScheduler::run_ready()
56,548,157✔
173
{
174
  THREAD_CHECKPOINT( scripts, 110 );
56,548,157✔
175
  while ( !runlist.empty() )
113,379,444✔
176
  {
177
    ExecList::iterator itr = runlist.begin();
56,831,287✔
178
    UOExecutor* ex = *itr;
56,831,287✔
179
    passert_paranoid( ex != nullptr );
180
    runlist.pop_front();  // remove it directly, since itr can get invalid during execution
56,831,287✔
181

182
    Clib::scripts_thread_script = ex->scriptname();
56,831,287✔
183

184
    int inscount = 0;
56,831,287✔
185
    int totcount = 0;
56,831,287✔
186
    int insleft = ex->priority() / priority_divide;
56,831,287✔
187
    if ( insleft == 0 )
56,831,287✔
188
      insleft = 1;
×
189

190
    THREAD_CHECKPOINT( scripts, 111 );
56,831,287✔
191

192
    while ( ex->runnable() )
56,831,898✔
193
    {
194
      ++ex->instr_cycles;
56,831,464✔
195
      THREAD_CHECKPOINT( scripts, 112 );
56,831,464✔
196
      Clib::scripts_thread_scriptPC = ex->PC;
56,831,464✔
197
      ex->execInstr();
56,831,464✔
198

199
      THREAD_CHECKPOINT( scripts, 113 );
56,831,464✔
200

201
      if ( ex->blocked() )
56,831,464✔
202
      {
203
        ex->warn_runaway_on_cycle =
9,425,516✔
204
            ex->instr_cycles + Plib::systemstate.config.runaway_script_threshold;
9,425,516✔
205
        ex->runaway_cycles = 0;
9,425,516✔
206
        break;
9,425,516✔
207
      }
208

209
      if ( ex->instr_cycles == ex->warn_runaway_on_cycle )
47,405,948✔
210
      {
211
        ex->runaway_cycles += Plib::systemstate.config.runaway_script_threshold;
15✔
212
        if ( ex->warn_on_runaway() )
15✔
213
        {
214
          std::string tmp = fmt::format( "Runaway script[{}]: ({} cycles)\n", ex->pid(),
15✔
215
                                         ex->scriptname(), ex->runaway_cycles );
15✔
216
          ex->show_context( tmp, ex->PC );
15✔
217
          SCRIPTLOG( tmp );
15✔
218
        }
15✔
219
        ex->warn_runaway_on_cycle += Plib::systemstate.config.runaway_script_threshold;
15✔
220
      }
221

222
      if ( ex->critical() )
47,405,948✔
223
      {
224
        ++inscount;
114✔
225
        ++totcount;
114✔
226
        if ( inscount > 1000 )
114✔
227
        {
228
          inscount = 0;
×
229
          if ( Plib::systemstate.config.report_critical_scripts )
×
230
          {
231
            std::string tmp = fmt::format( "Critical script {} has run for {} instructions\n",
232
                                           ex->scriptname(), totcount );
×
233
            ex->show_context( tmp, ex->PC );
×
234
            ERROR_PRINT( tmp );
×
235
          }
×
236
        }
237
        continue;
114✔
238
      }
114✔
239

240
      if ( !--insleft )
47,405,834✔
241
      {
242
        break;
47,405,337✔
243
      }
244
    }
245

246
    // hmm, this new terminology (runnable()) is confusing
247
    // in this case.  Technically, something that is blocked
248
    // isn't runnable.
249
    if ( !ex->runnable() )
56,831,287✔
250
    {
251
      if ( ex->error() || ex->done )
828✔
252
      {
253
        THREAD_CHECKPOINT( scripts, 114 );
819✔
254

255
        if ( ( ex->pParent != nullptr ) && ex->pParent->runnable() )
819✔
256
        {
257
          ranlist.push_back( ex );
325✔
258
          ex->pParent->revive();
325✔
259
        }
260
        else
261
        {
262
          // Check if the script has a child script running
263
          // Set the parent of the child script nullptr to stop crashing when trying to return to
264
          // parent script
265
          if ( ex->pChild != nullptr )
494✔
266
            ex->pChild->pParent = nullptr;
×
267
          if ( !ex->keep_alive() )
494✔
268
          {
269
            delete ex;
177✔
270
          }
271
          else
272
          {
273
            ex->in_hold_list( Core::HoldListType::NOTIMEOUT_LIST );
317✔
274
            notimeoutholdlist.insert( ex );
317✔
275
          }
276
        }
277
        continue;
828✔
278
      }
279
      else if ( !ex->blocked() )
9✔
280
      {
281
        THREAD_CHECKPOINT( scripts, 115 );
9✔
282

283
        ex->in_hold_list( Core::HoldListType::DEBUGGER_LIST );
9✔
284
        debuggerholdlist.insert( ex );
9✔
285
        continue;
9✔
286
      }
287
    }
288

289
    if ( ex->blocked() )
56,830,459✔
290
    {
291
      THREAD_CHECKPOINT( scripts, 116 );
9,425,516✔
292

293
      if ( ex->sleep_until_clock() )
9,425,516✔
294
      {
295
        ex->in_hold_list( Core::HoldListType::TIMEOUT_LIST );
9,425,158✔
296
        ex->hold_itr( holdlist.insert( HoldList::value_type( ex->sleep_until_clock(), ex ) ) );
9,425,158✔
297
      }
298
      else
299
      {
300
        ex->in_hold_list( Core::HoldListType::NOTIMEOUT_LIST );
358✔
301
        notimeoutholdlist.insert( ex );
358✔
302
      }
303

304
      --ex->sleep_cycles;  // it'd get counted twice otherwise
9,425,516✔
305
      --stateManager.profilevars.sleep_cycles;
9,425,516✔
306

307
      THREAD_CHECKPOINT( scripts, 117 );
9,425,516✔
308
    }
309
    else
310
    {
311
      ranlist.push_back( ex );
47,404,943✔
312
    }
313
  }
314
  THREAD_CHECKPOINT( scripts, 118 );
56,548,157✔
315

316
  runlist.swap( ranlist );
56,548,157✔
317
  THREAD_CHECKPOINT( scripts, 119 );
56,548,157✔
318
}
56,548,157✔
319

320
void ScriptScheduler::schedule( UOExecutor* exec )
187✔
321
{
322
  exec->setDebugLevel( Bscript::Executor::NONE );
187✔
323
  enqueue( exec );
187✔
324
}
187✔
325

326
unsigned int ScriptScheduler::get_new_pid( UOExecutor* exec )
219✔
327
{
328
  for ( ;; )
329
  {
330
    unsigned int newpid = next_pid;
219✔
331

332
    // increase next_pid and wrap so it's always within the positive values that fit an int
333
    if ( next_pid == INT_MAX )
219✔
334
      next_pid = PID_MIN;
×
335
    else
336
      ++next_pid;
219✔
337

338
    // NOTE: The code below is pessimistic, is there a way to avoid checking the pidlist every time?
339
    // (Nando, 06-Nov-2016)
340

341
    if ( pidlist.find( newpid ) == pidlist.end() )
219✔
342
    {
343
      pidlist[newpid] = exec;
219✔
344
      return newpid;
219✔
345
    }
346
  }
×
347
}
348

349
bool ScriptScheduler::find_exec( unsigned int pid, UOExecutor** exec )
88✔
350
{
351
  auto itr = pidlist.find( pid );
88✔
352
  if ( itr != pidlist.end() )
88✔
353
  {
354
    *exec = ( *itr ).second;
86✔
355
    return true;
86✔
356
  }
357
  else
358
  {
359
    *exec = nullptr;
2✔
360
    return false;
2✔
361
  }
362
}
363

364
bool ScriptScheduler::logScriptVariables( const std::string& name ) const
1✔
365
{
366
  std::string log = fmt::format( "{} {}\n", GET_LOG_FILESTAMP, name );
2✔
367
  std::vector<Bscript::Executor*> scripts;
1✔
368
  for ( const auto& exec : runlist )
2✔
369
  {
370
    if ( exec != nullptr && stricmp( exec->scriptname().c_str(), name.c_str() ) == 0 )
1✔
371
      scripts.push_back( exec );
×
372
  }
373
  for ( const auto& exec : ranlist )
1✔
374
  {
375
    if ( exec != nullptr && stricmp( exec->scriptname().c_str(), name.c_str() ) == 0 )
×
376
      scripts.push_back( exec );
×
377
  }
378
  for ( const auto& exec : holdlist )
3✔
379
  {
380
    if ( exec.second != nullptr && stricmp( exec.second->scriptname().c_str(), name.c_str() ) == 0 )
2✔
381
      scripts.push_back( exec.second );
×
382
  }
383
  for ( const auto& exec : notimeoutholdlist )
2✔
384
  {
385
    if ( exec != nullptr && stricmp( exec->scriptname().c_str(), name.c_str() ) == 0 )
1✔
386
      scripts.push_back( exec );
1✔
387
  }
388
  for ( const auto& exec : scripts )
2✔
389
  {
390
    fmt::format_to( std::back_inserter( log ), "Size: {}", exec->sizeEstimate() );
2✔
391
    auto prog = const_cast<Bscript::EScriptProgram*>( exec->prog() );
1✔
392
    if ( prog->read_dbg_file() != 0 )
1✔
393
    {
394
      log += " failed to load debug info\n";
×
395
      break;
×
396
    }
397
    size_t i = 0;
1✔
398
    log += "\nGlobals\n";
1✔
399
    for ( const auto& global : ( *exec->Globals2 ) )
1✔
400
    {
401
      fmt::format_to(
×
402
          std::back_inserter( log ), "  {} ({}) {}\n",
403
          prog->globalvarnames.size() > i ? prog->globalvarnames[i] : std::to_string( i ),
×
404
          global->impref().typeOf(), global->impref().sizeEstimate() );
×
405
    }
406
    log += "Locals\n";
1✔
407
    auto log_stack = [&]( unsigned PC, Bscript::BObjectRefVec* locals )
1✔
408
    {
409
      fmt::format_to( std::back_inserter( log ), "  {}: {}\n",
1✔
410
                      prog->dbg_filenames[prog->dbg_filenum[PC]], prog->dbg_linenum[PC] );
1✔
411

412

413
      unsigned block = prog->dbg_ins_blocks[PC];
1✔
414
      size_t left = locals->size();
1✔
415
      while ( left )
22✔
416
      {
417
        while ( left <= prog->blocks[block].parentvariables )
27✔
418
        {
419
          block = prog->blocks[block].parentblockidx;
6✔
420
        }
421
        const Bscript::EPDbgBlock& progblock = prog->blocks[block];
21✔
422
        size_t varidx = left - 1 - progblock.parentvariables;
21✔
423
        left--;
21✔
424
        Bscript::BObjectImp* ptr = ( *locals )[varidx]->impptr();
21✔
425
        fmt::format_to( std::back_inserter( log ), "  {} ({}) {}\n",
21✔
426
                        progblock.localvarnames[varidx], ptr->typeOf(), ptr->sizeEstimate() );
42✔
427
      }
428
    };
1✔
429
    log_stack( exec->PC, exec->Locals2 );
1✔
430
    for ( int stack_i = static_cast<int>( exec->ControlStack.size() ) - 1; stack_i >= 0; --stack_i )
1✔
431
    {
432
      fmt::format_to( std::back_inserter( log ), "Stack {}\n", stack_i );
×
433
      log_stack( exec->ControlStack[stack_i].PC, exec->upperLocals2[stack_i] );
×
434
    }
435
  }
436
  auto logf = OPEN_FLEXLOG( "log/scriptmemory.log", false );
1✔
437
  FLEXLOGLN( logf, log );
1✔
438
  CLOSE_FLEXLOG( logf );
1✔
439
  return true;
1✔
440
}
1✔
441

442
void ScriptScheduler::revive_debugged( UOExecutor* exec )
9✔
443
{
444
  debuggerholdlist.erase( exec );
9✔
445
  enqueue( exec );
9✔
446
}
9✔
447

448
void ScriptScheduler::revive_timeout( UOExecutor* exec, TimeoutHandle hold_itr )
9,425,152✔
449
{
450
  holdlist.erase( hold_itr );
9,425,152✔
451
  enqueue( exec );
9,425,152✔
452
}
9,425,152✔
453

454
void ScriptScheduler::revive_notimeout( UOExecutor* exec )
675✔
455
{
456
  notimeoutholdlist.erase( exec );
675✔
457
  enqueue( exec );
675✔
458
}
675✔
459

460
void ScriptScheduler::enqueue( UOExecutor* exec )
9,426,023✔
461
{
462
  passert_always( exec->in_hold_list() == NO_LIST );
9,426,023✔
463
  runlist.push_back( exec );
9,426,023✔
464
}
9,426,023✔
465

466
void ScriptScheduler::free_pid( unsigned int pid )
219✔
467
{
468
  pidlist.erase( pid );
219✔
469
}
219✔
470
}  // namespace Core
471
}  // namespace Pol
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc