QP/C  6.1.1
qxk_mutex.c
Go to the documentation of this file.
1 
41 #define QP_IMPL /* this is QP implementation */
42 #include "qf_port.h" /* QF port */
43 #include "qxk_pkg.h" /* QXK package-scope interface */
44 #include "qassert.h" /* QP embedded systems-friendly assertions */
45 #ifdef Q_SPY /* QS software tracing enabled? */
46  #include "qs_port.h" /* include QS port */
47 #else
48  #include "qs_dummy.h" /* disable the QS software tracing */
49 #endif /* Q_SPY */
50 
51 /* protection against including this source file in a wrong project */
52 #ifndef qxk_h
53  #error "Source file included in a project NOT based on the QXK kernel"
54 #endif /* qxk_h */
55 
56 Q_DEFINE_THIS_MODULE("qxk_mutex")
57 
58 /****************************************************************************/
85 void QXMutex_init(QXMutex * const me, uint_fast8_t ceiling) {
87 
94  Q_REQUIRE_ID(100,
95  (ceiling <= (uint_fast8_t)QF_MAX_ACTIVE)
96  && ((ceiling == (uint_fast8_t)0)
97  || (QF_active_[ceiling] == (QActive *)0)));
98 
99  me->ceiling = (uint8_t)ceiling;
100  me->lockNest = (uint8_t)0;
101  QF_bzero(&me->waitSet, (uint_fast16_t)sizeof(me->waitSet));
102 
103  if (ceiling != (uint_fast8_t)0) {
104  /* reserve the ceiling priority level for this mutex */
105  QF_active_[ceiling] = (QActive *)me;
106  }
107  QF_CRIT_EXIT_();
108 }
109 
110 /****************************************************************************/
131 bool QXMutex_lock(QXMutex * const me,
132  uint_fast16_t const nTicks)
133 {
134  QXThread *curr;
136 
137  QF_CRIT_ENTRY_();
138  curr = (QXThread *)QXK_attr_.curr;
139 
148  Q_REQUIRE_ID(200, (!QXK_ISR_CONTEXT_()) /* don't call from an ISR! */
149  && (curr != (QXThread *)0) /* current thread must be extended */
150  && ((me->ceiling == (uint8_t)0) /* below ceiling */
151  || (curr->super.startPrio < me->ceiling))
152  && (QXK_attr_.lockHolder != curr->super.prio) /* not holding a lock */
153  && (curr->super.super.temp.obj == (QMState *)0)); /* not blocked */
154 
155  /* is the mutex available? */
156  if (me->lockNest == (uint8_t)0) {
157  me->lockNest = (uint8_t)1;
158 
159  if (me->ceiling != (uint8_t)0) {
160  /* the priority slot must be set to this mutex */
161  Q_ASSERT_ID(210, QF_active_[me->ceiling] == (QActive *)me);
162 
163  /* switch the priority of this thread to the mutex ceiling */
164  curr->super.prio = me->ceiling;
165  QF_active_[me->ceiling] = &curr->super;
166 
168  (uint_fast8_t)curr->super.startPrio);
170  }
171  me->holderPrio = (uint8_t)curr->super.startPrio;
172 
173  QS_BEGIN_NOCRIT_(QS_MUTEX_LOCK, (void *)0, (void *)0)
174  QS_TIME_(); /* timestamp */
175  QS_2U8_(curr->super.startPrio, /* the start prio */
176  me->ceiling); /* current ceiling */
178  }
179  /* is the mutex locked by this thread already (nested locking)? */
180  else if (me->holderPrio == curr->super.startPrio) {
181 
182  /* the nesting level must not exceed 0xFF */
183  Q_ASSERT_ID(220, me->lockNest < (uint8_t)0xFF);
184 
185  ++me->lockNest;
186  }
187  else { /* the mutex is alredy locked by a different thread */
188 
189  /* the ceiling holder priority must be valid */
190  Q_ASSERT_ID(230, me->holderPrio != (uint8_t)0);
191 
192  if (me->ceiling != (uint8_t)0) {
193  /* the prio slot must be claimed by the thr. holding the mutex */
194  Q_ASSERT_ID(240, QF_active_[me->ceiling] != (QActive *)0);
195  }
196  /* remove this curr prio from the ready set (block)
197  * and insert to the waiting set on this mutex */
199  QPSet_insert(&me->waitSet, (uint_fast8_t)curr->super.prio);
200 
201  /* store the blocking object (this mutex) */
202  curr->super.super.temp.obj = (QMState *)me;
203  QXThread_teArm_(curr, (QSignal)QXK_SEMA_SIG, nTicks);
204 
205  /* schedule the next thread if multitasking started */
206  (void)QXK_sched_();
207  QF_CRIT_EXIT_();
208  QF_CRIT_EXIT_NOP(); /* BLOCK here */
209 
210  QF_CRIT_ENTRY_();
211  /* the blocking object must be this mutex */
212  Q_ASSERT_ID(240, curr->super.super.temp.obj == (QMState *)me);
213  curr->super.super.temp.obj = (QMState const *)0; /* clear */
214  }
215  QF_CRIT_EXIT_();
216 
217  /* signal of non-zero means that the time event has not expired */
218  return (bool)(curr->timeEvt.super.sig != (QSignal)0);
219 }
220 
221 /****************************************************************************/
242 bool QXMutex_tryLock(QXMutex * const me) {
243  QActive *curr;
245 
246  QF_CRIT_ENTRY_();
247  curr = QXK_attr_.curr;
248  if (curr == (QActive *)0) { /* called from a basic thread? */
249  curr = QF_active_[QXK_attr_.actPrio];
250  }
251 
260  Q_REQUIRE_ID(300, (!QXK_ISR_CONTEXT_()) /* don't call from an ISR! */
262  && (curr != (QActive *)0) /* current thread must be valid */
263  && ((me->ceiling == (uint8_t)0) /* below ceiling */
264  || (curr->startPrio < me->ceiling))
265  && (curr->prio != QXK_attr_.lockHolder)); /* not holding a lock */
266 
267  /* is the mutex available? */
268  if (me->lockNest == (uint8_t)0) {
269  me->lockNest = (uint8_t)1;
270 
271  if (me->ceiling != (uint8_t)0) {
272  /* the priority slot must be set to this mutex */
273  Q_ASSERT_ID(310, QF_active_[me->ceiling] == (QActive *)me);
274 
275  /* switch the priority of this thread to the mutex ceiling */
276  curr->prio = me->ceiling;
277  QF_active_[me->ceiling] = curr;
278 
279  QPSet_remove(&QXK_attr_.readySet, (uint_fast8_t)curr->startPrio);
281  }
282 
283  /* make curr thread the new mutex holder */
284  me->holderPrio = curr->startPrio;
285 
286  QS_BEGIN_NOCRIT_(QS_MUTEX_LOCK, (void *)0, curr)
287  QS_TIME_(); /* timestamp */
288  QS_2U8_(curr->startPrio, /* the start prio */
289  me->ceiling); /* the current ceiling */
291  }
292  /* is the mutex held by this thread already (nested locking)? */
293  else if (me->holderPrio == curr->startPrio) {
294  /* the nesting level must not exceed 0xFF */
295  Q_ASSERT_ID(320, me->lockNest < (uint8_t)0xFF);
296 
297  ++me->lockNest;
298  }
299  else { /* the mutex is alredy locked by a different thread */
300  if (me->ceiling != (uint8_t)0) {
301  /* the prio slot must be claimed by the mutex holder */
302  Q_ASSERT_ID(330,
303  QF_active_[me->ceiling] == QF_active_[me->holderPrio]);
304  }
305  curr = (QActive *)0; /* means that mutex is NOT available */
306  }
307  QF_CRIT_EXIT_();
308 
309  return (curr != (QActive *)0);
310 }
311 
312 /****************************************************************************/
332 void QXMutex_unlock(QXMutex * const me) {
333  QActive *curr;
335 
336  QF_CRIT_ENTRY_();
337  curr = QXK_attr_.curr;
338  if (curr == (QActive *)0) { /* called from a basic thread? */
339  curr = QF_active_[QXK_attr_.actPrio];
340  }
341 
350  Q_REQUIRE_ID(400, (!QXK_ISR_CONTEXT_()) /* don't call from an ISR! */
351  && (curr != (QActive *)0) /* current thread must be valid */
352  && (curr->startPrio == me->holderPrio)
353  && ((me->ceiling == (uint8_t)0) /* curr at ceiling prio */
354  || (curr->prio == me->ceiling))
355  && (me->lockNest > (uint8_t)0)); /* locked at least once */
356 
357  /* is this the last nesting level? */
358  if (me->lockNest == (uint8_t)1) {
359 
360  if (me->ceiling != (uint8_t)0) {
361  /* restore the holding thread's priority to the original */
362  curr->prio = curr->startPrio;
363 
364  /* remove the boosted priority and insert the original priority */
366  QPSet_insert(&QXK_attr_.readySet, (uint_fast8_t)curr->startPrio);
367  }
368 
369  /* the mutex no longer held by a thread */
370  me->holderPrio = (uint8_t)0;
371 
372  QS_BEGIN_NOCRIT_(QS_MUTEX_UNLOCK, (void *)0, (void *)0)
373  QS_TIME_(); /* timestamp */
374  QS_2U8_((uint8_t)curr->startPrio, /* the start priority */
375  me->ceiling); /* the mutex ceiling */
377 
378  /* are any other threads waiting for this mutex? */
379  if (QPSet_notEmpty(&me->waitSet)) {
380  uint_fast8_t p;
381  QXThread *thr;
382 
383  /* find the highest-priority waiting thread */
384  QPSet_findMax(&me->waitSet, p);
385  thr = (QXThread *)QF_active_[p];
386 
387  /* the waiting thread must:
388  * (1) the ceiling must not be used; or if used
389  * the thread must have priority below the ceiling
390  * (2) be extended
391  * (3) not be redy to run
392  * (4) have still the start priority
393  * (5) be blocked on this mutex
394  */
395  Q_ASSERT_ID(410,
396  ((me->ceiling == (uint8_t)0) /* below ceiling */
397  || (p < (uint_fast8_t)me->ceiling))
398  && (thr != (QXThread *)0) /* extended thread */
400  && (thr->super.prio == thr->super.startPrio)
401  && (thr->super.super.temp.obj == (QMState *)me));
402 
403  /* disarm the internal time event */
404  (void)QXThread_teDisarm_(thr);
405 
406  /* this thread is no longer waiting for the mutex */
407  QPSet_remove(&me->waitSet, p);
408 
409  if (me->ceiling != (uint8_t)0) {
410  /* switch the priority of this thread to the mutex ceiling */
411  thr->super.prio = me->ceiling;
412  QF_active_[me->ceiling] = &thr->super;
413  }
414 
415  /* make thr the new mutex holder */
416  me->holderPrio = (uint8_t)thr->super.startPrio;
417  /* make the thread ready to run (at the ceiling prio) */
419 
420  QS_BEGIN_NOCRIT_(QS_MUTEX_LOCK, (void *)0, thr)
421  QS_TIME_(); /* timestamp */
422  QS_2U8_(thr->super.startPrio, /* start priority */
423  me->ceiling); /* ceiling priority */
425  }
426  else { /* no threads are waiting for this mutex */
427  me->lockNest = (uint8_t)0;
428 
429  if (me->ceiling != (uint8_t)0) {
430  /* put the mutex at the priority ceiling slot */
431  QF_active_[me->ceiling] = (QActive *)me;
432  }
433  }
434 
435  /* schedule the next thread if multitasking started */
436  if (QXK_sched_() != (uint_fast8_t)0) {
437  QXK_activate_(); /* activate a basic thread */
438  }
439  }
440  else { /* releasing the */
441  --me->lockNest; /* release one level */
442  }
443  QF_CRIT_EXIT_();
444 }
#define QS_2U8_(data1_, data2_)
Internal QS macro to output 2 unformatted uint8_t data elements.
Definition: qs.h:708
QXK_Attr QXK_attr_
global attributes of the QXK kernel
Definition: qxk.c:58
#define QF_CRIT_EXIT_NOP()
No-operation for exiting a critical section.
Definition: qf.h:810
uint8_t prio
QF priority (1..QF_MAX_ACTIVE) of this active object.
Definition: qf.h:153
QSignal sig
signal of the event instance
Definition: qep.h:154
Internal (package scope) QXK/C interface.
uint8_t volatile lockHolder
prio of the lock holder
Definition: qxk.h:86
#define QPSet_findMax(me_, n_)
Find the maximum element in the set, and assign it to n_.
Definition: qpset.h:84
#define QF_CRIT_ENTRY_()
This is an internal macro for entering a critical section.
Definition: qf_pkg.h:69
struct QActive *volatile curr
current thread pointer (NULL=basic)
Definition: qxk.h:82
#define QXK_ISR_CONTEXT_()
Internal macro that reports the execution context (ISR vs.
Definition: qxk.h:174
void QXThread_teArm_(QXThread *const me, QSignal sig, uint_fast16_t const nTicks)
internal function to arm the private time event for a given thread.
Definition: qxk_xthr.c:526
void QXMutex_init(QXMutex *const me, uint_fast8_t ceiling)
initialize the QXK priority-ceiling mutex QXMutex
Definition: qxk_mutex.c:85
#define QF_CRIT_STAT_
This is an internal macro for defining the critical section status type.
Definition: qf_pkg.h:57
#define QS_BEGIN_NOCRIT_(rec_, objFilter_, obj_)
Internal macro to begin a QS record without entering critical section.
Definition: qs.h:686
bool QXMutex_tryLock(QXMutex *const me)
try to lock the QXK priority-ceiling mutex QXMutex
Definition: qxk_mutex.c:242
#define Q_DEFINE_THIS_MODULE(name_)
Define the user-specified module name for assertions in this file.
Definition: qassert.h:101
Blocking Mutex the QXK preemptive kernel.
Definition: qxthread.h:248
unsigned char uint8_t
exact-width 8-bit unsigned int
Definition: stdint.h:28
eXtended (blocking) thread of the QXK preemptive kernel
Definition: qxthread.h:71
uint_fast8_t QXK_sched_(void)
QXK scheduler finds the highest-priority thread ready to run.
Definition: qxk.c:371
unsigned int uint_fast8_t
fast at-least 8-bit unsigned int
Definition: stdint.h:35
uint16_t QSignal
QSignal represents the signal of an event.
Definition: qep.h:130
QPSet readySet
ready-set of basic and extended threads
Definition: qxk.h:89
void QF_bzero(void *const start, uint_fast16_t len)
Clear a specified region of memory to zero.
Definition: qf_act.c:159
QHsm super
inherits QHsm
Definition: qf.h:111
#define QPSet_insert(me_, n_)
Insert element n_ into the set me_, n_= 1..32.
Definition: qpset.h:74
State object for the QMsm class (QM State Machine).
Definition: qep.h:402
#define QPSet_notEmpty(me_)
Evaluates to TRUE if the priority set me_ is not empty.
Definition: qpset.h:67
bool QXThread_teDisarm_(QXThread *const me)
internal function to disarm the private time event for a given thread.
Definition: qxk_xthr.c:568
#define Q_ASSERT_ID(id_, test_)
General purpose assertion with user-specified assertion-id.
Definition: qassert.h:136
void QXMutex_unlock(QXMutex *const me)
unlock the QXK priority-ceiling mutex QXMutex
Definition: qxk_mutex.c:332
uint8_t volatile lockPrio
lock prio (0 == no-lock)
Definition: qxk.h:85
unsigned int uint_fast16_t
fast at-least 16-bit unsigned int
Definition: stdint.h:37
a mutex was unlocked
Definition: qs.h:120
#define QF_MAX_ACTIVE
The maximum number of active objects in the application.
Definition: qf_port.h:58
uint8_t volatile actPrio
prio of the active AO
Definition: qxk.h:84
Customizable and memory-efficient assertions for embedded systems.
#define Q_REQUIRE_ID(id_, test_)
Assertion for checking preconditions with user-specified assertion-id.
Definition: qassert.h:254
#define QPSet_hasElement(me_, n_)
Evaluates to TRUE if the priority set me_ has element n_.
Definition: qpset.h:70
QPSet waitSet
set of extended-threads waiting on this mutex
Definition: qxthread.h:249
union QHsmAttr temp
temporary: tran.
Definition: qep.h:284
#define QPSet_remove(me_, n_)
Remove element n_ from the set me_, n_= 1..32.
Definition: qpset.h:78
struct QMState const * obj
pointer to QMState object
Definition: qep.h:252
#define QS_TIME_()
Internal macro to output time stamp to a QS record.
Definition: qs.h:205
void QXK_activate_(void)
QXK activator activates the next active object.
Definition: qxk.c:450
a mutex was locked
Definition: qs.h:119
#define QS_END_NOCRIT_()
Internal QS macro to end a QS record without exiting critical section.
Definition: qs.h:700
Active Object (based on QHsm implementation)
Definition: qf.h:110
QActive * QF_active_[QF_MAX_ACTIVE+1]
array of registered active objects
Definition: qf_act.c:53
bool QXMutex_lock(QXMutex *const me, uint_fast16_t const nTicks)
lock the QXK priority-ceiling mutex QXMutex
Definition: qxk_mutex.c:131
#define QF_CRIT_EXIT_()
This is an internal macro for exiting a critical section.
Definition: qf_pkg.h:81