简体   繁体   中英

How to protect resource while sharing mutex to outside code without deadlock?

I have a class that protects a resource from concurrent access like -

class Protector{ Resource R; shared_mutex _m; 
    ... some 5-6 methods that access resource R like
    void safeResourceOperation(){
        lock_guard lock(_m);
        // do Operation on R
    }
} 

Now for a specific usecase, there's a need to share this mutex with outside world. Instead of making this mutex public and risk leaving it locked by someone, I provided a method that takes lambda and calls it under lock guard, for eg. -

class Protector{
    template<typename Fn> void lockedOp(Fn f) {
        lock_guard lock(_m);
        f();
    }
}

Now the problem arises when some user of this class tries to do this -

Protector p;
p.lockedOp([&]{
    // does something and then
    p.safeResourceOperation()  // <-- results in deadlock
}); 

To solve this, I thought of keeping a boolean variable inside lockedOp that disables mutex on all safeFunctions while it's being called -

template<typename Fn> void lockedOp(Fn f) {
    lock_guard lock(_m);
    atomic_bypass_mutex = true;
    f();
    atomic_bypass_mutex = false;
}

void safeResourceOperation(){
    if(!atomic_bypass_mutex) {
        lock_guard lock(_m);
        doOperationsOnResource();
    } else {
        doOperationsOnResource();
    }
}

But there would be a problem when lockedOp is being called in thread1 and sets bypass = True and some other thread2 calls p.safeResourceOperation() (not inside lockedOp ). It will also get bypassed mutex and thread-safety gets compromised.

How should I solve this problem? One alternative that I see is keeping thread-id's and their thread-local boolean variables in a map and only allowing bypasses for the same thread-id. But I've a feeling this might be a bit complex solution.

Personally I find your usage of protector a bit strange. Your are mixing concepts by putting business logic inside of your protector, specifically the safeResourceOperation method.

Protector p;
p.lockedOp([&]{
    // does something and then
    p.safeResourceOperation();  // <-- results in deadlock
}); 

I would expect a better design would be to extract the protector logic, so you'll get something like:

Protector<Resource> p;
p.lockedOp([&](auto &resource){
    // does something and then
    resource.safeResourceOperation();
}); 

If you really care about non-locked access this class can also provide an extra method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM