-
Notifications
You must be signed in to change notification settings - Fork 745
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add PyMutex wrappers #4523
base: main
Are you sure you want to change the base?
Add PyMutex wrappers #4523
Changes from 5 commits
4865289
874d608
b1afb8b
b3d193b
1229634
307e8cf
f0ead33
4a0bf3b
17263cc
37ffa3a
10f5faf
7df69a0
e35ea76
604b9bb
22ee351
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* Added a Rust wrapper for `PyMutex`, available on Python 3.13 and newer. |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,98 @@ | ||||||
use std::cell::UnsafeCell; | ||||||
use std::ops::{Deref, DerefMut}; | ||||||
|
||||||
/// Wrapper for [`PyMutex`](https://docs.python.org/3/c-api/init.html#c.PyMutex), exposing an RAII guard interface similar to `std::sync::Mutex`. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should go into depth about how it's different from std's Mutex as well. |
||||||
#[derive(Debug)] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a derived There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Std's Mutex' Debug impl uses |
||||||
pub struct PyMutex<T> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
We should be able to support unsized types, I think. |
||||||
_mutex: UnsafeCell<crate::ffi::PyMutex>, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
To avoid a bunch of |
||||||
data: UnsafeCell<T>, | ||||||
} | ||||||
|
||||||
/// RAII guard to handle releasing a PyMutex lock. | ||||||
#[derive(Debug)] | ||||||
pub struct PyMutexGuard<'a, T> { | ||||||
mutex: &'a mut PyMutex<T>, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
|
||||||
impl<T> PyMutex<T> { | ||||||
/// Acquire the mutex, blocking the current thread until it is able to do so. | ||||||
pub fn lock(&mut self) -> PyMutexGuard<'_, T> { | ||||||
unsafe { crate::ffi::PyMutex_Lock(self._mutex.get_mut()) }; | ||||||
PyMutexGuard { mutex: &mut *self } | ||||||
} | ||||||
|
||||||
/// Create a new mutex in an unlocked state ready for use. | ||||||
pub fn new(value: T) -> Self { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be
Suggested change
|
||||||
Self { | ||||||
_mutex: UnsafeCell::new(crate::ffi::PyMutex::new()), | ||||||
data: UnsafeCell::new(value), | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
impl<'a, T> Drop for PyMutexGuard<'a, T> { | ||||||
fn drop(&mut self) { | ||||||
unsafe { crate::ffi::PyMutex_Unlock(self.mutex._mutex.get_mut()) }; | ||||||
} | ||||||
} | ||||||
|
||||||
impl<'a, T> Deref for PyMutexGuard<'a, T> { | ||||||
ngoldbaum marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
type Target = T; | ||||||
|
||||||
fn deref(&self) -> &T { | ||||||
// safety: cannot be null pointer because PyMutexGuard::new always | ||||||
// creates a valid PyMutex pointer | ||||||
unsafe { &*self.mutex.data.get() } | ||||||
} | ||||||
} | ||||||
|
||||||
impl<'a, T> DerefMut for PyMutexGuard<'a, T> { | ||||||
fn deref_mut(&mut self) -> &mut T { | ||||||
// safety: cannot be null pointer because PyMutexGuard::new always | ||||||
// creates a valid PyMutex pointer | ||||||
self.mutex.data.get_mut() | ||||||
} | ||||||
} | ||||||
|
||||||
#[cfg(test)] | ||||||
mod tests { | ||||||
use super::*; | ||||||
use crate::sync::GILOnceCell; | ||||||
use crate::types::{PyAnyMethods, PyDict, PyDictMethods, PyNone}; | ||||||
use crate::Py; | ||||||
use crate::Python; | ||||||
|
||||||
#[test] | ||||||
fn test_pymutex() { | ||||||
let mut mutex = Python::with_gil(|py| -> PyMutex<Py<PyDict>> { | ||||||
let d = PyDict::new(py); | ||||||
PyMutex::new(d.unbind()) | ||||||
}); | ||||||
|
||||||
Python::with_gil(|py| { | ||||||
let mut mutex = py.allow_threads(|| -> PyMutex<Py<PyDict>> { | ||||||
std::thread::spawn(|| { | ||||||
let dict_guard = mutex.lock(); | ||||||
Python::with_gil(|py| { | ||||||
let dict = dict_guard.bind(py); | ||||||
dict.set_item(PyNone::get(py), PyNone::get(py)).unwrap(); | ||||||
}); | ||||||
drop(dict_guard); | ||||||
mutex | ||||||
}) | ||||||
.join() | ||||||
.unwrap() | ||||||
}); | ||||||
|
||||||
let dict_guard = mutex.lock(); | ||||||
let d = dict_guard.bind(py); | ||||||
|
||||||
assert!(d | ||||||
.get_item(PyNone::get(py)) | ||||||
.unwrap() | ||||||
.unwrap() | ||||||
.eq(PyNone::get(py)) | ||||||
.unwrap()); | ||||||
}); | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This not part of the PR but since colesbury's clarification I believe this is unnecessary, so it can be removed.