/*
 * Thread 0 acorda todas as threads, mas uma delas volta a dormir.
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

volatile int s = 0;  

pthread_cond_t cond;
pthread_mutex_t mutex;


int preciso_esperar(int i) {
  return (s >= 0 || s == -i);
}

void marca_thread_i_vai_dormir(int i) {
  if (s >= 0)
    s = i;
}

void *thread_i(void* v) {
  int i = (int) v;
  
  /* sleep(random() % 5); */

  pthread_mutex_lock(&mutex);
  while (preciso_esperar(i)) {
    printf("Thread %d vai esperar... \n", i);
    marca_thread_i_vai_dormir(i);
    pthread_cond_wait(&cond, &mutex);
    printf("Thread %d acordou... \n", i);
  }
  pthread_mutex_unlock(&mutex);
  
  return NULL;
}

int devo_acordar_alguma_thread() {
  return (s > 0);
}

void marca_thread_0_ja_executou() {
  s = -s;
}

void *thread_0(void* v) {
 
  /*  sleep(2); */

  pthread_mutex_lock(&mutex);
  if (devo_acordar_alguma_thread()) {
    printf("Thread 0 vai acordar todas as threads... \n");
    pthread_cond_broadcast(&cond);
  }
  marca_thread_0_ja_executou();
  pthread_mutex_unlock(&mutex);
  
  return NULL;
}

#define N 5
int main() {
  pthread_t thr[N];
  int i;
  
  pthread_mutex_init(&mutex, NULL);
  pthread_cond_init(&cond, NULL);

  for (i = 1; i < N; i++)
    pthread_create(&thr[i], NULL, thread_i, (void *) i);
  pthread_create(&thr[0], NULL, thread_0, NULL);

  for (i = 1; i < N; i++)
    pthread_join(thr[i], NULL);
  pthread_join(thr[0], NULL);

  pthread_mutex_destroy(&mutex);
  pthread_cond_destroy(&cond);
  
  return 0;
}