Skip to content

Commit

Permalink
feat(xsnap): FFI to enable/disable metering (#3480)
Browse files Browse the repository at this point in the history
API:

```js
    const noLimit = 0;

    const limit = currentMeterLimit();
    const before = resetMeter(noLimit, 0);
    try {
      // unmetered work...
    } finally {
      resetMeter(limit, before);
    }
```

fixes #3457
  • Loading branch information
dckc authored Jul 15, 2021
1 parent 14febd9 commit 94d9417
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 1 deletion.
28 changes: 27 additions & 1 deletion packages/xsnap/src/xsnap.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ static void fx_setImmediate(txMachine* the);
// static void fx_setTimeout(txMachine* the);
static void fx_setTimer(txMachine* the, txNumber interval, txBoolean repeat);
static void fx_setTimerCallback(txJob* job);
static void fx_currentMeterLimit(xsMachine* the);
static void fx_resetMeter(xsMachine* the);

static void fxFulfillModuleFile(txMachine* the);
static void fxRejectModuleFile(txMachine* the);
Expand All @@ -79,13 +81,15 @@ static char* fxWriteNetStringError(int code);
// The order of the callbacks materially affects how they are introduced to
// code that runs from a snapshot, so must be consistent in the face of
// upgrade.
#define mxSnapshotCallbackCount 5
#define mxSnapshotCallbackCount 7
txCallback gxSnapshotCallbacks[mxSnapshotCallbackCount] = {
fx_issueCommand, // 0
fx_print, // 1
fx_setImmediate, // 2
fx_gc, // 3
fx_performance_now, // 4
fx_currentMeterLimit, // 5
fx_resetMeter, // 6
// fx_evalScript,
// fx_isPromiseJobQueueEmpty,
// fx_setInterval,
Expand Down Expand Up @@ -559,6 +563,8 @@ void fxBuildAgent(xsMachine* the)
slot = fxNextSlotProperty(the, slot, the->stack, xsID("performance"), XS_DONT_ENUM_FLAG);
mxPop();

slot = fxNextHostFunctionProperty(the, slot, fx_currentMeterLimit, 1, xsID("currentMeterLimit"), XS_DONT_ENUM_FLAG);
slot = fxNextHostFunctionProperty(the, slot, fx_resetMeter, 1, xsID("resetMeter"), XS_DONT_ENUM_FLAG);
// mxPush(mxObjectPrototype);
// fxNextHostFunctionProperty(the, fxLastProperty(the, fxNewObjectInstance(the)), fx_print, 1, xsID("log"), XS_DONT_ENUM_FLAG);
// slot = fxNextSlotProperty(the, slot, the->stack, xsID("console"), XS_DONT_ENUM_FLAG);
Expand Down Expand Up @@ -907,6 +913,26 @@ void fx_print(xsMachine* the)
fflush(stdout);
}

static void fx_currentMeterLimit(xsMachine* the)
{
#if mxMetering
xsResult = xsInteger(gxCurrentMeter);
#endif
}

static void fx_resetMeter(xsMachine* the)
{
#if mxMetering
xsIntegerValue argc = xsToInteger(xsArgc);
if (argc < 2) {
xsTypeError("expected newMeterLimit, newMeterIndex");
}
xsResult = xsInteger(the->meterIndex);
gxCurrentMeter = xsToInteger(xsArg(0));
the->meterIndex = xsToInteger(xsArg(1));
#endif
}

// void fx_setInterval(txMachine* the)
// {
// fx_setTimer(the, fxToNumber(the, mxArgv(1)), 1);
Expand Down
46 changes: 46 additions & 0 deletions packages/xsnap/test/test-xs-perf.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,49 @@ test('high resolution timer', async t => {
t.log({ milliseconds, date: new Date(milliseconds) });
t.is('number', typeof milliseconds);
});

test('metering can be switched off / on at run-time', async t => {
const opts = options(io);
const vat = xsnap(opts);
t.teardown(() => vat.terminate());
const { meterUsage: { compute: noUnMeteredCompute } } = await vat.evaluate(`
for (let work=0; work < 1000; work++) {}
const limit = currentMeterLimit();
const before = resetMeter(0, 0);
try {
// nothing
} finally {
resetMeter(limit, before);
}
for (let work=0; work < 1000; work++) {}
`);
const { meterUsage: { compute: someUnMeteredCompute } } = await vat.evaluate(`
for (let work=0; work < 1000; work++) {}
const limit = currentMeterLimit();
const before = resetMeter(0, 0);
try {
for (let work=0; work < 2000; work++) {}
} finally {
resetMeter(limit, before);
}
for (let work=0; work < 1000; work++) {}
`);
t.log({ noUnMeteredCompute, someUnMeteredCompute });
t.is(noUnMeteredCompute, someUnMeteredCompute);
});

test('metering switch - start compartment only', async t => {
const opts = options(io);
const vat = xsnap(opts);
await vat.evaluate(`
const send = it => issueCommand(ArrayBuffer.fromString(it));
resetMeter(0, 0);
try {
(new Compartment()).evalate('resetMeter(0, 0)');
} catch (_err) {
send('no meteringSwitch in Compartment');
}
`);
await vat.close();
t.deepEqual(['no meteringSwitch in Compartment'], opts.messages);
});

0 comments on commit 94d9417

Please sign in to comment.