Application state in a Tokio Service. What's the idiomatic way?


#1

I am writing a Modus-TCP server as my initiation into this. Modes is a very simple protocol: you have a block of 64*1024 16 bit numbers. You write to them. You read them. End of story.

So far my server does all the read operations. Now when it came to doing the writes, I got caught in a bind. The Service trait (that is, the struct that has it) is the top level of the Tokio protocol stay… And the call() function in that trait has a non-mutable reference to self.

Which means if I put a mutable member in the struct, I don’t get to write to it in call().

So should I spawn a thread, with two channels, and have the Service::call() method pass in the request into one channel, get the response from the other, and let that thread do the gets and sets?


#2

Based on this example from tokio you should probably use interior mutability: https://github.com/tokio-rs/tokio-proto/blob/master/src/util/client_proxy.rs#L23


#3

Thank you, though I am far too much a newbie to know how to do that.

Here’s what I have for the moment, I’m main():

    let (in_,req_out)=channel::<ModbusRequestPDU>();
let (resp_in,out)=channel::<ModbusResponsePDU>();
//let mut block = BlankRegisters::new();
thread::spawn(move ||{
    let mut block = BlankRegisters::new();
    let tx = resp_in.clone();
    loop {
        println!("Loop");
        let req = req_out.recv().unwrap();
        println!("req {:?}",req);
        let resp = block.call(req);
        println!("resp {:?}",resp);
        tx.send(
            resp).unwrap();
    }
});

TcpServer::new(ModbusProto, args.flag_addr.parse().unwrap())
    .serve(|| Ok(ModbusService::new(
        in_,//.clone(),
        out)));

#4

I got to figure our the right way to share these channels to get this to run.


#5

I think what I’m talking about will be much simpler than your code.

Interior mutability means a type like RefCell - RefCell has a method called borrow_mut that lets you get a mutable reference to the value inside it, even when you only have an immutable reference to the RefCell.

You put the component of your Service that needs to mutate in a RefCell, and then call borrow_mut on it when you need to mutate it. Its much simpler than creating a new thread and such.

Here’s the docs for RefCell: https://doc.rust-lang.org/std/cell/struct.RefCell.html