2006-10-06 17:05:27 +00:00
|
|
|
|
/*! \file cque.cpp
|
2007-01-03 03:11:53 +00:00
|
|
|
|
* \brief Commands and functions for manipulating the command queue.
|
2006-10-06 17:05:27 +00:00
|
|
|
|
*
|
|
|
|
|
|
* This forms the upper-level command list queue, and includes timed commands
|
|
|
|
|
|
* and semaphores. The lower-level task implementation is found in timer.cpp.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
#include "copyright.h"
|
|
|
|
|
|
#include "autoconf.h"
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
#include "externs.h"
|
|
|
|
|
|
|
|
|
|
|
|
bool break_called = false;
|
|
|
|
|
|
|
|
|
|
|
|
static CLinearTimeDelta GetProcessorUsage(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
CLinearTimeDelta ltd;
|
2008-01-29 12:16:54 -08:00
|
|
|
|
#if defined(WINDOWS_PROCESSES)
|
2012-02-15 11:39:03 -08:00
|
|
|
|
|
|
|
|
|
|
FILETIME ftCreate;
|
|
|
|
|
|
FILETIME ftExit;
|
|
|
|
|
|
FILETIME ftKernel;
|
|
|
|
|
|
FILETIME ftUser;
|
2026-03-03 11:59:40 -07:00
|
|
|
|
GetProcessTimes(GetCurrentProcess(), &ftCreate, &ftExit, &ftKernel, &ftUser);
|
Replace custom integer types (INT8..INT64, UINT8..UINT64) with <cstdint> standard types.
Remove platform-specific typedef chains and limit macros from config.h,
add #include <cstdint>, and replace all usages across 46 files with
int8_t..int64_t, uint8_t..uint64_t, uintptr_t, and standard INT*_MIN/
INT*_MAX/UINT*_MAX limit macros. Semantic aliases (UTF8, UTF16, UTF32,
MUX_CID, MUX_IID, LBUF_OFFSET, etc.) are preserved but now defined in
terms of standard types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:53:58 -07:00
|
|
|
|
ltd.Set100ns(*reinterpret_cast<int64_t*>(&ftUser));
|
2012-02-15 11:39:03 -08:00
|
|
|
|
|
2008-01-29 12:16:54 -08:00
|
|
|
|
#endif // WINDOWS_PROCESSES
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2012-02-15 11:39:03 -08:00
|
|
|
|
#if defined(UNIX_PROCESSES)
|
|
|
|
|
|
#if defined(HAVE_GETRUSAGE)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
struct rusage usage;
|
|
|
|
|
|
getrusage(RUSAGE_SELF, &usage);
|
|
|
|
|
|
ltd.SetTimeValueStruct(&usage.ru_utime);
|
|
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
|
|
CLinearTimeAbsolute ltaNow;
|
|
|
|
|
|
ltaNow.GetLocal();
|
|
|
|
|
|
ltd = ltaNow - mudstate.start_time;
|
|
|
|
|
|
#endif
|
2012-02-15 11:39:03 -08:00
|
|
|
|
#endif
|
|
|
|
|
|
return ltd;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// add_to: Adjust an object's queue or semaphore count.
|
|
|
|
|
|
//
|
2026-03-05 16:00:09 -07:00
|
|
|
|
static bool add_to(const dbref executor, const int am, int attrnum, int *pnum)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
dbref aowner;
|
|
|
|
|
|
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf atr_gotten = LBuf_Adopt(atr_get("add_to.68", executor, attrnum, &aowner, &aflags));
|
2026-07-28 19:34:54 -06:00
|
|
|
|
// int64_t, not int (#1402): semaphore counts are attribute integers.
|
|
|
|
|
|
//
|
|
|
|
|
|
int64_t num = mux_atoi64(atr_gotten);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
num += am;
|
|
|
|
|
|
|
2026-07-28 19:34:54 -06:00
|
|
|
|
UTF8 buff[I64BUF_SIZE];
|
2006-09-01 22:59:23 +00:00
|
|
|
|
size_t nlen = 0;
|
|
|
|
|
|
*buff = '\0';
|
|
|
|
|
|
if (num)
|
|
|
|
|
|
{
|
2026-07-28 19:34:54 -06:00
|
|
|
|
nlen = mux_i64toa(num, buff);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-03-05 16:00:09 -07:00
|
|
|
|
if (!atr_add_raw_LEN(executor, attrnum, buff, nlen))
|
|
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_PROBLEMS, "QUE", "SEMAPH");
|
2026-07-28 19:34:54 -06:00
|
|
|
|
log_printf(T("add_to(#%d/%d): failed to persist semaphore count %lld."),
|
|
|
|
|
|
executor, attrnum, static_cast<long long>(num));
|
2026-03-05 16:00:09 -07:00
|
|
|
|
ENDLOG;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (pnum)
|
|
|
|
|
|
{
|
2026-07-28 19:34:54 -06:00
|
|
|
|
// Caller still takes int; clamp after full-width arithmetic.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (num > INT_MAX)
|
|
|
|
|
|
{
|
|
|
|
|
|
*pnum = INT_MAX;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (num < INT_MIN)
|
|
|
|
|
|
{
|
|
|
|
|
|
*pnum = INT_MIN;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
*pnum = static_cast<int>(num);
|
|
|
|
|
|
}
|
2026-03-05 16:00:09 -07:00
|
|
|
|
}
|
|
|
|
|
|
return true;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-24 08:26:59 -06:00
|
|
|
|
// Max recursive depth for process_command_list_inline (@include,
|
|
|
|
|
|
// @dolist/now, and any other inline action-list expansion). Bounds
|
|
|
|
|
|
// stack growth from mutual @include recursion.
|
|
|
|
|
|
//
|
|
|
|
|
|
static const int MAX_INCLUDE_NEST = 50;
|
|
|
|
|
|
|
2026-06-10 09:00:21 -06:00
|
|
|
|
// process_command_list_inline: run a semicolon-separated command list
|
|
|
|
|
|
// synchronously, honoring @break/@assert (the loop stops when
|
|
|
|
|
|
// break_called fires) and ;| piping (%|), exactly like the queued
|
|
|
|
|
|
// runner below. Used by @dolist/now and @include (#788).
|
|
|
|
|
|
//
|
|
|
|
|
|
// The list is its own pipe domain, like a fresh queue entry: the
|
|
|
|
|
|
// enclosing pipe context is saved and restored, so an inline list can
|
|
|
|
|
|
// itself run inside a piped segment without corrupting the outer
|
|
|
|
|
|
// capture, and pipes inside the list behave as they would queued.
|
|
|
|
|
|
//
|
|
|
|
|
|
// parse_to() rewrites the buffer in place; callers pass a private copy.
|
|
|
|
|
|
//
|
|
|
|
|
|
void process_command_list_inline(dbref executor, dbref caller, dbref enactor,
|
|
|
|
|
|
int eval, UTF8 *clist,
|
|
|
|
|
|
const UTF8 *cargs[], int ncargs)
|
|
|
|
|
|
{
|
2026-07-24 08:26:59 -06:00
|
|
|
|
if (mudstate.include_nest_lev >= MAX_INCLUDE_NEST)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("Include nesting limit exceeded."));
|
2026-07-24 08:26:59 -06:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
mudstate.include_nest_lev++;
|
|
|
|
|
|
|
2026-06-10 09:00:21 -06:00
|
|
|
|
UTF8 *save_pout = mudstate.pout;
|
|
|
|
|
|
UTF8 *save_poutnew = mudstate.poutnew;
|
|
|
|
|
|
UTF8 *save_poutbufc = mudstate.poutbufc;
|
|
|
|
|
|
dbref save_poutobj = mudstate.poutobj;
|
|
|
|
|
|
bool save_inpipe = mudstate.inpipe;
|
|
|
|
|
|
int save_nest = mudstate.pipe_nest_lev;
|
|
|
|
|
|
mudstate.pout = nullptr;
|
|
|
|
|
|
mudstate.poutnew = nullptr;
|
|
|
|
|
|
mudstate.poutbufc = nullptr;
|
|
|
|
|
|
mudstate.poutobj = NOTHING;
|
|
|
|
|
|
mudstate.inpipe = false;
|
|
|
|
|
|
mudstate.pipe_nest_lev = 0;
|
|
|
|
|
|
|
|
|
|
|
|
while ( clist
|
|
|
|
|
|
&& !break_called)
|
|
|
|
|
|
{
|
|
|
|
|
|
UTF8 *cp = parse_to(&clist, ';', EV_STRIP_AROUND);
|
|
|
|
|
|
if ( cp
|
|
|
|
|
|
&& *cp)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Will this command be piped into the next?
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( clist
|
|
|
|
|
|
&& *clist == '|'
|
|
|
|
|
|
&& mudstate.pipe_nest_lev < mudconf.ntfy_nest_lim)
|
|
|
|
|
|
{
|
|
|
|
|
|
clist++;
|
|
|
|
|
|
mudstate.pipe_nest_lev++;
|
|
|
|
|
|
mudstate.inpipe = true;
|
|
|
|
|
|
|
|
|
|
|
|
mudstate.poutnew = alloc_lbuf("inline_list.pipe");
|
|
|
|
|
|
mudstate.poutbufc = mudstate.poutnew;
|
|
|
|
|
|
mudstate.poutobj = executor;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
mudstate.inpipe = false;
|
|
|
|
|
|
mudstate.poutobj = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
process_command(executor, caller, enactor, eval, false, cp,
|
|
|
|
|
|
cargs, ncargs);
|
|
|
|
|
|
|
|
|
|
|
|
// Transition %| value.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (mudstate.pout)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(mudstate.pout);
|
|
|
|
|
|
mudstate.pout = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (mudstate.poutnew)
|
|
|
|
|
|
{
|
|
|
|
|
|
*mudstate.poutbufc = '\0';
|
|
|
|
|
|
mudstate.pout = mudstate.poutnew;
|
|
|
|
|
|
mudstate.poutnew = nullptr;
|
|
|
|
|
|
mudstate.poutbufc = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clean up this list's %| and restore the enclosing pipe context.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (mudstate.pout)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(mudstate.pout);
|
|
|
|
|
|
mudstate.pout = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (mudstate.poutnew)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(mudstate.poutnew);
|
|
|
|
|
|
mudstate.poutnew = nullptr;
|
|
|
|
|
|
mudstate.poutbufc = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
mudstate.pout = save_pout;
|
|
|
|
|
|
mudstate.poutnew = save_poutnew;
|
|
|
|
|
|
mudstate.poutbufc = save_poutbufc;
|
|
|
|
|
|
mudstate.poutobj = save_poutobj;
|
|
|
|
|
|
mudstate.inpipe = save_inpipe;
|
|
|
|
|
|
mudstate.pipe_nest_lev = save_nest;
|
2026-07-24 08:26:59 -06:00
|
|
|
|
|
|
|
|
|
|
mudstate.include_nest_lev--;
|
2026-06-10 09:00:21 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// This Task assumes that pEntry is already unlinked from any lists it may
|
|
|
|
|
|
// have been related to.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
static void Task_RunQueueEntry(void *pEntry, const int iUnused)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(iUnused);
|
|
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto point = static_cast<BQUE*>(pEntry);
|
|
|
|
|
|
const dbref executor = point->executor;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( Good_obj(executor)
|
|
|
|
|
|
&& !Going(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
giveto(executor, mudconf.waitcost);
|
|
|
|
|
|
mudstate.curr_enactor = point->enactor;
|
|
|
|
|
|
mudstate.curr_executor = executor;
|
|
|
|
|
|
a_Queue(Owner(executor), -1);
|
|
|
|
|
|
point->executor = NOTHING;
|
|
|
|
|
|
if (!Halted(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Load scratch args.
|
|
|
|
|
|
//
|
|
|
|
|
|
for (int i = 0; i < MAX_GLOBAL_REGS; i++)
|
|
|
|
|
|
{
|
2006-11-05 13:43:16 +00:00
|
|
|
|
if (mudstate.global_regs[i])
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2006-11-05 13:43:16 +00:00
|
|
|
|
RegRelease(mudstate.global_regs[i]);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
mudstate.global_regs[i] = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
mudstate.global_regs[i] = point->scr[i];
|
2018-10-03 17:54:51 +00:00
|
|
|
|
point->scr[i] = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
NamedRegsClear(mudstate.named_regs);
|
|
|
|
|
|
mudstate.named_regs = point->named_scr;
|
|
|
|
|
|
point->named_scr = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-12-18 00:51:54 -08:00
|
|
|
|
#if defined(STUB_SLAVE)
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != mudstate.pResultsSet)
|
2007-12-18 00:51:54 -08:00
|
|
|
|
{
|
2007-12-18 18:36:20 -08:00
|
|
|
|
mudstate.pResultsSet->Release();
|
2018-10-03 17:54:51 +00:00
|
|
|
|
mudstate.pResultsSet = nullptr;
|
2007-12-18 00:51:54 -08:00
|
|
|
|
}
|
2007-12-18 18:36:20 -08:00
|
|
|
|
mudstate.pResultsSet = point->pResultsSet;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
point->pResultsSet = nullptr;
|
2007-12-19 20:46:49 -08:00
|
|
|
|
mudstate.iRow = point->iRow;
|
2007-12-18 00:51:54 -08:00
|
|
|
|
#endif // STUB_SLAVE
|
|
|
|
|
|
|
2026-03-07 17:51:48 -07:00
|
|
|
|
// Restore iter/switch context from queue entry.
|
|
|
|
|
|
//
|
|
|
|
|
|
bool bIterContext = (point->iter_token != nullptr);
|
|
|
|
|
|
bool bSwitchContext = (point->switch_token != nullptr);
|
|
|
|
|
|
const UTF8 *save_switch = nullptr;
|
|
|
|
|
|
|
|
|
|
|
|
if (bIterContext)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool bLoopInBounds = ( 0 <= mudstate.in_loop
|
|
|
|
|
|
&& mudstate.in_loop < MAX_ITEXT);
|
|
|
|
|
|
if (bLoopInBounds)
|
|
|
|
|
|
{
|
|
|
|
|
|
mudstate.itext[mudstate.in_loop] = point->iter_token;
|
|
|
|
|
|
mudstate.inum[mudstate.in_loop] = point->iter_number;
|
|
|
|
|
|
}
|
|
|
|
|
|
mudstate.in_loop++;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (bSwitchContext)
|
|
|
|
|
|
{
|
|
|
|
|
|
save_switch = mudstate.switch_token;
|
|
|
|
|
|
mudstate.switch_token = point->switch_token;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *command = point->comm;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
mux_assert(!mudstate.inpipe);
|
|
|
|
|
|
mux_assert(mudstate.pipe_nest_lev == 0);
|
|
|
|
|
|
mux_assert(mudstate.poutobj == NOTHING);
|
|
|
|
|
|
mux_assert(!mudstate.pout);
|
|
|
|
|
|
|
|
|
|
|
|
break_called = false;
|
|
|
|
|
|
while ( command
|
|
|
|
|
|
&& !break_called)
|
|
|
|
|
|
{
|
|
|
|
|
|
mux_assert(!mudstate.poutnew);
|
|
|
|
|
|
mux_assert(!mudstate.poutbufc);
|
|
|
|
|
|
|
2026-03-03 20:03:33 -07:00
|
|
|
|
UTF8 *cp = parse_to(&command, ';', EV_STRIP_AROUND);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if ( cp
|
|
|
|
|
|
&& *cp)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Will command be piped?
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( command
|
|
|
|
|
|
&& *command == '|'
|
|
|
|
|
|
&& mudstate.pipe_nest_lev < mudconf.ntfy_nest_lim)
|
|
|
|
|
|
{
|
|
|
|
|
|
command++;
|
|
|
|
|
|
mudstate.pipe_nest_lev++;
|
|
|
|
|
|
mudstate.inpipe = true;
|
|
|
|
|
|
|
|
|
|
|
|
mudstate.poutnew = alloc_lbuf("process_command.pipe");
|
|
|
|
|
|
mudstate.poutbufc = mudstate.poutnew;
|
|
|
|
|
|
mudstate.poutobj = executor;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
mudstate.inpipe = false;
|
|
|
|
|
|
mudstate.poutobj = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CLinearTimeAbsolute ltaBegin;
|
|
|
|
|
|
ltaBegin.GetUTC();
|
2018-06-03 03:27:25 -06:00
|
|
|
|
alarm_clock.set(mudconf.max_cmdsecs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
CLinearTimeDelta ltdUsageBegin = GetProcessorUsage();
|
|
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const UTF8 *log_cmdbuf = process_command(executor, point->caller,
|
|
|
|
|
|
point->enactor, point->eval, false, cp, const_cast<const UTF8**>(point->env),
|
|
|
|
|
|
point->nargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
CLinearTimeAbsolute ltaEnd;
|
|
|
|
|
|
ltaEnd.GetUTC();
|
2018-06-03 03:27:25 -06:00
|
|
|
|
if (alarm_clock.alarmed)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("GAME: Expensive activity abbreviated."));
|
fix: exempt wizard players from the CPU guard's collateral HALT
When a command exceeds max_cmdsecs, the runaway guard abbreviates it
(via the polled alarm flag) and then HALTs both the enactor and the
executor. The HALT fires after process_command has already returned,
so it never protects the current timeslice -- it only quarantines
future work. That is the right response for a machine object, whose
future work is more of the same runaway loop, but wrong for a wizard
player, whose future work is the next typed command -- often the one
that fixes the problem. Classic casualty: an admin running @dbclean
as God left #1 HALTED, with its queue flushed and wait_que refusing
new entries, until the flag was cleared by hand.
Both guard sites now skip the HALT flag and the halt_que flush,
per-object, when the target is a wizard player (God included): the
queue-side guard in Task_RunQueueEntry and the interactive guard in
the network command path. Machine objects -- even wizard-owned ones --
and mortal players quarantine exactly as before, and abbreviation
itself is untouched for everyone. A LOG_PROBLEMS line records each
suppressed HALT, closing the visibility gap where the default
rpt_cmdsecs (120s) exceeds max_cmdsecs (60s).
This also fixes a latent no-op: the executor-side s_Flags call used
point->executor, which the runner sets to NOTHING before executing,
so it stamped the db[-1] sentinel slot (and issued a junk SQLite
UpdateFlags(-1)) instead of the real executor. The enactor line was
what actually darked #1. The guard now uses the correct local.
No smoke case: exercising the guard needs a multi-second burn against
a lowered lag_limit, which is timing-dependent and wrong for the
harness. Verified by direct muxscript probes across all three lanes
(wizard player exempt and live; machine object HALTed; mortal player
HALTed) plus a clean full smoke run.
Closes #896.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:11:32 -06:00
|
|
|
|
|
|
|
|
|
|
// The command has already returned by this point --
|
|
|
|
|
|
// polling loops abbreviated it mid-run -- so the HALT
|
|
|
|
|
|
// only quarantines future work. That is right for a
|
|
|
|
|
|
// machine object, whose future work is more of the
|
|
|
|
|
|
// same runaway, but wrong for a wizard player, whose
|
|
|
|
|
|
// future work is the next typed command -- often the
|
|
|
|
|
|
// one that fixes the problem. Don't dark the admin.
|
|
|
|
|
|
//
|
2026-07-24 08:26:59 -06:00
|
|
|
|
// halt_que's first arg is the owner key (matched
|
|
|
|
|
|
// against Owner(entry->executor)), not the running
|
|
|
|
|
|
// object. For a non-player object, halt only that
|
|
|
|
|
|
// object's entries; for a player, halt all of theirs.
|
|
|
|
|
|
// Good_obj before touching flags (enactor/executor
|
|
|
|
|
|
// may be gone by the time the alarm fires).
|
|
|
|
|
|
//
|
fix: exempt wizard players from the CPU guard's collateral HALT
When a command exceeds max_cmdsecs, the runaway guard abbreviates it
(via the polled alarm flag) and then HALTs both the enactor and the
executor. The HALT fires after process_command has already returned,
so it never protects the current timeslice -- it only quarantines
future work. That is the right response for a machine object, whose
future work is more of the same runaway loop, but wrong for a wizard
player, whose future work is the next typed command -- often the one
that fixes the problem. Classic casualty: an admin running @dbclean
as God left #1 HALTED, with its queue flushed and wait_que refusing
new entries, until the flag was cleared by hand.
Both guard sites now skip the HALT flag and the halt_que flush,
per-object, when the target is a wizard player (God included): the
queue-side guard in Task_RunQueueEntry and the interactive guard in
the network command path. Machine objects -- even wizard-owned ones --
and mortal players quarantine exactly as before, and abbreviation
itself is untouched for everyone. A LOG_PROBLEMS line records each
suppressed HALT, closing the visibility gap where the default
rpt_cmdsecs (120s) exceeds max_cmdsecs (60s).
This also fixes a latent no-op: the executor-side s_Flags call used
point->executor, which the runner sets to NOTHING before executing,
so it stamped the db[-1] sentinel slot (and issued a junk SQLite
UpdateFlags(-1)) instead of the real executor. The enactor line was
what actually darked #1. The guard now uses the correct local.
No smoke case: exercising the guard needs a multi-second burn against
a lowered lag_limit, which is timing-dependent and wrong for the
harness. Verified by direct muxscript probes across all three lanes
(wizard player exempt and live; machine object HALTed; mortal player
HALTed) plus a clean full smoke run.
Closes #896.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:11:32 -06:00
|
|
|
|
bool bExemptEnactor = isPlayer(point->enactor)
|
|
|
|
|
|
&& Wizard(point->enactor);
|
|
|
|
|
|
bool bExemptExecutor = isPlayer(executor)
|
|
|
|
|
|
&& Wizard(executor);
|
2026-07-24 08:26:59 -06:00
|
|
|
|
if ( !bExemptEnactor
|
|
|
|
|
|
&& Good_obj(point->enactor))
|
fix: exempt wizard players from the CPU guard's collateral HALT
When a command exceeds max_cmdsecs, the runaway guard abbreviates it
(via the polled alarm flag) and then HALTs both the enactor and the
executor. The HALT fires after process_command has already returned,
so it never protects the current timeslice -- it only quarantines
future work. That is the right response for a machine object, whose
future work is more of the same runaway loop, but wrong for a wizard
player, whose future work is the next typed command -- often the one
that fixes the problem. Classic casualty: an admin running @dbclean
as God left #1 HALTED, with its queue flushed and wait_que refusing
new entries, until the flag was cleared by hand.
Both guard sites now skip the HALT flag and the halt_que flush,
per-object, when the target is a wizard player (God included): the
queue-side guard in Task_RunQueueEntry and the interactive guard in
the network command path. Machine objects -- even wizard-owned ones --
and mortal players quarantine exactly as before, and abbreviation
itself is untouched for everyone. A LOG_PROBLEMS line records each
suppressed HALT, closing the visibility gap where the default
rpt_cmdsecs (120s) exceeds max_cmdsecs (60s).
This also fixes a latent no-op: the executor-side s_Flags call used
point->executor, which the runner sets to NOTHING before executing,
so it stamped the db[-1] sentinel slot (and issued a junk SQLite
UpdateFlags(-1)) instead of the real executor. The enactor line was
what actually darked #1. The guard now uses the correct local.
No smoke case: exercising the guard needs a multi-second burn against
a lowered lag_limit, which is timing-dependent and wrong for the
harness. Verified by direct muxscript probes across all three lanes
(wizard player exempt and live; machine object HALTed; mortal player
HALTed) plus a clean full smoke run.
Closes #896.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:11:32 -06:00
|
|
|
|
{
|
2026-07-24 08:26:59 -06:00
|
|
|
|
s_Halted(point->enactor);
|
|
|
|
|
|
if (isPlayer(point->enactor))
|
|
|
|
|
|
{
|
|
|
|
|
|
halt_que(point->enactor, NOTHING);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
halt_que(Owner(point->enactor), point->enactor);
|
|
|
|
|
|
}
|
fix: exempt wizard players from the CPU guard's collateral HALT
When a command exceeds max_cmdsecs, the runaway guard abbreviates it
(via the polled alarm flag) and then HALTs both the enactor and the
executor. The HALT fires after process_command has already returned,
so it never protects the current timeslice -- it only quarantines
future work. That is the right response for a machine object, whose
future work is more of the same runaway loop, but wrong for a wizard
player, whose future work is the next typed command -- often the one
that fixes the problem. Classic casualty: an admin running @dbclean
as God left #1 HALTED, with its queue flushed and wait_que refusing
new entries, until the flag was cleared by hand.
Both guard sites now skip the HALT flag and the halt_que flush,
per-object, when the target is a wizard player (God included): the
queue-side guard in Task_RunQueueEntry and the interactive guard in
the network command path. Machine objects -- even wizard-owned ones --
and mortal players quarantine exactly as before, and abbreviation
itself is untouched for everyone. A LOG_PROBLEMS line records each
suppressed HALT, closing the visibility gap where the default
rpt_cmdsecs (120s) exceeds max_cmdsecs (60s).
This also fixes a latent no-op: the executor-side s_Flags call used
point->executor, which the runner sets to NOTHING before executing,
so it stamped the db[-1] sentinel slot (and issued a junk SQLite
UpdateFlags(-1)) instead of the real executor. The enactor line was
what actually darked #1. The guard now uses the correct local.
No smoke case: exercising the guard needs a multi-second burn against
a lowered lag_limit, which is timing-dependent and wrong for the
harness. Verified by direct muxscript probes across all three lanes
(wizard player exempt and live; machine object HALTed; mortal player
HALTed) plus a clean full smoke run.
Closes #896.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:11:32 -06:00
|
|
|
|
}
|
2026-07-24 08:26:59 -06:00
|
|
|
|
if ( !bExemptExecutor
|
|
|
|
|
|
&& Good_obj(executor))
|
fix: exempt wizard players from the CPU guard's collateral HALT
When a command exceeds max_cmdsecs, the runaway guard abbreviates it
(via the polled alarm flag) and then HALTs both the enactor and the
executor. The HALT fires after process_command has already returned,
so it never protects the current timeslice -- it only quarantines
future work. That is the right response for a machine object, whose
future work is more of the same runaway loop, but wrong for a wizard
player, whose future work is the next typed command -- often the one
that fixes the problem. Classic casualty: an admin running @dbclean
as God left #1 HALTED, with its queue flushed and wait_que refusing
new entries, until the flag was cleared by hand.
Both guard sites now skip the HALT flag and the halt_que flush,
per-object, when the target is a wizard player (God included): the
queue-side guard in Task_RunQueueEntry and the interactive guard in
the network command path. Machine objects -- even wizard-owned ones --
and mortal players quarantine exactly as before, and abbreviation
itself is untouched for everyone. A LOG_PROBLEMS line records each
suppressed HALT, closing the visibility gap where the default
rpt_cmdsecs (120s) exceeds max_cmdsecs (60s).
This also fixes a latent no-op: the executor-side s_Flags call used
point->executor, which the runner sets to NOTHING before executing,
so it stamped the db[-1] sentinel slot (and issued a junk SQLite
UpdateFlags(-1)) instead of the real executor. The enactor line was
what actually darked #1. The guard now uses the correct local.
No smoke case: exercising the guard needs a multi-second burn against
a lowered lag_limit, which is timing-dependent and wrong for the
harness. Verified by direct muxscript probes across all three lanes
(wizard player exempt and live; machine object HALTed; mortal player
HALTed) plus a clean full smoke run.
Closes #896.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:11:32 -06:00
|
|
|
|
{
|
2026-07-24 08:26:59 -06:00
|
|
|
|
s_Halted(executor);
|
|
|
|
|
|
if (isPlayer(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
halt_que(executor, NOTHING);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
halt_que(Owner(executor), executor);
|
|
|
|
|
|
}
|
fix: exempt wizard players from the CPU guard's collateral HALT
When a command exceeds max_cmdsecs, the runaway guard abbreviates it
(via the polled alarm flag) and then HALTs both the enactor and the
executor. The HALT fires after process_command has already returned,
so it never protects the current timeslice -- it only quarantines
future work. That is the right response for a machine object, whose
future work is more of the same runaway loop, but wrong for a wizard
player, whose future work is the next typed command -- often the one
that fixes the problem. Classic casualty: an admin running @dbclean
as God left #1 HALTED, with its queue flushed and wait_que refusing
new entries, until the flag was cleared by hand.
Both guard sites now skip the HALT flag and the halt_que flush,
per-object, when the target is a wizard player (God included): the
queue-side guard in Task_RunQueueEntry and the interactive guard in
the network command path. Machine objects -- even wizard-owned ones --
and mortal players quarantine exactly as before, and abbreviation
itself is untouched for everyone. A LOG_PROBLEMS line records each
suppressed HALT, closing the visibility gap where the default
rpt_cmdsecs (120s) exceeds max_cmdsecs (60s).
This also fixes a latent no-op: the executor-side s_Flags call used
point->executor, which the runner sets to NOTHING before executing,
so it stamped the db[-1] sentinel slot (and issued a junk SQLite
UpdateFlags(-1)) instead of the real executor. The enactor line was
what actually darked #1. The guard now uses the correct local.
No smoke case: exercising the guard needs a multi-second burn against
a lowered lag_limit, which is timing-dependent and wrong for the
harness. Verified by direct muxscript probes across all three lanes
(wizard player exempt and live; machine object HALTed; mortal player
HALTed) plus a clean full smoke run.
Closes #896.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 17:11:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
if ( bExemptEnactor
|
|
|
|
|
|
|| bExemptExecutor)
|
|
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_PROBLEMS, "CMD", "CPU");
|
|
|
|
|
|
log_name_and_loc(executor);
|
|
|
|
|
|
log_text(T(" expensive activity abbreviated; wizard player exempted from HALT"));
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2018-06-03 03:27:25 -06:00
|
|
|
|
alarm_clock.clear();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
CLinearTimeDelta ltdUsageEnd = GetProcessorUsage();
|
|
|
|
|
|
CLinearTimeDelta ltd = ltdUsageEnd - ltdUsageBegin;
|
|
|
|
|
|
db[executor].cpu_time_used += ltd;
|
|
|
|
|
|
|
|
|
|
|
|
ltd = ltaEnd - ltaBegin;
|
2006-12-28 20:23:22 +00:00
|
|
|
|
if (mudconf.rpt_cmdsecs < ltd)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_PROBLEMS, "CMD", "CPU");
|
|
|
|
|
|
log_name_and_loc(executor);
|
Migrate 47 more alloc_lbuf/free_lbuf sites to LBuf RAII
Second wave: convert manual alloc/free pairs in cque (1), db_rw (4),
conf (5), db (2), engine_com (6), help (7), levels (2), predicates (5),
player (7), funmath (8). Eliminates ~80 explicit free_lbuf calls
including multi-exit error paths in getboolexp1 (5 frees → 0),
get_list, AnnounceConnect/Disconnect, and the eight NOEVAL function
variants (cand/cor/firstof/allof and bool variants).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:55:40 -06:00
|
|
|
|
LBuf logbuf = LBuf_Src("do_top.LOG.cpu");
|
2008-12-27 18:37:01 -08:00
|
|
|
|
mux_sprintf(logbuf, LBUF_SIZE, T(" queued command taking %s secs (enactor #%d): "),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
ltd.ReturnSecondsString(4), point->enactor);
|
|
|
|
|
|
log_text(logbuf);
|
|
|
|
|
|
log_text(log_cmdbuf);
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Transition %| value.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (mudstate.pout)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(mudstate.pout);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
mudstate.pout = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
if (mudstate.poutnew)
|
|
|
|
|
|
{
|
|
|
|
|
|
*mudstate.poutbufc = '\0';
|
|
|
|
|
|
mudstate.pout = mudstate.poutnew;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
mudstate.poutnew = nullptr;
|
|
|
|
|
|
mudstate.poutbufc = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Clean up %| value.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (mudstate.pout)
|
|
|
|
|
|
{
|
|
|
|
|
|
free_lbuf(mudstate.pout);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
mudstate.pout = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
mudstate.pipe_nest_lev = 0;
|
|
|
|
|
|
mudstate.inpipe = false;
|
|
|
|
|
|
mudstate.poutobj = NOTHING;
|
2026-03-07 17:51:48 -07:00
|
|
|
|
|
|
|
|
|
|
// Restore iter/switch context.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (bIterContext)
|
|
|
|
|
|
{
|
|
|
|
|
|
mudstate.in_loop--;
|
|
|
|
|
|
bool bLoopInBounds = ( 0 <= mudstate.in_loop
|
|
|
|
|
|
&& mudstate.in_loop < MAX_ITEXT);
|
|
|
|
|
|
if (bLoopInBounds)
|
|
|
|
|
|
{
|
|
|
|
|
|
mudstate.itext[mudstate.in_loop] = nullptr;
|
|
|
|
|
|
mudstate.inum[mudstate.in_loop] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (bSwitchContext)
|
|
|
|
|
|
{
|
|
|
|
|
|
mudstate.switch_token = save_switch;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
for (auto& i : point->scr)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
if (i)
|
2006-11-05 13:43:16 +00:00
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
RegRelease(i);
|
|
|
|
|
|
i = nullptr;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
}
|
2022-03-14 15:56:59 -06:00
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
NamedRegsClear(point->named_scr);
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
for (auto& global_reg : mudstate.global_regs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (global_reg)
|
2006-11-05 13:43:16 +00:00
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
RegRelease(global_reg);
|
|
|
|
|
|
global_reg = nullptr;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
NamedRegsClear(mudstate.named_regs);
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2007-12-18 00:51:54 -08:00
|
|
|
|
#if defined(STUB_SLAVE)
|
2007-12-19 20:46:49 -08:00
|
|
|
|
mudstate.iRow = RS_TOP;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != mudstate.pResultsSet)
|
2007-12-18 00:51:54 -08:00
|
|
|
|
{
|
2007-12-18 18:36:20 -08:00
|
|
|
|
mudstate.pResultsSet->Release();
|
2018-10-03 17:54:51 +00:00
|
|
|
|
mudstate.pResultsSet = nullptr;
|
2007-12-18 00:51:54 -08:00
|
|
|
|
}
|
|
|
|
|
|
#endif // STUB_SLAVE
|
|
|
|
|
|
|
2026-03-07 17:51:48 -07:00
|
|
|
|
if (point->switch_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
MEMFREE(point->switch_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
point->switch_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (point->iter_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
MEMFREE(point->iter_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
point->iter_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-11-05 13:43:16 +00:00
|
|
|
|
MEMFREE(point->text);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
point->text = nullptr;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
free_qentry(point);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// que_want: Do we want this queue entry?
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
static bool que_want(const BQUE *entry, const dbref ptarg, dbref otarg)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
if ( ptarg != NOTHING
|
|
|
|
|
|
&& ptarg != Owner(entry->executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return ( otarg == NOTHING
|
|
|
|
|
|
|| otarg == entry->executor);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
static void Task_SemaphoreTimeout(void *pExpired, const int iUnused)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(iUnused);
|
|
|
|
|
|
|
|
|
|
|
|
// A semaphore has timed out.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto point = static_cast<BQUE*>(pExpired);
|
2026-03-05 16:00:09 -07:00
|
|
|
|
(void)add_to(point->u.s.sem, -1, point->u.s.attr, nullptr);
|
2007-09-25 23:17:29 -07:00
|
|
|
|
point->u.s.sem = NOTHING;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Task_RunQueueEntry(point, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
void Task_SQLTimeout(void *pExpired, const int iUnused)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-10-20 15:59:17 +00:00
|
|
|
|
UNUSED_PARAMETER(iUnused);
|
|
|
|
|
|
|
2007-09-24 22:59:44 -07:00
|
|
|
|
// A SQL Query has timed out. Actually, this isn't supported.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto point = static_cast<BQUE*>(pExpired);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Task_RunQueueEntry(point, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static dbref Halt_Player_Target;
|
|
|
|
|
|
static dbref Halt_Object_Target;
|
|
|
|
|
|
static int Halt_Entries;
|
|
|
|
|
|
static dbref Halt_Player_Run;
|
|
|
|
|
|
static dbref Halt_Entries_Run;
|
|
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
static int CallBack_HaltQueue(const PTASK_RECORD p)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
if ( p->fpTask == Task_RunQueueEntry
|
|
|
|
|
|
|| p->fpTask == Task_SQLTimeout
|
|
|
|
|
|
|| p->fpTask == Task_SemaphoreTimeout)
|
|
|
|
|
|
{
|
|
|
|
|
|
// This is a @wait, timed Semaphore Task, or timed SQL Query.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto point = static_cast<BQUE*>(p->arg_voidptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (que_want(point, Halt_Player_Target, Halt_Object_Target))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Accounting for pennies and queue quota.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref dbOwner = point->executor;
|
|
|
|
|
|
if (!isPlayer(dbOwner))
|
|
|
|
|
|
{
|
|
|
|
|
|
dbOwner = Owner(dbOwner);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (dbOwner != Halt_Player_Run)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Halt_Player_Run != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
giveto(Halt_Player_Run, mudconf.waitcost * Halt_Entries_Run);
|
|
|
|
|
|
a_Queue(Halt_Player_Run, -Halt_Entries_Run);
|
|
|
|
|
|
}
|
|
|
|
|
|
Halt_Player_Run = dbOwner;
|
|
|
|
|
|
Halt_Entries_Run = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
Halt_Entries++;
|
|
|
|
|
|
Halt_Entries_Run++;
|
|
|
|
|
|
if (p->fpTask == Task_SemaphoreTimeout)
|
|
|
|
|
|
{
|
2026-03-05 16:00:09 -07:00
|
|
|
|
(void)add_to(point->u.s.sem, -1, point->u.s.attr, nullptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
for (auto& i : point->scr)
|
2006-11-05 13:43:16 +00:00
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
if (i)
|
2006-11-05 13:43:16 +00:00
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
RegRelease(i);
|
|
|
|
|
|
i = nullptr;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
NamedRegsClear(point->named_scr);
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2026-03-07 17:51:48 -07:00
|
|
|
|
if (point->switch_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
MEMFREE(point->switch_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
point->switch_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (point->iter_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
MEMFREE(point->iter_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
point->iter_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
MEMFREE(point->text);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
point->text = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
free_qentry(point);
|
|
|
|
|
|
return IU_REMOVE_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
//
|
|
|
|
|
|
// halt_que: Remove all queued commands that match (executor, object).
|
|
|
|
|
|
//
|
|
|
|
|
|
// (NOTHING, NOTHING) matches all queue entries.
|
|
|
|
|
|
// (NOTHING, <object>) matches only queue entries run from <object>.
|
|
|
|
|
|
// (<executor>, NOTHING) matches only queue entries owned by <executor>.
|
|
|
|
|
|
// (<executor>, <object>) matches only queue entries run from <objects>
|
|
|
|
|
|
// and owned by <executor>.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
int halt_que(const dbref executor, const dbref object)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
Halt_Player_Target = executor;
|
|
|
|
|
|
Halt_Object_Target = object;
|
|
|
|
|
|
Halt_Entries = 0;
|
|
|
|
|
|
Halt_Player_Run = NOTHING;
|
|
|
|
|
|
Halt_Entries_Run = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// Process @wait, timed semaphores, and untimed semaphores.
|
|
|
|
|
|
//
|
|
|
|
|
|
scheduler.TraverseUnordered(CallBack_HaltQueue);
|
|
|
|
|
|
|
|
|
|
|
|
if (Halt_Player_Run != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
giveto(Halt_Player_Run, mudconf.waitcost * Halt_Entries_Run);
|
|
|
|
|
|
a_Queue(Halt_Player_Run, -Halt_Entries_Run);
|
|
|
|
|
|
Halt_Player_Run = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
return Halt_Entries;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 19:21:06 -07:00
|
|
|
|
static uint64_t Halt_Pid_Target;
|
2026-03-04 16:50:02 -07:00
|
|
|
|
static dbref Halt_Pid_Executor;
|
|
|
|
|
|
static int Halt_Pid_Entries;
|
|
|
|
|
|
static dbref Halt_Pid_Player_Run;
|
|
|
|
|
|
static int Halt_Pid_Entries_Run;
|
|
|
|
|
|
|
|
|
|
|
|
static int CallBack_HaltQueueByPid(const PTASK_RECORD p)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( p->fpTask == Task_RunQueueEntry
|
|
|
|
|
|
|| p->fpTask == Task_SQLTimeout
|
|
|
|
|
|
|| p->fpTask == Task_SemaphoreTimeout)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p->m_Ticket == Halt_Pid_Target)
|
|
|
|
|
|
{
|
|
|
|
|
|
const auto point = static_cast<BQUE*>(p->arg_voidptr);
|
|
|
|
|
|
|
|
|
|
|
|
// Ownership check: non-wizards can only halt their own entries.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ( !Can_Halt(Halt_Pid_Executor)
|
|
|
|
|
|
&& Owner(point->executor) != Owner(Halt_Pid_Executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Accounting for pennies and queue quota.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref dbOwner = point->executor;
|
|
|
|
|
|
if (!isPlayer(dbOwner))
|
|
|
|
|
|
{
|
|
|
|
|
|
dbOwner = Owner(dbOwner);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (dbOwner != Halt_Pid_Player_Run)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Halt_Pid_Player_Run != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
giveto(Halt_Pid_Player_Run, mudconf.waitcost * Halt_Pid_Entries_Run);
|
|
|
|
|
|
a_Queue(Halt_Pid_Player_Run, -Halt_Pid_Entries_Run);
|
|
|
|
|
|
}
|
|
|
|
|
|
Halt_Pid_Player_Run = dbOwner;
|
|
|
|
|
|
Halt_Pid_Entries_Run = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
Halt_Pid_Entries++;
|
|
|
|
|
|
Halt_Pid_Entries_Run++;
|
|
|
|
|
|
if (p->fpTask == Task_SemaphoreTimeout)
|
|
|
|
|
|
{
|
2026-03-05 16:00:09 -07:00
|
|
|
|
(void)add_to(point->u.s.sem, -1, point->u.s.attr, nullptr);
|
2026-03-04 16:50:02 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& i : point->scr)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i)
|
|
|
|
|
|
{
|
|
|
|
|
|
RegRelease(i);
|
|
|
|
|
|
i = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
NamedRegsClear(point->named_scr);
|
2026-03-04 16:50:02 -07:00
|
|
|
|
|
2026-03-07 17:51:48 -07:00
|
|
|
|
if (point->switch_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
MEMFREE(point->switch_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
point->switch_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (point->iter_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
MEMFREE(point->iter_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
point->iter_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-04 16:50:02 -07:00
|
|
|
|
MEMFREE(point->text);
|
|
|
|
|
|
point->text = nullptr;
|
|
|
|
|
|
free_qentry(point);
|
|
|
|
|
|
return IU_REMOVE_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 19:21:06 -07:00
|
|
|
|
static int halt_que_pid(const dbref executor, const uint64_t pid)
|
2026-03-04 16:50:02 -07:00
|
|
|
|
{
|
|
|
|
|
|
Halt_Pid_Target = pid;
|
|
|
|
|
|
Halt_Pid_Executor = executor;
|
|
|
|
|
|
Halt_Pid_Entries = 0;
|
|
|
|
|
|
Halt_Pid_Player_Run = NOTHING;
|
|
|
|
|
|
Halt_Pid_Entries_Run = 0;
|
|
|
|
|
|
|
|
|
|
|
|
scheduler.TraverseUnordered(CallBack_HaltQueueByPid);
|
|
|
|
|
|
|
|
|
|
|
|
if (Halt_Pid_Player_Run != NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
giveto(Halt_Pid_Player_Run, mudconf.waitcost * Halt_Pid_Entries_Run);
|
|
|
|
|
|
a_Queue(Halt_Pid_Player_Run, -Halt_Pid_Entries_Run);
|
|
|
|
|
|
Halt_Pid_Player_Run = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
return Halt_Pid_Entries;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// do_halt: Command interface to halt_que.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
void do_halt(const dbref executor, const dbref caller, dbref enactor, const int eval, int key, UTF8 *target, const UTF8 *cargs[], int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2006-09-06 06:17:46 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref executor_targ, obj_targ;
|
|
|
|
|
|
|
|
|
|
|
|
if ((key & HALT_ALL) && !Can_Halt(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-04 16:50:02 -07:00
|
|
|
|
// Halt by PID.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (key & HALT_PID)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (key & HALT_ALL)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("Can’t specify /pid and /all"));
|
2026-03-04 16:50:02 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!target || !*target)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("You must specify a PID."));
|
2026-03-04 16:50:02 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-05 19:21:06 -07:00
|
|
|
|
const UTF8 *p = target;
|
|
|
|
|
|
while (mux_isspace(*p))
|
|
|
|
|
|
{
|
|
|
|
|
|
p++;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ('\0' == *p || '-' == *p)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("PID must be a non-negative integer."));
|
2026-03-05 19:21:06 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ('+' == *p)
|
|
|
|
|
|
{
|
|
|
|
|
|
p++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t pid = 0;
|
|
|
|
|
|
bool bHaveDigits = false;
|
|
|
|
|
|
while (mux_isdigit(*p))
|
|
|
|
|
|
{
|
|
|
|
|
|
bHaveDigits = true;
|
|
|
|
|
|
const uint64_t digit = static_cast<uint64_t>(*p - '0');
|
|
|
|
|
|
if (pid > (UINT64_MAX - digit) / 10U)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("PID is out of range."));
|
2026-03-05 19:21:06 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
pid = 10U * pid + digit;
|
|
|
|
|
|
p++;
|
|
|
|
|
|
}
|
|
|
|
|
|
while (mux_isspace(*p))
|
|
|
|
|
|
{
|
|
|
|
|
|
p++;
|
|
|
|
|
|
}
|
|
|
|
|
|
if ( !bHaveDigits
|
|
|
|
|
|
|| '\0' != *p)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("PID must be a non-negative integer."));
|
2026-03-05 19:21:06 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-03-04 16:50:02 -07:00
|
|
|
|
const int numhalted = halt_que_pid(executor, pid);
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (0 == numhalted)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(Owner(executor), M_("No queue entry with that PID was found."));
|
2026-03-04 16:50:02 -07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 22:57:08 -06:00
|
|
|
|
// #1661 / #1622: count goes to the catalogue, not English y/ies.
|
|
|
|
|
|
//
|
|
|
|
|
|
notify(Owner(executor), tprintf(MN_("%d queue entry removed.",
|
|
|
|
|
|
"%d queue entries removed.", numhalted), numhalted));
|
2026-03-04 16:50:02 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// Figure out what to halt.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!target || !*target)
|
|
|
|
|
|
{
|
|
|
|
|
|
obj_targ = NOTHING;
|
|
|
|
|
|
if (key & HALT_ALL)
|
|
|
|
|
|
{
|
|
|
|
|
|
executor_targ = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
executor_targ = Owner(executor);
|
|
|
|
|
|
if (!isPlayer(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
obj_targ = executor;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Can_Halt(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
obj_targ = match_thing(executor, target);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
obj_targ = match_controlled(executor, target);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!Good_obj(obj_targ))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (key & HALT_ALL)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("Can’t specify a target and /all"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isPlayer(obj_targ))
|
|
|
|
|
|
{
|
|
|
|
|
|
executor_targ = obj_targ;
|
|
|
|
|
|
obj_targ = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
executor_targ = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const int numhalted = halt_que(executor_targ, obj_targ);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (Quiet(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-07-27 22:57:08 -06:00
|
|
|
|
// #1661 / #1622: count goes to the catalogue, not English y/ies.
|
|
|
|
|
|
//
|
|
|
|
|
|
notify(Owner(executor), tprintf(MN_("%d queue entry removed.",
|
|
|
|
|
|
"%d queue entries removed.", numhalted), numhalted));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int Notify_Key;
|
|
|
|
|
|
static int Notify_Num_Done;
|
|
|
|
|
|
static int Notify_Num_Max;
|
|
|
|
|
|
static int Notify_Sem;
|
|
|
|
|
|
static int Notify_Attr;
|
|
|
|
|
|
|
|
|
|
|
|
// NFY_DRAIN or NFY_NFYALL
|
|
|
|
|
|
//
|
|
|
|
|
|
static int CallBack_NotifySemaphoreDrainOrAll(PTASK_RECORD p)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p->fpTask == Task_SemaphoreTimeout)
|
|
|
|
|
|
{
|
|
|
|
|
|
// This represents a semaphore.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto point = static_cast<BQUE*>(p->arg_voidptr);
|
2007-09-25 23:17:29 -07:00
|
|
|
|
if ( point->u.s.sem == Notify_Sem
|
|
|
|
|
|
&& ( point->u.s.attr == Notify_Attr
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| !Notify_Attr))
|
|
|
|
|
|
{
|
|
|
|
|
|
Notify_Num_Done++;
|
2007-11-26 22:23:34 -08:00
|
|
|
|
if (NFY_DRAIN == (Notify_Key & NFY_MASK))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Discard the command
|
|
|
|
|
|
//
|
|
|
|
|
|
giveto(point->executor, mudconf.waitcost);
|
|
|
|
|
|
a_Queue(Owner(point->executor), -1);
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
for (auto& i : point->scr)
|
2006-11-05 13:43:16 +00:00
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
if (i)
|
2006-11-05 13:43:16 +00:00
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
RegRelease(i);
|
|
|
|
|
|
i = nullptr;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
NamedRegsClear(point->named_scr);
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2026-03-07 17:51:48 -07:00
|
|
|
|
if (point->switch_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
MEMFREE(point->switch_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
point->switch_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (point->iter_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
MEMFREE(point->iter_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
point->iter_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
MEMFREE(point->text);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
point->text = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
free_qentry(point);
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return IU_REMOVE_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Allow the command to run. The priority may have been
|
|
|
|
|
|
// PRIORITY_SUSPEND, so we need to change it.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (isPlayer(point->enactor))
|
|
|
|
|
|
{
|
|
|
|
|
|
p->iPriority = PRIORITY_PLAYER;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
p->iPriority = PRIORITY_OBJECT;
|
|
|
|
|
|
}
|
|
|
|
|
|
p->ltaWhen.GetUTC();
|
|
|
|
|
|
p->fpTask = Task_RunQueueEntry;
|
|
|
|
|
|
return IU_UPDATE_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-11-26 22:23:34 -08:00
|
|
|
|
// NFY_NFY
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2007-11-26 22:23:34 -08:00
|
|
|
|
static int CallBack_NotifySemaphoreFirst(PTASK_RECORD p)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// If we've notified enough, exit.
|
|
|
|
|
|
//
|
2007-11-26 22:23:34 -08:00
|
|
|
|
if ( NFY_NFY == (Notify_Key & NFY_MASK)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
&& Notify_Num_Done >= Notify_Num_Max)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_DONE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (p->fpTask == Task_SemaphoreTimeout)
|
|
|
|
|
|
{
|
|
|
|
|
|
// This represents a semaphore.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto point = static_cast<BQUE*>(p->arg_voidptr);
|
2007-09-25 23:17:29 -07:00
|
|
|
|
if ( point->u.s.sem == Notify_Sem
|
|
|
|
|
|
&& ( point->u.s.attr == Notify_Attr
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|| !Notify_Attr))
|
|
|
|
|
|
{
|
|
|
|
|
|
Notify_Num_Done++;
|
|
|
|
|
|
|
|
|
|
|
|
// Allow the command to run. The priority may have been
|
|
|
|
|
|
// PRIORITY_SUSPEND, so we need to change it.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (isPlayer(point->enactor))
|
|
|
|
|
|
{
|
|
|
|
|
|
p->iPriority = PRIORITY_PLAYER;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
p->iPriority = PRIORITY_OBJECT;
|
|
|
|
|
|
}
|
|
|
|
|
|
p->ltaWhen.GetUTC();
|
|
|
|
|
|
p->fpTask = Task_RunQueueEntry;
|
|
|
|
|
|
return IU_UPDATE_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// nfy_que: Notify commands from the queue and perform or discard them.
|
|
|
|
|
|
|
|
|
|
|
|
int nfy_que(dbref sem, int attr, int key, int count)
|
|
|
|
|
|
{
|
|
|
|
|
|
int cSemaphore = 1;
|
|
|
|
|
|
if (attr)
|
|
|
|
|
|
{
|
|
|
|
|
|
int aflags;
|
|
|
|
|
|
dbref aowner;
|
2026-04-05 18:34:51 -06:00
|
|
|
|
LBuf str = LBuf_Adopt(atr_get("nfy_que.562", sem, attr, &aowner, &aflags));
|
fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373)
Completes the sweep the issue called for. mux_atol returns long, which
is 32-bit on LLP64, so every caller silently truncated on Windows. Two
of those were real defects (the truthiness family and cf_size, fixed in
the preceding commits); the rest were latent, waiting for a value large
enough to matter.
Rather than audit 290 sites for whether each can reach 2^31 today, use
the 64-bit parser everywhere and remove the class. A dbref cannot
overflow now, but nothing stops a later caller passing that same site a
timestamp or a byte count.
Pure 1:1 substitution: 285 lines changed, and every removed line
contained mux_atol while every added line contains mux_atoi64. No
control flow, no types, no behaviour beyond the wider parse.
This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so
the generated code there is unchanged. It only widens the parse on
Windows. Narrowing destinations are unaffected either way: `int x =
mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both
models.
Left alone: mux_atol itself in mathutil, its declaration, and three
comments that name it. Callers that genuinely want 32-bit semantics can
still ask for them; none appear to.
Verified on Windows: full solution builds clean with no new warnings,
smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched --
identical to before the sweep, with the same 16 build-configuration
failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST).
Spot checks after the change: the boolean family returns 1 for multiples
of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited,
and arithmetic, string and list functions are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:03:45 -06:00
|
|
|
|
cSemaphore = mux_atoi64(str);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Notify_Num_Done = 0;
|
2006-12-28 20:23:22 +00:00
|
|
|
|
if (0 < cSemaphore)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
Notify_Key = key;
|
|
|
|
|
|
Notify_Sem = sem;
|
|
|
|
|
|
Notify_Attr = attr;
|
|
|
|
|
|
Notify_Num_Max = count;
|
2007-11-26 22:23:34 -08:00
|
|
|
|
if (NFY_NFY == (key & NFY_MASK))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-11-26 22:23:34 -08:00
|
|
|
|
scheduler.TraverseOrdered(CallBack_NotifySemaphoreFirst);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
scheduler.TraverseUnordered(CallBack_NotifySemaphoreDrainOrAll);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Update the sem waiters count.
|
|
|
|
|
|
//
|
2007-11-26 22:23:34 -08:00
|
|
|
|
if (NFY_NFY == (key & NFY_MASK))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-03-05 16:00:09 -07:00
|
|
|
|
(void)add_to(sem, -count, attr, nullptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-05 16:00:09 -07:00
|
|
|
|
if (!atr_clr(sem, attr))
|
|
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_PROBLEMS, "QUE", "SEMAPH");
|
|
|
|
|
|
log_printf(T("nfy_que(#%d/%d): failed to clear semaphore count."), sem, attr);
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Notify_Num_Done;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// do_notify: Command interface to nfy_que
|
|
|
|
|
|
|
|
|
|
|
|
void do_notify
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref executor,
|
|
|
|
|
|
dbref caller,
|
|
|
|
|
|
dbref enactor,
|
2007-09-01 23:47:27 -07:00
|
|
|
|
int eval,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int key,
|
|
|
|
|
|
int nargs,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *what,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UTF8 *count,
|
|
|
|
|
|
const UTF8 *cargs[],
|
|
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2007-09-01 23:47:27 -07:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const UTF8 *obj = parse_to(&what, '/', 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
init_match(executor, obj, NOTYPE);
|
|
|
|
|
|
match_everything(0);
|
|
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const dbref thing = noisy_match_result();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!Good_obj(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!Controls(executor, thing) && !Link_ok(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
int atr = A_SEMAPHORE;
|
|
|
|
|
|
if ( what
|
|
|
|
|
|
&& what[0] != '\0')
|
|
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const UTF8 *AttributeName = what;
|
|
|
|
|
|
const int i = mkattr(executor, AttributeName);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (0 < i)
|
|
|
|
|
|
{
|
|
|
|
|
|
atr = i;
|
|
|
|
|
|
if (atr != A_SEMAPHORE)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Do they have permission to set this attribute?
|
|
|
|
|
|
//
|
2026-03-03 18:39:35 -07:00
|
|
|
|
ATTR *ap = static_cast<ATTR *>(anum_get(atr));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!bCanSetAttr(executor, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int loccount;
|
|
|
|
|
|
if ( count
|
|
|
|
|
|
&& count[0] != '\0')
|
|
|
|
|
|
{
|
fix(win32): migrate the remaining mux_atol callers to mux_atoi64 (#1373)
Completes the sweep the issue called for. mux_atol returns long, which
is 32-bit on LLP64, so every caller silently truncated on Windows. Two
of those were real defects (the truthiness family and cf_size, fixed in
the preceding commits); the rest were latent, waiting for a value large
enough to matter.
Rather than audit 290 sites for whether each can reach 2^31 today, use
the 64-bit parser everywhere and remove the class. A dbref cannot
overflow now, but nothing stops a later caller passing that same site a
timestamp or a byte count.
Pure 1:1 substitution: 285 lines changed, and every removed line
contained mux_atol while every added line contains mux_atoi64. No
control flow, no types, no behaviour beyond the wider parse.
This is a NO-OP on LP64 -- long is already 64-bit on Linux and macOS, so
the generated code there is unchanged. It only widens the parse on
Windows. Narrowing destinations are unaffected either way: `int x =
mux_atoi64(s)` truncates exactly as `int x = mux_atol(s)` did, on both
models.
Left alone: mux_atol itself in mathutil, its declaration, and three
comments that name it. Callers that genuinely want 32-bit semantics can
still ask for them; none appear to.
Verified on Windows: full solution builds clean with no new warnings,
smoke is 1418 passed / 16 failed / 0 crashes / 306 of 306 dispatched --
identical to before the sweep, with the same 16 build-configuration
failures (exp3 module not loaded, hmac/digest behind UNIX_DIGEST).
Spot checks after the change: the boolean family returns 1 for multiples
of 2^32, cf_size round-trips 3000000000 and still reads -1 as unlimited,
and arithmetic, string and list functions are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 10:03:45 -06:00
|
|
|
|
loccount = mux_atoi64(count);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
loccount = 1;
|
|
|
|
|
|
}
|
2006-12-28 20:23:22 +00:00
|
|
|
|
|
|
|
|
|
|
if (0 < loccount)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
nfy_que(thing, atr, key, loccount);
|
2007-11-26 22:23:34 -08:00
|
|
|
|
if ( !Quiet(executor)
|
|
|
|
|
|
&& !Quiet(thing)
|
|
|
|
|
|
&& !(key & NFY_QUIET))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-11-26 22:23:34 -08:00
|
|
|
|
if (NFY_DRAIN == (key & NFY_MASK))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify_quiet(executor, M_("Drained."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify_quiet(executor, M_("Notified."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// setup_que: Set up a queue entry.
|
|
|
|
|
|
//
|
2006-11-05 13:43:16 +00:00
|
|
|
|
static BQUE *setup_que
|
|
|
|
|
|
(
|
|
|
|
|
|
dbref executor,
|
|
|
|
|
|
dbref caller,
|
|
|
|
|
|
dbref enactor,
|
|
|
|
|
|
int eval,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *command,
|
2006-11-05 13:43:16 +00:00
|
|
|
|
int nargs,
|
2007-04-09 23:40:23 +00:00
|
|
|
|
const UTF8 *args[],
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
reg_ref *sargs[],
|
|
|
|
|
|
NamedRegsMap *named_sargs = nullptr
|
2006-11-05 13:43:16 +00:00
|
|
|
|
)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
// Can we run commands at all?
|
|
|
|
|
|
//
|
|
|
|
|
|
if (Halted(executor))
|
2006-11-05 13:43:16 +00:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2026-07-24 08:26:59 -06:00
|
|
|
|
// Make sure executor can afford to do it. Keep the charged amount so
|
|
|
|
|
|
// later OOM can refund exactly what payfor took (waitcost, plus at most
|
|
|
|
|
|
// one machinecost penny).
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2026-07-24 08:26:59 -06:00
|
|
|
|
int cost = mudconf.waitcost;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (mudconf.machinecost && RandomINT32(0, mudconf.machinecost-1) == 0)
|
|
|
|
|
|
{
|
2026-07-24 08:26:59 -06:00
|
|
|
|
cost++;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-07-24 08:26:59 -06:00
|
|
|
|
if (!payfor(executor, cost))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(Owner(executor), M_("Not enough money to queue command."));
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Wizards and their objs may queue up to db_top+1 cmds. Players are
|
|
|
|
|
|
// limited to QUEUE_QUOTA. -mnp
|
|
|
|
|
|
//
|
2026-07-24 08:26:59 -06:00
|
|
|
|
int a = QueueMax(Owner(executor));
|
2006-12-28 20:23:22 +00:00
|
|
|
|
if (a < a_Queue(Owner(executor), 1))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
a_Queue(Owner(executor), -1);
|
2026-07-24 12:49:34 -06:00
|
|
|
|
// #1080: refund the payfor charge — OOM paths already do this.
|
|
|
|
|
|
//
|
|
|
|
|
|
giveto(executor, cost);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
notify(Owner(executor),
|
2007-03-14 22:33:04 +00:00
|
|
|
|
T("Run away objects: too many commands queued. Halted."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
halt_que(Owner(executor), NOTHING);
|
|
|
|
|
|
|
|
|
|
|
|
// Halt also means no command execution allowed.
|
|
|
|
|
|
//
|
|
|
|
|
|
s_Halted(executor);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
return nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// We passed all the tests.
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate the length of the save string.
|
|
|
|
|
|
//
|
|
|
|
|
|
size_t tlen = 0;
|
|
|
|
|
|
static size_t nCommand;
|
|
|
|
|
|
static size_t nLenEnv[NUM_ENV_VARS];
|
|
|
|
|
|
|
|
|
|
|
|
if (command)
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
nCommand = strlen(reinterpret_cast<char *>(command)) + 1;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
tlen = nCommand;
|
|
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2006-12-28 20:23:22 +00:00
|
|
|
|
if (NUM_ENV_VARS < nargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
nargs = NUM_ENV_VARS;
|
|
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
for (a = 0; a < nargs; a++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (args[a])
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
nLenEnv[a] = strlen(reinterpret_cast<const char *>(args[a])) + 1;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
tlen += nLenEnv[a];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-24 08:26:59 -06:00
|
|
|
|
// Create the queue entry and load the save string.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto tmp = alloc_qentry("setup_que.qblock");
|
2026-07-24 08:26:59 -06:00
|
|
|
|
if (nullptr == tmp)
|
|
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_PROBLEMS, "QUE", "MEM");
|
|
|
|
|
|
log_printf(T("setup_que: out of memory allocating queue entry."));
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
giveto(executor, cost);
|
|
|
|
|
|
a_Queue(Owner(executor), -1);
|
|
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
2018-10-03 17:54:51 +00:00
|
|
|
|
tmp->comm = nullptr;
|
2026-07-24 08:26:59 -06:00
|
|
|
|
tmp->text = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2022-03-14 15:56:59 -06:00
|
|
|
|
UTF8 *tptr = tmp->text = static_cast<UTF8*>(MEMALLOC(tlen));
|
Remove ISOUTOFMEMORY macro; add per-site OOM recovery and fix g_dump_child_pid
Eliminate the ISOUTOFMEMORY macro that unconditionally aborted on allocation
failure. Each of the 23 call sites now handles OOM appropriately:
- Fatal sites (buffer pools, db array, anum table): mux_assert or OutOfMemory
- Recoverable sites (queue, mail, commands, guests, vattrs, config, restart,
forward lists): log the failure and return gracefully
Also fix g_dump_child_pid portability: volatile pid_t -> volatile sig_atomic_t
with explicit casts in ganl_adapter.cpp.
Close integer overflow issue as false alarm (getstring_noalloc uses a bounded
static buffer, so nBuffer+1 cannot wrap).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:31:20 -06:00
|
|
|
|
if (nullptr == tptr)
|
|
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_PROBLEMS, "QUE", "MEM");
|
|
|
|
|
|
log_printf(T("setup_que: out of memory allocating %zu bytes for queue entry."), tlen);
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
free_qentry(tmp);
|
2026-07-24 08:26:59 -06:00
|
|
|
|
giveto(executor, cost);
|
|
|
|
|
|
a_Queue(Owner(executor), -1);
|
Remove ISOUTOFMEMORY macro; add per-site OOM recovery and fix g_dump_child_pid
Eliminate the ISOUTOFMEMORY macro that unconditionally aborted on allocation
failure. Each of the 23 call sites now handles OOM appropriately:
- Fatal sites (buffer pools, db array, anum table): mux_assert or OutOfMemory
- Recoverable sites (queue, mail, commands, guests, vattrs, config, restart,
forward lists): log the failure and return gracefully
Also fix g_dump_child_pid portability: volatile pid_t -> volatile sig_atomic_t
with explicit casts in ganl_adapter.cpp.
Close integer overflow issue as false alarm (getstring_noalloc uses a bounded
static buffer, so nBuffer+1 cannot wrap).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:31:20 -06:00
|
|
|
|
return nullptr;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (command)
|
|
|
|
|
|
{
|
|
|
|
|
|
memcpy(tptr, command, nCommand);
|
|
|
|
|
|
tmp->comm = tptr;
|
|
|
|
|
|
tptr += nCommand;
|
|
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
for (a = 0; a < nargs; a++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (args[a])
|
|
|
|
|
|
{
|
|
|
|
|
|
memcpy(tptr, args[a], nLenEnv[a]);
|
|
|
|
|
|
tmp->env[a] = tptr;
|
|
|
|
|
|
tptr += nLenEnv[a];
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
tmp->env[a] = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
for ( ; a < NUM_ENV_VARS; a++)
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
tmp->env[a] = nullptr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (sargs)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (a = 0; a < MAX_GLOBAL_REGS; a++)
|
|
|
|
|
|
{
|
2006-11-05 13:43:16 +00:00
|
|
|
|
tmp->scr[a] = sargs[a];
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (sargs[a])
|
|
|
|
|
|
{
|
2006-11-05 13:43:16 +00:00
|
|
|
|
RegAddRef(sargs[a]);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
tmp->named_scr = NamedRegsCopy(named_sargs ? named_sargs : mudstate.named_regs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for (a = 0; a < MAX_GLOBAL_REGS; a++)
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
tmp->scr[a] = nullptr;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
}
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
tmp->named_scr = nullptr;
|
2006-11-05 13:43:16 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2007-12-18 00:51:54 -08:00
|
|
|
|
#if defined(STUB_SLAVE)
|
2007-12-19 09:59:35 -08:00
|
|
|
|
tmp->iRow = mudstate.iRow;
|
2007-12-18 18:36:20 -08:00
|
|
|
|
tmp->pResultsSet = mudstate.pResultsSet;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != mudstate.pResultsSet)
|
2007-12-18 00:51:54 -08:00
|
|
|
|
{
|
2007-12-18 18:36:20 -08:00
|
|
|
|
mudstate.pResultsSet->AddRef();
|
2007-12-18 00:51:54 -08:00
|
|
|
|
}
|
|
|
|
|
|
#endif // STUB_SLAVE
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// Load the rest of the queue block.
|
|
|
|
|
|
//
|
|
|
|
|
|
tmp->executor = executor;
|
|
|
|
|
|
tmp->IsTimed = false;
|
2007-09-25 23:17:29 -07:00
|
|
|
|
tmp->u.s.sem = NOTHING;
|
|
|
|
|
|
tmp->u.s.attr = 0;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
tmp->enactor = enactor;
|
|
|
|
|
|
tmp->caller = caller;
|
2006-09-06 05:46:01 +00:00
|
|
|
|
tmp->eval = eval;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
tmp->nargs = nargs;
|
2026-03-07 17:51:48 -07:00
|
|
|
|
tmp->switch_token = nullptr;
|
|
|
|
|
|
tmp->iter_token = nullptr;
|
|
|
|
|
|
tmp->iter_number = 0;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return tmp;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// wait_que: Add commands to the wait or semaphore queues.
|
2026-07-24 08:26:59 -06:00
|
|
|
|
// Returns true if the command was queued, false if not (CF_INTERP off,
|
|
|
|
|
|
// setup_que failed, etc.). Callers that mutate state before calling
|
|
|
|
|
|
// (e.g. do_wait's semaphore add_to) must reverse on false.
|
2006-09-01 22:59:23 +00:00
|
|
|
|
//
|
2026-07-24 08:26:59 -06:00
|
|
|
|
bool wait_que
|
2006-09-01 22:59:23 +00:00
|
|
|
|
(
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const dbref executor,
|
|
|
|
|
|
const dbref caller,
|
2006-11-05 13:43:16 +00:00
|
|
|
|
dbref enactor,
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const int eval,
|
|
|
|
|
|
const bool bTimed,
|
|
|
|
|
|
const CLinearTimeAbsolute <aWhen,
|
|
|
|
|
|
const dbref sem,
|
|
|
|
|
|
const int attr,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *command,
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const int nargs,
|
2007-04-09 23:40:23 +00:00
|
|
|
|
const UTF8 *args[],
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
reg_ref *sargs[],
|
2026-03-07 17:51:48 -07:00
|
|
|
|
NamedRegsMap *named_sargs,
|
|
|
|
|
|
const UTF8 *iter_token,
|
|
|
|
|
|
int iter_number,
|
|
|
|
|
|
const UTF8 *switch_token
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!(mudconf.control_flags & CF_INTERP))
|
|
|
|
|
|
{
|
2026-07-24 08:26:59 -06:00
|
|
|
|
return false;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-11-05 13:43:16 +00:00
|
|
|
|
BQUE *tmp = setup_que(executor, caller, enactor, eval,
|
|
|
|
|
|
command,
|
|
|
|
|
|
nargs, args,
|
Implement named global registers (#351)
Add TinyMUSH/RhostMUSH-style named registers alongside the existing
36 single-char registers (0-9, a-z). Named registers use alphanumeric
and underscore names up to 32 chars, case-insensitive.
- setq()/setr() accept multi-char names and multi-pair arguments:
setq(myvar,value), setq(a,v1,myvar,v2)
- r() reads named registers: r(myvar)
- %q<name> substitution in the evaluator: %q<myvar>
- regmatch() can assign captures to named registers
- Named registers are stored in a heap-allocated NamedRegsMap (nullptr
when empty) to avoid constructor/destructor issues with pool_alloc'd
BQUE structs
- Save/restore uses a parallel static stack so all 44+ call sites are
unchanged
- Full queue integration: setup_que, wait_que, Task_RunQueueEntry,
all free_qentry and program_data cleanup sites
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 19:03:14 -07:00
|
|
|
|
sargs, named_sargs);
|
2006-11-05 13:43:16 +00:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!tmp)
|
|
|
|
|
|
{
|
2026-07-24 08:26:59 -06:00
|
|
|
|
return false;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-07 17:51:48 -07:00
|
|
|
|
// Copy iter/switch context if provided.
|
|
|
|
|
|
//
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
// Exact-size clones, not LBUFs: tokens are typically a few bytes
|
|
|
|
|
|
// (a list element, a switch match), but a queued fan-out holds one
|
|
|
|
|
|
// per entry until dispatch — 5000 queued @dolist entries held
|
|
|
|
|
|
// 5000 x 32KB = 164MB of pool LBUFs for <=5-byte strings (queue
|
|
|
|
|
|
// review, 2026-07-21).
|
2026-03-07 17:51:48 -07:00
|
|
|
|
if (iter_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
tmp->iter_token = StringClone(iter_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
tmp->iter_number = iter_number;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (switch_token)
|
|
|
|
|
|
{
|
perf(queue): exact-size queue-entry tokens; survey the queue's context shipping
Queue review (docs/survey-queue.md): the design holds up — two heaps
with a monotone-ticket tie-break making FIFO-within-cycle a hard
guarantee, priorities as a dequeue floor, and the scheduler deadline
feeding the network poll timeout so an idle server does nothing.
Context shipped per BQUE is reference-based everywhere semantics
permit: the command+env snapshot is ONE exact-sized allocation,
%q registers are refcounted (RegAddRef), the named-register map is
null-when-empty and shares refcounted values, and dispatch is pure
move semantics. Exactly one copy problem existed:
iter_token/switch_token (the ##/#$ context of queued @dolist/@switch)
each held a full 32KB pool LBUF per entry from enqueue to dispatch,
for strings that are typically a few bytes. Measured before/after
with @list allocations at 5,000 queued entries: 5,002 -> 2 Lbufs in
use; max RSS of a 20k-entry @dolist: 244MB -> 21MB. Fix is
StringClone (exact-size) at the single allocation site, MEMFREE at
the four free sites; ##/#$ content is byte-identical through queued
dispatch.
Also recorded in the survey, no action: safer_iter gates iter_token
shipping entirely; muxscript's EOF drain is paced and time-capped
(~15s) so queue THROUGHPUT needs a live-netmux stress harness (the
natural next hardening step); setup_que's static scratch locals are
single-threaded-only.
Tests: smoke 1319/1319 both toggles; oracle 9/9; jit_diff 400/0
LOGIC; ##/#$ queued-dispatch probes byte-identical.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:20:38 -06:00
|
|
|
|
tmp->switch_token = StringClone(switch_token);
|
2026-03-07 17:51:48 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int iPriority;
|
|
|
|
|
|
if (isPlayer(tmp->enactor))
|
|
|
|
|
|
{
|
|
|
|
|
|
iPriority = PRIORITY_PLAYER;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
iPriority = PRIORITY_OBJECT;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
tmp->IsTimed = bTimed;
|
|
|
|
|
|
tmp->waittime = ltaWhen;
|
2007-09-25 23:17:29 -07:00
|
|
|
|
tmp->u.s.sem = sem;
|
|
|
|
|
|
tmp->u.s.attr = attr;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2026-07-31 09:09:18 -06:00
|
|
|
|
bool bDeferred = false;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (sem == NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Not a semaphore, so let it run it immediately or put it on
|
|
|
|
|
|
// the wait queue.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (tmp->IsTimed)
|
|
|
|
|
|
{
|
2026-07-31 09:09:18 -06:00
|
|
|
|
bDeferred = scheduler.DeferTask(tmp->waittime, iPriority,
|
|
|
|
|
|
Task_RunQueueEntry, tmp, 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-31 09:09:18 -06:00
|
|
|
|
bDeferred = scheduler.DeferImmediateTask(iPriority,
|
|
|
|
|
|
Task_RunQueueEntry, tmp, 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!tmp->IsTimed)
|
|
|
|
|
|
{
|
|
|
|
|
|
// In this case, the timeout task below will never run,
|
|
|
|
|
|
// but it allows us to manage all semaphores together in
|
|
|
|
|
|
// the same data structure.
|
|
|
|
|
|
//
|
|
|
|
|
|
iPriority = PRIORITY_SUSPEND;
|
|
|
|
|
|
}
|
2026-07-31 09:09:18 -06:00
|
|
|
|
bDeferred = scheduler.DeferTask(tmp->waittime, iPriority,
|
|
|
|
|
|
Task_SemaphoreTimeout, tmp, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// #1871: if the scheduler could not hold a TASK_RECORD, the command is
|
|
|
|
|
|
// not queued — free the BQUE and reverse payfor/quota so a silent OOM
|
|
|
|
|
|
// does not strand accounting until restart.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!bDeferred)
|
|
|
|
|
|
{
|
|
|
|
|
|
STARTLOG(LOG_PROBLEMS, "QUE", "MEM");
|
|
|
|
|
|
log_printf(T("wait_que: scheduler refused task; dropping queue entry."));
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
|
|
|
|
|
|
giveto(executor, mudconf.waitcost);
|
|
|
|
|
|
a_Queue(Owner(executor), -1);
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& i : tmp->scr)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i)
|
|
|
|
|
|
{
|
|
|
|
|
|
RegRelease(i);
|
|
|
|
|
|
i = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
NamedRegsClear(tmp->named_scr);
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(STUB_SLAVE)
|
|
|
|
|
|
if (nullptr != tmp->pResultsSet)
|
|
|
|
|
|
{
|
|
|
|
|
|
tmp->pResultsSet->Release();
|
|
|
|
|
|
tmp->pResultsSet = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif // STUB_SLAVE
|
|
|
|
|
|
|
|
|
|
|
|
if (tmp->switch_token)
|
|
|
|
|
|
{
|
|
|
|
|
|
MEMFREE(tmp->switch_token);
|
|
|
|
|
|
tmp->switch_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (tmp->iter_token)
|
|
|
|
|
|
{
|
|
|
|
|
|
MEMFREE(tmp->iter_token);
|
|
|
|
|
|
tmp->iter_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MEMFREE(tmp->text);
|
|
|
|
|
|
tmp->text = nullptr;
|
|
|
|
|
|
free_qentry(tmp);
|
|
|
|
|
|
return false;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2026-07-24 08:26:59 -06:00
|
|
|
|
return true;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2007-12-18 00:51:54 -08:00
|
|
|
|
#if defined(STUB_SLAVE)
|
2007-12-18 18:36:20 -08:00
|
|
|
|
bool QueryComplete_bDone = false;
|
Replace custom integer types (INT8..INT64, UINT8..UINT64) with <cstdint> standard types.
Remove platform-specific typedef chains and limit macros from config.h,
add #include <cstdint>, and replace all usages across 46 files with
int8_t..int64_t, uint8_t..uint64_t, uintptr_t, and standard INT*_MIN/
INT*_MAX/UINT*_MAX limit macros. Semantic aliases (UTF8, UTF16, UTF32,
MUX_CID, MUX_IID, LBUF_OFFSET, etc.) are preserved but now defined in
terms of standard types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:53:58 -07:00
|
|
|
|
uint32_t QueryComplete_hQuery = 0;
|
2018-10-03 17:54:51 +00:00
|
|
|
|
CResultsSet *QueryComplete_prsResultsSet = nullptr;
|
2007-09-25 23:17:29 -07:00
|
|
|
|
|
|
|
|
|
|
static int CallBack_QueryComplete(PTASK_RECORD p)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (QueryComplete_bDone)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_DONE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Task_SQLTimeout == p->fpTask)
|
|
|
|
|
|
{
|
|
|
|
|
|
// This represents a query.
|
|
|
|
|
|
//
|
2026-03-03 18:39:35 -07:00
|
|
|
|
BQUE *point = static_cast<BQUE *>(p->arg_voidptr);
|
2007-09-25 23:17:29 -07:00
|
|
|
|
if (point->u.hQuery == QueryComplete_hQuery)
|
|
|
|
|
|
{
|
|
|
|
|
|
p->iPriority = PRIORITY_OBJECT;
|
|
|
|
|
|
p->ltaWhen.GetUTC();
|
|
|
|
|
|
p->fpTask = Task_RunQueueEntry;
|
|
|
|
|
|
|
2007-12-18 18:36:20 -08:00
|
|
|
|
point->u.s.sem = NOTHING;
|
|
|
|
|
|
point->u.s.attr = 0;
|
|
|
|
|
|
QueryComplete_prsResultsSet->AddRef();
|
|
|
|
|
|
point->pResultsSet = QueryComplete_prsResultsSet;
|
2007-12-19 20:46:49 -08:00
|
|
|
|
point->iRow = RS_TOP;
|
2007-09-25 23:17:29 -07:00
|
|
|
|
|
|
|
|
|
|
QueryComplete_bDone = true;
|
|
|
|
|
|
return IU_UPDATE_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This can be called as a side-effect of talking with the stubslave.
|
|
|
|
|
|
// Therefore, we only want to raise the priority of the corresponding take
|
|
|
|
|
|
// from SUSPENDED to OBJECT.
|
|
|
|
|
|
//
|
Replace custom integer types (INT8..INT64, UINT8..UINT64) with <cstdint> standard types.
Remove platform-specific typedef chains and limit macros from config.h,
add #include <cstdint>, and replace all usages across 46 files with
int8_t..int64_t, uint8_t..uint64_t, uintptr_t, and standard INT*_MIN/
INT*_MAX/UINT*_MAX limit macros. Semantic aliases (UTF8, UTF16, UTF32,
MUX_CID, MUX_IID, LBUF_OFFSET, etc.) are preserved but now defined in
terms of standard types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:53:58 -07:00
|
|
|
|
void query_complete(uint32_t hQuery, uint32_t iError, CResultsSet *prsResultsSet)
|
2007-09-25 23:17:29 -07:00
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr != prsResultsSet)
|
2007-12-18 23:44:26 -08:00
|
|
|
|
{
|
|
|
|
|
|
prsResultsSet->SetError(iError);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-25 23:17:29 -07:00
|
|
|
|
QueryComplete_bDone = false;
|
|
|
|
|
|
QueryComplete_hQuery = hQuery;
|
2007-12-18 18:36:20 -08:00
|
|
|
|
QueryComplete_prsResultsSet = prsResultsSet;
|
2007-09-25 23:17:29 -07:00
|
|
|
|
scheduler.TraverseUnordered(CallBack_QueryComplete);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
QueryComplete_prsResultsSet = nullptr;
|
2007-09-25 23:17:29 -07:00
|
|
|
|
}
|
2007-12-18 00:51:54 -08:00
|
|
|
|
#endif // STUB_SLAVE
|
2007-09-25 23:17:29 -07:00
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// sql_que: Add commands to the sql queue.
|
|
|
|
|
|
//
|
|
|
|
|
|
void sql_que
|
|
|
|
|
|
(
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const dbref executor,
|
|
|
|
|
|
const dbref caller,
|
|
|
|
|
|
const dbref enactor,
|
|
|
|
|
|
const int eval,
|
|
|
|
|
|
const dbref thing,
|
|
|
|
|
|
const int attr,
|
|
|
|
|
|
const UTF8 *dbname,
|
|
|
|
|
|
const UTF8 *query,
|
2006-11-05 13:43:16 +00:00
|
|
|
|
int nargs,
|
2007-09-24 20:13:48 -07:00
|
|
|
|
const UTF8 *args[],
|
2006-11-05 13:43:16 +00:00
|
|
|
|
reg_ref *sargs[]
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
Replace custom integer types (INT8..INT64, UINT8..UINT64) with <cstdint> standard types.
Remove platform-specific typedef chains and limit macros from config.h,
add #include <cstdint>, and replace all usages across 46 files with
int8_t..int64_t, uint8_t..uint64_t, uintptr_t, and standard INT*_MIN/
INT*_MAX/UINT*_MAX limit macros. Semantic aliases (UTF8, UTF16, UTF32,
MUX_CID, MUX_IID, LBUF_OFFSET, etc.) are preserved but now defined in
terms of standard types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:53:58 -07:00
|
|
|
|
static uint32_t next_handle = 0;
|
2007-10-11 12:05:29 -07:00
|
|
|
|
|
2007-09-24 22:59:44 -07:00
|
|
|
|
if ( !(mudconf.control_flags & CF_INTERP)
|
2018-10-03 17:54:51 +00:00
|
|
|
|
|| nullptr == mudstate.pIQueryControl)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-25 23:17:29 -07:00
|
|
|
|
ATTR *pattr = atr_num(attr);
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr == pattr)
|
2007-09-25 23:17:29 -07:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UTF8 mbuf[MBUF_SIZE];
|
2008-12-27 18:37:01 -08:00
|
|
|
|
mux_sprintf(mbuf, MBUF_SIZE, T("@trigger #%d/%s"), thing, pattr->name);
|
2006-11-05 13:43:16 +00:00
|
|
|
|
BQUE *tmp = setup_que(executor, caller, enactor, eval,
|
2007-09-25 23:17:29 -07:00
|
|
|
|
mbuf,
|
2006-11-05 13:43:16 +00:00
|
|
|
|
nargs, args,
|
|
|
|
|
|
sargs);
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!tmp)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
Replace custom integer types (INT8..INT64, UINT8..UINT64) with <cstdint> standard types.
Remove platform-specific typedef chains and limit macros from config.h,
add #include <cstdint>, and replace all usages across 46 files with
int8_t..int64_t, uint8_t..uint64_t, uintptr_t, and standard INT*_MIN/
INT*_MAX/UINT*_MAX limit macros. Semantic aliases (UTF8, UTF16, UTF32,
MUX_CID, MUX_IID, LBUF_OFFSET, etc.) are preserved but now defined in
terms of standard types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:53:58 -07:00
|
|
|
|
const uint32_t hQuery = next_handle++;
|
2007-09-25 23:17:29 -07:00
|
|
|
|
|
|
|
|
|
|
tmp->u.hQuery = hQuery;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2026-07-24 08:26:59 -06:00
|
|
|
|
// Hold the entry until Query completes (or fails). waittime is not
|
|
|
|
|
|
// used as a real SQL timeout (Task_SQLTimeout just runs the entry),
|
|
|
|
|
|
// but must be initialized before DeferTask.
|
|
|
|
|
|
//
|
|
|
|
|
|
tmp->waittime.GetUTC();
|
2026-07-31 09:09:18 -06:00
|
|
|
|
if (!scheduler.DeferTask(tmp->waittime, PRIORITY_SUSPEND, Task_SQLTimeout, tmp, 0))
|
|
|
|
|
|
{
|
|
|
|
|
|
// #1871: same rollback as wait_que / Query-reject path.
|
|
|
|
|
|
//
|
|
|
|
|
|
STARTLOG(LOG_PROBLEMS, "QUE", "MEM");
|
|
|
|
|
|
log_printf(T("sql_que: scheduler refused task; dropping queue entry."));
|
|
|
|
|
|
ENDLOG;
|
|
|
|
|
|
|
|
|
|
|
|
giveto(executor, mudconf.waitcost);
|
|
|
|
|
|
a_Queue(Owner(executor), -1);
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& i : tmp->scr)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i)
|
|
|
|
|
|
{
|
|
|
|
|
|
RegRelease(i);
|
|
|
|
|
|
i = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
NamedRegsClear(tmp->named_scr);
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(STUB_SLAVE)
|
|
|
|
|
|
if (nullptr != tmp->pResultsSet)
|
|
|
|
|
|
{
|
|
|
|
|
|
tmp->pResultsSet->Release();
|
|
|
|
|
|
tmp->pResultsSet = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif // STUB_SLAVE
|
|
|
|
|
|
|
|
|
|
|
|
if (tmp->switch_token)
|
|
|
|
|
|
{
|
|
|
|
|
|
MEMFREE(tmp->switch_token);
|
|
|
|
|
|
tmp->switch_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (tmp->iter_token)
|
|
|
|
|
|
{
|
|
|
|
|
|
MEMFREE(tmp->iter_token);
|
|
|
|
|
|
tmp->iter_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MEMFREE(tmp->text);
|
|
|
|
|
|
tmp->text = nullptr;
|
|
|
|
|
|
free_qentry(tmp);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const MUX_RESULT mr = mudstate.pIQueryControl->Query(hQuery, dbname, query);
|
2007-09-24 22:59:44 -07:00
|
|
|
|
if (MUX_FAILED(mr))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-24 08:26:59 -06:00
|
|
|
|
// Query rejected: cancel the hold, free the BQUE, and refund
|
|
|
|
|
|
// money/quota exactly as a successful dequeue would.
|
|
|
|
|
|
//
|
2026-03-06 08:19:40 -07:00
|
|
|
|
scheduler.CancelTask(Task_SQLTimeout, tmp, 0);
|
2026-07-24 08:26:59 -06:00
|
|
|
|
|
|
|
|
|
|
giveto(executor, mudconf.waitcost);
|
|
|
|
|
|
a_Queue(Owner(executor), -1);
|
|
|
|
|
|
|
|
|
|
|
|
for (auto& i : tmp->scr)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (i)
|
|
|
|
|
|
{
|
|
|
|
|
|
RegRelease(i);
|
|
|
|
|
|
i = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
NamedRegsClear(tmp->named_scr);
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(STUB_SLAVE)
|
|
|
|
|
|
if (nullptr != tmp->pResultsSet)
|
|
|
|
|
|
{
|
|
|
|
|
|
tmp->pResultsSet->Release();
|
|
|
|
|
|
tmp->pResultsSet = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif // STUB_SLAVE
|
|
|
|
|
|
|
|
|
|
|
|
if (tmp->switch_token)
|
|
|
|
|
|
{
|
|
|
|
|
|
MEMFREE(tmp->switch_token);
|
|
|
|
|
|
tmp->switch_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (tmp->iter_token)
|
|
|
|
|
|
{
|
|
|
|
|
|
MEMFREE(tmp->iter_token);
|
|
|
|
|
|
tmp->iter_token = nullptr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MEMFREE(tmp->text);
|
|
|
|
|
|
tmp->text = nullptr;
|
|
|
|
|
|
free_qentry(tmp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// do_wait: Command interface to wait_que
|
|
|
|
|
|
//
|
|
|
|
|
|
void do_wait
|
|
|
|
|
|
(
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const dbref executor,
|
|
|
|
|
|
const dbref caller,
|
|
|
|
|
|
const dbref enactor,
|
|
|
|
|
|
const int eval,
|
2007-09-02 21:44:11 -07:00
|
|
|
|
int key,
|
|
|
|
|
|
int nargs,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *event,
|
|
|
|
|
|
UTF8 *cmd,
|
2007-04-09 23:40:23 +00:00
|
|
|
|
const UTF8 *cargs[],
|
2007-09-02 21:44:11 -07:00
|
|
|
|
int ncargs
|
2006-09-01 22:59:23 +00:00
|
|
|
|
)
|
|
|
|
|
|
{
|
2007-09-02 21:44:11 -07:00
|
|
|
|
UNUSED_PARAMETER(nargs);
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
CLinearTimeAbsolute ltaWhen;
|
|
|
|
|
|
CLinearTimeDelta ltd;
|
|
|
|
|
|
|
|
|
|
|
|
// If arg1 is all numeric, do simple (non-sem) timed wait.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (is_rational(event))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (key & WAIT_UNTIL)
|
|
|
|
|
|
{
|
|
|
|
|
|
ltaWhen.SetSecondsString(event);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ltaWhen.GetUTC();
|
|
|
|
|
|
ltd.SetSecondsString(event);
|
|
|
|
|
|
ltaWhen += ltd;
|
|
|
|
|
|
}
|
2006-11-05 13:43:16 +00:00
|
|
|
|
wait_que(executor, caller, enactor, eval, true, ltaWhen, NOTHING, 0,
|
|
|
|
|
|
cmd,
|
|
|
|
|
|
ncargs, cargs,
|
|
|
|
|
|
mudstate.global_regs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Semaphore wait with optional timeout.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const UTF8 *what = parse_to(&event, '/', 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
init_match(executor, what, NOTYPE);
|
|
|
|
|
|
match_everything(0);
|
|
|
|
|
|
|
|
|
|
|
|
dbref thing = noisy_match_result();
|
|
|
|
|
|
if (!Good_obj(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!Controls(executor, thing) && !Link_ok(thing))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Get timeout, default 0.
|
|
|
|
|
|
//
|
|
|
|
|
|
int atr = A_SEMAPHORE;
|
|
|
|
|
|
bool bTimed = false;
|
|
|
|
|
|
if (event && *event)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (is_rational(event))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (key & WAIT_UNTIL)
|
|
|
|
|
|
{
|
|
|
|
|
|
ltaWhen.SetSecondsString(event);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ltaWhen.GetUTC();
|
|
|
|
|
|
ltd.SetSecondsString(event);
|
|
|
|
|
|
ltaWhen += ltd;
|
|
|
|
|
|
}
|
|
|
|
|
|
bTimed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-03-03 18:39:35 -07:00
|
|
|
|
const UTF8 *EventAttributeName = reinterpret_cast<UTF8 *>(event);
|
2007-03-12 08:15:29 +00:00
|
|
|
|
ATTR *ap = atr_str(EventAttributeName);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (!ap)
|
|
|
|
|
|
{
|
2007-03-12 08:15:29 +00:00
|
|
|
|
atr = mkattr(executor, EventAttributeName);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (atr <= 0)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify_quiet(executor, M_("Invalid attribute."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
ap = atr_num(atr);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
atr = ap->number;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!bCanSetAttr(executor, thing, ap))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify_quiet(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-24 08:26:59 -06:00
|
|
|
|
// Don't raise the semaphore if the queue is disabled — wait_que
|
|
|
|
|
|
// would silently no-op and leave the count elevated.
|
|
|
|
|
|
//
|
|
|
|
|
|
if (!(mudconf.control_flags & CF_INTERP))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const dbref sem = thing;
|
2026-03-05 16:00:09 -07:00
|
|
|
|
int num = 0;
|
|
|
|
|
|
if (!add_to(thing, 1, atr, &num))
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify_quiet(executor, M_("Semaphore update failed."));
|
2026-03-05 16:00:09 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (num <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Thing over-notified, run the command immediately.
|
|
|
|
|
|
//
|
|
|
|
|
|
thing = NOTHING;
|
|
|
|
|
|
bTimed = false;
|
|
|
|
|
|
}
|
2026-07-24 08:26:59 -06:00
|
|
|
|
if (!wait_que(executor, caller, enactor, eval, bTimed, ltaWhen, thing, atr,
|
2006-11-05 13:43:16 +00:00
|
|
|
|
cmd,
|
|
|
|
|
|
ncargs, cargs,
|
2026-07-24 08:26:59 -06:00
|
|
|
|
mudstate.global_regs))
|
|
|
|
|
|
{
|
|
|
|
|
|
// setup_que failed after we claimed the semaphore; reverse.
|
|
|
|
|
|
//
|
|
|
|
|
|
(void)add_to(sem, -1, atr, nullptr);
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// do_query: Command interface to sql_que
|
|
|
|
|
|
//
|
|
|
|
|
|
void do_query
|
|
|
|
|
|
(
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const dbref executor,
|
|
|
|
|
|
const dbref caller,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
dbref enactor,
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const int eval,
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int key,
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const int nargs,
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *dbref_attr,
|
|
|
|
|
|
UTF8 *dbname_query,
|
2007-09-24 20:13:48 -07:00
|
|
|
|
const UTF8 *cargs[],
|
2006-09-01 22:59:23 +00:00
|
|
|
|
int ncargs
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
2007-09-24 20:13:48 -07:00
|
|
|
|
UNUSED_PARAMETER(nargs);
|
2007-09-24 22:59:44 -07:00
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr == mudstate.pIQueryControl)
|
2007-09-24 22:59:44 -07:00
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify_quiet(executor, M_("Query server is not available."));
|
2007-09-24 22:59:44 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (key & QUERY_SQL)
|
|
|
|
|
|
{
|
|
|
|
|
|
// SQL Query.
|
|
|
|
|
|
//
|
|
|
|
|
|
dbref thing;
|
|
|
|
|
|
ATTR *pattr;
|
|
|
|
|
|
|
2007-09-24 22:59:44 -07:00
|
|
|
|
if (!( parse_attrib(executor, dbref_attr, &thing, &pattr)
|
2018-10-03 17:54:51 +00:00
|
|
|
|
&& nullptr != pattr))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify_quiet(executor, M_("No match."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!Controls(executor, thing))
|
|
|
|
|
|
{
|
2007-03-23 23:42:21 +00:00
|
|
|
|
notify_quiet(executor, T(NOPERM_MESSAGE));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *pQuery = dbname_query;
|
2022-03-14 16:08:21 -06:00
|
|
|
|
const UTF8 *pDBName = parse_to(&pQuery, '/', 0);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (nullptr == pQuery)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("QUERY: No Query."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2007-09-24 22:59:44 -07:00
|
|
|
|
sql_que(executor, caller, enactor, eval, thing, pattr->number,
|
|
|
|
|
|
pDBName, pQuery, ncargs, cargs, mudstate.global_regs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify_quiet(executor, M_("At least one query option is required."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static CLinearTimeAbsolute Show_lsaNow;
|
|
|
|
|
|
static int Total_SystemTasks;
|
|
|
|
|
|
static int Total_RunQueueEntry;
|
|
|
|
|
|
static int Shown_RunQueueEntry;
|
|
|
|
|
|
static int Total_SemaphoreTimeout;
|
|
|
|
|
|
static int Shown_SemaphoreTimeout;
|
|
|
|
|
|
static dbref Show_Player_Target;
|
|
|
|
|
|
static dbref Show_Object_Target;
|
|
|
|
|
|
static int Show_Key;
|
|
|
|
|
|
static dbref Show_Player;
|
|
|
|
|
|
static int Show_bFirstLine;
|
|
|
|
|
|
|
|
|
|
|
|
int Total_SQLTimeout;
|
|
|
|
|
|
int Shown_SQLTimeout;
|
|
|
|
|
|
|
2022-03-14 16:08:21 -06:00
|
|
|
|
static int CallBack_ShowDispatches(const PTASK_RECORD p)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
Total_SystemTasks++;
|
|
|
|
|
|
CLinearTimeDelta ltd = p->ltaWhen - Show_lsaNow;
|
|
|
|
|
|
if (p->fpTask == dispatch_DatabaseDump)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[%d]auto-@dump"), ltd.ReturnSeconds()));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (p->fpTask == dispatch_FreeListReconstruction)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[%d]auto-@dbck"), ltd.ReturnSeconds()));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (p->fpTask == dispatch_IdleCheck)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[%d]Check for idle players"), ltd.ReturnSeconds()));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (p->fpTask == dispatch_CheckEvents)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[%d]Test for @daily time"), ltd.ReturnSeconds()));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2015-01-06 04:45:40 +00:00
|
|
|
|
else if (p->fpTask == dispatch_KeepAlive)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[%d]Keep Alive"), ltd.ReturnSeconds()));
|
2015-01-06 04:45:40 +00:00
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
else if (p->fpTask == dispatch_CacheTick)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[%d]Database cache tick"), ltd.ReturnSeconds()));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (p->fpTask == Task_ProcessCommand)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[%d]Further command quota"), ltd.ReturnSeconds()));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Total_SystemTasks--;
|
|
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-05 19:21:06 -07:00
|
|
|
|
static void ShowPsLine(const BQUE *tmp, const uint64_t pid)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *bufp = unparse_object(Show_Player, tmp->executor, false);
|
2007-09-25 23:17:29 -07:00
|
|
|
|
if (tmp->IsTimed && Good_obj(tmp->u.s.sem))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
CLinearTimeDelta ltd = tmp->waittime - Show_lsaNow;
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[PID %llu][#%d/%d]%s:%s"),
|
2026-03-05 19:21:06 -07:00
|
|
|
|
static_cast<unsigned long long>(pid), tmp->u.s.sem,
|
|
|
|
|
|
ltd.ReturnSeconds(), bufp, tmp->comm));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (tmp->IsTimed)
|
|
|
|
|
|
{
|
|
|
|
|
|
CLinearTimeDelta ltd = tmp->waittime - Show_lsaNow;
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[PID %llu][%d]%s:%s"),
|
2026-03-05 19:21:06 -07:00
|
|
|
|
static_cast<unsigned long long>(pid), ltd.ReturnSeconds(), bufp,
|
|
|
|
|
|
tmp->comm));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2007-09-25 23:17:29 -07:00
|
|
|
|
else if (Good_obj(tmp->u.s.sem))
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[PID %llu][#%d]%s:%s"),
|
2026-03-05 19:21:06 -07:00
|
|
|
|
static_cast<unsigned long long>(pid), tmp->u.s.sem, bufp,
|
|
|
|
|
|
tmp->comm));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_("[PID %llu]%s:%s"),
|
2026-03-05 19:21:06 -07:00
|
|
|
|
static_cast<unsigned long long>(pid), bufp, tmp->comm));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
2007-03-14 00:55:08 +00:00
|
|
|
|
UTF8 *bp = bufp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (Show_Key == PS_LONG)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < tmp->nargs; i++)
|
|
|
|
|
|
{
|
2018-10-03 17:54:51 +00:00
|
|
|
|
if (tmp->env[i] != nullptr)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
2007-03-14 22:33:04 +00:00
|
|
|
|
safe_str(T("; Arg"), bufp, &bp);
|
2026-03-03 18:39:35 -07:00
|
|
|
|
safe_chr(static_cast<UTF8>(i + '0'), bufp, &bp);
|
2026-07-26 21:52:48 -06:00
|
|
|
|
safe_str(T("=‘"), bufp, &bp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
safe_str(tmp->env[i], bufp, &bp);
|
2026-07-26 21:52:48 -06:00
|
|
|
|
safe_str(T("’"), bufp, &bp);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
*bp = '\0';
|
|
|
|
|
|
bp = unparse_object(Show_Player, tmp->enactor, false);
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(Show_Player, tprintf(M_(" Enactor: %s%s"), bp, bufp));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
free_lbuf(bp);
|
|
|
|
|
|
}
|
|
|
|
|
|
free_lbuf(bufp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int CallBack_ShowWait(PTASK_RECORD p)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p->fpTask != Task_RunQueueEntry)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Total_RunQueueEntry++;
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto tmp = static_cast<BQUE*>(p->arg_voidptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (que_want(tmp, Show_Player_Target, Show_Object_Target))
|
|
|
|
|
|
{
|
|
|
|
|
|
Shown_RunQueueEntry++;
|
|
|
|
|
|
if (Show_Key == PS_SUMM)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Show_bFirstLine)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(Show_Player, M_("----- Wait Queue -----"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Show_bFirstLine = false;
|
|
|
|
|
|
}
|
2026-03-04 16:50:02 -07:00
|
|
|
|
ShowPsLine(tmp, p->m_Ticket);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int CallBack_ShowSemaphore(PTASK_RECORD p)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p->fpTask != Task_SemaphoreTimeout)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Total_SemaphoreTimeout++;
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto tmp = static_cast<BQUE*>(p->arg_voidptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (que_want(tmp, Show_Player_Target, Show_Object_Target))
|
|
|
|
|
|
{
|
|
|
|
|
|
Shown_SemaphoreTimeout++;
|
|
|
|
|
|
if (Show_Key == PS_SUMM)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Show_bFirstLine)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(Show_Player, M_("----- Semaphore Queue -----"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Show_bFirstLine = false;
|
|
|
|
|
|
}
|
2026-03-04 16:50:02 -07:00
|
|
|
|
ShowPsLine(tmp, p->m_Ticket);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int CallBack_ShowSQLQueries(PTASK_RECORD p)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p->fpTask != Task_SQLTimeout)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Total_SQLTimeout++;
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto tmp = static_cast<BQUE*>(p->arg_voidptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (que_want(tmp, Show_Player_Target, Show_Object_Target))
|
|
|
|
|
|
{
|
|
|
|
|
|
Shown_SQLTimeout++;
|
|
|
|
|
|
if (Show_Key == PS_SUMM)
|
|
|
|
|
|
{
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Show_bFirstLine)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(Show_Player, M_("----- SQL Queries -----"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Show_bFirstLine = false;
|
|
|
|
|
|
}
|
2026-03-04 16:50:02 -07:00
|
|
|
|
ShowPsLine(tmp, p->m_Ticket);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// do_ps: tell executor what commands they have pending in the queue
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
void do_ps(const dbref executor, const dbref caller, const dbref enactor, const int eval, int key, UTF8 *target, const UTF8 *cargs[], const int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2006-09-06 06:17:46 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
dbref executor_targ, obj_targ;
|
|
|
|
|
|
|
|
|
|
|
|
// Figure out what to list the queue for.
|
|
|
|
|
|
//
|
|
|
|
|
|
if ((key & PS_ALL) && !See_Queue(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
notify(executor, NOPERM_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!target || !*target)
|
|
|
|
|
|
{
|
|
|
|
|
|
obj_targ = NOTHING;
|
|
|
|
|
|
if (key & PS_ALL)
|
|
|
|
|
|
{
|
|
|
|
|
|
executor_targ = NOTHING;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
executor_targ = Owner(executor);
|
|
|
|
|
|
if (!isPlayer(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
obj_targ = executor;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
executor_targ = Owner(executor);
|
|
|
|
|
|
obj_targ = match_controlled(executor, target);
|
|
|
|
|
|
if (obj_targ == NOTHING)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (key & PS_ALL)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("Can’t specify a target and /all"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isPlayer(obj_targ))
|
|
|
|
|
|
{
|
|
|
|
|
|
executor_targ = obj_targ;
|
|
|
|
|
|
obj_targ = NOTHING;
|
|
|
|
|
|
}
|
Fix command-side verb correctness bugs (@clone, @ps, whisper, @flag, @mark)
Correctness sweep of the command-side verb handlers. Five confirmed bugs
plus a help-text correction, verified by dual-lens review and code reading
(the read-only smoke harness can't exercise these verbs directly):
- @clone/cost on an exit bypassed the "must control current location"
check (it lived only on the non-/cost path), letting a builder splice a
cloned exit into a room they do not control. (#855)
- The @mark/@mark_all/@apply_marked DB-cleaning refusal cited @unmark_all,
which does not exist (produces "Huh?"); corrected to @mark_all/clear.
(#856)
- @ps <object> reported nothing for a controlled object owned by another
player; do_ps was missing the non-player-target else clause that the
sibling @halt has (clear the owner filter). (#857)
- whisper "<quoted name>" skipped the locality/connected gate the unquoted
form applies, giving a success confirmation plus a delivery error and
polluting A_LASTWHISPER; also fixed an adjacent quoted-name continue that
did not advance the parser. (#858)
- @flag/remove of an unknown/empty flag name was silent; now reports an
error like the other flag-name failure paths. (#859)
- report help said 8-hour segments but the code uses 4 (deliberately, per
4a845139f); corrected the help. (#860)
Also restores the "## JIT / DBT Engine" CHANGES heading dropped during an
earlier 2.14.0.8 edit. Build clean, all 1264 smoke tests still pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:29:11 -06:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Scope the listing to the object alone (any owner), matching
|
|
|
|
|
|
// @halt; match_controlled already verified permission, so a
|
|
|
|
|
|
// controller can inspect an object owned by another player.
|
|
|
|
|
|
//
|
|
|
|
|
|
executor_targ = NOTHING;
|
|
|
|
|
|
}
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
key = key & ~PS_ALL;
|
|
|
|
|
|
|
|
|
|
|
|
switch (key)
|
|
|
|
|
|
{
|
|
|
|
|
|
case PS_BRIEF:
|
|
|
|
|
|
case PS_SUMM:
|
|
|
|
|
|
case PS_LONG:
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("Illegal combination of switches."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Show_lsaNow.GetUTC();
|
|
|
|
|
|
Total_SystemTasks = 0;
|
|
|
|
|
|
Total_RunQueueEntry = 0;
|
|
|
|
|
|
Shown_RunQueueEntry = 0;
|
|
|
|
|
|
Total_SemaphoreTimeout = 0;
|
|
|
|
|
|
Shown_SemaphoreTimeout = 0;
|
2007-12-17 11:44:57 -08:00
|
|
|
|
Total_SQLTimeout = 0;
|
|
|
|
|
|
Shown_SQLTimeout = 0;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Show_Player_Target = executor_targ;
|
|
|
|
|
|
Show_Object_Target = obj_targ;
|
|
|
|
|
|
Show_Key = key;
|
|
|
|
|
|
Show_Player = executor;
|
|
|
|
|
|
Show_bFirstLine = true;
|
|
|
|
|
|
scheduler.TraverseOrdered(CallBack_ShowWait);
|
|
|
|
|
|
Show_bFirstLine = true;
|
|
|
|
|
|
scheduler.TraverseOrdered(CallBack_ShowSemaphore);
|
|
|
|
|
|
Show_bFirstLine = true;
|
|
|
|
|
|
scheduler.TraverseOrdered(CallBack_ShowSQLQueries);
|
|
|
|
|
|
if (Wizard(executor))
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("----- System Queue -----"));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
scheduler.TraverseOrdered(CallBack_ShowDispatches);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Display stats.
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
UTF8* bufp = alloc_mbuf("do_ps");
|
2008-12-27 18:37:01 -08:00
|
|
|
|
mux_sprintf(bufp, MBUF_SIZE, T("Totals: Wait Queue...%d/%d Semaphores...%d/%d SQL %d/%d"),
|
2006-09-01 22:59:23 +00:00
|
|
|
|
Shown_RunQueueEntry, Total_RunQueueEntry,
|
|
|
|
|
|
Shown_SemaphoreTimeout, Total_SemaphoreTimeout,
|
|
|
|
|
|
Shown_SQLTimeout, Total_SQLTimeout);
|
|
|
|
|
|
notify(executor, bufp);
|
|
|
|
|
|
if (Wizard(executor))
|
|
|
|
|
|
{
|
2008-12-27 18:37:01 -08:00
|
|
|
|
mux_sprintf(bufp, MBUF_SIZE, T(" System Tasks.....%d"), Total_SystemTasks);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
notify(executor, bufp);
|
|
|
|
|
|
}
|
|
|
|
|
|
free_mbuf(bufp);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static CLinearTimeDelta ltdWarp;
|
|
|
|
|
|
static int CallBack_Warp(PTASK_RECORD p)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( p->fpTask == Task_RunQueueEntry
|
|
|
|
|
|
|| p->fpTask == Task_SQLTimeout
|
|
|
|
|
|
|| p->fpTask == Task_SemaphoreTimeout)
|
|
|
|
|
|
{
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const auto point = static_cast<BQUE*>(p->arg_voidptr);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (point->IsTimed)
|
|
|
|
|
|
{
|
|
|
|
|
|
point->waittime -= ltdWarp;
|
2006-09-21 05:29:35 +00:00
|
|
|
|
p->ltaWhen -= ltdWarp;
|
2006-09-01 22:59:23 +00:00
|
|
|
|
return IU_UPDATE_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return IU_NEXT_TASK;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// do_queue: Queue management
|
|
|
|
|
|
//
|
2022-03-14 15:56:59 -06:00
|
|
|
|
void do_queue(const dbref executor, const dbref caller, const dbref enactor, const int eval, const int key, UTF8 *arg, const UTF8 *cargs[], const int ncargs)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
|
|
|
|
|
UNUSED_PARAMETER(caller);
|
|
|
|
|
|
UNUSED_PARAMETER(enactor);
|
2006-09-06 06:17:46 +00:00
|
|
|
|
UNUSED_PARAMETER(eval);
|
2007-09-02 13:48:10 -07:00
|
|
|
|
UNUSED_PARAMETER(cargs);
|
|
|
|
|
|
UNUSED_PARAMETER(ncargs);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
if (key == QUEUE_KICK)
|
|
|
|
|
|
{
|
2026-07-28 19:34:54 -06:00
|
|
|
|
// Parse full width, then clamp for RunTasks(int) (#1402).
|
|
|
|
|
|
//
|
|
|
|
|
|
int64_t wanted = mux_atoi64(arg);
|
|
|
|
|
|
int i;
|
|
|
|
|
|
if (wanted > INT_MAX)
|
|
|
|
|
|
{
|
|
|
|
|
|
i = INT_MAX;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (wanted < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
i = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
i = static_cast<int>(wanted);
|
|
|
|
|
|
}
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const int save_minPriority = scheduler.GetMinPriority();
|
2006-09-01 22:59:23 +00:00
|
|
|
|
if (save_minPriority <= PRIORITY_CF_DEQUEUE_DISABLED)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("Warning: automatic dequeueing is disabled."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
scheduler.SetMinPriority(PRIORITY_CF_DEQUEUE_ENABLED);
|
|
|
|
|
|
}
|
|
|
|
|
|
CLinearTimeAbsolute lsaNow;
|
|
|
|
|
|
lsaNow.GetUTC();
|
|
|
|
|
|
scheduler.ReadyTasks(lsaNow);
|
2022-03-14 15:56:59 -06:00
|
|
|
|
const int ncmds = scheduler.RunTasks(i);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
scheduler.SetMinPriority(save_minPriority);
|
|
|
|
|
|
|
|
|
|
|
|
if (!Quiet(executor))
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(executor, tprintf(M_("%d commands processed."), ncmds));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (key == QUEUE_WARP)
|
|
|
|
|
|
{
|
2026-07-28 19:34:54 -06:00
|
|
|
|
// SetSeconds takes int64_t (UnderlyingTickType) (#1402).
|
|
|
|
|
|
//
|
|
|
|
|
|
const int64_t iWarp = mux_atoi64(arg);
|
2006-09-01 22:59:23 +00:00
|
|
|
|
ltdWarp.SetSeconds(iWarp);
|
|
|
|
|
|
if (scheduler.GetMinPriority() <= PRIORITY_CF_DEQUEUE_DISABLED)
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("Warning: automatic dequeueing is disabled."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
scheduler.TraverseUnordered(CallBack_Warp);
|
|
|
|
|
|
|
|
|
|
|
|
if (Quiet(executor))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2006-12-28 20:23:22 +00:00
|
|
|
|
if (0 < iWarp)
|
2006-09-01 22:59:23 +00:00
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(executor, tprintf(M_("WaitQ timer advanced %d seconds."), iWarp));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else if (iWarp < 0)
|
|
|
|
|
|
{
|
nls: mark cque/command/predicates/mail notify prose with M_() (#1681)
Phase 3 coverage, parallel to mux_table table-header work:
cque @ps timer labels, WaitQ warp, commands processed
command switch errors, dig/open/create/kill costs, money help,
queue/login limits, lua/cache stats display
predicates not-enough-money, command table add/not-found
mail folder/send/clear/reject/stats *sentences* only
Left alone: mail %-column layouts, pose "%s %s", softcode __name keys,
NOPERM_MESSAGE, tabular predicate listings. pot 723-ish -> 795 msgids;
xx filled; ko msgmerge only.
2026-07-28 08:54:03 -06:00
|
|
|
|
notify(executor, tprintf(M_("WaitQ timer set back %d seconds."), iWarp));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-27 11:47:18 +00:00
|
|
|
|
notify(executor, M_("Object queue appended to player queue."));
|
2006-09-01 22:59:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|