QP/C  7.3.3
Real-Time Embedded Framework
Loading...
Searching...
No Matches
qxk_mutex.c
Go to the documentation of this file.
1//$file${src::qxk::qxk_mutex.c} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2//
3// Model: qpc.qm
4// File: ${src::qxk::qxk_mutex.c}
5//
6// This code has been generated by QM 6.1.1 <www.state-machine.com/qm>.
7// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
8//
9// This code is covered by the following QP license:
10// License # : LicenseRef-QL-dual
11// Issued to : Any user of the QP/C real-time embedded framework
12// Framework(s) : qpc
13// Support ends : 2024-12-31
14// License scope:
15//
16// Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>.
17//
18// Q u a n t u m L e a P s
19// ------------------------
20// Modern Embedded Software
21//
22// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
23//
24// This software is dual-licensed under the terms of the open source GNU
25// General Public License version 3 (or any later version), or alternatively,
26// under the terms of one of the closed source Quantum Leaps commercial
27// licenses.
28//
29// The terms of the open source GNU General Public License version 3
30// can be found at: <www.gnu.org/licenses/gpl-3.0>
31//
32// The terms of the closed source Quantum Leaps commercial licenses
33// can be found at: <www.state-machine.com/licensing>
34//
35// Redistributions in source code must retain this top-level comment block.
36// Plagiarizing this software to sidestep the license obligations is illegal.
37//
38// Contact information:
39// <www.state-machine.com/licensing>
40// <info@state-machine.com>
41//
42//$endhead${src::qxk::qxk_mutex.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
43#define QP_IMPL // this is QP implementation
44#include "qp_port.h" // QP port
45#include "qp_pkg.h" // QP package-scope interface
46#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
47#ifdef Q_SPY // QS software tracing enabled?
48 #include "qs_port.h" // QS port
49 #include "qs_pkg.h" // QS facilities for pre-defined trace records
50#else
51 #include "qs_dummy.h" // disable the QS software tracing
52#endif // Q_SPY
53
54// protection against including this source file in a wrong project
55#ifndef QXK_H_
56 #error "Source file included in a project NOT based on the QXK kernel"
57#endif // QXK_H_
58
59Q_DEFINE_THIS_MODULE("qxk_mutex")
60
61//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
62// Check for the minimum required QP version
63#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U))
64#error qpc version 7.3.0 or higher required
65#endif
66//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
67
68//$define${QXK::QXMutex} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
69
70//${QXK::QXMutex} ............................................................
71
72//${QXK::QXMutex::init} ......................................................
73//! @public @memberof QXMutex
74void QXMutex_init(QXMutex * const me,
75 QPrioSpec const prioSpec)
76{
79 QF_MEM_SYS();
80
81 Q_REQUIRE_INCRIT(100, (prioSpec & 0xFF00U) == 0U);
82
83 me->ao.prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio.
84 me->ao.pthre = 0U; // preemption-threshold (not used)
85 QActive * const ao = &me->ao;
86
87 QF_MEM_APP();
89
90 QActive_register_(ao); // register this mutex as AO
91}
92
93//${QXK::QXMutex::lock} ......................................................
94//! @public @memberof QXMutex
95bool QXMutex_lock(QXMutex * const me,
96 QTimeEvtCtr const nTicks)
97{
100 QF_MEM_SYS();
101
102 QXThread * const curr = QXK_PTR_CAST_(QXThread*, QXK_priv_.curr);
103
104 // precondition, this mutex operation must:
105 // - NOT be called from an ISR;
106 // - be called from an eXtended thread;
107 // - the mutex-prio. must be in range;
108 // - the thread must NOT be already blocked on any object.
110 && (curr != (QXThread *)0)
111 && (me->ao.prio <= QF_MAX_ACTIVE)
112 && (curr->super.super.temp.obj == (QMState *)0));
113 // also: the thread must NOT be holding a scheduler lock.
115 QXK_priv_.lockHolder != (uint_fast8_t)curr->super.prio);
116
117 // is the mutex available?
118 bool locked = true; // assume that the mutex will be locked
119 if (me->ao.eQueue.nFree == 0U) {
120 me->ao.eQueue.nFree = 1U; // mutex lock nesting
121
122 // also: the newly locked mutex must have no holder yet
123 Q_REQUIRE_INCRIT(203, me->ao.osObject == (void *)0);
124
125 // set the new mutex holder to the curr thread and
126 // save the thread's prio in the mutex
127 // NOTE: reuse the otherwise unused eQueue data member.
128 me->ao.osObject = curr;
129 me->ao.eQueue.head = (QEQueueCtr)curr->super.prio;
130
131 QS_BEGIN_PRE_(QS_MTX_LOCK, curr->super.prio)
132 QS_TIME_PRE_(); // timestamp
133 QS_OBJ_PRE_(me); // this mutex
134 QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio
135 QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting
136 QS_END_PRE_()
137
138 if (me->ao.prio != 0U) { // prio.-ceiling protocol used?
139 // the holder prio. must be lower than that of the mutex
140 // and the prio. slot must be occupied by this mutex
141 Q_ASSERT_INCRIT(210, (curr->super.prio < me->ao.prio)
142 && (QActive_registry_[me->ao.prio] == &me->ao));
143
144 // remove the thread's original prio from the ready set
145 // and insert the mutex's prio into the ready set
146 QPSet_remove(&QXK_priv_.readySet,
147 (uint_fast8_t)me->ao.eQueue.head);
148 QPSet_insert(&QXK_priv_.readySet,
149 (uint_fast8_t)me->ao.prio);
150 #ifndef Q_UNSAFE
151 QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis);
152 #endif
153 // put the thread into the AO registry in place of the mutex
154 QActive_registry_[me->ao.prio] = &curr->super;
155
156 // set thread's prio to that of the mutex
157 curr->super.prio = me->ao.prio;
158 #ifndef Q_UNSAFE
159 curr->super.prio_dis = (uint8_t)(~curr->super.prio);
160 #endif
161 }
162 }
163 // is the mutex locked by this thread already (nested locking)?
164 else if (me->ao.osObject == &curr->super) {
165
166 // the nesting level beyond the arbitrary but high limit
167 // most likely means cyclic or recursive locking of a mutex.
168 Q_ASSERT_INCRIT(220, me->ao.eQueue.nFree < 0xFFU);
169
170 ++me->ao.eQueue.nFree; // lock one more level
171
172 QS_BEGIN_PRE_(QS_MTX_LOCK, curr->super.prio)
173 QS_TIME_PRE_(); // timestamp
174 QS_OBJ_PRE_(me); // this mutex
175 QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio
176 QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting
177 QS_END_PRE_()
178 }
179 else { // the mutex is already locked by a different thread
180 // the mutex holder must be valid
181 Q_ASSERT_INCRIT(230, me->ao.osObject != (void *)0);
182
183 if (me->ao.prio != 0U) { // prio.-ceiling protocol used?
184 // the prio slot must be occupied by the thr. holding the mutex
186 == QACTIVE_CAST_(me->ao.osObject));
187 }
188
189 // remove the curr thread's prio from the ready set (will block)
190 // and insert it to the waiting set on this mutex
191 uint_fast8_t const p = (uint_fast8_t)curr->super.prio;
192 QPSet_remove(&QXK_priv_.readySet, p);
193 #ifndef Q_UNSAFE
194 QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis);
195 #endif
196 QPSet_insert(&me->waitSet, p);
197
198 // set the blocking object (this mutex)
199 curr->super.super.temp.obj = QXK_PTR_CAST_(QMState*, me);
200 QXThread_teArm_(curr, (enum_t)QXK_TIMEOUT_SIG, nTicks);
201
202 QS_BEGIN_PRE_(QS_MTX_BLOCK, curr->super.prio)
203 QS_TIME_PRE_(); // timestamp
204 QS_OBJ_PRE_(me); // this mutex
205 QS_2U8_PRE_((uint8_t)me->ao.eQueue.head, // holder prio
206 curr->super.prio); // blocked thread prio
207 QS_END_PRE_()
208
209 // schedule the next thread if multitasking started
210 (void)QXK_sched_(); // schedule other threads
211
212 QF_MEM_APP();
213 QF_CRIT_EXIT();
214 QF_CRIT_EXIT_NOP(); // BLOCK here !!!
215
216 // AFTER unblocking...
218 QF_MEM_SYS();
219 // the blocking object must be this mutex
221 == QXK_PTR_CAST_(QMState*, me));
222
223 // did the blocking time-out? (signal of zero means that it did)
224 if (curr->timeEvt.super.sig == 0U) {
225 if (QPSet_hasElement(&me->waitSet, p)) { // still waiting?
226 QPSet_remove(&me->waitSet, p); // remove unblocked thread
227 locked = false; // the mutex was NOT locked
228 }
229 }
230 else { // blocking did NOT time out
231 // the thread must NOT be waiting on this mutex
232 Q_ASSERT_INCRIT(260, !QPSet_hasElement(&me->waitSet, p));
233 }
234 curr->super.super.temp.obj = (QMState *)0; // clear blocking obj.
235 }
236 QF_MEM_APP();
237 QF_CRIT_EXIT();
238
239 return locked;
240}
241
242//${QXK::QXMutex::tryLock} ...................................................
243//! @public @memberof QXMutex
244bool QXMutex_tryLock(QXMutex * const me) {
247 QF_MEM_SYS();
248
249 QActive *curr = QXK_priv_.curr;
250 if (curr == (QActive *)0) { // called from a basic thread?
251 curr = QActive_registry_[QXK_priv_.actPrio];
252 }
253
254 // precondition, this mutex must:
255 // - NOT be called from an ISR;
256 // - the calling thread must be valid;
257 // - the mutex-prio. must be in range
259 && (curr != (QActive *)0)
260 && (me->ao.prio <= QF_MAX_ACTIVE));
261 // also: the thread must NOT be holding a scheduler lock.
263 QXK_priv_.lockHolder != (uint_fast8_t)curr->prio);
264
265 // is the mutex available?
266 if (me->ao.eQueue.nFree == 0U) {
267 me->ao.eQueue.nFree = 1U; // mutex lock nesting
268
269 // also the newly locked mutex must have no holder yet
270 Q_REQUIRE_INCRIT(303, me->ao.osObject == (void *)0);
271
272 // set the new mutex holder to the curr thread and
273 // save the thread's prio in the mutex
274 // NOTE: reuse the otherwise unused eQueue data member.
275 me->ao.osObject = curr;
276 me->ao.eQueue.head = (QEQueueCtr)curr->prio;
277
278 QS_BEGIN_PRE_(QS_MTX_LOCK, curr->prio)
279 QS_TIME_PRE_(); // timestamp
280 QS_OBJ_PRE_(me); // this mutex
281 QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio
282 QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting
283 QS_END_PRE_()
284
285 if (me->ao.prio != 0U) { // prio.-ceiling protocol used?
286 // the holder prio. must be lower than that of the mutex
287 // and the prio. slot must be occupied by this mutex
288 Q_ASSERT_INCRIT(310, (curr->prio < me->ao.prio)
289 && (QActive_registry_[me->ao.prio] == &me->ao));
290
291 // remove the thread's original prio from the ready set
292 // and insert the mutex's prio into the ready set
293 QPSet_remove(&QXK_priv_.readySet,
294 (uint_fast8_t)me->ao.eQueue.head);
295 QPSet_insert(&QXK_priv_.readySet,
296 (uint_fast8_t)me->ao.prio);
297 #ifndef Q_UNSAFE
298 QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis);
299 #endif
300 // put the thread into the AO registry in place of the mutex
301 QActive_registry_[me->ao.prio] = curr;
302
303 // set thread's prio to that of the mutex
304 curr->prio = me->ao.prio;
305 #ifndef Q_UNSAFE
306 curr->prio_dis = (uint8_t)(~curr->prio);
307 #endif
308 }
309 }
310 // is the mutex locked by this thread already (nested locking)?
311 else if (me->ao.osObject == curr) {
312 // the nesting level must not exceed the specified limit
313 Q_ASSERT_INCRIT(320, me->ao.eQueue.nFree < 0xFFU);
314
315 ++me->ao.eQueue.nFree; // lock one more level
316
317 QS_BEGIN_PRE_(QS_MTX_LOCK, curr->prio)
318 QS_TIME_PRE_(); // timestamp
319 QS_OBJ_PRE_(me); // this mutex
320 QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio
321 QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting
322 QS_END_PRE_()
323 }
324 else { // the mutex is already locked by a different thread
325 if (me->ao.prio != 0U) { // prio.-ceiling protocol used?
326 // the prio slot must be occupied by the thr. holding the mutex
328 == QACTIVE_CAST_(me->ao.osObject));
329 }
330
331 QS_BEGIN_PRE_(QS_MTX_BLOCK_ATTEMPT, curr->prio)
332 QS_TIME_PRE_(); // timestamp
333 QS_OBJ_PRE_(me); // this mutex
334 QS_2U8_PRE_((uint8_t)me->ao.eQueue.head, // holder prio
335 curr->prio); // trying thread prio
336 QS_END_PRE_()
337
338 curr = (QActive *)0; // means that mutex is NOT available
339 }
340 QF_MEM_APP();
341 QF_CRIT_EXIT();
342
343 return curr != (QActive *)0;
344}
345
346//${QXK::QXMutex::unlock} ....................................................
347//! @public @memberof QXMutex
348void QXMutex_unlock(QXMutex * const me) {
351 QF_MEM_SYS();
352
353 QActive *curr = QXK_priv_.curr;
354 if (curr == (QActive *)0) { // called from a basic thread?
355 curr = QActive_registry_[QXK_priv_.actPrio];
356 }
357
359 && (curr != (QActive *)0));
360 Q_REQUIRE_INCRIT(401, me->ao.eQueue.nFree > 0U);
361 Q_REQUIRE_INCRIT(403, me->ao.osObject == curr);
362
363 // is this the last nesting level?
364 if (me->ao.eQueue.nFree == 1U) {
365
366 if (me->ao.prio != 0U) { // prio.-ceiling protocol used?
367 // prio. must be in range
369
370 // restore the holding thread's prio from the mutex
371 curr->prio = (uint8_t)me->ao.eQueue.head;
372 #ifndef Q_UNSAFE
373 curr->prio_dis = (uint8_t)(~curr->prio);
374 #endif
375
376 // put the mutex back into the AO registry
377 QActive_registry_[me->ao.prio] = &me->ao;
378
379 // remove the mutex' prio from the ready set
380 // and insert the original thread's prio.
381 QPSet_remove(&QXK_priv_.readySet,
382 (uint_fast8_t)me->ao.prio);
383 QPSet_insert(&QXK_priv_.readySet,
384 (uint_fast8_t)me->ao.eQueue.head);
385 #ifndef Q_UNSAFE
386 QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis);
387 #endif
388 }
389
390 QS_BEGIN_PRE_(QS_MTX_UNLOCK, curr->prio)
391 QS_TIME_PRE_(); // timestamp
392 QS_OBJ_PRE_(me); // this mutex
393 QS_2U8_PRE_((uint8_t)me->ao.eQueue.head, // holder prio
394 0U); // nesting
395 QS_END_PRE_()
396
397 // are any other threads waiting on this mutex?
398 if (QPSet_notEmpty(&me->waitSet)) {
399 // find the highest-prio. thread waiting on this mutex
400 uint_fast8_t const p = QPSet_findMax(&me->waitSet);
401
402 // remove this thread from waiting on the mutex
403 // and insert it into the ready set.
404 QPSet_remove(&me->waitSet, p);
405 QPSet_insert(&QXK_priv_.readySet, p);
406 #ifndef Q_UNSAFE
407 QPSet_update_(&QXK_priv_.readySet, &QXK_priv_.readySet_dis);
408 #endif
409
410 QXThread * const thr =
412
413 // the waiting thread must:
414 // - be registered in QF
415 // - have the prio. corresponding to the registration
416 // - be an extended thread
417 // - be blocked on this mutex
418 Q_ASSERT_INCRIT(420, (thr != (QXThread *)0)
419 && (thr->super.prio == (uint8_t)p)
420 && (thr->super.super.state.act == Q_ACTION_CAST(0))
421 && (thr->super.super.temp.obj
422 == QXK_PTR_CAST_(QMState*, me)));
423
424 // disarm the internal time event
425 (void)QXThread_teDisarm_(thr);
426
427 // set the new mutex holder to the curr thread and
428 // save the thread's prio in the mutex
429 // NOTE: reuse the otherwise unused eQueue data member.
430 me->ao.osObject = thr;
431 me->ao.eQueue.head = (QEQueueCtr)thr->super.prio;
432
433 QS_BEGIN_PRE_(QS_MTX_LOCK, thr->super.prio)
434 QS_TIME_PRE_(); // timestamp
435 QS_OBJ_PRE_(me); // this mutex
436 QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio
437 QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting
438 QS_END_PRE_()
439
440 if (me->ao.prio != 0U) { // prio.-ceiling protocol used?
441 // the holder prio. must be lower than that of the mutex
443 && (thr->super.prio < me->ao.prio));
444
445 // put the thread into AO registry in place of the mutex
446 QActive_registry_[me->ao.prio] = &thr->super;
447 }
448 }
449 else { // no threads are waiting for this mutex
450 me->ao.eQueue.nFree = 0U; // free up the nesting count
451
452 // the mutex no longer held by any thread
453 me->ao.osObject = (void *)0;
454 me->ao.eQueue.head = 0U;
455 me->ao.eQueue.tail = 0U;
456
457 if (me->ao.prio != 0U) { // prio.-ceiling protocol used?
458 // put the mutex back at the original mutex slot
461 }
462 }
463
464 // schedule the next thread if multitasking started
465 if (QXK_sched_() != 0U) { // activation needed?
466 QXK_activate_(); // synchronously activate basic-thread(s)
467 }
468 }
469 else { // releasing one level of nested mutex lock
470 --me->ao.eQueue.nFree; // unlock one level
471
472 QS_BEGIN_PRE_(QS_MTX_UNLOCK_ATTEMPT, curr->prio)
473 QS_TIME_PRE_(); // timestamp
474 QS_OBJ_PRE_(me); // this mutex
475 QS_U8_PRE_((uint8_t)me->ao.eQueue.head); // holder prio
476 QS_U8_PRE_((uint8_t)me->ao.eQueue.nFree); // nesting
477 QS_END_PRE_()
478 }
479 QF_MEM_APP();
480 QF_CRIT_EXIT();
481}
482//$enddef${QXK::QXMutex} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
uint16_t QEQueueCtr
Definition qequeue.h:53
QActive * QActive_registry_[QF_MAX_ACTIVE+1U]
Definition qf_act.c:64
#define QF_MEM_APP()
Definition qp.h:1284
#define Q_ACTION_CAST(action_)
Definition qp.h:529
uint32_t QTimeEvtCtr
Definition qp.h:633
#define QF_MEM_SYS()
Definition qp.h:1279
int enum_t
Definition qp.h:109
#define QF_CRIT_EXIT_NOP()
Definition qp.h:1265
uint16_t QPrioSpec
Definition qp.h:619
#define QF_MAX_ACTIVE
Definition qp_config.h:112
Internal (package scope) QP/C interface.
#define QACTIVE_CAST_(ptr_)
Definition qp_pkg.h:96
Sample QP/C port.
#define QXK_ISR_CONTEXT_()
Check if the code executes in the ISR context.
Definition qp_port.h:131
#define QS_TIME_PRE_()
Definition qs.h:450
@ QS_MTX_UNLOCK
a mutex was unlocked
Definition qs.h:190
@ QS_MTX_BLOCK
a mutex blocked a thread
Definition qs.h:189
@ QS_MTX_UNLOCK_ATTEMPT
a mutex unlock was attempted
Definition qs.h:193
@ QS_MTX_LOCK
a mutex was locked
Definition qs.h:188
@ QS_MTX_BLOCK_ATTEMPT
a mutex blocking was attempted
Definition qs.h:192
QS/C package-scope interface.
Sample QS/C port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:58
#define Q_ASSERT_INCRIT(id_, expr_)
Definition qsafe.h:72
#define QF_CRIT_EXIT()
Definition qsafe.h:62
#define Q_REQUIRE_INCRIT(id_, expr_)
Definition qsafe.h:136
#define QF_CRIT_STAT
Definition qsafe.h:54
#define QXK_PTR_CAST_(type_, ptr_)
Definition qxk.h:363
@ QXK_TIMEOUT_SIG
Definition qxk.h:392
Active object class (based on the QHsm implementation strategy)
Definition qp.h:800
uint8_t prio
Definition qp.h:805
uint8_t pthre
Definition qp.h:808
QACTIVE_OS_OBJ_TYPE osObject
Definition qp.h:817
uint8_t prio_dis
Definition qp.h:827
QACTIVE_EQUEUE_TYPE eQueue
Definition qp.h:822
QAsm super
Definition qp.h:802
union QAsmAttr temp
Definition qp.h:289
union QAsmAttr state
Definition qp.h:286
QSignal sig
Definition qp.h:151
State object for the QMsm class (QM State Machine).
Definition qp.h:239
QEvt super
Definition qp.h:971
Blocking Mutex of the QXK preemptive kernel.
Definition qxk.h:243
QActive ao
Definition qxk.h:247
QPSet waitSet
Definition qxk.h:250
eXtended (blocking) thread of the QXK preemptive kernel
Definition qxk.h:145
QActive super
Definition qxk.h:147
QTimeEvt timeEvt
Definition qxk.h:152
struct QMState const * obj
Definition qp.h:269
QActionHandler act
Definition qp.h:266