Preparation to recursive mutexes, now unlock primitives have the mutex as parameter.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6706 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
gdisirio 2014-02-12 09:43:27 +00:00
parent 1033792b73
commit 6c90d27a46
8 changed files with 104 additions and 68 deletions

View File

@ -183,6 +183,15 @@
*/ */
#define CH_CFG_USE_MUTEXES TRUE #define CH_CFG_USE_MUTEXES TRUE
/**
* @brief Enables recursive behavior on mutexes.
* @note Recursive mutexes are heavier and have an increased
* memory footprint.
*
* @note The default is @p FALSE.
*/
#define CH_CFG_USE_MUTEXES_RECURSIVE TRUE
/** /**
* @brief Conditional Variables APIs. * @brief Conditional Variables APIs.
* @details If enabled then the conditional variables APIs are included * @details If enabled then the conditional variables APIs are included

View File

@ -839,8 +839,7 @@ static inline void osalMutexLock(mutex_t *mp) {
static inline void osalMutexUnlock(mutex_t *mp) { static inline void osalMutexUnlock(mutex_t *mp) {
#if CH_CFG_USE_MUTEXES #if CH_CFG_USE_MUTEXES
(void)mp; chMtxUnlock(mp);
chMtxUnlock();
#elif CH_CFG_USE_SEMAPHORES #elif CH_CFG_USE_SEMAPHORES
chSemSignal((semaphore_t *)mp); chSemSignal((semaphore_t *)mp);
#else #else

View File

@ -62,6 +62,9 @@ struct mutex {
@p NULL. */ @p NULL. */
mutex_t *m_next; /**< @brief Next @p mutex_t into an mutex_t *m_next; /**< @brief Next @p mutex_t into an
owner-list or @p NULL. */ owner-list or @p NULL. */
#if CH_CFG_USE_MUTEXES_RECURSIVE || defined(__DOXYGEN__)
cnt_t m_taken; /**< @brief Mutex recursion counter. */
#endif
}; };
/*===========================================================================*/ /*===========================================================================*/
@ -75,7 +78,11 @@ struct mutex {
* *
* @param[in] name the name of the mutex variable * @param[in] name the name of the mutex variable
*/ */
#if CH_CFG_USE_MUTEXES_RECURSIVE || defined(__DOXYGEN__)
#define _MUTEX_DATA(name) {_threads_queue_t_DATA(name.m_queue), NULL, NULL, 0}
#else
#define _MUTEX_DATA(name) {_threads_queue_t_DATA(name.m_queue), NULL, NULL} #define _MUTEX_DATA(name) {_threads_queue_t_DATA(name.m_queue), NULL, NULL}
#endif
/** /**
* @brief Static mutex initializer. * @brief Static mutex initializer.
@ -99,8 +106,8 @@ extern "C" {
void chMtxLockS(mutex_t *mp); void chMtxLockS(mutex_t *mp);
bool chMtxTryLock(mutex_t *mp); bool chMtxTryLock(mutex_t *mp);
bool chMtxTryLockS(mutex_t *mp); bool chMtxTryLockS(mutex_t *mp);
mutex_t *chMtxUnlock(void); void chMtxUnlock(mutex_t *mp);
mutex_t *chMtxUnlockS(void); void chMtxUnlockS(mutex_t *mp);
void chMtxUnlockAll(void); void chMtxUnlockAll(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
@ -124,6 +131,17 @@ static inline bool chMtxQueueNotEmptyS(mutex_t *mp) {
return queue_notempty(&mp->m_queue); return queue_notempty(&mp->m_queue);
} }
/**
* @brief Returns the next mutex in the mutexes stack of the current thread.
*
* @return A pointer to the next mutex in the stack.
* @retval NULL if the stack is empty.
*/
static inline mutex_t *chMtxGetNextMutex(void) {
return chThdGetSelfX()->p_mtxlist;
}
#endif /* CH_CFG_USE_MUTEXES */ #endif /* CH_CFG_USE_MUTEXES */
#endif /* _CHMTX_H_ */ #endif /* _CHMTX_H_ */

View File

@ -210,7 +210,8 @@ msg_t chCondWaitS(condition_variable_t *cp) {
chDbgCheck(cp != NULL); chDbgCheck(cp != NULL);
chDbgAssert(ctp->p_mtxlist != NULL, "not owning a mutex"); chDbgAssert(ctp->p_mtxlist != NULL, "not owning a mutex");
mp = chMtxUnlockS(); mp = chMtxGetNextMutex();
chMtxUnlockS(mp);
ctp->p_u.wtobjp = cp; ctp->p_u.wtobjp = cp;
queue_prio_insert(ctp, &cp->c_queue); queue_prio_insert(ctp, &cp->c_queue);
chSchGoSleepS(CH_STATE_WTCOND); chSchGoSleepS(CH_STATE_WTCOND);
@ -293,7 +294,8 @@ msg_t chCondWaitTimeoutS(condition_variable_t *cp, systime_t time) {
chDbgCheck((cp != NULL) && (time != TIME_IMMEDIATE)); chDbgCheck((cp != NULL) && (time != TIME_IMMEDIATE));
chDbgAssert(currp->p_mtxlist != NULL, "not owning a mutex"); chDbgAssert(currp->p_mtxlist != NULL, "not owning a mutex");
mp = chMtxUnlockS(); mp = chMtxGetNextMutex();
chMtxUnlockS(mp);
currp->p_u.wtobjp = cp; currp->p_u.wtobjp = cp;
queue_prio_insert(currp, &cp->c_queue); queue_prio_insert(currp, &cp->c_queue);
msg = chSchGoSleepTimeoutS(CH_STATE_WTCOND, time); msg = chSchGoSleepTimeoutS(CH_STATE_WTCOND, time);

View File

@ -47,7 +47,7 @@
*/ */
#if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__) #if CH_CFG_USE_MUTEXES || defined(__DOXYGEN__)
#define H_LOCK(h) chMtxLock(&(h)->h_mtx) #define H_LOCK(h) chMtxLock(&(h)->h_mtx)
#define H_UNLOCK(h) chMtxUnlock() #define H_UNLOCK(h) chMtxUnlock(&(h)->h_mtx)
#else #else
#define H_LOCK(h) chSemWait(&(h)->h_sem) #define H_LOCK(h) chSemWait(&(h)->h_sem)
#define H_UNLOCK(h) chSemSignal(&(h)->h_sem) #define H_UNLOCK(h) chSemSignal(&(h)->h_sem)

View File

@ -41,12 +41,18 @@
* owner of the mutex. * owner of the mutex.
* . * .
* <h2>Constraints</h2> * <h2>Constraints</h2>
* In ChibiOS/RT the Unlock operations are always performed in * In ChibiOS/RT the Unlock operations must always be performed
* lock-reverse order. The unlock API does not even have a parameter, * in lock-reverse order. This restriction both improves the
* the mutex to unlock is selected from an internal, per-thread, stack * performance and is required for an efficient implementation
* of owned mutexes. This both improves the performance and is * of the priority inheritance mechanism.<br>
* required for an efficient implementation of the priority * Operating under this restriction also ensures that deadlocks
* inheritance mechanism. * are no possible.
*
* <h2>Recursive mode</h2>
* By default mutexes are not recursive, this mean that it is not
* possible to take a mutex already owned by the same thread.
* It is possible to enable the recursive behavior by enabling the
* option @p CH_CFG_USE_MUTEXES_RECURSIVE.
* *
* <h2>The priority inversion problem</h2> * <h2>The priority inversion problem</h2>
* The mutexes in ChibiOS/RT implements the <b>full</b> priority * The mutexes in ChibiOS/RT implements the <b>full</b> priority
@ -270,39 +276,40 @@ bool chMtxTryLockS(mutex_t *mp) {
* @post The mutex is unlocked and removed from the per-thread stack of * @post The mutex is unlocked and removed from the per-thread stack of
* owned mutexes. * owned mutexes.
* *
* @return A pointer to the unlocked mutex. * @param[in] mp pointer to the @p mutex_t structure
* *
* @api * @api
*/ */
mutex_t *chMtxUnlock(void) { void chMtxUnlock(mutex_t *mp) {
thread_t *ctp = currp; thread_t *ctp = currp;
mutex_t *ump, *mp; mutex_t *lmp;
chSysLock(); chSysLock();
chDbgAssert(ctp->p_mtxlist != NULL, "owned mutexes list empty"); chDbgAssert(ctp->p_mtxlist != NULL, "owned mutexes list empty");
chDbgAssert(ctp->p_mtxlist != mp, "not next in list");
chDbgAssert(ctp->p_mtxlist->m_owner == ctp, "ownership failure"); chDbgAssert(ctp->p_mtxlist->m_owner == ctp, "ownership failure");
/* Removes the top mutex from the thread's owned mutexes list and marks it /* Removes the top mutex from the thread's owned mutexes list and marks
as not owned.*/ it as not owned. Note, it is assumed to be the same mutex passed as
ump = ctp->p_mtxlist; parameter of this function.*/
ctp->p_mtxlist = ump->m_next; ctp->p_mtxlist = mp->m_next;
/* If a thread is waiting on the mutex then the fun part begins.*/ /* If a thread is waiting on the mutex then the fun part begins.*/
if (chMtxQueueNotEmptyS(ump)) { if (chMtxQueueNotEmptyS(mp)) {
thread_t *tp; thread_t *tp;
/* Recalculates the optimal thread priority by scanning the owned /* Recalculates the optimal thread priority by scanning the owned
mutexes list.*/ mutexes list.*/
tprio_t newprio = ctp->p_realprio; tprio_t newprio = ctp->p_realprio;
mp = ctp->p_mtxlist; lmp = ctp->p_mtxlist;
while (mp != NULL) { while (lmp != NULL) {
/* If the highest priority thread waiting in the mutexes list has a /* If the highest priority thread waiting in the mutexes list has a
greater priority than the current thread base priority then the final greater priority than the current thread base priority then the final
priority will have at least that priority.*/ priority will have at least that priority.*/
if (chMtxQueueNotEmptyS(mp) && (mp->m_queue.p_next->p_prio > newprio)) if (chMtxQueueNotEmptyS(lmp) && (lmp->m_queue.p_next->p_prio > newprio))
newprio = mp->m_queue.p_next->p_prio; newprio = lmp->m_queue.p_next->p_prio;
mp = mp->m_next; lmp = lmp->m_next;
} }
/* Assigns to the current thread the highest priority among all the /* Assigns to the current thread the highest priority among all the
@ -311,16 +318,16 @@ mutex_t *chMtxUnlock(void) {
/* Awakens the highest priority thread waiting for the unlocked mutex and /* Awakens the highest priority thread waiting for the unlocked mutex and
assigns the mutex to it.*/ assigns the mutex to it.*/
tp = queue_fifo_remove(&ump->m_queue); tp = queue_fifo_remove(&mp->m_queue);
ump->m_owner = tp; mp->m_owner = tp;
ump->m_next = tp->p_mtxlist; mp->m_next = tp->p_mtxlist;
tp->p_mtxlist = ump; tp->p_mtxlist = mp;
chSchWakeupS(tp, MSG_OK); chSchWakeupS(tp, MSG_OK);
} }
else else
ump->m_owner = NULL; mp->m_owner = NULL;
chSysUnlock(); chSysUnlock();
return ump;
} }
/** /**
@ -331,53 +338,54 @@ mutex_t *chMtxUnlock(void) {
* @post This function does not reschedule so a call to a rescheduling * @post This function does not reschedule so a call to a rescheduling
* function must be performed before unlocking the kernel. * function must be performed before unlocking the kernel.
* *
* @return A pointer to the unlocked mutex. * @param[in] mp pointer to the @p mutex_t structure
* *
* @sclass * @sclass
*/ */
mutex_t *chMtxUnlockS(void) { void chMtxUnlockS(mutex_t *mp) {
thread_t *ctp = currp; thread_t *ctp = currp;
mutex_t *ump, *mp; mutex_t *lmp;
chDbgCheckClassS();
chDbgAssert(ctp->p_mtxlist != NULL, "owned mutexes list empty"); chDbgAssert(ctp->p_mtxlist != NULL, "owned mutexes list empty");
chDbgAssert(ctp->p_mtxlist != mp, "not next in list");
chDbgAssert(ctp->p_mtxlist->m_owner == ctp, "ownership failure"); chDbgAssert(ctp->p_mtxlist->m_owner == ctp, "ownership failure");
/* Removes the top mutex from the owned mutexes list and marks it as not /* Removes the top mutex from the thread's owned mutexes list and marks
owned.*/ it as not owned. Note, it is assumed to be the same mutex passed as
ump = ctp->p_mtxlist; parameter of this function.*/
ctp->p_mtxlist = ump->m_next; ctp->p_mtxlist = mp->m_next;
/* If a thread is waiting on the mutex then the fun part begins.*/ /* If a thread is waiting on the mutex then the fun part begins.*/
if (chMtxQueueNotEmptyS(ump)) { if (chMtxQueueNotEmptyS(mp)) {
thread_t *tp; thread_t *tp;
/* Recalculates the optimal thread priority by scanning the owned /* Recalculates the optimal thread priority by scanning the owned
mutexes list.*/ mutexes list.*/
tprio_t newprio = ctp->p_realprio; tprio_t newprio = ctp->p_realprio;
mp = ctp->p_mtxlist; lmp = ctp->p_mtxlist;
while (mp != NULL) { while (lmp != NULL) {
/* If the highest priority thread waiting in the mutexes list has a /* If the highest priority thread waiting in the mutexes list has a
greater priority than the current thread base priority then the final greater priority than the current thread base priority then the final
priority will have at least that priority.*/ priority will have at least that priority.*/
if (chMtxQueueNotEmptyS(mp) && (mp->m_queue.p_next->p_prio > newprio)) if (chMtxQueueNotEmptyS(lmp) && (lmp->m_queue.p_next->p_prio > newprio))
newprio = mp->m_queue.p_next->p_prio; newprio = lmp->m_queue.p_next->p_prio;
lmp = lmp->m_next;
mp = mp->m_next;
} }
/* Assigns to the current thread the highest priority among all the
waiting threads.*/
ctp->p_prio = newprio; ctp->p_prio = newprio;
/* Awakens the highest priority thread waiting for the unlocked mutex and /* Awakens the highest priority thread waiting for the unlocked mutex and
assigns the mutex to it.*/ assigns the mutex to it.*/
tp = queue_fifo_remove(&ump->m_queue); tp = queue_fifo_remove(&mp->m_queue);
ump->m_owner = tp; mp->m_owner = tp;
ump->m_next = tp->p_mtxlist; mp->m_next = tp->p_mtxlist;
tp->p_mtxlist = ump; tp->p_mtxlist = mp;
chSchReadyI(tp); chSchReadyI(tp);
} }
else else
ump->m_owner = NULL; mp->m_owner = NULL;
return ump;
} }
/** /**

View File

@ -601,13 +601,13 @@ static void bmk12_execute(void) {
test_start_timer(1000); test_start_timer(1000);
do { do {
chMtxLock(&mtx1); chMtxLock(&mtx1);
chMtxUnlock(); chMtxUnlock(&mtx1);
chMtxLock(&mtx1); chMtxLock(&mtx1);
chMtxUnlock(); chMtxUnlock(&mtx1);
chMtxLock(&mtx1); chMtxLock(&mtx1);
chMtxUnlock(); chMtxUnlock(&mtx1);
chMtxLock(&mtx1); chMtxLock(&mtx1);
chMtxUnlock(); chMtxUnlock(&mtx1);
n++; n++;
#if defined(SIMULATOR) #if defined(SIMULATOR)
ChkIntSources(); ChkIntSources();

View File

@ -90,7 +90,7 @@ static msg_t thread1(void *p) {
chMtxLock(&m1); chMtxLock(&m1);
test_emit_token(*(char *)p); test_emit_token(*(char *)p);
chMtxUnlock(); chMtxUnlock(&m1);
return 0; return 0;
} }
@ -103,7 +103,7 @@ static void mtx1_execute(void) {
threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread1, "C"); threads[2] = chThdCreateStatic(wa[2], WA_SIZE, prio+3, thread1, "C");
threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread1, "B"); threads[3] = chThdCreateStatic(wa[3], WA_SIZE, prio+4, thread1, "B");
threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread1, "A"); threads[4] = chThdCreateStatic(wa[4], WA_SIZE, prio+5, thread1, "A");
chMtxUnlock(); chMtxUnlock(&m1);
test_wait_threads(); test_wait_threads();
test_assert(1, prio == chThdGetPriorityX(), "wrong priority level"); test_assert(1, prio == chThdGetPriorityX(), "wrong priority level");
test_assert_sequence(2, "ABCDE"); test_assert_sequence(2, "ABCDE");
@ -349,7 +349,7 @@ static msg_t thread4a(void *p) {
(void)p; (void)p;
chThdSleepMilliseconds(50); chThdSleepMilliseconds(50);
chMtxLock(&m2); chMtxLock(&m2);
chMtxUnlock(); chMtxUnlock(&m2);
return 0; return 0;
} }
@ -358,7 +358,7 @@ static msg_t thread4b(void *p) {
(void)p; (void)p;
chThdSleepMilliseconds(150); chThdSleepMilliseconds(150);
chMtxLock(&m1); chMtxLock(&m1);
chMtxUnlock(); chMtxUnlock(&m1);
return 0; return 0;
} }
@ -378,7 +378,7 @@ static void mtx4_execute(void) {
test_assert(3, chThdGetPriorityX() == p1, "wrong priority level"); test_assert(3, chThdGetPriorityX() == p1, "wrong priority level");
chThdSleepMilliseconds(100); chThdSleepMilliseconds(100);
test_assert(4, chThdGetPriorityX() == p2, "wrong priority level"); test_assert(4, chThdGetPriorityX() == p2, "wrong priority level");
chMtxUnlock(); chMtxUnlock(&m1);
test_assert(5, chThdGetPriorityX() == p1, "wrong priority level"); test_assert(5, chThdGetPriorityX() == p1, "wrong priority level");
chThdSleepMilliseconds(100); chThdSleepMilliseconds(100);
test_assert(6, chThdGetPriorityX() == p1, "wrong priority level"); test_assert(6, chThdGetPriorityX() == p1, "wrong priority level");
@ -398,7 +398,7 @@ static void mtx4_execute(void) {
chThdSleepMilliseconds(100); chThdSleepMilliseconds(100);
test_assert(11, chThdGetPriorityX() == p2, "wrong priority level"); test_assert(11, chThdGetPriorityX() == p2, "wrong priority level");
chSysLock(); chSysLock();
chMtxUnlockS(); chMtxUnlockS(&m1);
chSchRescheduleS(); chSchRescheduleS();
chSysUnlock(); chSysUnlock();
test_assert(12, chThdGetPriorityX() == p1, "wrong priority level"); test_assert(12, chThdGetPriorityX() == p1, "wrong priority level");
@ -444,7 +444,7 @@ static void mtx5_execute(void) {
test_assert(2, !b, "not locked"); test_assert(2, !b, "not locked");
chSysLock(); chSysLock();
chMtxUnlockS(); chMtxUnlockS(&m1);
chSysUnlock(); chSysUnlock();
test_assert(3, queue_isempty(&m1.m_queue), "queue not empty"); test_assert(3, queue_isempty(&m1.m_queue), "queue not empty");
@ -487,7 +487,7 @@ static msg_t thread10(void *p) {
chMtxLock(&m1); chMtxLock(&m1);
chCondWait(&c1); chCondWait(&c1);
test_emit_token(*(char *)p); test_emit_token(*(char *)p);
chMtxUnlock(); chMtxUnlock(&m1);
return 0; return 0;
} }
@ -580,8 +580,8 @@ static msg_t thread11(void *p) {
chCondWait(&c1); chCondWait(&c1);
#endif #endif
test_emit_token(*(char *)p); test_emit_token(*(char *)p);
chMtxUnlock(); chMtxUnlock(&m1);
chMtxUnlock(); chMtxUnlock(&m2);
return 0; return 0;
} }
@ -589,7 +589,7 @@ static msg_t thread12(void *p) {
chMtxLock(&m2); chMtxLock(&m2);
test_emit_token(*(char *)p); test_emit_token(*(char *)p);
chMtxUnlock(); chMtxUnlock(&m2);
return 0; return 0;
} }