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

/* Test of the mechanism for showing all locks held by a thread.  Test
   the case where the earlier thread held, at the time of the access,
   some locks, at least one of which is deleted by the time the second
   access (the race) happens.  This causes problems for Helgrind's
   error reporting mechanism in that it can no longer show the deleted
   lock in the error message.x */

pthread_mutex_t mx1a;
pthread_mutex_t mx1b;
pthread_mutex_t mx2a;
pthread_mutex_t mx2b;

int x = 0;

void* child_fn1 ( void* arg )
{
   int r;
   // We are the first-accessing thread.  Take and release two locks
   // and then destroy one of them.
   r= pthread_mutex_lock(&mx1a);  assert(!r);
   r= pthread_mutex_lock(&mx1b);  assert(!r);
   x = 1;
   r= pthread_mutex_unlock(&mx1b);  assert(!r);
   r= pthread_mutex_unlock(&mx1a);  assert(!r);
   r= pthread_mutex_destroy(&mx1a);  assert(!r);
   sleep(1);
   return NULL;
}

void* child_fn2 ( void* arg )
{
   int r;
   // We are the second-accessing thread.  Take and release
   // our two locks, but don't otherwise mess with them. 
   sleep(1);
   r= pthread_mutex_lock(&mx2a);  assert(!r);
   r= pthread_mutex_lock(&mx2b);  assert(!r);
   x = 1;
   r= pthread_mutex_unlock(&mx2b);  assert(!r);
   r= pthread_mutex_unlock(&mx2a);  assert(!r);
   return NULL;
}

int main ( int argc, char** argv )
{
   pthread_t child1, child2;
   int r;

   r= pthread_mutex_init(&mx1a, NULL);  assert(!r);
   r= pthread_mutex_init(&mx1b, NULL);  assert(!r);
   r= pthread_mutex_init(&mx2a, NULL);  assert(!r);
   r= pthread_mutex_init(&mx2b, NULL);  assert(!r);

   r= pthread_create(&child2, NULL, child_fn2, NULL);  assert(!r);
   r= pthread_create(&child1, NULL, child_fn1, NULL);  assert(!r);

   r= pthread_join(child1, NULL);  assert(!r);
   r= pthread_join(child2, NULL);  assert(!r);

   // don't destroy mx1a; it's already destroyed.
   r= pthread_mutex_destroy(&mx1b);  assert(!r);
   r= pthread_mutex_destroy(&mx2a);  assert(!r);
   r= pthread_mutex_destroy(&mx2b);  assert(!r);

   return 0;
}