1

I am write an app using boost:asio.

I have a single io_serice::run() thread, and many worker threads. All the worker threads may send msg at any time.

Here is how I implement the send_msg().

// Note: send_msg() could be called from any thread.
// 'msg' must be 'malloc'ed, and its owner ship will be transfered  to '_send_q'
//
//  NetLibConnection has base classes of tcp::socket and  boost::enable_shared_from_this
void NetLibConnection::send_msg(PlainNetLibMsg*  msg)
{
    AutoLocker __dummy(this->_lock_4_send_q);    // _lock_4_send_q is a 'mutex'

    bool write_in_progress = ! this->_send_q.empty(); // _send_q is std::deque<PlainNetLibMsg* >, 
                                           // the 'send_q' mechansim is learned from  boost_asio_example/cpp03/chat  
    this->_send_q.push_back(msg);
    if (write_in_progress)
    {
        return;
    }

    this->get_io_service().post(      // queue the 'send operation' to a singlton io_serivce::run() thread

        boost::bind(&NetLibConnection::async_send_front_of_q
                , boost::dynamic_pointer_cast<NetLibConnection>(shared_from_this())
        ) 
    );
      
}

void NetLibConnection::async_send_front_of_q()
{
    boost::asio::async_write(*this 
        , boost::asio::buffer( this->_send_q.front() , _send_q.front()->header.DataSize + sizeof(NetLibChunkHeader) )
        , this->_strand.wrap(  // this great post   https://stackoverflow.com/questions/12794107/why-do-i-need-strand-per-connection-when-using-boostasio/
                              // convinced me that I should use strand along with  Connection
            boost::bind( &NetLibConnection::handle_send
                        , boost::dynamic_pointer_cast<NetLibConnection>(shared_from_this()) 
                        , boost::asio::placeholders::error
            )
        )
    );
}

The code works fine. But I am not satisfied with its verbosity. I feel the senq_q acts as the same role of strand. Since

  1. all real async_write call happen in a single io_service::run() thread
  2. all real async_write are queued one-by-one via the send_q

Do I still need the strand?

sehe
  • 374,641
  • 47
  • 450
  • 633
grizzlybears
  • 492
  • 2
  • 9
  • 1
    You are correct. All you need to do is post the write. You don't need your own queue as well. – user207421 Aug 17 '20 at 04:27
  • 2
    In C and C++, names with `__` or starting with `_` + uppercase letter are reserved for the implementation, so `__dummy` is an illegal name for a variable in your application. – Erlkoenig Aug 17 '20 at 08:29
  • 1
    In general I think the question "Am I paranoid with XXX" reduces to "Am I paranoid", so that would be good for https://psychology.stackexchange.com/ :) – sehe Aug 17 '20 at 16:20

1 Answers1

2

Yes, indeed. The documentation details this here:

Threads And Boost Asio

By only calling io_service::run() from a single thread, the user's code can avoid the development complexity associated with synchronisation. For example, a library user can implement scalable servers that are single-threaded (from the user's point of view).

Thinking a bit more broadly, your scenario is the simplest form of having a single logical strand. There are other ways in which you can maintain logical strands (by chaining handlers), see this most excellent answer on the subject: Why do I need strand per connection when using boost::asio?

sehe
  • 374,641
  • 47
  • 450
  • 633