Below is the code is given in OSTEP book regarding the implementation of a lock using test and set instruction. My question is that in such implementation, couldn't a thread that is not holding the lock call the unlock function and take away the lock?
typedef struct __lock_t {
int flag;
} lock_t;
void init(lock_t *lock) {
// 0 indicates that lock is available, 1 that it is held
lock->flag = 0;
}
void lock(lock_t *lock) {
while (TestAndSet(&lock->flag, 1) == 1)
; // spin-wait (do nothing)
}
void unlock(lock_t *lock) {
lock->flag = 0;
}
It's assumed that it's a coding error so that case should not be happening.
My question is that in such implementation, couldn't a thread that is not holding the lock call the unlock function and take away the lock?
There's nothing wrong with (e.g.) one thread acquiring a lock, notifying another thread that it can proceed (or maybe spawning a new thread), then the other thread releasing the lock.
Something is very wrong if a single thread acquires a lock and then releases it twice.
To detect bugs (while still supporting all legitimate scenarios), you'd want to detect "unlock() called when lock not held". You could do that by using an atomic TestAndClear() in the unlock().
If you want to enforce "only the thread that acquired the lock may release the lock" then you have to know which thread acquired the lock, so you have to store a thread ID in the lock when its acquired (e.g. maybe by using an atomic "compare, and exchange if it was equal").
Related
Multiple threads will be “busy waiting” for the next_action variable to be set. Ideally one thread would call perform_action whenever the main sets it to a nonzero.
// choose a time-consuming activity based on action ...
void perform_action(int action);
int next_action = 0;
void* threadfunc(void*)
{
while (1)
{
while (next_action == 0);
int my_action = next_action;
next_action = 0;
perform_action(my_action);
}
}
int main()
{
// assume we've spawned threads executing threadfunc ...
while (1)
{
// determine what action should be dispatched to a thread next
next_action = read_action_from_user();
// exit the program
if (next_action == -1) { break; }
}
}
For this issue I would use a semaphore.
Using a while loop will waste processor time by constantly checking for changes in variable.
Here we can use synchronization methods that can alert when is available.
A semaphore has two options: acquire and release.
Main thread initially acquires the semaphore, and the thread is enqueued to acquire it. Thread will wait until semaphore becomes available.
When main thread sets it releases the semaphore to signal that a nonzero value has been set to it.
The thread will now wake up, acquire the semaphore, perform requested operation and release the semaphore.
When main thread needs to change the semaphore, it must acquire the semaphore again, set the and release the semaphore.
To acquire the semaphore, main thread must necessarily wait until the thread has finished.
I would not use a mutex because you also need a signaling mechanism to wake up your thread, and not only a protection for a shared variable.
See also Conditional Variable vs Semaphore
I'm currently learning about concurrency at my University. In this context I have to implement the reader/writer problem in C, and I think I'm on the right track.
My thought on the problem is, that we need two locks rd_lock and wr_lock. When a writer thread wants to change our global variable, it tries to grab both locks, writes to the global and unlocks. When a reader wants to read the global, it checks if wr_lock is currently locked, and then reads the value, however one of the reader threads should grab the rd_lock, but the other readers should not care if rd_lock is locked.
I am not allowed to use the implementation already in the pthread library.
typedef struct counter_st {
int value;
} counter_t;
counter_t * counter;
pthread_t * threads;
int readers_tnum;
int writers_tnum;
pthread_mutex_t rd_lock;
pthread_mutex_t wr_lock;
void * reader_thread() {
while(true) {
pthread_mutex_lock(&rd_lock);
pthread_mutex_trylock(&wr_lock);
int value = counter->value;
printf("%d\n", value);
pthread_mutex_unlock(&rd_lock);
}
}
void * writer_thread() {
while(true) {
pthread_mutex_lock(&wr_lock);
pthread_mutex_lock(&rd_lock);
// TODO: increment value of counter->value here.
counter->value += 1;
pthread_mutex_unlock(&rd_lock);
pthread_mutex_unlock(&wr_lock);
}
}
int main(int argc, char **args) {
readers_tnum = atoi(args[1]);
writers_tnum = atoi(args[2]);
pthread_mutex_init(&rd_lock, 0);
pthread_mutex_init(&wr_lock, 0);
// Initialize our global variable
counter = malloc(sizeof(counter_t));
counter->value = 0;
pthread_t * threads = malloc((readers_tnum + writers_tnum) * sizeof(pthread_t));
int started_threads = 0;
// Spawn reader threads
for(int i = 0; i < readers_tnum; i++) {
int code = pthread_create(&threads[started_threads], NULL, reader_thread, NULL);
if (code != 0) {
printf("Could not spawn a thread.");
exit(-1);
} else {
started_threads++;
}
}
// Spawn writer threads
for(int i = 0; i < writers_tnum; i++) {
int code = pthread_create(&threads[started_threads], NULL, writer_thread, NULL);
if (code != 0) {
printf("Could not spawn a thread.");
exit(-1);
} else {
started_threads++;
}
}
}
Currently it just prints a lot of zeroes, when run with 1 reader and 1 writer, which means, that it never actually executes the code in the writer thread. I know that this is not going to work as intended with multiple readers, however I don't understand what is wrong, when running it with one of each.
Don't think of the locks as "reader lock" and "writer lock".
Because you need to allow multiple concurrent readers, readers cannot hold a mutex. (If they do, they are serialized; only one can hold a mutex at the same time.) They can take one for a short duration (before they begin the access, and after they end the access), to update state, but that's it.
Split the timeline for having a rwlock into three parts: "grab rwlock", "do work", "release rwlock".
For example, you could use one mutex, one condition variable, and a counter. The counter holds the number of active readers. The condition variable is signaled on by the last reader, and by writers just before they release the mutex, to wake up a waiting writer. The mutex protects both, and is held by writers for the whole duration of their write operation.
So, in pseudocode, you might have
Function rwlock_rdlock:
Take mutex
Increment counter
Release mutex
End Function
Function rwlock_rdunlock:
Take mutex
Decrement counter
If counter == 0, Then:
Signal_on cond
End If
Release mutex
End Function
Function rwlock_wrlock:
Take mutex
While counter > 0:
Wait_on cond
End Function
Function rwlock_unlock:
Signal_on cond
Release mutex
End Function
Remember that whenever you wait on a condition variable, the mutex is atomically released for the duration of the wait, and automatically grabbed when the thread wakes up. So, for waiting on a condition variable, a thread will have the mutex both before and after the wait, but not during the wait itself.
Now, the above approach is not the only one you might implement.
In particular, you might note that in the above scheme, there is a different "unlock" operation you must use, depending on whether you took a read or a write lock on the rwlock. In POSIX pthread_rwlock_t implementation, there is just one pthread_rwlock_unlock().
Whatever scheme you design, it is important to examine it whether it works right in all situations: a lone read-locker, a lone-write-locker, several read-lockers, several-write-lockers, a lone write-locker and one read-locker, a lone write-locker and several read-lockers, several write-lockers and a lone read-locker, and several read- and write-lockers.
For example, let's consider the case when there are several active readers, and a writer wants to write-lock the rwlock.
The writer grabs the mutex. It then notices that the counter is nonzero, so it starts waiting on the condition variable. When the last reader -- note how the order of the readers exiting does not matter, since a simple counter is used! -- unlocks its readlock on the rwlock, it signals on the condition variable, which wakes up the writer. The writer then grabs the mutex, sees the counter is zero, and proceeds to do its work. During that time, the mutex is held by the writer, so all new readers will block, until the writer releases the mutex. Because the writer will also signal on the condition variable when it releases the mutex, it is a race between other waiting writers and waiting readers, who gets to go next.
In my code I am using pthread_mutx_trylock() to check thread 1 has completed his
job and release the mutexlock or not ?Please let me know either its a valid way or not ?
In Thread 1:
pthread_mutex_lock(&sync_wait);
// Waiting for return type.
pthread_mutex_unlock(&sync_wait);
In Thread 2:
while (pthread_mutex_trylock(&sync_wait) == 0) {
}; // Wait until other thread has lock
// Waiting till thread 1 sync wait lock has not released.
pthread_mutex_unlock(&sync_wait);
From manual Page
The pthread_mutex_trylock() function shall return zero if a lock on
the mutex object referenced by mutex is acquired. Otherwise, an error
number is returned to indicate the error.
// so this will loop forever once you aquire lock
while (pthread_mutex_trylock(&sync_wait) == 0) {
}; // Wait until other thread has lock
Edit:
This section of code should handle your scenario
while ( int ret = pthread_mutex_trylock( &sync_wait) )
{
// Unable to get Mutex probably some other thread aquired it
// sleep for some time usleep or even better use pthread_mutex_timedlock
// Ideally possible ret values should have been handled but for now
// this will do
}
and yes pthread_mutex_unlock( );once done with work
here is the manpage
also there is a question on so about difference between pthread_mutex_lock and pthread_mutex_trylock here
this is another example of handling multiple return values from pthread_try_lock()
If you want to wake up a specific thread once another thread reaches a certain point in its execution, mutexes are typically not the appropriate synchronization primitive. Alternatives are:
Barriers and the pthread_barrier_wait function
Conditional variables and the pthread_cond_wait and pthread_cond_signal functions
Possibly semaphores and the sem_wait and sem_post functions
I've been working on a project lately, and i need to manage a pair of thread pools.
What the worker threads in the pools do is basically execute some kind of pop operation to each respective queue, eventually wait on a condition variable (pthread_cond_t) if there is no available value in the queue, and once they get an item, parse it and execute operations accordingly.
What i'm concerned about is the fact that i want to have no memory leaks, and to achieve that i noticed that calling a pthread_cancel on each thread when the main process is exiting is definitely a bad idea, as it leaves a lot of garbage around.
The point is, my first thought was to use a exit flag which i can set when the threads need to exit, so that they can easily free memory and call a pthread_exit...
I guess i should set this flag, then send a broadcast signal to the threads waiting on the condition variable and check the flag right after the pop operation...
Is this really the correct way to implement a good thread pool termination? I don't feel that much confident about this...
I'm writing some pseudo-code here to explain what i'm talking about
Each pool thread will run some code structured like this:
/* Worker thread (which will run on each pool thread) */
{ /* Thread initialization */ ... }
loop {
value = pop();
{ /* Mutex lock because of the shared flag */ ... }
if (flag) {{ /* Free memory and unlock mutex */ ... } pthread_exit(); }
{ /* Unlock the mutex */ ... }
{ /* Elaborate value */ ... }
}
return NULL;
And there will be some kind of pool_stopRunning() function which will look like:
/* pool_stopRunning() function code */
{ /* Acquire flag mutex */ ... }
setFlag(flag);
{ /* Unlock flag mutex */ ... }
{ /* Acquire queue mutex */ ... }
pthread_cond_broadcast(...);
{ /* Unlock queue mutex */ ... }
Thanks in advance, i just need to be sure that there isn't a fancy-er way to stop a thread pool... (or get to know a better way, by any chance)
As always, i'm sorry if there is any typo, i'm not and english speaker and it's kind of late right now >:
What you are describing will work, but I would suggest a different approach...
You already have a mechanism for assigning tasks to threads, complete with all appropriate synchronization. So instead of complicating the design with some new parallel mechanism, just define a new type of task called "STOP". If there are N threads servicing a queue and you want to terminate them, push N STOP tasks onto the queue. Then just wait for all of the threads to terminate. (This last can be done via "join", so it should not require any new mechanism, either.)
No muss, no fuss.
With respect to symmetry with setting the flag and reducing serialization, this code:
{ /* Mutex lock because of the shared flag */ ... }
if (flag) {{ /* Free memory and unlock mutex */ ... } pthread_exit(); }
{ /* Unlock the mutex */ ... }
should look like this:
{ /* Mutex lock because of the shared flag */ ... }
flagcopy = readFlag();
{ /* Unlock the mutex */ ... }
if (flagcopy) {{ /* Free memory ... } pthread_exit(); }
Having said that, you can (should?) factor the mutex code into the setFlag and readFlag methods.
There is one more thing. If the flag is only a boolean and it is only changed once before the whole thing shuts down (i.e., it's never unset after being set), then I would argue that protecting the read with a mutex is not required.
I say this because if the above assumptions are true and if the loop's duration is very short and the loop iteration frequency is high, then you would be imposing undue serialization upon the business task and potentially increasing the response time unacceptably.
When I wake up a thread waiting on a condition variable while holding the corresponding mutex, can I assume that the woken thread will run after I release the mutex and before anyone else (myself included) can lock the mutex again? Or can I only be sure that it will run at some point in the future?
To be precise, assume that I have the following functions.
bool taken = false;
int waiting = 0;
pthread_mutex_t m; // properly initialised elsewhere
pthread_cond_t c;
void enter() {
pthread_mutex_lock(&m);
// Is `taken || (waiting == 0)` guaranteed here?
while (taken) {
++ waiting;
pthread_cond_wait(&c, &m);
-- waiting;
}
taken = true;
pthread_mutex_unlock(&m);
}
void leave() {
pthread_mutex_lock(&m);
taken = false;
if (waiting > 0) {
pthread_cond_signal(&c);
}
pthread_mutex_unlock(&m);
}
(This is a toy example, not meant to be useful.)
Also assume that all threads are using these functions as
enter()
// some work here
leave()
Can I then be sure that directly after acquiring the mutex in enter() (see comment in the code), if taken is false waiting has to be zero? [It seems to me that this should be the case, because the woken thread will assume to find the state that the waking thread has left behind (if the wake-up was not spurious) and this could otherwise not be guaranteed, but I did not find it clearly worded anywhere.]
I am mainly interested in the behaviour on (modern) Linux, but of course knowing whether this is defined by POSIX would also be of interest.
Note: This may have already been asked in another question, but I hope that mine is clearer.
After t0 does pthread_cond_signal, t1 is no longer waiting for the condition variable, but it is also not running, because t0 still holds the mutex; t1 is instead waiting for the mutex. The thread t2 may also be waiting for the mutex, at the beginning of enter(). Now t0 releases the mutex. Both t1 and t2 are waiting for it. Is t1 handled in a special way and guaranteed to get it, or could t2 get it instead?
Thanks for your comment, Carsten, that cleared it up.
No, see this answer for more reference. Given your example, t2 could acquire the mutex before t1 and a race condition may occur leading to unexpected outcomes.
Reiterating, t0 may initially have the mutex and be in the while loop holding on the line pthread_cond_wait(&c, &m);and the mutex is released atomically reference. t1 could call leave() acquiring the mutex signaling the condition c, and then release the mutex. t0 will prepare to run --waiting by trying to acquire the now-freed mutex by t1, but it can become context switched by the OS. Some other thread t2 waiting on the mutex can grab it and run enter() causing undesired outcomes, see non-reentrant. t2 then releases the mutex. t0 may swaps back only to see values have been mutated.