511db25230
El SDK CI - dev / build-and-test (pull_request) Failing after 14m31s
The async/future measurements were produced by a C stub in /tmp, and that artifact was destroyed when the session worktrees were removed. The log then asserted results with nothing behind them -- a claim inside an evidence record, which is exactly what turns a chain of custody into a pile. Rerun, not reconstructed. Rebuilding the missing file would have been a fabrication with a fresh timestamp; rerunning produces new evidence with its own. lang/tests/integration/fixtures/future.c the future, as a tagged heap object lang/tests/integration/async_future.sh the harness, 6/6 ok unbound: synchronous, correct result ok unbound: el_await on a non-future passes through, no crash ok bound: does not crash ok bound: the awaited result is correct ok bound: the caller continues BEFORE the body finishes ok bound: wrap returns in <10ms while the body takes 50ms LABELLED AS A REPLICATION. The outcomes were already known when this harness was written, so its expectations are NOT predictions committed in advance. Its evidentiary value is that a third party can reproduce it, not that it was called ahead of time. Recording it as anything stronger would corrupt the record it is meant to repair. The fixture also carries the P5 defect and its fix in a comment: the first el_await dereferenced ->magic off an unvalidated slot and SIGSEGV'd on the unbound path, sixty seconds after the same defect was diagnosed elsewhere in the runtime.
67 lines
2.6 KiB
C
67 lines
2.6 KiB
C
/* future.c — a FUTURE as one more magic-tagged heap object.
|
|
*
|
|
* Fixture for tests/integration/async_future.sh. Linked into the probe but
|
|
* never referenced from El source: everything here is reached only by binding
|
|
* a construct AFTER the binary exists.
|
|
*
|
|
* The claim under test: @async needs no compiler change. el_val_t already
|
|
* carries List, Map, Geometry, Manifold and Bin as magic-tagged heap pointers;
|
|
* a future is one more, and el_seam_wrap hands the target the body so it can
|
|
* decide whether and when to invoke it.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
typedef int64_t el_val_t;
|
|
|
|
#define EL_MAGIC_FUT 0xE1F07000u
|
|
typedef struct { uint32_t magic; pthread_t th; el_val_t result; int done;
|
|
el_val_t (*body)(void*); void* env; } ElFuture;
|
|
|
|
static long t0_us;
|
|
static long now_us(void){ struct timespec ts; clock_gettime(CLOCK_MONOTONIC,&ts);
|
|
return ts.tv_sec*1000000L + ts.tv_nsec/1000; }
|
|
|
|
static void* fut_runner(void* v){
|
|
ElFuture* f = (ElFuture*)v;
|
|
printf("BODY_START %ld\n", now_us()-t0_us);
|
|
usleep(50000); /* 50ms, so interleaving is visible */
|
|
f->result = f->body(f->env);
|
|
f->done = 1;
|
|
printf("BODY_END %ld\n", now_us()-t0_us);
|
|
return NULL;
|
|
}
|
|
|
|
/* wraps_body target: returns the HANDLE immediately, never the result */
|
|
el_val_t defer(el_val_t fn, el_val_t con, el_val_t (*b)(void*), void* e){
|
|
(void)fn; (void)con;
|
|
t0_us = now_us();
|
|
ElFuture* f = calloc(1,sizeof(ElFuture));
|
|
f->magic = EL_MAGIC_FUT; f->body = b; f->env = e;
|
|
pthread_create(&f->th, NULL, fut_runner, f);
|
|
printf("WRAP_RETURNED %ld\n", now_us()-t0_us);
|
|
return (el_val_t)(intptr_t)f;
|
|
}
|
|
|
|
/* el_await — block on the handle and yield the real result.
|
|
*
|
|
* NEVER dereference to decide whether a slot is a pointer. el_val_t carries
|
|
* integers too, so reading ->magic off an integer dereferences that integer AS
|
|
* AN ADDRESS. The first version of this function did exactly that and
|
|
* SIGSEGV'd on the unbound path -- sixty seconds after the same defect was
|
|
* diagnosed elsewhere in the runtime. Check the floor and alignment first. */
|
|
el_val_t el_await(el_val_t h){
|
|
if (h < 0x10000) return h; /* small ints / low addresses */
|
|
if (h & 0x7) return h; /* malloc returns 8-aligned */
|
|
ElFuture* f = (ElFuture*)(intptr_t)h;
|
|
if (f->magic != EL_MAGIC_FUT) return h; /* safe to read now */
|
|
pthread_join(f->th, NULL);
|
|
el_val_t r = f->result;
|
|
free(f);
|
|
return r;
|
|
}
|