0

I have been trying to learn pthreads, with mutex and cond, but I can't figure out how to reset a varible ONLY after the last thread that used it doesn't need it anymore.

I have the following code:

int i = 0;
pthread_mutex_t mutex;
pthread_cond_t cond;

int test() {
  if (i == 0) {
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
  }
  pthread_mutex_lock(&mutex);
  ++i;

  if (i % 5 != 0) {
    pthread_cond_wait(&cond, &mutex);
  }

  else {
    pthread_cond_broadcast(&cond);
  }

  int j = i;

  i = 0; /* problem line */
  pthread_mutex_unlock(&mutex);
  //i = 0; /* tried here too */

  return j;
}

I have multiple threads calling this test() method.

The first 4 threads will wait until the 5th is called, and then all 5 threads will return 5. But the 6th+ will return '6','7',... etc, I want to reset the i variable i = 0, so the count will restart.

I thought of doing another thread_cond_t to make the threads wait until all j = i are finished. But I don't think that is right (or at least unnecesary).

The actual output is (undetermined order because of threads):

0
0
0
0
5
0
0
0
5

I want them all be 5s.

Edit: I made j global and i is modified after the broadcast and it has worked so far:

int i = 0, j;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int test() {
  pthread_mutex_lock(&mutex);
  ++i;

  if (i % 5 != 0) {
    pthread_cond_wait(&cond, &mutex);
  } else {
    pthread_cond_broadcast(&cond);
    j = i;
  }
  i = 0;
  pthread_mutex_unlock(&mutex);

  return j;
}

This code successfully resets the variable after reaching 5, and prints that value on all threads, but it seems I suffer from dataraces when I try many threads.

Pold
  • 1
  • 3
  • are you sure that the first thread can call the 2 init functions, the lock and the increment before the second thread starts? – mch Dec 10 '16 at 22:59
  • Actually, I had the same doubt, but couldn't guess another way to initialize both mutex and cond only once, I have not had problems with it so far (multiple tries on the code), but insight is welcomed. – Pold Dec 10 '16 at 23:02
  • 1
    can you call the init functions before you start the threads? You can also use the initializer `PTHREAD_MUTEX_INITIALIZER`. The rest of the code looks okay. It is possible that you initialize the mutex twice and so invoke undefined behaviour. – mch Dec 10 '16 at 23:08
  • I can't initialize the init() functions before starting the threads, another function (not editable) calls the `test()` method directly. Will try mutex_initializer as you say. Thank you. – Pold Dec 10 '16 at 23:10
  • You can't afford to test `i` outside the protection of the mutex, can you? I think that's the point that @mch was making too. There's a `PTHREAD_COND_INITIALIZER` too. Since those are variables at file scope and initialized to all bytes zero, then you will probably get away with it, though POSIX doesn't say that you will (that's why it provides the PTHREAD_{MUTEX,COND}_INITIALIZER macros). You might find information of interest at [Mutex status after spurious wakeup](http://stackoverflow.com/questions/41007503/mutex-status-after-spurious-wakeup), too. – Jonathan Leffler Dec 10 '16 at 23:14
  • 1
    @JonathanLeffler You're right, I have changed the code, and will edit my post now. Now it works as intended thank you both mch and JonathanLeffer. I have a new problem tho ;(, when I test big amount of threads it fails, it seems I have datarace problems, and I'm not really sure how to solve it. – Pold Dec 10 '16 at 23:39

1 Answers1

0

This code successfully resets the variable after reaching 5, and prints that value on all threads, but it seems I suffer from dataraces when I try many threads.

With the edited code, consider this course of events: Four threads are waiting in pthread_cond_wait, the fifth thread just passes pthread_cond_broadcast (causing the first four to suspend on mutex before returning from pthread_cond_wait), and a sixth thread is already waiting in pthread_mutex_lock. Now after setting i = 0 the fifth passes pthread_mutex_unlock and say the sixth resumes with ++i, setting i to 1, and thereafter goes to wait in pthread_cond_wait (causing the mutex to unlock). Then one of the first four threads resumes and passes i = 0, so the counter i is unduly reset.
To rectify this, reset i only in the thread which does the pthread_cond_broadcast, i. e. change

  }
  i = 0;

to

    i = 0;
  }

(move the i = 0 into the else { pthread_cond_broadcast block).

Armali
  • 18,255
  • 14
  • 57
  • 171