kaushal9696 opened a new issue, #50908:
URL: https://github.com/apache/arrow/issues/50908

   **Components:** Python, C++
   
   ## Environment
   
   - pyarrow 25.0.0 (PyPI wheel, bundled mimalloc), pandas + pyarrow 
dataset/parquet path
   - Python 3.14.4, CPython, Linux x86_64 (Ubuntu-family, kernel 7.0.0), 
uvicorn/FastAPI service
   - Not OOM (VmRSS 161 MB at crash, process peak 130 MB), not disk.
   
   ## Summary
   
   If the **first import of pyarrow and Arrow's first allocation happen on a 
short-lived worker thread that subsequently exits**, a later allocation on a 
*new* thread crashes with SIGSEGV inside the bundled mimalloc's per-thread heap 
initialisation. When the import + first allocation happen on the main thread 
(or any thread that stays alive), the same workload is stable under heavy 
concurrency.
   
   This bites any FastAPI/uvicorn-style service that lazily imports 
pandas/pyarrow inside a request handler: anyio's worker threads have a 
10-second idle lifetime (`WorkerThread.MAX_IDLE_TIME`), so the import lands on 
a thread that is pruned seconds later, and the next burst of traffic on fresh 
threads segfaults the process.
   
   ## Crash
   
   From a core dump (gdb, `thread apply all bt`; crashing thread was the newest 
of 23):
   
   ```
   #5  mi_thread_init ()                                        libarrow.so.2500
   #6  _mi_malloc_generic ()
   #8  arrow::BaseMemoryPoolImpl<MimallocAllocator>::Allocate(...)
   #12 arrow::AllocateEmptyBitmap(...)
   #14 parquet::SerializedFile::PreBuffer(...)
   #45 pyarrow._dataset.Scanner.to_table
   #49 _PyEval_EvalFrameDefault
   ```
   
   Disassembly at the fault: `mov (%rdx),%rax` then `=> mov 0x18(%rax),%rax` 
with **rax == 0** — a NULL dereference inside `mi_thread_init`.
   
   ## Isolation matrix (3 runs per configuration, deterministic)
   
   | pyarrow import | first Arrow allocation | outcome |
   |---|---|---|
   | throwaway thread (exits) | same throwaway thread | **SEGV 3/3** |
   | main thread | throwaway thread | survived 3/3 |
   | throwaway thread | main thread | survived 3/3 |
   | main thread | main thread | survived 3/3 |
   
   The trigger is **thread identity at load/first-allocation time, not 
concurrency**: with the import on the main thread, 8 concurrent fresh threads 
performed 6,000 parquet reads with no fault.
   
   ## Reproduction shape
   
   ```python
   import threading
   
   def load_and_read():
       import pyarrow.dataset as ds          # first pyarrow import in the 
process
       ds.dataset("some.parquet").to_table() # first Arrow allocation
   
   t = threading.Thread(target=load_and_read)
   t.start(); t.join()                       # thread exits — heap/TLS torn down
   
   # later: allocations from NEW short-lived threads (e.g. anyio worker threads
   # serving an ASGI app) crash in mi_thread_init.
   ```
   
   End-to-end reproduction (our service shape): uvicorn app whose handler 
lazily imports pandas and reads parquet; hammer with 21 concurrent GETs per 
burst, 12 s idle between bursts (idle > anyio's 10 s worker lifetime so each 
burst gets fresh threads). Pre-workaround the process died with signal 11 on 
the third burst, reproducibly. Post-workaround: 10 bursts, 210 requests, all 
200.
   
   ## Hypothesis
   
   mimalloc's process-wide initialisation appears to bind state to the thread 
that performed it; when that thread exits, its TLS/heap teardown leaves a 
structure NULL that `mi_thread_init` later dereferences on a new thread. (We 
can share the full backtrace and the isolation script on request.)
   
   ## Workarounds (verified)
   
   - Import pyarrow and perform one real allocation on the **main thread** 
before any worker-thread use (we ship this as a startup warm-up).
   - `ARROW_DEFAULT_MEMORY_POOL=system` (set before import) also sidesteps it, 
at the cost of changing the allocator globally.
   
   ---
   
   *Reporter context: hit in production behind FastAPI/uvicorn; root-caused 
from an apport core dump; happy to provide the isolation script, full `thread 
apply all bt`, and library build info.*
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to