aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-01 05:34:25 +0100
committereunhhu <[email protected]>2026-10-01 05:34:25 +0100
commitbe297ead16b41938e84bcc3f1be628bc8725f2f4 (patch)
tree0f2e4a17b79781a751b2d8fe21e494384f1f0de6
parentd24881de03433c30ee619ee1eec3ae962ae3db19 (diff)
downloadpure-be297ead16b41938e84bcc3f1be628bc8725f2f4.tar.gz
pure-be297ead16b41938e84bcc3f1be628bc8725f2f4.zip
Count work only under a budget, and say why a view failed only then (017)
The work budget added about 26 instructions to every row of a table a host shows (bank, a table of 10,000 rows: +3.0% instructions an ask), which 017's one-app criterion allows at 1%. A sample put most of it in show_given asking every cell why it failed, and the rest in evaluate and the row lookup writing that reason and the count for every cell. Now nothing is counted without a budget (PureApp.work stays), the reason is written only when a view, a cell or a row has no value, and pure-serve asks it only of a cell without one. The table: +0.94%; the other bank queries within 0.4%. The tests that read the count set a budget that is never reached.
-rw-r--r--core/module.c39
-rw-r--r--core/module.h6
-rw-r--r--core/test-module.c8
-rw-r--r--serve/serve.c17
4 files changed, 40 insertions, 30 deletions
diff --git a/core/module.c b/core/module.c
index a5bc3c5..f3fbcd2 100644
--- a/core/module.c
+++ b/core/module.c
@@ -1442,10 +1442,12 @@ static PureResult fault(PureApp *app, PureFault kind, uint32_t pc) {
}
/* Count what the unit running (an event, or a view's evaluation) is about
- * to run, as written: false past the app's work budget. */
-static bool charge(PureApp *app, uint64_t instructions) {
+ * to run, as written: false past the app's work budget. Without a budget
+ * nothing is counted. */
+static inline bool charge(PureApp *app, uint64_t instructions) {
+ if (!app->work_budget) return true;
app->work += instructions;
- return !app->work_budget || app->work <= app->work_budget;
+ return app->work <= app->work_budget;
}
static bool reserve(PureArena *arena, size_t more) {
@@ -3445,10 +3447,10 @@ static PureResult evaluate(PureApp *app, uint32_t view, size_t row, Slot *out) {
PureFault fault = app->fault;
uint32_t fault_pc = app->fault_pc;
PureBlock code = app->module->view_code[view];
- app->work = 0;
app->fault = PURE_FAULT_NONE;
- PureResult result = charge(app, code.length) ? run(app->module, values, code, &ev, out) : PURE_LIMIT;
- app->view_fault = result == PURE_OK ? PURE_FAULT_NONE : app->fault != PURE_FAULT_NONE ? app->fault : PURE_FAULT_WORK;
+ bool counted = !app->work_budget || (app->work = 0, charge(app, code.length));
+ PureResult result = counted ? run(app->module, values, code, &ev, out) : PURE_LIMIT;
+ if (result != PURE_OK) app->view_fault = counted ? app->fault : PURE_FAULT_WORK; /* why it has no value */
app->fault = fault;
app->fault_pc = fault_pc;
return result;
@@ -3536,13 +3538,17 @@ bool pure_app_row_item(PureApp *app, uint32_t table, size_t row, size_t *item) {
if (table >= m->views || m->view_type[table] != PURE_VIEW_TABLE) return false;
if (!m->view_code[table].length) {
*item = row;
- return row < pure_list_count(app->lists[m->view_list[table]]);
+ if (row < pure_list_count(app->lists[m->view_list[table]])) return true;
+ app->view_fault = PURE_FAULT_NONE;
+ return false;
}
struct PureRows *rows = filtered(app, table);
+ if (rows && row < rows->count) {
+ *item = rows->items[row];
+ return true;
+ }
app->view_fault = rows ? rows->fault : PURE_FAULT_NONE;
- if (!rows || row >= rows->count) return false;
- *item = rows->items[row];
- return true;
+ return false;
}
bool pure_app_item_row(PureApp *app, uint32_t table, size_t item, size_t *row) {
@@ -3567,12 +3573,13 @@ bool pure_app_item_row(PureApp *app, uint32_t table, size_t item, size_t *row) {
/* A view outside a table (column false), or a column and a row of its table. */
static PureResult view_of(PureApp *app, uint32_t view, bool column, size_t row, uint8_t type, Slot *slot) {
const PureModule *m = app->module;
- app->view_fault = PURE_FAULT_NONE;
- if (view >= m->views) return PURE_INVALID;
- uint8_t kind = m->view_type[view] == PURE_VIEW_BOOL ? PURE_VIEW_NUM : m->view_type[view]; /* a bool's value is 0 or 1 */
- if (kind != type || (m->view_table[view] != 0) != column) return PURE_INVALID;
- if (column && !pure_app_row_item(app, m->view_table[view] - 1u, row, &row)) return PURE_NOT_FOUND;
- return evaluate(app, view, row, slot);
+ if (view >= m->views || (m->view_type[view] == PURE_VIEW_BOOL ? PURE_VIEW_NUM : m->view_type[view]) != type ||
+ (m->view_table[view] != 0) != column) { /* a bool's value is 0 or 1 */
+ app->view_fault = PURE_FAULT_NONE;
+ return PURE_INVALID;
+ }
+ if (column && !pure_app_row_item(app, m->view_table[view] - 1u, row, &row)) return PURE_NOT_FOUND; /* it says why */
+ return evaluate(app, view, row, slot); /* and so does this */
}
bool pure_app_view(PureApp *app, uint32_t view, double *out) {
diff --git a/core/module.h b/core/module.h
index c4919c9..2cc3109 100644
--- a/core/module.h
+++ b/core/module.h
@@ -554,11 +554,11 @@ size_t pure_app_state_bytes(const PureApp *);
* item of its list (of its slice, when it takes one) it goes over, all
* counted as the fold or walk begins. Past it the event fails with the fault
* PURE_FAULT_WORK (PURE_LIMIT) and changes nothing, and a view has no value.
- * 0: no budget, at first. */
+ * 0: no budget, at first, and then nothing is counted (PureApp.work stays). */
void pure_app_work(PureApp *, uint64_t instructions);
/* Why the last view evaluated on this app (or a table's rows asked for)
- * had no value: PURE_FAULT_WORK past the work budget, another fault, or
- * PURE_FAULT_NONE when it had one. */
+ * had no value, once it had none: PURE_FAULT_WORK past the work budget,
+ * another fault, or PURE_FAULT_NONE. pure_app_rows always says. */
PureFault pure_app_view_fault(const PureApp *);
/* Readers on other threads. Right after a save an app can make a snapshot of
diff --git a/core/test-module.c b/core/test-module.c
index f627dea..46da5f1 100644
--- a/core/test-module.c
+++ b/core/test-module.c
@@ -2677,6 +2677,8 @@ static void lane_folds(void) {
memset(module_b.view_plan, 0, sizeof(module_b.view_plan));
PureApp a, b;
CHECK(pure_app_start(&a, &module_a, (PureData){0}) == PURE_OK && pure_app_start(&b, &module_b, (PureData){0}) == PURE_OK);
+ pure_app_work(&a, UINT64_MAX); /* counted, never past it */
+ pure_app_work(&b, UINT64_MAX);
size_t l_items = roll(&g, 4) ? roll(&g, 200) : 0, m_items = roll(&g, 4) ? roll(&g, 100) : 0;
for (size_t i = 0; i < l_items + m_items; ++i) {
bool in_l = i < l_items;
@@ -4350,6 +4352,7 @@ static void work_budget(void) {
CHECK(load(walk_spec(walk_code, LEN(walk_code), walk_inputs, 3, walk_handlers, 3, walk_lists, NULL, 0), &module_a) == PURE_OK);
PureApp app;
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
+ pure_app_work(&app, UINT64_MAX); /* counted, never past it */
unsigned char payload[8];
for (uint32_t item = 0; item < 5; ++item) {
put32(put32(payload, item), 2);
@@ -4373,15 +4376,16 @@ static void work_budget(void) {
/* Views: open is 4 instructions and its fold's 3 for each task. */
CHECK(load(fold_spec(fold_code, LEN(fold_code), fold_views, LEN(fold_views), fold_handlers, 2), &module_a) == PURE_OK);
CHECK(pure_app_start(&app, &module_a, (PureData){0}) == PURE_OK);
+ pure_app_work(&app, UINT64_MAX);
for (const char *word = "abc"; *word; ++word)
CHECK(pure_app_text(&app, 0, (PureSpan){(const unsigned char *)word, 1}) == PURE_OK && app.work == 7);
double value;
- CHECK(number_view(&app, 0) == 3 && app.work == 4 + 3 * 3 && pure_app_view_fault(&app) == PURE_FAULT_NONE);
+ CHECK(number_view(&app, 0) == 3 && app.work == 4 + 3 * 3);
pure_app_work(&app, 4 + 3 * 3 - 1);
CHECK(!pure_app_view(&app, 0, &value) && pure_app_view_fault(&app) == PURE_FAULT_WORK);
CHECK(app.fault == PURE_FAULT_NONE); /* a view leaves the app's own fault alone */
pure_app_work(&app, 4 + 3 * 3);
- CHECK(number_view(&app, 0) == 3 && pure_app_view_fault(&app) == PURE_FAULT_NONE);
+ CHECK(number_view(&app, 0) == 3);
pure_app_work(&app, 0);
CHECK(pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_app_point(&app, 1, 0) == PURE_OK &&
pure_app_input(&app, 1, PURE_EDGE_DOWN) == PURE_OK && pure_app_point(&app, 1, 1) == PURE_OK &&
diff --git a/serve/serve.c b/serve/serve.c
index 5ba137f..b7a122e 100644
--- a/serve/serve.c
+++ b/serve/serve.c
@@ -1425,28 +1425,27 @@ static PureResult show_given(PureApp *app, const Route *route, size_t available,
for (unsigned k = 0; k < count; ++k) {
put_bytes(&o, columns[k].key + !k, columns[k].length - !k);
uint8_t kind = m->view_type[columns[k].view];
+ bool valued;
if (kind == PURE_VIEW_I64) {
int64_t integer;
- if (!pure_app_cell_i64(app, columns[k].view, row, &integer)) put_bytes(&o, "null", 4);
- else put_integer(&o, integer);
+ if ((valued = pure_app_cell_i64(app, columns[k].view, row, &integer))) put_integer(&o, integer);
} else if (kind == PURE_VIEW_NUM || kind == PURE_VIEW_BOOL) {
- if (!pure_app_cell(app, columns[k].view, row, &value)) put_bytes(&o, "null", 4);
- else if (kind == PURE_VIEW_BOOL) put_bytes(&o, value ? "true" : "false", value ? 4 : 5);
+ if (!(valued = pure_app_cell(app, columns[k].view, row, &value))) {
+ } else if (kind == PURE_VIEW_BOOL) put_bytes(&o, value ? "true" : "false", value ? 4 : 5);
else put_number(&o, value);
} else {
size_t mark = o.size;
put_bytes(&o, "\"", 1);
- if (pure_app_cell_text(app, columns[k].view, row, put_json_span, &o) == PURE_OK) put_bytes(&o, "\"", 1);
- else if (!o.failed) {
- o.size = mark;
- put_bytes(&o, "null", 4);
- }
+ if ((valued = pure_app_cell_text(app, columns[k].view, row, put_json_span, &o) == PURE_OK)) put_bytes(&o, "\"", 1);
+ else o.size = o.failed ? o.size : mark;
}
+ if (valued) continue;
if (pure_app_view_fault(app) == PURE_FAULT_WORK) { /* a cell past the work budget fails the table */
status = PURE_LIMIT;
end = row;
break;
}
+ put_bytes(&o, "null", 4); /* a cell without a value */
}
put_bytes(&o, "}", 1);
}