Skip to content

Commit

Permalink
pthread_cond_wait: Add mutex to protect the waiter count
Browse files Browse the repository at this point in the history
The load/compare and RMW to wait_count need protection. Bumping the
counter can be done by atomic_fetch_add as there is no race here.
However reading the counter when signaling needs to take a lock so that
if multiple threads signal at the same time, only one has exclusive
access to the counter.

NOTE:
The assumption that the user will call pthread_cond_signal /
pthread_cond_broadcast with the mutex given to pthread_cond_wait held is
simply not true. It MAY hold it, but it is not forced. Thus, using the
user space lock for protecting the wait counter as well is not valid!

The pthread_cond_signal() or pthread_cond_broadcast() functions may be called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or pthread_cond_timedwait() have associated with the condition variable during their waits; however, if predictable scheduling behaviour is required, then that mutex is locked by the thread calling pthread_cond_signal() or pthread_cond_broadcast().

[1] https://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_cond_signal.html
  • Loading branch information
pussuw committed Jan 15, 2025
1 parent 61f0c97 commit 262e5dd
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 15 deletions.
3 changes: 2 additions & 1 deletion include/pthread.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,8 @@ struct pthread_cond_s
{
sem_t sem;
clockid_t clockid;
uint16_t wait_count;
unsigned int wait_count;
mutex_t mutex;
};

#ifndef __PTHREAD_COND_T_DEFINED
Expand Down
3 changes: 3 additions & 0 deletions libs/libc/pthread/pthread_condinit.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include <debug.h>
#include <errno.h>

#include <nuttx/atomic.h>

/****************************************************************************
* Public Functions
****************************************************************************/
Expand Down Expand Up @@ -75,6 +77,7 @@ int pthread_cond_init(FAR pthread_cond_t *cond,
{
cond->clockid = attr ? attr->clockid : CLOCK_REALTIME;
cond->wait_count = 0;
nxmutex_init(&cond->mutex);
}

sinfo("Returning %d\n", ret);
Expand Down
15 changes: 9 additions & 6 deletions sched/pthread/pthread_condbroadcast.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include <errno.h>
#include <debug.h>

#include <nuttx/atomic.h>

#include "pthread/pthread.h"

/****************************************************************************
Expand All @@ -42,10 +44,7 @@
*
* Description:
* A thread broadcast on a condition variable.
* pthread_cond_broadcast shall unblock all threads currently blocked on a
* specified condition variable cond. We need own the mutex that threads
* calling pthread_cond_wait or pthread_cond_timedwait have associated
* with the condition variable during their wait.
*
* Input Parameters:
* None
*
Expand All @@ -68,9 +67,11 @@ int pthread_cond_broadcast(FAR pthread_cond_t *cond)
}
else
{
nxmutex_lock(&cond->mutex);

/* Loop until all of the waiting threads have been restarted. */

while (cond->wait_count > 0)
while (atomic_load(&cond->wait_count) > 0)
{
/* If the value is less than zero (meaning that one or more
* thread is waiting), then post the condition semaphore.
Expand All @@ -83,8 +84,10 @@ int pthread_cond_broadcast(FAR pthread_cond_t *cond)
* above post).
*/

cond->wait_count--;
atomic_fetch_sub(&cond->wait_count, 1);
}

nxmutex_unlock(&cond->mutex);
}

sinfo("Returning %d\n", ret);
Expand Down
3 changes: 2 additions & 1 deletion sched/pthread/pthread_condclockwait.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <assert.h>
#include <debug.h>

#include <nuttx/atomic.h>
#include <nuttx/irq.h>
#include <nuttx/wdog.h>
#include <nuttx/signal.h>
Expand Down Expand Up @@ -113,7 +114,7 @@ int pthread_cond_clockwait(FAR pthread_cond_t *cond,

sinfo("Give up mutex...\n");

cond->wait_count++;
atomic_fetch_add(&cond->wait_count, 1);

/* Give up the mutex */

Expand Down
14 changes: 8 additions & 6 deletions sched/pthread/pthread_condsignal.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <errno.h>
#include <debug.h>

#include <nuttx/atomic.h>

#include "pthread/pthread.h"

/****************************************************************************
Expand All @@ -41,10 +43,6 @@
*
* Description:
* A thread can signal on a condition variable.
* pthread_cond_signal shall unblock a thread currently blocked on a
* specified condition variable cond. We need own the mutex that threads
* calling pthread_cond_wait or pthread_cond_timedwait have associated
* with the condition variable during their wait.
*
* Input Parameters:
* None
Expand All @@ -68,12 +66,16 @@ int pthread_cond_signal(FAR pthread_cond_t *cond)
}
else
{
if (cond->wait_count > 0)
nxmutex_lock(&cond->mutex);

if (atomic_load(&cond->wait_count) > 0)
{
sinfo("Signalling...\n");
cond->wait_count--;
atomic_fetch_sub(&cond->wait_count, 1);
ret = -nxsem_post(&cond->sem);
}

nxmutex_unlock(&cond->mutex);
}

sinfo("Returning %d\n", ret);
Expand Down
3 changes: 2 additions & 1 deletion sched/pthread/pthread_condwait.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <errno.h>
#include <debug.h>

#include <nuttx/atomic.h>
#include <nuttx/cancelpt.h>

#include "pthread/pthread.h"
Expand Down Expand Up @@ -88,7 +89,7 @@ int pthread_cond_wait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex)

sinfo("Give up mutex / take cond\n");

cond->wait_count++;
atomic_fetch_add(&cond->wait_count, 1);
ret = pthread_mutex_breaklock(mutex, &nlocks);

status = -nxsem_wait_uninterruptible(&cond->sem);
Expand Down

0 comments on commit 262e5dd

Please sign in to comment.