Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can SyncClient be used in a library context? #1

Open
calderonth opened this issue Jun 26, 2020 · 1 comment
Open

Can SyncClient be used in a library context? #1

calderonth opened this issue Jun 26, 2020 · 1 comment

Comments

@calderonth
Copy link

Hello,

I've been now trying to use your library to connect to an existing SunRPC service and I am able to do so using a standalone binary.
That's all good and well but in a more complex scenario, say a shared library handling the connection to the service, one will have to initialize a SyncClient for instance and then use it in various point of the code.

I have been unable to figure out a way to use a single instance of a SyncClient (as I need to connection to remain open I can't keep opening connections). I've explored using a shared state to create a context but I find myself quickly in a situation where the SyncClient does not implement Copy/Clone traits.

I was thinking of defining a configuration structure initialized when the library is loaded and that would create a connection to the service with a SyncClient that would be stored in the Config structure.

Do you have any thought on how to do that?

@jvff
Copy link
Owner

jvff commented Jun 26, 2020

I think there are a few solutions that could be used. The first would be to use the AsyncClient instead, since each RPC call method requires &self, meaning that it can be shared between threads. The downside is that you'd have to set-up your own async reactor (using an old version of Tokio). So it could be something like:

use tokio_core::reactor::{Core, Handle};

pub struct ClientHandle<'a> {
    client: &'a AsyncClient,
    reactor: Handle,
}

pub fn setup(address: IpAddr) {
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let client = AsyncClient::connect(address, &handle);

    let client1 = ClientHandle {
        client: &client,
        reactor: core.handle();
    };
    let client2 = ClientHandle {
        client: &client,
        reactor: core.handle();
    };
    // ...
    // And when you need to use them you could do something like:
    core.run(client2.rpc_method());
}

Another option would be to wrap the SyncClient in an Arc<Mutex<...>>. That makes it (cheaply) clonable, but the cost comes when needing to use the API, since you will need to lock the mutex while the call is being performed. So it could be something like:

pub fn example(address: IpAddr) {
    let client = Client::connect(address).unwrap();
    let sharable_client = Arc::new(Box::new(client));
    let client1 = sharable_client.clone();
    let client2 = sharable_client.clone();
    // ...

    // And then to use it:
    client2.lock().unwrap().rpc_method().unwrap();
}

I'll need to update this crate soon to use the new async/await syntax, and maybe I'll be able to improve this situation. Maybe be creating a ConcurrentSyncClient that has its methods using &self instead of &mut self.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants