jeudi 4 mars 2021

Design pattern to ensure on_close() is called once after all async r/w's are finished?

This question is asked from the context of Boost ASIO (C++).

Say you are using a library to do some async i/o on a socket, where:

  • you are always waiting to receive data
  • you occasionally send some data

Since you are always waiting to receive data (e.g. you trigger another async_read() from your completion handler), at any given time, you will either have:

  1. an async read operation in progress
  2. an async read operation in progress and an async write operation in progress

Now say you wanted to call some other function, on_close(), when the connection closes. In Boost ASIO, a connection error or cancel() will cause any oustanding async reads/writes to give an error to your completion handler. But there is no guarantee whether you are in scenario 1. or 2., nor is there a guarantee that the write will error before the read or vice versa. So to implement this, I can only imagine adding two variables called is_reading and is_writing which are set to true by async_read() and async_write() respectively, and set to false by the completion handlers. Then, from either completion handler, when there is an error and I think the connection may be closing, I would check if there is still an async operation in the opposite direction, and call on_close() if not.

The code, more or less:

atomic_bool is_writing;
atomic_bool is_reading;

...

void read_callback(error_code& error, size_t bytes_transferred)
{
  is_reading = false;

  if (error)
  {
    if (!is_writing) on_close();
  }
  else
  {
    process_data(bytes_transferred);
  
    async_read(BUF_SIZE);  // this will set is_reading to true
  }
}

void write_callback(error_code& error, size_t bytes_transferred)
{
  is_writing = false;

  if (error)
  {
    if (!is_reading) on_close();
  }
}

Assume that this is a single-threaded app, but the thread is handling multiple sockets so you can't just let the thread end.

Is there a better way to design this? To make sure on_close() is called after the last async operation finishes?

Aucun commentaire:

Enregistrer un commentaire