Skip to content

Commit

Permalink
refactor: factor out computronCounter
Browse files Browse the repository at this point in the history
  • Loading branch information
dckc committed Nov 22, 2024
1 parent f5c74ea commit fe2f37f
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 112 deletions.
115 changes: 115 additions & 0 deletions packages/cosmic-swingset/src/computron-counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// @ts-check

import { assert } from '@endo/errors';
import {
BeansPerBlockComputeLimit,
BeansPerVatCreation,
BeansPerXsnapComputron,
} from './sim-params.js';

const { hasOwn } = Object;

/**
* @typedef {object} BeansPerUnit
* @property {bigint} blockComputeLimit
* @property {bigint} vatCreation
* @property {bigint} xsnapComputron
*/

/**
* Return a stateful run policy that supports two phases: first allow
* non-cleanup work (presumably deliveries) until an overrideable computron
* budget is exhausted, then (iff no work was done and at least one vat cleanup
* budget field is positive) a cleanup phase that allows cleanup work (and
* presumably nothing else) until one of those fields is exhausted.
* https://github.com/Agoric/agoric-sdk/issues/8928#issuecomment-2053357870
*
* @param {object} params
* @param {BeansPerUnit} params.beansPerUnit
* @param {import('@agoric/swingset-vat').CleanupBudget} [params.vatCleanupBudget]
* @param {boolean} [ignoreBlockLimit]
* @returns {import('./launch-chain.js').ChainRunPolicy}
*/
export function computronCounter(
{ beansPerUnit, vatCleanupBudget },
ignoreBlockLimit = false,
) {
const {
[BeansPerBlockComputeLimit]: blockComputeLimit,
[BeansPerVatCreation]: vatCreation,
[BeansPerXsnapComputron]: xsnapComputron,
} = beansPerUnit;
assert.typeof(blockComputeLimit, 'bigint');
assert.typeof(vatCreation, 'bigint');
assert.typeof(xsnapComputron, 'bigint');

let totalBeans = 0n;
const shouldRun = () => ignoreBlockLimit || totalBeans < blockComputeLimit;

const remainingCleanups = { default: Infinity, ...vatCleanupBudget };
const defaultCleanupBudget = remainingCleanups.default;
let cleanupStarted = false;
let cleanupDone = false;
const cleanupPossible =
Object.values(remainingCleanups).length > 0
? Object.values(remainingCleanups).some(n => n > 0)
: defaultCleanupBudget > 0;
if (!cleanupPossible) cleanupDone = true;
/** @type {() => (false | import('@agoric/swingset-vat').CleanupBudget)} */
const allowCleanup = () =>
cleanupStarted && !cleanupDone && { ...remainingCleanups };
const startCleanup = () => {
assert(!cleanupStarted);
cleanupStarted = true;
return totalBeans === 0n && !cleanupDone;
};
const didCleanup = details => {
for (const [phase, count] of Object.entries(details.cleanups)) {
if (phase === 'total') continue;
if (!hasOwn(remainingCleanups, phase)) {
// TODO: log unknown phases?
remainingCleanups[phase] = defaultCleanupBudget;
}
remainingCleanups[phase] -= count;
if (remainingCleanups[phase] <= 0) cleanupDone = true;
}
// We return true to allow processing of any BOYD/GC prompted by cleanup,
// even if cleanup as such is now done.
return true;
};

const policy = harden({
vatCreated() {
totalBeans += vatCreation;
return shouldRun();
},
crankComplete(details = {}) {
assert.typeof(details, 'object');
if (details.computrons) {
assert.typeof(details.computrons, 'bigint');

// TODO: xsnapComputron should not be assumed here.
// Instead, SwingSet should describe the computron model it uses.
totalBeans += details.computrons * xsnapComputron;
}
return shouldRun();
},
crankFailed() {
const failedComputrons = 1000000n; // who knows, 1M is as good as anything
totalBeans += failedComputrons * xsnapComputron;
return shouldRun();
},
emptyCrank() {
return shouldRun();
},
allowCleanup,
didCleanup,

shouldRun,
remainingBeans: () =>
ignoreBlockLimit ? undefined : blockComputeLimit - totalBeans,
totalBeans: () => totalBeans,
startCleanup,
});
return policy;
}
113 changes: 1 addition & 112 deletions packages/cosmic-swingset/src/launch-chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,11 @@ import {
makeSlogCallbacks,
} from './kernel-stats.js';

import {
BeansPerBlockComputeLimit,
BeansPerVatCreation,
BeansPerXsnapComputron,
} from './sim-params.js';
import { parseParams } from './params.js';
import { makeQueue, makeQueueStorageMock } from './helpers/make-queue.js';
import { exportStorage } from './export-storage.js';
import { parseLocatedJson } from './helpers/json.js';

const { hasOwn } = Object;
import { computronCounter } from './computron-counter.js';

/** @import { BlockInfo } from '@agoric/internal/src/chain-utils.js' */
/** @import { Mailbox, RunPolicy, SwingSetConfig } from '@agoric/swingset-vat' */
Expand Down Expand Up @@ -282,111 +276,6 @@ export async function buildSwingset(
* }} ChainRunPolicy
*/

/**
* @typedef {object} BeansPerUnit
* @property {bigint} blockComputeLimit
* @property {bigint} vatCreation
* @property {bigint} xsnapComputron
*/

/**
* Return a stateful run policy that supports two phases: first allow
* non-cleanup work (presumably deliveries) until an overrideable computron
* budget is exhausted, then (iff no work was done and at least one vat cleanup
* budget field is positive) a cleanup phase that allows cleanup work (and
* presumably nothing else) until one of those fields is exhausted.
* https://github.com/Agoric/agoric-sdk/issues/8928#issuecomment-2053357870
*
* @param {object} params
* @param {BeansPerUnit} params.beansPerUnit
* @param {import('@agoric/swingset-vat').CleanupBudget} [params.vatCleanupBudget]
* @param {boolean} [ignoreBlockLimit]
* @returns {ChainRunPolicy}
*/
function computronCounter(
{ beansPerUnit, vatCleanupBudget },
ignoreBlockLimit = false,
) {
const {
[BeansPerBlockComputeLimit]: blockComputeLimit,
[BeansPerVatCreation]: vatCreation,
[BeansPerXsnapComputron]: xsnapComputron,
} = beansPerUnit;
assert.typeof(blockComputeLimit, 'bigint');
assert.typeof(vatCreation, 'bigint');
assert.typeof(xsnapComputron, 'bigint');

let totalBeans = 0n;
const shouldRun = () => ignoreBlockLimit || totalBeans < blockComputeLimit;

const remainingCleanups = { default: Infinity, ...vatCleanupBudget };
const defaultCleanupBudget = remainingCleanups.default;
let cleanupStarted = false;
let cleanupDone = false;
const cleanupPossible =
Object.values(remainingCleanups).length > 0
? Object.values(remainingCleanups).some(n => n > 0)
: defaultCleanupBudget > 0;
if (!cleanupPossible) cleanupDone = true;
/** @type {() => (false | import('@agoric/swingset-vat').CleanupBudget)} */
const allowCleanup = () =>
cleanupStarted && !cleanupDone && { ...remainingCleanups };
const startCleanup = () => {
assert(!cleanupStarted);
cleanupStarted = true;
return totalBeans === 0n && !cleanupDone;
};
const didCleanup = details => {
for (const [phase, count] of Object.entries(details.cleanups)) {
if (phase === 'total') continue;
if (!hasOwn(remainingCleanups, phase)) {
// TODO: log unknown phases?
remainingCleanups[phase] = defaultCleanupBudget;
}
remainingCleanups[phase] -= count;
if (remainingCleanups[phase] <= 0) cleanupDone = true;
}
// We return true to allow processing of any BOYD/GC prompted by cleanup,
// even if cleanup as such is now done.
return true;
};

const policy = harden({
vatCreated() {
totalBeans += vatCreation;
return shouldRun();
},
crankComplete(details = {}) {
assert.typeof(details, 'object');
if (details.computrons) {
assert.typeof(details.computrons, 'bigint');

// TODO: xsnapComputron should not be assumed here.
// Instead, SwingSet should describe the computron model it uses.
totalBeans += details.computrons * xsnapComputron;
}
return shouldRun();
},
crankFailed() {
const failedComputrons = 1000000n; // who knows, 1M is as good as anything
totalBeans += failedComputrons * xsnapComputron;
return shouldRun();
},
emptyCrank() {
return shouldRun();
},
allowCleanup,
didCleanup,

shouldRun,
remainingBeans: () =>
ignoreBlockLimit ? undefined : blockComputeLimit - totalBeans,
totalBeans: () => totalBeans,
startCleanup,
});
return policy;
}

/**
* @template [T=unknown]
* @typedef {object} LaunchOptions
Expand Down

0 comments on commit fe2f37f

Please sign in to comment.