summaryrefslogtreecommitdiffstats
path: root/cpukit/posix/src/condwaitsupp.c
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2016-04-22 14:37:13 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2016-04-27 08:50:41 +0200
commit7f4ee2b4ae39928ab5f449048e562ef6b2c5d17d (patch)
treeec62caff9b95826169b43cc8a8e66b24fa861015 /cpukit/posix/src/condwaitsupp.c
parenttelnetd: Fix warnings (diff)
downloadrtems-7f4ee2b4ae39928ab5f449048e562ef6b2c5d17d.tar.bz2
posix: Avoid Giant lock for condition variables
Update #2555.
Diffstat (limited to 'cpukit/posix/src/condwaitsupp.c')
-rw-r--r--cpukit/posix/src/condwaitsupp.c188
1 files changed, 103 insertions, 85 deletions
diff --git a/cpukit/posix/src/condwaitsupp.c b/cpukit/posix/src/condwaitsupp.c
index c907f16cdd..b578a5f76f 100644
--- a/cpukit/posix/src/condwaitsupp.c
+++ b/cpukit/posix/src/condwaitsupp.c
@@ -18,15 +18,11 @@
#include "config.h"
#endif
-#include <pthread.h>
-#include <errno.h>
-
-#include <rtems/system.h>
-#include <rtems/score/watchdog.h>
-#include <rtems/score/statesimpl.h>
-#include <rtems/score/threadimpl.h>
#include <rtems/posix/condimpl.h>
#include <rtems/posix/muteximpl.h>
+#include <rtems/score/assert.h>
+#include <rtems/score/statesimpl.h>
+#include <rtems/score/threadimpl.h>
THREAD_WAIT_QUEUE_OBJECT_ASSERT(
POSIX_Condition_variables_Control,
@@ -41,88 +37,110 @@ int _POSIX_Condition_variables_Wait_support(
)
{
POSIX_Condition_variables_Control *the_cond;
- Objects_Locations location;
+ POSIX_Mutex_Control *the_mutex;
+ ISR_lock_Context lock_context;
int status;
int mutex_status;
+ CORE_mutex_Status core_mutex_status;
+ Per_CPU_Control *cpu_self;
Thread_Control *executing;
- the_cond = _POSIX_Condition_variables_Get( cond, &location );
- switch ( location ) {
-
- case OBJECTS_LOCAL:
-
- if ( the_cond->Mutex && ( the_cond->Mutex != *mutex ) ) {
- _Objects_Put( &the_cond->Object );
- return EINVAL;
- }
-
-
- mutex_status = pthread_mutex_unlock( mutex );
- /*
- * Historically, we ignored the return code since the behavior
- * is undefined by POSIX. But GNU/Linux returns EPERM in this
- * case, so we follow their lead.
- */
- if ( mutex_status ) {
- _Objects_Put( &the_cond->Object );
- return EPERM;
- }
-
- if ( !already_timedout ) {
- the_cond->Mutex = *mutex;
-
- executing = _Thread_Executing;
- executing->Wait.return_code = 0;
-
- _Thread_queue_Enqueue(
- &the_cond->Wait_queue,
- POSIX_CONDITION_VARIABLES_TQ_OPERATIONS,
- executing,
- STATES_WAITING_FOR_CONDITION_VARIABLE
- | STATES_INTERRUPTIBLE_BY_SIGNAL,
- timeout,
- ETIMEDOUT
- );
-
- _Objects_Put( &the_cond->Object );
-
- /*
- * Switch ourself out because we blocked as a result of the
- * _Thread_queue_Enqueue.
- */
-
- /*
- * If the thread is interrupted, while in the thread queue, by
- * a POSIX signal, then pthread_cond_wait returns spuriously,
- * according to the POSIX standard. It means that pthread_cond_wait
- * returns a success status, except for the fact that it was not
- * woken up a pthread_cond_signal or a pthread_cond_broadcast.
- */
- status = executing->Wait.return_code;
- if ( status == EINTR )
- status = 0;
-
- } else {
- _Objects_Put( &the_cond->Object );
- status = ETIMEDOUT;
- }
-
- /*
- * When we get here the dispatch disable level is 0.
- */
-
- mutex_status = pthread_mutex_lock( mutex );
- if ( mutex_status )
- return EINVAL;
-
- return status;
-
-#if defined(RTEMS_MULTIPROCESSING)
- case OBJECTS_REMOTE:
-#endif
- case OBJECTS_ERROR:
- break;
+ if ( mutex == NULL ) {
+ return EINVAL;
+ }
+
+ the_cond = _POSIX_Condition_variables_Get( cond, &lock_context );
+
+ if ( the_cond == NULL ) {
+ return EINVAL;
+ }
+
+ _POSIX_Condition_variables_Acquire_critical( the_cond, &lock_context );
+
+ if (
+ the_cond->mutex != POSIX_CONDITION_VARIABLES_NO_MUTEX
+ && the_cond->mutex != *mutex
+ ) {
+ _POSIX_Condition_variables_Release( the_cond, &lock_context );
+ return EINVAL;
+ }
+
+ the_cond->mutex = *mutex;
+
+ cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
+ executing = _Per_CPU_Get_executing( cpu_self );
+
+ /*
+ * Historically, we ignored the unlock status since the behavior
+ * is undefined by POSIX. But GNU/Linux returns EPERM in this
+ * case, so we follow their lead.
+ */
+
+ the_mutex = _POSIX_Mutex_Get_no_protection( mutex );
+ if (
+ the_mutex == NULL
+ || !_CORE_mutex_Is_owner( &the_mutex->Mutex, executing )
+ ) {
+ _POSIX_Condition_variables_Release( the_cond, &lock_context );
+ _Thread_Dispatch_enable( cpu_self );
+ return EPERM;
+ }
+
+ if ( !already_timedout ) {
+ executing->Wait.return_code = 0;
+ _Thread_queue_Enqueue_critical(
+ &the_cond->Wait_queue.Queue,
+ POSIX_CONDITION_VARIABLES_TQ_OPERATIONS,
+ executing,
+ STATES_WAITING_FOR_CONDITION_VARIABLE,
+ timeout,
+ ETIMEDOUT,
+ &lock_context
+ );
+ } else {
+ _POSIX_Condition_variables_Release( the_cond, &lock_context );
+ executing->Wait.return_code = ETIMEDOUT;
+ }
+
+ _ISR_lock_ISR_disable( &lock_context );
+ core_mutex_status = _CORE_mutex_Surrender(
+ &the_mutex->Mutex,
+ NULL,
+ 0,
+ &lock_context
+ );
+ _Assert( core_mutex_status == CORE_MUTEX_STATUS_SUCCESSFUL );
+ (void) core_mutex_status;
+
+ /*
+ * Switch ourself out because we blocked as a result of the
+ * _Thread_queue_Enqueue_critical().
+ */
+
+ _Thread_Dispatch_enable( cpu_self );
+
+ status = (int) executing->Wait.return_code;
+
+ /*
+ * If the thread is interrupted, while in the thread queue, by
+ * a POSIX signal, then pthread_cond_wait returns spuriously,
+ * according to the POSIX standard. It means that pthread_cond_wait
+ * returns a success status, except for the fact that it was not
+ * woken up a pthread_cond_signal() or a pthread_cond_broadcast().
+ */
+
+ if ( status == EINTR ) {
+ status = 0;
+ }
+
+ /*
+ * When we get here the dispatch disable level is 0.
+ */
+
+ mutex_status = pthread_mutex_lock( mutex );
+ if ( mutex_status != 0 ) {
+ return EINVAL;
}
- return EINVAL;
+ return status;
}