You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@arrow.apache.org by "yo-han (via GitHub)" <gi...@apache.org> on 2026/10/18 20:52:02 UTC
[I] [Python] Process aborts at interpreter exit: `~PyReadableFile` takes the GIL on a ThreadPool worker during finalization (`pd.read_parquet` on a local path) [arrow]
yo-han opened a new issue, #54536:
URL: https://github.com/apache/arrow/issues/54536
### Describe the bug, including details regarding any error messages, version, and platform.
A short-lived process that reads a local parquet file through pandas and then exits sometimes aborts with SIGABRT (exit 134), after all of its work and output are done. stderr shows `terminate called without an active exception` and `Fatal Python error: Aborted`, with no Python frame.
pandas opens a Python file handle for a local path and passes it to the dataset reader, which wraps it in a `PyReadableFile`. A reader task can drop the last reference on a ThreadPool worker thread while the interpreter is finalizing. `OwnedRefNoGIL::~OwnedRefNoGIL` then calls `PyGILState_Ensure`, CPython ends the thread with `pthread_exit`, the forced unwind passes a noexcept destructor, and `std::terminate` is called.
**Reproduction**
```python
# write.py: run once
import pandas as pd
pd.DataFrame({"a": [1, 2], "b": [1.0, 2.0]}).to_parquet("p.parquet", index=False)
```
```python
# read.py: run many times, a few in parallel
import pandas as pd, sys
pd.read_parquet(sys.argv[1], columns=["a", "b"])
print("ok")
```
```
python write.py
seq 3000 | xargs -P 4 -I{} sh -c 'python read.py p.parquet >/dev/null 2>&1 || echo $?' | sort | uniq -c
```
Expected: no output (every run exits 0). Observed: `38 134`, that is 38 of 3000 runs abort.
The same loop with `pyarrow.parquet.read_table(sys.argv[1], columns=["a", "b"]).to_pandas()` in place of `pd.read_parquet` gives no output, so 0 of 3000 runs abort. The file is then opened natively, and no Python object is held by the reader.
**Environment**
- pyarrow 26.0.0 (pyarrow 25.0.1 aborts at the same rate)
- pandas 3.0.6, numpy 2.5.3
- CPython 3.12.12
- Debian 12 (bookworm), aarch64, in Docker
**Stack of the aborting thread (gdb)**
```
#0 __pthread_kill_implementation
#3 abort
#4 __gnu_cxx::__verbose_terminate_handler()
#6 std::terminate()
#7 __gxx_personality_v0
#9 _Unwind_ForcedUnwind
#10 __pthread_unwind
#12 __pthread_exit
#13 PyThread_exit_thread
#14 take_gil
#15 PyEval_RestoreThread
#16 PyGILState_Ensure
#17 arrow::py::OwnedRefNoGIL::~OwnedRefNoGIL()
#18 arrow::py::PyReadableFile::~PyReadableFile()
#19 std::_Sp_counted_base<...>::_M_release_last_use_cold()
#20 std::_Function_handler<arrow::Result<std::shared_ptr<arrow::io::RandomAccessFile>>(), arrow::dataset::FileSource::FileSource(...)>
#21 arrow::dataset::FileSource::~FileSource()
#22 arrow::dataset::ParquetFileFragment::~ParquetFileFragment()
... on a ThreadPool worker thread (ThreadPool::LaunchWorkersUnlocked)
```
**Possible fix.** Skip the decref in `OwnedRefNoGIL`'s destructor when the interpreter is finalizing (`Py_IsFinalizing()`), as other C++ extensions do. Alternatively, release the scan's fragments on the calling thread before `read_table` returns.
**Related.** ARROW-16597 has the same mechanism in Flight: `PyThread_exit_thread` → `pthread_exit` while C++ code is on the stack, at interpreter exit.
### Component(s)
Python
--
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: issues-unsubscribe@arrow.apache.org.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [I] [Python] Process aborts at interpreter exit: `~PyReadableFile` takes the GIL on a ThreadPool worker during finalization (`pd.read_parquet` on a local path) [arrow]
Posted by "wpyaoxueros (via GitHub)" <gi...@apache.org>.
wpyaoxueros commented on issue #54536:
URL: https://github.com/apache/arrow/issues/54536#issuecomment-6287709330
Additional reproduction on the current released wheels, run with Codex assistance on the designated remote workstation (Linux x86_64, CPython 3.12.15, PyArrow 26.0.0, pandas 3.0.6):
- 2 of 300 fresh subprocesses using `pandas.read_parquet` aborted with signal 6 and `terminate called without an active exception`.
- 0 of 300 control subprocesses using `pyarrow.parquet.read_table(...).to_pandas()` aborted.
This is on x86_64, not the reporter's aarch64 platform, but it reproduces the same exit-time failure on the Python-file-handle path.
I inspected current `main` (`ee382216db4f8d764ed17c16c5baef45afe3f757`): `PyReadableFile` owns the Python file through `OwnedRefNoGIL`; that destructor checks `Py_IsInitialized()` and then acquires the GIL before decref. The previously merged #49943 protects `PyBuffer` and `NumPyBuffer`, but not this `OwnedRefNoGIL` path.
I would like to investigate a focused fix and regression test if a committer can assign this issue. Before coding, guidance would help on the supported finalization check across Arrow's Python versions: the remote CPython 3.12.15 headers declare `_Py_IsFinalizing`, while the issue proposes `Py_IsFinalizing`.
AI disclosure: Codex assisted with setting up and running the reproduction and inspecting current source. No code changes have been made.
--
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: github-unsubscribe@arrow.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org