/* Host-callback acceptance probe, mode-selected by argv[1]: * run — the full happy path: registration return codes (0 known, * -1 unknown/NULL name), a service-shaped export streaming * chunks through the bytes+u32 channel interleaved with * computation (contents, order, and thread identity all * recorded by the callback and asserted after the entry * returns — the calls are synchronous, on the calling * thread), string/bool and scalar channels, host RETURNS * riding i32/u32 back into compiled code, re-registration * routing to the new context, and finally a NULL-fn clear * followed by a call: the SC4025 structured trap through * the sink, exactly once, naming the entry the host called * orphan — a channel the host never registered: the first call * traps SC4025 (symbol cb_poke_orphan), the sink longjmps * out (the conforming survival pattern), and the poisoned * library aborts the next entry deterministically * preregister — an unregistered-channel call BEFORE sink registration * aborts (the funnel's last resort) */ #include #include #include #include #include extern void cb_init(void); extern void cb_set_panic_sink(void (*fn)(void *, const uint8_t *, size_t, uint64_t), void *ctx); extern void cb_collect(void); extern int32_t cb_set_callback(const char *name, void (*fn)(void), void *ctx); extern double cb_stream(double n, double base); extern double cb_ask_host(double x); extern double cb_poke_orphan(void); typedef void (*cb_fn)(void); /* ── chunk recording (the bytes+u32 channel) ─────────────────────────── */ typedef struct { char bytes[8]; size_t len; uint32_t seq; } ChunkRec; typedef struct { const char *tag; ChunkRec chunks[16]; int count; int thread_ok; /* every delivery arrived on the registering thread */ } ChunkLog; static void on_chunk(void *ctx, const uint8_t *p, size_t len, uint32_t seq) { ChunkLog *log = (ChunkLog *)ctx; ChunkRec *rec = &log->chunks[log->count]; /* Borrowed for the duration of the call only: copy out. */ memcpy(rec->bytes, p, len < sizeof rec->bytes ? len : sizeof rec->bytes); rec->len = len; rec->seq = seq; log->count++; } static pthread_t main_thread; static void on_chunk_thread_check(void *ctx, const uint8_t *p, size_t len, uint32_t seq) { ChunkLog *log = (ChunkLog *)ctx; on_chunk(ctx, p, len, seq); if (!pthread_equal(pthread_self(), main_thread)) log->thread_ok = 0; } static void dump_chunks(const ChunkLog *log) { printf("%s: %d chunk(s), thread_ok=%d\n", log->tag, log->count, log->thread_ok); for (int i = 0; i < log->count; i++) { printf(" seq=%u len=%zu bytes=%.*s\n", log->chunks[i].seq, log->chunks[i].len, (int)log->chunks[i].len, log->chunks[i].bytes); } } /* ── the other channels ──────────────────────────────────────────────── */ static char note_log[512]; static void on_note(void *ctx, const uint8_t *p, size_t len, uint8_t last) { (void)ctx; size_t used = strlen(note_log); snprintf(note_log + used, sizeof note_log - used, "[%.*s last=%d]", (int)len, (const char *)p, last != 0); } static int32_t on_progress(void *ctx, double done, double total) { (void)ctx; return (int32_t)(total - done); } static uint32_t on_mix(void *ctx, uint8_t a, int32_t b) { (void)ctx; return (uint32_t)a + (uint32_t)(-b); } /* ── the panic sink (the traps probe's parse rule) ───────────────────── */ static jmp_buf trap_jmp; static int sink_calls = 0; static void show(const uint8_t *msg, size_t len) { if (len == 0 || msg[0] != 0x01) { printf("baseline printable=%d text=%.*s", len > 0 && msg[0] >= 0x20, (int)len, (const char *)msg); return; } static const char *names[4] = {"text", "code", "symbol", "remediation"}; const uint8_t *p = msg + 1, *end = msg + len; int fields = 0; for (;;) { const uint8_t *sep = memchr(p, 0x1f, (size_t)(end - p)); const uint8_t *stop = sep != NULL ? sep : end; if (fields < 4) printf("%s=[%.*s]\n", names[fields], (int)(stop - p), (const char *)p); fields++; if (sep == NULL) break; p = sep + 1; } printf("fields=%d text_printable=%d\n", fields, len > 1 && msg[1] >= 0x20); } static void sink(void *ctx, const uint8_t *msg, size_t len, uint64_t addr) { (void)ctx; sink_calls++; printf("sink[%d]:\n", sink_calls); show(msg, len); printf("addr: %s\n", addr != 0 ? "nonzero" : "zero"); longjmp(trap_jmp, 1); } int main(int argc, char **argv) { const char *mode = argc > 1 ? argv[1] : "run"; main_thread = pthread_self(); static ChunkLog log_a = {.tag = "log_a", .thread_ok = 1}; static ChunkLog log_b = {.tag = "log_b", .thread_ok = 1}; if (strcmp(mode, "preregister") == 0) { /* No sink registered: the unregistered-channel trap must abort. * Nothing after the call may print. */ cb_init(); cb_poke_orphan(); printf("UNREACHABLE\n"); return 0; } if (strcmp(mode, "orphan") == 0) { cb_set_panic_sink(sink, NULL); /* Everything BUT orphan registered: reaching the one unregistered * channel is the defined trap, whatever else is wired. */ cb_set_callback("emitChunk", (cb_fn)on_chunk_thread_check, &log_a); cb_set_callback("progress", (cb_fn)on_progress, NULL); cb_set_callback("note", (cb_fn)on_note, NULL); cb_set_callback("mix", (cb_fn)on_mix, NULL); cb_init(); if (setjmp(trap_jmp) == 0) { cb_poke_orphan(); printf("UNREACHABLE\n"); } else { printf("survived, sink_calls=%d\n", sink_calls); fflush(stdout); cb_stream(1, 1); /* must abort — the library is poisoned */ printf("UNREACHABLE\n"); } return 0; } /* mode "run" */ /* Registration is a pure store, legal before init; return codes are the * defined refusal surface (-1 unknown or NULL name, 0 stored). */ printf("reg unknown: %d\n", (int)cb_set_callback("nope", (cb_fn)on_chunk, &log_a)); printf("reg null-name: %d\n", (int)cb_set_callback(NULL, (cb_fn)on_chunk, &log_a)); printf("reg emitChunk: %d\n", (int)cb_set_callback("emitChunk", (cb_fn)on_chunk_thread_check, &log_a)); printf("reg progress: %d\n", (int)cb_set_callback("progress", (cb_fn)on_progress, NULL)); printf("reg note: %d\n", (int)cb_set_callback("note", (cb_fn)on_note, NULL)); printf("reg mix: %d\n", (int)cb_set_callback("mix", (cb_fn)on_mix, NULL)); cb_set_panic_sink(sink, NULL); cb_init(); /* The service-shaped stream: chunks arrive synchronously, in order, on * this thread, fully delivered by the time the entry returns. */ double r1 = cb_stream(4, 3); printf("stream(4,3) = %g\n", r1); dump_chunks(&log_a); printf("notes: %s\n", note_log); log_a.count = 0; note_log[0] = '\0'; double r2 = cb_stream(2, 7); printf("stream(2,7) = %g\n", r2); dump_chunks(&log_a); printf("notes: %s\n", note_log); /* Host returns ride i32/u32 back into compiled code. */ printf("askHost(5) = %g\n", cb_ask_host(5)); /* Re-registration: latest wins — chunks route to the NEW context. */ printf("reg emitChunk again: %d\n", (int)cb_set_callback("emitChunk", (cb_fn)on_chunk_thread_check, &log_b)); log_a.count = 0; double r3 = cb_stream(1, 9); printf("stream(1,9) = %g\n", r3); printf("log_a after reroute: %d chunk(s)\n", log_a.count); dump_chunks(&log_b); /* Buffer results and callback channels coexist: collect between calls. */ cb_collect(); /* NULL clears the channel; the next call through it is the SC4025 * structured trap — exactly once, naming the entry the host called. */ printf("reg emitChunk clear: %d\n", (int)cb_set_callback("emitChunk", NULL, NULL)); if (setjmp(trap_jmp) == 0) { cb_stream(1, 1); printf("UNREACHABLE\n"); } else { printf("survived, sink_calls=%d\n", sink_calls); } return 0; }