Files
el/lang/tests/integration/fixtures/future.c
T
bigmerge 511db25230
El SDK CI - dev / build-and-test (pull_request) Failing after 14m31s
rerun cycle 18 rather than reconstruct it
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.
2026-08-17 11:03:59 -05:00

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;
}