
//                              -*- Mode: CFA -*-
//
// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
//
// The contents of this file are covered under the licence agreement in the
// file "LICENCE" distributed with Cforall.
//
// mutex.c --
//
// Author           : Thierry Delisle
// Created On       : Fri May 25 01:37:11 2018
// Last Modified By : Thierry Delisle
// Last Modified On : Fri May 25 01:37:51 2018
// Update Count     : 0
//

#include "mutex"

#include "kernel_private.h"

//-----------------------------------------------------------------------------
// Locks

// Exclusive lock - non-recursive
// ---
void ?{}(mutex_lock & this) {
	this.lock{};
	this.blocked_threads{};
}

void ^?{}(mutex_lock & this) {
	// default
}

void lock(mutex_lock & this) with(this) {
	lock( lock __cfaabi_dbg_ctx2 );
	if( is_locked ) {
		append( blocked_threads, kernelTLS.this_thread );
		BlockInternal( &lock );
	}
	else {
		is_locked = true;
		unlock( lock );
	}
}

bool try_lock(mutex_lock & this) with(this) {
	bool ret = false;
	lock( lock __cfaabi_dbg_ctx2 );
	if( !is_locked ) {
		ret = true;
		is_locked = true;
	}
	unlock( lock );
	return ret;
}

void unlock(mutex_lock & this) {
	lock( this.lock __cfaabi_dbg_ctx2 );
	this.is_locked = (this.blocked_threads != 0);
	WakeThread(
		pop_head( this.blocked_threads )
	);
	unlock( this.lock );
}

// Exclusive lock - non-recursive
// ---
void ?{}(recursive_mutex_lock & this) {
	this.lock{};
	this.blocked_threads{};
	this.owner = NULL;
	this.recursion_count = 0;
}

void ^?{}(recursive_mutex_lock & this) {
	// default
}

void lock(recursive_mutex_lock & this) with(this) {
	lock( lock __cfaabi_dbg_ctx2 );
	if( owner == NULL ) {
		owner = kernelTLS.this_thread;
		recursion_count = 1;
		unlock( lock );
	}
	else if( owner == kernelTLS.this_thread ) {
		recursion_count++;
		unlock( lock );
	}
	else {
		append( blocked_threads, kernelTLS.this_thread );
		BlockInternal( &lock );
	}
}

bool try_lock(recursive_mutex_lock & this) with(this) {
	bool ret = false;
	lock( lock __cfaabi_dbg_ctx2 );
	if( owner == NULL ) {
		owner = kernelTLS.this_thread;
		recursion_count = 1;
		ret = true;
	}
	else if( owner == kernelTLS.this_thread ) {
		recursion_count++;
		ret = true;
	}
	unlock( lock );
	return ret;
}

void unlock(recursive_mutex_lock & this) with(this) {
	lock( lock __cfaabi_dbg_ctx2 );
	recursion_count--;
	if( recursion_count == 0 ) {
		thread_desc * thrd = pop_head( blocked_threads );
		owner = thrd;
		recursion_count = (thrd ? 1 : 0);
		WakeThread( thrd );
	}
	unlock( lock );
}

//-----------------------------------------------------------------------------
// Conditions
void ?{}(condition_variable & this) {
	this.blocked_threads{};
}

void ^?{}(condition_variable & this) {
	// default
}

void notify_one(condition_variable & this) with(this) {
	lock( lock __cfaabi_dbg_ctx2 );
	WakeThread(
		pop_head( this.blocked_threads )
	);
	unlock( lock );
}

void notify_all(condition_variable & this) with(this) {
	lock( lock __cfaabi_dbg_ctx2 );
	while(this.blocked_threads) {
		WakeThread(
			pop_head( this.blocked_threads )
		);
	}
	unlock( lock );
}

void wait(condition_variable & this) {
	lock( this.lock __cfaabi_dbg_ctx2 );
	append( this.blocked_threads, kernelTLS.this_thread );
	BlockInternal( &this.lock );
}

forall(dtype L | is_lock(L))
void wait(condition_variable & this, L & l) {
	lock( this.lock __cfaabi_dbg_ctx2 );
	append( this.blocked_threads, kernelTLS.this_thread );
	void __unlock(void) {
		unlock(l);
		unlock(this.lock);
	}
	BlockInternal( __unlock );
	lock(l);
}

//-----------------------------------------------------------------------------
// Scopes
forall(dtype L | is_lock(L))
void lock_all  ( L * locks[], size_t count) {
	// Sort locks based on addresses
	__libcfa_small_sort(locks, count);

	// Lock all
	for(size_t i = 0; i < count; i++) {
		L * l = locks[i];
		lock( *l );
	}
}

forall(dtype L | is_lock(L))
void unlock_all( L * locks[], size_t count) {
	// Lock all
	for(size_t i = 0; i < count; i++) {
		L * l = locks[i];
		unlock( *l );
	}
}