Mercurial > flash_v2
view packages/kernel/current/src/sync/mutex.cxx @ 64:c38311975d4f ecos-sw-2000-01-28
Merge from eCos master repository on 2000-01-28-04:28:11-GMT
| author | jlarmour |
|---|---|
| date | Fri, 28 Jan 2000 04:59:39 +0000 |
| parents | 7a6ac9edc838 |
| children | bf00f99aec69 |
line wrap: on
line source
//========================================================================== // // sync/mutex.cxx // // Mutex and condition variable implementation // //========================================================================== //####COPYRIGHTBEGIN#### // // ------------------------------------------- // The contents of this file are subject to the Red Hat eCos Public License // Version 1.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://sourceware.cygnus.com/ecos // // Software distributed under the License is distributed on an // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations under // the License. // // The Original Code is eCos - Embedded Configurable Operating System, // released September 30, 1998. // // The Initial Developer of the Original Code is Red Hat. // Portions created by Red Hat are // Copyright (C) 1998, 1999, 2000 Red Hat, Inc. // All Rights Reserved. // ------------------------------------------- // //####COPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): nickg // Contributors: nickg, jlarmour // Date: 1999-02-17 // Purpose: Mutex implementation // Description: This file contains the implementations of the mutex // and condition variable classes. // //####DESCRIPTIONEND#### // //========================================================================== #include <pkgconf/kernel.h> #include <cyg/kernel/ktypes.h> // base kernel types #include <cyg/infra/cyg_trac.h> // tracing macros #include <cyg/infra/cyg_ass.h> // assertion macros #include <cyg/kernel/instrmnt.h> // instrumentation #include <cyg/kernel/mutex.hxx> // our header #include <cyg/kernel/thread.inl> // thread inlines #include <cyg/kernel/sched.inl> // scheduler inlines #include <cyg/kernel/clock.inl> // clock inlines // ------------------------------------------------------------------------- // Constructor Cyg_Mutex::Cyg_Mutex() { CYG_REPORT_FUNCTION(); locked = false; owner = NULL; CYG_REPORT_RETURN(); } // ------------------------------------------------------------------------- // Destructor Cyg_Mutex::~Cyg_Mutex() { CYG_REPORT_FUNCTION(); CYG_ASSERT( owner == NULL, "Deleting mutex with owner"); CYG_ASSERT( queue.empty(), "Deleting mutex with waiting threads"); CYG_REPORT_RETURN(); } // ------------------------------------------------------------------------- #ifdef CYGDBG_USE_ASSERTS bool Cyg_Mutex::check_this( cyg_assert_class_zeal zeal) const { // CYG_REPORT_FUNCTION(); // check that we have a non-NULL pointer first if( this == NULL ) return false; switch( zeal ) { case cyg_system_test: case cyg_extreme: case cyg_thorough: case cyg_quick: case cyg_trivial: if( locked && owner == NULL ) return false; if( !locked && owner != NULL ) return false; case cyg_none: default: break; }; return true; } #endif // ------------------------------------------------------------------------- // Lock and/or wait cyg_bool Cyg_Mutex::lock(void) { CYG_REPORT_FUNCTYPE("returning %d"); cyg_bool result = true; Cyg_Thread *self = Cyg_Thread::self(); // Prevent preemption Cyg_Scheduler::lock(); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_INSTRUMENT_MUTEX(LOCK, this, 0); // Loop while the mutex is locked, sleeping each time around // the loop. This copes with the possibility of a higher priority // thread grabbing the mutex between the wakeup in unlock() and // this thread actually starting. #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE self->count_mutex(); #endif while( locked && result ) { CYG_ASSERT( self != owner, "Locking mutex I already own"); self->set_sleep_reason( Cyg_Thread::WAIT ); self->sleep(); queue.enqueue( self ); #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE owner->inherit_priority(self); #endif CYG_INSTRUMENT_MUTEX(WAIT, this, 0); CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, "Called with non-zero scheduler lock"); // Unlock scheduler and allow other threads // to run Cyg_Scheduler::unlock(); Cyg_Scheduler::lock(); CYG_ASSERTCLASS( this, "Bad this pointer"); switch( self->get_wake_reason() ) { case Cyg_Thread::DESTRUCT: case Cyg_Thread::BREAK: result = false; break; case Cyg_Thread::EXIT: self->exit(); break; default: break; } } if( result ) { locked = true; owner = self; CYG_INSTRUMENT_MUTEX(LOCKED, this, 0); } #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE else { self->uncount_mutex(); self->disinherit_priority(); } #endif // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_REPORT_RETVAL(result); return result; } // ------------------------------------------------------------------------- // Try to lock and return success cyg_bool Cyg_Mutex::trylock(void) { CYG_REPORT_FUNCTYPE("returning %d"); CYG_ASSERTCLASS( this, "Bad this pointer"); cyg_bool result = true; // Prevent preemption Cyg_Scheduler::lock(); // If the mutex is not locked, grab it // for ourself. Otherwise return failure. if( !locked ) { Cyg_Thread *self = Cyg_Thread::self(); locked = true; owner = self; #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE self->count_mutex(); #endif } else result = false; CYG_INSTRUMENT_MUTEX(TRY, this, result); // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); CYG_REPORT_RETVAL(result); return result; } // ------------------------------------------------------------------------- // unlock void Cyg_Mutex::unlock(void) { CYG_REPORT_FUNCTION(); // Prevent preemption Cyg_Scheduler::lock(); CYG_INSTRUMENT_MUTEX(UNLOCK, this, 0); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERT( locked, "Unlock mutex that is not locked"); CYG_ASSERT( owner == Cyg_Thread::self(), "Unlock mutex I do not own"); if( !queue.empty() ) { // The queue is non-empty, so grab the next // thread from it and wake it up. Cyg_Thread *thread = queue.dequeue(); CYG_ASSERTCLASS( thread, "Bad thread pointer"); #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE // Give the owner-to-be a chance to inherit from the remaining // queue or the relinquishing thread: thread->relay_priority(owner, &queue); #endif thread->set_wake_reason( Cyg_Thread::DONE ); thread->wake(); CYG_INSTRUMENT_MUTEX(WAKE, this, thread); } #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE owner->uncount_mutex(); owner->disinherit_priority(); #endif locked = false; owner = NULL; CYG_ASSERTCLASS( this, "Bad this pointer"); // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); CYG_REPORT_RETURN(); } // ------------------------------------------------------------------------- // Release all waiting threads. void Cyg_Mutex::release() { CYG_REPORT_FUNCTION(); // Prevent preemption Cyg_Scheduler::lock(); CYG_INSTRUMENT_MUTEX(RELEASE, this, 0); CYG_ASSERTCLASS( this, "Bad this pointer"); while( !queue.empty() ) { // The queue is non-empty, so grab each // thread from it and release it. Cyg_Thread *thread = queue.dequeue(); CYG_ASSERTCLASS( thread, "Bad thread pointer"); thread->release(); CYG_INSTRUMENT_MUTEX(RELEASED, this, thread); } CYG_ASSERTCLASS( this, "Bad this pointer"); // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); CYG_REPORT_RETURN(); } //========================================================================== // Condition variables Cyg_Condition_Variable::Cyg_Condition_Variable( Cyg_Mutex &mx // linked mutex ) { CYG_REPORT_FUNCTION(); mutex = &mx; CYG_ASSERTCLASS( mutex, "Invalid mutex argument"); CYG_REPORT_RETURN(); } // ------------------------------------------------------------------------- // Destructor Cyg_Condition_Variable::~Cyg_Condition_Variable() { CYG_REPORT_FUNCTION(); CYG_ASSERT( queue.empty(), "Deleting condvar with waiting threads"); CYG_REPORT_RETURN(); } // ------------------------------------------------------------------------- #ifdef CYGDBG_USE_ASSERTS bool Cyg_Condition_Variable::check_this( cyg_assert_class_zeal zeal) const { bool result = true; CYG_REPORT_FUNCTYPE("returning %d"); CYG_REPORT_FUNCARG1("zeal = %d", zeal); // check that we have a non-NULL pointer first if( this == NULL ) result = false; else { switch( zeal ) { case cyg_system_test: case cyg_extreme: case cyg_thorough: if( !mutex->check_this(zeal) ) result = false; case cyg_quick: case cyg_trivial: case cyg_none: default: break; } } CYG_REPORT_RETVAL(result); return result; } #endif // ------------------------------------------------------------------------- // Wait for condition to be true // Note: if this function is entered with the scheduler locked (e.g. to // suspend DSR processing) then there is no need to take the lock. Also // in this case, exit with the scheduler locked, which allows this function // to be used in a totally thread-safe manner. void Cyg_Condition_Variable::wait(void) { CYG_REPORT_FUNCTION(); Cyg_Thread *self = Cyg_Thread::self(); cyg_int32 current_lock = Cyg_Scheduler::get_sched_lock(); if (current_lock == 0) // Prevent preemption Cyg_Scheduler::lock(); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); CYG_ASSERTCLASS( self, "Bad self thread"); CYG_INSTRUMENT_CONDVAR(WAIT, this, 0); mutex->unlock(); self->set_sleep_reason( Cyg_Thread::WAIT ); self->sleep(); queue.enqueue( self ); CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, "Called with non-zero scheduler lock"); // Unlock the scheduler and switch threads Cyg_Scheduler::unlock(); CYG_INSTRUMENT_CONDVAR(WOKE, this, self->get_wake_reason()); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); switch( self->get_wake_reason() ) { case Cyg_Thread::EXIT: self->exit(); break; default: break; } // When we awake, we must re-acquire the mutex. Note that while // it is essential to release the mutex and queue on the CV // atomically relative to other threads, to avoid races, it is not // necessary for us to re-acquire the mutex in the same atomic // action. Hence we can do it after unlocking the scheduler. // We need to loop here in case the thread is released while waiting // for the mutex. It is essential that we exit this function with the // mutex claimed. while ( !mutex->lock() ) continue; CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); CYG_ASSERT( mutex->owner == self, "Not mutex owner"); CYG_REPORT_RETURN(); if (current_lock) // Reacquire the DSR pseudo lock Cyg_Scheduler::lock(); } // ------------------------------------------------------------------------- // Wake one thread void Cyg_Condition_Variable::signal(void) { CYG_REPORT_FUNCTION(); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); // Prevent preemption Cyg_Scheduler::lock(); CYG_INSTRUMENT_CONDVAR(SIGNAL, this, 0); if( !queue.empty() ) { // The queue is non-empty, so grab the next // thread from it and wake it up. Cyg_Thread *thread = queue.dequeue(); CYG_ASSERTCLASS( thread, "Bad thread pointer"); thread->set_wake_reason( Cyg_Thread::DONE ); thread->wake(); CYG_INSTRUMENT_CONDVAR(WAKE, this, thread); } CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); CYG_REPORT_RETURN(); } // ------------------------------------------------------------------------- // Set cond true, wake all threads void Cyg_Condition_Variable::broadcast(void) { CYG_REPORT_FUNCTION(); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); // Prevent preemption Cyg_Scheduler::lock(); CYG_INSTRUMENT_CONDVAR(BROADCAST, this, 0); // Grab all the threads from the queue and let them // go. while( !queue.empty() ) { Cyg_Thread *thread = queue.dequeue(); CYG_ASSERTCLASS( thread, "Bad thread pointer"); thread->set_wake_reason( Cyg_Thread::DONE ); thread->wake(); CYG_INSTRUMENT_CONDVAR(WAKE, this, thread); } CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); CYG_REPORT_RETURN(); } // ------------------------------------------------------------------------- // Optional timed wait on a CV #if defined(CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT) cyg_bool Cyg_Condition_Variable::wait( cyg_tick_count timeout ) { CYG_REPORT_FUNCTYPE("returning %d"); CYG_REPORT_FUNCARG1("timeout = %d", timeout); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); cyg_bool result = true; Cyg_Thread *self = Cyg_Thread::self(); CYG_ASSERTCLASS( self, "Bad self thread"); // Prevent preemption Cyg_Scheduler::lock(); CYG_INSTRUMENT_CONDVAR(TIMED_WAIT, this, 0 ); mutex->unlock(); // The ordering of sleep() and set_timer() here are // important. If the timeout is in the past, the thread // will be woken up immediately and will not sleep. self->sleep(); // Set the timer and sleep reason self->set_timer( timeout, Cyg_Thread::TIMEOUT ); // Only enqueue if the timeout has not already fired. if( self->get_wake_reason() == Cyg_Thread::NONE ) queue.enqueue( self ); CYG_ASSERT( Cyg_Scheduler::get_sched_lock() == 1, "Called with non-zero scheduler lock"); // Unlock the scheduler and switch threads Cyg_Scheduler::unlock(); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); self->clear_timer(); CYG_INSTRUMENT_CONDVAR(WOKE, this, self->get_wake_reason()); switch( self->get_wake_reason() ) { case Cyg_Thread::TIMEOUT: case Cyg_Thread::DESTRUCT: // which, the cv or the mutex? case Cyg_Thread::BREAK: result = false; break; case Cyg_Thread::EXIT: self->exit(); break; default: break; } // When we awake, we must re-acquire the mutex. Note that while // it is essential to release the mutex and queue on the CV // atomically relative to other threads, to avoid races, it is not // necessary for us to re-acquire the mutex in the same atomic // action. Hence we can do it after unlocking the scheduler. // FIXME: what if we woke up above due to TIMEOUT/DESTRUCT/BREAK? // In that situation is it correct to not lock the mutex? if (false != result) result = mutex->lock(); CYG_ASSERTCLASS( this, "Bad this pointer"); CYG_ASSERTCLASS( mutex, "Corrupt mutex"); CYG_REPORT_RETVAL(result); return result; } #endif // ------------------------------------------------------------------------- // EOF sync/mutex.cxx
