Communicating bidirectionally with Rocket in another thread


#1

In my main thread, I create a Manager, which itself spins up another process and abstracts the interaction with it. While most of the time commands are being performed on the Manager, I need to also get data out of it. For example, manager.take() will perform a camera “take” on the main bus, while manager.get_bus_status() will return a struct describing the current state of the bus. A stripped down example is here.

To send commands from the Rocket route to the main thread, I passed Rocket a SyncChannel to manage. Then, the route can send messages which are processed in the main thread. This works really well and also allows me to provide rudimentary console input in yet another thread.

However, I also need to be able to receive data in the route. I initially thought I could create another (sender, receiver) pair, this time specifically for the main thread to send responses to the Rocket route thread, but a Receiver does not implement Sync, which the Rocket .manage(...) call requires.

An option posted by “athrowaway3z” was to pass a sender back through the channel itself. I can’t figure out if that’s elegant or a little crazy, but it seems to work. I modified my code to be something like this, and I’m able to pass messages back. I don’t need to use SyncChannel again in the route, so at least there’s a bit less overhead in the creation of the next channel.

In some ways, this feels really nice because it means there’s no potential for any kind of data mix-up to multiple clients since the channel is created specifically for a single HTTP request. If it’s not fulfilled that request hangs, but everything else can continue working.

Lines 24-26 seem like a lot of boilerplate that will get repeated over and over, but I may be able to wrap those in a single call. I also feel like it might not be the most performant, though that probably won’t be an issue in my application.

Is there a language feature I’m missing to streamline this scenario?

Thanks,
Louis


#2

You could “upgrade” the Receiver to be Sync by wrapping it in a Mutex. That would avoid channel allocations on each request, as you have now. I think Rocket is singlethreaded currently (right?) so there won’t be contention on the mutex.

Yeah, this approach of sending a “self addressed return channel” is useful and a nice approach to turn 1-way communication into a bidirectional conversation. Allocating a new channel per request might be problematic though.

You probably also want to make sure you don’t freeze up Rocket by blocking on the channel in the route handler. But maybe that’s ok for your case.

Ultimately, what you likely want is an async communication channel between Rocket and whatever background/worker thread(s) service the Rocket calls. But that will require Rocket to support async first.


#3

I had actually thought Rocket was multithreading (and that’s why it forced me to use the SyncChannel in the first place), but I think I had a misconception there. It looks like it’s not according to the readme. Because of this, I think if I block in the Rocket route, that freezes up the server.

An async communication channel between Rocket and the rest of the application sounds exactly like what I want.

For my simple implementation currently, the self-addressed return channel might be good enough. Alternatively, I’m looking at using a separate returning data strategy. Since most of the time a command will impact the data that multiple clients see, I’m thinking of using web-sockets to push data to each client after each client executes a command. This keeps the rocket route 1 directional for the most part, and avoids lots of polling, since each client needs to update in real-time.