3
3
//! Interaction with python's global interpreter lock
4
4
5
5
use crate :: { ffi, internal_tricks:: Unsendable , Python } ;
6
- use parking_lot:: { const_mutex, Mutex } ;
6
+ use parking_lot:: { const_mutex, Mutex , Once } ;
7
7
use std:: cell:: { Cell , RefCell } ;
8
- use std:: { mem:: ManuallyDrop , ptr:: NonNull , sync } ;
8
+ use std:: { mem:: ManuallyDrop , ptr:: NonNull } ;
9
9
10
- static START : sync :: Once = sync :: Once :: new ( ) ;
10
+ static START : Once = Once :: new ( ) ;
11
11
12
12
thread_local ! {
13
13
/// This is a internal counter in pyo3 monitoring whether this thread has the GIL.
@@ -45,16 +45,17 @@ pub(crate) fn gil_is_acquired() -> bool {
45
45
/// If both the Python interpreter and Python threading are already initialized,
46
46
/// this function has no effect.
47
47
///
48
+ /// # Features
49
+ ///
50
+ /// This function is only available with the `embedding` feature.
51
+ ///
48
52
/// # Panic
49
53
/// If the Python interpreter is initialized but Python threading is not,
50
54
/// a panic occurs.
51
55
/// It is not possible to safely access the Python runtime unless the main
52
56
/// thread (the thread which originally initialized Python) also initializes
53
57
/// threading.
54
- ///
55
- /// When writing an extension module, the `#[pymodule]` macro
56
- /// will ensure that Python threading is initialized.
57
- ///
58
+ #[ cfg( feature = "embedding" ) ]
58
59
pub fn prepare_freethreaded_python ( ) {
59
60
// Protect against race conditions when Python is not yet initialized
60
61
// and multiple threads concurrently call 'prepare_freethreaded_python()'.
@@ -66,15 +67,15 @@ pub fn prepare_freethreaded_python() {
66
67
// as we can't make the existing Python main thread acquire the GIL.
67
68
assert_ne ! ( ffi:: PyEval_ThreadsInitialized ( ) , 0 ) ;
68
69
} else {
69
- // Initialize Python.
70
- // We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
71
- // Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
72
- // Note that the 'main thread' notion in Python isn't documented properly;
73
- // and running Python without one is not officially supported.
74
-
75
- // PyPy does not support the embedding API
70
+ // TODO remove this cfg once build.rs rejects embedding feature misuse.
76
71
#[ cfg( not( PyPy ) ) ]
77
72
{
73
+ // Initialize Python.
74
+ // We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
75
+ // Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
76
+ // Note that the 'main thread' notion in Python isn't documented properly;
77
+ // and running Python without one is not officially supported.
78
+
78
79
ffi:: Py_InitializeEx ( 0 ) ;
79
80
80
81
// Make sure Py_Finalize will be called before exiting.
@@ -87,22 +88,21 @@ pub fn prepare_freethreaded_python() {
87
88
}
88
89
}
89
90
libc:: atexit ( finalize) ;
90
- }
91
91
92
- // > Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have
93
- // > to call it yourself anymore.
94
- #[ cfg( not( Py_3_7 ) ) ]
95
- if ffi:: PyEval_ThreadsInitialized ( ) == 0 {
96
- ffi:: PyEval_InitThreads ( ) ;
92
+ // > Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have
93
+ // > to call it yourself anymore.
94
+ #[ cfg( not( Py_3_7 ) ) ]
95
+ if ffi:: PyEval_ThreadsInitialized ( ) == 0 {
96
+ ffi:: PyEval_InitThreads ( ) ;
97
+ }
98
+
99
+ // Py_InitializeEx() will acquire the GIL, but we don't want to hold it at this point
100
+ // (it's not acquired in the other code paths)
101
+ // So immediately release the GIL:
102
+ let _thread_state = ffi:: PyEval_SaveThread ( ) ;
103
+ // Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
104
+ // and will be restored by PyGILState_Ensure.
97
105
}
98
- // PyEval_InitThreads() will acquire the GIL,
99
- // but we don't want to hold it at this point
100
- // (it's not acquired in the other code paths)
101
- // So immediately release the GIL:
102
- #[ cfg( not( PyPy ) ) ]
103
- let _thread_state = ffi:: PyEval_SaveThread ( ) ;
104
- // Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
105
- // and will be restored by PyGILState_Ensure.
106
106
}
107
107
} ) ;
108
108
}
@@ -137,8 +137,25 @@ impl GILGuard {
137
137
/// If PyO3 does not yet have a `GILPool` for tracking owned PyObject references, then this
138
138
/// new `GILGuard` will also contain a `GILPool`.
139
139
pub ( crate ) fn acquire ( ) -> GILGuard {
140
+ #[ cfg( feature = "auto-initialize" ) ]
140
141
prepare_freethreaded_python ( ) ;
141
142
143
+ #[ cfg( not( feature = "auto-initialize" ) ) ]
144
+ START . call_once_force ( |_| unsafe {
145
+ // Use call_once_force because if there is a panic because the interpreter is not
146
+ // initialized, it's fine for the user to initialize the interpreter and retry.
147
+ assert_ne ! (
148
+ ffi:: Py_IsInitialized ( ) ,
149
+ 0 ,
150
+ "The Python interpreter is not initalized and the `auto-initialize` feature is not enabled."
151
+ ) ;
152
+ assert_ne ! (
153
+ ffi:: PyEval_ThreadsInitialized ( ) ,
154
+ 0 ,
155
+ "Python threading is not initalized and the `auto-initialize` feature is not enabled."
156
+ ) ;
157
+ } ) ;
158
+
142
159
let gstate = unsafe { ffi:: PyGILState_Ensure ( ) } ; // acquire GIL
143
160
144
161
// If there's already a GILPool, we should not create another or this could lead to
0 commit comments