/* * rwlock.c - kernel local read/write lock implementation. * * Author 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 #include #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////////// // Extern global variables ////////////////////////////////////////////////////////////////////////////// extern char * lock_type_str[]; // allocated in kernel_init.c ////////////////////////////////// void rwlock_init( rwlock_t * lock, uint32_t type ) { lock->taken = 0; lock->count = 0; list_root_init( &lock->rd_root ); list_root_init( &lock->wr_root ); busylock_init( &lock->lock , type ); } ///////////////////////////////////////// void rwlock_rd_acquire( rwlock_t * lock ) { thread_t * this = CURRENT_THREAD; // check calling thread can yield thread_assert_can_yield( this , __FUNCTION__ ); // get busylock busylock_acquire( &lock->lock ); // block and deschedule if lock already taken while( lock->taken ) { #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { printk("\n[DBG] %s : thread %x in process %x READ BLOCK on rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // get pointer on calling thread thread_t * this = CURRENT_THREAD; // register reader thread in waiting queue list_add_last( &lock->rd_root , &this->wait_list ); // block reader thread thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK ); // release busylock busylock_release( &lock->lock ); // deschedule sched_yield("reader wait rwlock"); // get busylock busylock_acquire( &lock->lock ); } #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { printk("\n[DBG] %s : thread %x in process READ ACQUIRE on rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // increment number of readers lock->count++; // release busylock busylock_release( &lock->lock ); } // end rwlock_rd_acquire() ///////////////////////////////////////// void rwlock_wr_acquire( rwlock_t * lock ) { thread_t * this = CURRENT_THREAD; // check calling thread can yield thread_assert_can_yield( this , __FUNCTION__ ); // get busylock busylock_acquire( &lock->lock ); // block and deschedule if lock already taken or existing read access while( lock->taken || lock->count ) { #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { printk("\n[DBG] %s : thread %x in process WRITE BLOCK on rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // get pointer on calling thread thread_t * this = CURRENT_THREAD; // register writer in waiting queue list_add_last( &lock->wr_root , &this->wait_list ); // block reader thread thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK ); // release busylock busylock_release( &lock->lock ); // deschedule sched_yield("writer wait rwlock"); // get busylock busylock_acquire( &lock->lock ); } #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { printk("\n[DBG] %s : thread %x in process WRITE ACQUIRE on rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // take the rwlock lock->taken = 1; // release busylock busylock_release( &lock->lock ); } // end rwlock_wr_acquire() ///////////////////////////////////////// void rwlock_rd_release( rwlock_t * lock ) { // synchronize memory before lock release hal_fence(); // get busylock busylock_acquire( &lock->lock ); #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { thread_t * this = CURRENT_THREAD; printk("\n[DBG] %s : thread %x in process READ RELEASE on rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // decrement number of readers lock->count--; // release first writer in waiting queue if no current readers // and writers waiting queue non empty if( (lock->count == 0) && (list_is_empty( &lock->wr_root ) == false) ) { // get first writer thread thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list ); #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { thread_t * this = CURRENT_THREAD; printk("\n[DBG] %s : thread %x in process %x UNBLOCK thread %x in process %d" " / rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, thread->trdid, thread->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // remove this waiting thread from waiting list list_unlink( &thread->wait_list ); // unblock this waiting thread thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK ); } // release all readers in waiting queue if writers waiting queue empty // and readers waiting queue non empty else if( list_is_empty( &lock->wr_root ) && (list_is_empty( &lock->rd_root ) == false) ) { while( list_is_empty( &lock->rd_root ) == false ) { // get first reader thread thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list ); #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { thread_t * this = CURRENT_THREAD; printk("\n[DBG] %s : thread %x in process %x UNBLOCK thread %x in process %d" " / rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, thread->trdid, thread->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // remove this waiting thread from waiting list list_unlink( &thread->wait_list ); // unblock this waiting thread thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK ); } } // release busylock busylock_release( &lock->lock ); } // end rwlock_rd_release() ///////////////////////////////////////// void rwlock_wr_release( rwlock_t * lock ) { // synchronize memory before lock release hal_fence(); // get busylock busylock_acquire( &lock->lock ); #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { thread_t * this = CURRENT_THREAD; printk("\n[DBG] %s : thread %x in process WRITE RELEASE on rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // release the rwlock lock->taken = 0; // release first waiting writer thread if writers waiting queue non empty if( list_is_empty( &lock->wr_root ) == false ) { // get first writer thread thread_t * thread = LIST_FIRST( &lock->wr_root , thread_t , wait_list ); #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { thread_t * this = CURRENT_THREAD; printk("\n[DBG] %s : thread %x in process %x UNBLOCK thread %x in process %d" " / rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, thread->trdid, thread->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // remove this waiting thread from waiting list list_unlink( &thread->wait_list ); // unblock this waiting thread thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK ); } // check readers waiting queue and release all if writers waiting queue empty else { while( list_is_empty( &lock->rd_root ) == false ) { // get first reader thread thread_t * thread = LIST_FIRST( &lock->rd_root , thread_t , wait_list ); #if DEBUG_RWLOCK if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) { thread_t * this = CURRENT_THREAD; printk("\n[DBG] %s : thread %x in process %x UNBLOCK thread %x in process %d" " / rwlock %s [%x,%x]\n", __FUNCTION__, this->trdid, this->process->pid, thread->trdid, thread->process->pid, lock_type_str[lock->lock.type], local_cxy, lock ); } #endif // remove this waiting thread from waiting list list_unlink( &thread->wait_list ); // unblock this waiting thread thread_unblock( XPTR( local_cxy , thread ) , THREAD_BLOCKED_LOCK ); } } // release busylock busylock_release( &lock->lock ); } // end rwlock_wr_release()