Skip to content

Split MEOS initialization in MeosWiringRuntime into process-once and per-thread - #46

Merged
estebanzimanyi merged 1 commit into
MobilityDB:mainfrom
estebanzimanyi:fix/meoswiringruntime-layered-init
Jul 23, 2026
Merged

Split MEOS initialization in MeosWiringRuntime into process-once and per-thread#46
estebanzimanyi merged 1 commit into
MobilityDB:mainfrom
estebanzimanyi:fix/meoswiringruntime-layered-init

Conversation

@estebanzimanyi

Copy link
Copy Markdown
Member

MeosWiringRuntime initializes MEOS on each task thread by installing a no-exit error handler and then calling the full meos_initialize(). Two problems follow:

  1. The handler is defeated by ordering. meos_initialize() re-installs MEOS's exiting default handler, so calling it after the no-exit handler discards the no-exit handler — every thread ends with the exiting default, and any MEOS error ends the JVM with exit(EXIT_FAILURE).
  2. The per-thread reset race. The error handler is process-global, so running full meos_initialize() per thread means one thread's initialization replaces the handler every other thread relies on.

MEOS setup has two lifetimes:

  • process-global — the allocator and the error handler;
  • thread-local — the timezone and collation caches; the PROJ, GEOS and GSL contexts are thread-local too and are created lazily by MEOS on first use.

This change installs the process-global part once per JVM through a holder whose class initializer runs under the JVM class-initialization lock — meos_initialize() first, then the no-exit handler, so the no-exit handler stands — and runs only meos_initialize_timezone + meos_initialize_collation per thread. PROJ/GEOS/GSL still initialize lazily per thread, so the wiring operators are unaffected. MEOSBridge delegates here, so it is covered too.

This mirrors the same fix in MobilitySpark (MeosThread) and MobilityKafka (MEOSBridge).

…per-thread

MeosWiringRuntime initialized MEOS on each task thread by installing a
no-exit error handler and then calling the full meos_initialize(). That
order defeats the handler: meos_initialize() re-installs MEOS's exiting
default handler, so the no-exit handler never takes effect and any MEOS
error ends the JVM with exit(EXIT_FAILURE). Running meos_initialize() per
thread is unsafe regardless, because the handler is process-global — one
thread's initialization replaces the handler every other thread relies on.

MEOS setup has two lifetimes: the allocator and error handler are
process-global, while the timezone and collation caches are thread-local
(the PROJ, GEOS and GSL contexts are thread-local too and created lazily on
first use). Install the process-global part once per JVM through a holder
whose class initializer runs under the JVM class-initialization lock —
meos_initialize() first, then the no-exit handler — and run only
meos_initialize_timezone and meos_initialize_collation per thread. The
no-exit handler now stands, and no thread re-installs it.
@estebanzimanyi
estebanzimanyi merged commit ddfb6ca into MobilityDB:main Jul 23, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant