简体   繁体   中英

C++ std::timed_mutex has recursive behaviour

i have a problem. i want to use a mutex for my program. so what happens is this: i am constructing an object that holds a std::timed_mutex. on creation this object locks the mutex because it should be unlocked later on. the same thread that created the mutex should now wait for that mutex while some other thread does work in the background. joining the thread is no option.

class A{
    std::timed_mutex mutex;
    A(){
        mutex.lock();
    }

    bool waitForIt(int timeout){
        if(mutex.try_lock_for(std::chrono::milliseconds(timeout))){
            mutex.unlock();
            return true;
        }else{
            return false;
        }
    }
}

when calling waitForIt from the same thread the program just goes through and instantly gets a false, totally ignoring the timeout.(yes its intended to unlock the mutex afterwards. it should mime something like an event so every thread waiting gets through)

so in the documentation it says this mutex has a nonrecursive behaviour. but testing revealed that for example i can use the .lock() multiple times from the same thread without getting blocked. i also can use try_lock_for multiple times and every time get true!!! if i once use lock before the try_lock_fors i always get false. sadly i need something that also blocks the same thread that locked the mutex. and i have no idea what to use. im programming on linux btw. so maybe there is a native solution?

also i didnt find a semaphore in the std libs.i could use that instead of the mutex. using my own implementation would be possible but i dont know how to make my own semaphore. any ideas?

as people dont seems to understand that its not that simple:

class IObservable : public IInterface{
private:
    std::list<std::shared_ptr<IObserver>> observers;
public:
    virtual ~IObservable(){}

    void AddObserver(std::shared_ptr<IObserver> observer);
    void RemoveObserver(std::shared_ptr<IObserver> observer);
    void ClearObservers();
    void TellCompleted(bool wasCanceled = false, std::shared_ptr<void> status = 0);
    TYPEIDHASHFUNC(IObservable)
};

IObservable is the thing that threads can add observers to. the thing deriving from IObservable calls the method TellCompleted at the end of its actions.

class IObserver : public IInterface{
public:
    virtual ~IObserver(){}

    virtual CompleteResult Complete(bool wasCanceled, std::shared_ptr<void> status) = 0;
    virtual bool WaitForCompletion(int timeoutInMs) = 0;
    virtual bool IsCompleted() const = 0;
    virtual bool WasCanceled() const = 0;
    virtual std::shared_ptr<void> GetStatus() const = 0;
    virtual void Reset() = 0;
    TYPEIDHASHFUNC(IObserver)
};

IObserver are the observer that can be added to a IObservable. if IObservable completes the method Complete gets called on each observer that was added to the observable

class BasicObserver : public IObserver{
private:
    bool isCompleted;
    bool wasCanceled;
    CompleteResult completeResult;
    std::shared_ptr<void> status;
    std::timed_mutex mutex;
public:
    BasicObserver(CompleteResult completeResult);
    ~BasicObserver();

    CompleteResult Complete(bool wasCanceled, std::shared_ptr<void> status);
    bool WaitForCompletion(int timeoutInMs);
    bool IsCompleted() const;
    bool WasCanceled() const;
    std::shared_ptr<void> GetStatus() const;
    void Reset();
    TYPEIDHASHFUNC(BasicObserver)
};

this is one implementation of an observer. it holds the mutex and implements the WaitForCompletion with the timeout. WaitForCompletion should block. when complete is being called its mutex should be unlocked. when the timeout runs WaitForCompletion returns false

BasicObserver::BasicObserver(CompleteResult completeResult):
    isCompleted(false),
    wasCanceled(false),
    completeResult(completeResult)
{
    std::thread createThread([this]{
        this->mutex.lock();
    });
    createThread.join();
}

BasicObserver::~BasicObserver(){
}

CompleteResult BasicObserver::Complete(bool wasCanceled, std::shared_ptr<void> status){
    this->wasCanceled = wasCanceled;
    this->status = status;
    isCompleted = true;
    mutex.unlock();
    return completeResult;
}

bool BasicObserver::WaitForCompletion(int timeoutInMs){
    std::chrono::milliseconds time(timeoutInMs);
    if(mutex.try_lock_for(time)){
        mutex.unlock();
        return true;
    }else{
        return false;
    }
}

bool BasicObserver::IsCompleted() const{
    return isCompleted;
}

bool BasicObserver::WasCanceled() const{
    return wasCanceled;
}

std::shared_ptr<void> BasicObserver::GetStatus() const{
    return status;
}

void BasicObserver::Reset(){
    isCompleted = false;
    wasCanceled = false;
    status = 0;
    std::chrono::milliseconds time(250);
    mutex.try_lock_for(time); //if this fails it might be already resetted
}

//edit: solved by using a semaphore instead (sem_t from semaphore.h)

您可以使用condation_variable ,特别是wait_untilwait_for

I would consider a redesign of your locking structure. Why not have the lock held by the main thread, and when event x happens you unlock it. If you need to block for a duration I would just make the thread sleep. Have all working threads blocking on the mutex trying to acquire the lock, if they need to run concurrently have them immediately release the lock once they acquire it.

maybe use a second mutex to emulate event x.

i want to setup the lock from thread 1 then start a thread 2 that does something (wait for input from hardware in this case) and then wait for the mutex in thread 1. thread 2 then unlocks the mutex when i press the switch on the hardware. im using some kind of observer pattern. so i have something observable where i add an observer to(in this case the class A is the observer). at some point the observable tells all added observers that it completed its task and thus unlocks the mutex. as we have hardware here it could be that the hardware locks up or a sensor doesnt work. so i NEED a timeout. – fredlllll 3 mins ago

EDIT - Maybe this would work?

Hold lock in thread 1, after thread 2 gets input block on that lock. Have thread 1 release the lock after timeout duration, maybe sleep a little to allow threads through then acquire the lock again. Have thread 2 release lock 1 then begin blocking on a second mutex after acquiring mutex 1, have hardware switch unlock mutex 2 which causes thread 2 to lock mutex2 then unlock mutex 2. Have hardware switch acquire mutex 2 again.

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