Mercurial > ecos
diff packages/kernel/current/src/sync/mutex.cxx @ 0:3111d98ba7b3 ecos-v1_1-release
Initial commit of eCos version 1.1
| author | jlarmour |
|---|---|
| date | Tue, 11 May 1999 11:16:07 +0000 |
| parents | |
| children | 443894e2e912 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/kernel/current/src/sync/mutex.cxx @@ -0,0 +1,562 @@ +//========================================================================== +// +// sync/mutex.cxx +// +// Mutex and condition variable implementation +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Cygnus 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 "AS IS" +// 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 Cygnus Operating System, released +// September 30, 1998. +// +// The Initial Developer of the Original Code is Cygnus. Portions created +// by Cygnus are Copyright (C) 1998 Cygnus Solutions. All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 1997-10-21 +// 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; +} + +// ------------------------------------------------------------------------- +// 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"); +} + +// ------------------------------------------------------------------------- + +#ifdef CYGDBG_USE_ASSERTS + +bool Cyg_Mutex::check_this( cyg_assert_class_zeal zeal) +{ +// 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() +{ + CYG_REPORT_FUNCTION(); + + 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. + + while( locked ) + { + 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; + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE + + self->count_mutex(); + +#endif + + CYG_INSTRUMENT_MUTEX(LOCKED, this, 0); + } + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + + CYG_ASSERTCLASS( this, "Bad this pointer"); + + return result; +} + +// ------------------------------------------------------------------------- +// Try to lock and return success + +cyg_bool Cyg_Mutex::trylock() +{ + CYG_REPORT_FUNCTION(); + + 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(); + + return result; +} + +// ------------------------------------------------------------------------- +// unlock + +void Cyg_Mutex::unlock() +{ + 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"); + +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INHERITANCE + + owner->uncount_mutex(); + owner->disinherit_priority(); + +#endif + + locked = false; + owner = NULL; + + 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_MUTEX(WAKE, this, thread); + + } + + CYG_ASSERTCLASS( this, "Bad this pointer"); + + // Unlock the scheduler and maybe switch threads + Cyg_Scheduler::unlock(); + +} + +//========================================================================== +// Condition variables + +Cyg_Condition_Variable::Cyg_Condition_Variable( + Cyg_Mutex &mx // linked mutex + ) +{ + CYG_REPORT_FUNCTION(); + + mutex = &mx; + + CYG_ASSERTCLASS( mutex, "Invalid mutex argument"); +} + +// ------------------------------------------------------------------------- +// Destructor + +Cyg_Condition_Variable::~Cyg_Condition_Variable() +{ + CYG_REPORT_FUNCTION(); + + CYG_ASSERT( queue.empty(), "Deleting condvar with waiting threads"); +} + +// ------------------------------------------------------------------------- + +#ifdef CYGDBG_USE_ASSERTS + +bool Cyg_Condition_Variable::check_this( cyg_assert_class_zeal zeal) +{ + 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: + if( !mutex->check_this(zeal) ) return false; + case cyg_quick: + case cyg_trivial: + case cyg_none: + default: + break; + }; + + return true; +} + +#endif + +// ------------------------------------------------------------------------- +// Wait for condition to be true + +void Cyg_Condition_Variable::wait() +{ + CYG_REPORT_FUNCTION(); + + Cyg_Thread *self = Cyg_Thread::self(); + + // 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. + + mutex->lock(); + + CYG_ASSERTCLASS( this, "Bad this pointer"); + CYG_ASSERTCLASS( mutex, "Corrupt mutex"); + CYG_ASSERT( mutex->owner == self, "Not mutex owner"); +} + +// ------------------------------------------------------------------------- +// Wake one thread + +void Cyg_Condition_Variable::signal() +{ + 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(); + +} + +// ------------------------------------------------------------------------- +// Set cond true, wake all threads + +void Cyg_Condition_Variable::broadcast() +{ + 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(); +} + +// ------------------------------------------------------------------------- +// Optional timed wait on a CV + +#if defined(CYGMFN_KERNEL_SYNCH_CONDVAR_TIMED_WAIT) && defined(CYGFUN_KERNEL_THREADS_TIMER) + +cyg_bool Cyg_Condition_Variable::wait( cyg_tick_count timeout ) +{ + CYG_REPORT_FUNCTION(); + + 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. + + mutex->lock(); + + CYG_ASSERTCLASS( this, "Bad this pointer"); + CYG_ASSERTCLASS( mutex, "Corrupt mutex"); + + return result; +} + +#endif + + +// ------------------------------------------------------------------------- +// EOF sync/mutex.cxx
