SuperTinyKernel™ RTOS 1.05.3
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
stk::sync::Mutex Class Reference

Recursive mutex primitive that allows the same thread to acquire the lock multiple times. More...

#include <stk_sync_mutex.h>

Inheritance diagram for stk::sync::Mutex:
Collaboration diagram for stk::sync::Mutex:

Public Member Functions

 Mutex ()
 Constructor.
 ~Mutex ()
 Destructor.
bool TimedLock (Timeout timeout)
 Acquire lock.
void Lock ()
 Acquire lock.
bool TryLock ()
 Acquire the lock.
void Unlock ()
 Release lock.
void SetTraceName (const char *name)
 Set name.
const char * GetTraceName () const
 Get name.

Private Types

typedef DLHeadType ListHeadType
 List head type for ISyncObject elements.
typedef DLEntryType ListEntryType
 List entry type of ISyncObject elements.
typedef DListEntry< ISyncObject, _ClosedLoop > DLEntryType
 Convenience alias for this entry type. Used to avoid repeating the full template spelling.
typedef DListHead< ISyncObject, _ClosedLoop > DLHeadType
 Convenience alias for the corresponding list head type.

Private Member Functions

 STK_NONCOPYABLE_CLASS (Mutex)
virtual void AddWaitObject (IWaitObject *wobj)
 Called by kernel when a new task starts waiting on this event.
virtual void RemoveWaitObject (IWaitObject *wobj)
 Called by kernel when a waiting task is being removed (timeout expired, wait aborted, task terminated etc.).
virtual bool Tick (Timeout elapsed_ticks)
 Called by kernel on every system tick to handle timeout logic of waiting tasks.
void WakeOne ()
 Wake the first task in the wait list (FIFO order).
void WakeAll ()
 Wake all tasks currently in the wait list.
DLHeadTypeGetHead () const
 Get the list head this entry currently belongs to.
DLEntryTypeGetNext () const
 Get the next entry in the list.
DLEntryTypeGetPrev () const
 Get the previous entry in the list.
bool IsLinked () const
 Check whether this entry is currently a member of any list.
 operator ISyncObject * ()
 Implicit conversion to a mutable pointer to the host object (T).
 operator const ISyncObject * () const
 Implicit conversion to a const pointer to the host object (T).
void Link (DLHeadType *head, DLEntryType *next, DLEntryType *prev)
 Wire this entry into a list between prev and next.
void Unlink ()
 Remove this entry from its current list.

Private Attributes

TId m_owner_tid
 thread id of the current owner
uint16_t m_recursion_count
 recursion depth
IWaitObject::ListHeadType m_wait_list
 tasks blocked on this object
DLHeadTypem_head
 Owning list head, or NULL when the entry is not linked.
DLEntryTypem_next
 Next entry in the list, or NULL (open list boundary) / first entry (closed loop).
DLEntryTypem_prev
 Previous entry in the list, or NULL (open list boundary) / last entry (closed loop).

Static Private Attributes

static const uint16_t RECURSION_MAX = 0xFFFEu
 maximum nesting depth

Detailed Description

Recursive mutex primitive that allows the same thread to acquire the lock multiple times.

Recursive mutex tracks ownership and a recursion count. If the owning thread calls Lock() again, the count is incremented and the call returns immediately without blocking. The lock is only fully released when Unlock() has been called an equal number of times.

// Example: Recursive locking in nested methods
stk::sync::Mutex g_ResourceMtx;
void Method_Internal() {
// second acquisition (recursion count = 2)
g_ResourceMtx.Lock();
// ... perform internal logic ...
g_ResourceMtx.Unlock();
}
void Method_Public() {
// first acquisition (recursion count = 1)
if (g_ResourceMtx.TimedLock(100)) {
// safe to call: same thread already owns the lock
Method_Internal();
g_ResourceMtx.Unlock();
}
}
Recursive mutex primitive that allows the same thread to acquire the lock multiple times.
void Unlock()
Release lock.
void Lock()
Acquire lock.
bool TimedLock(Timeout timeout)
Acquire lock.
Note
Only available when kernel is compiled with KERNEL_SYNC mode enabled.
See also
ISyncObject, IWaitObject, IKernelService::Wait

Definition at line 54 of file stk_sync_mutex.h.

Member Typedef Documentation

◆ DLEntryType

typedef DListEntry<ISyncObject, _ClosedLoop> stk::util::DListEntry< ISyncObject, _ClosedLoop >::DLEntryType
inherited

Convenience alias for this entry type. Used to avoid repeating the full template spelling.

Definition at line 70 of file stk_linked_list.h.

◆ DLHeadType

typedef DListHead<ISyncObject, _ClosedLoop> stk::util::DListEntry< ISyncObject, _ClosedLoop >::DLHeadType
inherited

Convenience alias for the corresponding list head type.

Definition at line 75 of file stk_linked_list.h.

◆ ListEntryType

List entry type of ISyncObject elements.

Definition at line 313 of file stk_common.h.

◆ ListHeadType

List head type for ISyncObject elements.

Definition at line 308 of file stk_common.h.

Constructor & Destructor Documentation

◆ Mutex()

stk::sync::Mutex::Mutex ( )
inlineexplicit

Constructor.

Definition at line 59 of file stk_sync_mutex.h.

60 {}
const TId TID_NONE
Reserved task/thread id representing zero/none thread id.
Definition stk_common.h:128
TId m_owner_tid
thread id of the current owner
uint16_t m_recursion_count
recursion depth

References m_owner_tid, m_recursion_count, and stk::TID_NONE.

Referenced by STK_NONCOPYABLE_CLASS().

Here is the caller graph for this function:

◆ ~Mutex()

stk::sync::Mutex::~Mutex ( )
inline

Destructor.

Note
If tasks are still waiting at destruction time it is considered a logical error (dangling waiters). An assertion is triggered in debug builds.
MISRA deviation: [STK-DEV-005] Rule 10-3-2.

Definition at line 67 of file stk_sync_mutex.h.

68 {
69 STK_ASSERT(m_wait_list.IsEmpty()); // API contract: must not be destroyed with waiting tasks
70 }
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
Definition stk_defs.h:330
IWaitObject::ListHeadType m_wait_list
tasks blocked on this object
Definition stk_common.h:373

References stk::ISyncObject::m_wait_list, and STK_ASSERT.

Member Function Documentation

◆ AddWaitObject()

virtual void stk::ISyncObject::AddWaitObject ( IWaitObject * wobj)
inlinevirtualinherited

Called by kernel when a new task starts waiting on this event.

Parameters
[in]wobjWait object representing blocked task.

Definition at line 318 of file stk_common.h.

319 {
320 STK_ASSERT(wobj->GetHead() == nullptr);
321 m_wait_list.LinkBack(wobj);
322 }

References stk::util::DListEntry< T, _ClosedLoop >::GetHead(), m_wait_list, and STK_ASSERT.

Referenced by stk::Kernel< TMode, TSize, TStrategy, TPlatform >::KernelTask::WaitObject::SetupWait().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetHead()

DLHeadType * stk::util::DListEntry< ISyncObject, _ClosedLoop >::GetHead ( ) const
inlineinherited

Get the list head this entry currently belongs to.

Returns
Pointer to the owning DListHead, or NULL if the entry is not linked.

Definition at line 80 of file stk_linked_list.h.

80{ return m_head; }
Intrusive doubly-linked list node. Embed this as a base class in any object (T) that needs to partici...

◆ GetNext()

DLEntryType * stk::util::DListEntry< ISyncObject, _ClosedLoop >::GetNext ( ) const
inlineinherited

Get the next entry in the list.

Returns
Pointer to the next DListEntry, or NULL if this is the last entry (open list) or the first entry (closed loop, where next wraps to first).
Note
In a closed loop (_ClosedLoop == true) this pointer is never NULL when the entry is linked.

Definition at line 88 of file stk_linked_list.h.

88{ return m_next; }

◆ GetPrev()

DLEntryType * stk::util::DListEntry< ISyncObject, _ClosedLoop >::GetPrev ( ) const
inlineinherited

Get the previous entry in the list.

Returns
Pointer to the previous DListEntry, or NULL if this is the first entry (open list) or the last entry (closed loop, where prev wraps to last).
Note
In a closed loop (_ClosedLoop == true) this pointer is never NULL when the entry is linked.

Definition at line 96 of file stk_linked_list.h.

96{ return m_prev; }

◆ GetTraceName()

const char * stk::ITraceable::GetTraceName ( ) const
inlineinherited

Get name.

Returns
Name string, or NULL if not set or if STK_SYNC_DEBUG_NAMES is 0.

Definition at line 278 of file stk_common.h.

279 {
280 #if STK_SYNC_DEBUG_NAMES
281 return m_trace_name;
282 #else
283 return nullptr;
284 #endif
285 }

◆ IsLinked()

bool stk::util::DListEntry< ISyncObject, _ClosedLoop >::IsLinked ( ) const
inlineinherited

Check whether this entry is currently a member of any list.

Returns
true if linked (m_head != NULL); false otherwise.

Definition at line 101 of file stk_linked_list.h.

101{ return (GetHead() != nullptr); }

◆ Link()

void stk::util::DListEntry< ISyncObject, _ClosedLoop >::Link ( DLHeadType * head,
DLEntryType * next,
DLEntryType * prev )
inlineprivateinherited

Wire this entry into a list between prev and next.

Parameters
[in]headThe owning DListHead. Stored as a back-pointer for IsLinked() and ownership checks.
[in]nextThe entry that will follow this one, or NULL if this becomes the last entry.
[in]prevThe entry that will precede this one, or NULL if this becomes the first entry.
Note
Called exclusively by DListHead::Link(). Assumes the entry is not currently linked. Updates the neighbours' forward/back pointers to splice this entry in.

Definition at line 137 of file stk_linked_list.h.

138 {
139 m_head = head;
140 m_next = next;
141 m_prev = prev;
142
143 if (m_prev != nullptr)
144 m_prev->m_next = this;
145
146 if (m_next != nullptr)
147 m_next->m_prev = this;
148 }
DLEntryType * m_next
Next entry in the list, or NULL (open list boundary) / first entry (closed loop).
DLEntryType * m_prev
Previous entry in the list, or NULL (open list boundary) / last entry (closed loop).

◆ Lock()

void stk::sync::Mutex::Lock ( )
inlinevirtual

Acquire lock.

Warning
ISR-unsafe.

Implements stk::IMutex.

Definition at line 83 of file stk_sync_mutex.h.

83{ (void)TimedLock(WAIT_INFINITE); }
const Timeout WAIT_INFINITE
Timeout value: block indefinitely until the synchronization object is signaled.
Definition stk_common.h:139

References TimedLock(), and stk::WAIT_INFINITE.

Referenced by stk_mutex_lock().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ operator const ISyncObject *()

stk::util::DListEntry< ISyncObject, _ClosedLoop >::operator const ISyncObject * ( ) const
inlineinherited

Implicit conversion to a const pointer to the host object (T).

Note
Safe because T must derive from DListEntry<T, _ClosedLoop>. Eliminates the need for explicit static_cast at call sites.
MISRA deviation: [STK-DEV-004] Rule 5-2-x.

Definition at line 115 of file stk_linked_list.h.

115{ return static_cast<const T *>(this); }

◆ operator ISyncObject *()

stk::util::DListEntry< ISyncObject, _ClosedLoop >::operator ISyncObject* ( )
inlineinherited

Implicit conversion to a mutable pointer to the host object (T).

Note
Safe because T must derive from DListEntry<T, _ClosedLoop>. Eliminates the need for explicit static_cast at call sites.
MISRA deviation: [STK-DEV-004] Rule 5-2-x.

Definition at line 108 of file stk_linked_list.h.

108{ return static_cast<T *>(this); }

◆ RemoveWaitObject()

virtual void stk::ISyncObject::RemoveWaitObject ( IWaitObject * wobj)
inlinevirtualinherited

Called by kernel when a waiting task is being removed (timeout expired, wait aborted, task terminated etc.).

Parameters
[in]wobjWait object to remove from the wait list.

Reimplemented in stk::sync::Event.

Definition at line 327 of file stk_common.h.

328 {
329 STK_ASSERT(wobj->GetHead() == &m_wait_list);
330 m_wait_list.Unlink(wobj);
331 }

References stk::util::DListEntry< T, _ClosedLoop >::GetHead(), m_wait_list, and STK_ASSERT.

Referenced by stk::sync::Event::RemoveWaitObject().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ SetTraceName()

void stk::ITraceable::SetTraceName ( const char * name)
inlineinherited

Set name.

Parameters
[in]nameNull-terminated string or NULL.
Note
If STK_SYNC_DEBUG_NAMES is 0 then calling this function has no effect.

Definition at line 266 of file stk_common.h.

267 {
268 #if STK_SYNC_DEBUG_NAMES
269 m_trace_name = name;
270 #else
271 (void)name;
272 #endif
273 }

◆ STK_NONCOPYABLE_CLASS()

stk::sync::Mutex::STK_NONCOPYABLE_CLASS ( Mutex )
private

References Mutex().

Here is the call graph for this function:

◆ Tick()

bool stk::ISyncObject::Tick ( Timeout elapsed_ticks)
inlinevirtualinherited

Called by kernel on every system tick to handle timeout logic of waiting tasks.

Implementation of ISyncObject::Tick, see ISyncObject. Placed here as it depends on hw namespace.

Parameters
[in]elapsed_ticksNumber of ticks elapsed between this and previous calls, in case of KERNEL_TICKLESS mode this value can be >1, for non-tickless mode it is always 1.
Returns
true if this synchronization object still has waiters with a finite timeout and requires further tick calls. false if the wait list is empty or all remaining waiters have infinite timeouts, signaling to the kernel that it may stop calling Tick() for this object until a new waiter is added.
Note
When this method returns false, the kernel unlinks this object from its active sync list. It will be re-linked automatically when the next waiter is added via AddWaitObject().

Definition at line 467 of file stk_arch.h.

468{
469 // note: ScopedCriticalSection usage
470 //
471 // Single-core: no critical section needed - Tick() runs inside the
472 // SysTick ISR which already executes with interrupts disabled, making
473 // re-entrancy impossible on the local core.
474 //
475 // Multi-core: critical section is required because the tick handler on
476 // each core may call Tick() concurrently for the same Semaphore instance,
477 // and ISyncObject::Tick() is not re-entrant.
478#if (STK_ARCH_CPU_COUNT > 1)
479 hw::CriticalSection::ScopedLock cs_;
480#endif
481
482 IWaitObject *itr = static_cast<IWaitObject *>(m_wait_list.GetFirst());
483
484 while (itr != nullptr)
485 {
486 IWaitObject *next = static_cast<IWaitObject *>(itr->GetNext());
487
488 if (!itr->Tick(elapsed_ticks))
489 itr->Wake(true);
490
491 itr = next;
492 }
493
494 return !m_wait_list.IsEmpty();
495}

References stk::util::DListEntry< T, _ClosedLoop >::GetNext(), m_wait_list, stk::IWaitObject::Tick(), and stk::IWaitObject::Wake().

Here is the call graph for this function:

◆ TimedLock()

bool stk::sync::Mutex::TimedLock ( Timeout timeout)
inline

Acquire lock.

Parameters
[in]timeoutMaximum time to wait (ticks).
Note
Maximum number of recursive locks must not exceed 0xFFFEU.
Warning
ISR-unsafe.
Returns
True if lock acquired, false if timeout occurred.

Definition at line 109 of file stk_sync_mutex.h.

110{
111 STK_ASSERT(!hw::IsInsideISR()); // API contract: caller must not be in ISR
112
113 IKernelService *svc = IKernelService::GetInstance();
114 TId current_tid = svc->GetTid();
115
116 ScopedCriticalSection cs_;
117
118 // already owned by the calling thread (recursive path)
119 if ((m_recursion_count != 0U) && (m_owner_tid == current_tid))
120 {
121 STK_ASSERT(m_recursion_count < RECURSION_MAX); // API contract: caller must not exceed max recursion depth
122
123 m_recursion_count = static_cast<uint16_t>(m_recursion_count + 1U);
124 return true;
125 }
126
127 // mutex is free (fast path)
128 if (m_recursion_count == 0U)
129 {
130 // kernel invariant: counter is zero so owner must be TID_NONE
131 if (m_owner_tid != TID_NONE)
133
135 m_owner_tid = current_tid;
136 __stk_full_memfence();
137
138 return true;
139 }
140
141 // try lock behavior
142 if (timeout == NO_WAIT)
143 return false;
144
145 // mutex owned by another thread (slow path/blocking)
146 if (svc->Wait(this, &cs_, timeout)->IsTimeout())
147 return false;
148
149 // kernel invariant: if either condition is false, the low-level lock and the
150 // recursion counter are out of sync, this is an internal defect, not a caller error
151 if ((m_owner_tid != current_tid) || (m_recursion_count != 1U))
153
154 return true;
155}
#define STK_KERNEL_PANIC(id)
Called when the kernel detects an unrecoverable internal fault.
Definition stk_arch.h:63
const Timeout NO_WAIT
Timeout value: return immediately if the synchronization object is not yet signaled (non-blocking pol...
Definition stk_common.h:145
Word TId
Definition stk_common.h:117
@ KERNEL_PANIC_ASSERT
Internal assertion failed (maps from STK_ASSERT).
Definition stk_common.h:56
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
Definition stktest.cpp:103
static IKernelService * GetInstance()
Get CPU-local instance of the kernel service.
Definition stktest.cpp:69
static const uint16_t RECURSION_MAX
maximum nesting depth

References stk::IKernelService::GetInstance(), stk::IKernelService::GetTid(), stk::hw::IsInsideISR(), stk::IWaitObject::IsTimeout(), stk::KERNEL_PANIC_ASSERT, m_owner_tid, m_recursion_count, stk::NO_WAIT, RECURSION_MAX, STK_ASSERT, STK_KERNEL_PANIC, stk::TID_NONE, and stk::IKernelService::Wait().

Referenced by Lock(), stk_mutex_timed_lock(), and TryLock().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ TryLock()

bool stk::sync::Mutex::TryLock ( )
inline

Acquire the lock.

Warning
ISR-unsafe.
Returns
True if lock acquired, false if lock is already acquired by another task.

Definition at line 89 of file stk_sync_mutex.h.

89{ return TimedLock(NO_WAIT); }

References stk::NO_WAIT, and TimedLock().

Referenced by stk_mutex_trylock().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Unlink()

void stk::util::DListEntry< ISyncObject, _ClosedLoop >::Unlink ( )
inlineprivateinherited

Remove this entry from its current list.

Note
Called exclusively by DListHead::Unlink(). Patches the neighbours' pointers to bridge over this entry, then clears m_head, m_next, and m_prev to NULL so the entry is in a clean unlinked state.
Does not update DListHead::m_count or m_first / m_last — those are the responsibility of the calling DListHead::Unlink().

Definition at line 157 of file stk_linked_list.h.

158 {
159 if (m_prev != nullptr)
161
162 if (m_next != nullptr)
164
165 m_head = nullptr;
166 m_next = nullptr;
167 m_prev = nullptr;
168 }

◆ Unlock()

void stk::sync::Mutex::Unlock ( )
inlinevirtual

Release lock.

Warning
ISR-unsafe.

Implements stk::IMutex.

Definition at line 161 of file stk_sync_mutex.h.

162{
163 STK_ASSERT(!hw::IsInsideISR()); // API contract: caller must not be in ISR
164 STK_ASSERT(m_owner_tid == GetTid()); // API contract: caller must own the lock
165 STK_ASSERT(m_recursion_count != 0U); // API contract: must have matching Lock()
166
167 ScopedCriticalSection cs_;
168
169 m_recursion_count = static_cast<uint16_t>(m_recursion_count - 1U);
170
171 if (m_recursion_count == 0U)
172 {
173 if (!m_wait_list.IsEmpty())
174 {
175 // pass ownership directly to the first waiter (FIFO order)
176 IWaitObject *waiter = static_cast<IWaitObject *>(m_wait_list.GetFirst());
177
178 // transfer ownership to the waiter
180 m_owner_tid = waiter->GetTid();
181 __stk_full_memfence();
182
183 waiter->Wake(false);
184 }
185 else
186 {
187 // free completely if there are no waiters
189 __stk_full_memfence();
190 }
191 }
192}
TId GetTid()
Get task/thread Id of the calling task.
Definition stk_helper.h:217

References stk::GetTid(), stk::IWaitObject::GetTid(), stk::hw::IsInsideISR(), m_owner_tid, m_recursion_count, stk::ISyncObject::m_wait_list, STK_ASSERT, stk::TID_NONE, and stk::IWaitObject::Wake().

Referenced by stk_mutex_unlock().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WakeAll()

void stk::ISyncObject::WakeAll ( )
inlineprotectedinherited

Wake all tasks currently in the wait list.

Note
Each woken task is notified with timeout=false, indicating a successful signal (not a timeout expiry).
Does nothing if no tasks are currently waiting.

Definition at line 367 of file stk_common.h.

368 {
369 while (!m_wait_list.IsEmpty())
370 static_cast<IWaitObject *>(m_wait_list.GetFirst())->Wake(false);
371 }

References m_wait_list, and stk::IWaitObject::Wake().

Referenced by stk::sync::ConditionVariable::NotifyAll(), stk::sync::Event::Pulse(), stk::sync::Event::Set(), and stk::test::SyncObjectMock::WakeAll().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WakeOne()

void stk::ISyncObject::WakeOne ( )
inlineprotectedinherited

Wake the first task in the wait list (FIFO order).

Note
The woken task is notified with timeout=false, indicating a successful signal (not a timeout expiry).
Does nothing if no tasks are currently waiting.

Definition at line 357 of file stk_common.h.

358 {
359 if (!m_wait_list.IsEmpty())
360 static_cast<IWaitObject *>(m_wait_list.GetFirst())->Wake(false);
361 }

References m_wait_list, and stk::IWaitObject::Wake().

Referenced by stk::sync::ConditionVariable::NotifyOne(), stk::sync::Event::Pulse(), stk::sync::Event::Set(), stk::sync::Semaphore::Signal(), and stk::test::SyncObjectMock::WakeOne().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_head

DLHeadType* stk::util::DListEntry< ISyncObject, _ClosedLoop >::m_head
privateinherited

Owning list head, or NULL when the entry is not linked.

Definition at line 170 of file stk_linked_list.h.

◆ m_next

DLEntryType* stk::util::DListEntry< ISyncObject, _ClosedLoop >::m_next
privateinherited

Next entry in the list, or NULL (open list boundary) / first entry (closed loop).

Definition at line 171 of file stk_linked_list.h.

◆ m_owner_tid

TId stk::sync::Mutex::m_owner_tid
private

thread id of the current owner

Definition at line 101 of file stk_sync_mutex.h.

Referenced by Mutex(), TimedLock(), and Unlock().

◆ m_prev

DLEntryType* stk::util::DListEntry< ISyncObject, _ClosedLoop >::m_prev
privateinherited

Previous entry in the list, or NULL (open list boundary) / last entry (closed loop).

Definition at line 172 of file stk_linked_list.h.

◆ m_recursion_count

uint16_t stk::sync::Mutex::m_recursion_count
private

recursion depth

Definition at line 102 of file stk_sync_mutex.h.

Referenced by Mutex(), TimedLock(), and Unlock().

◆ m_wait_list

◆ RECURSION_MAX

const uint16_t stk::sync::Mutex::RECURSION_MAX = 0xFFFEu
staticprivate

maximum nesting depth

Definition at line 99 of file stk_sync_mutex.h.

Referenced by TimedLock().


The documentation for this class was generated from the following file: