Crash when terminating process if Py_Finalize isn't called
- Lingua principale
- Python
- Stelle
- 187
- Fork
- 112
- Merge medio
- 13h 29m
- PR unite (30g)
- 17
Descrizione
### What happens?
If Python is embedded in another application and the process terminates without a chance for Py_Finalize to be called, there's a DuckDBPyConnection object that gets destroyed by the C runtime when the process ends, and that causes the process to crash.
In ~DuckDBPyConnection it's assumed the GIL is already held, but this isn't true when the process is being terminated (gil_scoped_release calls PyEval_SaveThread, and PyEval_SaveThread crashes because the GIL is not held). This object is cleaned up if Py_Finalize is called via a capsule, but that shouldn't be relied on.
A quick fix would be to add Py_IsInitialized and PyGILState_Check checks to ~DuckDBPyConnection, but a better fix would be to not do this cleanup in the destructor of a static and instead to move this to module state so Python can manage its lifecycle. I see there's a comment about PEP 489 already in the code suggesting the same.
### To Reproduce
```
# Importing duckdb is enough to cause this script to crash.
# Comment this line out and it will work fine.
import duckdb
import ctypes
# ctypes releases the GIL around this call (standard behavior for foreign DLL calls,
# unlike ctypes.pythonapi calls). ExitProcess never returns, so the GIL stays released
# from here on - (no Python code running, GIL not held).
# Py_Finalize is never called. ExitProcess still triggers DLL_PROCESS_DETACH for every
# loaded DLL, so _duckdb.pyd's static destructor runs with no GIL held and no valid
# thread state for this thread.
ctypes.windll.kernel32.ExitProcess(0)
```
### OS:
Windows
### DuckDB Package Version:
1.5.4
### Python Version:
3.14.5
### Full Name:
Tony Roberts
### Affiliation:
PyXLL Ltd
### What is the latest build you tested with? If possible, we recommend testing with the latest nightly build.
I have tested with a source build
### Did you include all relevant data sets for reproducing the issue?
Not applicable - the reproduction does not require a data set
### Did you include all code required to reproduce the issue?
- [x] Yes, I have
### Did you include all relevant configuration to reproduce the issue?
- [x] Yes, I have
Guida per i contributori
Apri la guida per i contributori
Direzione di ricerca
Inizia dal comportamento del distruttore di DuckDBPyConnection e di Py_Finalize e PyGILState_Check descritto nel report, quindi esamina il commento PEP 489 esistente. Usa la riproduzione fornita con Windows ctypes.ExitProcess; il lavoro è concluso quando l’importazione di duckdb e la terminazione senza Py_Finalize non causano più crash.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- c, python
- Ambito
- backend-api-design
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Attiva
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 45/100