Skip to content

Commit fa7b3ce

Browse files
committed
Fix connection with tokio
Recent changes moved the reactor register in the io_loop. The io_loop thread is not managed by an executor, and tokio doesn't like this. This could and should be fixed in tokio-reactor-trait but requires a breaking change there. In the meantime, let's just move that part to the internal RPC task which runs inside the executor. While at it, make sure the tokio example runs properly by integrating in the tests. Fixes #436
1 parent a1893bc commit fa7b3ce

6 files changed

Lines changed: 71 additions & 17 deletions

File tree

examples/tokio.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,28 @@
11
use lapin::{
22
BasicProperties, Connection, ConnectionProperties,
33
message::DeliveryResult,
4-
options::{BasicAckOptions, BasicConsumeOptions, BasicPublishOptions, QueueDeclareOptions},
4+
options::{
5+
BasicAckOptions, BasicCancelOptions, BasicConsumeOptions, BasicPublishOptions,
6+
QueueDeclareOptions,
7+
},
58
types::FieldTable,
69
};
710

8-
#[tokio::main]
9-
async fn main() {
10-
let uri = "amqp://localhost:5672";
11+
async fn tokio_main(forever: bool) {
12+
if std::env::var("RUST_LOG").is_err() {
13+
unsafe { std::env::set_var("RUST_LOG", "info") };
14+
}
15+
16+
tracing_subscriber::fmt::init();
17+
18+
let addr = std::env::var("AMQP_ADDR").unwrap_or_else(|_| "amqp://127.0.0.1:5672/%2f".into());
1119
let options = ConnectionProperties::default()
1220
// Use tokio executor and reactor.
1321
// At the moment the reactor is only available for unix.
1422
.with_executor(tokio_executor_trait::Tokio::current())
1523
.with_reactor(tokio_reactor_trait::Tokio);
1624

17-
let connection = Connection::connect(uri, options).await.unwrap();
25+
let connection = Connection::connect(&addr, options).await.unwrap();
1826
let channel = connection.create_channel().await.unwrap();
1927

2028
let _queue = channel
@@ -70,5 +78,25 @@ async fn main() {
7078
.await
7179
.unwrap();
7280

73-
std::future::pending::<()>().await;
81+
if forever {
82+
std::future::pending::<()>().await;
83+
} else {
84+
channel
85+
.basic_cancel("tag_foo", BasicCancelOptions::default())
86+
.await
87+
.unwrap();
88+
connection.close(200, "OK").await.unwrap();
89+
}
90+
}
91+
92+
#[tokio::main]
93+
async fn main() {
94+
tokio_main(true).await
95+
}
96+
97+
#[test]
98+
fn connection() {
99+
tokio::runtime::Runtime::new()
100+
.expect("failed to build tokio runtime")
101+
.block_on(tokio_main(false));
74102
}

src/connection.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,9 @@ impl Connection {
188188
let status = ConnectionStatus::new(&uri);
189189
let frames = Frames::default();
190190
let socket_state = SocketState::default();
191-
let internal_rpc = InternalRPC::new(executor.clone(), socket_state.handle());
192-
let heartbeat = Heartbeat::new(status.clone(), executor.clone(), reactor.clone());
191+
let internal_rpc =
192+
InternalRPC::new(executor.clone(), reactor.clone(), socket_state.handle());
193+
let heartbeat = Heartbeat::new(status.clone(), executor.clone(), reactor);
193194
let channels = Channels::new(
194195
configuration.clone(),
195196
status.clone(),
@@ -215,7 +216,7 @@ impl Connection {
215216
);
216217

217218
internal_rpc.start(conn.channels.clone());
218-
conn.io_loop.register(io_loop.start(reactor)?);
219+
conn.io_loop.register(io_loop.start()?);
219220
conn.start(uri, options).await
220221
}
221222

@@ -326,7 +327,8 @@ mod tests {
326327
let status = ConnectionStatus::new(&uri);
327328
let frames = Frames::default();
328329
let socket_state = SocketState::default();
329-
let internal_rpc = InternalRPC::new(executor.clone(), socket_state.handle());
330+
let internal_rpc =
331+
InternalRPC::new(executor.clone(), reactor.clone(), socket_state.handle());
330332
let heartbeat = Heartbeat::new(status.clone(), executor.clone(), reactor);
331333
let channels = Channels::new(
332334
configuration.clone(),

src/internal_rpc.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ use crate::{
55
error_holder::ErrorHolder,
66
killswitch::KillSwitch,
77
options::{BasicAckOptions, BasicCancelOptions, BasicNackOptions, BasicRejectOptions},
8+
reactor::FullReactor,
89
socket_state::SocketStateHandle,
910
types::{ChannelId, DeliveryTag, Identifier, ReplyCode},
1011
};
1112
use executor_trait::FullExecutor;
1213
use flume::{Receiver, Sender};
14+
use reactor_trait::{AsyncIOHandle, IOHandle};
1315
use std::{collections::HashMap, fmt, future::Future, sync::Arc};
1416
use tracing::trace;
1517

1618
pub(crate) struct InternalRPC {
1719
rpc: Receiver<Option<InternalCommand>>,
1820
handle: InternalRPCHandle,
1921
channels_status: HashMap<ChannelId, KillSwitch>,
22+
reactor: Arc<dyn FullReactor + Send + Sync>,
2023
}
2124

2225
#[derive(Clone)]
@@ -127,6 +130,14 @@ impl InternalRPCHandle {
127130
self.send(InternalCommand::InitConnectionShutdown(error));
128131
}
129132

133+
pub(crate) fn reactor_register(
134+
&self,
135+
handle: IOHandle,
136+
resolver: PromiseResolver<Box<dyn AsyncIOHandle + Send>>,
137+
) {
138+
self.send(InternalCommand::ReactorRegister(handle, resolver));
139+
}
140+
130141
pub(crate) fn remove_channel(&self, channel_id: ChannelId, error: Error) {
131142
self.send(InternalCommand::RemoveChannel(channel_id, error));
132143
}
@@ -230,6 +241,7 @@ enum InternalCommand {
230241
FinishConnectionShutdown,
231242
InitConnectionRecovery(Error),
232243
InitConnectionShutdown(Error),
244+
ReactorRegister(IOHandle, PromiseResolver<Box<dyn AsyncIOHandle + Send>>),
233245
RemoveChannel(ChannelId, Error),
234246
SendConnectionCloseOk(Error),
235247
SetChannelStatus(ChannelId, KillSwitch),
@@ -242,6 +254,7 @@ enum InternalCommand {
242254
impl InternalRPC {
243255
pub(crate) fn new(
244256
executor: Arc<dyn FullExecutor + Send + Sync>,
257+
reactor: Arc<dyn FullReactor + Send + Sync>,
245258
waker: SocketStateHandle,
246259
) -> Self {
247260
let (sender, rpc) = flume::unbounded();
@@ -254,6 +267,7 @@ impl InternalRPC {
254267
rpc,
255268
handle,
256269
channels_status: Default::default(),
270+
reactor,
257271
}
258272
}
259273

@@ -368,6 +382,9 @@ impl InternalRPC {
368382
channels.init_connection_recovery(error);
369383
}
370384
InitConnectionShutdown(error) => channels.init_connection_shutdown(error),
385+
ReactorRegister(handle, resolver) => {
386+
resolver.complete(self.reactor.register(handle).map_err(Error::from));
387+
}
371388
RemoveChannel(channel_id, error) => {
372389
if !self.channel_ok(channel_id) {
373390
continue;

src/io_loop.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
Configuration, ConnectionStatus, Error, ErrorKind, PromiseResolver, Result, TcpStream,
2+
Configuration, ConnectionStatus, Error, ErrorKind, Promise, PromiseResolver, Result, TcpStream,
33
buffer::Buffer,
44
channels::Channels,
55
connection_status::ConnectionState,
@@ -8,7 +8,6 @@ use crate::{
88
internal_rpc::InternalRPCHandle,
99
killswitch::KillSwitch,
1010
protocol::{self, AMQPError, AMQPHardError},
11-
reactor::FullReactor,
1211
socket_state::SocketState,
1312
tcp::HandshakeResult,
1413
thread::JoinHandle,
@@ -160,10 +159,7 @@ impl IoLoop {
160159
}
161160
}
162161

163-
pub(crate) fn start(
164-
mut self,
165-
reactor: Arc<dyn FullReactor + Send + Sync>,
166-
) -> Result<JoinHandle> {
162+
pub(crate) fn start(mut self) -> Result<JoinHandle> {
167163
let waker = self.socket_state.handle();
168164
let current_span = tracing::Span::current();
169165
let handle = ThreadBuilder::new()
@@ -176,7 +172,10 @@ impl IoLoop {
176172
let writable_waker = self.socket_state.writable_waker();
177173
let mut writable_context = Context::from_waker(&writable_waker);
178174
let (mut stream, res) = loop {
179-
let mut stream = Box::into_pin(reactor.register(IOHandle::new(self.tcp_connect()?))?);
175+
let (promise, resolver) = Promise::new();
176+
let handle = IOHandle::new(self.tcp_connect()?);
177+
self.internal_rpc.reactor_register(handle, resolver);
178+
let mut stream = Box::into_pin(promise.wait()?);
180179
let mut res = Ok(());
181180

182181
while self.should_continue(&connection_killswitch) {

src/promise.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use flume::{Receiver, Sender};
33
use std::{
44
fmt,
55
future::Future,
6+
io,
67
pin::Pin,
78
sync::{Arc, RwLock},
89
task::{Context, Poll},
@@ -65,6 +66,12 @@ impl<T: Send + 'static> Promise<T> {
6566
pub(crate) fn try_wait(&self) -> Option<Result<T>> {
6667
self.recv.try_recv().ok()
6768
}
69+
70+
pub(crate) fn wait(&self) -> Result<T> {
71+
self.recv
72+
.recv()
73+
.unwrap_or_else(|err| Err(io::Error::other(err).into()))
74+
}
6875
}
6976

7077
impl<T: Send + 'static> Future for Promise<T> {

tests/tokio.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../examples/tokio.rs

0 commit comments

Comments
 (0)