boost::asio need for asynchronous locking mechanism

Viewed 31

I need to develop an api, that synchronously allows access to a shared asynchrounous resource. For simplicity lets assume a boolean resource resource, that might take some time to switch its state. It is currently set to false and two clients want to set it to true concurrently. I would like the second client to see that a operation is in progres, and asynchronously wait for its completion. Is there a possibility to somehow add a completion handler to a running asynchronous operation?

enter image description here

What I am currently trying to do, is creating one of asio's timers, set its expire time to a very large value and cancel it when the operation finishes. Any clients get notified through the timer cancel.

void set_true() {
    mytimer.expires_after(<a long time>);
    if ( !resource.busy )
        resource.busy = true;
        resource.async_set_true( [](){
                resource.busy = false
                mytimer.cancel();
        });
    mytimer.async_wait(do_return);
}

void do_return(...) {
    ...
}

While I think, that this will work, it feels like

  1. this is a misuse of the timer classes, since I am not even using their "timer" functionality
  2. with some kind of asynchronously awaitable lock, this task would be solved more naturally (combining the busy flag and the notifying into one primitive)

I searched for asynchronous locks in asio, but could not manage to find one. Yet this problem seems so fundamental to me, that I cannot believe, that there is no better solution to this, than the one I mentioned. Note that I don't want to use real locks and I don't need advice to make my code thread-safe, because the API is supposed to run single threaded.

Questions

  1. is there any asynchronous lock mechanism in asio?
  2. if there isn't, is there a reason why?
  3. is there an alternative way using strands to elegantly solve this problem?
1 Answers

Short answer, No, there isn't a locking mechanism. The correct approach is to use strands.

Longer answer:

In almost all cases, if you need to have a set of steps that occur serially, then the ASIO solution is to use strands. In your case, your two clients to submit a callback to the strand, which can look at the overall state, and take the correct operation.

If you want your client to be synchronous (i.e. wait for the operation to complete), the callback that is passed to the strand should use some sort of synchronization primitive, such as a promise, to signal completion.

So the client would create a promise, get the future via get_future, and pass the promise, along with any other information to the strand. Any number of clients could legally do this.

Then, the strand will execute all of the callbacks in some sequence, guaranteeing that only one callback is executing at a time. On completion, the promise could be completed, and then the next operation will occur. The clients can wait until their callback is complete.

Related