Skip to content

Commit 0e4a5f8

Browse files
committed
Integrate nxmutex support fully into nxsem
This puts the mutex support fully inside nxsem, allowing locking the mutex and setting the holder with single atomic operation. This enables fast mutex locking from userspace, avoiding taking critical_sections, which may be heavy in SMP and cleanup of nxmutex library in the future. Signed-off-by: Jukka Laitinen <[email protected]>
1 parent 9643ee7 commit 0e4a5f8

20 files changed

+513
-107
lines changed

include/nuttx/mutex.h

+6-4
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,12 @@
3636
* Pre-processor Definitions
3737
****************************************************************************/
3838

39-
#define NXMUTEX_NO_HOLDER ((pid_t)-1)
40-
#define NXMUTEX_INITIALIZER {NXSEM_INITIALIZER(1, SEM_TYPE_MUTEX | \
41-
SEM_PRIO_INHERIT), NXMUTEX_NO_HOLDER}
42-
#define NXRMUTEX_INITIALIZER {NXMUTEX_INITIALIZER, 0}
39+
#define NXMUTEX_NO_HOLDER NXSEM_NO_MHOLDER
40+
#define NXMUTEX_INITIALIZER { \
41+
NXSEM_INITIALIZER(NXMUTEX_NO_HOLDER, SEM_TYPE_MUTEX | SEM_PRIO_INHERIT), \
42+
NXMUTEX_NO_HOLDER}
43+
44+
#define NXRMUTEX_INITIALIZER {NXMUTEX_INITIALIZER, 0}
4345

4446
/****************************************************************************
4547
* Public Type Definitions

include/nuttx/semaphore.h

+25-6
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,42 @@
4545
/* semcount, flags, waitlist, hhead */
4646

4747
# define NXSEM_INITIALIZER(c, f) \
48-
{(c), (f), SEM_WAITLIST_INITIALIZER, NULL}
48+
{{c}, (f), SEM_WAITLIST_INITIALIZER, NULL}
4949
# else
5050
/* semcount, flags, waitlist, holder[2] */
5151

5252
# define NXSEM_INITIALIZER(c, f) \
53-
{(c), (f), SEM_WAITLIST_INITIALIZER, SEMHOLDER_INITIALIZER}
53+
{{(c)}, (f), SEM_WAITLIST_INITIALIZER, SEMHOLDER_INITIALIZER}
5454
# endif
5555
#else /* CONFIG_PRIORITY_INHERITANCE */
5656
/* semcount, flags, waitlist */
5757

5858
# define NXSEM_INITIALIZER(c, f) \
59-
{(c), (f), SEM_WAITLIST_INITIALIZER}
59+
{{(c)}, (f), SEM_WAITLIST_INITIALIZER}
6060
#endif /* CONFIG_PRIORITY_INHERITANCE */
6161

62-
/* Macro to retrieve sem count */
62+
/* Macros to retrieve sem count and to check if nxsem is mutex */
6363

64-
#define NXSEM_COUNT(s) ((FAR atomic_t *)&(s)->semcount)
64+
#define NXSEM_COUNT(s) ((FAR atomic_t *)&(s)->val.semcount)
65+
#define NXSEM_IS_MUTEX(s) (((s)->flags & SEM_TYPE_MUTEX) != 0)
66+
67+
/* Mutex related helper macros */
68+
69+
#define NXSEM_MBLOCKS_BIT (((uint32_t)1) << 31)
70+
#define NXSEM_NO_MHOLDER ((uint32_t)0x7ffffffe)
71+
#define NXSEM_MRESET ((uint32_t)0x7fffffff)
72+
73+
/* Macro to retrieve mutex's atomic holder's ptr */
74+
75+
#define NXSEM_MHOLDER(s) ((FAR atomic_t *)&(s)->val.mholder)
76+
77+
/* Check if holder value (TID) is not NO_HOLDER or RESET */
78+
79+
#define NXSEM_MACQUIRED(h) (!(((h) & NXSEM_NO_MHOLDER) == NXSEM_NO_MHOLDER))
80+
81+
/* Check if mutex is acquired and blocks some other task */
82+
83+
#define NXSEM_MBLOCKS(h) (((h) & NXSEM_MBLOCKS_BIT) != 0)
6584

6685
/****************************************************************************
6786
* Public Type Definitions
@@ -128,7 +147,7 @@ extern "C"
128147
*
129148
****************************************************************************/
130149

131-
int nxsem_init(FAR sem_t *sem, int pshared, unsigned int value);
150+
int nxsem_init(FAR sem_t *sem, int pshared, int32_t value);
132151

133152
/****************************************************************************
134153
* Name: nxsem_destroy

include/pthread.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,9 @@ typedef struct pthread_mutex_s pthread_mutex_t;
348348
#define PTHREAD_MUTEX_DEFAULT_PRIO_FLAGS (PTHREAD_MUTEX_DEFAULT_PRIO_INHERIT | \
349349
PTHREAD_MUTEX_DEFAULT_PRIO_PROTECT)
350350

351-
#define PTHREAD_NXMUTEX_INITIALIZER { \
352-
NXSEM_INITIALIZER(1, SEM_TYPE_MUTEX | PTHREAD_MUTEX_DEFAULT_PRIO_FLAGS), \
351+
#define PTHREAD_NXMUTEX_INITIALIZER { \
352+
NXSEM_INITIALIZER(NXMUTEX_NO_HOLDER, \
353+
SEM_TYPE_MUTEX | PTHREAD_MUTEX_DEFAULT_PRIO_FLAGS), \
353354
NXMUTEX_NO_HOLDER}
354355
#define PTHREAD_NXRMUTEX_INITIALIZER {PTHREAD_NXMUTEX_INITIALIZER, 0}
355356

include/semaphore.h

+13-5
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,16 @@ struct semholder_s
104104

105105
struct sem_s
106106
{
107-
volatile int32_t semcount; /* >0 -> Num counts available */
108-
/* <0 -> Num tasks waiting for semaphore */
107+
union
108+
{
109+
volatile int32_t semcount; /* >0 -> Num counts available */
110+
/* <0 -> Num tasks waiting for semaphore */
111+
volatile uint32_t mholder; /* == NXMUTEX_NO_HOLDER -> mutex has no holder */
112+
/* == NXMUTEX_RESET -> mutex has been reset */
113+
/* Otherwise: */
114+
/* bits[30:0]: TID of the current holder */
115+
/* bit [31]: Mutex is blocking some task */
116+
} val;
109117

110118
/* If priority inheritance is enabled, then we have to keep track of which
111119
* tasks hold references to the semaphore.
@@ -137,18 +145,18 @@ typedef struct sem_s sem_t;
137145
/* semcount, flags, waitlist, hhead */
138146

139147
# define SEM_INITIALIZER(c) \
140-
{(c), 0, SEM_WAITLIST_INITIALIZER, NULL}
148+
{{(c)}, 0, SEM_WAITLIST_INITIALIZER, NULL}
141149
# else
142150
/* semcount, flags, waitlist, holder[2] */
143151

144152
# define SEM_INITIALIZER(c) \
145-
{(c), 0, SEM_WAITLIST_INITIALIZER, SEMHOLDER_INITIALIZER}
153+
{{(c)}, 0, SEM_WAITLIST_INITIALIZER, SEMHOLDER_INITIALIZER}
146154
# endif
147155
#else
148156
/* semcount, flags, waitlist */
149157

150158
# define SEM_INITIALIZER(c) \
151-
{(c), 0, SEM_WAITLIST_INITIALIZER}
159+
{{(c)}, 0, SEM_WAITLIST_INITIALIZER}
152160
#endif
153161

154162
#define SEM_WAITLIST(sem) (&((sem)->waitlist))

libs/libc/misc/lib_mutex.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
* Pre-processor Definitions
3636
****************************************************************************/
3737

38-
#define NXMUTEX_RESET ((pid_t)-2)
38+
#define NXMUTEX_RESET NXSEM_MRESET
3939

4040
/****************************************************************************
4141
* Private Functions
@@ -113,7 +113,7 @@ static void nxmutex_add_backtrace(FAR mutex_t *mutex)
113113

114114
int nxmutex_init(FAR mutex_t *mutex)
115115
{
116-
int ret = nxsem_init(&mutex->sem, 0, 1);
116+
int ret = nxsem_init(&mutex->sem, 0, NXMUTEX_NO_HOLDER);
117117

118118
if (ret < 0)
119119
{
@@ -197,7 +197,7 @@ bool nxmutex_is_hold(FAR mutex_t *mutex)
197197

198198
int nxmutex_get_holder(FAR mutex_t *mutex)
199199
{
200-
return mutex->holder;
200+
return mutex->holder < NXMUTEX_NO_HOLDER ? mutex->holder : -1;
201201
}
202202

203203
/****************************************************************************

libs/libc/semaphore/sem_getvalue.c

+21-1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@
5050
* be zero or a negative number whose absolute value represents the number
5151
* of tasks waiting for the semaphore.
5252
*
53+
* For a mutex, this sets svalue to 1 for a non-acquired mutex, 0 for an
54+
* acquired mutex and -1 for an acquired mutex which is blocking some
55+
* other task
56+
*
5357
* Input Parameters:
5458
* sem - Semaphore descriptor
5559
* sval - Buffer by which the value is returned
@@ -65,7 +69,23 @@ int nxsem_get_value(FAR sem_t *sem, FAR int *sval)
6569
{
6670
if (sem != NULL && sval != NULL)
6771
{
68-
*sval = atomic_read(NXSEM_COUNT(sem));
72+
if (NXSEM_IS_MUTEX(sem))
73+
{
74+
uint32_t mholder = atomic_read(NXSEM_MHOLDER(sem));
75+
if (NXSEM_MACQUIRED(mholder))
76+
{
77+
*sval = NXSEM_MBLOCKS(mholder) ? -1 : 0;
78+
}
79+
else
80+
{
81+
*sval = 1;
82+
}
83+
}
84+
else
85+
{
86+
*sval = atomic_read(NXSEM_COUNT(sem));
87+
}
88+
6989
return OK;
7090
}
7191

libs/libc/semaphore/sem_init.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,15 @@
6262
*
6363
****************************************************************************/
6464

65-
int nxsem_init(FAR sem_t *sem, int pshared, unsigned int value)
65+
int nxsem_init(FAR sem_t *sem, int pshared, int32_t value)
6666
{
6767
UNUSED(pshared);
6868

6969
DEBUGASSERT(sem != NULL && value <= SEM_VALUE_MAX);
7070

71-
/* Initialize the semaphore count */
71+
/* Initialize the semaphore count or mutex holder */
7272

73-
sem->semcount = (int32_t)value;
73+
sem->val.semcount = value;
7474

7575
/* Initialize semaphore wait list */
7676

libs/libc/semaphore/sem_post.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <errno.h>
3030
#include <assert.h>
3131

32+
#include <nuttx/sched.h>
3233
#include <nuttx/semaphore.h>
3334
#include <nuttx/atomic.h>
3435

@@ -133,8 +134,9 @@ int nxsem_post(FAR sem_t *sem)
133134
# endif
134135
)
135136
{
136-
int32_t old = 0;
137-
if (atomic_try_cmpxchg_release(NXSEM_COUNT(sem), &old, 1))
137+
int32_t old = _SCHED_GETTID();
138+
if (atomic_try_cmpxchg_release(NXSEM_MHOLDER(sem), &old,
139+
NXSEM_NO_MHOLDER))
138140
{
139141
return OK;
140142
}

libs/libc/semaphore/sem_trywait.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <assert.h>
3131
#include <sched.h>
3232

33+
#include <nuttx/sched.h>
3334
#include <nuttx/init.h>
3435
#include <nuttx/semaphore.h>
3536
#include <nuttx/atomic.h>
@@ -127,8 +128,9 @@ int nxsem_trywait(FAR sem_t *sem)
127128
#endif
128129
)
129130
{
130-
int32_t old = 1;
131-
return atomic_try_cmpxchg_acquire(NXSEM_COUNT(sem), &old, 0) ?
131+
int32_t old = NXSEM_NO_MHOLDER;
132+
return atomic_try_cmpxchg_acquire(NXSEM_MHOLDER(sem), &old,
133+
_SCHED_GETTID()) ?
132134
OK : -EAGAIN;
133135
}
134136

libs/libc/semaphore/sem_wait.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <assert.h>
3131
#include <sched.h>
3232

33+
#include <nuttx/sched.h>
3334
#include <nuttx/init.h>
3435
#include <nuttx/cancelpt.h>
3536
#include <nuttx/semaphore.h>
@@ -155,8 +156,9 @@ int nxsem_wait(FAR sem_t *sem)
155156
# endif
156157
)
157158
{
158-
int32_t old = 1;
159-
if (atomic_try_cmpxchg_acquire(NXSEM_COUNT(sem), &old, 0))
159+
int32_t old = NXSEM_NO_MHOLDER;
160+
if (atomic_try_cmpxchg_acquire(NXSEM_MHOLDER(sem), &old,
161+
_SCHED_GETTID()))
160162
{
161163
return OK;
162164
}

sched/pthread/pthread_mutexinit.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ int pthread_mutex_init(FAR pthread_mutex_t *mutex,
6666
return EINVAL;
6767
}
6868

69-
/* Initialize the mutex like a semaphore with initial count = 1 */
69+
/* Initialize the semaphore of type mutex */
7070

7171
status = mutex_init(&mutex->mutex);
7272
if (status < 0)

sched/semaphore/sem_destroy.c

+18-3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@
6161
int nxsem_destroy(FAR sem_t *sem)
6262
{
6363
int32_t old;
64+
int32_t new;
65+
FAR atomic_t *plock;
66+
bool mutex = NXSEM_IS_MUTEX(sem);
6467

6568
DEBUGASSERT(sem != NULL);
6669

@@ -74,15 +77,27 @@ int nxsem_destroy(FAR sem_t *sem)
7477
* leave the count unchanged but still return OK.
7578
*/
7679

77-
old = atomic_read(NXSEM_COUNT(sem));
80+
if (mutex)
81+
{
82+
plock = NXSEM_MHOLDER(sem);
83+
new = NXSEM_NO_MHOLDER;
84+
}
85+
else
86+
{
87+
plock = NXSEM_COUNT(sem);
88+
new = 1;
89+
}
90+
7891
do
7992
{
80-
if (old < 0)
93+
old = atomic_read(plock);
94+
if ((mutex && NXSEM_MBLOCKS(old)) ||
95+
(!mutex && old < 0))
8196
{
8297
break;
8398
}
8499
}
85-
while (!atomic_try_cmpxchg_release(NXSEM_COUNT(sem), &old, 1));
100+
while (!atomic_try_cmpxchg_release(plock, &old, new));
86101

87102
/* Release holders of the semaphore */
88103

sched/semaphore/sem_holder.c

+28-5
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,9 @@ void nxsem_canceled(FAR struct tcb_s *stcb, FAR sem_t *sem)
876876
{
877877
/* Check our assumptions */
878878

879-
DEBUGASSERT(atomic_read(NXSEM_COUNT(sem)) <= 0);
879+
DEBUGASSERT((NXSEM_IS_MUTEX(sem) || atomic_read(NXSEM_COUNT(sem)) <= 0) &&
880+
(!NXSEM_IS_MUTEX(sem) ||
881+
NSEM_MACQUIRED(atomic_read(NXSEM_MHOLDER(sem)))));
880882

881883
/* Adjust the priority of every holder as necessary */
882884

@@ -970,11 +972,32 @@ void nxsem_release_all(FAR struct tcb_s *htcb)
970972

971973
nxsem_freeholder(sem, pholder);
972974

973-
/* Increment the count on the semaphore, to releases the count
974-
* that was taken by sem_wait() or sem_post().
975-
*/
975+
if (!NXSEM_IS_MUTEX(sem))
976+
{
977+
/* Increment the count on the semaphore, to releases the count
978+
* that was taken by sem_wait() or sem_post().
979+
*/
980+
981+
atomic_fetch_add(NXSEM_COUNT(sem), 1);
982+
}
983+
else
984+
{
985+
/* Lock the mutex for us by setting the blocking bit. We are in
986+
* critical section
987+
*/
976988

977-
atomic_fetch_add(NXSEM_COUNT(sem), 1);
989+
nxsem_get_mholder_acquire(sem);
990+
991+
/* Restore the mutex holder to the highest holding priority PID,
992+
* and set the blocking bit according to how many items are still
993+
* in the wait queue
994+
*/
995+
996+
FAR struct tcb_s *wtcb =
997+
(FAR struct tcb_s *)dq_peek(SEM_WAITLIST(sem));
998+
999+
nxsem_set_mholder(wtcb, sem);
1000+
}
9781001
}
9791002
}
9801003

0 commit comments

Comments
 (0)