mercredi 3 mai 2017

C++: Ensuring that only one instance of a function is running?

I'm just getting into concurrent programming. Most probably my issue is very common, but since I can't find a good name for it, I can't google it.

I have a C++ UWP application where I try to apply MVVM pattern, but I guess that the pattern or even being UWP is not relevant.

First, I have a service interface that exposes an operation:

struct IService
{
    virtual task<int> Operation() = 0;
};

Of course, I provide a concrete implementation, but it is not relevant for this discussion. The operation is potentially long-running: it makes an HTTP request.

Then I have a class that uses the service (again, irrelevant details omitted):

class ViewModel
{
    unique_ptr<IService> service;
public:
    task<void> Refresh();
};

I use coroutines:

task<void> ViewModel::Refresh()
{
    auto result = co_await service->Operation();
    // use result to update UI
}

The Refresh function is invoked on timer every minute, or in response to a user request. What I want is: if a Refresh operation is already in progress when a new one is started or requested, then abandon the second one and just wait for the first one to finish (or time out). In other words, I don't want to queue all the calls to Refresh - if a call is already in progress, I prefer to skip a call until the next timer tick.

My attempt (probably very naive) was:

mutex refresh;
task<void> ViewModel::Refresh()
{
    unique_lock<mutex> lock(refresh, try_to_lock);
    if (!lock)
    {
        lock.release();
        co_return;
    }
    auto result = co_await service->Operation();
    // use result to update UI
}

But of course an assertion fails: unlock of unowned mutex. I guess that the problem is the unlock of mutex by unique_lock destructor, which happens in the continuation of the coroutine and on a different thread (other than the one it was originally locked on).

Using Visual C++ 2017.

Aucun commentaire:

Enregistrer un commentaire