Index: packages/kernel/current/include/mutex.hxx =================================================================== RCS file: /cvs/ecos/ecos/packages/kernel/current/include/mutex.hxx,v retrieving revision 1.6 diff -u -r1.6 mutex.hxx --- mutex.hxx 2001/08/10 19:27:56 1.6 +++ mutex.hxx 2001/10/22 21:09:55 @@ -51,6 +51,8 @@ #include // assertion macros #include +#include // scheduler +#include // scheduler inlines // ------------------------------------------------------------------------- // Mutex. @@ -59,13 +61,8 @@ { friend class Cyg_Condition_Variable; - cyg_atomic locked; // true if locked. This may seem - // redundant due to "owner" below, - // but is intentionally present for - // future SMP support. + volatile cyg_atomic locked; // owner and want flag bit - Cyg_Thread *owner; // Current locking thread - Cyg_ThreadQueue queue; // Queue of waiting threads #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DYNAMIC @@ -90,6 +87,13 @@ #endif +private: + enum { MUTEX_WANT = 1 }; + + cyg_bool lock_inner(Cyg_Thread *); + + void unlock_inner(Cyg_Thread *); + public: CYGDBG_DEFINE_CHECK_THIS @@ -112,8 +116,7 @@ void release(); // release all waiting threads - // Get the current owning thread - inline Cyg_Thread *get_owner() { return owner; } + Cyg_Thread *get_owner(); // Get the current owning thread #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING @@ -131,6 +134,82 @@ #endif }; + +inline Cyg_Thread *Cyg_Mutex::get_owner() +{ + return (Cyg_Thread *) (locked & ~MUTEX_WANT); +} + +inline cyg_bool Cyg_Mutex::lock() +{ + cyg_bool result; + + Cyg_Thread *self = Cyg_Thread::self(); + + CYG_INSTRUMENT_MUTEX(LOCK, this, 0); + +#ifdef CYGFUN_HAL_ATOMIC_UPDATE_SUPPORT + + result = HAL_COMPARE_AND_SWAP_ACQUIRE(&locked, 0, (cyg_atomic) self); + + if (!result) result = lock_inner(self); + +#else + + result = lock_inner(self); + +#endif + + if (result) CYG_INSTRUMENT_MUTEX(LOCKED, this, 0); + + return result; +} + +inline cyg_bool Cyg_Mutex::trylock() +{ + cyg_bool result; + + Cyg_Thread *self = Cyg_Thread::self(); + +#ifdef CYGFUN_HAL_ATOMIC_UPDATE_SUPPORT + + result = HAL_COMPARE_AND_SWAP_ACQUIRE(&locked, 0, (cyg_atomic) self); + +#else + + Cyg_Scheduler::lock(); + + if (!locked) { + locked = (cyg_atomic) self; + result = true; + } else + result = false; + + Cyg_Scheduler::unlock(); + +#endif + + CYG_INSTRUMENT_MUTEX(TRY, this, result); + + return result; +} + +inline void Cyg_Mutex::unlock() +{ + Cyg_Thread *self = Cyg_Thread::self(); + + CYG_INSTRUMENT_MUTEX(UNLOCK, this, 0); + +#ifdef CYGFUN_HAL_ATOMIC_UPDATE_SUPPORT + + if (HAL_COMPARE_AND_SWAP_RELEASE(&locked, (cyg_atomic) self, 0)) + return; + +#endif + + unlock_inner(self); + +} // ------------------------------------------------------------------------- // Condition variable. Index: packages/kernel/current/src/sync/mutex.cxx =================================================================== RCS file: /cvs/ecos/ecos/packages/kernel/current/src/sync/mutex.cxx,v retrieving revision 1.12 diff -u -r1.12 mutex.cxx --- mutex.cxx 2001/09/12 00:59:25 1.12 +++ mutex.cxx 2001/10/22 21:09:56 @@ -80,8 +80,7 @@ { CYG_REPORT_FUNCTION(); - locked = false; - owner = NULL; + locked = 0; #if defined(CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT) && \ defined(CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DYNAMIC) @@ -128,8 +127,7 @@ { CYG_REPORT_FUNCTION(); - locked = false; - owner = NULL; + locked = 0; protocol = protocol_arg; @@ -160,7 +158,7 @@ { CYG_REPORT_FUNCTION(); - CYG_ASSERT( owner == NULL, "Deleting mutex with owner"); + CYG_ASSERT( locked == 0, "Deleting mutex with owner"); CYG_ASSERT( queue.empty(), "Deleting mutex with waiting threads"); CYG_REPORT_RETURN(); } @@ -184,8 +182,12 @@ case cyg_thorough: case cyg_quick: case cyg_trivial: + if( locked == MUTEX_WANT ) return false; +#if 0 + if( Cyg_Thread::self() == NULL ) return true; if( locked && owner == NULL ) return false; if( !locked && owner != NULL ) return false; +#endif case cyg_none: default: break; @@ -200,42 +202,71 @@ // Lock and/or wait cyg_bool -Cyg_Mutex::lock(void) +Cyg_Mutex::lock_inner(Cyg_Thread *self) { CYG_REPORT_FUNCTYPE("returning %d"); cyg_bool result = true; - Cyg_Thread *self = Cyg_Thread::self(); + cyg_atomic v; // 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_INVERSION_PROTOCOL + while (result) { + +#ifdef CYGFUN_HAL_ATOMIC_UPDATE_SUPPORT + + do { + while ((v = locked) == 0) { + // The owner has since released the mutex, + // try grabbing it again. + + Cyg_Scheduler::unlock(); - self->count_mutex(); + if (HAL_COMPARE_AND_SWAP_ACQUIRE(&locked, v, (cyg_atomic) self)) + return true; + Cyg_Scheduler::lock(); + } + } while (!HAL_COMPARE_AND_SWAP_ACQUIRE(&locked, v, v | MUTEX_WANT)); + +#else + + if ((v = locked) == 0) { + locked = (cyg_atomic) self; + result = true; + break; + } + + locked = v | MUTEX_WANT; + #endif + Cyg_Thread *owner = (Cyg_Thread *) (v & ~MUTEX_WANT); + + CYG_ASSERT( self != owner, "Locking mutex I already own"); + + if (!(v & MUTEX_WANT)) { +#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL + + owner->count_mutex(); + +#endif #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING - - IF_PROTOCOL_CEILING - self->set_priority_ceiling(ceiling); + IF_PROTOCOL_CEILING + owner->set_priority_ceiling(ceiling); + #endif + } - while( locked && result ) - { - CYG_ASSERT( self != owner, "Locking mutex I already own"); - self->set_sleep_reason( Cyg_Thread::WAIT ); self->sleep(); @@ -273,34 +304,6 @@ } - if( result ) - { - locked = true; - owner = self; - - CYG_INSTRUMENT_MUTEX(LOCKED, this, 0); - } - else - { -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL - - self->uncount_mutex(); - -#endif -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT - - IF_PROTOCOL_INHERIT - self->disinherit_priority(); - -#endif -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING - - IF_PROTOCOL_CEILING - self->clear_priority_ceiling(); - -#endif - } - // Unlock the scheduler and maybe switch threads Cyg_Scheduler::unlock(); @@ -312,117 +315,75 @@ } // ------------------------------------------------------------------------- -// 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_INVERSION_PROTOCOL - - self->count_mutex(); - -#endif -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING - - IF_PROTOCOL_CEILING - self->set_priority_ceiling(ceiling); - -#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_Mutex::unlock_inner(Cyg_Thread *self) { CYG_REPORT_FUNCTION(); // Prevent preemption Cyg_Scheduler::lock(); - CYG_INSTRUMENT_MUTEX(UNLOCK, this, 0); + Cyg_Thread *owner = get_owner(); 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"); + CYG_ASSERT( owner == 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. +// A race window exists here: one of the blocking thread may inherit +// a higher priority than the owner-to-be, before the owner-to-be gets +// a chance to run (and acquire the mutex). - Cyg_Thread *thread = queue.dequeue(); + if (locked & MUTEX_WANT) { - CYG_ASSERTCLASS( thread, "Bad thread pointer"); + while ( !queue.empty() ) { -#ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT + // The queue is non-empty, so grab the next + // thread from it and wake it up. - // Give the owner-to-be a chance to inherit from the remaining - // queue or the relinquishing thread: - - IF_PROTOCOL_INHERIT - thread->relay_priority(owner, &queue); + Cyg_Thread *thread = queue.dequeue(); -#endif + CYG_ASSERTCLASS( thread, "Bad thread pointer"); - thread->set_wake_reason( Cyg_Thread::DONE ); + thread->set_wake_reason( Cyg_Thread::DONE ); - thread->wake(); + thread->wake(); - CYG_INSTRUMENT_MUTEX(WAKE, this, thread); + CYG_INSTRUMENT_MUTEX(WAKE, this, thread); - } + } #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL - owner->uncount_mutex(); + owner->uncount_mutex(); #endif + #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT - IF_PROTOCOL_INHERIT - owner->disinherit_priority(); + IF_PROTOCOL_INHERIT + owner->disinherit_priority(); #endif #ifdef CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING - IF_PROTOCOL_CEILING - owner->clear_priority_ceiling(); + IF_PROTOCOL_CEILING + owner->clear_priority_ceiling(); #endif - locked = false; - owner = NULL; + } + +#ifdef CYGFUN_HAL_ATOMIC_UPDATE_SUPPORT + + HAL_STORE_RELEASE(&locked, 0); + +#else + + locked = 0; + +#endif CYG_ASSERTCLASS( this, "Bad this pointer");