Ensuring the operation of only one instance of a function?

I just enter parallel programming. Most likely, my problem is very common, but since I can’t find a good name for her, I can’t use it.

I have a C ++ UWP application where I try to apply the MVVM pattern, but I assume that the pattern or even UWP does not matter.

Firstly, I have a service interface that provides an operation:

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

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

Then I have a class that uses the service (again, irrelevant data is 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 update function is activated by the timer every minute or in response to a user request. I want: if the update operation is already running when a new one is starting or is being requested, open the second one and wait until the first one finishes (or time out). In other words, I don't want to queue all Refresh calls - if the call is already in progress, I prefer to skip the call until the next timer.

My attempt (possibly very naive):

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

Edit after the original post: I commented out the line in the code snippet above, as this does not make any difference. The problem remains the same.

But, of course, the statement fails: unlock of unowned mutex . I assume that the problem is the unlock of mutex from the unique_lock destructor, which occurs in the continuation of the coroutine and in another thread (except the one that was originally blocked).

Using Visual C ++ 2017.

+5
source share
1 answer

use std::atomic_bool :

 std::atomic_bool isRunning = false; if (isRunning.exchange(true, std::memory_order_acq_rel) == false){ try{ auto result = co_await Refresh(); isRunning.store(false, std::memory_order_release); //use result } catch(...){ isRunning.store(false, std::memory_order_release); throw; } } 

Two possible improvements: wrap isRunning.store in the RAII class and use std::shared_ptr<std::atomic_bool> if the lifetime, if the atomic_bool area atomic_bool limited.

+1
source

All Articles