Skip to content

Lack of Send and Sync on Box<dyn Primitive> makes library very difficult to use in async code #653

@edvardfagerholmsc

Description

@edvardfagerholmsc

The lack of Send and Sync make the library almost unusable. I've had to resort to weird thread local tricks like the one below:

mod hello {
    tonic::include_proto!("hello");
}

use std::cell::Cell;

use hello::{HelloRequest,HelloResponse};
use hello::hello_service_server::{HelloService,HelloServiceServer};
use tonic::transport::Server;
use tonic::{Request, Response, Status};

// This would be passed through the environment to the container.
static KEY: &str = "my encryption key";

thread_local! {
    static DECRYPTER: Cell<Option<Box<dyn tink_core::Aead>>> = Cell::new(None)
}

fn parse_encryption_key(key: &str) -> Option<Box<dyn tink_core::Aead>> {
    if key != "" {
        let mut key_reader = tink_core::keyset::BinaryReader::new(key.as_bytes());
        match tink_core::keyset::insecure::read(&mut key_reader) {
            Ok(h) => Some(tink_aead::new(&h).unwrap()),
            Err(e) => None        }
    } else {
        None
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "[::1]:50051";
    let service = MyServer { };

    Server::builder()
        .add_service(HelloServiceServer::new(service))
        .serve(addr.parse().unwrap())
        .await?;

    Ok(())
}

struct MyServer {
}

#[tonic::async_trait]
impl HelloService for MyServer {
    async fn hello(
        &self,
        req: Request<HelloRequest>,
    ) -> Result<Response<HelloResponse>, Status> {
        DECRYPTER.with(|c| {
            let decrypter = match c.take() {
                Some(d) => d,
                None => parse_encryption_key(KEY).unwrap()
            };
            c.set(Some(decrypter));
        });

        Ok(Response::new(HelloResponse::default()))
    }
}

What I'd like to be able to do is just:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let decrypter = parse_encryption_key(KEY).unwrap();
    let addr = "[::1]:50051";
    let service = MyServer { decrypter };

    Server::builder()
        .add_service(HelloServiceServer::new(service))
        .serve(addr.parse().unwrap())
        .await?;

    Ok(())
}

struct MyServer {
    decrypter: ...
}

The types should be such that this is easily supported. Most DB client libraries etc. play well together with Tonic. Not having Send and Sync also makes it impossible to hold a reference across await points.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions