// RUN: %clang_cc1 -analyze -analyzer-checker=experimental.unix.PthreadLock -verify %s

// Tests performing normal locking patterns and wrong locking orders

typedef struct {
	void	*foo;
} pthread_mutex_t;

typedef pthread_mutex_t lck_mtx_t;

extern int pthread_mutex_lock(pthread_mutex_t *);
extern int pthread_mutex_unlock(pthread_mutex_t *);
extern int pthread_mutex_trylock(pthread_mutex_t *);
extern int lck_mtx_lock(lck_mtx_t *);
extern int lck_mtx_unlock(lck_mtx_t *);
extern int lck_mtx_try_lock(lck_mtx_t *);

pthread_mutex_t mtx1, mtx2;
lck_mtx_t lck1, lck2;

void
ok1(void)
{
	pthread_mutex_lock(&mtx1); // no-warning
}

void
ok2(void)
{
	pthread_mutex_unlock(&mtx1); // no-warning
}

void
ok3(void)
{
	pthread_mutex_lock(&mtx1);	// no-warning
	pthread_mutex_unlock(&mtx1);	// no-warning
	pthread_mutex_lock(&mtx1);	// no-warning
	pthread_mutex_unlock(&mtx1);	// no-warning
}

void
ok4(void)
{
	pthread_mutex_lock(&mtx1);	// no-warning
	pthread_mutex_unlock(&mtx1);	// no-warning
	pthread_mutex_lock(&mtx2);	// no-warning
	pthread_mutex_unlock(&mtx2);	// no-warning
}

void
ok5(void)
{
	if (pthread_mutex_trylock(&mtx1) == 0)	// no-warning
		pthread_mutex_unlock(&mtx1);	// no-warning
}

void
ok6(void)
{
	lck_mtx_lock(&lck1);		// no-warning
}

void
ok7(void)
{
	if (lck_mtx_try_lock(&lck1) != 0)	// no-warning
		lck_mtx_unlock(&lck1);		// no-warning
}

void
bad1(void)
{
	pthread_mutex_lock(&mtx1);	// no-warning
	pthread_mutex_lock(&mtx1);	// expected-warning{{This lock has already been acquired}}
}

void
bad2(void)
{
	pthread_mutex_lock(&mtx1);	// no-warning
	pthread_mutex_unlock(&mtx1);	// no-warning
	pthread_mutex_lock(&mtx1);	// no-warning
	pthread_mutex_lock(&mtx1);	// expected-warning{{This lock has already been acquired}}
}

void
bad3(void)
{
	pthread_mutex_lock(&mtx1);	// no-warning
	pthread_mutex_lock(&mtx2);	// no-warning
	pthread_mutex_unlock(&mtx1);	// expected-warning{{This was not the most recently acquired lock}}
	pthread_mutex_unlock(&mtx2);
}

void
bad4(void)
{
	if (pthread_mutex_trylock(&mtx1)) // no-warning
		return;
	pthread_mutex_lock(&mtx2);	// no-warning
	pthread_mutex_unlock(&mtx1);	// expected-warning{{This was not the most recently acquired lock}}
}

void
bad5(void)
{
	lck_mtx_lock(&lck1);	// no-warning
	lck_mtx_lock(&lck1);	// expected-warning{{This lock has already been acquired}}
}

void
bad6(void)
{
	lck_mtx_lock(&lck1);	// no-warning
	lck_mtx_unlock(&lck1);	// no-warning
	lck_mtx_lock(&lck1);	// no-warning
	lck_mtx_lock(&lck1);	// expected-warning{{This lock has already been acquired}}
}

void
bad7(void)
{
	lck_mtx_lock(&lck1);	// no-warning
	lck_mtx_lock(&lck2);	// no-warning
	lck_mtx_unlock(&lck1);	// expected-warning{{This was not the most recently acquired lock}}
	lck_mtx_unlock(&lck2);
}

void
bad8(void)
{
	if (lck_mtx_try_lock(&lck1) == 0) // no-warning
		return;
	lck_mtx_lock(&lck2);		// no-warning
	lck_mtx_unlock(&lck1);		// expected-warning{{This was not the most recently acquired lock}}
}
