Skip to content

Commit bb837bd

Browse files
committed
Make the default notifications iterator read nonblocking
It is always super confusing as to when a notification that's been sent to the client will actually show up in the old version of this iterator, so it's best to have it see if there's anything waiting in the TCP buffer. Closes #149
1 parent 278ee1c commit bb837bd

File tree

5 files changed

+76
-13
lines changed

5 files changed

+76
-13
lines changed

src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,24 @@ impl InnerConnection {
527527
}
528528
}
529529

530+
fn read_message_with_notification_nonblocking(&mut self)
531+
-> std::io::Result<Option<BackendMessage>> {
532+
debug_assert!(!self.desynchronized);
533+
loop {
534+
match try_desync!(self, self.stream.read_message_nonblocking()) {
535+
Some(NoticeResponse { fields }) => {
536+
if let Ok(err) = DbError::new_raw(fields) {
537+
self.notice_handler.handle_notice(err);
538+
}
539+
}
540+
Some(ParameterStatus { parameter, value }) => {
541+
self.parameters.insert(parameter, value);
542+
}
543+
val => return Ok(val),
544+
}
545+
}
546+
}
547+
530548
fn read_message(&mut self) -> std_io::Result<BackendMessage> {
531549
loop {
532550
match try!(self.read_message_with_notification()) {

src/message.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
66

77
use types::Oid;
88
use util;
9-
use priv_io::ReadTimeout;
9+
use priv_io::StreamOptions;
1010

1111
use self::BackendMessage::*;
1212
use self::FrontendMessage::*;
@@ -287,10 +287,12 @@ pub trait ReadMessage {
287287

288288
fn read_message_timeout(&mut self, timeout: Duration) -> io::Result<Option<BackendMessage>>;
289289

290+
fn read_message_nonblocking(&mut self) -> io::Result<Option<BackendMessage>>;
291+
290292
fn finish_read_message(&mut self, ident: u8) -> io::Result<BackendMessage>;
291293
}
292294

293-
impl<R: BufRead + ReadTimeout> ReadMessage for R {
295+
impl<R: BufRead + StreamOptions> ReadMessage for R {
294296
fn read_message(&mut self) -> io::Result<BackendMessage> {
295297
let ident = try!(self.read_u8());
296298
self.finish_read_message(ident)
@@ -314,6 +316,24 @@ impl<R: BufRead + ReadTimeout> ReadMessage for R {
314316
}
315317
}
316318

319+
fn read_message_nonblocking(&mut self) -> io::Result<Option<BackendMessage>> {
320+
try!(self.set_nonblocking(true));
321+
let ident = self.read_u8();
322+
try!(self.set_nonblocking(false));
323+
324+
match ident {
325+
Ok(ident) => self.finish_read_message(ident).map(Some),
326+
Err(e) => {
327+
let e: io::Error = e.into();
328+
if e.kind() == io::ErrorKind::WouldBlock {
329+
Ok(None)
330+
} else {
331+
Err(e)
332+
}
333+
}
334+
}
335+
}
336+
317337
fn finish_read_message(&mut self, ident: u8) -> io::Result<BackendMessage> {
318338
// subtract size of length value
319339
let len = try!(self.read_u32::<BigEndian>()) - mem::size_of::<u32>() as u32;

src/notification.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ impl<'conn> Notifications<'conn> {
4242
/// # Note
4343
///
4444
/// This iterator may start returning `Some` after previously returning
45-
/// `None` if more notifications are received. However, those notifications
46-
/// will not be registered until the connection is used in some way.
45+
/// `None` if more notifications are received.
4746
pub fn iter<'a>(&'a self) -> Iter<'a> {
4847
Iter { conn: self.conn }
4948
}
@@ -72,7 +71,7 @@ impl<'conn> Notifications<'conn> {
7271
}
7372

7473
impl<'a, 'conn> IntoIterator for &'a Notifications<'conn> {
75-
type Item = Notification;
74+
type Item = Result<Notification>;
7675
type IntoIter = Iter<'a>;
7776

7877
fn into_iter(self) -> Iter<'a> {
@@ -92,10 +91,27 @@ pub struct Iter<'a> {
9291
}
9392

9493
impl<'a> Iterator for Iter<'a> {
95-
type Item = Notification;
94+
type Item = Result<Notification>;
95+
96+
fn next(&mut self) -> Option<Result<Notification>> {
97+
let mut conn = self.conn.conn.borrow_mut();
9698

97-
fn next(&mut self) -> Option<Notification> {
98-
self.conn.conn.borrow_mut().notifications.pop_front()
99+
if let Some(notification) = conn.notifications.pop_front() {
100+
return Some(Ok(notification));
101+
}
102+
103+
match conn.read_message_with_notification_nonblocking() {
104+
Ok(Some(NotificationResponse { pid, channel, payload })) => {
105+
Some(Ok(Notification {
106+
pid: pid,
107+
channel: channel,
108+
payload: payload,
109+
}))
110+
}
111+
Ok(None) => None,
112+
Err(err) => Some(Err(Error::Io(err))),
113+
_ => unreachable!(),
114+
}
99115
}
100116
}
101117

src/priv_io.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,12 @@ use message::FrontendMessage::SslRequest;
2222
const DEFAULT_PORT: u16 = 5432;
2323

2424
#[doc(hidden)]
25-
pub trait ReadTimeout {
25+
pub trait StreamOptions {
2626
fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()>;
27+
fn set_nonblocking(&self, nonblock: bool) -> io::Result<()>;
2728
}
2829

29-
impl ReadTimeout for BufStream<Box<StreamWrapper>> {
30+
impl StreamOptions for BufStream<Box<StreamWrapper>> {
3031
fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
3132
match self.get_ref().get_ref().0 {
3233
InternalStream::Tcp(ref s) => {
@@ -36,6 +37,14 @@ impl ReadTimeout for BufStream<Box<StreamWrapper>> {
3637
InternalStream::Unix(ref s) => s.set_read_timeout(timeout),
3738
}
3839
}
40+
41+
fn set_nonblocking(&self, nonblock: bool) -> io::Result<()> {
42+
match self.get_ref().get_ref().0 {
43+
InternalStream::Tcp(ref s) => s.set_nonblocking(nonblock),
44+
#[cfg(feature = "unix_socket")]
45+
InternalStream::Unix(ref s) => s.set_nonblocking(nonblock),
46+
}
47+
}
3948
}
4049

4150
/// A connection to the Postgres server.

tests/test.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -575,20 +575,20 @@ fn test_notification_iterator_some() {
575575
pid: 0,
576576
channel: "test_notification_iterator_one_channel".to_string(),
577577
payload: "hello".to_string()
578-
}, it.next().unwrap());
578+
}, it.next().unwrap().unwrap());
579579
check_notification(Notification {
580580
pid: 0,
581581
channel: "test_notification_iterator_one_channel2".to_string(),
582582
payload: "world".to_string()
583-
}, it.next().unwrap());
583+
}, it.next().unwrap().unwrap());
584584
assert!(it.next().is_none());
585585

586586
or_panic!(conn.execute("NOTIFY test_notification_iterator_one_channel, '!'", &[]));
587587
check_notification(Notification {
588588
pid: 0,
589589
channel: "test_notification_iterator_one_channel".to_string(),
590590
payload: "!".to_string()
591-
}, it.next().unwrap());
591+
}, it.next().unwrap().unwrap());
592592
assert!(it.next().is_none());
593593
}
594594

0 commit comments

Comments
 (0)