Everything that makes a parquet_table safe to use from more than one thread: the table's own
lock, the counters behind the append/read contract, and the shared refusal every structural
mutation routes through.
This file exists so the #ifdef _OPENMP plumbing lives in exactly one place. Every other
file in the table layer calls the plain procedures below and never sees a conditional or an
omp_lib import. The two exceptions are unsafe_first_touch and record_open_thread, which
stayed in parquet_tables_read.f90 next to the materialization path they guard; they implement
the same ownership test unsafe_shared_mutation below uses, and the three must agree.
The concurrency model, in one paragraph. Reading an already-resident column is free: no
lock, no bookkeeping, unlimited threads. It is not literally atomic-free -- every value
accessor makes the one atomic read of append_active that table_check_no_append below
performs -- but that single relaxed read is the whole cost, and it is the property everything
here exists to protect. Any change that puts a lock, or a second atomic, on the resident read
path is a design change rather than an optimisation. A first touch is refused on a shared
table (unsafe_first_touch), because it publishes an allocation with no ordering guarantee
behind it. A structural change is refused on a shared table (unsafe_shared_mutation).
%append is the one mutation that is allowed concurrently, and it is allowed because this
file serialises it -- so the caller never writes !$omp critical by hand and cannot wrap the
wrong statement.
What the guards can and cannot see. They key on OpenMP thread identity, so a caller
threading some other way (pthreads through C interop, coarrays) gets no enforcement at all, and
a read through a %col pointer the caller already holds is invisible to the library the way
every pointer dereference is. Both are documented limits, not gaps to close later; the
%generation() counter is what a caller checks a pointer against.