v0.19 more porting: basics of actor system and postgres driver #1483

Merged
fare merged 68 commits from v0.19-std-more-porting into v0.19-staging 2026-09-07 02:54:57 +00:00
Owner

assisted by ds4 flash on the spark and chatgpt sol.

assisted by ds4 flash on the spark and chatgpt sol.
vyzo changed title from WIP: v0.19 more porting: postgres driver to WIP: v0.19 more porting: basics of actor system and postgres driver 2026-09-03 14:29:10 +00:00
vyzo added 27 commits 2026-09-04 13:10:33 +00:00
it is a security issue otherwise, as the host authorizes based
on the protocol.
vyzo added 16 commits 2026-09-04 19:05:48 +00:00
vyzo changed title from WIP: v0.19 more porting: basics of actor system and postgres driver to v0.19 more porting: basics of actor system and postgres driver 2026-09-06 13:14:53 +00:00
vyzo requested review from fare 2026-09-06 13:15:02 +00:00
Author
Owner

this is ready for review now.

this is ready for review now.
Owner

I'll give a manual look, but here's what GPT 5.6 Sol (High) says:

I reviewed the current head, d9da644e79. This is a fairly large PR—61 commits and 82 files—so I concentrated on the newly ported actor machinery, connection pool and time/date code, and skimmed the PostgreSQL changes. (cons.io git)

I would request changes. I see four fairly serious bugs and two lesser ones:

  1. [P1] Expired actor messages are never removed from the mailbox. In generate-receive-envelope-e, the expired case is just (loop), whereas a successfully matched envelope calls thread-mailbox-extract-and-rewind. (cons.io git) Gambit's semantics matter here: thread-mailbox-next explicitly leaves the message in the mailbox; only thread-mailbox-extract-and-rewind removes the last examined message. (Gambit Scheme)

    So a late reply to a timed-out interaction becomes permanent garbage. Every later selective receive can encounter it again after the cursor is rewound. Repeated timeouts can therefore cause unbounded mailbox growth and progressively slower receives.

    I think the expired arm needs roughly:

    (begin
      (thread-mailbox-extract-and-rewind)
      (loop))
    

    plus a regression test with a late reply.

  2. [P1] Direct numeric <- / << timeouts appear broken by a typo. reaction-timeout says:

    (seconds->time (+ (##current-time-point timeo)))
    

    i.e. timeo is being passed to ##current-time-point, rather than added to its result. This looks like it simply wants:

    (seconds->time (+ (##current-time-point) timeo))
    

    The generated receive path feeds real-valued timeout: arguments through this function. (cons.io git)

    The tests don't catch this because the ->> tests ultimately pass an already-constructed time object to <-; there is no direct (<- timeout: 0.5 ...) test. (cons.io git)

  3. [P1] ->>> computes relative deadlines incorrectly, especially for fractional timeouts. It currently does:

    (+ (current-time-seconds) (exact (ceiling timeo)))
    

    but current-time-seconds itself is floor(##current-time-point). (cons.io git)

    Thus, at time 100.9, a timeout of 0.5 expires at 101, i.e. only 0.1 seconds later. A timeout of 5 seconds actually lasts between roughly 4 and 5 seconds. The new 0.5 test merely checks that it eventually times out, so it cannot catch this. (cons.io git)

    I think there should be a precise local deadline based on ##current-time-point, distinct from the integer Unix-second expiration carried in the envelope. If the protocol requires integer expiry, doing ceiling should happen after adding the precise current time, not separately.

  4. [P1] A closed connection pool can be resurrected by a late put. connection-pool-close closes both idle and checked-out connections, replaces cp.connect with an erroring procedure, and empties conns/out. But connection-pool-put unconditionally puts its connection back into cp.conns. And connection-pool-get checks cp.conns before invoking cp.connect. (cons.io git)

    So:

    A gets conn
    close(pool)        ; closes conn, clears lists
    A puts conn        ; closed conn goes back into cp.conns
    B gets pool        ; receives closed conn
    

    The new close test gets very close to this case—it deliberately closes a pool with a checked-out connection—but never returns that connection after closing. (cons.io git)

    I'd give the pool an explicit closed? state. get should check it first, and put after closure should at least drop/close the returned connection rather than enqueue it.

  5. [P2] envelope-expired? assumes every Options has an expiration, although the type explicitly says otherwise. Options.expire is optional with default #f, alongside independent continue and auth fields, but:

    (and e.options
         (<= e.options.expire ...))
    

    compares #f numerically whenever an envelope has options but no expiration. (cons.io git)

    At minimum:

    (and e.options
         e.options.expire
         (<= e.options.expire ...))
    

    This may not be exercised much while ensemble support is back in TODO state, but the data model explicitly permits such envelopes.

  6. [P2] string->date silently ignores strptime failure. The C wrapper deliberately throws away strptime's return value, then string->date returns the Date structure unconditionally. (cons.io git) That means malformed input—or possibly partially consumed input—produces whatever struct tm happens to contain instead of reporting a parse failure.

    I would have the wrapper preserve whether strptime returned NULL and probably also require complete input consumption for string->date.

The first one is the nastiest to me: it's a subtle selective-receive leak that could survive ordinary testing for a long time. The pool-close race is also a definite lifecycle bug. The two timeout calculations deserve direct timing tests rather than merely checking that a Timeout? is eventually raised.

I didn't spot anything comparably alarming in the PostgreSQL query port itself from this pass.

I'll give a manual look, but here's what GPT 5.6 Sol (High) says: I reviewed the current head, `d9da644e79`. This is a fairly large PR—61 commits and 82 files—so I concentrated on the newly ported actor machinery, connection pool and time/date code, and skimmed the PostgreSQL changes. ([cons.io git][1]) I would **request changes**. I see four fairly serious bugs and two lesser ones: 1. **[P1] Expired actor messages are never removed from the mailbox.** In `generate-receive-envelope-e`, the expired case is just `(loop)`, whereas a successfully matched envelope calls `thread-mailbox-extract-and-rewind`. ([cons.io git][2]) Gambit's semantics matter here: `thread-mailbox-next` explicitly *leaves the message in the mailbox*; only `thread-mailbox-extract-and-rewind` removes the last examined message. ([Gambit Scheme][3]) So a late reply to a timed-out interaction becomes permanent garbage. Every later selective receive can encounter it again after the cursor is rewound. Repeated timeouts can therefore cause unbounded mailbox growth and progressively slower receives. I think the expired arm needs roughly: ```scheme (begin (thread-mailbox-extract-and-rewind) (loop)) ``` plus a regression test with a late reply. 2. **[P1] Direct numeric `<-` / `<<` timeouts appear broken by a typo.** `reaction-timeout` says: ```scheme (seconds->time (+ (##current-time-point timeo))) ``` i.e. `timeo` is being passed to `##current-time-point`, rather than added to its result. This looks like it simply wants: ```scheme (seconds->time (+ (##current-time-point) timeo)) ``` The generated receive path feeds real-valued `timeout:` arguments through this function. ([cons.io git][2]) The tests don't catch this because the `->>` tests ultimately pass an already-constructed time object to `<-`; there is no direct `(<- timeout: 0.5 ...)` test. ([cons.io git][4]) 3. **[P1] `->>>` computes relative deadlines incorrectly, especially for fractional timeouts.** It currently does: ```scheme (+ (current-time-seconds) (exact (ceiling timeo))) ``` but `current-time-seconds` itself is `floor(##current-time-point)`. ([cons.io git][2]) Thus, at time `100.9`, a timeout of `0.5` expires at `101`, i.e. only 0.1 seconds later. A timeout of 5 seconds actually lasts between roughly 4 and 5 seconds. The new `0.5` test merely checks that it eventually times out, so it cannot catch this. ([cons.io git][4]) I think there should be a precise local deadline based on `##current-time-point`, distinct from the integer Unix-second expiration carried in the envelope. If the protocol requires integer expiry, doing `ceiling` should happen **after adding** the precise current time, not separately. 4. **[P1] A closed connection pool can be resurrected by a late `put`.** `connection-pool-close` closes both idle and checked-out connections, replaces `cp.connect` with an erroring procedure, and empties `conns`/`out`. But `connection-pool-put` unconditionally puts its connection back into `cp.conns`. And `connection-pool-get` checks `cp.conns` *before* invoking `cp.connect`. ([cons.io git][5]) So: ```text A gets conn close(pool) ; closes conn, clears lists A puts conn ; closed conn goes back into cp.conns B gets pool ; receives closed conn ``` The new close test gets very close to this case—it deliberately closes a pool with a checked-out connection—but never returns that connection after closing. ([cons.io git][4]) I'd give the pool an explicit `closed?` state. `get` should check it first, and `put` after closure should at least drop/close the returned connection rather than enqueue it. 5. **[P2] `envelope-expired?` assumes every `Options` has an expiration, although the type explicitly says otherwise.** `Options.expire` is optional with default `#f`, alongside independent `continue` and `auth` fields, but: ```scheme (and e.options (<= e.options.expire ...)) ``` compares `#f` numerically whenever an envelope has options but no expiration. ([cons.io git][6]) At minimum: ```scheme (and e.options e.options.expire (<= e.options.expire ...)) ``` This may not be exercised much while ensemble support is back in TODO state, but the data model explicitly permits such envelopes. 6. **[P2] `string->date` silently ignores `strptime` failure.** The C wrapper deliberately throws away `strptime`'s return value, then `string->date` returns the `Date` structure unconditionally. ([cons.io git][7]) That means malformed input—or possibly partially consumed input—produces whatever `struct tm` happens to contain instead of reporting a parse failure. I would have the wrapper preserve whether `strptime` returned NULL and probably also require complete input consumption for `string->date`. The first one is the nastiest to me: it's a subtle selective-receive leak that could survive ordinary testing for a long time. The pool-close race is also a definite lifecycle bug. The two timeout calculations deserve direct timing tests rather than merely checking that a `Timeout?` is eventually raised. I didn't spot anything comparably alarming in the PostgreSQL query port itself from this pass. [1]: https://git.cons.io/mighty-gerbils/gerbil/pulls/1483 "#1483 - v0.19 more porting: basics of actor system and postgres driver - mighty-gerbils/gerbil - cons.io git" [2]: https://git.cons.io/mighty-gerbils/gerbil/src/commit/d9da644e79efd22e44178fd05355fafe3dd42d04/src/std/actor/interaction.ss "gerbil/src/std/actor/interaction.ss at d9da644e79efd22e44178fd05355fafe3dd42d04 - mighty-gerbils/gerbil - cons.io git" [3]: https://gambitscheme.org/4.9.7/manual/ "Gambit, a portable implementation of Scheme" [4]: https://git.cons.io/mighty-gerbils/gerbil/pulls/1483/files "#1483 - v0.19 more porting: basics of actor system and postgres driver - mighty-gerbils/gerbil - cons.io git" [5]: https://git.cons.io/mighty-gerbils/gerbil/src/commit/d9da644e79efd22e44178fd05355fafe3dd42d04/src/std/db/connpool.ss "gerbil/src/std/db/connpool.ss at d9da644e79efd22e44178fd05355fafe3dd42d04 - mighty-gerbils/gerbil - cons.io git" [6]: https://git.cons.io/mighty-gerbils/gerbil/src/commit/d9da644e79efd22e44178fd05355fafe3dd42d04/src/std/actor/message.ss "gerbil/src/std/actor/message.ss at d9da644e79efd22e44178fd05355fafe3dd42d04 - mighty-gerbils/gerbil - cons.io git" [7]: https://git.cons.io/mighty-gerbils/gerbil/src/commit/d9da644e79efd22e44178fd05355fafe3dd42d04/src/std/time/date.ss "gerbil/src/std/time/date.ss at d9da644e79efd22e44178fd05355fafe3dd42d04 - mighty-gerbils/gerbil - cons.io git"
Author
Owner

ok i shall fix with the help of my friend sol max.

ok i shall fix with the help of my friend sol max.
fare left a comment

My admittedly cursory review didn't find much.

But in general, I think the tests should include more tests about failure cases and recovery from them.

My admittedly cursory review didn't find much. But in general, I think the tests should include more tests about failure cases and recovery from them.
@ -40,6 +40,12 @@ If you want to run all the tests in a directory and
it's subdirectory recursively, do `./build.sh test your-directory/...`.
If you want to run all tests in the stdlib do `./build.sh test std/...`
## Runing a build-local gxi
Owner

Running

Running
Author
Owner

sol addressed all the issues, assisted by gerbilosaurus rex who takes credit for the O(1) mailbox-extract-and-next idea.

sol addressed all the issues, assisted by gerbilosaurus rex who takes credit for the O(1) mailbox-extract-and-next idea.
vyzo requested review from fare 2026-09-06 21:08:08 +00:00
Author
Owner

i'll fix the typo.

i'll fix the typo.
Author
Owner

all fixed

all fixed
fare force-pushed v0.19-std-more-porting from 48ace8fa9b to 7e79fde5df 2026-09-07 02:26:40 +00:00 Compare
fare merged commit c5a49c5c20 into v0.19-staging 2026-09-07 02:54:57 +00:00
vyzo deleted branch v0.19-std-more-porting 2026-09-07 10:24:00 +00:00
Sign in to join this conversation.
No description provided.