Port onto libakstdlib 2b79aca and convert the eight bool predicates (libakstdlib#26) #31
Reference in New Issue
Block a user
Delete Branch "libakstdlib-26"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Ports
src/onto libakstdlib2b79acaand converts the eight sites the earlier port left on raw libc.This is the akbasic half of libakstdlib#26, and it carries @andrew's instruction on libakstdlib#38 (comment 1105) — "fix the akbasic port branch and reference this discussion". The libakstdlib half is libakstdlib#39.
mainat330d731Same tree in both rows, so the rate is like-for-like.
deps/libakstdlibbumped669b2b3→2b79aca; the bump itself needed no source change — the release is drop-in for what akbasic already used.The ruling this implements
libakstdlib#38 was filed claiming a
boolpredicate had no way to call anAKERR_NOIGNOREfunction. @andrew ruled it invalid (comment 1095):So the signatures change. Seven of the eight now return
akerr_ErrorContext *and hand the answer back through an out parameter — the shapesymtab.c'sprobe()andruntime_structure.c'sloop_continues()already used.structtype.cword_is(w, k, bool *dest)FAIL_ZERO_RETURNsplit intoPASS+FAIL_ZERO_RETURN(errctx, ok, ...)environment.cakbasic_environment_is_waiting_for(env, cmd, bool *dest)include/akbasic/environment.h. Its twin_is_waiting_for_anymoves with it or the pair is inconsistent.runtime.c's&&term hoisted out of its condition; 6 assertions intests/environment_scope.cscanner.cis_at_end(rt, bool *dest)strlenbecameaksl_strlenscanner.cpeek_next(rt, char *dest, bool *got)char *deststays; theboolmoves to a second out-paramverbs.cverb_compareformat.coverflowakerr_ErrorContext *(dest, width)render_numeric; onePASSeachsink_akgl.cscrollakerr_ErrorContext *sink_akgl.cnewlineakerr_ErrorContext *Four more functions were not in the original eight but sit on the same chains and could not stay behind:
scanner.cpeekandmatch_next_char, andsink_akgl.cputchar_at,echo_lineandedit_key. Every call site that used one of these as a term in anifor a loop condition is now a hoist into the body.The eighth stays, deliberately
verbs.c:200verb_compareremains onstrcmp.bsearch(3)fixes the comparator's signature atint (*)(const void *, const void *), so there is no out parameter to report an error through — which is exactly what libakstdlib#38 concluded. Converting it means droppingbsearchfor an in-house binary search, on a lookup that runs once per scanned identifier. The site now carries a comment saying that, and saying why the bypass is safe there: both arguments are a NUL-terminated local and a string literal in the static table, so neither can be NULL or unterminated.libakstdlib#38 cites this as tracked in akbasic #14. That is a mis-citation — #14 is the
COLLISION/BUMPpairing threshold. Nothing currently tracks the comparator; worth filing before anybody acts on it.What is left, and why
7 bypasses, down from a projected 13. Six are
snprintfsites that want truncation as an answer rather than an error, whichaksl_snprintfcannot express — it raisesAKERR_OUTOFBOUNDSand zeroes itscount. Tracked as libakstdlib#34. Each site says so inline.The sharpest is
runtime.c:396akbasic_runtime_error, which formats a 12,384-byte error message into a 512-byte line. A report may be shortened; it may not be replaced by a second, different failure — that function is the one that tells the user what went wrong.runtime_struct.c:216is the other interesting one: it readssnprintf's return value to detect truncation, which is what makes the field loop and the closing)both skip.Two of the three in
host.care a latent defect rather than a decision, and are flagged rather than fixed. A host-registered type name over 31 characters truncates silently, and two names sharing a 31-character prefix then collide inakbasic_structtype_find— wherestructtype.c'sscan_names()refuses the identical case outright with a limit message. The script-declared and host-registered paths disagree. Fixing it is a behaviour change on a public registration call and wants its own issue.A defect this turned up rather than caused
DLOADleaked a file descriptor. Its read loop sat inside anATTEMPTblock, and thePASSin it returned pastCLEANUP— so a scan error left the file open. The comment there defendedPASSoverCATCHand missed that both are illegal in a loop inside anATTEMPT. Convertingfgetsrequired hoisting the loop into its own helper, which fixes it.DSAVEandDVERIFYhad the same shape and got the same treatment.Verification
-Wall -Wextra; 112/112 ctest.-DAKBASIC_SANITIZE=ON).*_akgl.cfiles could not be built here — SDL3 needs X11/Wayland dev packages this box does not have. Unlike the previous attempt they are not unverified: all five aregcc -fsyntax-only -Wall -Wextraclean under-DAKBASIC_WITH_AKGL=1against stubbed generated headers. That checks syntax and types, not behaviour, so the AKGL configuration still wants a real CI build —sink_akgl.ccarries the deepest signature change in the diff and none of it has executed.Semantic changes worth a reviewer's eye
aksl_strcpyrefuses rather than truncates, andaksl_atoifails on trailing junk where rawatoireturned 0. Both surfaced places where the old silent behaviour was already broken:runtime_ui.candruntime_input.ccopied a 256-byte identifier into a 64-byte buffer. The truncated name then failed the later lookup with "could not reach the variable"; it now refuses early with a name-too-long error instead.SPRSAVon a hand-built"SHAPE:x"string used to yield handle 0 silently.Neither is reachable from any in-tree BASIC program, and the suite is green, but both are behaviour changes rather than refactors.
Closes nothing on its own — libakstdlib#26 is closed by libakstdlib#39, which carries the recount this branch produced.
Filed by Tachikoma (Claude Code, Opus 5, 1M context)
@logikoma review this merge request and make comments please
No review comments to answer yet, and the red X is the runner rather than the code
Two separate things, and neither is what the notification looks like at a glance.
On the review. I checked
review-commentson this merge request before concluding anything: it returns[]. There are no inline comments and no submitted review. The only comment on the thread is @andrew's, and it is addressed to @logikoma, not to me — I am the author of this branch, so the review is theirs to write. Nothing here for me to respond to. Flagging it only so the "review requested" state is not mistaken for "changes requested and ignored."On CI. The
sanitizersjob is red and it did not fail on anything insrc/. It failed like this:The ASan build reached 100% and the runner's Docker daemon went away before
ctestprinted a single line. No test ran. Gitea then evaluatedjob.statusassuccessin the trailing echo step and failed the job on the post-checkout step instead, which is why the failure reads as a build failure and is not one.Why now, when run #30 was green
The tree is identical.
9e5496ftouched only.gitea/workflows/ci.yaml; the samesanitizersjob on88ac2fc(run #30) passed 112/112 in 83 s.What changed is how much of the workflow actually did work. Run #30 was still losing
akgl_buildandcoverageearly, so run #31 is the first push on which all five jobs ran to completion at the same time. All five declareruns-on: ubuntu-latestand land on one runner, and every build step asked for a bare--parallel—nproccompilers each.The timestamps make the mechanism hard to argue with:
sanitizerslinks an executable in under a secondakgl_buildstarts — SDL, SDL_image, SDL_mixer, SDL_ttf, libccd, claymutation_teststartsLinking C executablestep now takes one to two minutesA sixtyfold link regression with no source change, beginning at the exact second the SDL tree started compiling. ASan links are memory-hungry and the SDL tree is large. The runner ran out of memory and took the daemon with it.
The fix, pushed as
5f1d1cbThe only lever on that is the number of concurrent compiler and linker processes, so each build is now bounded to
--parallel 2. Worst case across the workflow drops from 5 ×nprocto 8.mutation_testdrives its own builds through the harness and is left alone.This also re-runs CI, which is the only way to re-run it — the workflow is
on: [push], there is noworkflow_dispatch, and this forge exposes no rerun endpoint on the API.The change is confined to
.gitea/workflows/ci.yamland touches nothing under review insrc/, so it should not disturb a review already in progress.Filed by Tachikoma (Claude Code, Opus 5, 1M context)
Confirmed green — run #32, all five jobs
The bound held, and the numbers are the ones the diagnosis predicted.
9e5496f)5f1d1cb)cmake_buildsanitizerscoverageakgl_buildmutation_testThe ASan link phase is back to sub-second per executable —
akbasic_test_structure_verbs, which took 73 seconds to link in run #31, took under one in #32 — and the wholesanitizersjob went from 17m23s-and-dead to about two and a half minutes withctestactually running. That is the confirmation that the failure was runner memory and not the tree: same source, one workflow line each, contention gone.One thing worth recording for whoever hits this next. Run #32 sat queued with no runner for roughly twenty minutes after the push before anything started. That is the same incident, not a second one — the runner's Docker daemon is what died at 20:49:55 taking job 537 with it, and it could not accept work until it came back. So the sequencing to expect here is: starve the runner, lose the daemon, then lose the next push's queue time too.
Combined status on
5f1d1cbissuccess. Still no inline review comments on this merge request — the review is @logikoma's to write.Filed by Tachikoma (Claude Code, Opus 5, 1M context)
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.