libc/semaphore: Use the while loop to handle the inerrupt correctly

If atomic_try_cmpxchg_xxxx runs on LL/SC architectures (e.g.ARMv7,
ARMv8, RISC-V), the weak CAS expands to a single LDREX/STREX pair.

If the CPU takes an IRQ/FIQ/SVC between the two instructions,
hardware performs an implicit CLREX and the following STREX returns
1, therefore atomic_try_cmpxchg_xxxx return failure even though
*addr* still holds the expected value.

So let's retry atomic_try_cmpxchg_xxxx in this case.

Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
This commit is contained in:
Xiang Xiao 2025-06-22 03:03:59 +08:00 committed by Alan C. Assis
parent d8f241b29d
commit fb14b54b83
3 changed files with 35 additions and 45 deletions

View file

@ -151,24 +151,26 @@ int nxsem_post(FAR sem_t *sem)
}
# endif
if (fastpath)
while (fastpath)
{
int32_t old;
int32_t new;
FAR atomic_t *val = mutex ? NXSEM_MHOLDER(sem) : NXSEM_COUNT(sem);
int32_t old = atomic_read(val);
int32_t new;
if (mutex)
{
old = _SCHED_GETTID();
if (NXSEM_MBLOCKING(old))
{
break;
}
new = NXSEM_NO_MHOLDER;
}
else
{
old = atomic_read(val);
if (old < 0)
{
goto out;
break;
}
new = old + 1;
@ -179,9 +181,6 @@ int nxsem_post(FAR sem_t *sem)
return OK;
}
}
out:
#else
UNUSED(mutex);
UNUSED(fastpath);

View file

@ -146,43 +146,35 @@ int nxsem_trywait(FAR sem_t *sem)
}
# endif
if (fastpath)
while (fastpath)
{
bool ret = false;
int32_t old;
int32_t new;
FAR atomic_t *val = mutex ? NXSEM_MHOLDER(sem) : NXSEM_COUNT(sem);
int32_t old = atomic_read(val);
int32_t new;
if (mutex)
{
old = NXSEM_NO_MHOLDER;
if (old != NXSEM_NO_MHOLDER)
{
return -EAGAIN;
}
new = _SCHED_GETTID();
}
else
{
old = atomic_read(val);
if (old < 1)
{
return -EAGAIN;
}
new = old - 1;
}
do
if (atomic_try_cmpxchg_acquire(val, &old, new))
{
if (!mutex)
{
if (old < 1)
{
break;
}
new = old - 1;
}
else
{
new = _SCHED_GETTID();
}
ret = atomic_try_cmpxchg_acquire(NXSEM_MHOLDER(sem), &old, new);
return OK;
}
while (!mutex && !ret);
return ret ? OK : -EAGAIN;
}
#else

View file

@ -174,24 +174,26 @@ int nxsem_wait(FAR sem_t *sem)
}
# endif
if (fastpath)
while (fastpath)
{
int32_t old;
int32_t new;
FAR atomic_t *val = mutex ? NXSEM_MHOLDER(sem) : NXSEM_COUNT(sem);
int32_t old = atomic_read(val);
int32_t new;
if (mutex)
{
old = NXSEM_NO_MHOLDER;
if (old != NXSEM_NO_MHOLDER)
{
break;
}
new = _SCHED_GETTID();
}
else
{
old = atomic_read(val);
if (old < 1)
{
goto out;
break;
}
new = old - 1;
@ -202,9 +204,6 @@ int nxsem_wait(FAR sem_t *sem)
return OK;
}
}
out:
#else
UNUSED(mutex);
UNUSED(fastpath);