std::async, and copies – gotcha!

Disclaimer: I love C++11! It’s the best thing that happened to C++ since I’m using it (mid-90’s). I’ve been hoping for lambda‘s in C++ since I discovered and started using Lisp. Elisions and move semantics – together with unique and shared pointers – pretty much free you from thinking about memory management! It’s just amazing! Check out “Going Native” talks on YouTube, incl. amazing talks by the likes of Stroustrup, Sutter, Lavavej, et al.

If you don’t want to read my rant, here’s the point – function objects are objects – they can get copied, unless you specify otherwise, so beware that you’re working on a copy, or pass std::ref.

This post is more of a note to self than learning/reference point – but since people learn mainly through errors, it might be helpful to someone else as well. It’s the classical RTFM.

C++11’s future, async, promise, et al. are just wonderful. Combined with lambda‘s and functions-as-objects (via std::function template), they allow for a much cleaner interface design. [how clean is my use of these is a different question :]

I’ve started with a simple problem – create multithreaded named-pipe server for Windows service. With a clean interface.

So, I started with a simple class:

using std::string;
using std::function;

class pipe_server
{
public:
    //...
    void add_handler(string signature, function<string(string)>);
    void operator()();
    void stop();
};

Most of the interface is omitted; handler is simply function object that accepts string (message received from client) and returns string – response. The operator() is provided for use as a thread function.

The pipe_server class then takes care of the pipe opening, accepting connections, receiving message from the client, starting a thread to run handler in, and sending the response to client. Messages have fixed format, with signature as head of the message, and that’s the signature parameter to add_handler(). It’s not that generic, but just enough for what I needed.

Now, a simple minimal test:

int main()
{
    pipe_server ps;
    ps.add_handler("test", [](string) -> string { return "passed"; });
    ps.add_handler("quit", [&](string) mutable -> string { ps.stop(); return "quitting"; });
    auto pst = std::async(ps);
    pst.wait();
    return 0;
}

Now, if server receives “test” message, it responds with “passed“. If it receives “quit“, it does respond with “quitting“, but it does not actually stop!

The problem of course is – std::async creates a copy of the pipe_server object, and runs that copy in a new thread. This is not new to C++11, objects get copied all the time since ever.

Now the cool part – C++11 to the rescue!

To detect this problem, and since I didn’t want pipe_server to be copied anyways, adding

pipe_server(const pipe_server&) = delete;
pipe_server& operator=(const pipe_server&) = delete;

to the class interface broke the program. VS2013 says: file <type_trails>, line 1545, “attempting to reference a deleted function”. Not a helpful message. But, I knew now what to look for. [I should test this in clang and gcc to see if their error messages are more helpful – it’s nice to know I broke the _Decay_copy template, but I didn’t write that code, I want to know how I broke it :]

It’s obvious that the object gets copied somewhere – in the lambda or in std::async() call. In lambda, hardly – it captures all by reference and is marked mutable. I think that should be enough.

So it’s the std::async() call that copies the object (it was obvious to me by now), but what to do? C++11 to the rescue again!

auto pst = std::async(std::ref(ps));

Done!

Leave a comment