Exception thrown while blocking boost :: unique_lock which is already blocked in another thread

There is a global boost :: unique_lock object. One thread is blocking it. When another thread tries to block, the "already owns the mutex" exception. Why is this happening? I expect the thread to block until another thread calls to unlock.

boost::mutex mutex;
boost::unique_lock<boost::mutex> lock(mutex);

static void* scheduller(void* arg)
{
    boost::this_thread::sleep(boost::posix_time::seconds(5));
    lock.lock();    
    return 0;
}

static void* usb(void* arg)
{
    lock.lock();
    boost::this_thread::sleep(boost::posix_time::seconds(20));
    return 0;
}

int BEvent_test()
{
    lock.unlock();
    int a = 0;
    boost::thread thread1(usb,&a);
    boost::thread thread2(scheduller,&a);
    boost::this_thread::sleep(boost::posix_time::seconds(100));
    return 0;

}

      

In the BEvent_test function, I am doing the unlock because the unique lock is locked in the constructor. Delays make sure the scheduler thread starts after the usb thread. When it tries to do lock (), an exception is thrown. Why?

+3


source to share


3 answers


Misuse unique_lock

, in particular, these objects should not be shared across threads as they are not thread safe. Yes, it sounds strange at first, but it makes sense because being thread safe and being thread safe are two different things.

The idea unique_lock

is to help you manage locks on mutexes. To do this, you only create these objects locally. This is also the reason why it was originally blocked and you seem to "get around" by opening it. However, you should try to use your volume / lifespan to manage the lock.



Now you are saying what you need unique_lock

to use the event. Yes and no. You need one, but not exactly the same for every thread using this event. Instead, you can use different local locks from different threads. BTW: The reason the event requires a lock is that while waiting for the event, the lock must be released. After receiving the event (or timeout), it must also re-commit the lock. In other words, the lock serves as a proxy for the mutex, offering indirect actions and some additional guarantees.

The reason you are getting the exception is because you are trying to block unique_lock

twice. I don't think it's important that it's from the same thread, I wouldn't even say that it's really from the same topic. However, when you lock the mutex (through unique_lock

), you are saying that you are now entering a critical section. This implies that you have not been to this critical section before. If it unique_lock

now detects that the lock has already been held, it means that something in your code has gone wrong, because the code seems to be confusing, whether it is in a critical section or not. For this reason, you get an exception as motivation to fix your code.

+4


source


I am not familiar with acceleration sync. From the documentation here, it seems to me that the idea is the code where the object is created unique_lock

to have ownership mutex

for a given duration (time, lifetime unique_lock

, etc.). I think you should try to block mutex

and not unique_lock

on other threads.



As I can see, unique_lock

it is useful to block mutex

in a method and know that no matter what happens, when the ( unique_lock

destroy) method exits, the mutex will be released. You don't need to check for exceptions, etc. to release mutex

.

+2


source


The message does not mean that the mutex is locked on another thread. This means that you already have a mutex locked on the same thread . And since your mutex is not recursive blocking, this is a bug.


You are manually locking / unlocking the mutex.

Difficult to see from the shown code snippet but easy

  • do it wrong (forget to unlock in time)
  • it is almost impossible to rule it out (the code is clearly not like that).

Instead, you should use scoped_lock

(or lock_guard / unique_lock) to balance locking / unlocking in an automatic and safe manner.


UPDATE

On re-reading the code, you just used it unique_lock

completely wrong:

Live On Coliru

#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread.hpp>
#include <iostream>

#define TRACE(msg) do { std::cout << msg << " " << __FUNCTION__ << " at " << relative_ms() << "ms\n"; } while(false)

int relative_ms() {
    using boost::posix_time::second_clock;
    static auto t0 = second_clock::local_time();

    return (second_clock::local_time() - t0).total_milliseconds();
}

boost::mutex mutex;

static void scheduler()
{
    boost::unique_lock<boost::mutex> lock(mutex);
    TRACE("Enter");

    boost::this_thread::sleep_for(boost::chrono::seconds(2));

    TRACE("Leave");
}

void usb()
{
    boost::unique_lock<boost::mutex> lock(mutex);
    TRACE("Enter");

    boost::this_thread::sleep_for(boost::chrono::seconds(5));

    TRACE("Leave");
}

int main()
{
    TRACE("Enter");
    boost::thread thread1(usb);
    boost::thread thread2(scheduler);
    boost::this_thread::sleep_for(boost::chrono::seconds(10));
    TRACE("Leave");
}

      

Printing, for example

Enter main at 0ms
Enter usb at 0ms
Leave usb at 5000ms
Enter scheduler at 5000ms
Leave scheduler at 7000ms
Leave main at 10000ms

      

or

Enter main at 0ms
Enter scheduler at 0ms
Leave scheduler at 2000ms
Enter usb at 2000ms
Leave usb at 7000ms
Leave main at 10000ms

      

depends only on which thread first grabs the mutex.

+2


source







All Articles