pyo3/src/pythonrun.rs

422 lines
13 KiB
Rust
Raw Normal View History

2017-06-20 21:10:12 +00:00
// Copyright (c) 2017-present PyO3 Project and Contributors
2015-04-19 03:22:03 +00:00
//
2017-06-20 21:10:12 +00:00
// based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython
2015-04-19 03:22:03 +00:00
2017-06-21 06:26:28 +00:00
use std::{sync, rc, marker, mem};
2017-07-09 06:08:57 +00:00
use spin;
use ffi;
use python::Python;
2017-06-24 15:28:31 +00:00
use objects::PyObjectRef;
2015-01-05 16:05:53 +00:00
static START: sync::Once = sync::ONCE_INIT;
static START_PYO3: sync::Once = sync::ONCE_INIT;
2015-01-05 16:05:53 +00:00
2015-06-27 20:45:35 +00:00
/// Prepares the use of Python in a free-threaded context.
2015-04-18 20:20:19 +00:00
///
2015-06-27 20:45:35 +00:00
/// If the Python interpreter is not already initialized, this function
2015-04-18 20:20:19 +00:00
/// will initialize it with disabled signal handling
2015-06-27 20:45:35 +00:00
/// (Python will not raise the `KeyboardInterrupt` exception).
2015-04-18 20:20:19 +00:00
/// Python signal handling depends on the notion of a 'main thread', which must be
2015-06-27 20:45:35 +00:00
/// the thread that initializes the Python interpreter.
///
/// If both the Python interpreter and Python threading are already initialized,
/// this function has no effect.
///
/// # Panic
/// If the Python interpreter is initialized but Python threading is not,
/// a panic occurs.
/// It is not possible to safely access the Python runtime unless the main
/// thread (the thread which originally initialized Python) also initializes
/// threading.
///
/// When writing an extension module, the `py_module_initializer!` macro
/// will ensure that Python threading is initialized.
///
2015-01-04 04:50:28 +00:00
pub fn prepare_freethreaded_python() {
2015-06-27 20:45:35 +00:00
// Protect against race conditions when Python is not yet initialized
2015-01-04 19:11:18 +00:00
// and multiple threads concurrently call 'prepare_freethreaded_python()'.
2015-06-27 20:45:35 +00:00
// Note that we do not protect against concurrent initialization of the Python runtime
// by other users of the Python C API.
2015-01-04 04:50:28 +00:00
START.call_once(|| unsafe {
2015-01-04 19:11:18 +00:00
if ffi::Py_IsInitialized() != 0 {
2015-06-27 20:45:35 +00:00
// If Python is already initialized, we expect Python threading to also be initialized,
// as we can't make the existing Python main thread acquire the GIL.
2015-01-04 19:11:18 +00:00
assert!(ffi::PyEval_ThreadsInitialized() != 0);
} else {
2015-06-27 20:45:35 +00:00
// If Python isn't initialized yet, we expect that Python threading isn't initialized either.
2015-01-04 19:11:18 +00:00
assert!(ffi::PyEval_ThreadsInitialized() == 0);
2015-06-27 20:45:35 +00:00
// Initialize Python.
2015-01-04 19:11:18 +00:00
// We use Py_InitializeEx() with initsigs=0 to disable Python signal handling.
// Signal handling depends on the notion of a 'main thread', which doesn't exist in this case.
2015-06-27 20:45:35 +00:00
// Note that the 'main thread' notion in Python isn't documented properly;
// and running Python without one is not officially supported.
2015-01-04 19:11:18 +00:00
ffi::Py_InitializeEx(0);
ffi::PyEval_InitThreads();
// PyEval_InitThreads() will acquire the GIL,
// but we don't want to hold it at this point
2015-01-04 04:50:28 +00:00
// (it's not acquired in the other code paths)
2015-01-04 19:11:18 +00:00
// So immediately release the GIL:
let _thread_state = ffi::PyEval_SaveThread();
2015-06-27 20:45:35 +00:00
// Note that the PyThreadState returned by PyEval_SaveThread is also held in TLS by the Python runtime,
2015-01-04 19:11:18 +00:00
// and will be restored by PyGILState_Ensure.
2015-01-04 04:50:28 +00:00
}
2017-06-21 06:26:28 +00:00
prepare_pyo3_library();
});
}
#[doc(hidden)]
pub fn prepare_pyo3_library() {
START_PYO3.call_once(|| unsafe {
2017-06-21 06:26:28 +00:00
// initialize release pool
2017-07-09 06:08:57 +00:00
POINTERS = Box::into_raw(Box::new(Pointers::new()));
2015-01-04 04:50:28 +00:00
});
2015-01-05 16:05:53 +00:00
}
2016-03-05 16:41:04 +00:00
/// RAII type that represents the Global Interpreter Lock acquisition.
2015-06-27 20:45:35 +00:00
///
/// # Example
/// ```
2017-05-13 05:43:17 +00:00
/// use pyo3::Python;
2015-06-27 20:45:35 +00:00
///
/// {
/// let gil_guard = Python::acquire_gil();
/// let py = gil_guard.python();
/// } // GIL is released when gil_guard is dropped
/// ```
#[must_use]
pub struct GILGuard {
2017-06-23 20:42:46 +00:00
owned: usize,
borrowed: usize,
gstate: ffi::PyGILState_STATE,
// hack to opt out of Send on stable rust, which doesn't
// have negative impls
no_send: marker::PhantomData<rc::Rc<()>>
}
2017-06-11 15:46:23 +00:00
/// The Drop implementation for `GILGuard` will release the GIL.
impl Drop for GILGuard {
fn drop(&mut self) {
2017-06-21 06:26:28 +00:00
unsafe {
2017-07-09 06:08:57 +00:00
let pool: &'static mut Pointers = mem::transmute(POINTERS);
pool.drain(self.owned, self.borrowed, true);
2017-06-21 06:26:28 +00:00
ffi::PyGILState_Release(self.gstate);
}
}
}
2017-07-09 06:08:57 +00:00
struct Pointers {
owned: Vec<*mut ffi::PyObject>,
borrowed: Vec<*mut ffi::PyObject>,
pointers: *mut Vec<*mut ffi::PyObject>,
p: spin::Mutex<*mut Vec<*mut ffi::PyObject>>,
}
impl Pointers {
fn new() -> Pointers {
Pointers {
owned: Vec::with_capacity(250),
borrowed: Vec::with_capacity(250),
pointers: Box::into_raw(Box::new(Vec::with_capacity(250))),
2017-07-09 18:37:20 +00:00
p: spin::Mutex::new(Box::into_raw(Box::new(Vec::with_capacity(250)))),
2017-07-09 06:08:57 +00:00
}
}
unsafe fn release_pointers(&mut self) {
let mut v = self.p.lock();
// vec of pointers
2017-07-09 06:08:57 +00:00
let ptr = *v;
let vec: &'static mut Vec<*mut ffi::PyObject> = mem::transmute(ptr);
if vec.is_empty() {
return
}
// switch vectors
2017-07-09 06:08:57 +00:00
*v = self.pointers;
self.pointers = ptr;
drop(v);
// release py objects
2017-07-09 06:08:57 +00:00
for ptr in vec.iter_mut() {
ffi::Py_DECREF(*ptr);
}
vec.set_len(0);
}
pub unsafe fn drain(&mut self, owned: usize, borrowed: usize, pointers: bool) {
2017-07-09 06:08:57 +00:00
let len = self.owned.len();
if owned < len {
for ptr in &mut self.owned[owned..len] {
ffi::Py_DECREF(*ptr);
}
self.owned.set_len(owned);
}
let len = self.borrowed.len();
if borrowed < len {
self.borrowed.set_len(borrowed);
}
if pointers {
self.release_pointers();
}
2017-07-09 06:08:57 +00:00
}
}
static mut POINTERS: *mut Pointers = 0 as *mut _;
2017-06-21 06:26:28 +00:00
pub struct Pool {
2017-06-23 20:42:46 +00:00
owned: usize,
borrowed: usize,
pointers: bool,
no_send: marker::PhantomData<rc::Rc<()>>,
}
impl Pool {
#[inline]
pub unsafe fn new() -> Pool {
2017-07-09 06:08:57 +00:00
let p: &'static mut Pointers = mem::transmute(POINTERS);
2017-07-09 18:37:20 +00:00
Pool {owned: p.owned.len(),
2017-07-09 06:08:57 +00:00
borrowed: p.borrowed.len(),
pointers: true,
2017-07-09 18:37:20 +00:00
no_send: marker::PhantomData}
}
#[inline]
pub unsafe fn new_no_pointers() -> Pool {
2017-07-09 18:37:20 +00:00
let p: &'static mut Pointers = mem::transmute(POINTERS);
Pool {owned: p.owned.len(),
borrowed: p.borrowed.len(),
pointers: false,
no_send: marker::PhantomData}
}
}
impl Drop for Pool {
fn drop(&mut self) {
unsafe {
2017-07-09 06:08:57 +00:00
let pool: &'static mut Pointers = mem::transmute(POINTERS);
pool.drain(self.owned, self.borrowed, self.pointers);
}
}
}
2017-07-09 06:08:57 +00:00
pub unsafe fn register_pointer(obj: *mut ffi::PyObject)
2017-06-30 02:51:45 +00:00
{
2017-07-09 06:08:57 +00:00
let pool: &'static mut Pointers = mem::transmute(POINTERS);
let v = pool.p.lock();
let pool: &'static mut Vec<*mut ffi::PyObject> = mem::transmute(*v);
pool.push(obj);
}
2017-07-09 06:08:57 +00:00
pub unsafe fn register_owned<'p>(_py: Python<'p>, obj: *mut ffi::PyObject) -> &'p PyObjectRef
2017-06-30 02:51:45 +00:00
{
2017-07-09 06:08:57 +00:00
let pool: &'static mut Pointers = mem::transmute(POINTERS);
pool.owned.push(obj);
mem::transmute(&pool.owned[pool.owned.len()-1])
2017-06-23 20:42:46 +00:00
}
2017-07-09 06:08:57 +00:00
pub unsafe fn register_borrowed<'p>(_py: Python<'p>, obj: *mut ffi::PyObject) -> &'p PyObjectRef
{
let pool: &'static mut Pointers = mem::transmute(POINTERS);
pool.borrowed.push(obj);
mem::transmute(&pool.borrowed[pool.borrowed.len()-1])
}
impl GILGuard {
/// Acquires the global interpreter lock, which allows access to the Python runtime.
2015-06-27 20:45:35 +00:00
///
/// If the Python runtime is not already initialized, this function will initialize it.
/// See [prepare_freethreaded_python()](fn.prepare_freethreaded_python.html) for details.
pub fn acquire() -> GILGuard {
2017-06-23 20:42:46 +00:00
prepare_freethreaded_python();
2017-06-21 06:26:28 +00:00
unsafe {
let gstate = ffi::PyGILState_Ensure(); // acquire GIL
2017-07-09 06:08:57 +00:00
let pool: &'static mut Pointers = mem::transmute(POINTERS);
GILGuard { owned: pool.owned.len(),
borrowed: pool.borrowed.len(),
2017-06-23 20:42:46 +00:00
gstate: gstate,
no_send: marker::PhantomData }
2017-06-21 06:26:28 +00:00
}
}
2015-04-18 20:20:19 +00:00
/// Retrieves the marker type that proves that the GIL was acquired.
2015-06-21 22:35:01 +00:00
#[inline]
pub fn python<'p>(&'p self) -> Python<'p> {
unsafe { Python::assume_gil_acquired() }
}
}
#[cfg(test)]
mod test {
use std;
use {ffi, pythonrun};
use python::Python;
2017-07-13 20:05:50 +00:00
use object::PyObject;
use super::{Pool, Pointers, POINTERS};
#[test]
fn test_owned() {
pythonrun::prepare_pyo3_library();
unsafe {
let p: &'static mut Pointers = std::mem::transmute(POINTERS);
let cnt;
let empty;
{
let gil = Python::acquire_gil();
let py = gil.python();
empty = ffi::PyTuple_New(0);
cnt = ffi::Py_REFCNT(empty) - 1;
let _ = pythonrun::register_owned(py, empty);
assert_eq!(p.owned.len(), 1);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.owned.len(), 0);
assert_eq!(cnt, ffi::Py_REFCNT(empty));
}
}
}
#[test]
fn test_owned_nested() {
pythonrun::prepare_pyo3_library();
unsafe {
let p: &'static mut Pointers = std::mem::transmute(POINTERS);
let cnt;
let empty;
{
let gil = Python::acquire_gil();
let py = gil.python();
assert_eq!(p.owned.len(), 0);
// empty tuple is singleton
empty = ffi::PyTuple_New(0);
cnt = ffi::Py_REFCNT(empty) - 1;
let _ = pythonrun::register_owned(py, empty);
assert_eq!(p.owned.len(), 1);
{
let _pool = Pool::new();
let empty = ffi::PyTuple_New(0);
let _ = pythonrun::register_owned(py, empty);
assert_eq!(p.owned.len(), 2);
}
assert_eq!(p.owned.len(), 1);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.owned.len(), 0);
assert_eq!(cnt, ffi::Py_REFCNT(empty));
}
}
}
#[test]
fn test_borrowed() {
pythonrun::prepare_pyo3_library();
unsafe {
let p: &'static mut Pointers = std::mem::transmute(POINTERS);
let cnt;
{
let gil = Python::acquire_gil();
let py = gil.python();
assert_eq!(p.borrowed.len(), 0);
cnt = ffi::Py_REFCNT(ffi::Py_True());
pythonrun::register_borrowed(py, ffi::Py_True());
assert_eq!(p.borrowed.len(), 1);
assert_eq!(ffi::Py_REFCNT(ffi::Py_True()), cnt);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.borrowed.len(), 0);
assert_eq!(ffi::Py_REFCNT(ffi::Py_True()), cnt);
}
}
}
#[test]
fn test_borrowed_nested() {
pythonrun::prepare_pyo3_library();
unsafe {
let p: &'static mut Pointers = std::mem::transmute(POINTERS);
let cnt;
{
let gil = Python::acquire_gil();
let py = gil.python();
assert_eq!(p.borrowed.len(), 0);
cnt = ffi::Py_REFCNT(ffi::Py_True());
pythonrun::register_borrowed(py, ffi::Py_True());
assert_eq!(p.borrowed.len(), 1);
assert_eq!(ffi::Py_REFCNT(ffi::Py_True()), cnt);
{
let _pool = Pool::new();
assert_eq!(p.borrowed.len(), 1);
pythonrun::register_borrowed(py, ffi::Py_True());
assert_eq!(p.borrowed.len(), 2);
}
assert_eq!(p.borrowed.len(), 1);
assert_eq!(ffi::Py_REFCNT(ffi::Py_True()), cnt);
}
{
let _gil = Python::acquire_gil();
assert_eq!(p.borrowed.len(), 0);
assert_eq!(ffi::Py_REFCNT(ffi::Py_True()), cnt);
}
}
}
#[test]
fn test_pyobject_drop() {
pythonrun::prepare_pyo3_library();
unsafe {
let p: &'static mut Pointers = std::mem::transmute(POINTERS);
let ob;
let cnt;
let empty;
{
let gil = Python::acquire_gil();
let py = gil.python();
assert_eq!(p.owned.len(), 0);
// empty tuple is singleton
empty = ffi::PyTuple_New(0);
cnt = ffi::Py_REFCNT(empty);
ob = PyObject::from_owned_ptr(py, empty);
}
drop(ob);
assert_eq!(cnt, ffi::Py_REFCNT(empty));
{
let _gil = Python::acquire_gil();
}
assert_eq!(cnt - 1, ffi::Py_REFCNT(empty));
}
}
}