1

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)

fredlllll
  • 75
  • 8
  • dont write your own semaphore... if you are on linux you have all of POSIX at your fingertips... there is also this related question: http://stackoverflow.com/questions/4792449/c0x-has-no-semaphores-how-to-synchronize-threads – Grady Player Feb 28 '14 at 00:17
  • posix semaphore http://www.csc.villanova.edu/~mdamian/threads/posixsem.html – Grady Player Feb 28 '14 at 00:18
  • i need a function to wait with a timeout. seems like the posix semaphore cant do this? – fredlllll Feb 28 '14 at 00:21
  • try_lock loop is the naive way to do it. Why would you want t non-recursive lock from the same thread, the only advantage is deadlock right? – Grady Player Feb 28 '14 at 00:25
  • 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 Feb 28 '14 at 00:32

2 Answers2

0

You could use a condation_variable, specifically wait_until or wait_for.

OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • but this uses a mutex internally right? that still doesnt work with the same thread does it? also it acts like an auto reset event while i need something miming a manual reset event. – fredlllll Feb 28 '14 at 00:23
  • Do you mean *condition_variable*? – Jay Elston Feb 08 '19 at 18:01
0

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.

Dave S
  • 3,378
  • 1
  • 20
  • 34
  • no redesign. definitely not. i dont even have access to the main thread in my software(from the class). the class above is a simplified version of what im doing. – fredlllll Feb 28 '14 at 00:34
  • would it work starting a new thread just for the creation of that mutex and joining this thread? – fredlllll Feb 28 '14 at 00:44
  • I can't see why not, mutexs are meant to be shared between threads and the new thread could lock and unlock it. Just be careful you don't have a two threads each holding 1 mutex trying to lock the other. – Dave S Feb 28 '14 at 00:48
  • `BasicObserver::BasicObserver(CompleteResult completeResult): isCompleted(false), wasCanceled(false), completeResult(completeResult) { std::thread createThread([this]{ this->mutex.lock(); }); createThread.join(); }` sorry but stackoverflow is a bitch and i cant format the code. but that would work right? /edit: it doesnt. program still runs over the try_lock_for as if it wasnt there – fredlllll Feb 28 '14 at 00:51
  • I'm not familiar with BasicObserver =\ It might work if joining the thread allows the mutex to be unlocked. otherwise you need to unlock it first. I just think a timed_mutex may be unnecessary in this case and you could do it between sleeping and simple mutex locks. timed_mutex isn't supposed to be used for causing a thread to block for x duration, just prevent threads from blocking indefinitely on locks they cannot acquire. – Dave S Feb 28 '14 at 00:56
  • i added some implementation details of basic observer and such. fact is that some thread that now tries to use the WaitForCompletion just rushes through ignoring the timeout in the wait. – fredlllll Feb 28 '14 at 01:04
  • Ok so if the observer holds a mutex, it shouldn't be blocking on that mutex, it should block on a second mutex that is unlocked when complete is called by another thread. then it can acquire that second mutex and return true or timeout on the second mutex and return false. – Dave S Feb 28 '14 at 01:05
  • what? could you maybe specify that more? maybe its because its 2am but i dont know what you are talking about. why should i aquire a second mutex on completing?? – fredlllll Feb 28 '14 at 01:08
  • You need to block the observer thread until timeout or it works right? You can't block on a mutex when you already hold the lock for it. If you need to block on a mutex until some event happens, then another thread needs to hold the lock on a second mutex, then unlocks that mutex when the event happens. You could then immediately unlock the second mutex, using it only to signal the event and the thread that held the second mutex can acquire it again. – Dave S Feb 28 '14 at 01:10
  • but if you look in the constructor im using another thread to aquire the lock of the mutex there. isnt that what you say? the mutex is hold by a dead thread. or is it forbidden to lock a mutex that is held by a dead thread? – fredlllll Feb 28 '14 at 01:12
  • You shouldn't ever leave a mutex locked by a dead thread. Only the thread that locked the mutex should unlock it. – Dave S Feb 28 '14 at 01:13
  • If you leave a mutex locked by a dead thread you can never unlock it again. You can have the thread sleep, or wait for some event but before you join the thread you need to unlock the mutex it locked. – Dave S Feb 28 '14 at 01:18
  • so how do i get the behaviour i want? i mean a semaphore of size 1 should do that right? but how can i get a semaphore where i can wait with a timeout? – fredlllll Feb 28 '14 at 01:22
  • you can use a timed mutex, but the thread that created it needs to be the same thread that unlocks it. Say we have lockThread, it creates the lock then waits around for complete signal that lets us know the hardware operation is complete. When that happens lockThread unlocks the lock, the observer thread then grabs that lock and returns true. if lockThread never unlocks the lock, then the observer thread timesout and returns false. – Dave S Feb 28 '14 at 01:31
  • cant i just use a semaphore(sem_t)? i dont think that this mutex thing will work. – fredlllll Feb 28 '14 at 01:38
  • I think what you want to do is create the mutex and lock it before you make the observer, and pass it as a parameter to the observer. Then the thread that makes the observer unlocks the mutex when the completion happened. The observer should be blocking on the mutex, once the unlock happens it will acquire the lock and it can unlock the mutex and return true. If it times out it can return false. Otherwise I don't think I understand what you are trying to do. – Dave S Feb 28 '14 at 01:45
  • and i dont understand what you are trying to tell me. i cant use the same thread to unlock the mutex. thats just not possible. i cant guarantee it. it may or may not be the case. but im making the observer in thread 1 so thread 1 can wait for thread 2. also i think i have a general timing issue on my vm... sem_timedwait also returns immediately with a timeout. – fredlllll Feb 28 '14 at 01:59
  • Ok what I am trying to tell you is, mutex's are objects shared between threads. Thread 1 locks the mutex, then it does stuff. Thread 2 tries to lock the mutex, it blocks until thread 1 unlocks the mutex. If thread 1 never unlocks the mutex, thread 2 never returns, unless you are using a timed_mutex and the timeout value hits. The code std::thread createThread([this]{ this->mutex.lock(); }); createThread.join(); will mean that this->mutex can't be unlocked again. – Dave S Feb 28 '14 at 02:06
  • What you could do, is have the thread check for isCompleted to == true or some other condition then unlock the mutex. Then the BasicObserver::waitForCompletion could return true. – Dave S Feb 28 '14 at 02:11
  • i thought that mutexes on linux dont know ownership. well i was wrong. i used a semaphore(sem_t) now and it works. yeah i could do some busy waiting. but this is supposed to run on a tiny computer on a robot that also has to do its A* for path finding. i cant waste any performance. using the semaphore works. although i find the timedwait pretty disturbing. also i finally can go to bed now :D. thx for your time – fredlllll Feb 28 '14 at 02:18