monarch_hyperactor/
pympsc.rs1use std::sync::Arc;
15use std::sync::Mutex;
16use std::sync::mpsc;
17
18use monarch_types::MapPyErr;
19use pyo3::Bound;
20use pyo3::IntoPyObject;
21use pyo3::IntoPyObjectExt;
22use pyo3::Py;
23use pyo3::PyAny;
24use pyo3::PyResult;
25use pyo3::Python;
26use pyo3::pyclass;
27use pyo3::pymethods;
28use pyo3::types::PyModule;
29use pyo3::types::PyModuleMethods;
30use pyo3::wrap_pyfunction;
31
32use crate::py_cell::PyCell;
33use crate::pywaker::PyEvent;
34use crate::pywaker::{self};
35
36pub fn channel() -> Result<(Sender, PyReceiver), nix::Error> {
38 let (tx, rx) = mpsc::channel();
39 let rx = Arc::new(Mutex::new(rx));
40
41 let (waker, event) = pywaker::event()?;
42
43 Ok((
44 Sender::new(tx, Arc::new(waker)),
45 PyReceiver {
46 rx,
47 event: PyCell::new(event),
48 },
49 ))
50}
51
52pub trait IntoPyObjectBox: Send {
54 fn into_py_object(self: Box<Self>, py: Python<'_>) -> PyResult<Py<PyAny>>;
55}
56
57impl<T> IntoPyObjectBox for T
58where
59 T: for<'py> IntoPyObject<'py> + Send,
60{
61 fn into_py_object(self: Box<Self>, py: Python<'_>) -> PyResult<Py<PyAny>> {
62 (*self).into_py_any(py)
63 }
64}
65
66#[derive(Debug)]
68pub struct SendError;
69
70impl std::fmt::Display for SendError {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "SendError")
73 }
74}
75
76impl std::error::Error for SendError {}
77
78#[derive(Clone)]
81pub struct Sender {
82 tx: mpsc::Sender<Box<dyn IntoPyObjectBox>>,
86 waker: Arc<pywaker::Waker>,
89}
90
91impl std::fmt::Debug for Sender {
92 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93 f.debug_struct("Sender").finish_non_exhaustive()
94 }
95}
96
97impl Sender {
98 pub(crate) fn new(
101 tx: mpsc::Sender<Box<dyn IntoPyObjectBox>>,
102 waker: Arc<pywaker::Waker>,
103 ) -> Self {
104 Self { tx, waker }
105 }
106
107 pub fn send<T>(&self, msg: T) -> Result<(), SendError>
110 where
111 T: IntoPyObjectBox + Send + 'static,
112 {
113 self.tx.send(Box::new(msg)).map_err(|_| SendError)?;
116 let _ = self.waker.wake();
117 Ok(())
118 }
119}
120
121#[pyclass(name = "Receiver", module = "monarch._src.actor.mpsc")]
124pub struct PyReceiver {
125 rx: Arc<Mutex<mpsc::Receiver<Box<dyn IntoPyObjectBox>>>>,
126 event: PyCell<PyEvent>,
127}
128
129impl std::fmt::Debug for PyReceiver {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.debug_struct("PyReceiver").finish_non_exhaustive()
132 }
133}
134
135#[pymethods]
136impl PyReceiver {
137 fn try_recv(&self, py: Python<'_>) -> PyResult<Option<Py<PyAny>>> {
138 match self.rx.lock().unwrap().try_recv() {
139 Ok(boxed_msg) => Ok(Some(boxed_msg.into_py_object(py)?)),
140 Err(mpsc::TryRecvError::Empty) => Ok(None),
141 Err(mpsc::TryRecvError::Disconnected) => {
142 Err(pyo3::exceptions::PyEOFError::new_err("Channel closed"))
143 }
144 }
145 }
146
147 fn _event<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyEvent>> {
148 let py_event = self.event.clone_ref(py)?;
149 Ok(py_event.bind(py).clone())
150 }
151}
152
153pub(crate) mod testing {
154 use pyo3::PyErr;
155 use pyo3::exceptions::PyRuntimeError;
156 use pyo3::pyfunction;
157 use pyo3::types::PyAnyMethods;
158
159 use super::*;
160
161 pub(crate) struct Unconvertible;
164
165 impl<'py> IntoPyObject<'py> for Unconvertible {
166 type Target = PyAny;
167 type Output = Bound<'py, PyAny>;
168 type Error = PyErr;
169
170 fn into_pyobject(self, _py: Python<'py>) -> PyResult<Self::Output> {
171 Err(PyRuntimeError::new_err("unconvertible test item"))
172 }
173 }
174
175 #[pyclass(module = "monarch._rust_bindings.monarch_hyperactor.pympsc")]
180 pub(crate) struct PyTestSender {
181 sender: Arc<Mutex<Sender>>,
182 }
183
184 #[pymethods]
185 impl PyTestSender {
186 fn send(&self, _py: Python<'_>, obj: Py<PyAny>) -> PyResult<()> {
187 self.sender.lock().unwrap().send(obj).map_pyerr()?;
188 Ok(())
189 }
190
191 fn send_unconvertible(&self) -> PyResult<()> {
192 self.sender
193 .lock()
194 .expect("test sender lock should not be poisoned: sends do not panic")
195 .send(Unconvertible)
196 .map_pyerr()?;
197 Ok(())
198 }
199 }
200
201 impl PyTestSender {
202 pub(crate) fn new(sender: Sender) -> Self {
203 Self {
204 sender: Arc::new(Mutex::new(sender)),
205 }
206 }
207 }
208
209 #[pyfunction]
210 fn channel_for_test(_py: Python<'_>) -> PyResult<(PyTestSender, PyReceiver)> {
211 let (tx, rx) = channel().map_pyerr()?;
212 Ok((PyTestSender::new(tx), rx))
213 }
214
215 pub(super) fn register_python_bindings(hyperactor_mod: &Bound<'_, PyModule>) -> PyResult<()> {
216 hyperactor_mod.add_class::<PyTestSender>()?;
217 let channel_for_test = wrap_pyfunction!(channel_for_test, hyperactor_mod)?;
218 channel_for_test.setattr(
219 "__module__",
220 "monarch._rust_bindings.monarch_hyperactor.pympsc",
221 )?;
222
223 hyperactor_mod.add_function(channel_for_test)?;
224 Ok(())
225 }
226}
227
228pub fn register_python_bindings(hyperactor_mod: &Bound<'_, PyModule>) -> PyResult<()> {
229 hyperactor_mod.add_class::<PyReceiver>()?;
230 testing::register_python_bindings(hyperactor_mod)?;
231 Ok(())
232}