Skip to content

Commit f87debb

Browse files
committed
refactor to always poll per mutation
1 parent 0ddc198 commit f87debb

5 files changed

Lines changed: 66 additions & 100 deletions

File tree

pulsebeam-runtime/src/net/mod.rs

Lines changed: 21 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub struct RecvPacketBatch {
2929
pub stride: usize,
3030
pub len: usize,
3131
pub transport: Transport,
32+
33+
offset: usize,
3234
}
3335

3436
impl RecvPacketBatch {
@@ -38,42 +40,16 @@ impl RecvPacketBatch {
3840
&self.buf
3941
}
4042

41-
pub fn iter(&self) -> RecvPacketBatchIter<'_> {
42-
RecvPacketBatchIter {
43-
batch: self,
44-
offset: 0,
45-
}
46-
}
47-
}
48-
49-
impl<'a> IntoIterator for &'a RecvPacketBatch {
50-
type Item = &'a [u8];
51-
type IntoIter = RecvPacketBatchIter<'a>;
52-
53-
fn into_iter(self) -> Self::IntoIter {
54-
self.iter()
55-
}
56-
}
57-
58-
pub struct RecvPacketBatchIter<'a> {
59-
batch: &'a RecvPacketBatch,
60-
offset: usize,
61-
}
62-
63-
impl<'a> Iterator for RecvPacketBatchIter<'a> {
64-
type Item = &'a [u8];
65-
66-
fn next(&mut self) -> Option<Self::Item> {
67-
assert!(
68-
self.batch.stride != 0,
69-
"stride must not be zero in iteration"
70-
);
71-
let tail = self.batch.len.min(self.offset + self.batch.stride);
43+
// https://stackoverflow.com/questions/68606470/how-to-return-a-reference-when-implementing-an-iterator
44+
// GAT is not stabilized, so it's not possible to return a reference in an iterator yet.
45+
pub fn next_packet(&mut self) -> Option<&[u8]> {
46+
assert!(self.stride != 0, "stride must not be zero in iteration");
47+
let tail = self.len.min(self.offset + self.stride);
7248
if self.offset >= tail {
7349
return None;
7450
}
7551

76-
let buf = &self.batch.buf[self.offset..tail];
52+
let buf = &self.buf[self.offset..tail];
7753
self.offset = tail;
7854
Some(buf)
7955
}
@@ -201,38 +177,38 @@ mod tests {
201177

202178
#[test]
203179
fn recv_packet_batch_iter_yields_multiple_segments_without_off_by_one() {
204-
let batch = RecvPacketBatch {
180+
let mut batch = RecvPacketBatch {
205181
src: test_addr(),
206182
dst: test_addr(),
207183
buf: (0u8..20).collect(),
208184
stride: 6,
209185
len: 20,
210186
transport: Transport::Udp(UdpMode::Scalar),
187+
offset: 0,
211188
};
212189

213-
let chunks: Vec<&[u8]> = batch.iter().collect();
214-
assert_eq!(chunks.len(), 4);
215-
assert_eq!(chunks[0], &[0, 1, 2, 3, 4, 5]);
216-
assert_eq!(chunks[1], &[6, 7, 8, 9, 10, 11]);
217-
assert_eq!(chunks[2], &[12, 13, 14, 15, 16, 17]);
218-
assert_eq!(chunks[3], &[18, 19]);
190+
assert_eq!(batch.next_packet(), Some(&[0, 1, 2, 3, 4, 5][..]));
191+
assert_eq!(batch.next_packet(), Some(&[6, 7, 8, 9, 10, 11][..]));
192+
assert_eq!(batch.next_packet(), Some(&[12, 13, 14, 15, 16, 17][..]));
193+
assert_eq!(batch.next_packet(), Some(&[18, 19][..]));
194+
assert_eq!(batch.next_packet(), None);
219195
}
220196

221197
#[test]
222198
fn recv_packet_batch_iter_yields_multiple_segments_with_exact_len() {
223-
let batch = RecvPacketBatch {
199+
let mut batch = RecvPacketBatch {
224200
src: test_addr(),
225201
dst: test_addr(),
226202
buf: (0u8..20).collect(),
227203
stride: 6,
228204
len: 18,
229205
transport: Transport::Udp(UdpMode::Scalar),
206+
offset: 0,
230207
};
231208

232-
let chunks: Vec<&[u8]> = batch.iter().collect();
233-
assert_eq!(chunks.len(), 3);
234-
assert_eq!(chunks[0], &[0, 1, 2, 3, 4, 5]);
235-
assert_eq!(chunks[1], &[6, 7, 8, 9, 10, 11]);
236-
assert_eq!(chunks[2], &[12, 13, 14, 15, 16, 17]);
209+
assert_eq!(batch.next_packet(), Some(&[0, 1, 2, 3, 4, 5][..]));
210+
assert_eq!(batch.next_packet(), Some(&[6, 7, 8, 9, 10, 11][..]));
211+
assert_eq!(batch.next_packet(), Some(&[12, 13, 14, 15, 16, 17][..]));
212+
assert_eq!(batch.next_packet(), None);
237213
}
238214
}

pulsebeam-runtime/src/net/tcp.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ impl TcpTransport {
455455
stride: len,
456456
len,
457457
transport: Transport::Tcp,
458+
offset: 0,
458459
});
459460
}
460461
TcpEvent::Closed(src) => {

pulsebeam-runtime/src/net/udp.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ impl UdpTransportReader {
158158
stride: m.stride, // Downstream will use this to skip through buf
159159
len: m.len, // Total length of the GRO batch
160160
transport: Transport::Udp(UdpMode::Batch),
161+
offset: 0,
161162
});
162163
}
163164
Ok(out.len() - prev_len)

pulsebeam-runtime/src/net/udp_scalar.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ impl UdpTransportReader {
100100
buf: slot,
101101
stride: n,
102102
len: n,
103+
offset: 0,
103104
});
104105
}
105106
Err(err) => {

pulsebeam/src/participant/core.rs

Lines changed: 42 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -280,72 +280,54 @@ impl ParticipantCore {
280280
}
281281

282282
pub fn poll(&mut self, now: Instant, events: &mut EventQueue) {
283-
loop {
284-
let next_deadline = self.poll_until_deadline(now, events);
285-
match next_deadline {
286-
None => {
287-
events.exit();
288-
return;
289-
}
290-
Some(deadline) if deadline > now => {
291-
events.update_deadline(deadline);
292-
return;
293-
}
294-
Some(_) => {
295-
// Deadline is immediate, feed another Timeout and keep draining
296-
// https://discordapp.com/channels/1436725650302959829/1436796781462687817
297-
let _ = self.rtc.handle_input(Input::Timeout(now.into()));
298-
}
299-
}
300-
}
301-
}
283+
'drain: loop {
284+
let Some(rtc_deadline) = self.poll_rtc(events) else {
285+
events.exit();
286+
return;
287+
};
302288

303-
fn poll_until_deadline(&mut self, now: Instant, events: &mut EventQueue) -> Option<Instant> {
304-
if now >= self.last_slow_poll + SLOW_POLL_INTERVAL {
305-
self.poll_slow(now, events);
306-
self.last_slow_poll = now;
307-
}
289+
if now >= self.last_slow_poll + SLOW_POLL_INTERVAL {
290+
self.poll_slow(now, events);
291+
self.last_slow_poll = now;
292+
continue;
293+
}
308294

309-
while let Some(batch) = self.pending_ingress.pop_front() {
310-
let transport = match batch.transport {
311-
Transport::Udp(_) => str0m::net::Protocol::Udp,
312-
Transport::Tcp => str0m::net::Protocol::Tcp,
313-
};
295+
while let Some(batch) = self.pending_ingress.front_mut() {
296+
let transport = match batch.transport {
297+
Transport::Udp(_) => str0m::net::Protocol::Udp,
298+
Transport::Tcp => str0m::net::Protocol::Tcp,
299+
};
314300

315-
for pkt in batch.into_iter() {
316-
if let Ok(contents) = (*pkt).try_into() {
317-
let recv = str0m::net::Receive {
318-
proto: transport,
319-
source: batch.src,
320-
destination: batch.dst,
321-
contents,
322-
};
323-
if self
324-
.rtc
325-
.handle_input(Input::Receive(now.into(), recv))
326-
.is_err()
327-
{
328-
continue;
329-
}
301+
let src = batch.src;
302+
let dst = batch.dst;
303+
let Some(pkt) = batch.next_packet() else {
304+
self.pending_ingress.pop_front();
305+
continue;
306+
};
330307

331-
self.poll_rtc(events)?;
332-
} else {
308+
let Ok(contents) = (*pkt).try_into() else {
333309
tracing::warn!(src = %batch.src, "Dropping malformed UDP packet");
334-
}
310+
// no point iterating the batch, this is already malicous
311+
self.pending_ingress.pop_front();
312+
continue;
313+
};
314+
315+
let recv = str0m::net::Receive {
316+
proto: transport,
317+
source: src,
318+
destination: dst,
319+
contents,
320+
};
321+
let _ = self.rtc.handle_input(Input::Receive(now.into(), recv));
322+
continue 'drain;
335323
}
336-
}
337324

338-
loop {
339-
let rtc_deadline = self.poll_rtc(events)?;
340325
let did_work = self.signaling.poll(&mut self.rtc, &self.downstream);
341326
if did_work {
342-
// Signaling wrote data. The RTC engine is now "dirty" (has output to send).
343-
// We loop back to `poll_rtc` immediately to flush `Output::Transmit`.
344327
continue;
345328
}
346329

347330
if self.downstream.dirty_allocation {
348-
// Make sure rtc is updated with new allocations
349331
let assignments_changed = self.downstream.update_allocations(&mut self.rtc.bwe());
350332
if assignments_changed {
351333
self.signaling.mark_assignments_dirty();
@@ -355,9 +337,14 @@ impl ParticipantCore {
355337
}
356338

357339
let next_slow_poll = self.last_slow_poll + SLOW_POLL_INTERVAL;
340+
let deadline = rtc_deadline.min(next_slow_poll);
341+
342+
if deadline > now {
343+
events.update_deadline(deadline);
344+
return;
345+
}
358346

359-
// No new work generated. We are synced. Return the RTC deadline.
360-
return Some(rtc_deadline.min(next_slow_poll));
347+
let _ = self.rtc.handle_input(Input::Timeout(now.into()));
361348
}
362349
}
363350

0 commit comments

Comments
 (0)