/**
 * @file mutex.c
 * @brief Implementation of mutexes.
 * 
 * Mutexes are implemented as binar semaphores with a handle to a thread owning it.
 * There are implemented two grups of functions in this file. Firs are debug and the second
 * is non debug.
 * The difference betwen it is, that first
 * @see semaphores.c 
 *  
 * This file is based on Kalisto, Development Kernel copyrighted (c) to 
 * Distributed Systems Research Group MFF UK, Czech republic.
 */
 
#include "mutex.h"
#include "debug.h"
#include "semaphore.h"
#include "errnums.h"
#include "int.h"
#include "io.h"

/**
 * @brief Initilaization of the mutex.
 * This function initiliaze structure for the mutex.
 */

void mutex_init (mutex_t * mtx){
	sem_init(&(mtx->sem), 1);
	mtx->owner = NULL;
}

/**
 * @brief Free semaphore structures.
 * Function call panic if any thread is waiting on semaphore.
 * 
 * @param mtx - mutex to be destroyed
 */  
void mutex_destroy (mutex_t * mtx){
	sem_destroy(&(mtx->sem));
}


/**
 * @brief Attempts to lock mutex, it blocks calling thread on mutex if mutex already locked
 * 
 * Non debug version, dont care if thread unlocking the mutex is owner of it
 * 
 * @param mtx - mutex to be locked
 */        
void mutex_lock_nondebug (mutex_t * mtx){
	/*lock == put down semaphore */
	sem_down(&(mtx->sem));
}

/**
 * @brief Attempts to lock mutex, it blocks calling thread on mutex if mutex already locked
 * 
 * This is a Debug version of this function. If thread unlocking is not owner
 * cause kernel panic
 * 
 * @param mtx - mutex to be locked
 */    
void mutex_lock_debug (mutex_t * mtx){
	// lock == put down semaphore 
	sem_down(&(mtx->sem));
	// set owner = thread who is locking the semaphore (mutex)
	mtx->owner = thread_get_current();
}

/**
 * @brief An attempt to lock mutex with maximum usec waiting time
 * 
 * Non debug version, dont care if thread unlocking the mutex is owner of it
 * 
 * @param mtx - mutex to
 * @param usec in usec to wait on the semaphore.
 *  
 * @return ETIMEDOUT on non-succesfull attempt to lock mutex before waiting time, EOK else
 */
int mutex_lock_timeout_nondebug (mutex_t * mtx, const unsigned int usec){
	int res;

	res = sem_down_timeout(&(mtx->sem), usec);
	/* return succes or unsucces */	
	return res;
}


/**
 * @brief An attempt to lock mutex with maximum usec waiting time
 * 
 * This is a Debug version of this function. If thread unlocking is not owner
 * cause kernel panic
 * 
 * @param mtx - mutex to
 * @param usec in usec to wait on the semaphore.
 *  
 * @return ETIMEDOUT on non-succesfull attempt to lock mutex before waiting time, EOK else
 */
int mutex_lock_timeout_debug (mutex_t * mtx, const unsigned int usec){
	int res;

	res = sem_down_timeout(&(mtx->sem), usec);
	// if debuging is enabled, write thread owner
	if (res == EOK)
		mtx->owner = thread_get_current();
   
	// return succes or unsucces	
	return res;
}

/**
 * @brief unlock mutex, wake up all threads waiting on it.
 * 
 * Function unlock mutex, wakeup all the threads vaiting on synchro primitives.
 * Non debug version, dont care if thread unlocking the mutex is owner of it
 * 
 * @param mtx - mutex to be unlocked
 */ 
void mutex_unlock_nondebug (mutex_t * mtx){
	save_and_disable_interrupts();
	/* unlock mutex */
	if (!mtx->sem.value)
			#ifdef MUTEX_DEBUG
			printk("Current thread [%d] living at %p unlocks mutex %p\n", current_thread->id, current_thread, mtx);
			#endif 
	sem_up(&(mtx->sem));
	restore_interrupts();
}

/**
 * @brief unlock mutex, wake up all threads waiting on it.
 * 
 * Function unlock mutex, wakeup all the threads vaiting on synchro primitives.
 * This is a Debug version of this function. If thread unlocking is not owner
 * cause kernel panic
 * 
 * @param mtx - mutex to be unlocked
 */ 
void mutex_unlock_debug (mutex_t * mtx){
	thread_t thr = thread_get_current();
			#ifdef MUTEX_DEBUG
			printk("Current thread [%d] living at %p is unlocking mutex with owner thread %[d] living at %p\n", thr->id, thr, mtx->owner->id, mtx->owner);
			#endif 
	/* check if the unlocking thread is owner, if debuging is allowed */
	if (thr != mtx->owner) {
			panic("ERROR: this thread can not unlock thread, is not owner of the lock\n");		
		return;
	}
	save_and_disable_interrupts();
	/* unlock mutex */
	if (!mtx->sem.value)
			#ifdef MUTEX_DEBUG
			printk("Current thread [%d] living at %p unlocks mutex %p\n", current_thread->id, current_thread, mtx);
			#endif 
	sem_up(&(mtx->sem));
	restore_interrupts();
}
