[GH-ISSUE #713] How to use SyncClient with tokio_threadpool? #278

Closed
opened 2026-03-07 23:14:42 +03:00 by kerem · 8 comments
Owner

Originally created by @jgillich on GitHub (Mar 24, 2019).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/713

I'm getting this error when I try to use SyncClient:

thread 'tokio-runtime-worker-1' panicked at 'Multiple executors at once: EnterError { reason: 
"attempted to run an executor while another executor is already running" }'

This is in a synchronous context (Juniper resolver) within tokio_threadpool::blocking.

Originally created by @jgillich on GitHub (Mar 24, 2019). Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/713 I'm getting this error when I try to use SyncClient: thread 'tokio-runtime-worker-1' panicked at 'Multiple executors at once: EnterError { reason: "attempted to run an executor while another executor is already running" }' This is in a synchronous context (Juniper resolver) within `tokio_threadpool::blocking`.
kerem closed this issue 2026-03-07 23:14:47 +03:00
Author
Owner

@bluejekyll commented on GitHub (Mar 24, 2019):

Ah, SyncClient uses Tokio internally. This error is an artifact of the thread-local-executor usage in Tokio. The short answer is that you'll need to switch to use the ClientFuture (inconsistent naming I know), and switch over to use the futures for the client.

As an aside, the trust-dns-resolver is a full stub resolver, i'm not sure what the Juniper resolver is.

<!-- gh-comment-id:475995552 --> @bluejekyll commented on GitHub (Mar 24, 2019): Ah, SyncClient uses Tokio internally. This error is an artifact of the thread-local-executor usage in Tokio. The short answer is that you'll need to switch to use the ClientFuture (inconsistent naming I know), and switch over to use the futures for the client. As an aside, the trust-dns-resolver is a full stub resolver, i'm not sure what the Juniper resolver is.
Author
Owner

@jgillich commented on GitHub (Mar 24, 2019):

Sorry, I should have mentioned that I was referring to Juniper the GraphQL library. It's fully synchronous but wrapped by Warp (the web framework), which is async. I've already tried ClientFuture, but tokio is a bit confusing. Everything I can find seems to indicate that I need to spawn a new thread, do you know if that's correct or if there's another way?

<!-- gh-comment-id:476003699 --> @jgillich commented on GitHub (Mar 24, 2019): Sorry, I should have mentioned that I was referring to [Juniper the GraphQL library](https://github.com/graphql-rust/juniper). It's fully synchronous but wrapped by Warp (the web framework), which is async. I've already tried ClientFuture, but tokio is a bit confusing. Everything I can find seems to indicate that I need to spawn a new thread, do you know if that's correct or if there's another way?
Author
Owner

@bluejekyll commented on GitHub (Mar 24, 2019):

Yes, the ClientFuture is confusing, there are examples of it's usage, though: https://docs.rs/trust-dns/0.16.0-alpha.2/trust_dns/#async-usage. The ClientFuture would not require a separate thread, but SyncClient would.

I'd like to simplify the Client if possible. It's been a little while since I've looked at trying to do that.

<!-- gh-comment-id:476004153 --> @bluejekyll commented on GitHub (Mar 24, 2019): Yes, the `ClientFuture` is confusing, there are examples of it's usage, though: https://docs.rs/trust-dns/0.16.0-alpha.2/trust_dns/#async-usage. The `ClientFuture` would not require a separate thread, but `SyncClient` would. I'd like to simplify the Client if possible. It's been a little while since I've looked at trying to do that.
Author
Owner

@jgillich commented on GitHub (Mar 24, 2019):

I tried that but it's also panicking with the message from above.

<!-- gh-comment-id:476005000 --> @jgillich commented on GitHub (Mar 24, 2019): I tried that but it's also panicking with the message from above.
Author
Owner

@bluejekyll commented on GitHub (Mar 24, 2019):

Do you have a link to code? If I find some time, I could take a look and see what's going on?

<!-- gh-comment-id:476005146 --> @bluejekyll commented on GitHub (Mar 24, 2019): Do you have a link to code? If I find some time, I could take a look and see what's going on?
Author
Owner

@jgillich commented on GitHub (Mar 24, 2019):

Sure, here's an example: https://github.com/jgillich/clientfuture

To trigger the field, do

curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ dns }" }' http://localhost:8080/graphql

The synch wrapper is in juniper_warp: https://github.com/graphql-rust/juniper/blob/master/juniper_warp/src/lib.rs#L181

<!-- gh-comment-id:476007140 --> @jgillich commented on GitHub (Mar 24, 2019): Sure, here's an example: https://github.com/jgillich/clientfuture To trigger the field, do curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ dns }" }' http://localhost:8080/graphql The synch wrapper is in juniper_warp: https://github.com/graphql-rust/juniper/blob/master/juniper_warp/src/lib.rs#L181
Author
Owner

@bluejekyll commented on GitHub (Mar 24, 2019):

Ok, so your code is spawning a new Runtime for tokio. Since this is already running, you don't want a new one. What you want is to get a handle to the Executor that's already registered. I think you might want to look at https://docs.rs/tokio-executor/0.1.7/tokio_executor/struct.DefaultExecutor.html which has a current, and would allow you to get at the currently registered executor.

<!-- gh-comment-id:476007874 --> @bluejekyll commented on GitHub (Mar 24, 2019): Ok, so your code is spawning a new Runtime for tokio. Since this is already running, you don't want a new one. What you want is to get a handle to the Executor that's already registered. I think you might want to look at https://docs.rs/tokio-executor/0.1.7/tokio_executor/struct.DefaultExecutor.html which has a current, and would allow you to get at the currently registered executor.
Author
Owner

@jgillich commented on GitHub (Mar 24, 2019):

Woo yes, that's it, thank you!

For future reference, this works:

let mut exec = tokio_executor::DefaultExecutor::current();

let stream = UdpClientStream::new(([8,8,8,8], 53).into());
let (bg, mut client) = ClientFuture::connect(stream);
exec.spawn(Box::new(bg)).unwrap();

let query = client.query(Name::from_str("www.example.com.").unwrap(), DNSClass::IN, RecordType::A);

let (tx, rx) = futures::sync::oneshot::channel();
exec.spawn(Box::new(query.then(move |r| tx.send(r).map_err(|_| unreachable!())))).unwrap();
let response = rx.wait().unwrap().unwrap();

if let &RData::A(addr) = response.answers()[0].rdata() {
    assert_eq!(addr, Ipv4Addr::new(93, 184, 216, 34));
}
<!-- gh-comment-id:476010864 --> @jgillich commented on GitHub (Mar 24, 2019): Woo yes, that's it, thank you! For future reference, this works: ```rust let mut exec = tokio_executor::DefaultExecutor::current(); let stream = UdpClientStream::new(([8,8,8,8], 53).into()); let (bg, mut client) = ClientFuture::connect(stream); exec.spawn(Box::new(bg)).unwrap(); let query = client.query(Name::from_str("www.example.com.").unwrap(), DNSClass::IN, RecordType::A); let (tx, rx) = futures::sync::oneshot::channel(); exec.spawn(Box::new(query.then(move |r| tx.send(r).map_err(|_| unreachable!())))).unwrap(); let response = rx.wait().unwrap().unwrap(); if let &RData::A(addr) = response.answers()[0].rdata() { assert_eq!(addr, Ipv4Addr::new(93, 184, 216, 34)); } ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#278
No description provided.