/* * remote_condvar.c - remote kernel condition variable 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 #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; 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 = (process_t *)GET_PTR( ref_xp ); // allocate memory for new condvar in reference cluster if( ref_cxy == local_cxy ) // local cluster is the reference { kmem_req_t req; req.type = KMEM_CONDVAR; req.flags = AF_ZERO; condvar_ptr = kmem_alloc( &req ); condvar_xp = XPTR( local_cxy , condvar_ptr ); } else // reference cluster is remote { rpc_kcm_alloc_client( ref_cxy , KMEM_CONDVAR , &condvar_xp ); condvar_ptr = GET_PTR( condvar_xp ); } if( condvar_xp == XPTR_NULL ) return 0xFFFFFFFF; // 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 if( condvar_cxy == local_cxy ) // reference is local { kmem_req_t req; req.type = KMEM_SEM; req.ptr = condvar_ptr; kmem_free( &req ); } else // reference is remote { rpc_kcm_free_client( condvar_cxy , condvar_ptr , KMEM_CONDVAR ); } } // 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 assert( (this->busylocks == 0), "cannot yield : busylocks = %d\n", this->busylocks ); // 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()