Reader/Writer implementation in C - c

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.

Related

Reader Writer Problem With Writer Priority Problem

I came across this problem as I am learning more about operating systems. In my code, I've tried making the reader having priority and it worked, so next I modified it a bit to make the writer have the priority. When I ran the code, the output was exactly the same and it seemed like the writer did not have the priority. Here is the code with comments. I am not sure what I've done wrong, since I modified a lot of the code but the output remains the same if I did not change it at all.
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
/*
This program provides a possible solution for first readers writers problem using mutex and semaphore.
I have used 10 readers and 5 producers to demonstrate the solution. You can always play with these values.
*/
// Semaphore initialization for writer and reader
sem_t wrt;
sem_t rd;
// Mutex 1 blocks other readers, mutex 2 blocks other writers
pthread_mutex_t mutex1;
pthread_mutex_t mutex2;
// Value the writer is changing, we are simply multiplying this value by 2
int cnt = 2;
int numreader = 0;
int numwriter = 0;
void *writer(void *wno)
{
pthread_mutex_lock(&mutex2);
numwriter++;
if(numwriter == 1){
sem_wait(&rd);
}
pthread_mutex_unlock(&mutex2);
sem_wait(&wrt);
// Writing Section
cnt = cnt*2;
printf("Writer %d modified cnt to %d\n",(*((int *)wno)),cnt);
sem_post(&wrt);
pthread_mutex_lock(&mutex2);
numwriter--;
if(numwriter == 0){
sem_post(&rd);
}
pthread_mutex_unlock(&mutex2);
}
void *reader(void *rno)
{
sem_wait(&rd);
pthread_mutex_lock(&mutex1);
numreader++;
if(numreader == 1){
sem_wait(&wrt);
}
pthread_mutex_unlock(&mutex1);
sem_post(&rd);
// Reading Section
printf("Reader %d: read cnt as %d\n",*((int *)rno),cnt);
pthread_mutex_lock(&mutex1);
numreader--;
if(numreader == 0){
sem_post(&wrt);
}
pthread_mutex_unlock(&mutex1);
}
int main()
{
pthread_t read[10],write[5];
pthread_mutex_init(&mutex1, NULL);
pthread_mutex_init(&mutex2, NULL);
sem_init(&wrt,0,1);
sem_init(&rd,0,1);
int a[10] = {1,2,3,4,5,6,7,8,9,10}; //Just used for numbering the writer and reader
for(int i = 0; i < 5; i++) {
pthread_create(&write[i], NULL, (void *)writer, (void *)&a[i]);
}
for(int i = 0; i < 10; i++) {
pthread_create(&read[i], NULL, (void *)reader, (void *)&a[i]);
}
for(int i = 0; i < 5; i++) {
pthread_join(write[i], NULL);
}
for(int i = 0; i < 10; i++) {
pthread_join(read[i], NULL);
}
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
sem_destroy(&wrt);
sem_destroy(&rd);
return 0;
}
Output (for both is the same. I think if writer had priority it will change first, then will be read):
Alternative Semantics
Much of what you want to do can probably be accomplished with less overhead. For example, in the classic reader-writer problem, readers shouldn’t need to block other readers.
You might be able to replace the reader-writer pattern with a publisher-consumer pattern that manages pointers to blocks of data with acquire-consume memory ordering. You only need locking at all if one thread needs to update the same block of memory after it was originally written.
POSIX and Linux have an implementation of reader-writer locks in the system library, which were designed to avoid starvation. This is most likely the high-level construct you want.
If you still want to implement your own, one implementation would use a count of current readers, a count of pending writers and a flag that indicates whether a write is in progress. It packs all these values into an atomic bitfield that it updates with a compare-and-swap.
Reader threads would retrieve the value, check whether there are any starving writers waiting, and if not, increment the count of readers. If there are writers, it backs off (perhaps spinning and yielding the CPU, perhaps sleeping on a condition variable). If there is a write in progress, it waits for that to complete. If it sees only other reads in progress, it goes ahead.
Writer threads would check if there are any reads or writes in progress. If so, they increment the count of waiting writers, and wait. If not, they set the write-in-progress bit and proceed.
Packing all these fields into the same atomic bitfield guarantees that no thread will think it’s safe to use the buffer while another thread thinks it’s safe to write: if two threads try to update the state at the same time, one will always fail.
If You Stick With Semaphores
You can still have reader threads check sem_getvalue() on the writer semaphore, and back off if they see any starved writers are waiting. One method would be to wait on a condition variable that threads signal when they are done with the buffer. A reader that sees that it holds the mutex while writers are waiting can try to wake up one writer thread and go back to sleep, and a reader that sees only other readers are waiting can wake up a reader, which will wake up the next reader, and so on.

Readers-Writers problem writers-preference (readers may starve)

I have problem with readers-writers problem. I want to write writers favor solution using mutex. So far i have written this
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <memory.h>
#include <stdbool.h>
#include <stdint.h>
#include<unistd.h>
int NO_READERS;
int NO_WRITERS;
int NO_READERS_READING = 0; // How many readers need shared resources
int NO_WRITERS_WRITING = 0; // How many writers need shared resources
pthread_mutex_t resourceMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t tryResourceMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t readerMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t writerMutex = PTHREAD_MUTEX_INITIALIZER;
void *readerJob(void *arg) {
int *id = (int*)arg;
while (1) {
pthread_mutex_lock(&tryResourceMutex); // Indicate reader is trying to enter
pthread_mutex_lock(&readerMutex);
NO_READERS_READING++; // Indicate that you are needing the shared resource (one more reader)
if (NO_READERS_READING == 1) {
pthread_mutex_lock(&resourceMutex);
}
pthread_mutex_unlock(&readerMutex);
pthread_mutex_unlock(&tryResourceMutex);
printf("READER ID %d WALKED IN \n",*id);
printf("ReaderQ: %d , WriterQ: %d [in: R:%d W:%d]\n",
NO_READERS - NO_READERS_READING,
NO_WRITERS - NO_WRITERS_WRITING,
NO_READERS_READING,
NO_WRITERS_WRITING);
sleep(1);
pthread_mutex_lock(&readerMutex);
NO_READERS_READING--;
if (NO_READERS_READING == 0) { // Check if you are the last reader
pthread_mutex_unlock(&resourceMutex);
}
pthread_mutex_unlock(&readerMutex);
}
return 0;
}
void *writerJob(void *arg) {
int *id = (int*)arg;
while (1) {
pthread_mutex_lock(&writerMutex);
NO_WRITERS_WRITING++;
if (NO_WRITERS_WRITING == 1) {
pthread_mutex_lock(&tryResourceMutex); // If there are no other writers lock the readers out
}
pthread_mutex_unlock(&writerMutex);
pthread_mutex_lock(&resourceMutex);
printf("WRITER ID %d WALKED IN \n",*id);
printf("ReaderQ: %d , WriterQ: %d [in: R:%d W:%d]\n",
NO_READERS - NO_READERS_READING,
NO_WRITERS - NO_WRITERS_WRITING,
NO_READERS_READING,
NO_WRITERS_WRITING);
sleep(1);
pthread_mutex_unlock(&resourceMutex);
pthread_mutex_lock(&writerMutex);
NO_WRITERS_WRITING--;
if (NO_WRITERS_WRITING == 0) {
pthread_mutex_unlock(&tryResourceMutex); // If there are no writers left unlock the readers
}
pthread_mutex_unlock(&writerMutex);
}
return 0;
}
int main(int argc, char *argv[]) {
NO_READERS = atoi(argv[1]);
NO_WRITERS = atoi(argv[2]);
// Initialize arrays of threads IDs
pthread_t *readersThreadsIds = malloc(NO_READERS * sizeof(pthread_t));
pthread_t *writersThreadsIds = malloc(NO_READERS * sizeof(pthread_t));
// Initialize shared memory (array) with random numbers
// Create readers threads
for (int i = 0; i < NO_READERS; ++i) {
int* id = (int*)(malloc(sizeof(int)));
*id = i;
pthread_create(&readersThreadsIds[i], NULL, readerJob,(void*)id);
}
// Create writers threads
for (int i = 0; i < NO_WRITERS; ++i) {
int* id = (int*)(malloc(sizeof(int)));
*id = i;
pthread_create(&writersThreadsIds[i], NULL, writerJob, (void*)id);
}
// Wait for readers to finish
for (int i = 0; i < NO_READERS; ++i) {
pthread_join(readersThreadsIds[i], NULL);
}
// Wait for writers to finish
for (int i = 0; i < NO_WRITERS; ++i) {
pthread_join(writersThreadsIds[i], NULL);
}
free(readersThreadsIds);
free(writersThreadsIds);
pthread_mutex_destroy(&resourceMutex);
pthread_mutex_destroy(&tryResourceMutex);
pthread_mutex_destroy(&readerMutex);
pthread_mutex_destroy(&writerMutex);
return 0;
}
And I'm not sure if this should be working like this. Can anyone check this for me? I want to have information about which reader or writer is going in or out. It seems like it stuck in some point but I don't know why.
It seems to do what you want, that is give preference to the writers. Because your threads loop acquiring and releasing the lock; if you have more than one writer, the writers will take turns passing it between themselves and starve the readers. That is, every time one releases the resourceMutex, there is another writer waiting on it, so NO_WRITERS_WRITING will never hit zero.
To see it operating as intended, add a delay at the top of the while loop of each thread:
usleep((rand() % 10000) * 10000);
That will permit the readers to periodically get access, whenever all the writers are in the usleep().
At the begining all readers are coming in,
By "coming in", I take you to mean executing the printf() calls in the readerJob loop. It's not surprising that the readers all come in first, because you start them first, and in the likely event that the first reader thread to attempt to lock tryResourceMutex does so before any writer thread does, it will then lock resourceMutex(), too, preventing any writer from "coming in". But that does not prevent writers from incrementing NO_WRITERS_WRITING. And it also does not preventing one of them from locking tryResourceMutex and holding it locked.
The sleep() call in the reader will then (probably) cause resourceMutex to be held continuously long enough that all the readers come in before any of the writers do, since each writer needs to acquire resourceMutex to come in.
then also writers which shouldn't be possible at the same time.
I don't see that in my tests. But I do see what I already described: the writer count increases from zero, even though they are prevented from coming in while any readers are inside. In effect, the name of your variable NO_WRITERS_WRITING is inconsistent with your actual usage -- indicates how many writers are writing or waiting to write.
When the readers leave they are blocked from reentering right away because one of the writers holds tryResourceMutex. Eventually, then, the last reader will exit and release the resourceMutex. This will allow the writers to proceed, one at a time, but with the sleep() call positioned where it is in the writer loop, it is extremely unlikely that the number of writers will ever fall to zero to allow any of the readers to re-enter. If it did, however, then very likely the same cycle would repeat: all of the readers would enter, once, while all the writers queue up.
Then all readers are gone but there are more than one writer at the same time in library.
Again, no. Only one writer is inside at a time, but the others are queued most of the time, so NO_WRITERS_WRITING will almost always be equal to NO_WRITERS.
Bottom line, then: you have confused yourself. You are using variable NO_WRITERS_WRITING primarily to represent the number of writers that are ready to write, but your messaging uses it as if it were the number actually writing. The same does not apply to NO_READERS_READING because once a thread acquires the mutex needed to modify that variable, nothing else prevents it from proceeding on into the room.
One more thing: to make the simulation interesting -- i.e. to keep the writers from taking permanent control -- you should implement a delay, preferably a random one, after each thread leaves the room, before it tries to reenter. And the delay for writers should probably be substantially longer than the delay for readers.

Main thread and worker thread initialization

I'm creating a multi-thread program in C and I've some troubles.
There you have the function which create the threads :
void create_thread(t_game_data *game_data)
{
size_t i;
t_args *args = malloc(sizeof(t_args));
i = 0;
args->game = game_data;
while (i < 10)
{
args->initialized = 0;
args->id = i;
printf("%zu CREATION\n", i);//TODO: Debug
pthread_create(&game_data->object[i]->thread_id, NULL, &do_action, args);
i++;
while (args->initialized == 0)
continue;
}
}
Here you have my args struct :
typedef struct s_args
{
t_game_data *object;
size_t id;
int initialized;
}args;
And finally, the function which handle the created threads
void *do_action(void *v_args)
{
t_args *args;
t_game_data *game;
size_t id;
args = v_args;
game = args->game;
id = args->id;
args->initialized = 1;
[...]
return (NULL);
}
The problem is :
The main thread will create new thread faster than the new thread can init his variables :
args = v_args;
game = args->game;
id = args->id;
So, sometime, 2 different threads will get same id from args->id.
To solve that, I use an variable initialized as a bool so make "sleep" the main thread during the new thread's initialization.
But I think that is really sinful.
Maybe there is a way to do that with a mutex? But I heard it wasn't "legal" to unlock a mutex which does not belong his thread.
Thanks for your answers!
The easiest solution to this problem would be to pass a different t_args object to each new thread. To do that, move the allocation inside the loop, and make each thread responsible for freeing its own argument struct:
void create_thread(t_game_data *game_data) {
for (size_t i = 0; i < 10; i++) {
t_args *args = malloc(sizeof(t_args));
if (!args) {
/* ... handle allocation error ... */
} else {
args->game = game_data;
args->id = i;
printf("%zu CREATION\n", i);//TODO: Debug
if (pthread_create(&game_data->object[i]->thread_id, NULL,
&do_action, args) != 0) {
// thread creation failed
free(args);
// ...
}
}
}
}
// ...
void *do_action(void *v_args) {
t_args *args = v_args;
t_game_data *game = args->game;
size_t id = args->id;
free(v_args);
args = v_args = NULL;
// ...
return (NULL);
}
But you also write:
To solve that, I use an variable initialized as a bool so make "sleep"
the main thread during the new thread's initialization.
But I think that is really sinful. Maybe there is a way to do that
with a mutex? But I heard it wasn't "legal" to unlock a mutex which
does not belong his thread.
If you nevertheless wanted one thread to wait for another thread to modify some data, as your original strategy requires, then you must employ either atomic data or some kind of synchronization object. Your code otherwise contains a data race, and therefore has undefined behavior. In practice, you cannot assume in your original code that the main thread will ever see the new thread's write to args->initialized. "Sinful" is an unusual way to describe that, but maybe appropriate if you belong to the Church of the Holy C.
You could solve that problem with a mutex by protecting just the test of args->initialized in your loop -- not the whole loop -- with a mutex, and protecting the threads' write to that object with the same mutex, but that's nasty and ugly. It would be far better to wait for the new thread to increment a semaphore (not a busy wait, and the initialized variable is replaced by the semaphore), or to set up and wait on a condition variable (again not a busy wait, but the initialized variable or an equivalent is still needed).
The problem is that in create_thread you are passing the same t_args structure to each thread. In reality, you probably want to create your own t_args structure for each thread.
What's happening is your 1st thread is starting up with the args passed to it. Before that thread can run do_action the loop is modifying the args structure. Since thread2 and thread1 will both be pointing to the same args structure, when they run do_action they will have the same id.
Oh, and don't forget to not leak your memory
Your solution should work in theory except for a couple of major problems.
The main thread will sit spinning in the while loop that checks the flag using CPU cycles (this is the least bad problem and can be OK if you know it won't have to wait long)
Compiler optimisers can get trigger happy with respect to empty loops. They are also often unaware that a variable may get modified by other threads and can make bad decisions on that basis.
On multi core systems, the main thread may never see the change to args->initiialzed or at least not until much later if the change is in the cache of another core that hasn't been flushed back to main memory yet.
You can use John Bollinger's solution that mallocs a new set of args for each thread and it is fine. The only down side is a malloc/free pair for each thread creation. The alternative is to use "proper" synchronisation functions like Santosh suggests. I would probably consider this except I would use a semaphore as being a bit simpler than a condition variable.
A semaphore is an atomic counter with two operations: wait and signal. The wait operation decrements the semaphore if its value is greater than zero, otherwise it puts the thread into a wait state. The signal operation increments the semaphore, unless there are threads waiting on it. If there are, it wakes one of the threads up.
The solution is therefore to create a semaphore with an initial value of 0, start the thread and wait on the semaphore. The thread then signals the semaphore when it is finished with the initialisation.
#include <semaphore.h>
// other stuff
sem_t semaphore;
void create_thread(t_game_data *game_data)
{
size_t i;
t_args args;
i = 0;
if (sem_init(&semaphore, 0, 0) == -1) // third arg is initial value
{
// error
}
args.game = game_data;
while (i < 10)
{
args.id = i;
printf("%zu CREATION\n", i);//TODO: Debug
pthread_create(&game_data->object[i]->thread_id, NULL, &do_action, args);
sem_wait(&semaphore);
i++;
}
sem_destroy(&semaphore);
}
void *do_action(void *v_args) {
t_args *args = v_args;
t_game_data *game = args->game;
size_t id = args->id;
sem_post(&semaphore);
// Rest of the thread work
return NULL;
}
Because of the synchronisation, I can reuse the args struct safely, in fact, I don't even need to malloc it - it's small so I declare it local to the function.
Having said all that, I still think John Bollinger's solution is better for this use-case but it's useful to be aware of semaphores generally.
You should consider using condition variable for this. You can find an example here http://maxim.int.ru/bookshelf/PthreadsProgram/htm/r_28.html.
Basically wait in the main thread and signal in your other threads.

Pthread synchronization using mutex not syncing words properly

The goal of this program is to copy a string taken in by user input word for word using multithreading. Each thread copies every fourth word so for instance the first thread copies the first and fifth words, the second copies the second and sixth words, etc. I have done quite a bit of research on mutex and I believe I have implemented the mutex lock properly however the string still comes up as jumbled nonsense when it prints. Can someone shed some light as to why the threads aren't synchronizing?
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
void *processString(void *);
char msg1[100];
char msg2[100];
char * reg;
char * token;
char * tokens[10];
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t = PTHREAD_COND_INITIALIZER;
int main(){
int i = 0, j;
pthread_t workers[4];
printf("Please input a string of words separated by whitespace characters: \n");
scanf("%99[^\n]", msg1); //take in a full string including whitespace characters
//tokenize string into individual words
token = strtok(msg1, " ");
while(token != NULL){
tokens[i] = (char *) malloc (sizeof(token));
tokens[i] = token;
token = strtok(NULL, " ");
i++;
}
for(j = 0; j < 4; j++){
if(pthread_create(&workers[j], NULL, processString, (void *) j))
printf("Error creating pthreads");
}
for(i = 0; i < 4; i++){
pthread_join(workers[i], NULL);
}
pthread_mutex_destroy(&lock);
printf("%s\n", msg2);
return 0;
}
//each thread copies every fourth word
void *processString(void *ptr){
int j = (int) ptr, i = 0;
pthread_mutex_lock(&lock);
while(tokens[i * 4 + j] != NULL){
reg = (char *) malloc (sizeof(tokens[i * 4 + j]));
reg = tokens[i * 4 + j];
strcat(msg2, reg);
strcat(msg2, " ");
i++;
}
pthread_mutex_unlock(&lock);
return NULL;
}
As #EOF wrote in comments, mutexes provide only mutual exclusion. They prevent multiple cooperating threads from running concurrently, but they do not inherently provide any control over the order in which they are acquired by such threads. Additionally, as I described in comments myself, mutexes do provide mutual exclusion: if one thread holds a mutex then no other thread will be able to acquire that mutex, nor proceed past an attempt to do so, until that mutex is released.
There is no native synchronization object that provides directly for making threads take turns. That's not usually what you want threads to do, after all. You can arrange for it with semaphores, but that gets messy quickly as you add more threads. A pretty clean solution involves using a shared global variable to indicate which thread's turn it is to run. Access to that variable must be protected by a mutex, since all threads involved must both read and write it, but there's a problem with that: what if the thread that currently holds the mutex is not the one whose turn it is to run?
It is possible for all the threads to loop, continuously acquiring the mutex, testing the variable, and either proceeding or releasing the mutex. Unfortunately, such a busy wait tends to perform very poorly, and in general, you can't be confident that the thread that can make progress at any given point in the execution will manage to acquire the mutex in bounded time.
This is where condition variables come in. A condition variable is a synchronization object that permits any number of threads to suspend activity until some condition is satisfied, as judged by another, non-suspended thread. Using such a tool avoids the performance-draining busy-wait, and in your case it can help ensure that all your threads get their chance to run in bounded time. The general-purpose per-thread usage model for condition variables is as follows:
acquire the mutex protecting the shared variable(s) by which to judge whether I can proceed
Test whether I can proceed. If so, jump to step 5.
I can't proceed right now. Perform a wait on the condition variable.
I have awakened from the wait; go back to step 2.
Do the work I need to do.
Broadcast a signal to wake all threads waiting on the condition variable.
Release the mutex.
Variations on that are possible, but I recommend that you do not vary from it until and unless you know exactly why you want to do so, and exactly why the variation you have in mind is safe. Note, too, that when a thread performs a wait on a condition variable associated with a given mutex, it automatically releases that mutex while it waits, and re-acquires it before returning from the wait. This allows other threads to proceed in the meantime, and, in particular, to wait on the same condition variable.
As it applies to your problem, the shared state you want your threads to test is the aforementioned variable that indicates which thread's turn it is, and the the condition you want your threads to wait on is that it has become a different thread's turn (but this is implicit in the way you use the condition variable; condition variables themselves are generic). Note also that this means that part of the work each thread must do before signaling the other threads is to update which thread's turn it is. And since each thread may need to take multiple turns, you will want to wrap the whole procedure in a loop.

While loop synchronization

I am working on a project with a user defined number of threads I am using 7 at the moment. I have a while loop that runs in each thread but I need all of the threads to wait for each other at the end of the while loop. The tricky thing is that some of the threads do not all end on the same number of times through the loop.
void *entryFunc(void *param)
{
int *i = (int *)param;
int nextPrime;
int p = latestPrime;
while(latestPrime < testLim)
{
sem_wait(&sem);
nextPrime = findNextPrime(latestPrime);
if(nextPrime != -1)
{
latestPrime = nextPrime;
p = latestPrime;
}
else
{
sem_post(&sem);
break;
}
sem_post(&sem);
if(p < 46341)
{
incrementNotPrimes(p);
}
/*
sem_wait(&sem2);
doneCount++;
sem_post(&sem2);
while(go != 1);
sem_wait(&sem2);
doneCount--;
//sem_post(&sem3);
sem_post(&sem2);
*/
}
return NULL;
}
where the chunk of code is commented out is part of my last attempt at solving this problem. That is where the functions all need to wait for each other. I have a feeling I am missing something simple.
If your problem is that on each thread, the while loop has a different numbers of iterations and some threads never reach the synchronization point after exiting the loop, you could use a barrier. Check here for an example.
However you need to decrease the number of threads at the barrier after you exit each thread. Waiting at the barrier will end after count number of threads reached that point.
So you need to update the barrier object each time a thread finishes. And make sure you do this atomically.
As I mentioned in the comments, you should use a barrier instead of a semaphore for this kind of situation, as it should be simpler to implement (barriers have been designed exactly to solve that problem). However, you may still use a semaphore with a little bit of arithmetic
arithmetic: your goal is to have all thread execute the same code path, but somehow the last thread to finish its task should wake all the other threads up. One way to achieve that is to have at the end of the function an atomic counter which each thread would decrement, and if the counter reaches 0, the thread simply calls as many time sem_post as necessary to release all the waiting threads, instead of issuing a sem_wait as the others.
A second method, this time using only a semaphore, is also possible. Since we cannot differentiate the last thread for the others, all the threads must do the same operations with the semaphore, ie try to release everyone, but also wait for the last one. So the idea is to initialize the semaphore to (1-n)*(n+1), so that each of the n-1 first threads would fail at waking up their friends with n+1 calls to sem_post, but still work toward getting the semaphore at exactly 0. Then the last thread would do the same, pushing the semaphore value to n+1, thus releasing the locked threads, and leaving room for it to also perform its sem_wait and be released immediately.
void *entryFunc(void *param)
{
int *i = (int *)param;
int nextPrime;
int p = latestPrime, j;
while(latestPrime < testLim){
nextPrime = findNextPrime(latestPrime);
if(nextPrime != -1)
{
latestPrime = nextPrime;
p = latestPrime;
}
if(p < 46341)
{
incrementNotPrimes(p);
}
}
for (j=0;j<=THREAD_COUNT;j++)
sem_post(&sem);
sem_wait(&sem);
return NULL;
}
The problem with this approach is that it doesn't deal with how the semaphore should be reset in between uses (if your program needs to repeat this mechanism, it will need to reset the semaphore value, since it will end up being 1 after this code has been executed successfully).

Resources