I've got a resource class being guarded by a std::mutex
, where any methods accessing it must be locked and only executed by a single thread. This works fine if individual methods are called separately, but now I've got a requirement for batching those methods together. In this case, the mutex needs to be locked only once and those methods must not lock that mutex again(otherwise will end up in deadlock).
class Resource {
mutable std::mutex mtx;
int value = 10;
void handleOpStart() const {/* Activate batch mode */ std::cout << "HandleOp activate!"<< std::endl; }
void handleOpEnd() const {/* Deactivate batch mode */ std::cout << "HandleOp deactivate!"<< std::endl; }
public:
int read() const {
std::lock_guard<std::mutex> lock(mtx);
handleOpStart(); auto result = value 10; handleOpEnd();
return result;
}
void write(int val) {
std::lock_guard<std::mutex> lock(mtx);
handleOpStart(); value = val; handleOpEnd();
}
template<typename Fn>
void batch(Fn fn) {
std::lock_guard<std::mutex> lock(mtx);
handleOpStart();
fn();
handleOpEnd();
}
void print() const {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Value: " << value << std::endl;
}
};
int main() {
Resource r;
r.print();
r.write(r.read());
r.print();
r.batch([&] {
// will cause deadlock
auto someVal = r.read();
auto someOtherVal = 10 someVal;
r.write(r.read() someOtherVal);
});
}
Now I can't remove mutex from individual methods because they can be called separately from outside batch context. I can't keep the mutex because they can also be called inside it. If I keep a boolean variable inside class that deactivates mutex on the batch
function call, then it also deactivates the other individual functions being called from other threads, defeating the purpose again.
One solution would be to write down a non-mutex set of all the read/write functions and only call them within batch context, not outside. But the number of such resources accessing functions is large, thus increasing maintenance and might introduce bugs.
Looking for alternatives to solve this problem.
CodePudding user response:
This is exactly what std::recursive_mutex
is for, it can be locked multiple times from the same thread. It will only fully unlock when the number of unlock calls matches the number of lock calls.