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_fastmutex.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_FASTMUTEX_H_
11#define STK_SYNC_FASTMUTEX_H_
12
13#include "stk_sync_cs.h"
14
18
19namespace stk {
20namespace sync {
21
51class FastMutex final : public SyncObjectBase, public IMutex, public ITraceable
52{
53public:
57 {}
58
65 {
66 STK_ASSERT(m_wait_list.IsEmpty()); // API contract: must not be destroyed with waiting tasks
67 }
68
81 EWaitResult TimedLockEx(Timeout timeout_ticks);
82
91 bool TimedLock(Timeout timeout_ticks) { return (TimedLockEx(timeout_ticks) == WAIT_RESULT_SIGNAL); }
92
102 void Lock() override;
103
109 bool TryLock() { return TimedLock(NO_WAIT); }
110
114 void Unlock() override;
115
120 TId GetOwner() const { return m_owner_tid; }
121
126 bool IsLocked() const { return (m_owner_tid != TID_NONE); }
127
128private:
130
132};
133
134// ---------------------------------------------------------------------------
135// TimedLock
136// ---------------------------------------------------------------------------
137
139{
141 const TId current_tid = svc->GetTid();
142
144
145 const TId owner_tid = m_owner_tid;
146
147 // NO_WAIT on a busy mutex is reported as an immediately-expired timeout
149
150 STK_ASSERT(current_tid != TID_NONE); // API contract: must be called inside STK task
151
152 // fast path: mutex is free
153 if (owner_tid == TID_NONE)
154 {
155 m_owner_tid = current_tid;
156 __stk_full_memfence();
157
158 result = WAIT_RESULT_SIGNAL;
159 }
160 // self-lock: non-recursive mutex, the caller would dead-lock on itself (or silently continue without
161 // owning a fresh lock): unrecoverable API contract violation, FastMutex is not recursive
162 else if (owner_tid == current_tid)
163 {
165 }
166 // slow path: block until available or timeout expires
167 else if (timeout_ticks != NO_WAIT)
168 {
169 STK_ASSERT(!hw::IsInsideISR()); // API contract: caller must not be in ISR for a blocking call
170
171 // boost priority of the owner to avoid priority inversion (in case of SwitchStrategyFixedPriority,
172 // otherwise ignored by the kernel), noop if ISwitchStrategy::PRIORITY_INHERITANCE_API = 0
173 svc->InheritWeight(owner_tid, GetUserTaskFromTid(current_tid)->GetWeight());
174
175 // mutex owned by another thread (slow path/blocking)
176 result = svc->Wait(this, &cs_, timeout_ticks);
177
178 if (result != WAIT_RESULT_SIGNAL)
179 {
180 // kernel invariant: a wait that did not end in a signal must not leave this thread as the
181 // owner (Unlock() hands over ownership only together with a signal), otherwise the caller
182 // would be told it failed while owning a lock nobody will release
183 STK_ASSERT(m_owner_tid != current_tid);
184
185 // if owner did not change, undo priority boost to avoid stuck elevated priority: lookup for a
186 // higher weight within existing wait objects, noop if ISwitchStrategy::PRIORITY_INHERITANCE_API = 0
187 if (owner_tid == m_owner_tid)
188 {
189 svc->RestoreWeight(owner_tid, this);
190 }
191 }
192 else
193 {
194 // kernel invariant: ownership must have been transferred to this thread by Unlock(),
195 // otherwise this is an internal defect, not a caller error
196 if (m_owner_tid != current_tid)
197 {
199 }
200 }
201 }
202 // try-lock variant: owned by someone else, but no-wait requested
203 else
204 {
205 // result is WAIT_RESULT_TIMEOUT already, noop
206 }
207
208 return result;
209}
210
211// ---------------------------------------------------------------------------
212// Lock
213// ---------------------------------------------------------------------------
214
215inline void FastMutex::Lock()
216{
217 EWaitResult result;
218
219 do
220 {
221 result = TimedLockEx(WAIT_INFINITE);
222 }
223 while (result == WAIT_RESULT_CANCELED);
224
225 if (result != WAIT_RESULT_SIGNAL)
226 {
228 }
229}
230
231// ---------------------------------------------------------------------------
232// Unlock
233// ---------------------------------------------------------------------------
234
235inline void FastMutex::Unlock()
236{
237 const ScopedCriticalSection cs_;
238
240 (m_owner_tid != TID_NONE)); // API contract: caller must own the lock
241
242 if (!m_wait_list.IsEmpty())
243 {
244 // slow path: contended unlock, hand over to the first waiter
246
247 // restore priority of the owner (it could have been boosted by waiters of this mutex),
248 // noop if ISwitchStrategy::PRIORITY_INHERITANCE_API = 0; not needed if there are no waiters
249 // because a boost is undone by the waiter itself on timeout/cancellation
250 if (m_owner_tid != TID_NONE)
251 {
253 }
254
255 // pass ownership directly to the first waiter (FIFO order)
257
258 // transfer ownership to the waiter
259 m_owner_tid = waiter->GetTid();
260 __stk_full_memfence();
261
262 // wake up
263 waiter->Wake(false);
264
265 // boost priority from the highest-priority task still in wait list, skip the lookup if none left,
266 // noop if ISwitchStrategy::PRIORITY_INHERITANCE_API = 0
267 if (!m_wait_list.IsEmpty())
268 {
271 }
272 }
273 else
274 {
275 // fast path: free completely if there are no waiters
277 __stk_full_memfence();
278 }
279}
280
281} // namespace sync
282} // namespace stk
283
284#endif /* STK_SYNC_FASTMUTEX_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:234
EWaitResult
Wait result (see IKernelService::Wait).
Definition stk_common.h:137
@ WAIT_RESULT_CANCELED
The wait was cancelled by another task via IKernel::CancelTaskWait(), independent of signal/timeout.
Definition stk_common.h:141
@ WAIT_RESULT_TIMEOUT
The wake was caused by a timeout expiry.
Definition stk_common.h:140
@ WAIT_RESULT_SIGNAL
The wake was caused by a signal.
Definition stk_common.h:139
@ KERNEL_PANIC_SYNC_DEADLOCK
Deadlock caused by a synchronization primitive: a task tried to acquire a non-recursive primitive (e....
Definition stk_common.h:74
@ KERNEL_PANIC_ASSERT
Internal assertion failed (maps from STK_ASSERT).
Definition stk_common.h:64
int32_t Timeout
Timeout time (ticks).
Definition stk_common.h:170
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:228
static TId GetTid()
Get task/thread Id of the calling task.
Definition stk_helper.h:376
static constexpr TId TID_NONE
Reserved task/thread id representing zero/none thread id.
Definition stk_common.h:222
Word TId
Task (thread) id.
Definition stk_common.h:165
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:497
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:559
Weight FindWeightHigherThan(Weight comp) const
Find higher weight within linked wait objects.
Definition stk_helper.h:351
Interface for mutex synchronization primitive.
Definition stk_common.h:766
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:312
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
bool IsLocked() const
Check if the mutex is currently locked.
bool TryLock()
Try to acquire the lock without blocking.
void Lock() override
Acquire lock.
bool TimedLock(Timeout timeout_ticks)
Acquire lock.
void Unlock() override
Release lock.
EWaitResult TimedLockEx(Timeout timeout_ticks)
Acquire lock, preserving the full wait outcome.
TId m_owner_tid
thread id of the current owner (TID_NONE if free)
TId GetOwner() const
Get owner of the mutex.