// -*- Mode: CFA -*- // // Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo // // The contents of this file are covered under the licence agreement in the // file "LICENCE" distributed with Cforall. // // monitor_desc.c -- // // Author : Thierry Delisle // Created On : Thd Feb 23 12:27:26 2017 // Last Modified By : Thierry Delisle // Last Modified On : -- // Update Count : 0 // #include "monitor" #include "kernel_private.h" #include "libhdr.h" void set_owner( monitor_desc * this, thread_desc * owner ) { //Pass the monitor appropriately this->owner = owner; //We are passing the monitor to someone else, which means recursion level is not 0 this->recursion = owner ? 1 : 0; } extern "C" { void __enter_monitor_desc(monitor_desc * this, monitor_desc * leader) { lock( &this->lock ); thread_desc * thrd = this_thread(); //Update the stack owner this->stack_owner = leader; if( !this->owner ) { //No one has the monitor, just take it this->owner = thrd; this->recursion = 1; } else if( this->owner == thrd) { //We already have the monitor, just not how many times we took it assert( this->recursion > 0 ); this->recursion += 1; } else { //Some one else has the monitor, wait in line for it append( &this->entry_queue, thrd ); ScheduleInternal( &this->lock ); //ScheduleInternal will unlock spinlock, no need to unlock ourselves return; } unlock( &this->lock ); return; } // leave pseudo code : // decrement level // leve == 0 ? // no : done // yes : // signal stack empty ? // has leader : // bulk acquiring means we don't own the signal stack // ignore it but don't release the monitor // yes : // next in entry queue is new owner // no : // top of the signal stack is the owner // context switch to him right away // void __leave_monitor_desc(monitor_desc * this, monitor_desc * leader) { lock( &this->lock ); thread_desc * thrd = this_thread(); assert( thrd == this->owner || this->stack_owner ); //Leaving a recursion level, decrement the counter this->recursion -= 1; //If we haven't left the last level of recursion //it means we don't need to do anything if( this->recursion != 0) { this->stack_owner = leader; unlock( &this->lock ); return; } //If we don't own the signal stack then just leave it to the owner if( this->stack_owner ) { this->stack_owner = leader; unlock( &this->lock ); return; } //We are the stack owner and have left the last recursion level. //We are in charge of passing the monitor thread_desc * new_owner = 0; //Check the signaller stack new_owner = pop( &this->signal_stack ); if( new_owner ) { //The signaller stack is not empty, //transfer control immediately set_owner( this, new_owner ); this->stack_owner = leader; ScheduleInternal( &this->lock, new_owner ); return; } // No signaller thread // Get the next thread in the entry_queue new_owner = pop_head( &this->entry_queue ); set_owner( this, new_owner ); //Update the stack owner this->stack_owner = leader; //We can now let other threads in safely unlock( &this->lock ); //We need to wake-up the thread ScheduleThread( new_owner ); } } static inline void enter(monitor_desc ** monitors, int count) { __enter_monitor_desc( monitors[0], NULL ); for(int i = 1; i < count; i++) { __enter_monitor_desc( monitors[i], monitors[0] ); } } static inline void leave(monitor_desc ** monitors, int count) { __leave_monitor_desc( monitors[0], NULL ); for(int i = count - 1; i >= 0; i--) { __leave_monitor_desc( monitors[i], monitors[0] ); } } void ?{}( monitor_guard_t * this, monitor_desc ** m, int count ) { this->m = m; this->count = count; qsort(this->m, count); enter( this->m, this->count ); this->prev_mntrs = this_thread()->current_monitors; this->prev_count = this_thread()->current_monitor_count; this_thread()->current_monitors = m; this_thread()->current_monitor_count = count; } void ^?{}( monitor_guard_t * this ) { leave( this->m, this->count ); this_thread()->current_monitors = this->prev_mntrs; this_thread()->current_monitor_count = this->prev_count; } //----------------------------------------------------------------------------- // Internal scheduling void wait( condition * this ) { // LIB_DEBUG_FPRINTF("Waiting\n"); thread_desc * this_thrd = this_thread(); if( !this->monitors ) { this->monitors = this_thrd->current_monitors; this->monitor_count = this_thrd->current_monitor_count; } unsigned short count = this->monitor_count; //Check that everything is as expected assert( this->monitors != NULL ); assert( this->monitor_count != 0 ); unsigned int recursions[ count ]; //Save the current recursion levels to restore them later spinlock * locks [ count ]; //We need to pass-in an array of locks to ScheduleInternal // LIB_DEBUG_FPRINTF("Getting ready to wait\n"); //Loop on all the monitors and release the owner for( unsigned int i = 0; i < count; i++ ) { monitor_desc * cur = this->monitors[i]; assert( cur ); // LIB_DEBUG_FPRINTF("cur %p lock %p\n", cur, &cur->lock); //Store the locks for later locks[i] = &cur->lock; //Protect the monitors lock( locks[i] ); { //Save the recursion levels recursions[i] = cur->recursion; //Release the owner cur->recursion = 0; cur->owner = NULL; } //Release the monitor unlock( locks[i] ); } // LIB_DEBUG_FPRINTF("Waiting now\n"); //Everything is ready to go to sleep ScheduleInternal( locks, count ); //WE WOKE UP //We are back, restore the owners and recursions for( unsigned int i = 0; i < count; i++ ) { monitor_desc * cur = this->monitors[i]; //Protect the monitors lock( locks[i] ); { //Release the owner cur->owner = this_thrd; cur->recursion = recursions[i]; } //Release the monitor unlock( locks[i] ); } } static void __signal_internal( condition * this ) { if( !this->blocked.head ) return; //Check that everything is as expected assert( this->monitors ); assert( this->monitor_count != 0 ); LIB_DEBUG_DO( if ( this->monitors != this_thread()->current_monitors ) { abortf( "Signal on condition %p made outside of the correct monitor(s)", this ); } // if ); monitor_desc * owner = this->monitors[0]; lock( &owner->lock ); { thread_desc * unblock = pop_head( &this->blocked ); push( &owner->signal_stack, unblock ); } unlock( &owner->lock ); } void signal( condition * this ) { __signal_internal( this ); }