• exec/tests/socket/listening-close.jssrc/sbbs3/js_socket.cpp js_socket.

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Thu Nov 5 18:23:16 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/13d94e64515bba9c62164314
    Added Files:
    exec/tests/socket/listening-close.js
    Modified Files:
    src/sbbs3/js_socket.cpp js_socket.h
    Log Message:
    js: close a ListeningSocket's listeners on close() and finalize (#1296)

    A ListeningSocket keeps its listeners in a socket set and has no
    sock, so do_js_close() returned at its INVALID_SOCKET check before
    closing anything, and js_finalize_socket() skipped the set because the
    object is marked external. Neither close() nor garbage collection
    released the port: a script re-run in the same process (jsexec -l, a
    looping static service, the IRCd's /RESTART) either failed to bind
    (Unix) or bound a second listener beside the orphaned one (Windows with SO_REUSEADDR).

    do_js_close() now destroys the set, after removing its event handlers.
    A new set_owned flag, set only by the ListeningSocket constructor,
    limits that to sets the script created: a static service's
    server.socket wraps the set that Services owns and destroys itself, and
    is marked external too, so destroying every set here would have freed
    that one twice. A closed ListeningSocket then behaves like a closed
    Socket.

    The free(p->set) in js_finalize_socket() is removed: no non-external
    socket has a set, so it never ran.

    Add exec/tests/socket/listening-close.js: the port can be bound again
    after close(), after a second close(), after garbage collection, and
    after close() with a read handler installed.

    Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net