/*
 * busylock.c - local kernel-busy waiting lock implementation.
 *
 * Authors     Alain Greiner (2016,2017,2018)
 *
 * 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
    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) && 
    ((uint32_t)hal_get_cycles() > DEBUG_BUSYLOCK) )
{
    xptr_t root_xp = XPTR( local_cxy , &this->busylocks_root );

    // update thread list of busylocks
    xlist_add_last( root_xp , XPTR( local_cxy , &lock->xlist ) );
}
#endif

#if( DEBUG_BUSYLOCK && DEBUG_BUSYLOCK_THREAD_XP )
if( (lock->type != LOCK_CHDEV_TXT0) && 
    (XPTR( local_cxy , this ) == DEBUG_BUSYLOCK_THREAD_XP) )
{
    // get cluster and local pointer of target thread
    cxy_t      thread_cxy = GET_CXY( DEBUG_BUSYLOCK_THREAD_XP );
    thread_t * thread_ptr = GET_PTR( DEBUG_BUSYLOCK_THREAD_XP );

    // display message on kernel TXT0
    printk("\n### thread [%x,%x] ACQUIRE lock %s\n",
    thread_cxy, thread_ptr, 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) && 
    ((uint32_t)hal_get_cycles() > DEBUG_BUSYLOCK) )
{
    // remove lock from thread list of busylocks
    xlist_unlink( XPTR( local_cxy , &lock->xlist ) );
}
#endif

#if( DEBUG_BUSYLOCK && DEBUG_BUSYLOCK_THREAD_XP )
if( (lock->type != LOCK_CHDEV_TXT0) && 
    (XPTR( local_cxy , this ) == DEBUG_BUSYLOCK_THREAD_XP) )
{
    // get cluster and local pointer of target thread
    cxy_t      thread_cxy = GET_CXY( DEBUG_BUSYLOCK_THREAD_XP );
    thread_t * thread_ptr = GET_PTR( DEBUG_BUSYLOCK_THREAD_XP );

    // display message on kernel TXT0
    printk("\n### thread [%x,%x] RELEASE lock %s\n",
    thread_cxy, thread_ptr, lock_type_str[lock->type] );
}
#endif

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

        
