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
/**
* @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.
* @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) {
#if CH_CFG_USE_MUTEXES
(void)mp;
chMtxUnlock();
chMtxUnlock(mp);
#elif CH_CFG_USE_SEMAPHORES
chSemSignal((semaphore_t *)mp);
#else

View File

@ -62,6 +62,9 @@ struct mutex {
@p NULL. */
mutex_t *m_next; /**< @brief Next @p mutex_t into an
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
*/
#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}
#endif
/**
* @brief Static mutex initializer.
@ -99,8 +106,8 @@ extern "C" {
void chMtxLockS(mutex_t *mp);
bool chMtxTryLock(mutex_t *mp);
bool chMtxTryLockS(mutex_t *mp);
mutex_t *chMtxUnlock(void);
mutex_t *chMtxUnlockS(void);
void chMtxUnlock(mutex_t *mp);
void chMtxUnlockS(mutex_t *mp);
void chMtxUnlockAll(void);
#ifdef __cplusplus
}
@ -124,6 +131,17 @@ static inline bool chMtxQueueNotEmptyS(mutex_t *mp) {
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 /* _CHMTX_H_ */

View File

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

View File

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

View File

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

View File

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

View File

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