Recursive mutexes, test code not done yet.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6707 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
gdisirio 2014-02-12 13:14:11 +00:00
parent 6c90d27a46
commit 7a3da58ae2
3 changed files with 206 additions and 132 deletions

View File

@ -63,7 +63,7 @@ struct mutex {
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__) #if CH_CFG_USE_MUTEXES_RECURSIVE || defined(__DOXYGEN__)
cnt_t m_taken; /**< @brief Mutex recursion counter. */ cnt_t m_cnt; /**< @brief Mutex recursion counter. */
#endif #endif
}; };

View File

@ -108,6 +108,9 @@ void chMtxObjectInit(mutex_t *mp) {
queue_init(&mp->m_queue); queue_init(&mp->m_queue);
mp->m_owner = NULL; mp->m_owner = NULL;
#if CH_CFG_USE_MUTEXES_RECURSIVE
mp->m_cnt = 0;
#endif
} }
/** /**
@ -145,65 +148,84 @@ void chMtxLockS(mutex_t *mp) {
/* Is the mutex already locked? */ /* Is the mutex already locked? */
if (mp->m_owner != NULL) { if (mp->m_owner != NULL) {
/* Priority inheritance protocol; explores the thread-mutex dependencies #if CH_CFG_USE_MUTEXES_RECURSIVE
boosting the priority of all the affected threads to equal the priority
of the running thread requesting the mutex.*/
thread_t *tp = mp->m_owner;
/* Does the running thread have higher priority than the mutex chDbgAssert(mp->m_cnt >= 1, "counter is not positive");
owning thread? */
while (tp->p_prio < ctp->p_prio) {
/* Make priority of thread tp match the running thread's priority.*/
tp->p_prio = ctp->p_prio;
/* The following states need priority queues reordering.*/ /* If the mutex is already owned by this thread, the counter is increased
switch (tp->p_state) { and there is no need of more actions.*/
case CH_STATE_WTMTX: if (mp->m_owner == ctp)
/* Re-enqueues the mutex owner with its new priority.*/ mp->m_cnt++;
queue_prio_insert(queue_dequeue(tp), else {
(threads_queue_t *)tp->p_u.wtobjp);
tp = ((mutex_t *)tp->p_u.wtobjp)->m_owner;
continue;
#if CH_CFG_USE_CONDVARS | \
(CH_CFG_USE_SEMAPHORES && CH_CFG_USE_SEMAPHORES_PRIORITY) | \
(CH_CFG_USE_MESSAGES && CH_CFG_USE_MESSAGES_PRIORITY)
#if CH_CFG_USE_CONDVARS
case CH_STATE_WTCOND:
#endif #endif
#if CH_CFG_USE_SEMAPHORES && CH_CFG_USE_SEMAPHORES_PRIORITY /* Priority inheritance protocol; explores the thread-mutex dependencies
case CH_STATE_WTSEM: boosting the priority of all the affected threads to equal the
#endif priority of the running thread requesting the mutex.*/
#if CH_CFG_USE_MESSAGES && CH_CFG_USE_MESSAGES_PRIORITY thread_t *tp = mp->m_owner;
case CH_STATE_SNDMSGQ:
#endif /* Does the running thread have higher priority than the mutex
/* Re-enqueues tp with its new priority on the queue.*/ owning thread? */
queue_prio_insert(queue_dequeue(tp), while (tp->p_prio < ctp->p_prio) {
(threads_queue_t *)tp->p_u.wtobjp); /* Make priority of thread tp match the running thread's priority.*/
break; tp->p_prio = ctp->p_prio;
#endif
case CH_STATE_READY: /* The following states need priority queues reordering.*/
#if CH_DBG_ENABLE_ASSERTS switch (tp->p_state) {
/* Prevents an assertion in chSchReadyI().*/ case CH_STATE_WTMTX:
tp->p_state = CH_STATE_CURRENT; /* Re-enqueues the mutex owner with its new priority.*/
#endif queue_prio_insert(queue_dequeue(tp),
/* Re-enqueues tp with its new priority on the ready list.*/ (threads_queue_t *)tp->p_u.wtobjp);
chSchReadyI(queue_dequeue(tp)); tp = ((mutex_t *)tp->p_u.wtobjp)->m_owner;
continue;
#if CH_CFG_USE_CONDVARS | \
(CH_CFG_USE_SEMAPHORES && CH_CFG_USE_SEMAPHORES_PRIORITY) | \
(CH_CFG_USE_MESSAGES && CH_CFG_USE_MESSAGES_PRIORITY)
#if CH_CFG_USE_CONDVARS
case CH_STATE_WTCOND:
#endif
#if CH_CFG_USE_SEMAPHORES && CH_CFG_USE_SEMAPHORES_PRIORITY
case CH_STATE_WTSEM:
#endif
#if CH_CFG_USE_MESSAGES && CH_CFG_USE_MESSAGES_PRIORITY
case CH_STATE_SNDMSGQ:
#endif
/* Re-enqueues tp with its new priority on the queue.*/
queue_prio_insert(queue_dequeue(tp),
(threads_queue_t *)tp->p_u.wtobjp);
break;
#endif
case CH_STATE_READY:
#if CH_DBG_ENABLE_ASSERTS
/* Prevents an assertion in chSchReadyI().*/
tp->p_state = CH_STATE_CURRENT;
#endif
/* Re-enqueues tp with its new priority on the ready list.*/
chSchReadyI(queue_dequeue(tp));
break;
}
break; break;
} }
break;
/* Sleep on the mutex.*/
queue_prio_insert(ctp, &mp->m_queue);
ctp->p_u.wtobjp = mp;
chSchGoSleepS(CH_STATE_WTMTX);
/* It is assumed that the thread performing the unlock operation assigns
the mutex to this thread.*/
chDbgAssert(mp->m_owner == ctp, "not owner");
chDbgAssert(ctp->p_mtxlist == mp, "not owned");
#if CH_CFG_USE_MUTEXES_RECURSIVE
chDbgAssert(mp->m_cnt == 1, "counter is not one");
} }
#endif
/* Sleep on the mutex.*/
queue_prio_insert(ctp, &mp->m_queue);
ctp->p_u.wtobjp = mp;
chSchGoSleepS(CH_STATE_WTMTX);
/* It is assumed that the thread performing the unlock operation assigns
the mutex to this thread.*/
chDbgAssert(mp->m_owner == ctp, "not owner");
chDbgAssert(ctp->p_mtxlist == mp, "not owned");
} }
else { else {
#if CH_CFG_USE_MUTEXES_RECURSIVE
chDbgAssert(mp->m_cnt == 0, "counter is not zero");
mp->m_cnt++;
#endif
/* It was not owned, inserted in the owned mutexes list.*/ /* It was not owned, inserted in the owned mutexes list.*/
mp->m_owner = ctp; mp->m_owner = ctp;
mp->m_next = ctp->p_mtxlist; mp->m_next = ctp->p_mtxlist;
@ -261,9 +283,24 @@ bool chMtxTryLockS(mutex_t *mp) {
chDbgCheckClassS(); chDbgCheckClassS();
chDbgCheck(mp != NULL); chDbgCheck(mp != NULL);
if (mp->m_owner != NULL) if (mp->m_owner != NULL) {
return false; #if CH_CFG_USE_MUTEXES_RECURSIVE
chDbgAssert(mp->m_cnt >= 1, "counter is not positive");
if (mp->m_owner == currp) {
mp->m_cnt++;
return true;
}
#endif
return false;
}
#if CH_CFG_USE_MUTEXES_RECURSIVE
chDbgAssert(mp->m_cnt == 0, "counter is not zero");
mp->m_cnt++;
#endif
mp->m_owner = currp; mp->m_owner = currp;
mp->m_next = currp->p_mtxlist; mp->m_next = currp->p_mtxlist;
currp->p_mtxlist = mp; currp->p_mtxlist = mp;
@ -284,48 +321,62 @@ void chMtxUnlock(mutex_t *mp) {
thread_t *ctp = currp; thread_t *ctp = currp;
mutex_t *lmp; mutex_t *lmp;
chDbgCheck(mp != NULL);
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");
#if CH_CFG_USE_MUTEXES_RECURSIVE
chDbgAssert(mp->m_cnt >= 1, "counter is not positive");
/* Removes the top mutex from the thread's owned mutexes list and marks if (--mp->m_cnt == 0) {
it as not owned. Note, it is assumed to be the same mutex passed as #endif
parameter of this function.*/
ctp->p_mtxlist = mp->m_next;
/* If a thread is waiting on the mutex then the fun part begins.*/ chDbgAssert(ctp->p_mtxlist == mp, "not next in list");
if (chMtxQueueNotEmptyS(mp)) {
thread_t *tp;
/* Recalculates the optimal thread priority by scanning the owned /* Removes the top mutex from the thread's owned mutexes list and marks
mutexes list.*/ it as not owned. Note, it is assumed to be the same mutex passed as
tprio_t newprio = ctp->p_realprio; parameter of this function.*/
lmp = ctp->p_mtxlist; ctp->p_mtxlist = mp->m_next;
while (lmp != NULL) {
/* If the highest priority thread waiting in the mutexes list has a /* If a thread is waiting on the mutex then the fun part begins.*/
greater priority than the current thread base priority then the final if (chMtxQueueNotEmptyS(mp)) {
priority will have at least that priority.*/ thread_t *tp;
if (chMtxQueueNotEmptyS(lmp) && (lmp->m_queue.p_next->p_prio > newprio))
newprio = lmp->m_queue.p_next->p_prio; /* Recalculates the optimal thread priority by scanning the owned
lmp = lmp->m_next; mutexes list.*/
tprio_t newprio = ctp->p_realprio;
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(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.*/
#if CH_CFG_USE_MUTEXES_RECURSIVE
mp->m_cnt = 1;
#endif
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
/* Assigns to the current thread the highest priority among all the mp->m_owner = NULL;
waiting threads.*/ #if CH_CFG_USE_MUTEXES_RECURSIVE
ctp->p_prio = newprio;
/* Awakens the highest priority thread waiting for the unlocked mutex and
assigns the mutex to it.*/
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 #endif
mp->m_owner = NULL;
chSysUnlock(); chSysUnlock();
} }
@ -346,46 +397,61 @@ void chMtxUnlockS(mutex_t *mp) {
thread_t *ctp = currp; thread_t *ctp = currp;
mutex_t *lmp; mutex_t *lmp;
chDbgCheckClassS();
chDbgCheck(mp != NULL);
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");
#if CH_CFG_USE_MUTEXES_RECURSIVE
chDbgAssert(mp->m_cnt >= 1, "counter is not positive");
/* Removes the top mutex from the thread's owned mutexes list and marks if (--mp->m_cnt == 0) {
it as not owned. Note, it is assumed to be the same mutex passed as #endif
parameter of this function.*/
ctp->p_mtxlist = mp->m_next;
/* If a thread is waiting on the mutex then the fun part begins.*/ chDbgAssert(ctp->p_mtxlist == mp, "not next in list");
if (chMtxQueueNotEmptyS(mp)) {
thread_t *tp;
/* Recalculates the optimal thread priority by scanning the owned /* Removes the top mutex from the thread's owned mutexes list and marks
mutexes list.*/ it as not owned. Note, it is assumed to be the same mutex passed as
tprio_t newprio = ctp->p_realprio; parameter of this function.*/
lmp = ctp->p_mtxlist; ctp->p_mtxlist = mp->m_next;
while (lmp != NULL) {
/* If the highest priority thread waiting in the mutexes list has a /* If a thread is waiting on the mutex then the fun part begins.*/
greater priority than the current thread base priority then the final if (chMtxQueueNotEmptyS(mp)) {
priority will have at least that priority.*/ thread_t *tp;
if (chMtxQueueNotEmptyS(lmp) && (lmp->m_queue.p_next->p_prio > newprio))
newprio = lmp->m_queue.p_next->p_prio; /* Recalculates the optimal thread priority by scanning the owned
lmp = lmp->m_next; mutexes list.*/
tprio_t newprio = ctp->p_realprio;
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(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.*/
#if CH_CFG_USE_MUTEXES_RECURSIVE
mp->m_cnt = 1;
#endif
tp = queue_fifo_remove(&mp->m_queue);
mp->m_owner = tp;
mp->m_next = tp->p_mtxlist;
tp->p_mtxlist = mp;
chSchReadyI(tp);
} }
else
/* Assigns to the current thread the highest priority among all the mp->m_owner = NULL;
waiting threads.*/ #if CH_CFG_USE_MUTEXES_RECURSIVE
ctp->p_prio = newprio;
/* Awakens the highest priority thread waiting for the unlocked mutex and
assigns the mutex to it.*/
tp = queue_fifo_remove(&mp->m_queue);
mp->m_owner = tp;
mp->m_next = tp->p_mtxlist;
tp->p_mtxlist = mp;
chSchReadyI(tp);
} }
else #endif
mp->m_owner = NULL;
} }
/** /**
@ -405,17 +471,24 @@ void chMtxUnlockAll(void) {
chSysLock(); chSysLock();
if (ctp->p_mtxlist != NULL) { if (ctp->p_mtxlist != NULL) {
do { do {
mutex_t *ump = ctp->p_mtxlist; mutex_t *mp = ctp->p_mtxlist;
ctp->p_mtxlist = ump->m_next; ctp->p_mtxlist = mp->m_next;
if (chMtxQueueNotEmptyS(ump)) { if (chMtxQueueNotEmptyS(mp)) {
thread_t *tp = queue_fifo_remove(&ump->m_queue); #if CH_CFG_USE_MUTEXES_RECURSIVE
ump->m_owner = tp; mp->m_cnt = 1;
ump->m_next = tp->p_mtxlist; #endif
tp->p_mtxlist = ump; thread_t *tp = queue_fifo_remove(&mp->m_queue);
mp->m_owner = tp;
mp->m_next = tp->p_mtxlist;
tp->p_mtxlist = mp;
chSchReadyI(tp); chSchReadyI(tp);
} }
else else {
ump->m_owner = NULL; #if CH_CFG_USE_MUTEXES_RECURSIVE
mp->m_cnt = 0;
#endif
mp->m_owner = NULL;
}
} while (ctp->p_mtxlist != NULL); } while (ctp->p_mtxlist != NULL);
ctp->p_prio = ctp->p_realprio; ctp->p_prio = ctp->p_realprio;
chSchRescheduleS(); chSchRescheduleS();

View File

@ -432,10 +432,10 @@ static void mtx5_setup(void) {
} }
static void mtx5_execute(void) { static void mtx5_execute(void) {
bool b;
tprio_t prio;
prio = chThdGetPriorityX(); #if !CH_CFG_USE_MUTEXES_RECURSIVE
bool b;
tprio_t prio = chThdGetPriorityX();
b = chMtxTryLock(&m1); b = chMtxTryLock(&m1);
test_assert(1, b, "already locked"); test_assert(1, b, "already locked");
@ -450,6 +450,7 @@ static void mtx5_execute(void) {
test_assert(3, queue_isempty(&m1.m_queue), "queue not empty"); test_assert(3, queue_isempty(&m1.m_queue), "queue not empty");
test_assert(4, m1.m_owner == NULL, "still owned"); test_assert(4, m1.m_owner == NULL, "still owned");
test_assert(5, chThdGetPriorityX() == prio, "wrong priority level"); test_assert(5, chThdGetPriorityX() == prio, "wrong priority level");
#endif /* !CH_CFG_USE_MUTEXES_RECURSIVE */
chMtxLock(&m1); chMtxLock(&m1);
chMtxUnlockAll(); chMtxUnlockAll();