monarch_hyperactor/
bootstrap.rs1use futures::future::try_join_all;
10use hyperactor::Gateway;
11use hyperactor::channel::ChannelAddr;
12use hyperactor::id::Label;
13use hyperactor_mesh::bootstrap::BootstrapCommand;
14use hyperactor_mesh::bootstrap::bootstrap;
15use hyperactor_mesh::bootstrap::halt;
16use hyperactor_mesh::bootstrap::host;
17use hyperactor_mesh::host_mesh::HostMesh;
18use hyperactor_mesh::mesh_id::HostMeshId;
19use monarch_types::MapPyErr;
20use pyo3::Bound;
21use pyo3::PyAny;
22use pyo3::PyResult;
23use pyo3::Python;
24use pyo3::exceptions::PyRuntimeError;
25use pyo3::pyfunction;
26use pyo3::types::PyAnyMethods;
27use pyo3::types::PyModule;
28use pyo3::types::PyModuleMethods;
29use pyo3::wrap_pyfunction;
30
31use crate::host_mesh::PyHostMesh;
32use crate::pytokio::PyPythonTask;
33use crate::runtime::GilSite;
34use crate::runtime::monarch_with_gil;
35
36#[pyfunction]
37#[pyo3(signature = ())]
38pub fn bootstrap_main(py: Python) -> PyResult<Bound<PyAny>> {
39 unsafe {
41 fbinit::perform_init();
42 };
43
44 hyperactor::internal_macro_support::tracing::debug!("entering async bootstrap");
45 crate::runtime::future_into_py::<_, i32>(py, async move {
46 let _destroy_guard = unsafe { fbinit::DestroyGuard::new() };
51 bootstrap()
52 .await
53 .map_err(|e| PyRuntimeError::new_err(format!("{:?}", e)))
54 })
55}
56
57#[pyfunction]
58pub fn run_worker_loop_forever(_py: Python<'_>, address: &str) -> PyResult<PyPythonTask> {
59 let (addr, listener) = ChannelAddr::from_zmq_url_with_listener(address)?;
60
61 let invoked_name = std::env::var("FB_XAR_INVOKED_NAME");
63
64 let mut env: std::collections::HashMap<String, String> = std::env::vars().collect();
65
66 let command = Some(if let Ok(invoked_name) = invoked_name {
67 let current_exe = std::path::PathBuf::from(&invoked_name);
69
70 env.insert(
72 "PAR_MAIN_OVERRIDE".to_string(),
73 "monarch._src.actor.bootstrap_main".to_string(),
74 );
75 BootstrapCommand {
76 program: current_exe,
77 arg0: Some(invoked_name),
78 args: vec![],
79 env,
80 }
81 } else {
82 let current_exe = std::env::args()
87 .next()
88 .map(std::path::PathBuf::from)
89 .or_else(|| std::env::current_exe().ok())
90 .ok_or_else(|| {
91 pyo3::PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
92 "Failed to determine current executable",
93 )
94 })?;
95 let current_exe_str = current_exe.to_string_lossy().to_string();
96 BootstrapCommand {
97 program: current_exe,
98 arg0: Some(current_exe_str),
99 args: vec![
100 "-m".to_string(),
101 "monarch._src.actor.bootstrap_main".to_string(),
102 ],
103 env,
104 }
105 });
106
107 PyPythonTask::new(async move {
108 let (_agent_handle, shutdown) =
109 host(addr, command, None, true, listener, Gateway::new(), None)
110 .await
111 .map_pyerr()?;
112 shutdown.join().await;
113 halt::<()>().await;
114 Ok(())
115 })
116}
117
118#[pyfunction]
119pub fn attach_to_workers(
120 instance: &crate::context::PyInstance,
121 workers: Vec<Bound<'_, PyPythonTask>>,
122 name: Option<&str>,
123) -> PyResult<PyPythonTask> {
124 let tasks = workers
125 .into_iter()
126 .map(|x| x.borrow_mut().take_task())
127 .collect::<PyResult<Vec<_>>>()?;
128
129 let name = HostMeshId::instance(Label::strip(name.unwrap_or("hosts")));
134 let instance = instance.clone();
135 PyPythonTask::new(async move {
136 let results = try_join_all(tasks).await?;
137
138 let addresses: Result<Vec<ChannelAddr>, anyhow::Error> =
139 monarch_with_gil(GilSite::Bootstrap, |py| {
140 results
141 .into_iter()
142 .map(|result| {
143 let url_str: String = result.bind(py).extract()?;
144 Ok(ChannelAddr::from_zmq_url(&url_str)?.into_dial_addr())
145 })
146 .collect()
147 })
148 .await;
149 let addresses = addresses?;
150
151 let host_mesh = HostMesh::attach(&*instance, name, addresses)
152 .await
153 .map_err(|e| anyhow::anyhow!("attach failed: {}", e))?;
154 Ok(PyHostMesh::new_owned(host_mesh))
155 })
156}
157
158pub fn register_python_bindings(hyperactor_mod: &Bound<'_, PyModule>) -> PyResult<()> {
159 let f = wrap_pyfunction!(bootstrap_main, hyperactor_mod)?;
160 f.setattr(
161 "__module__",
162 "monarch._rust_bindings.monarch_hyperactor.bootstrap",
163 )?;
164 hyperactor_mod.add_function(f)?;
165
166 let f = wrap_pyfunction!(run_worker_loop_forever, hyperactor_mod)?;
167 f.setattr(
168 "__module__",
169 "monarch._rust_bindings.monarch_hyperactor.bootstrap",
170 )?;
171 hyperactor_mod.add_function(f)?;
172
173 let f = wrap_pyfunction!(attach_to_workers, hyperactor_mod)?;
174 f.setattr(
175 "__module__",
176 "monarch._rust_bindings.monarch_hyperactor.bootstrap",
177 )?;
178 hyperactor_mod.add_function(f)?;
179
180 Ok(())
181}