mirror of
https://github.com/QuantumLeaps/qpc.git
synced 2025-01-28 07:03:10 +08:00
009bdb4175
Fixed bug in QXK activator 7.2.1 7.2.1 7.2.1
1000 lines
36 KiB
C
1000 lines
36 KiB
C
/*$file${include::qxk.h} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
/*
|
|
* Model: qpc.qm
|
|
* File: ${include::qxk.h}
|
|
*
|
|
* This code has been generated by QM 5.2.4 <www.state-machine.com/qm>.
|
|
* DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
|
|
*
|
|
* This code is covered by the following QP license:
|
|
* License # : LicenseRef-QL-dual
|
|
* Issued to : Any user of the QP/C real-time embedded framework
|
|
* Framework(s) : qpc
|
|
* Support ends : 2023-12-31
|
|
* License scope:
|
|
*
|
|
* Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
|
|
*
|
|
* This software is dual-licensed under the terms of the open source GNU
|
|
* General Public License version 3 (or any later version), or alternatively,
|
|
* under the terms of one of the closed source Quantum Leaps commercial
|
|
* licenses.
|
|
*
|
|
* The terms of the open source GNU General Public License version 3
|
|
* can be found at: <www.gnu.org/licenses/gpl-3.0>
|
|
*
|
|
* The terms of the closed source Quantum Leaps commercial licenses
|
|
* can be found at: <www.state-machine.com/licensing>
|
|
*
|
|
* Redistributions in source code must retain this top-level comment block.
|
|
* Plagiarizing this software to sidestep the license obligations is illegal.
|
|
*
|
|
* Contact information:
|
|
* <www.state-machine.com/licensing>
|
|
* <info@state-machine.com>
|
|
*/
|
|
/*$endhead${include::qxk.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
/*! @file
|
|
* @brief QXK/C (preemptive dual-mode kernel) platform-independent
|
|
* public interface.
|
|
*/
|
|
#ifndef QP_INC_QXK_H_
|
|
#define QP_INC_QXK_H_
|
|
|
|
/*==========================================================================*/
|
|
/* QF configuration for QXK -- data members of the QActive class... */
|
|
|
|
/* QXK event-queue used for AOs */
|
|
#define QF_EQUEUE_TYPE QEQueue
|
|
|
|
/* QXK OS-object used to store the private stack pointer for extended threads.
|
|
* (The private stack pointer is NULL for basic-threads).
|
|
*/
|
|
#define QF_OS_OBJECT_TYPE void*
|
|
|
|
/* QXK thread type used to store the private Thread-Local Storage pointer */
|
|
#define QF_THREAD_TYPE void*
|
|
|
|
/*! Access Thread-Local Storage (TLS) and cast it on the given `type_` */
|
|
#define QXK_TLS(type_) ((type_)QXK_current()->thread)
|
|
|
|
/*==========================================================================*/
|
|
#include "qequeue.h" /* QXK kernel uses the native QP event queue */
|
|
#include "qmpool.h" /* QXK kernel uses the native QP memory pool */
|
|
#include "qf.h" /* QF framework integrates directly with QXK */
|
|
|
|
/*==========================================================================*/
|
|
/*$declare${QXK::QXK-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QXK::QXK-base::onIdle} .................................................*/
|
|
/*! QXK idle callback (customized in BSPs for QXK)
|
|
* @static @public @memberof QXK
|
|
*
|
|
* @details
|
|
* QXK_onIdle() is called continuously by the QXK idle thread. This callback
|
|
* gives the application an opportunity to enter a power-saving CPU mode,
|
|
* or perform some other idle processing.
|
|
*
|
|
* @note
|
|
* QXK_onIdle() is invoked with interrupts enabled and must also return with
|
|
* interrupts enabled.
|
|
*/
|
|
void QXK_onIdle(void);
|
|
|
|
/*${QXK::QXK-base::schedLock} ..............................................*/
|
|
/*! QXK Scheduler lock
|
|
* @static @public @memberof QXK
|
|
*
|
|
* @details
|
|
* This function locks the QXK scheduler to the specified ceiling.
|
|
*
|
|
* @param[in] ceiling preemption ceiling to which the QXK scheduler
|
|
* needs to be locked
|
|
*
|
|
* @returns
|
|
* The previous QXK Scheduler lock status, which is to be used to unlock
|
|
* the scheduler by restoring its previous lock status in
|
|
* QXK_schedUnlock().
|
|
*
|
|
* @note
|
|
* A QXK scheduler can be locked from both basic threads (AOs) and
|
|
* extended threads and the scheduler locks can nest.
|
|
*
|
|
* @note
|
|
* QXK_schedLock() must be always followed by the corresponding
|
|
* QXK_schedUnlock().
|
|
*
|
|
* @attention
|
|
* QXK will fire an assertion if a thread holding the lock attempts
|
|
* to block.
|
|
*
|
|
* @sa QXK_schedUnlock()
|
|
*
|
|
* @usage
|
|
* The following example shows how to lock and unlock the QXK scheduler:
|
|
* @include qxk_lock.c
|
|
*/
|
|
QSchedStatus QXK_schedLock(uint_fast8_t const ceiling);
|
|
|
|
/*${QXK::QXK-base::schedUnlock} ............................................*/
|
|
/*! QXK Scheduler unlock
|
|
* @static @public @memberof QXK
|
|
*
|
|
* @details
|
|
* This function unlocks the QXK scheduler to the previous status.
|
|
*
|
|
* @param[in] stat previous QXK Scheduler lock status returned from
|
|
* QXK_schedLock()
|
|
*
|
|
* @note
|
|
* A QXK scheduler can be locked from both basic threads (AOs) and
|
|
* extended threads and the scheduler locks can nest.
|
|
*
|
|
* @note
|
|
* QXK_schedUnlock() must always follow the corresponding QXK_schedLock().
|
|
*
|
|
* @sa QXK_schedLock()
|
|
*
|
|
* @usage
|
|
* The following example shows how to lock and unlock the QXK scheduler:
|
|
* @include qxk_lock.c
|
|
*/
|
|
void QXK_schedUnlock(QSchedStatus const stat);
|
|
|
|
/*${QXK::QXK-base::Timeouts} ...............................................*/
|
|
/*! timeout signals for extended threads */
|
|
enum QXK_Timeouts {
|
|
QXK_DELAY_SIG = 1,
|
|
QXK_TIMEOUT_SIG
|
|
};
|
|
/*$enddecl${QXK::QXK-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
/*$declare${QXK::QXK-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QXK::QXK-extern-C::Attr} ...............................................*/
|
|
/*! @brief The QXK kernel class
|
|
* @class QXK
|
|
*
|
|
* @note
|
|
* The order and alignment of the data members in this struct might
|
|
* be important in QXK ports, where the members might be accessed
|
|
* in assembly.
|
|
*/
|
|
typedef struct QXK_Attr {
|
|
struct QActive * volatile curr; /*!< current thread (NULL=basic) */
|
|
struct QActive * volatile next; /*!< next thread to run */
|
|
struct QActive * volatile prev; /*!< previous thread */
|
|
uint8_t volatile actPrio; /*!< QF-prio of the active AO */
|
|
uint8_t volatile lockCeil; /*!< lock-ceiling (0==no-lock) */
|
|
uint8_t volatile lockHolder; /*!< prio of the lock holder */
|
|
} QXK;
|
|
|
|
/*${QXK::QXK-extern-C::attr_} ..............................................*/
|
|
/*! attributes of the QXK kernel */
|
|
extern QXK QXK_attr_;
|
|
|
|
/*${QXK::QXK-extern-C::sched_} .............................................*/
|
|
/*! QXK scheduler finds the highest-priority thread ready to run
|
|
* @static @private @memberof QXK
|
|
*
|
|
* @details
|
|
* The QXK scheduler finds the priority of the highest-priority thread
|
|
* that is ready to run.
|
|
*
|
|
* @returns
|
|
* the 1-based priority of the the thread (basic or extended) run next,
|
|
* or zero if no eligible thread is found.
|
|
*
|
|
* @attention
|
|
* QXK_sched_() must be always called with interrupts **disabled** and
|
|
* returns with interrupts **disabled**.
|
|
*/
|
|
uint_fast8_t QXK_sched_(void);
|
|
|
|
/*${QXK::QXK-extern-C::activate_} ..........................................*/
|
|
/*! QXK activator activates the next active object. The activated AO
|
|
* preempts the currently executing AOs.
|
|
* @static @private @memberof QXK
|
|
*
|
|
* @details
|
|
* QXK_activate_() activates ready-to run AOs that are above the initial
|
|
* active priority (QXK_attr_.actPrio).
|
|
*
|
|
* @attention
|
|
* QXK_activate_() must be always called with interrupts **disabled** and
|
|
* returns with interrupts **disabled**.
|
|
*/
|
|
void QXK_activate_(void);
|
|
|
|
/*${QXK::QXK-extern-C::current} ............................................*/
|
|
/*! obtain the currently executing active-object/thread
|
|
* @static @public @memberof QXK
|
|
*
|
|
* @returns
|
|
* pointer to the currently executing active-object/thread
|
|
*/
|
|
QActive * QXK_current(void);
|
|
|
|
/*${QXK::QXK-extern-C::stackInit_} .........................................*/
|
|
/*! initialize the private stack of a given AO (defined in QXK port) */
|
|
void QXK_stackInit_(
|
|
void * thr,
|
|
QXThreadHandler const handler,
|
|
void * const stkSto,
|
|
uint_fast16_t const stkSize);
|
|
|
|
/*${QXK::QXK-extern-C::contextSw} ..........................................*/
|
|
#if defined(Q_SPY) || defined(QF_ON_CONTEXT_SW)
|
|
/*! QXK context switch management
|
|
* @static @public @memberof QXK
|
|
*
|
|
* @details
|
|
* This function performs software tracing (if #Q_SPY is defined)
|
|
* and calls QF_onContextSw() (if #QF_ON_CONTEXT_SW is defined)
|
|
*
|
|
* @param[in] next pointer to the next thread (NULL for basic-thread)
|
|
*
|
|
* @attention
|
|
* QXK_contextSw() is invoked with interrupts **disabled** and must also
|
|
* return with interrupts **disabled**.
|
|
*/
|
|
void QXK_contextSw(QActive * const next);
|
|
#endif /* defined(Q_SPY) || defined(QF_ON_CONTEXT_SW) */
|
|
/*$enddecl${QXK::QXK-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
/*$declare${QXK::QXThread} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QXK::QXThread} .........................................................*/
|
|
/*! @brief eXtended (blocking) thread of the QXK preemptive kernel
|
|
* @class QXThread
|
|
* @extends QActive
|
|
*
|
|
* @details
|
|
* ::QXThread represents the eXtended (blocking) thread of the QXK
|
|
* kernel. Each extended thread in the application must be represented
|
|
* by the corresponding ::QXThread instance
|
|
*
|
|
* @note
|
|
* Typically, ::QXThread is instantiated directly in the application code.
|
|
* The customization of the thread occurs in the QXThread_ctor(), where you
|
|
* provide the thread-handler function as the parameter.
|
|
*
|
|
* @usage
|
|
* The following example illustrates how to instantiate and use an extended
|
|
* thread in your application.
|
|
* @include qxk_thread.c
|
|
*/
|
|
typedef struct QXThread {
|
|
/* protected: */
|
|
QActive super;
|
|
|
|
/* private: */
|
|
|
|
/*! time event to handle blocking timeouts */
|
|
QTimeEvt timeEvt;
|
|
} QXThread;
|
|
|
|
/* public: */
|
|
|
|
/*! constructor of an extended-thread
|
|
* @public @memberof QXThread
|
|
*
|
|
* @details
|
|
* Performs the first step of QXThread initialization by assigning the
|
|
* thread-handler function and the tick rate at which it will handle
|
|
* the timeouts.
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
* @param[in] handler the thread-handler function
|
|
* @param[in] tickRate the ticking rate for timeouts in this thread
|
|
* (see QXThread_delay() and QTIMEEVT_TICK_X())
|
|
*
|
|
* @note
|
|
* Must be called only ONCE before QXTHREAD_START().
|
|
*
|
|
* @usage
|
|
* The following example illustrates how to invoke QXThread_ctor() in the
|
|
* main() function
|
|
*
|
|
* @include
|
|
* qxk_thread_ctor.c
|
|
*/
|
|
void QXThread_ctor(QXThread * const me,
|
|
QXThreadHandler const handler,
|
|
uint_fast8_t const tickRate);
|
|
|
|
/*! delay (block) the current extended thread for a specified # ticks
|
|
* @static @public @memberof QXThread
|
|
*
|
|
* @details
|
|
* Blocking delay for the number of clock tick at the associated tick rate.
|
|
*
|
|
* @param[in] nTicks number of clock ticks (at the associated rate)
|
|
* to wait for the event to arrive.
|
|
* @note
|
|
* For the delay to work, the QTIMEEVT_TICK_X() macro needs to be called
|
|
* periodically at the associated clock tick rate.
|
|
*
|
|
* @sa QXThread_ctor()
|
|
* @sa QTIMEEVT_TICK_X()
|
|
*/
|
|
bool QXThread_delay(uint_fast16_t const nTicks);
|
|
|
|
/*! cancel the delay
|
|
* @public @memberof QXThread
|
|
*
|
|
* @details
|
|
* Cancel the blocking delay and cause return from the QXThread_delay()
|
|
* function.
|
|
*
|
|
* @returns
|
|
* "true" if the thread was actually blocked on QXThread_delay() and
|
|
* "false" otherwise.
|
|
*/
|
|
bool QXThread_delayCancel(QXThread * const me);
|
|
|
|
/*! obtain a message from the private message queue (block if no messages)
|
|
* @static @public @memberof QXThread
|
|
*
|
|
* @details
|
|
* The QXThread_queueGet() operation allows the calling extended thread to
|
|
* receive QP events directly into its own built-in event queue from an ISR,
|
|
* basic thread (AO), or another extended thread.
|
|
*
|
|
* If QXThread_queueGet() is called when no events are present in the
|
|
* thread's private event queue, the operation blocks the current extended
|
|
* thread until either an event is received, or a user-specified timeout
|
|
* expires.
|
|
*
|
|
* @param[in] nTicks number of clock ticks (at the associated rate)
|
|
* to wait for the event to arrive. The value of
|
|
* ::QXTHREAD_NO_TIMEOUT indicates that no timeout will
|
|
* occur and the queue will block indefinitely.
|
|
* @returns
|
|
* A pointer to the event. If the pointer is not NULL, the event was delivered.
|
|
* Otherwise the event pointer of NULL indicates that the queue has timed out.
|
|
*/
|
|
QEvt const * QXThread_queueGet(uint_fast16_t const nTicks);
|
|
|
|
/* protected: */
|
|
|
|
/*! Overrides QHsm_init_() */
|
|
void QXThread_init_(
|
|
QHsm * const me,
|
|
void const * const par,
|
|
uint_fast8_t const qs_id);
|
|
|
|
/*! Overrides QHsm_dispatch_() */
|
|
void QXThread_dispatch_(
|
|
QHsm * const me,
|
|
QEvt const * const e,
|
|
uint_fast8_t const qs_id);
|
|
|
|
/*! start QXThread private implementation
|
|
* @private @memberof QXThread
|
|
*
|
|
* @details
|
|
* Starts execution of an extended thread and registers it with the framework.
|
|
* The extended thread becomes ready-to-run immediately and is scheduled
|
|
* if the QXK is already running.
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
* @param[in] prio QF-priority of the thread, but no preemption-
|
|
* threshold. See also ::QPrioSpec.
|
|
* @param[in] qSto pointer to the storage for the ring buffer of the
|
|
* event queue. This cold be NULL, if this extended
|
|
* thread does not use the built-in event queue.
|
|
* @param[in] qLen length of the event queue [in events],
|
|
* or zero if queue not used
|
|
* @param[in] stkSto pointer to the stack storage (must be provided)
|
|
* @param[in] stkSize stack size [in bytes] (must not be zero)
|
|
* @param[in] par pointer to an extra parameter (might be NULL).
|
|
*
|
|
* @note
|
|
* Currently, extended trheads in QXK do NOT support preemption-threshold.
|
|
* The `prio` must NOT provide preemption-threshold and this function
|
|
* will assert it in the precondition.
|
|
*
|
|
* @usage
|
|
* QXThread_start_() should NOT be called directly, only via the macro
|
|
* QXTHREAD_START(). The following example shows starting an extended
|
|
* thread:
|
|
* @include qxk_start.c
|
|
*/
|
|
void QXThread_start_(
|
|
QActive * const me,
|
|
QPrioSpec const prioSpec,
|
|
QEvt const * * const qSto,
|
|
uint_fast16_t const qLen,
|
|
void * const stkSto,
|
|
uint_fast16_t const stkSize,
|
|
void const * const par);
|
|
|
|
/*! post to the QXThread event queue private implementation
|
|
* @private @memberof QXThread
|
|
*
|
|
* @details
|
|
* Direct event posting is the simplest asynchronous communication method
|
|
* available in QF. The following example illustrates how the Philo active
|
|
* object posts directly the HUNGRY event to the Table active object.
|
|
* <br>
|
|
* The parameter `margin` specifies the minimum number of free slots in
|
|
* the queue that must be available for posting to succeed. The function
|
|
* returns 1 (success) if the posting succeeded (with the provided margin)
|
|
* and 0 (failure) when the posting fails.
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
* @param[in] e pointer to the event to be posted
|
|
* @param[in] margin number of required free slots in the queue after
|
|
* posting the event. The special value #QF_NO_MARGIN
|
|
* means that this function will assert if posting fails.
|
|
* @param[in] sender pointer to a sender object (used only for QS tracing).
|
|
*
|
|
* @returns
|
|
* 'true' (success) if the posting succeeded (with the provided margin) and
|
|
* 'false' (failure) when the posting fails.
|
|
*
|
|
* @note
|
|
* Should be called only via the macro QXTHREAD_POST_X().
|
|
*
|
|
* @note
|
|
* The #QF_NO_MARGIN value of the `margin` parameter is special and
|
|
* denotes situation when the post() operation is assumed to succeed
|
|
* (event delivery guarantee). An assertion fires, when the event cannot
|
|
* be delivered in this case.
|
|
*
|
|
* @note
|
|
* For compatibility with the V-table from the superclass ::QActive, the
|
|
* me-pointer is typed as pointing to QActive. However, the `me` pointer
|
|
* here actually points to the QXThread subclass. Therefore the downcast
|
|
* (QXThread *)me is always correct.
|
|
*/
|
|
bool QXThread_post_(
|
|
QActive * const me,
|
|
QEvt const * const e,
|
|
uint_fast16_t const margin,
|
|
void const * const sender);
|
|
|
|
/*! post to the QXThread event queue (LIFO) private implementation
|
|
* @private @memberof QXThread
|
|
*
|
|
* @details
|
|
* Last-In-First-Out (LIFO) policy is not supported for extened threads.
|
|
*
|
|
* @param[in] me current instance pointer (see @ref oop)
|
|
* @param[in] e pointer to the event to post to the queue
|
|
*
|
|
* @sa
|
|
* QActive_postLIFO_()
|
|
*/
|
|
void QXThread_postLIFO_(
|
|
QActive * const me,
|
|
QEvt const * const e);
|
|
|
|
/* private: */
|
|
|
|
/*! block QXThread private implementation
|
|
* @private @memberof QXThread
|
|
*
|
|
* @details
|
|
* Internal implementation of blocking the given extended thread.
|
|
*
|
|
* @note
|
|
* Must be called from within a critical section
|
|
*/
|
|
void QXThread_block_(QXThread const * const me);
|
|
|
|
/*! unblock QXThread private implementation
|
|
* @private @memberof QXThread
|
|
*
|
|
* @details
|
|
* Internal implementation of un-blocking the given extended thread.
|
|
*
|
|
* @note
|
|
* must be called from within a critical section
|
|
*/
|
|
void QXThread_unblock_(QXThread const * const me);
|
|
|
|
/*! arm internal time event private implementation
|
|
* @private @memberof QXThread
|
|
*
|
|
* @details
|
|
* Internal implementation of arming the private time event for a given
|
|
* timeout at a given system tick rate.
|
|
*
|
|
* @note
|
|
* Must be called from within a critical section
|
|
*/
|
|
void QXThread_teArm_(QXThread * const me,
|
|
enum_t const sig,
|
|
uint_fast16_t const nTicks);
|
|
|
|
/*! disarm internal time event private implementation
|
|
* @private @memberof QXThread
|
|
*
|
|
* @details
|
|
* Internal implementation of disarming the private time event.
|
|
*
|
|
* @note
|
|
* Must be called from within a critical section
|
|
*/
|
|
bool QXThread_teDisarm_(QXThread * const me);
|
|
|
|
/*! dummy static to force generation of "struct QXThread" */
|
|
extern QXThread const * QXThread_dummy;
|
|
/*$enddecl${QXK::QXThread} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
/*$declare${QXK::QXThreadVtable} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QXK::QXThreadVtable} ...................................................*/
|
|
/*! @brief Virtual Table for the ::QXThread class
|
|
* (inherited from ::QActiveVtable)
|
|
*
|
|
* @note
|
|
* ::QXThread inherits ::QActive without adding any new virtual
|
|
* functions and therefore, ::QXThreadVtable is typedef'ed as ::QActiveVtable.
|
|
*/
|
|
typedef QActiveVtable QXThreadVtable;
|
|
/*$enddecl${QXK::QXThreadVtable} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
/*$declare${QXK::QXSemaphore} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QXK::QXSemaphore} ......................................................*/
|
|
/*! @brief Counting Semaphore of the QXK preemptive kernel
|
|
* @class QXSemaphore
|
|
*
|
|
* @details
|
|
* ::QXSemaphore is a blocking mechanism intended primarily for signaling
|
|
* @ref ::QXThread "extended threads". The semaphore is initialized with
|
|
* the maximum count (see QXSemaphore_init()), which allows you to create
|
|
* a binary semaphore (when the maximum count is 1) and
|
|
* counting semaphore when the maximum count is > 1.
|
|
*
|
|
* @usage
|
|
* The following example illustrates how to instantiate and use the semaphore
|
|
* in your application.
|
|
* @include qxk_sema.c
|
|
*/
|
|
typedef struct {
|
|
/* private: */
|
|
|
|
/*! set of extended threads waiting on this semaphore */
|
|
QPSet waitSet;
|
|
|
|
/*! semaphore up-down counter */
|
|
uint8_t volatile count;
|
|
|
|
/*! maximum value of the semaphore counter */
|
|
uint8_t max_count;
|
|
} QXSemaphore;
|
|
|
|
/* public: */
|
|
|
|
/*! initialize the counting semaphore
|
|
* @public @memberof QXSemaphore
|
|
*
|
|
* @details
|
|
* Initializes a semaphore with the specified count and maximum count.
|
|
* If the semaphore is used for resource sharing, both the initial count
|
|
* and maximum count should be set to the number of identical resources
|
|
* guarded by the semaphore. If the semaphore is used as a signaling
|
|
* mechanism, the initial count should set to 0 and maximum count to 1
|
|
* (binary semaphore).
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
* @param[in] count initial value of the semaphore counter
|
|
* @param[in] max_count maximum value of the semaphore counter.
|
|
* The purpose of the max_count is to limit the counter
|
|
* so that the semaphore cannot unblock more times than
|
|
* the maximum.
|
|
*
|
|
* @note
|
|
* QXSemaphore_init() must be called **before** the semaphore can be used
|
|
* (signaled or waited on).
|
|
*/
|
|
void QXSemaphore_init(QXSemaphore * const me,
|
|
uint_fast8_t const count,
|
|
uint_fast8_t const max_count);
|
|
|
|
/*! wait (block) on the semaphore
|
|
* @public @memberof QXSemaphore
|
|
*
|
|
* @details
|
|
* When an extended thread calls QXSemaphore_wait() and the value of the
|
|
* semaphore counter is greater than 0, QXSemaphore_wait() decrements the
|
|
* semaphore counter and returns (true) to its caller. However, if the value
|
|
* of the semaphore counter is 0, the function places the calling thread in
|
|
* the waiting list for the semaphore. The thread waits until the semaphore
|
|
* is signaled by calling QXSemaphore_signal(), or the specified timeout
|
|
* expires. If the semaphore is signaled before the timeout expires, QXK
|
|
* resumes the highest-priority extended thread waiting for the semaphore.
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
* @param[in] nTicks number of clock ticks (at the associated rate)
|
|
* to wait for the semaphore. The value of
|
|
* ::QXTHREAD_NO_TIMEOUT indicates that no timeout will
|
|
* occur and the semaphore will wait indefinitely.
|
|
*
|
|
* @returns
|
|
* 'true' if the semaphore has been signaled and 'false' if a timeout
|
|
* occurred.
|
|
*
|
|
* @note
|
|
* Multiple extended threads can wait for a given semaphore.
|
|
*/
|
|
bool QXSemaphore_wait(QXSemaphore * const me,
|
|
uint_fast16_t const nTicks);
|
|
|
|
/*! try wait on the semaphore (non-blocking)
|
|
* @public @memberof QXSemaphore
|
|
*
|
|
* @details
|
|
* This function checks if the semaphore counter is greater than 0,
|
|
* in which case the counter is decremented.
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
*
|
|
* @returns
|
|
* 'true' if the semaphore has count available and 'false' NOT available.
|
|
*
|
|
* @note
|
|
* This function can be called from any context, including ISRs and basic
|
|
* threads (active objects).
|
|
*/
|
|
bool QXSemaphore_tryWait(QXSemaphore * const me);
|
|
|
|
/*! signal (unblock) the semaphore
|
|
* @public @memberof QXSemaphore
|
|
*
|
|
* @details
|
|
* If the semaphore counter value is 0 or more, it is incremented, and
|
|
* this function returns to its caller. If the extended threads are waiting
|
|
* for the semaphore to be signaled, QXSemaphore_signal() removes the highest-
|
|
* priority thread waiting for the semaphore from the waiting list and makes
|
|
* this thread ready-to-run. The QXK scheduler is then called to determine if
|
|
* the awakened thread is now the highest-priority thread that is ready-to-run.
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
*
|
|
* @returns
|
|
* 'true' when the semaphore signaled and 'false' when the semaphore count
|
|
* exceeded the maximum.
|
|
*
|
|
* @note
|
|
* A semaphore can be signaled from many places, including from ISRs, basic
|
|
* threads (AOs), and extended threads.
|
|
*/
|
|
bool QXSemaphore_signal(QXSemaphore * const me);
|
|
/*$enddecl${QXK::QXSemaphore} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
/*$declare${QXK::QXMutex} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QXK::QXMutex} ..........................................................*/
|
|
/*! @brief Blocking Mutex the QXK preemptive kernel
|
|
* @class QXMutex
|
|
*
|
|
* @details
|
|
* ::QXMutex is a blocking mutual exclusion mechanism that can also apply
|
|
* the **priority-ceiling protocol** to avoid unbounded priority inversion
|
|
* (if initialized with a non-zero ceiling priority, see QXMutex_init()).
|
|
* In that case, ::QXMutex requires its own uinque QP priority level, which
|
|
* cannot be used by any thread or any other ::QXMutex.
|
|
* If initialized with preemption-ceiling of zero, ::QXMutex does **not**
|
|
* use the priority-ceiling protocol and does not require a unique QP
|
|
* priority (see QXMutex_init()).
|
|
* ::QXMutex is **recursive** (re-entrant), which means that it can be locked
|
|
* multiple times (up to 255 levels) by the *same* thread without causing
|
|
* deadlock.
|
|
* ::QXMutex is primarily intended for the @ref ::QXThread
|
|
* "extened (blocking) threads", but can also be used by the @ref ::QActive
|
|
* "basic threads" through the non-blocking QXMutex_tryLock() API.
|
|
*
|
|
* @note
|
|
* ::QXMutex should be used in situations when at least one of the extended
|
|
* threads contending for the mutex blocks while holding the mutex (between
|
|
* the QXMutex_lock() and QXMutex_unlock() operations). If no blocking is
|
|
* needed while holding the mutex, the more efficient non-blocking mechanism
|
|
* of @ref srs_qxk_schedLock() "selective QXK scheduler locking" should be used
|
|
* instead. @ref srs_qxk_schedLock() "Selective scheduler locking" is available
|
|
* for both @ref ::QActive "basic threads" and @ref ::QXThread "extended
|
|
* threads", so it is applicable to situations where resources are shared
|
|
* among all these threads.
|
|
*
|
|
* @usage
|
|
* The following example illustrates how to instantiate and use the mutex
|
|
* in your application.
|
|
* @include qxk_mutex.c
|
|
*/
|
|
typedef struct {
|
|
/* private: */
|
|
|
|
/*! active object used as a placeholder AO for this mutex
|
|
* in QActive_registry_[]
|
|
*/
|
|
QActive ao;
|
|
|
|
/*! set of extended-threads waiting on this mutex */
|
|
QPSet waitSet;
|
|
} QXMutex;
|
|
|
|
/* public: */
|
|
|
|
/*! initialize the QXK priority-ceiling mutex ::QXMutex
|
|
* @public @memberof QXMutex
|
|
*
|
|
* @details
|
|
* Initialize the QXK priority ceiling mutex.
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
* @param[in] prioSpec the priority specification for the mutex
|
|
* (See also ::QPrioSpec). This value might
|
|
* also be zero.
|
|
* @note
|
|
* `prioSpec == 0` means that the priority-ceiling protocol shall **not**
|
|
* be used by this mutex. Such mutex will **not** change (boost) the
|
|
* priority of the holding threads.<br>
|
|
*
|
|
* Conversely, `prioSpec != 0` means that the priority-ceiling protocol
|
|
* shall be used by this mutex. Such mutex **will** temporarily boost
|
|
* the priority and priority-threshold of the holding thread to the
|
|
* priority specification in `prioSpec` (see ::QPrioSpec).
|
|
*
|
|
* @usage
|
|
* @include qxk_mutex.c
|
|
*/
|
|
void QXMutex_init(QXMutex * const me,
|
|
QPrioSpec const prioSpec);
|
|
|
|
/*! lock the QXK priority-ceiling mutex ::QXMutex
|
|
* @public @memberof QXMutex
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
* @param[in] nTicks number of clock ticks (at the associated rate)
|
|
* to wait for the mutex. The value of
|
|
* ::QXTHREAD_NO_TIMEOUT indicates that no timeout will
|
|
* occur and the mutex could block indefinitely.
|
|
* @returns
|
|
* 'true' if the mutex has been acquired and 'false' if a timeout occurred.
|
|
*
|
|
* @note
|
|
* The mutex locks are allowed to nest, meaning that the same extended thread
|
|
* can lock the same mutex multiple times (< 255). However, each call to
|
|
* QXMutex_lock() must be balanced by the matching call to QXMutex_unlock().
|
|
*
|
|
* @usage
|
|
* @include qxk_mutex.c
|
|
*/
|
|
bool QXMutex_lock(QXMutex * const me,
|
|
uint_fast16_t const nTicks);
|
|
|
|
/*! try to lock the QXK priority-ceiling mutex ::QXMutex
|
|
* @public @memberof QXMutex
|
|
*
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
*
|
|
* @returns
|
|
* 'true' if the mutex was successfully locked and 'false' if the mutex was
|
|
* unavailable and was NOT locked.
|
|
*
|
|
* @note
|
|
* This function **can** be called from both basic threads (active objects)
|
|
* and extended threads.
|
|
*
|
|
* @note
|
|
* The mutex locks are allowed to nest, meaning that the same extended thread
|
|
* can lock the same mutex multiple times (<= 255). However, each successful
|
|
* call to QXMutex_tryLock() must be balanced by the matching call to
|
|
* QXMutex_unlock().
|
|
*/
|
|
bool QXMutex_tryLock(QXMutex * const me);
|
|
|
|
/*! unlock the QXK priority-ceiling mutex ::QXMutex
|
|
* @public @memberof QXMutex
|
|
*!
|
|
* @param[in,out] me current instance pointer (see @ref oop)
|
|
*
|
|
* @note
|
|
* This function **can** be called from both basic threads (active objects)
|
|
* and extended threads.
|
|
*
|
|
* @note
|
|
* The mutex locks are allowed to nest, meaning that the same extended thread
|
|
* can lock the same mutex multiple times (<= 225). However, each call to
|
|
* QXMutex_lock() or a *successful* call to QXMutex_tryLock() must be
|
|
* balanced by the matching call to QXMutex_unlock().
|
|
*
|
|
* @usage
|
|
* @include qxk_mutex.c
|
|
*/
|
|
void QXMutex_unlock(QXMutex * const me);
|
|
/*$enddecl${QXK::QXMutex} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
/*$declare${QXK-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QXK-macros::QXTHREAD_START} ............................................*/
|
|
/*! Virtual call to start an extened thread
|
|
*
|
|
* @details
|
|
* Starts execution of the thread and registers the AO with the framework.
|
|
*
|
|
* @param[in,out] me_ current instance pointer (see @ref oop)
|
|
* @param[in] prioSpec_ priority specification at which to start the
|
|
* extended thread (see ::QPrioSpec)
|
|
* @param[in] qSto_ pointer to the storage for the ring buffer of the
|
|
* event queue (used only with the built-in ::QEQueue)
|
|
* @param[in] qLen_ length of the event queue (in events)
|
|
* @param[in] stkSto_ pointer to the stack storage (used only when
|
|
* per-AO stack is needed)
|
|
* @param[in] stkSize_ stack size (in bytes)
|
|
* @param[in] par_ pointer to the additional port-specific parameter(s)
|
|
* (might be NULL).
|
|
* @usage
|
|
* @include qxk_start.c
|
|
*/
|
|
#define QXTHREAD_START(me_, prioSpec_, qSto_, qLen_, stkSto_, stkSize_, par_) \
|
|
do { \
|
|
Q_ASSERT((me_)->super.super.vptr); \
|
|
((*((QActiveVtable const *)((me_)->super.super.vptr))->start)( \
|
|
&(me_)->super, (prioSpec_), (QEvt const **)(qSto_), (qLen_), \
|
|
(stkSto_), (stkSize_), (par_))); \
|
|
} while (false)
|
|
|
|
/*${QXK-macros::QXTHREAD_NO_TIMEOUT} .......................................*/
|
|
/*! No-timeout when blocking on semaphores, mutextes, and queues */
|
|
#define QXTHREAD_NO_TIMEOUT ((uint_fast16_t)0)
|
|
|
|
/*${QXK-macros::QXTHREAD_POST_X} ...........................................*/
|
|
/*! Asynchronous posting events to the event queue of an eXtended thread
|
|
* @details
|
|
* This macro does not assert if the queue overflows and cannot accept
|
|
* the event with the specified margin of free slots remaining.
|
|
*
|
|
* @param[in,out] me_ current instance pointer (see @ref oop)
|
|
* @param[in] e_ pointer to the event to post
|
|
* @param[in] margin_ the minimum free slots in the queue, which
|
|
* must still be available after posting the event.
|
|
* The special value #QF_NO_MARGIN causes asserting
|
|
* failure in case event allocation fails.
|
|
* @param[in] sender_ pointer to the sender object (used in QS tracing)
|
|
*
|
|
* @returns
|
|
* 'true' if the posting succeeded, and 'false' if the posting failed due
|
|
* to insufficient margin of free slots available in the queue.
|
|
*
|
|
* @note
|
|
* The `sender_` parameter is actually only used when QS tracing is enabled
|
|
* (macro #Q_SPY is defined). When QS software tracing is disabled, the
|
|
* QXTHREAD_POST_X() macro does not pass the `sender_` parameter, so the
|
|
* overhead of passing this extra argument is entirely avoided.
|
|
*
|
|
* @note
|
|
* The pointer to the sender object is not necessarily a pointer to an
|
|
* active object. In fact, if QXTHREAD_POST_X() is called from an interrupt
|
|
* or other context, you can create a unique object just to unambiguously
|
|
* identify the sender of the event.
|
|
*
|
|
* @usage
|
|
* @include qf_postx.c
|
|
*/
|
|
#define QXTHREAD_POST_X(me_, e_, margin_, sender_) \
|
|
QACTIVE_POST_X(&(me_)->super, (e_), (margin_), (sender_))
|
|
/*$enddecl${QXK-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
|
|
/*==========================================================================*/
|
|
/* interface used only inside QP implementation, but not in applications */
|
|
#ifdef QP_IMPL
|
|
/* QXK implementation... */
|
|
/*$declare${QXK-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QXK-impl::QXK_ISR_CONTEXT_} ............................................*/
|
|
#ifndef QXK_ISR_CONTEXT_
|
|
/*! Internal macro that reports the execution context (ISR vs. thread)
|
|
*
|
|
* @returns true if the code executes in the ISR context and false
|
|
* otherwise
|
|
*/
|
|
#define QXK_ISR_CONTEXT_() (QF_intNest_ != 0U)
|
|
#endif /* ndef QXK_ISR_CONTEXT_ */
|
|
|
|
/*${QXK-impl::QF_SCHED_STAT_} ..............................................*/
|
|
/*! QXK scheduler lock status */
|
|
#define QF_SCHED_STAT_ QSchedStatus lockStat_;
|
|
|
|
/*${QXK-impl::QF_SCHED_LOCK_} ..............................................*/
|
|
/*! QXK selective scheduler locking */
|
|
#define QF_SCHED_LOCK_(ceil_) do { \
|
|
if (QXK_ISR_CONTEXT_()) { \
|
|
lockStat_ = 0xFFU; \
|
|
} else { \
|
|
lockStat_ = QXK_schedLock((ceil_)); \
|
|
} \
|
|
} while (false)
|
|
|
|
/*${QXK-impl::QF_SCHED_UNLOCK_} ............................................*/
|
|
/*! QXK selective scheduler unlocking */
|
|
#define QF_SCHED_UNLOCK_() do { \
|
|
if (lockStat_ != 0xFFU) { \
|
|
QXK_schedUnlock(lockStat_); \
|
|
} \
|
|
} while (false)
|
|
|
|
/*${QXK-impl::QACTIVE_EQUEUE_WAIT_} ........................................*/
|
|
/*! QXK native event queue waiting */
|
|
#define QACTIVE_EQUEUE_WAIT_(me_) \
|
|
(Q_ASSERT_ID(110, (me_)->eQueue.frontEvt != (QEvt *)0))
|
|
|
|
/*${QXK-impl::QACTIVE_EQUEUE_SIGNAL_} ......................................*/
|
|
/*! QXK native event queue signaling */
|
|
#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \
|
|
QPSet_insert(&QF_readySet_, (uint_fast8_t)(me_)->prio); \
|
|
if (!QXK_ISR_CONTEXT_()) { \
|
|
if (QXK_sched_() != 0U) { \
|
|
QXK_activate_(); \
|
|
} \
|
|
} \
|
|
} while (false)
|
|
|
|
/*${QXK-impl::QXK_PTR_CAST_} ...............................................*/
|
|
/*! internal macro to encapsulate casting of pointers for MISRA deviations
|
|
*
|
|
* @details
|
|
* This macro is specifically and exclusively used for casting pointers
|
|
* that are never de-referenced, but only used for internal bookkeeping and
|
|
* checking (via assertions) the correct operation of the QXK kernel.
|
|
* Such pointer casting is not compliant with MISRA-2012-Rule 11.3(req)
|
|
* as well as other messages (e.g., PC-Lint-Plus warning 826).
|
|
* Defining this specific macro for this purpose allows to selectively
|
|
* disable the warnings for this particular case.
|
|
*/
|
|
#define QXK_PTR_CAST_(type_, ptr_) ((type_)(ptr_))
|
|
|
|
/*${QXK-impl::QXTHREAD_CAST_} ..............................................*/
|
|
/*! internal macro to encapsulate casting of pointers for MISRA deviations
|
|
*
|
|
* @details
|
|
* This macro is specifically and exclusively used for downcasting pointers
|
|
* to QActive to pointers to QXThread in situations when it is known
|
|
* that such downcasting is correct.However, such pointer casting is not
|
|
* compliant with MISRA-2012-Rule 11.3(req) as well as other messages (e.g.,
|
|
* PC-Lint-Plus warning 826). Defining this specific macro for this purpose
|
|
* allows to selectively disable the warnings for this particular case.
|
|
*/
|
|
#define QXTHREAD_CAST_(ptr_) ((QXThread *)(ptr_))
|
|
|
|
/*${QXK-impl::QXK_threadExit_} .............................................*/
|
|
/*! called when QXThread exits
|
|
* @private @memberof QXThread
|
|
*
|
|
* @details
|
|
* Called when the extended-thread handler function returns.
|
|
*
|
|
* @note
|
|
* Most thread handler functions are structured as endless loops that never
|
|
* return. But it is also possible to structure threads as one-shot functions
|
|
* that perform their job and return. In that case this function peforms
|
|
* cleanup after the thread.
|
|
*/
|
|
void QXK_threadExit_(void);
|
|
/*$enddecl${QXK-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
|
|
/* Native QF event pool operations... */
|
|
/*$declare${QF-QMPool-impl} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
|
|
|
|
/*${QF-QMPool-impl::QF_EPOOL_TYPE_} ........................................*/
|
|
#define QF_EPOOL_TYPE_ QMPool
|
|
|
|
/*${QF-QMPool-impl::QF_EPOOL_INIT_} ........................................*/
|
|
#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \
|
|
(QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_)))
|
|
|
|
/*${QF-QMPool-impl::QF_EPOOL_EVENT_SIZE_} ..................................*/
|
|
#define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize)
|
|
|
|
/*${QF-QMPool-impl::QF_EPOOL_GET_} .........................................*/
|
|
#define QF_EPOOL_GET_(p_, e_, m_, qs_id_) \
|
|
((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qs_id_)))
|
|
|
|
/*${QF-QMPool-impl::QF_EPOOL_PUT_} .........................................*/
|
|
#define QF_EPOOL_PUT_(p_, e_, qs_id_) \
|
|
(QMPool_put(&(p_), (e_), (qs_id_)))
|
|
/*$enddecl${QF-QMPool-impl} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
|
|
#endif /* QP_IMPL */
|
|
|
|
#endif /* QP_INC_QXK_H_ */
|