hydro_lang/deploy/maelstrom/
deploy_runtime_maelstrom.rs1#![allow(
7 unused,
8 reason = "unused in trybuild but the __staged version is needed"
9)]
10#![allow(missing_docs, reason = "used internally")]
11
12use std::io::{BufRead, Write};
13use std::sync::Arc;
14use std::sync::atomic::{AtomicBool, Ordering};
15
16use futures::{Stream, StreamExt};
17use serde::{Deserialize, Serialize};
18use stageleft::{QuotedWithContext, RuntimeData, q};
19
20use crate::forward_handle::ForwardHandle;
21use crate::live_collections::boundedness::Unbounded;
22use crate::live_collections::keyed_stream::KeyedStream;
23use crate::live_collections::stream::{ExactlyOnce, NoOrder, TotalOrder};
24use crate::location::dynamic::LocationId;
25use crate::location::member_id::TaglessMemberId;
26use crate::location::{Cluster, LocationKey, MembershipEvent};
27use crate::nondet::nondet;
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct MaelstromMessage<T> {
32 pub src: String,
33 pub dest: String,
34 pub body: T,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct InitBody {
40 #[serde(rename = "type")]
41 pub msg_type: String,
42 pub msg_id: u64,
43 pub node_id: String,
44 pub node_ids: Vec<String>,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct InitOkBody {
50 #[serde(rename = "type")]
51 pub msg_type: String,
52 pub in_reply_to: u64,
53}
54
55pub struct MaelstromMeta {
58 pub node_id: String,
59 pub node_ids: Vec<String>,
60 stdin_tx: tokio::sync::broadcast::Sender<String>,
61 peers_ready: Arc<AtomicBool>,
65}
66
67impl MaelstromMeta {
68 pub fn subscribe_stdin(&self) -> tokio_stream::wrappers::BroadcastStream<String> {
71 tokio_stream::wrappers::BroadcastStream::new(self.stdin_tx.subscribe())
72 }
73
74 pub fn peers_ready(&self) -> Arc<AtomicBool> {
92 self.peers_ready.clone()
93 }
94
95 pub fn start_receiving(&self) {
97 let tx_clone = self.stdin_tx.clone();
98 let peers_ready = self.peers_ready.clone();
99 let node_ids: std::collections::HashSet<String> = self.node_ids.iter().cloned().collect();
100
101 std::thread::spawn(move || {
103 let stdin = std::io::stdin();
104 for line in stdin.lock().lines() {
105 match line {
106 Ok(l) => {
107 if !peers_ready.load(Ordering::Relaxed)
108 && message_activates_node(&l, &node_ids)
109 {
110 peers_ready.store(true, Ordering::Release);
111 }
112 let _ = tx_clone.send(l);
114 }
115 Err(_) => break,
116 }
117 }
118 });
119 }
120}
121
122fn message_activates_node(line: &str, node_ids: &std::collections::HashSet<String>) -> bool {
128 let Ok(msg) = serde_json::from_str::<MaelstromMessage<serde_json::Value>>(line) else {
129 return false;
130 };
131 node_ids.contains(&msg.src) || msg.src.starts_with('c')
132}
133
134pub fn maelstrom_init() -> MaelstromMeta {
138 let stdin = std::io::stdin();
139 let mut stdout = std::io::stdout();
140
141 let msg: MaelstromMessage<InitBody> = loop {
150 let mut line = String::new();
151 let bytes_read = stdin
152 .lock()
153 .read_line(&mut line)
154 .expect("Failed to read init message");
155
156 if bytes_read == 0 {
157 eprintln!("stdin closed before an init message was received; exiting");
163 std::process::exit(0);
164 }
165
166 let parsed: MaelstromMessage<serde_json::Value> =
167 serde_json::from_str(&line).expect("Failed to parse message while waiting for init");
168
169 if parsed.body.get("type").and_then(|t| t.as_str()) == Some("init") {
170 let body: InitBody =
171 serde_json::from_value(parsed.body).expect("Failed to parse init message");
172 break MaelstromMessage {
173 src: parsed.src,
174 dest: parsed.dest,
175 body,
176 };
177 }
178 eprintln!(
179 "dropping message received before init (lossy channel): {}",
180 line.trim_end()
181 );
182 };
183
184 let (stdin_tx, _) = tokio::sync::broadcast::channel::<String>(1024);
186
187 let meta = MaelstromMeta {
188 node_id: msg.body.node_id.clone(),
189 node_ids: msg.body.node_ids.clone(),
190 stdin_tx,
191 peers_ready: Arc::new(AtomicBool::new(false)),
192 };
193
194 let response = MaelstromMessage {
196 src: msg.body.node_id,
197 dest: msg.src,
198 body: InitOkBody {
199 msg_type: "init_ok".to_owned(),
200 in_reply_to: msg.body.msg_id,
201 },
202 };
203
204 let response_json = serde_json::to_string(&response).expect("Failed to serialize init_ok");
205 writeln!(stdout, "{}", response_json).expect("Failed to write init_ok");
206 stdout.flush().expect("Failed to flush stdout");
207
208 meta
209}
210
211pub(super) fn cluster_members<'a>(
215 meta: RuntimeData<&'a MaelstromMeta>,
216 _of_cluster: LocationKey,
217) -> impl QuotedWithContext<'a, &'a [TaglessMemberId], ()> + Clone + 'a {
218 q!({
219 let members: &'static [TaglessMemberId] = Box::leak(
220 meta.node_ids
221 .iter()
222 .map(|id| TaglessMemberId::from_maelstrom_node_id(id.clone()))
223 .collect::<Vec<TaglessMemberId>>()
224 .into_boxed_slice(),
225 );
226 members
227 })
228}
229
230pub(super) fn cluster_self_id<'a>(
232 meta: RuntimeData<&'a MaelstromMeta>,
233) -> impl QuotedWithContext<'a, TaglessMemberId, ()> + Clone + 'a {
234 q!(TaglessMemberId::from_maelstrom_node_id(
235 meta.node_id.clone()
236 ))
237}
238
239pub(super) fn cluster_membership_stream<'a>(
242 _location_id: &LocationId,
243) -> impl QuotedWithContext<'a, Box<dyn Stream<Item = (TaglessMemberId, MembershipEvent)> + Unpin>, ()>
244{
245 let meta: RuntimeData<&MaelstromMeta> = RuntimeData::new("__hydro_lang_maelstrom_meta");
246 q!(Box::new(futures::stream::iter(
247 meta.node_ids
248 .iter()
249 .map(|id| (
250 TaglessMemberId::from_maelstrom_node_id(id.clone()),
251 MembershipEvent::Joined
252 ))
253 .collect::<Vec<_>>()
254 ))
255 as Box<
256 dyn futures::Stream<Item = (TaglessMemberId, MembershipEvent)> + Unpin,
257 >)
258}
259
260pub(super) fn deploy_maelstrom_m2m(meta: RuntimeData<&MaelstromMeta>) -> (syn::Expr, syn::Expr) {
263 let sink_expr = q!({
267 let node_id = meta.node_id.clone();
268 let peers_ready = meta.peers_ready();
269 sinktools::map(
270 move |(dest_id, payload): (TaglessMemberId, bytes::Bytes)| {
271 let msg = serde_json::json!({
272 "src": node_id,
273 "dest": dest_id.get_maelstrom_node_id(),
274 "body": {
275 "type": "hydro_data",
276 "data": payload.to_vec()
277 }
278 });
279 let mut line = serde_json::to_string(&msg).unwrap();
280 line.push('\n');
281 line
282 },
283 futures::sink::unfold(
284 peers_ready,
285 |peers_ready: std::sync::Arc<std::sync::atomic::AtomicBool>, line: String| {
286 Box::pin(async move {
287 if peers_ready.load(std::sync::atomic::Ordering::Acquire) {
288 print!("{}", line);
289 std::io::stdout().flush().ok();
290 } else {
291 eprintln!(
292 "dropping message sent before peers are known to be up (lossy channel): {}",
293 line.trim_end()
294 );
295 }
296 Ok::<_, std::io::Error>(peers_ready)
297 })
298 },
299 ),
300 )
301 })
302 .splice_untyped_ctx(&());
303
304 let source_expr = q!({
306 let node_ids: std::collections::HashSet<String> = meta.node_ids.iter().cloned().collect();
307 let lines = meta.subscribe_stdin();
308 futures::StreamExt::filter_map(lines, move |line_result| {
309 let node_ids = node_ids.clone();
310 Box::pin(async move {
311 let line = line_result.ok()?;
312 let mut msg =
313 serde_json::from_str::<MaelstromMessage<serde_json::Value>>(&line).ok()?;
314 if msg
316 .body
317 .get("type")
318 .is_some_and(|t| t.as_str() == Some("hydro_data"))
319 {
320 let deser: Vec<u8> =
321 serde_json::from_value(msg.body.get_mut("data").unwrap().take()).unwrap();
322 Some(Ok::<_, std::io::Error>((
323 TaglessMemberId::from_maelstrom_node_id(msg.src),
324 bytes::BytesMut::from(&deser[..]),
325 )))
326 } else {
327 None
328 }
329 })
330 })
331 })
332 .splice_untyped_ctx(&());
333
334 (sink_expr, source_expr)
335}
336
337pub fn maelstrom_client_source(
343 meta: &MaelstromMeta,
344) -> impl Stream<Item = (String, serde_json::Value)> + Unpin {
345 use std::collections::HashSet;
346
347 let node_ids: HashSet<String> = meta.node_ids.iter().cloned().collect();
348 let lines = meta.subscribe_stdin();
349
350 Box::pin(lines.filter_map(move |line_result| {
351 let node_ids = node_ids.clone();
352 async move {
353 let line = line_result.ok()?;
354 let msg: MaelstromMessage<serde_json::Value> = serde_json::from_str(&line).ok()?;
355 if !node_ids.contains(&msg.src) {
357 Some((msg.src, msg.body))
358 } else {
359 None
360 }
361 }
362 }))
363}
364
365pub fn maelstrom_send_response(node_id: &str, client_id: &str, body: serde_json::Value) {
369 use std::io::Write;
370
371 let msg = MaelstromMessage {
372 src: node_id.to_owned(),
373 dest: client_id.to_owned(),
374 body,
375 };
376
377 let json = serde_json::to_string(&msg).expect("Failed to serialize response");
378 println!("{}", json);
379 std::io::stdout().flush().ok();
380}