Modifying boost :: Lockable using a semaphore rather than a mutex (formerly called: unlocking a mutex from another thread)
I am using the C ++ boost :: thread library, which in my case means I am using pthreads. Officially, the mutex must be unlocked from the same thread that is blocking it, and I want the effect to be able to be committed on one thread and then unlocked on another. There are many ways to do this. One possibility would be to write a new mutex class that allows this behavior.
For instance:
class inter_thread_mutex{
bool locked;
boost::mutex mx;
boost::condition_variable cv;
public:
void lock(){
boost::unique_lock<boost::mutex> lck(mx);
while(locked) cv.wait(lck);
locked=true;
}
void unlock(){
{
boost::lock_guard<boost::mutex> lck(mx);
if(!locked) error();
locked=false;
}
cv.notify_one();
}
// bool try_lock(); void error(); etc.
}
I should point out that the above code does not guarantee FIFO access, because if one thread calls lock () and another calls unlock (), that first thread can acquire the lock before other threads that are waiting. (Think about it, the boost :: thread documentation doesn't show any explicit scheduling guarantees for both mutexes or condition variables.) But let's just ignore this (and any other errors) for now.
My question is, if I decide to go this route, would I be able to use such a mutex as a model for the Lockable concept. For example, something goes wrong if I use boost::unique_lock< inter_thread_mutex >
RAII for access and then pass that lock to boost::condition_variable_any.wait()
, etc.
On the one hand, I don't understand why not. On the other hand, “I don’t understand why not” is usually a very bad way to determine if something will work.
The reason I am asking is that if it turns out that I have to write wrapper classes for locks and RAII state variables and something else, then I would rather find another way to achieve the same effect.
EDIT: The behavior I want is basically as follows. I have an object and it needs to be blocked whenever it changes. I want to lock an object from one thread and do some work on it. Then I want the object to be locked while I tell another worker thread to shut down. So the first thread can continue and do something else while the worker thread ends. When the worker thread exits, it unlocks the mutex.
And I want the transition to seem indifferent, so no one can lock the mutex in between when thread 1 starts up and thread 2 ends it.
Something like inter_thread_mutex looks like this will work and it will also allow the program to interact with it as if it were a normal mutex. So this seems like a clean solution. If there is a better solution, I would be glad to hear that as well.
EDIT AGAIN: The reason I need locks is because there are multiple main threads, and the locks are there so that they cannot access shared objects at the same time in invalid ways. So the code is already using loop level sequencing at the main thread level at the loop level. Also, there were no worker threads in the original implementation, and the mutexes were regular kosher mutexes.
Inter_thread_thingy appeared as an optimization primarily to improve response times. In many cases it was sufficient to ensure that the "first part" of operation A occurs before the "first part" of operation B. As a dumb example, let's say I punch through object 1 and give it a black eye. Then I tell object 1 to change its internal structure to reflect any tissue damage. I do not want to wait for tissue damage before I move on to hitting object 2. However, I want tissue damage to occur as part of the same operation; for example in between, I don't want any other thread to reconfigure the object in such a way that tissue damage would result in an invalid operation. (yes, this example is imperfect in many ways and I am not working on the game)
So, we made a change to the model where ownership of an object can be passed to a worker thread to complete an operation, and that actually works really well; each main thread can receive a lot more operations because there is no need to wait for all of them to complete. And since the sequence of events at the main thread level is still loop-based, it is easy to write the high-level operations of the master thread, since they can be based on the assumption that the operation has completed (more precisely, the critical "first part" that the sequence logic depends on) when the corresponding function call returns.
Finally, I thought it would be nice to use inter_thread mutex / semaphore thingies using RAII with boost locks to encapsulate the necessary synchronization needed for this to work.
a source to share
man pthread_unlock
(this is on OS X, similar wording on Linux ) has the answer:
NAME
pthread_mutex_unlock - unlock a mutex
SYNOPSIS
#include <pthread.h>
int
pthread_mutex_unlock (pthread_mutex_t * mutex);
DESCRIPTION
If the current thread holds the lock on mutex, then the
pthread_mutex_unlock () function unlocks mutex.
Calling pthread_mutex_unlock () with a mutex that the
calling thread does not hold will result in
undefined behavior .
...
My counter question would be - what kind of sync problem are you trying to solve with this? There is most likely an easier solution.
Neither pthreads
, nor boost::thread
(built on top of it) guarantees the order in which a competing mutex is acquired by competing threads.
a source to share
There are several ways to approach this. Both of these I'm going to propose would involve adding an extra piece of information to the object, as well as adding a mechanism to unblock a thread from a thread other than the one that owns it.
1) you can add some information to indicate the state of the object:
enum modification_state { consistent, // ready to be examined or to start being modified
phase1_complete, // ready for the second thread to finish the work
};
// first worker thread
lock();
do_init_work(object);
object.mod_state = phase1_complete;
unlock();
signal();
do_other_stuff();
// second worker thread
lock()
while( object.mod_state != phase1_complete )
wait()
do_final_work(obj)
object.mod_state = consistent;
unlock()
signal()
// some other thread that needs to read the data
lock()
while( object.mod_state != consistent )
wait();
read_data(obj)
unlock()
Works well with state variables because obviously you are not writing your own lock.
2) If you have a specific thread, you can give the object an owner.
// first worker
lock();
while( obj.owner != this_thread() ) wait();
do_initial_work(obj);
obj.owner = second_thread_id;
unlock()
signal()
...
This is almost the same solution as my first solution, but more flexible in adding / removing phases and less flexible in adding / removing threads.
To be honest, I'm not sure how the interute mutex will help you here. You will still need a semaphore or condition variable to signal that work has passed to the second thread.
a source to share
A slight modification to what you already have: how about storing the id of the thread you want to block in inter_thread_whatever
? Then open it up and send a message to that thread saying, "I want you to do whatever routine that tries this lock."
Then the condition in lock
becomes while(locked || (desired_locker != thisthread && desired_locker != 0))
. Technically, you "released the lock" on the first thread and "took it again" on the second thread, but there is no way any other thread can grab it in between, so it's as if you were transferring it straight from one to the other.
There is a potential problem that if a thread exits or is killed while it is the correct blocker for your lock, then that thread will block. But you already talked about the first thread waiting for a message from the second thread to say it successfully acquired the lock, so you probably already have a plan in view of what happens if that message isn't received. Add "reset desired_block field to inter_thread_whatever" to this plan.
It's all very hairy, but I'm not sure if what I suggested is correct. Is there a way that the "main" thread (the one that directs all these helpers) can simply make sure that it does not order any operations on what is protected by this lock until the first op completes (or fails, and some RAII will notify you)? You don't need locks as such if you can handle them at the message loop level.
a source to share
I don't think it is a good idea to say that your inter_thread_mutex
(binary_semaphore) can be seen as a Lockable model. The main problem is that the main feature of yours inter_thread_mutex
beats the concept Locakble
. If there inter_thread_mutex
was a locking model, in In [1] you would expect inter_thread_mutex m to be locked.
// thread T1
inter_thread_mutex m;
{
unique_lock<inter_thread_mutex> lk(m);
// [1]
}
But how another thread T2 can do m.unlock()
, and T1 is in [1], the guarantee is violated.
Binary semaphores can be used both Lockables
as each thread tries to block before unlocking. But the main purpose of your class is exactly the opposite.
This is one of the reasons why semaphores in Boost.Interprocess do not use lock / unlock to call functions, but wait / notify. Curiously, these are the same names used by the conditions :)
a source to share
A mutex is a mechanism for describing mutually exclusive blocks of code . It doesn't make sense for these blocks of code to cross thread boundaries. Trying to use such a concept in such a counter in an intuitive way can only lead to problems down the line.
It sounds a lot like you're looking for a different concept of multithreading, but without the details, it's hard to figure out what.
a source to share