2

Hyper has the following example of a Handler that implements Sync:

use std::sync::Mutex;
use std::sync::mpsc::{channel, Sender};
use hyper::server::{Handler, Server, Request, Response};

struct SenderHandler {
    sender: Mutex<Sender<&'static str>>
}

impl Handler for SenderHandler {
    fn handle(&self, req: Request, res: Response) {
        self.sender.lock().unwrap().send("start").unwrap();
    }
}

and states that a Handler must implement Sync, since Handler can be called from different threads.

To me, this sounds like an unnecessary performance penalty. What I would prefer would be to setup one SenderHandler per thread, each being independent, which would remove the requirement on implementing Sync.

Have I misunderstood Hyper, Rust's type-system or is this not possible?

PureW
  • 4,568
  • 3
  • 19
  • 27
  • As in the supplied example, I would love to remove the `Mutex` protecting the `Sender` since `Sender` is already designed for lock-free intrathread message-passing. All it requires is a `clone` per thread. – PureW Oct 15 '16 at 14:53
  • After a few minutes of thinking about it, I tend to agree with you. Cloning handlers would be more powerful, because you were also able to simulate the old behavior by having a single `Arc` in your handler. It's not that easy the other way around... (as far as I can see right now) – Lukas Kalbertodt Oct 15 '16 at 15:02

2 Answers2

4

This is a papercut issue.

Sync is trivially implemented for any stateless handler; having a Clone bound instead would mean having multiple concurrent states evolving independently from one another (with requests being dispatched randomly). I am not even sure there is a guarantee that while waiting during the handling of a request the same thread gets invoked with the result.

By using Sync, the author forces you to think about what it means to share state across queries (or even intra-query).


As in the supplied example, I would love to remove the Mutex protecting the Sender since Sender is already designed for lock-free intrathread message-passing. All it requires is a clone per thread.

You can have Sender implement Sync if it is (think it through, obviously), and then remove the Mutex.

It also possible, though not too elegant, to have one instance per thread using the thread_local! macro, and then a trampoline handler dispatch the work to the thread-local Sender upon invocation.


Finally, note that hyper is under redesign at the moment; it should move to Future which may or may not cause some reconsideration here.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • Thanks for this, I was not aware different threads could be handling various parts of processing of one request, is this always true or does it follow from hyper's design? – PureW Oct 15 '16 at 15:21
  • Also, how would I have `Sender` implement `Sync` and what does this entail? I thought the very design of `Sender` prohibits `Sync` and forces user to clone instead. – PureW Oct 15 '16 at 15:24
  • @PureW: which `Sender`? I thought this was a type in your code (since there is no qualification), if it's not then you cannot implement `Sync` for it. If it is, then you can implement `Sync` for it, using `unsafe`, if you know what you are doing; you might want to ask on reddit, the users forum or IRC though if this is all alien to you. – Matthieu M. Oct 15 '16 at 15:31
  • Sorry, the `Sender` is the one in `mpsc`. The full example has a `use std::sync::mpsc::Sender;` – PureW Oct 15 '16 at 15:36
3

Ok, so this actually seems impossible at the moment in Hyper. It is discussed in issue 248 and the developer prefers Sync over Clone:

We talked through this on IRC. Simple synopsis is that if it were Clone, users could easily have state on their handler that they think is modified with each request, but instead, it would have been cloned several times and not modify what they hoped. Instead, it's better force the user to make sure their state is synchronized.

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
PureW
  • 4,568
  • 3
  • 19
  • 27