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

pthread_mutex_t lock;

void* f_thread1(void *v) {

  pthread_mutex_lock(&lock);

  /*
    Dorme para que a thread2 execute o unlock antes da
    thread1 e assim possamos ilustrar a situação de erro.
   */
  sleep(2);

  if (pthread_mutex_unlock(&lock) == EPERM) {
    printf("Erro não esperado.\n");
  } else {
    printf("Unlock efetuado com sucesso.\n");
  }

  return NULL;
}

                      
void* f_thread2(void *v) {
  
  /*
    Faz com que a thread2 execute o unlock depois
    do lock efetuado pela thread1.
  */
  sleep(1);

  /*
    Mutexes do tipo RECURSIVE também verificam no momento do unlock
    se a thread que está executando o unlock é a mesma que executou
    o lock.

    Caso isto seja verdadeiro, a função pthread_mutex_lock retorna
    imediatamente EPERM, sinalizando o erro para quem a chamou.
   */
  if (pthread_mutex_unlock(&lock) == EPERM) {
    printf("Unlock de mutex que foi lockado por outra thread.\n");
  }

  return NULL;
}

int main() {
  pthread_t thr1, thr2;

  /*
    Este exemplo inicializa o mutex utilizando as funções de
    configuração de atributos.

    Os atributos são do tipo pthread_mutexattr_t e devem ser
    inicializados através da função pthread_mutexattr_init.

    O tipo do mutex pode ser configurado através da função
    pthread_mutexattr_settype.

   */
  pthread_mutexattr_t attr;
  pthread_mutexattr_init(&attr);
  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);

  pthread_mutex_init(&lock, &attr);

  pthread_create(&thr1, NULL, f_thread1, NULL);
  pthread_create(&thr2, NULL, f_thread2, NULL);
  pthread_join(thr1, NULL);
  pthread_join(thr2, NULL);

  printf("Finalizando programa\n");

  return 0;
}