Oninit® 4gl2everywhere — Sessions, Threading, and Operational Discipline
The bridge is one-thread-per-session. The listener accept loop spawns a fresh thread for each accepted TCP connection; that thread owns the session for its lifetime, runs the request loop until the client disconnects, and tears the session down through a deterministic cleanup checklist. Session threads do not share state.
Per-session state
Each session owns its own state — session id, client socket, the listener's effective config and logger, the resolved backend descriptor, and the backend-specific opaque state. State is allocated when the session thread starts, owned by that thread alone, and released on the cleanup path. No mutex protects it; only the owning thread reads or writes it after construction.
Session caps
Each listener carries a configurable session cap. When the cap is reached the listener replies to new connections with an SQ_ERR bearing SQLCODE -746 and the canonical IFXBRIDGE:CONNECTION_LIMIT:Maximum sessions reached body, then closes the socket. Sessions are decremented on cleanup, so the cap floats with real load.
Failure isolation
A broken session must not crash the listener. Three discipline rules enforce this: SIGPIPE is ignored at process startup; session threads never call exit(); and every send uses MSG_NOSIGNAL to suppress kernel-side SIGPIPE on a half-closed peer. Fatal session errors surface as SQ_ERR back to the client and tear down the session, never the process.
Graceful drain
SIGTERM and SIGINT trigger the same drain sequence: stop accepting new connections, close the listening socket, wait for in-flight sessions to finish, and exit cleanly. Drain is bounded only by sessions actually completing; new connections during drain receive IFXBRIDGE:SHUTDOWN:Listener draining and a TCP close. The shutdown block in YAML controls the maximum wait (0 = wait indefinitely).
SIGHUP triggers a runtime config reload. Reloadable sections are logging level / file / rotation, SQL trace settings, and per-listener session caps. The listener port and backend type are pinned at startup — changing them requires a restart. Sessions are not dropped on reload.
Multi-listener deployments
A single bridge process can run multiple listeners on different ports, each with its own backend, DSN, session cap, and optional log / sqltrace overrides. The listener block in YAML inherits log and sqltrace settings from the global block field-by-field, so cross-cutting changes are one-line edits and per-listener exceptions are explicit. Multi-listener is the natural shape for hybrid deployments — some 4GL clients pinned to Informix, others redirected to a modern backend, all under one process and one config.
Cleanup checklist
The session-end sequence runs exactly once per session: roll back any open transaction; disconnect the backend; close the SQL trace file; close the client socket; decrement the listener's session count. Cleanup is idempotent; the exactly-once guard prevents double teardown on any error path.