/* * remote_condvar.c - remote kernel condition variable 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 #include #include #include #include #include #include #include /////////////////////////////////////////////////// xptr_t remote_condvar_from_ident( intptr_t ident ) { // get pointer on local process_descriptor process_t * process = CURRENT_THREAD->process; // get extended pointer on reference process xptr_t ref_xp = process->ref_xp; // get cluster and local pointer on reference process cxy_t ref_cxy = GET_CXY( ref_xp ); process_t * ref_ptr = GET_PTR( ref_xp ); // get extended pointer on condvars list xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->condvar_root ); xptr_t lock_xp = XPTR( ref_cxy , &ref_ptr->sync_lock ); // get lock protecting synchro lists remote_queuelock_acquire( lock_xp ); // scan reference process condvar list xptr_t iter_xp; xptr_t condvar_xp; cxy_t condvar_cxy; remote_condvar_t * condvar_ptr; intptr_t current; bool_t found = false; XLIST_FOREACH( root_xp , iter_xp ) { condvar_xp = XLIST_ELEMENT( iter_xp , remote_condvar_t , list ); condvar_cxy = GET_CXY( condvar_xp ); condvar_ptr = GET_PTR( condvar_xp ); current = (intptr_t)hal_remote_lpt( XPTR( condvar_cxy , &condvar_ptr->ident ) ); if( current == ident ) { found = true; break; } } // relese lock protecting synchros lists remote_queuelock_release( lock_xp ); if( found == false ) return XPTR_NULL; else return condvar_xp; } // end remote_condvar_from_ident() ///////////////////////////////////////////////// error_t remote_condvar_create( intptr_t ident ) { remote_condvar_t * condvar_ptr; // get pointer on local process descriptor process_t * process = CURRENT_THREAD->process; // get extended pointer on reference process xptr_t ref_xp = process->ref_xp; // get reference process cluster and local pointer cxy_t ref_cxy = GET_CXY( ref_xp ); process_t * ref_ptr = (process_t *)GET_PTR( ref_xp ); // allocate memory for condvar descriptor condvar_ptr = kmem_alloc( bits_log2(sizeof(remote_condvar_t)) , AF_ZERO ); if( condvar_ptr == NULL ) { printk("\n[ERROR] in %s : cannot create condvar\n", __FUNCTION__ ); return -1; } // initialise condvar hal_remote_spt( XPTR( ref_cxy , &condvar_ptr->ident ) , (void *)ident ); xlist_root_init( XPTR( ref_cxy , &condvar_ptr->root ) ); xlist_entry_init( XPTR( ref_cxy , &condvar_ptr->list ) ); remote_busylock_init( XPTR( ref_cxy , &condvar_ptr->lock ), LOCK_CONDVAR_STATE ); // register condvar in reference process xlist xptr_t root_xp = XPTR( ref_cxy , &ref_ptr->condvar_root ); xptr_t list_xp = XPTR( ref_cxy , &condvar_ptr->list ); remote_queuelock_acquire( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); xlist_add_first( root_xp , list_xp ); remote_queuelock_release( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); return 0; } // end remote_condvar_create() //////////////////////////////////////////////// void remote_condvar_destroy( xptr_t condvar_xp ) { // get pointer on local process descriptor process_t * process = CURRENT_THREAD->process; // get extended pointer on reference process xptr_t ref_xp = process->ref_xp; // get reference process cluster and local pointer cxy_t ref_cxy = GET_CXY( ref_xp ); process_t * ref_ptr = GET_PTR( ref_xp ); // get condvar cluster and local pointer cxy_t condvar_cxy = GET_CXY( condvar_xp ); remote_condvar_t * condvar_ptr = GET_PTR( condvar_xp ); // get remote pointer on waiting queue root xptr_t root_xp = XPTR( condvar_cxy , &condvar_ptr->root ); if( !xlist_is_empty( root_xp ) ) // user error { printk("WARNING in %s for thread %x in process %x : " "destroy condvar, but waiting threads queue not empty\n", __FUNCTION__ , CURRENT_THREAD->trdid , CURRENT_THREAD->process->pid ); } // remove condvar from reference process xlist remote_queuelock_acquire( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); xlist_unlink( XPTR( condvar_cxy , &condvar_ptr->list ) ); remote_queuelock_release( XPTR( ref_cxy , &ref_ptr->sync_lock ) ); // release memory allocated for condvar descriptor kmem_remote_free( ref_cxy , condvar_ptr , bits_log2(sizeof(remote_condvar_t)) ); } // end remote_convar_destroy() //////////////////////////////////////////// void remote_condvar_wait( xptr_t condvar_xp, xptr_t mutex_xp ) { thread_t * this = CURRENT_THREAD; // check calling thread can yield thread_assert_can_yield( this , __FUNCTION__ ); // get condvar cluster and local pointer remote_condvar_t * condvar_ptr = GET_PTR( condvar_xp ); cxy_t condvar_cxy = GET_CXY( condvar_xp ); // register the calling thread in condvar waiting queue xlist_add_last( XPTR( condvar_cxy , &condvar_ptr->root ), XPTR( local_cxy , &this->wait_xlist ) ); // block the calling thread thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_USERSYNC ); // release the mutex remote_mutex_unlock( mutex_xp ); // deschedule sched_yield("blocked on condvar"); // re-acquire the mutex remote_mutex_lock( mutex_xp ); } // end remote_condvar_wait() /////////////////////////////////////////////// void remote_condvar_signal( xptr_t condvar_xp ) { // get condvar cluster and local pointer remote_condvar_t * condvar_ptr = GET_PTR( condvar_xp ); cxy_t condvar_cxy = GET_CXY( condvar_xp ); // does nothing if waiting queue empty if( xlist_is_empty( XPTR( condvar_cxy, &condvar_ptr->root ) ) == false ) { // get first waiting thread xptr_t thread_xp = XLIST_FIRST( XPTR( condvar_cxy , &condvar_ptr->root ), thread_t , wait_xlist ); // remove this waiting thread from queue thread_t * thread_ptr = GET_PTR( thread_xp ); cxy_t thread_cxy = GET_CXY( thread_xp ); xlist_unlink( XPTR( thread_cxy , &thread_ptr->wait_xlist ) ); // unblock this waiting thread thread_unblock( thread_xp , THREAD_BLOCKED_USERSYNC ); } } // end remote_condvar_signal() ////////////////////////////////////////////////// void remote_condvar_broadcast( xptr_t condvar_xp ) { // get condvar cluster and local pointer remote_condvar_t * condvar_ptr = GET_PTR( condvar_xp ); cxy_t condvar_cxy = GET_CXY( condvar_xp ); // does nothing if waiting queue empty while( xlist_is_empty( XPTR( condvar_cxy , &condvar_ptr->root ) ) == false ) { // get first waiting thread xptr_t thread_xp = XLIST_FIRST( XPTR( condvar_cxy , &condvar_ptr->root ), thread_t , wait_xlist ); // remove this waiting thread from queue thread_t * thread_ptr = GET_PTR( thread_xp ); cxy_t thread_cxy = GET_CXY( thread_xp ); xlist_unlink( XPTR( thread_cxy , &thread_ptr->wait_xlist ) ); // unblock this waiting thread thread_unblock( thread_xp , THREAD_BLOCKED_USERSYNC ); } } // end remote_condvar_broadcast()