cpython/pythonrun.rs
1// Copyright (c) 2015 Daniel Grunwald
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19use std::{marker, rc, sync};
20
21use crate::ffi;
22use crate::python::Python;
23
24static START: sync::Once = sync::Once::new();
25
26/// Prepares the use of Python in a free-threaded context.
27///
28/// If the Python interpreter is not already initialized, this function
29/// will initialize it with disabled signal handling
30/// (Python will not raise the `KeyboardInterrupt` exception).
31/// Python signal handling depends on the notion of a 'main thread', which must be
32/// the thread that initializes the Python interpreter.
33///
34/// If both the Python interpreter and Python threading are already initialized,
35/// this function has no effect.
36///
37/// # Panic
38/// If the Python interpreter is initialized but Python threading is not,
39/// a panic occurs.
40/// It is not possible to safely access the Python runtime unless the main
41/// thread (the thread which originally initialized Python) also initializes
42/// threading.
43///
44/// When writing an extension module, the `py_module_initializer!` macro
45/// will ensure that Python threading is initialized.
46///
47pub fn prepare_freethreaded_python() {
48 // Protect against race conditions when Python is not yet initialized
49 // and multiple threads concurrently call 'prepare_freethreaded_python()'.
50 // Note that we do not protect against concurrent initialization of the Python runtime
51 // by other users of the Python C API.
52 START.call_once(|| unsafe {
53 if ffi::Py_IsInitialized() != 0 {
54 // If Python is already initialized, we expect Python threading to also be initialized,
55 // as we can't make the existing Python main thread acquire the GIL.
56 assert!(ffi::PyEval_ThreadsInitialized() != 0);
57 } else {
58 #[cfg(feature = "python27-sys")]
59 {
60 // If Python isn't initialized yet, we expect that Python threading isn't initialized either.
61 assert!(ffi::PyEval_ThreadsInitialized() == 0);
62 // Note: starting with Python 3.2 it's no longer possible to initialize threading
63 // without initializing Python; and in Python 3.7 PyEval_ThreadsInitialized() started
64 // misbehaving when Python was not initialized yet.
65 }
66 // Initialize Python.
67 // We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
68 // Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
69 // Note that the 'main thread' notion in Python isn't documented properly;
70 // and running Python without one is not officially supported.
71 ffi::Py_InitializeEx(0);
72 ffi::PyEval_InitThreads();
73 // PyEval_InitThreads() will acquire the GIL,
74 // but we don't want to hold it at this point
75 // (it's not acquired in the other code paths)
76 // So immediately release the GIL:
77 let _thread_state = ffi::PyEval_SaveThread();
78 // Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
79 // and will be restored by PyGILState_Ensure.
80 }
81 });
82}
83
84/// RAII type that represents the Global Interpreter Lock acquisition.
85///
86/// # Example
87/// ```
88/// use cpython::Python;
89///
90/// {
91/// let gil_guard = Python::acquire_gil();
92/// let py = gil_guard.python();
93/// } // GIL is released when gil_guard is dropped
94/// ```
95#[must_use]
96pub struct GILGuard {
97 gstate: ffi::PyGILState_STATE,
98 // hack to opt out of Send on stable rust, which doesn't
99 // have negative impls
100 no_send: marker::PhantomData<rc::Rc<()>>,
101}
102
103/// The Drop implementation for GILGuard will release the GIL.
104impl Drop for GILGuard {
105 fn drop(&mut self) {
106 unsafe { ffi::PyGILState_Release(self.gstate) }
107 }
108}
109
110impl GILGuard {
111 /// Acquires the global interpreter lock, which allows access to the Python runtime.
112 ///
113 /// If the Python runtime is not already initialized, this function will initialize it.
114 /// See [prepare_freethreaded_python()](fn.prepare_freethreaded_python.html) for details.
115 pub fn acquire() -> GILGuard {
116 if !cfg!(feature = "no-auto-initialize") {
117 crate::pythonrun::prepare_freethreaded_python();
118 }
119 let gstate = unsafe { ffi::PyGILState_Ensure() }; // acquire GIL
120 GILGuard {
121 gstate,
122 no_send: marker::PhantomData,
123 }
124 }
125
126 /// Retrieves the marker type that proves that the GIL was acquired.
127 #[inline]
128 pub fn python(&self) -> Python<'_> {
129 unsafe { Python::assume_gil_acquired() }
130 }
131
132 /// Checks if the current thread holds the GIL.
133 ///
134 /// This method is only available with Python 3.
135 #[cfg(feature = "python3-sys")]
136 pub fn check() -> bool {
137 let gstate = unsafe { ffi::PyGILState_Check() };
138 gstate == 1
139 }
140}
141
142/// Mutex-like wrapper object for data that is protected by the Python GIL.
143///
144/// # Example
145/// ```
146/// use std::cell::Cell;
147/// use cpython::{Python, GILProtected};
148///
149/// let data = GILProtected::new(Cell::new(0));
150///
151/// {
152/// let gil_guard = Python::acquire_gil();
153/// let cell = data.get(gil_guard.python());
154/// cell.set(cell.get() + 1);
155/// }
156/// ```
157pub struct GILProtected<T> {
158 data: T,
159}
160
161unsafe impl<T: Send> Send for GILProtected<T> {}
162
163/// Because `GILProtected` ensures that the contained data
164/// is only accessed while the GIL is acquired,
165/// it can implement `Sync` even if the contained data
166/// does not.
167unsafe impl<T: Send> Sync for GILProtected<T> {}
168
169impl<T> GILProtected<T> {
170 /// Creates a new instance of `GILProtected`.
171 #[inline]
172 pub const fn new(data: T) -> GILProtected<T> {
173 GILProtected { data }
174 }
175
176 /// Returns a shared reference to the data stored in the `GILProtected`.
177 ///
178 /// Requires a `Python` instance as proof that the GIL is acquired.
179 #[inline]
180 pub fn get<'a>(&'a self, _py: Python<'a>) -> &'a T {
181 &self.data
182 }
183
184 /// Consumes the `GILProtected`, returning the wrapped value.
185 #[inline]
186 pub fn into_inner(self) -> T {
187 self.data
188 }
189}