Skip to content

Full multithreading safety with strandsΒ #872

@aaronalbers

Description

@aaronalbers

In my own ASIO based network libraries I spent days trying to track down a race condition and finally came across the following post:
https://stackoverflow.com/questions/36016135/asioasync-write-and-strand

That post informed me that my use of strands was only protecting the callback and not the invocation of the ASIO object methods. I see that same misuse of strands in websocketpp.

Example:

This was my initial understanding on how to use strands as you only need to wrap the callback:

if (config::enable_multithreading) {
  lib::asio::async_connect(
      tcon->get_raw_socket(),
      iterator,
      tcon->get_strand()->wrap(lib::bind(
          &type::handle_connect,
          this,
          tcon,
          con_timer,
          callback,
          lib::placeholders::_1
      ))
  );
} else {
  lib::asio::async_connect(
      tcon->get_raw_socket(),
      iterator,
      lib::bind(
          &type::handle_connect,
          this,
          tcon,
          con_timer,
          callback,
          lib::placeholders::_1
      )
  );
}

When in reality you also need to protect the call to async_connect() as it calls socket_.async_connect() and socket_.close() under the hood. Therefore in order to be safe we would actually need to do something like:

if (config::enable_multithreading) {
    lib::asio::dispatch(tcon->get_strand()->wrap([this, tcon, iterator, con_timer, callback]{
        lib::asio::async_connect(
            tcon->get_raw_socket(),
            iterator,
            tcon->get_strand()->wrap(lib::bind(
                &type::handle_connect,
                this,
                tcon,
                con_timer,
                callback,
                lib::placeholders::_1
            ))
        );
    }));
} else {
    lib::asio::async_connect(
        tcon->get_raw_socket(),
        iterator,
        lib::bind(
            &type::handle_connect,
            this,
            tcon,
            con_timer,
            callback,
            lib::placeholders::_1
        )
    );
}

This of course would have to be done for all ASIO objects (ie sockets, timers, etc)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions