Initialize MEOS per worker thread#137
Conversation
54e6181 to
f896b95
Compare
Reviewer's quickstart — ~8 minutesWhat this PR does in one sentence: moves Files (14): mostly Read first:
Verification: # Repro pre-PR: SIGSEGV on multi-threaded queries
duckdb -unsigned -c "LOAD mobilityduck; SET threads=4; SELECT count(*) FROM (SELECT atTime(traj::VARCHAR, '2026-01-01'::TIMESTAMP) FROM ...);"
# Pre-PR: occasionally crashes. Post-PR: clean.Cross-link: Linux arm64 needs #161 for the Why it's safe to merge: pure additive guards; valid-input paths are unaffected. The change correctly diagnoses MEOS's process-singleton initialization model as a multi-worker bug. |
DuckDB runs scalar and cast bodies on TaskScheduler worker threads; MEOS keeps the session timezone, errno, PROJ context and RNGs in thread-local storage and needs meos_initialize() on each thread before first use. A thread-local guard in the scalar exec wrapper and a cast trampoline run the per-thread init (and re-install the error handler) at every entry point, so timestamp formatting on a worker no longer dereferences a NULL session_timezone.
6900085 to
7b85d06
Compare
202034a to
880d8d9
Compare
|
Folded into #134: the MEOS pin bump and the changes that keep CI green (graceful icu degradation + stage_icu, per-thread MEOS init, deterministic ln/exp/log10, macOS int64 forwarder, wasm pg_config) are jointly required for the extension to build, load, and pass — they ship as one foundation PR. |
The MEOS 1.4 uplift keeps the session timezone, errno, PROJ context and RNGs in thread-local storage and requires every thread that calls into MEOS to run meos_initialize() before its first call. The extension only initialized MEOS once on the load thread via std::call_once, so DuckDB TaskScheduler worker threads executed scalar, cast and aggregate bodies with a NULL session_timezone and segfaulted in pg_next_dst_boundary on the first timestamp parse (reproduced locally as a SIGSEGV in 042_tgeogpoint_parity, previously masked because amd64 CI never reached the tests and arm64 runs no test step). A thread-local guard now runs the per-thread init and re-installs the process-global MobilityDuck error handler (meos_initialize() resets it to the exit-on-error default), invoked from the scalar exec wrapper and through a cast registration trampoline that covers every cast entry point. Locally the full suite goes from unrunnable to 58/59 files and 1311/1312 assertions with no segfaults; the one remaining assertion is an unrelated pre-existing ln() output drift from the MEOS bump.