/*
 * remote_busylock.h: remote kernel busy-waiting lock definition.     
 * 
 * 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-kernel; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef _REMOTE_BUSYLOCK_H_
#define _REMOTE_BUSYLOCK_H_

#include <kernel_config.h>
#include <hal_kernel_types.h>
#include <hal_shared_types.h>
#include <xlist.h>

/*******************************************************************************************
 * This synchronisation object sequencializes all concurrent read or write accesses to a
 * shared object located in any cluster, made by any thread(s) running in any cluster.
 * It uses a busy waiting policy when the lock is taken by another thread, and should
 * be used ro execute very short actions, such as basic allocators, or to protect  
 * higher level synchronisation objects, such as remote_queuelock and remote_rwlock.
 *
 * - To acquire the lock, we use a ticket policy to avoid starvation: the calling thread
 *   makes an atomic increment on a "ticket" allocator, and keep polling the "current" 
 *   value  until current == ticket. 
 *
 * - To release the lock, the owner thread increments the "current" value.
 * 
 * - When a thread takes a busylock, it enters a critical section: the acquire() 
 *   function disables the IRQs, takes the lock, increments the thread busylocks counter,
 *   save the SR in the lock descriptor and returns.
 *
 * - The release() function releases the lock, decrements the thread busylock
 *   counter, restores the SR to exit the critical section, and returns.
 *
 * WARNING: a thread cannot yield when it is holding a busylock (local or remote).
 *
 * This rule is checked by all functions containing a thread_yield() AND by the scheduler,
 * thanks to the busylocks counter stored in the calling thread descriptor.
 * 1) all functions call "thread_assert_can_yield()" before calling "thread_yield()".
 * 2) The scheduler checks that the calling thread does not hold any busylock.
 * In case of violation the core goes to sleep after a [PANIC] message on TXT0.
 ******************************************************************************************/

/*******************************************************************************************
 * This structure defines a remote_busylock.
 * - The <ticket> and <current> fields implement the ticket policy described above.
 * - The <type> and <xlist> fields are used for debug. The type defines the lock usage,
 *   as detailed in the kernel_config.h file. 
 * - The <save_sr> field is used to implement the critical section as decribed above.
 ******************************************************************************************/

typedef struct remote_busylock_s
{
	uint32_t            ticket;      /*! next free ticket index                           */
    volatile uint32_t   current;     /*! current owner index                              */
    uint32_t            type;        /*! lock type for debug                              */
    reg_t               save_sr;     /*! SR value                                         */

#if DEBUG_BUSYLOCK_TYPE
    xlist_entry_t       xlist;       /*! member of list of locks taken by same thread     */
#endif

}
remote_busylock_t;

/*******************************************************************************************
 * This function initializes a remote busylock.
 *******************************************************************************************
 * @ lock_xp    : extended pointer on remote busylock.
 * @ type       : lock type for debug.
 ******************************************************************************************/
void remote_busylock_init( xptr_t   lock_xp,
                           uint32_t type );

/*******************************************************************************************
 * This blocking function uses a busy waiting strategy to acquire a remote_busylock.
 * It makes an atomic increment on the "ticket" field to get a ticket value and increment
 * the ticket allocator. 
 * Then it polls the "current" field until (current == ticket), and returns.
 *******************************************************************************************
 * @ lock_xp    : extended pointer on remote busylock
 ******************************************************************************************/
void remote_busylock_acquire( xptr_t   lock_xp );

/*******************************************************************************************
 * This function releases a busylock by incrementing the "current field".
 *******************************************************************************************
 * @ lock_xp       : extended pointer on remote busylock
 ******************************************************************************************/
void remote_busylock_release( xptr_t  lock_xp );

#endif	/* _REMOTE_BUSYLOCK_H_ */
