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,20 @@ 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
+ /// # Availability
49
+ ///
50
+ /// This function is only available when linking against Python distributions that contain a
51
+ /// shared library.
52
+ ///
53
+ /// This function is not available on PyPy.
54
+ ///
48
55
/// # Panic
49
56
/// If the Python interpreter is initialized but Python threading is not,
50
57
/// a panic occurs.
51
58
/// It is not possible to safely access the Python runtime unless the main
52
59
/// thread (the thread which originally initialized Python) also initializes
53
60
/// threading.
54
- ///
55
- /// When writing an extension module, the `#[pymodule]` macro
56
- /// will ensure that Python threading is initialized.
57
- ///
61
+ #[ cfg( all( Py_SHARED , not( PyPy ) ) ) ]
58
62
pub fn prepare_freethreaded_python ( ) {
59
63
// Protect against race conditions when Python is not yet initialized
60
64
// and multiple threads concurrently call 'prepare_freethreaded_python()'.
@@ -72,34 +76,29 @@ pub fn prepare_freethreaded_python() {
72
76
// Note that the 'main thread' notion in Python isn't documented properly;
73
77
// and running Python without one is not officially supported.
74
78
75
- // PyPy does not support the embedding API
76
- #[ cfg( not( PyPy ) ) ]
77
- {
78
- ffi:: Py_InitializeEx ( 0 ) ;
79
-
80
- // Make sure Py_Finalize will be called before exiting.
81
- extern "C" fn finalize ( ) {
82
- unsafe {
83
- if ffi:: Py_IsInitialized ( ) != 0 {
84
- ffi:: PyGILState_Ensure ( ) ;
85
- ffi:: Py_Finalize ( ) ;
86
- }
79
+ ffi:: Py_InitializeEx ( 0 ) ;
80
+
81
+ // Make sure Py_Finalize will be called before exiting.
82
+ extern "C" fn finalize ( ) {
83
+ unsafe {
84
+ if ffi:: Py_IsInitialized ( ) != 0 {
85
+ ffi:: PyGILState_Ensure ( ) ;
86
+ ffi:: Py_Finalize ( ) ;
87
87
}
88
88
}
89
- libc:: atexit ( finalize) ;
90
89
}
90
+ libc:: atexit ( finalize) ;
91
91
92
92
// > Changed in version 3.7: This function is now called by Py_Initialize(), so you don’t have
93
93
// > to call it yourself anymore.
94
94
#[ cfg( not( Py_3_7 ) ) ]
95
95
if ffi:: PyEval_ThreadsInitialized ( ) == 0 {
96
96
ffi:: PyEval_InitThreads ( ) ;
97
97
}
98
- // PyEval_InitThreads() will acquire the GIL,
99
- // but we don't want to hold it at this point
98
+
99
+ // Py_InitializeEx() will acquire the GIL, but we don't want to hold it at this point
100
100
// (it's not acquired in the other code paths)
101
101
// So immediately release the GIL:
102
- #[ cfg( not( PyPy ) ) ]
103
102
let _thread_state = ffi:: PyEval_SaveThread ( ) ;
104
103
// Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
105
104
// and will be restored by PyGILState_Ensure.
@@ -137,7 +136,51 @@ impl GILGuard {
137
136
/// If PyO3 does not yet have a `GILPool` for tracking owned PyObject references, then this
138
137
/// new `GILGuard` will also contain a `GILPool`.
139
138
pub ( crate ) fn acquire ( ) -> GILGuard {
140
- prepare_freethreaded_python ( ) ;
139
+ // Maybe auto-initialize the GIL:
140
+ // - If auto-initialize feature set and supported, try to initalize the interpreter.
141
+ // - If the auto-initialize feature is set but unsupported, emit hard errors only when
142
+ // the extension-module feature is not activated - extension modules don't care about
143
+ // auto-initialize so this avoids breaking existing builds.
144
+ // - Otherwise, just check the GIL is initialized.
145
+ cfg_if:: cfg_if! {
146
+ if #[ cfg( all( feature = "auto-initialize" , Py_SHARED , not( PyPy ) ) ) ] {
147
+ prepare_freethreaded_python( ) ;
148
+ } else if #[ cfg( all( feature = "auto-initialize" , not( feature = "extension-module" ) , not( Py_SHARED ) , not( __pyo3_ci) ) ) ] {
149
+ compile_error!( concat!(
150
+ "The `auto-initialize` feature is not supported when linking Python " ,
151
+ "statically instead of with a shared library.\n \n " ,
152
+ "Please disable the `auto-initialize` feature, for example by entering the following " ,
153
+ "in your cargo.toml:\n \n " ,
154
+ " pyo3 = { version = \" 0.13.0\" , default-features = false }\n \n " ,
155
+ "Alternatively, compile PyO3 using a Python distribution which contains a shared " ,
156
+ "libary."
157
+ ) ) ;
158
+ } else if #[ cfg( all( feature = "auto-initialize" , not( feature = "extension-module" ) , PyPy , not( __pyo3_ci) ) ) ] {
159
+ compile_error!( concat!(
160
+ "The `auto-initialize` feature is not supported by PyPy.\n \n " ,
161
+ "Please disable the `auto-initialize` feature, for example by entering the following " ,
162
+ "in your cargo.toml:\n \n " ,
163
+ " pyo3 = { version = \" 0.13.0\" , default-features = false }" ,
164
+ ) ) ;
165
+ } else {
166
+ // extension module feature enabled and PyPy or static linking
167
+ // OR auto-initialize feature not enabled
168
+ START . call_once_force( |_| unsafe {
169
+ // Use call_once_force because if there is a panic because the interpreter is not
170
+ // initialized, it's fine for the user to initialize the interpreter and retry.
171
+ assert_ne!(
172
+ ffi:: Py_IsInitialized ( ) ,
173
+ 0 ,
174
+ "The Python interpreter is not initalized and the `auto-initialize` feature is not enabled."
175
+ ) ;
176
+ assert_ne!(
177
+ ffi:: PyEval_ThreadsInitialized ( ) ,
178
+ 0 ,
179
+ "Python threading is not initalized and the `auto-initialize` feature is not enabled."
180
+ ) ;
181
+ } ) ;
182
+ }
183
+ }
141
184
142
185
let gstate = unsafe { ffi:: PyGILState_Ensure ( ) } ; // acquire GIL
143
186
0 commit comments