Considering the following code:
#define _XOPEN_SOURCE 600
#define _DEFAULT_SOURCE
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define ENTRY_NUM 100
struct value {
pthread_mutex_t mutex;
int i;
};
struct entry {
atomic_uintptr_t val;
};
struct entry entries[ENTRY_NUM];
void* thread1(void *arg)
{
for (int i = 0; i != ENTRY_NUM; ++i) {
struct value *val = (struct value*) atomic_load(&entries[i].val);
if (val == NULL)
continue;
pthread_mutex_lock(&val->mutex);
printf("%d\n", val->i);
pthread_mutex_unlock(&val->mutex);
}
return NULL;
}
void* thread2(void *arg)
{
/*
* Do some costy operations before continuing.
*/
usleep(1);
for (int i = 0; i != ENTRY_NUM; ++i) {
struct value *val = (struct value*) atomic_load(&entries[i].val);
pthread_mutex_lock(&val->mutex);
atomic_store(&entries[i].val, (uintptr_t) NULL);
pthread_mutex_unlock(&val->mutex);
pthread_mutex_destroy(&val->mutex);
free(val);
}
return NULL;
}
int main() {
for (int i = 0; i != ENTRY_NUM; ++i) {
struct value *val = malloc(sizeof(struct value));
pthread_mutex_init(&val->mutex, NULL);
val->i = i;
atomic_store(&entries[i].val, (uintptr_t) val);
}
pthread_t ids[2];
pthread_create(&ids[0], NULL, thread1, NULL);
pthread_create(&ids[1], NULL, thread2, NULL);
pthread_join(ids[0], NULL);
pthread_join(ids[1], NULL);
return 0;
}
Suppose in function thread1, entries[i].val is loaded, then the scheduler schedule the process to sleep.
Then thread2 awakes from usleep, since ((struct val*) entries[0].val)->mutex aren't locked, thread2 locks it, stores NULL to entries[0].val and free the original of entries[0].val.
Now, is that a race condition? If so, how to avoid this without locking entries or entries[0]?
You are correct my friend, there is indeed a race condition in such code.
Let me open by overall and saying that thread is prawn to race conditions by definition and this is also correct for any other library implemented thread which is unacknowledged by your compiler ahead of compilation time.
Regarding your specific example, yes as you've explained yourself since we can not assume when does your scheduler go into action, thread1 could atomic load your entries, context switch to thread2, which will then free these entries before thread1 gets processor time again. How do you prevent or avoid such race conditions? avoid accessing them without locking them, even though atomic load is an "atomic read" you are logically allowing other threads to access these entries. the entire code scope of both thread1 and thread2 should be protected with a mutex. despite using atomic_load, you are just guaranteeing that at that atomic time, no other accesses to that entry will be made, but during the time between the atomic_load and your first calling to pthread_mutex_lock context switches can indeed occur! as you've mentioned yourself, this is both bad practice and logically wrong. - hence as I've already stated, you should protect the entire scope with pthread_mutex_lock
In general, as I've stated in the beginning of this, considering that your compiler is unaware to the concept of threads during compilation times, it is very sensitive to race conditions that you may not even be aware of, - e.g.: when accessing different areas of some shared memory, the compiler does not take into consideration that other threads may exist and accesses some memory as it desires, and may affect different areas of memories during, even though logically the code itself doesn't, and the "correctness" of the code is valid.
there was some paper published on this called
Threads Cannot be Implemented as a Library by Hans-J Boehm I highly suggest you read it, I promise it will increase your understanding of race conditions and threads using pthread at general!
Related
My program uses an enum as a semaphore. There are two possible values/states(Because it's a binary semaphore). The program compiles fine. signal() and wait() look logical. Why is program behavior so unpredictable? Even the integer printf is buggy. Here's the code:
#include <stdio.h>
#include <pthread.h>
typedef enum {IN_USE,NOT_IN_USE} binary_semaphore;
binary_semaphore s=NOT_IN_USE;
struct parameters{
int thread_num;
};
void wait(){
while(s==IN_USE);
s=IN_USE;
}
void signal(){
s=NOT_IN_USE;
}
void resource(void *params){
//assuming parameter is a parameters struct.
struct parameters *p=(struct parameters*)params;
wait();
printf("Resource is being used by thread %d\n",(*p).thread_num);
signal();
}
int main(void){
pthread_t threads[4];
struct parameters ps[4]={{1},{2},{3},{4}};
register int counter=0;
while(counter++<4){
pthread_create(&threads[counter],NULL,resource,(void*)&ps[counter]);
}
return 0;
}
What's wrong with my code?
Some of the outputs(Yes, they're different every time):-
(NOTHING)
Resource is being used by thread 32514
Resource is being used by thread 0
Resource is being used by thread 0
Resource is being used by thread 32602
Resource is being used by thread -24547608
Is it a garbage value issue?
What's wrong with my code?
Multiple things, largely discussed in comments already. The most significant one is that your code is rife with data races. That is one of the things that semaphores are often used to protect against, but in this case it is your semaphores themselves that are racy. Undefined behavior results.
Additional issues include
A pthreads thread function must return void *, but yours returns void
You overrun the bounds of your main()'s ps and threads arrays
You do not join your threads before the program exits.
You define functions with the same names as a C standard library function (signal()) and a standard POSIX function (wait()).
Nevertheless, if your C implementation supports the atomics option then you can use that to implement a working semaphore not too different from your original code:
#include <stdio.h>
#include <pthread.h>
#include <stdatomic.h>
// This is used only for defining the enum constants
enum sem_val { NOT_IN_USE, IN_USE };
// It is vital that sem be *atomic*.
// With `stdatomic.h` included, "_Atomic int" could also be spelled "atomic_int".
_Atomic int sem = ATOMIC_VAR_INIT(NOT_IN_USE);
struct parameters{
int thread_num;
};
void my_sem_wait() {
int expected_state = NOT_IN_USE;
// See discussion below
while (!atomic_compare_exchange_strong(&sem, &expected_state, IN_USE)) {
// Reset expected_state
expected_state = NOT_IN_USE;
}
}
void my_sem_signal() {
// This assignment is performed atomically because sem has atomic type
sem = NOT_IN_USE;
}
void *resource(void *params) {
//assuming parameter is a parameters struct.
struct parameters *p = params;
my_sem_wait();
printf("Resource is being used by thread %d\n", p->thread_num);
my_sem_signal();
return NULL;
}
int main(void) {
pthread_t threads[4];
struct parameters ps[4] = {{1},{2},{3},{4}};
for (int counter = 0; counter < 4; counter++) {
pthread_create(&threads[counter], NULL, resource, &ps[counter]);
}
// It is important to join the threads if you care that they run to completion
for (int counter = 0; counter < 4; counter++) {
pthread_join(threads[counter], NULL);
}
return 0;
}
Most of that is pretty straightforward, but the my_sem_wait() function bears a little more explanation. It uses an atomic compare-and-swap to make sure that threads proceed only if they change the value of the semaphore from NOT_IN_USE to IN_USE, with the comparison and conditional assignment being performed as a single atomic unit. Specifically, this ...
atomic_compare_exchange_strong(&sem, &expected_state, IN_USE)
... says "Atomically, compare the value of sem to the value of expected_state and if they compare equal then assign value IN_USE to to sem." The function additionally sets the value of expected_state to the one read from sem if they differ, and returns the result of the equality comparison that was performed (equivalently: returns 1 if the specified value was assigned to sem and 0 if not).
It is essential that the comparison and swap be performed as an atomic unit. Individual atomic reads and writes would ensure that there is no data race, but they would not ensure correct program behavior, because two threads waiting on the semaphore could both see it available at the same time, each before the other had a chance to mark it unavailable. Both would then proceed. The atomic compare and swap prevents one thread reading the value of the semaphore between another's read and update of that value.
Do note, however, that unlike a pthreads mutex or a POSIX semaphore, this semaphore waits busily. That means threads waiting to acquire the semaphore consume CPU while they do, rather than going to sleep as threads waiting on a pthreads mutex do. That may be ok if semaphore access is usually uncontended or if threads never hold it locked very long, but under other circumstances it can make your program much more resource-hungry than it needs to be.
You are encountering race conditions as well as undefined behavior. When you use a regular int as a semaphore in multithreaded applications, there's no guarantee that a process will read a variable's value and modify it before another process is able to read it, which is why you must use a concurrency library designed for your operating system. Furthermore, the function pointer you passed to pthread_create is not the right type, which is undefined behavior.
I replaced your "semaphore" enum with a pointer to pthread_mutex_t which is initialized on the stack of main(), and each thread gets a pointer to it as a member of their struct parameter.
I also changed the definition of void resource(void* params) to void* resource(void *params) as that is a prototype that matches what pthread_create expects as its third parameter.
Your wait() and signal() functions were able to be replaced one-to-one with pthread_mutex_lock() and pthread_mutex_unlock() from pthread.h which you have already included.
#include <stdio.h>
#include <pthread.h>
struct parameters{
int thread_num;
pthread_mutex_t *mutex; //mutex could be global if you prefer
};
void* resource(void *params){ //pthread_create expects a pointer to a function that takes a void* and returns a void*
//assuming parameter is a parameters struct.
struct parameters *p = (struct parameters*)params;
pthread_mutex_lock(p->mutex);
printf("Resource is being used by thread %d\n", p->thread_num);
pthread_mutex_unlock(p->mutex);
return NULL;
}
int main(void){
pthread_t threads[4];
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
struct parameters ps[4]={{1, &mutex},{2, &mutex},{3, &mutex},{4, &mutex}};
for(int counter = 0; counter < 4; ++counter)
pthread_create(&threads[counter], NULL, resource, &ps[counter]);
//Threads should be joined
for(int counter = 0; counter < 4; ++counter)
pthread_join(threads[counter], NULL);
}
This will eliminate the stochasticity that you are experiencing.
I have a program in which multiple threads are in a loop where they acquire a binary semaphore and then increase a global counter. However, by printing out the thread IDs, I notice that only one thread ever acquires the semaphore. Here's my MRE:
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define NUM_THREADS 10
#define MAX_COUNTER 100
struct threadCtx {
sem_t sem;
unsigned int counter;
};
static void *
threadFunc(void *args)
{
struct threadCtx *ctx = args;
pthread_t self;
bool done = false;
self = pthread_self();
while (!done) {
sem_wait(&ctx->sem);
if ( ctx->counter == MAX_COUNTER ) {
done = true;
}
else {
sleep(1);
printf("Thread %u increasing the counter to %u\n", (unsigned int)self, ++ctx->counter);
}
sem_post(&ctx->sem);
}
return NULL;
}
int main() {
pthread_t threads[NUM_THREADS];
struct threadCtx ctx = {.counter = 0};
sem_init(&sem.ctx, 0, 1);
for (int k=0; k<NUM_THREADS; k++) {
pthread_create(threads+k, NULL, threadFunc, &ctx);
}
for (int k=0; k<NUM_THREADS; k++) {
pthread_join(threads[k], NULL);
}
sem_destroy(&ctx.sem);
return 0;
}
The output is
Thread 1004766976 increasing the counter to 1
Thread 1004766976 increasing the counter to 2
Thread 1004766976 increasing the counter to 3
...
If I remove the call to sleep, the behavior is closer to what I would expect (i.e., the threads being woken up in a seemingly indeterminate manner). Why would this be?
David Schwartz's answer explains what is happening at a low level. That is to say, he's looking at it from the perspective of an OS developer or a hardware designer. Nothing wrong with that, but let's look at your program from the perspective of a Software Architect:
You've got multiple threads all executing the same loop. The loop locks the mutex,* it does some "work," and then it releases the mutex. OK, but what does it do next? Almost the very next thing that your loop does after releasing the mutex is it locks the mutex again. Your loop spends practically 100% of its time doing "work" with the mutex locked.
So, what's the point of running that same loop in multiple threads when there's never any opportunity for two or more threads to work at the same time?
If you want to use threads to do a parallel computation, you need to find/invent safe ways for the threads to do most of their work with the mutex unlocked. They should only lock a mutex for just long enough to post a result or, to take another assignment.
Sometimes that means writing code that is less efficient than single threaded code would be. But suppose that program (A) has a single thread that makes almost 100% use of a CPU, while program (B) uses eight CPUs but only uses them with 50% efficiency. Which program is going to win?
* I know, your example uses a sem_t (semaphore) object. But "semaphore" is what you are using. "Mutex" is the role in which you are using it.
Why would this be?
Context switches are expensive and your implementation is, wisely, minimizing them. Your threads are all fighting over the same resource, trying to schedule them closely will make performance much worse, probably for the entire system.
Since the thread that keeps getting the semaphore never uses up its timeslice, it will keep getting the resource. It is your responsibility to write code to do the work that you want done. It's the implementation's responsibility to execute your code as efficiently as it can, and that's what it's doing.
Most likely, what's going under the hood is this:
The thread that keeps getting the sempahore can always make forward progress except when it is sleeping. But when it is sleeping, no other thread that needs the sempahore can make forward progress.
The thread that keeps getting the semaphore never exhausts its timeslice because it sleeps before that happens.
So there is no reason for the implementation to ever block this thread other than when it is sleeping, meaning that no other thread can get the semaphore. If you don't want this thread to keep sleeping with the semaphore and blocking other threads, then write different code.
I want to print numbers using two threads T1 and T2. T1 should print numbers like 1,2,3,4,5 and then T2 should print 6,7,8,9,10 and again T1 should start and then T2 should follow. It should print from 1....100. I have two questions.
How can I complete the task using threads and one global variable?
How can I schedule threads in desired order in linux?
How can I schedule threads in desired order in linux?
You need to use locking primitives, such as mutexes or condition variables to influence scheduling order of threads. Or you have to make your threads work independently on the order.
How can I complete the task using threads and one global variable?
If you are allowed to use only one variable, then you can't use mutex (it will be the second variable). So the first thing you must do is to declare your variable atomic. Otherwise compiler may optimize your code in such a way that one thread will not see changes made by other thread. And for such simple code that you want, it will do so by caching variable on register. Use std::atomic_int. You may find an advice to use volatile int, but nowdays std::atomic_int is a more direct approach to specify what you want.
You can't use mutexes, so you can't make your threads wait. They will be constantly running and wasting CPU. But that's seems OK for the task. So you will need to write a spinlock. Threads will wait in a loop constantly checking the value. If value % 10 < 5 then first thread breaks the loop and does incrementing, otherwise second thread does the job.
Because the question looks like a homework I don't show here any code samples, you need to write them yourself.
1: the easiest way is to use mutexes.
this is a basic implementation with a unfair/undefined sheduling
int counter=1;
pthread_mutex_t mutex; //needs to be initialised
void incrementGlobal() {
for(int i=0;i<5;i++){
counter++;
printf("%i\n",counter);
}
}
T1/T2:
pthread_mutex_lock(&mutex);
incrementGlobal();
pthread_mutex_unlock(&mutex);
2: the correct order can be archieved with conditional-variables:
(but this needs more global-variables)
global:
int num_thread=2;
int current_thread_id=0;
pthread_cond_t cond; //needs to be initialised
T1/T2:
int local_thread_id; // the ID of the thread
while(true) {
phread_mutex_lock(&mutex);
while (current_thread_id != local_thread_id) {
pthread_cond_wait(&cond, &mutex);
}
incrementGlobal();
current_thread_id = (current_thread_id+1) % num_threads;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
}
How can I complete the task using threads and one global variable?
If you are using Linux, you can use POSIX library for threading in C programming Language. The library is <pthread.h>. However, as an alternative, a quite portable and relatively non-intrusive library, well supported on gcc and g++ and (with an old version) on MSVC is openMP.
It is not standard C and C++, but OpenMP itself is a standard.
How can I schedule threads in desired order in linux?
To achieve your desired operation of printing, you need to have a global variable which can be accessed by two threads of yours. Both threads take turns to access the global variable variable and perform operation (increment and print). However, to achieve desired order, you need to have a mutex. A mutex is a mutual exclusion semaphore, a special variant of a semaphore that only allows one locker at a time. It can be used when you have an instance of resource (global variable in your case) and that resource is being shared by two threads. A thread after locking that mutex can have exclusive access to an instance of resource and after completing it's operation, a thread should release the mutex for other threads.
You can start with threads and mutex in <pthread.h> from here.
The one of the possible solution to your problem could be this program is given below. However, i would suggest you to find try it yourself and then look at my solution.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
pthread_mutex_t lock;
int variable=0;
#define ONE_TIME_INC 5
#define MAX 100
void *thread1(void *arg)
{
while (1) {
pthread_mutex_lock(&lock);
printf("Thread1: \n");
int i;
for (i=0; i<ONE_TIME_INC; i++) {
if (variable >= MAX)
goto RETURN;
printf("\t\t%d\n", ++variable);
}
printf("Thread1: Sleeping\n");
pthread_mutex_unlock(&lock);
usleep(1000);
}
RETURN:
pthread_mutex_unlock(&lock);
return NULL;
}
void *thread2(void *arg)
{
while (1) {
pthread_mutex_lock(&lock);
printf("Thread2: \n");
int i;
for (i=0; i<ONE_TIME_INC; i++) {
if (variable >= MAX)
goto RETURN;
printf("%d\n", ++variable);
}
printf("Thread2: Sleeping\n");
pthread_mutex_unlock(&lock);
usleep(1000);
}
RETURN:
pthread_mutex_unlock(&lock);
return NULL;
}
int main()
{
if (pthread_mutex_init(&lock, NULL) != 0) {
printf("\n mutex init failed\n");
return 1;
}
pthread_t pthread1, pthread2;
if (pthread_create(&pthread1, NULL, thread1, NULL))
return -1;
if (pthread_create(&pthread2, NULL, thread2, NULL))
return -1;
pthread_join(pthread1, NULL);
pthread_join(pthread2, NULL);
pthread_mutex_destroy(&lock);
return 0;
}
I was trying to implement a multi-threaded program using binary semaphore. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <semaphore.h>
int g = 0;
sem_t *semaphore;
void *myThreadFun(void *vargp)
{
int myid = (int)vargp;
static int s = 0;
sem_wait(semaphore);
++s; ++g;
printf("Thread ID: %d, Static: %d, Global: %d\n", myid, s, g);
fflush(stdout);
sem_post(semaphore);
pthread_exit(0);
}
int main()
{
int i;
pthread_t tid;
if ((semaphore = sem_open("/semaphore", O_CREAT, 0644, 3))==SEM_FAILED) {
printf("semaphore initialization failed\n");
}
for (i = 0; i < 3; i++) {
pthread_create(&tid, NULL, myThreadFun, (void *)i);
}
pthread_exit(NULL);
return 0;
}
Now, when I opened the sempahore, I made the count 3. I was expecting that this wouldnt work, and I would get race condition, because each thread is now capable of decrementing the count.
Is there something wrong with the implementation? Also, if I make the count 0 during sem_open, wouldnt that initiate a deadlock condition, because all the threads should be blocked on sem_wait.
Now, when I opened the sempahore, I made the count 3. I was expecting that this wouldnt work, and I would get race condition, because each thread is now capable of decrementing the count.
And how do you judge that there isn't any race? Observing output consistent with what you could rely upon in the absence of a data race in no way proves that there isn't a data race. It merely fails provide any evidence of one.
However, you seem to be suggesting that there would be a data race inherent in more than one thread concurrently performing a sem_wait() on a semaphore whose value is initially greater than 1 (otherwise which counter are you talking about?). But that's utter nonsense. You're talking about a semaphore. It's a synchronization object. Such objects and the functions that manipulate them are the basis for thread synchronization. They themselves are either completely thread safe or terminally buggy.
Now, you are correct that you open the semaphore with an initial count sufficient to avoid any of your threads blocking in sem_wait(), and that therefore they can all run concurrently in the whole body of myThreadFun(). You have not established, however, that they in fact do run concurrently. There are several reasons why they might not do. If they do run concurrently, then the incrementing of shared variables s and g is indeed of concern, but again, even if you see no signs of a data race, that doesn't mean there isn't one.
Everything else aside, the fact that your threads all call sem_wait(), sem_post(), and printf() induces some synchronization in the form of memory barriers, which would reduce the likelihood of observing anomalous effects on s and g. sem_wait() and sem_post() must contain memory barriers in order to function correctly, regardless of the semaphore's current count. printf() calls are required to use locking to protect the state of the stream from corruption in multi-threaded programs, and it is reasonable to suppose that this will require a memory barrier.
Is there something wrong with the implementation?
Yes. It is not properly synchronized. Initialize the semaphore with count 1 so that the modifications of s and g occur only while exactly one thread has the semaphore locked.
Also, if I make the count 0 during sem_open, wouldnt that initiate a deadlock condition, because all the threads should be blocked on sem_wait.
If the semaphore has count 0 before any of the additional threads are started, then yes. It is therefore inappropriate to open the semaphore with count 0, unless you also subsequently post to it before starting the threads. But you are using a named semaphore. These persist until removed, and you never remove it. The count you specify to sem_open() has no effect unless a new semaphore needs to be created; when an existing semaphore is opened, its count is unchanged.
Also, do have the main thread join all the others before it terminates. It's not inherently wrong not to do so, but in most cases it's required for the semantics you want.
I'll get to the code in a bit that proves you had a race condition. I'll add a couple of different ways to trigger it so you can see how this works. I'm doing this on Linux and passing -std=gnu99 as a param to gcc ie
gcc -Wall -pedantic -lpthread -std=gnu99 semaphore.c -o semtex
Note. In your original example (assuming Linux) one fatal mistake you had was not removing the semaphore. If you run the following command you might see some of these sitting around on your machine
ls -la /dev/shm/sem.*
You need to make sure that there are no old semaphore sitting on the filesystem before running your program or you end up picking up the last settings from the old semaphore. You need to use sem_unlink to clean up.
To run it please use the following.
rm /dev/shm/sem.semtex;./semtex
I'm deliberately making sure that the semaphore is not there before running because if you have a DEADLOCK it can be left around and that causes all sorts of problems when testing.
Now, when I opened the sempahore, I made the count 3. I was expecting
that this wouldn't work, and I would get race condition, because each
thread is now capable of decrementing the count.
You had a race condition but sometimes C is so damned fast things can appear to work because your program can get everything it needs done in the time the OS has allocated to the thread ie the OS didn't preempt it at an important point.
This is one of those cases, the race condition is there you just need to squint a little bit to see it. In the following code you can tweak some parameters to see the a deadlock, correct usage and Undefined Behavior.
#define INITIAL_SEMAPHORE_VALUE CORRECT
The value INITIAL_SEMAPHORE_VALUE can take three values...
#define DEADLOCK 0
#define CORRECT 1
#define INCORRECT 2
I hope they're self explanatory. You can also use two methods to cause the race condition to blow up the program.
#define METHOD sleep
Set the METHOD to spin and you can play with the SPIN_COUNT and find out how many times the loop can run before you actually see a problem, this is C, it can get a lot done before it gets preempted. The code has most of the rest of the information you need in it.
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define DEADLOCK 0 // DEADLOCK
#define CORRECT 1 // CORRECT
#define INCORRECT 2 // INCORRECT
/*
* Change the following values to observe what happen.
*/
#define INITIAL_SEMAPHORE_VALUE CORRECT
//The next value provides to two different ways to trigger the problem, one
//using a tight loop and the other using a system call.
#define METHOD sleep
#if (METHOD == spin)
/* You need to increase the SPIN_COUNT to a value that's big enough that the
* kernel preempts the thread to see it fail. The value set here worked for me
* in a VM but might not work for you, tweak it. */
#define SPIN_COUNT 1000000
#else
/* The reason we can use such a small time for USLEEP is because we're making
* the kernel preempt the thread by using a system call.*/
#define USLEEP_TIME 1
#endif
#define TOT_THREADS 10
static int g = 0;
static int ret = 1729;
sem_t *semaphore;
void *myThreadFun(void *vargp) {
int myid = (int)vargp;
int w = 0;
static int s = 0;
if((w = sem_wait(semaphore)) != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
abort();
};
/* This is the interesting part... Between updating `s` and `g` we add
* a delay using one of two methods. */
s++;
#if ( METHOD == spin )
int spin = 0;
while(spin < SPIN_COUNT) {
spin++;
}
#else
usleep(USLEEP_TIME);
#endif
g++;
if(s != g) {
fprintf(stderr, "Fatal Error: s != g in thread: %d, s: %d, g: %d\n", myid, s, g);
abort();
}
printf("Thread ID: %d, Static: %d, Global: %d\n", myid, s, g);
// It's a false sense of security if you think the assert will fail on a race
// condition when you get the params to sem_open wrong It might not be
// detected.
assert(s == g);
if((w = sem_post(semaphore)) != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
abort();
};
return &ret;
}
int main(void){
int i;
void *status;
const char *semaphore_name = "semtex";
pthread_t tids[TOT_THREADS];
if((semaphore = sem_open(semaphore_name, O_CREAT, 0644, INITIAL_SEMAPHORE_VALUE)) == SEM_FAILED) {
fprintf(stderr, "Fatal Error: %s\n", strerror(errno));
abort();
}
for (i = 0; i < TOT_THREADS; i++) {
pthread_create(&tids[i], NULL, myThreadFun, (void *) (intptr_t) i);
}
for (i = 0; i < TOT_THREADS; i++) {
pthread_join(tids[i], &status);
assert(*(int*)status == 1729);
}
/*The following line was missing from your original code*/
sem_unlink(semaphore_name);
pthread_exit(0);
}
The following code is a simple thread game, that switches between threads causing the timer to decrease.
It works fine for 3 threads, causes and Abort(core dumped) for 4 threads, and causes a seg fault for 5 or more threads.
Anyone have any idea why this might be happening?
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <assert.h>
int volatile num_of_threads;
int volatile time_per_round;
int volatile time_left;
int volatile turn_id;
int volatile thread_running;
int volatile can_check;
void * player (void * id_in){
int id= (int)id_in;
while(1){
if(can_check){
if (time_left<=0){
break;
}
can_check=0;
if(thread_running){
if(turn_id==id-1){
turn_id=random()%num_of_threads;
time_left--;
}
}
can_check=1;
}
}
pthread_exit(NULL);
}
int main(int argc, char *args[]){
int i;
int buffer;
pthread_t * threads =(pthread_t *)malloc(num_of_threads*sizeof(pthread_t));
thread_running=0;
num_of_threads=atoi(args[1]);
can_check=0;
time_per_round = atoi(args[2]);
time_left=time_per_round;
srandom(time(NULL));
//Create Threads
for (i=0;i<num_of_threads;i++){
do{
buffer=pthread_create(&threads[i],NULL,player,(void *)(i+1));
}while(buffer == EAGAIN);
}
can_check=1;
time_left=time_per_round;
turn_id=random()%num_of_threads;
thread_running=1;
for (i=0;i<num_of_threads;i++){
assert(!pthread_join(threads[i], NULL));
}
return 0;
}
See below on why you should not depend on volatile in pthreads. However, your specific problem is probably because you malloc your pthread array, based on the num_of_threads variable before you've actually set num_of_thread from argv[]:
pthread_t *threads = (pthread_t *)malloc (num_of_threads * sizeof (pthread_t));
thread_running = 0;
num_of_threads = atoi (args[1]);
So there's a very good chance you're writing beyond the end of the threads array. The num_of_threads variable will probably be zero on start-up which means you're not allocating what you think you are. Move the allocation to after the extraction of the arguments and that should fix it.
And now, for your viewing pleasure :-), my original rant on the unsafe use of volatile, which I still stand by.
Do not rely on volatile to protect your shared variables. The correct way to do this is with the pthread_mutex_blab_blah_blah calls.
Of particular note, examine this code segment:
if (can_check) {
if (time_left <= 0) {
break;
}
// URK!!
can_check=0;
URK!! is the point where your current thread may be switched out and another run, leading to the possibility that two threads can be running a critical section of code.
My advice is to forget the can_check altogether and just protect all the shared variables with a mutex, something like (from memory):
void *player (void * id_in) {
int id = (int)id_in;
while (1) {
pthread_mutex_lock (&mutex);
if (time_left <= 0) {
pthread_mutex_unlock (&mutex);
break;
}
if (thread_running) {
if (turn_id == id-1) {
turn_id = random() % num_of_threads;
time_left--;
}
}
pthread_mutex_unlock (&mutex);
}
pthread_exit(NULL);
}
Then put at file-level:
pthread_mutexattr_t mutexattr; // prob. not needed at file level.
pthread_mutex_t mutex;
and, in main, before starting any other threads:
pthread_mutexattr_init (&mutexattr);
// Change attributes if needed.
pthread_mutex_init (&mutex, &mutex_attr);
// Then run all you other stuff here, make sure you've joined with all threads.
pthread_mutex_destroy (&mutex);
Oh yeah, although I haven't done it, you should also check the return codes for all those mutex calls. I'm not going to add that since it'll clog up the answer with unnecessary detail, but it's good practice.