/**
 * @file lock/lock.c  Pthread mutex locking
 *
 * Copyright (C) 2010 Creytiv.com
 */
#define _DEFAULT_SOURCE 1
#define __USE_UNIX98 1
#include <pthread.h>
#include <re_types.h>
#include <re_mem.h>
#include <re_lock.h>


#define DEBUG_MODULE "lock"
#define DEBUG_LEVEL 5
#include <re_dbg.h>


#ifndef RELEASE
#define LOCK_DEBUG 0
#endif


/** Defines a lock */
struct lock {
	pthread_mutex_t m;
};


static void lock_destructor(void *data)
{
	struct lock *l = data;

	int err = pthread_mutex_destroy(&l->m);
	if (err) {
		DEBUG_WARNING("pthread_mutex_destroy: %m\n", err);
	}
}


/**
 * Allocate a new lock
 *
 * @param lp Pointer to allocated lock object
 *
 * @return 0 if success, otherwise errorcode
 */
int lock_alloc(struct lock **lp)
{
	pthread_mutexattr_t attr;
	struct lock *l;

	if (!lp)
		return EINVAL;

	l = mem_zalloc(sizeof(*l), lock_destructor);
	if (!l)
		return ENOMEM;

	(void)pthread_mutex_init(&l->m, NULL);

	pthread_mutexattr_init(&attr);

#if LOCK_DEBUG
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
	DEBUG_NOTICE("init debug lock\n");
#else
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
#endif
	pthread_mutex_init(&l->m, &attr);

	*lp = l;
	return 0;
}


/**
 * Get the lock for reading
 *
 * @param l Lock object
 */
void lock_read_get(struct lock *l)
{
	const int err = pthread_mutex_lock(&l->m);
	if (err) {
		DEBUG_WARNING("lock_read_get: %m\n", err);
	}
}


/**
 * Get the lock for writing
 *
 * @param l Lock object
 */
void lock_write_get(struct lock *l)
{
	const int err = pthread_mutex_lock(&l->m);
	if (err) {
		DEBUG_WARNING("lock_write_get: %m\n", err);
	}
}


/**
 * Attempt to get a lock for reading
 *
 * @param l Lock object
 *
 * @return 0 if success, otherwise errorcode
 */
int lock_read_try(struct lock *l)
{
	return pthread_mutex_trylock(&l->m);
}


/**
 * Attempt to get a lock for writing
 *
 * @param l Lock object
 *
 * @return 0 if success, otherwise errorcode
 */
int lock_write_try(struct lock *l)
{
	return pthread_mutex_trylock(&l->m);
}


/**
 * Release a lock
 *
 * @param l Lock object
 */
void lock_rel(struct lock *l)
{
	const int err = pthread_mutex_unlock(&l->m);
	if (err) {
		DEBUG_WARNING("lock_rel: %m\n", err);
	}
}
