Merge pull request #6696 from lebrush/fix/semaphore

sys: sema: semaphore bugfixes
This commit is contained in:
Martine Lenders 2017-03-28 20:54:06 +02:00 committed by GitHub
commit fd16692c1c
3 changed files with 58 additions and 31 deletions

View File

@ -88,7 +88,7 @@ u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t count)
return (u32_t)(stop / US_PER_MS); return (u32_t)(stop / US_PER_MS);
} }
else { else {
sema_wait_timed((sema_t *)sem, 0); sema_wait((sema_t *)sem);
return 0; return 0;
} }
} }

View File

@ -97,19 +97,44 @@ void sema_create(sema_t *sema, unsigned int value);
*/ */
void sema_destroy(sema_t *sema); void sema_destroy(sema_t *sema);
/**
* @brief Wait for a semaphore, blocking or non-blocking.
*
* @details For commit purposes you should probably use sema_wait(),
* sema_wait_timed() and sema_try_wait() instead.
*
* @pre `(sema != NULL)`
*
* @param[in] sema A semaphore.
* @param[in] block if true, block until semaphore is available.
* @param[in] timeout if blocking, time in microseconds until the semaphore
* times out. 0 waits forever.
*
* @return 0 on success
* @return -ETIMEDOUT, if the semaphore times out.
* @return -ECANCELED, if the semaphore was destroyed.
* @return -EAGAIN, if the semaphore is not posted (only if block = 0)
*/
int _sema_wait(sema_t *sema, int block, uint64_t timeout);
/** /**
* @brief Wait for a semaphore being posted. * @brief Wait for a semaphore being posted.
* *
* @pre `(sema != NULL)` * @pre `(sema != NULL)`
* *
* @param[in] sema A semaphore. * @param[in] sema A semaphore.
* @param[in] timeout Time in microseconds until the semaphore times out. 0 for no timeout. * @param[in] timeout Time in microseconds until the semaphore times out.
* 0 does not wait.
* *
* @return 0 on success * @return 0 on success
* @return -ETIMEDOUT, if the semaphore times out. * @return -ETIMEDOUT, if the semaphore times out.
* @return -ECANCELED, if the semaphore was destroyed. * @return -ECANCELED, if the semaphore was destroyed.
* @return -EAGAIN, if the semaphore is not posted (only if timeout = 0)
*/ */
int sema_wait_timed(sema_t *sema, uint64_t timeout); static inline int sema_wait_timed(sema_t *sema, uint64_t timeout)
{
return _sema_wait(sema, (timeout != 0), timeout);
}
/** /**
* @brief Wait for a semaphore being posted (without timeout). * @brief Wait for a semaphore being posted (without timeout).
@ -123,7 +148,7 @@ int sema_wait_timed(sema_t *sema, uint64_t timeout);
*/ */
static inline int sema_wait(sema_t *sema) static inline int sema_wait(sema_t *sema)
{ {
return sema_wait_timed(sema, 0); return _sema_wait(sema, 1, 0);
} }
/** /**
@ -137,7 +162,10 @@ static inline int sema_wait(sema_t *sema)
* @return -EAGAIN, if the semaphore is not posted. * @return -EAGAIN, if the semaphore is not posted.
* @return -ECANCELED, if the semaphore was destroyed. * @return -ECANCELED, if the semaphore was destroyed.
*/ */
int sema_try_wait(sema_t *sema); static inline int sema_try_wait(sema_t *sema)
{
return _sema_wait(sema, 0, 0);
}
/** /**
* @brief Signal semaphore. * @brief Signal semaphore.

View File

@ -44,25 +44,7 @@ void sema_destroy(sema_t *sema)
mutex_unlock(&sema->mutex); mutex_unlock(&sema->mutex);
} }
int sema_try_wait(sema_t *sema) int _sema_wait(sema_t *sema, int block, uint64_t us)
{
assert(sema != NULL);
int ret = -ECANCELED;
if (sema->state == SEMA_OK) {
ret = -EAGAIN;
unsigned old = irq_disable();
if (sema->value > 0) {
--sema->value;
ret = 0;
}
irq_restore(old);
}
return ret;
}
int sema_wait_timed(sema_t *sema, uint64_t us)
{ {
assert(sema != NULL); assert(sema != NULL);
@ -70,27 +52,44 @@ int sema_wait_timed(sema_t *sema, uint64_t us)
return -ECANCELED; return -ECANCELED;
} }
int did_block = block;
unsigned old = irq_disable(); unsigned old = irq_disable();
if (sema->value == 0) { while ((sema->value == 0) && block) {
irq_restore(old); irq_restore(old);
int timeout = xtimer_mutex_lock_timeout(&sema->mutex, us); if (us == 0) {
mutex_lock(&sema->mutex);
}
else {
uint64_t start = xtimer_now_usec64();
block = !xtimer_mutex_lock_timeout(&sema->mutex, us);
uint64_t elapsed = xtimer_now_usec64() - start;
if (elapsed < us) {
us -= elapsed;
}
else {
block = 0;
}
}
if (sema->state != SEMA_OK) { if (sema->state != SEMA_OK) {
mutex_unlock(&sema->mutex); mutex_unlock(&sema->mutex);
return -ECANCELED; return -ECANCELED;
} }
if (timeout) {
return -ETIMEDOUT;
}
old = irq_disable(); old = irq_disable();
} }
if (sema->value == 0) {
irq_restore(old);
return (did_block) ? -ETIMEDOUT : -EAGAIN;
}
unsigned int value = --sema->value; unsigned int value = --sema->value;
irq_restore(old); irq_restore(old);
if (value > 0) { /* only unlock mutex if it was a blocking operation */
if (did_block && value > 0) {
mutex_unlock(&sema->mutex); mutex_unlock(&sema->mutex);
} }