SuperTinyKernel™ RTOS 1.08.x
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
stk_sync_mutex.h
Go to the documentation of this file.
1/*
2 * SuperTinyKernel(TM) RTOS: Lightweight High-Performance Deterministic C++ RTOS for Embedded Systems.
3 *
4 * Source: https://github.com/SuperTinyKernel-RTOS
5 *
6 * Copyright (c) 2022-2026 Neutron Code Limited <stk@neutroncode.com>. All Rights Reserved.
7 * License: MIT License, see LICENSE for a full text.
8 */
9
10#ifndef STK_SYNC_MUTEX_H_
11#define STK_SYNC_MUTEX_H_
12
13#include "stk_sync_cs.h"
14
18
19namespace stk {
20namespace sync {
21
54class Mutex final : private SyncObjectBase, public IMutex, public ITraceable
55{
56public:
57 static const uint16_t RECURSION_MAX = 0xFFFEU;
58
62 {}
63
70 {
71 STK_ASSERT(m_wait_list.IsEmpty()); // API contract: must not be destroyed with waiting tasks
72 }
73
85 EWaitResult TimedLockEx(Timeout timeout_ticks);
86
94 bool TimedLock(Timeout timeout_ticks) { return (TimedLockEx(timeout_ticks) == WAIT_RESULT_SIGNAL); }
95
105 void Lock() override;
106
112 bool TryLock() { return TimedLock(NO_WAIT); }
113
117 void Unlock() override;
118
123 TId GetOwner() const { return m_owner_tid; }
124
129 bool IsLocked() const { return (m_owner_tid != TID_NONE); }
130
140 uint16_t GetRecursionCount() const { return m_recursion_count; }
141
142private:
144
147};
148
149// ---------------------------------------------------------------------------
150// TimedLock
151// ---------------------------------------------------------------------------
152
154{
156 const TId current_tid = svc->GetTid();
157
159
160 const TId owner_tid = m_owner_tid;
161
162 // NO_WAIT on a busy mutex is reported as an immediately-expired timeout
164
165 STK_ASSERT(current_tid != TID_NONE); // API contract: must be called inside STK task
166
167 // recursive path: already owned by the calling thread
168 if ((m_recursion_count != 0U) && (owner_tid == current_tid))
169 {
170 STK_ASSERT(m_recursion_count < RECURSION_MAX); // API contract: caller must not exceed max recursion depth
171
172 m_recursion_count = static_cast<uint16_t>(m_recursion_count + 1U);
173 result = WAIT_RESULT_SIGNAL;
174 }
175 // fast path: mutex is free
176 else if (m_recursion_count == 0U)
177 {
178 // kernel invariant: counter is zero so owner must be TID_NONE
179 if (owner_tid != TID_NONE)
180 {
182 }
183
185 m_owner_tid = current_tid;
186 __stk_full_memfence();
187
188 result = WAIT_RESULT_SIGNAL;
189 }
190 // slow path: block until available or timeout expires
191 else if (timeout_ticks != NO_WAIT)
192 {
193 STK_ASSERT(!hw::IsInsideISR()); // API contract: caller must not be in ISR for a blocking call
194
195 // boost priority of the owner to avoid priority inversion (in case of SwitchStrategyFixedPriority,
196 // otherwise ignored by the kernel), noop if ISwitchStrategy::PRIORITY_INHERITANCE_API = 0
197 svc->InheritWeight(owner_tid, GetUserTaskFromTid(current_tid)->GetWeight());
198
199 // mutex owned by another thread (slow path/blocking)
200 result = svc->Wait(this, &cs_, timeout_ticks);
201
202 if (result != WAIT_RESULT_SIGNAL)
203 {
204 // kernel invariant: a wait that did not end in a signal must not leave this thread as the
205 // owner (Unlock() hands over ownership only together with a signal), otherwise the caller
206 // would be told it failed while owning a lock nobody will release
207 STK_ASSERT(m_owner_tid != current_tid);
208
209 // if owner did not change, undo priority boost to avoid stuck elevated priority: lookup for a
210 // higher weight within existing wait objects, noop if ISwitchStrategy::PRIORITY_INHERITANCE_API = 0
211 if (owner_tid == m_owner_tid)
212 {
213 svc->RestoreWeight(owner_tid, this);
214 }
215 }
216 else
217 {
218 // kernel invariant: if either condition is false, the low-level lock and the
219 // recursion counter are out of sync, this is an internal defect, not a caller error
220 if ((m_owner_tid != current_tid) || (m_recursion_count != 1U))
221 {
223 }
224 }
225 }
226 // try-lock variant: owned by someone else, but no-wait requested
227 else
228 {
229 // result is WAIT_RESULT_TIMEOUT already, noop
230 }
231
232 return result;
233}
234
235// ---------------------------------------------------------------------------
236// Lock
237// ---------------------------------------------------------------------------
238
239inline void Mutex::Lock()
240{
241 EWaitResult result;
242
243 do
244 {
245 result = TimedLockEx(WAIT_INFINITE);
246 }
247 while (result == WAIT_RESULT_CANCELED);
248
249 if (result != WAIT_RESULT_SIGNAL)
250 {
252 }
253}
254
255// ---------------------------------------------------------------------------
256// Unlock
257// ---------------------------------------------------------------------------
258
259inline void Mutex::Unlock()
260{
261 const ScopedCriticalSection cs_;
262
264 (m_owner_tid != TID_NONE)); // API contract: caller must own the lock
265 STK_ASSERT(m_recursion_count != 0U); // API contract: must have matching Lock()
266
267 m_recursion_count = static_cast<uint16_t>(m_recursion_count - 1U);
268
269 if (m_recursion_count == 0U)
270 {
272
273 // restore priority of the owner, noop if ISwitchStrategy::PRIORITY_INHERITANCE_API = 0
274 if (m_owner_tid != TID_NONE)
275 {
277 }
278
279 if (!m_wait_list.IsEmpty())
280 {
281 // pass ownership directly to the first waiter (FIFO order)
283
284 // transfer ownership to the waiter
286 m_owner_tid = waiter->GetTid();
287 __stk_full_memfence();
288
289 // wake up
290 waiter->Wake(false);
291
292 // boost priority from the highest-priority task still in wait list, skip the lookup if none left,
293 // noop if ISwitchStrategy::PRIORITY_INHERITANCE_API = 0
294 if (!m_wait_list.IsEmpty())
295 {
298 }
299 }
300 else
301 {
302 // free completely if there are no waiters
304 __stk_full_memfence();
305 }
306 }
307}
308
309} // namespace sync
310} // namespace stk
311
312#endif /* STK_SYNC_MUTEX_H_ */
#define STK_NONCOPYABLE_CLASS(TYPE)
Disables copy construction and assignment for a class.
Definition stk_defs.h:708
#define STK_ASSERT(e)
Runtime assertion. Halts execution if the expression e evaluates to false.
Definition stk_defs.h:516
#define STK_VIRT_DTOR
Makes destructors virtual and compliant to strict rules if STK_STRICT_COMPLIANCY=0.
Definition stk_defs.h:261
Implementation of synchronization primitive: stk::sync::ScopedCriticalSection.
Namespace of STK package.
static constexpr ITask * GetUserTaskFromTid(TId task_id) noexcept
Get task instance from its identifier.
Definition stk_arch.h:725
static constexpr Timeout NO_WAIT
Timeout value: return immediately if the synchronization object is not yet signaled (non-blocking pol...
Definition stk_common.h:220
EWaitResult
Wait result (see IKernelService::Wait).
Definition stk_common.h:123
@ WAIT_RESULT_CANCELED
The wait was cancelled by another task via IKernel::CancelTaskWait(), independent of signal/timeout.
Definition stk_common.h:127
@ WAIT_RESULT_TIMEOUT
The wake was caused by a timeout expiry.
Definition stk_common.h:126
@ WAIT_RESULT_SIGNAL
The wake was caused by a signal.
Definition stk_common.h:125
@ KERNEL_PANIC_ASSERT
Internal assertion failed (maps from STK_ASSERT).
Definition stk_common.h:63
int32_t Timeout
Timeout time (ticks).
Definition stk_common.h:156
static void STK_KERNEL_PANIC(stk::EKernelPanicId id)
Called when the kernel detects an unrecoverable internal fault.
Definition stk_arch.h:183
static constexpr Timeout WAIT_INFINITE
Timeout value: block indefinitely until the synchronization object is signaled.
Definition stk_common.h:214
static TId GetTid()
Get task/thread Id of the calling task.
Definition stk_helper.h:362
static constexpr TId TID_NONE
Reserved task/thread id representing zero/none thread id.
Definition stk_common.h:208
Word TId
Task (thread) id.
Definition stk_common.h:151
bool IsInsideISR()
Check whether the CPU is currently executing inside a hardware interrupt service routine (ISR).
Synchronization primitives for task coordination and resource protection.
Wait object.
Definition stk_common.h:471
virtual TId GetTid() const =0
Get thread Id of the task owning .
virtual void Wake(bool timeout)=0
Wake task.
Traceable object.
Definition stk_common.h:533
Weight FindWeightHigherThan(Weight comp) const
Find higher weight within linked wait objects.
Definition stk_helper.h:337
Interface for mutex synchronization primitive.
Definition stk_common.h:722
virtual TId GetTid() const =0
Get thread Id of the currently running task.
virtual void InheritWeight(TId tid, Weight weight)=0
Inherit weight for the task.
static IKernelService * GetInstance()
Get CPU-local instance of the kernel service.
virtual EWaitResult Wait(ISyncObject *sobj, IMutex *mutex, Timeout timeout)=0
Put calling process into a waiting state until synchronization object is signaled or timeout occurs.
virtual void RestoreWeight(TId tid, ISyncObject *sobj=nullptr)=0
Restore weight of the task to the original value.
SyncObjectBase()
Constructor.
Definition stk_helper.h:275
IWaitObject::ListHeadType m_wait_list
Tasks blocked on this object.
Definition stk_helper.h:298
friend class IKernelService
Definition stk_helper.h:253
static __stk_forceinline TTargetType * ListEntryToParent(TSourceType *const lentry)
Safely casts an intrusive list entry to its concrete parent container object type.
RAII-style low-level synchronization primitive for atomic code execution. Used as building brick for ...
Definition stk_sync_cs.h:54
EWaitResult TimedLockEx(Timeout timeout_ticks)
Acquire lock, preserving the full wait outcome.
bool TimedLock(Timeout timeout_ticks)
Acquire lock.
TId m_owner_tid
thread id of the current owner
~Mutex()
Destructor.
Mutex()
Constructor.
bool TryLock()
Try to acquire the lock without blocking.
TId GetOwner() const
Get owner of the mutex.
void Unlock() override
Release lock.
uint16_t m_recursion_count
recursion depth
void Lock() override
Acquire lock.
static const uint16_t RECURSION_MAX
maximum nesting depth
bool IsLocked() const
Check if the mutex is currently locked.
uint16_t GetRecursionCount() const
Get current recursion depth.