/*
 * busylock.c - local kernel busy waiting lock implementation.
 *
 * Authors     Alain Greiner (2016,2017,2018,2019,2020)
 *
 * Copyright (c) UPMC Sorbonne Universites
 *
 * This file is part of ALMOS-MKH.
 *
 * ALMOS-MKH is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2.0 of the License.
 *
 * ALMOS-MKH is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ALMOS-MKH; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <kernel_config.h>
#include <hal_kernel_types.h>
#include <hal_irqmask.h>
#include <hal_special.h>
#include <hal_atomic.h>
#include <thread.h>
#include <busylock.h>

//////////////////////////////////////////////////////////////////////////////
//                Extern global variables
//////////////////////////////////////////////////////////////////////////////

extern char               * lock_type_str[];    // allocated in kernel_init.c
extern chdev_directory_t    chdev_dir;          // allocated in kernel_init.c

//////////////////////////////////////
void busylock_init( busylock_t * lock,
                    uint32_t     type )
{
    lock->ticket  = 0;
    lock->current = 0;
    lock->type    = type;

#if DEBUG_BUSYLOCK_TYPE
    xlist_entry_init( XPTR( local_cxy , &lock->xlist ) ); 
#endif

}

//////////////////////////////////////////
void busylock_acquire( busylock_t * lock )
{
    reg_t      save_sr;
    thread_t * this = CURRENT_THREAD;

    // enter critical section 
    hal_disable_irq( &save_sr );
 
    // get one free ticket and update ticket allocator
    uint32_t ticket = hal_atomic_add( &lock->ticket , 1 );

    // poll current until success
    while( lock->current != ticket )  asm volatile ("nop");

    // increment thread locks counter
    this->busylocks++;

    // save SR in lock descriptor
    lock->save_sr = save_sr;

    // memory barrier to update lock and thread state
    hal_fence();

#if DEBUG_BUSYLOCK
if( lock->type != LOCK_CHDEV_TXT0 ) 
{
    // update thread list of busylocks
    xptr_t root_xp = XPTR( local_cxy , &this->busylocks_root );
    xlist_add_last( root_xp , XPTR( local_cxy , &lock->xlist ) );
}
#endif

#if( DEBUG_BUSYLOCK & 1 )
if( (lock->type != LOCK_CHDEV_TXT0) && 
    (this->process->pid == DEBUG_BUSYLOCK_PID) &&
    (this->trdid == DEBUG_BUSYLOCK_TRDID) )
{
    printk("\n[%s] thread[%x,%x] ACQUIRE lock %s\n",
    __FUNCTION__, this->process->pid, this->trdid, lock_type_str[lock->type] );
}
#endif

} // end busylock_acquire()

//////////////////////////////////////////
void busylock_release( busylock_t * lock )
{
    thread_t * this = CURRENT_THREAD;

    // memory barrier to update the protected object
    hal_fence();

    // update lock state
    lock->current++;

    // decrement thread busylocks counter
    this->busylocks--;

    // memory barrier to update busylock and thread state
    hal_fence();

#if DEBUG_BUSYLOCK
if( lock->type != LOCK_CHDEV_TXT0 ) 
{
    // remove lock from thread list of busylocks
    xlist_unlink( XPTR( local_cxy , &lock->xlist ) );
}
#endif

#if( DEBUG_BUSYLOCK & 1 )
if( (lock->type != LOCK_CHDEV_TXT0) && 
    (this->process->pid == DEBUG_BUSYLOCK_PID) &&
    (this->trdid == DEBUG_BUSYLOCK_TRDID) )
{
    printk("\n[%s] thread[%x,%x] RELEASE lock %s\n",
    __FUNCTION__, this->process->pid, this->trdid, lock_type_str[lock->type] );
}
#endif

    // exit critical section
    hal_restore_irq( lock->save_sr );
 
}  // end busylock_release()

        
