µOS++ IIIe Reference 7.0.0
The third edition of µOS++, a POSIX inspired open source framework, written in C++
Loading...
Searching...
No Matches
os-semaphore.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the µOS++ project (https://micro-os-plus.github.io/).
3 * Copyright (c) 2016-2025 Liviu Ionescu. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software
6 * for any purpose is hereby granted, under the terms of the MIT license.
7 *
8 * If a copy of the license was not distributed with this file, it can
9 * be obtained from https://opensource.org/licenses/mit.
10 */
11
12#if defined(OS_USE_OS_APP_CONFIG_H)
13#include <cmsis-plus/os-app-config.h>
14#endif
15
16#include <cmsis-plus/rtos/os.h>
17
18// ----------------------------------------------------------------------------
19
20#if defined(__clang__)
21#pragma clang diagnostic ignored "-Wc++98-compat"
22#endif
23
24// ----------------------------------------------------------------------------
25
26namespace os
27{
28 namespace rtos
29 {
30 // ------------------------------------------------------------------------
31
79 const semaphore::attributes_binary semaphore::initializer_binary{ 0 };
80
81 // ------------------------------------------------------------------------
82
148 semaphore::semaphore (const char* name, const attributes& attr)
149 : semaphore{ name, attr.sm_max_value, attr.sm_initial_value, attr }
150 {
151 }
152
157 semaphore::semaphore (const char* name, const count_t max_value,
158 const count_t initial_value,
159 const attributes& attr
161 __attribute__ ((unused))
162#endif
163 )
164 : object_named_system{ name }, //
165 max_value_ (max_value), //
166 initial_value_ (initial_value)
167 {
168#if defined(OS_TRACE_RTOS_SEMAPHORE)
169 trace::printf ("%s() @%p %s %u %u\n", __func__, this, this->name (),
170 initial_value, max_value_);
171#endif
172
173 // Don't call this from interrupt handlers.
175
176 // The CMSIS validator requires the max_value to be equal to
177 // the initial count, which can be 0, but we patch it on the way.
178 assert (max_value_ > 0);
179 assert (initial_value >= 0);
180 assert (initial_value <= max_value_);
181
182 count_ = initial_value;
183
184#if !defined(OS_USE_RTOS_PORT_SEMAPHORE)
185 clock_ = attr.clock != nullptr ? attr.clock : &sysclock;
186#endif
187
188#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
189
190 port::semaphore::create (this);
191
192#else
193
194 internal_init_ ();
195
196#endif
197 }
198
225 {
226#if defined(OS_TRACE_RTOS_SEMAPHORE)
227 trace::printf ("%s() @%p %s\n", __func__, this, name ());
228#endif
229
230#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
231
232 port::semaphore::destroy (this);
233
234#else
235
236 // There must be no threads waiting for this semaphore.
237 assert (list_.empty ());
238
239#endif
240 }
241
246 void
247 semaphore::internal_init_ (void)
248 {
249
250 count_ = initial_value_;
251
252#if !defined(OS_USE_RTOS_PORT_SEMAPHORE)
253
254 // Wake-up all threads, if any.
255 // Need not be inside the critical section,
256 // the list is protected by inner `resume_one()`.
257 list_.resume_all ();
258
259#endif /* !defined(OS_USE_RTOS_PORT_SEMAPHORE) */
260 }
261
262 /*
263 * Internal function.
264 * Should be called from an interrupts critical section.
265 */
266 bool
267 semaphore::internal_try_wait_ (void)
268 {
269 if (count_ > 0)
270 {
271#pragma GCC diagnostic push
272#if defined(__clang__)
273#pragma clang diagnostic ignored "-Wdeprecated-volatile"
274#elif defined(__GNUC__)
275#pragma GCC diagnostic ignored "-Wvolatile"
276#endif
277 --count_;
278#pragma GCC diagnostic pop
279
280#if defined(OS_TRACE_RTOS_SEMAPHORE)
281 trace::printf ("%s() @%p %s >%u\n", __func__, this, name (), count_);
282#endif
283 return true;
284 }
285
286 // Count may be 0.
287#if defined(OS_TRACE_RTOS_SEMAPHORE)
288 trace::printf ("%s() @%p %s false\n", __func__, this, name ());
289#endif
290 return false;
291 }
292
340 {
341
342#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
343
344#if defined(OS_TRACE_RTOS_SEMAPHORE)
345 trace::printf ("%s() @%p %s\n", __func__, this, name ());
346#endif
347
348 return port::semaphore::post (this);
349
350#else
351
352 // Don't call this from high priority interrupts.
353 assert (port::interrupts::is_priority_valid ());
354
355 {
356 // ----- Enter critical section ---------------------------------------
358
359 if (count_ >= this->max_value_)
360 {
361#if defined(OS_TRACE_RTOS_SEMAPHORE)
362 trace::printf ("%s() @%p %s EAGAIN\n", __func__, this, name ());
363#endif
364 return EAGAIN;
365 }
366
367#pragma GCC diagnostic push
368#if defined(__clang__)
369#pragma clang diagnostic ignored "-Wdeprecated-volatile"
370#elif defined(__GNUC__)
371#pragma GCC diagnostic ignored "-Wvolatile"
372#endif
373 ++count_;
374#pragma GCC diagnostic pop
375
376#if defined(OS_TRACE_RTOS_SEMAPHORE)
377 trace::printf ("%s() @%p %s count %u\n", __func__, this, name (),
378 count_);
379#endif
380 // ----- Exit critical section ----------------------------------------
381 }
382
383 // Wake-up one thread.
384 list_.resume_one ();
385
386 return result::ok;
387
388#endif
389 }
390
425 {
426#if defined(OS_TRACE_RTOS_SEMAPHORE)
427 trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
428#endif
429
430 // Don't call this from interrupt handlers.
432 // Don't call this from critical regions.
433 os_assert_err (!scheduler::locked (), EPERM);
434
435#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
436
437 return port::semaphore::wait (this);
438
439#else
440
441 // Extra test before entering the loop, with its inherent weight.
442 // Trade size for speed.
443 {
444 // ----- Enter critical section ---------------------------------------
446
447 if (internal_try_wait_ ())
448 {
449 return result::ok;
450 }
451 // ----- Exit critical section ----------------------------------------
452 }
453
454 thread& crt_thread = this_thread::thread ();
455
456 // Prepare a list node pointing to the current thread.
457 // Do not worry for being on stack, it is temporarily linked to the
458 // list and guaranteed to be removed before this function returns.
459 internal::waiting_thread_node node{ crt_thread };
460
461 for (;;)
462 {
463 {
464 // ----- Enter critical section -----------------------------------
466
467 if (internal_try_wait_ ())
468 {
469 return result::ok;
470 }
471
472 // Add this thread to the semaphore waiting list.
473 scheduler::internal_link_node (list_, node);
474 // state::suspended set in above link().
475 // ----- Exit critical section ------------------------------------
476 }
477
479
480 // Remove the thread from the semaphore waiting list,
481 // if not already removed by post().
482 scheduler::internal_unlink_node (node);
483
484 if (crt_thread.interrupted ())
485 {
486#if defined(OS_TRACE_RTOS_SEMAPHORE)
487 trace::printf ("%s() EINTR @%p %s\n", __func__, this, name ());
488#endif
489 return EINTR;
490 }
491 }
492
493 /* NOTREACHED */
494 return ENOTRECOVERABLE;
495
496#endif
497 }
498
525 {
526#if defined(OS_TRACE_RTOS_SEMAPHORE)
527 trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
528#endif
529
530 // Don't call this from high priority interrupts.
531 assert (port::interrupts::is_priority_valid ());
532
533#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
534
535 return port::semaphore::try_wait (this);
536
537#else
538
539 {
540 // ----- Enter critical section ---------------------------------------
542
543 if (internal_try_wait_ ())
544 {
545 return result::ok;
546 }
547 else
548 {
549 return EWOULDBLOCK;
550 }
551 // ----- Exit critical section ----------------------------------------
552 }
553
554#endif
555 }
556
599 {
600#if defined(OS_TRACE_RTOS_SEMAPHORE)
601#pragma GCC diagnostic push
602#if defined(__clang__)
603#elif defined(__GNUC__)
604#pragma GCC diagnostic ignored "-Wuseless-cast"
605#endif
606 trace::printf ("%s(%u) @%p %s <%u\n", __func__,
607 static_cast<unsigned int> (timeout), this, name (),
608 count_);
609#pragma GCC diagnostic pop
610#endif
611
612 // Don't call this from interrupt handlers.
614 // Don't call this from critical regions.
615 os_assert_err (!scheduler::locked (), EPERM);
616
617#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
618
619 return port::semaphore::timed_wait (this, timeout);
620
621#else
622
623 // Extra test before entering the loop, with its inherent weight.
624 // Trade size for speed.
625 {
626 // ----- Enter critical section ---------------------------------------
628
629 if (internal_try_wait_ ())
630 {
631 return result::ok;
632 }
633 // ----- Exit critical section ----------------------------------------
634 }
635
636 thread& crt_thread = this_thread::thread ();
637
638 // Prepare a list node pointing to the current thread.
639 // Do not worry for being on stack, it is temporarily linked to the
640 // list and guaranteed to be removed before this function returns.
641 internal::waiting_thread_node node{ crt_thread };
642
643 internal::clock_timestamps_list& clock_list = clock_->steady_list ();
644 clock::timestamp_t timeout_timestamp = clock_->steady_now () + timeout;
645
646 // Prepare a timeout node pointing to the current thread.
647 internal::timeout_thread_node timeout_node{ timeout_timestamp,
648 crt_thread };
649
650 for (;;)
651 {
652 {
653 // ----- Enter critical section -----------------------------------
655
656 if (internal_try_wait_ ())
657 {
658 return result::ok;
659 }
660
661 // Add this thread to the semaphore waiting list,
662 // and the clock timeout list.
663 scheduler::internal_link_node (list_, node, clock_list,
664 timeout_node);
665 // state::suspended set in above link().
666 // ----- Exit critical section ------------------------------------
667 }
668
670
671 // Remove the thread from the semaphore waiting list,
672 // if not already removed by post() and from the clock
673 // timeout list, if not already removed by the timer.
674 scheduler::internal_unlink_node (node, timeout_node);
675
676 if (crt_thread.interrupted ())
677 {
678#if defined(OS_TRACE_RTOS_SEMAPHORE)
679#pragma GCC diagnostic push
680#if defined(__clang__)
681#elif defined(__GNUC__)
682#pragma GCC diagnostic ignored "-Wuseless-cast"
683#endif
684 trace::printf ("%s(%u) EINTR @%p %s\n", __func__,
685 static_cast<unsigned int> (timeout), this,
686 name ());
687#pragma GCC diagnostic pop
688#endif
689 return EINTR;
690 }
691
692 if (clock_->steady_now () >= timeout_timestamp)
693 {
694#if defined(OS_TRACE_RTOS_SEMAPHORE)
695#pragma GCC diagnostic push
696#if defined(__clang__)
697#elif defined(__GNUC__)
698#pragma GCC diagnostic ignored "-Wuseless-cast"
699#endif
700 trace::printf ("%s(%u) ETIMEDOUT @%p %s\n", __func__,
701 static_cast<unsigned int> (timeout), this,
702 name ());
703#pragma GCC diagnostic pop
704#endif
705 return ETIMEDOUT;
706 }
707 }
708
709 /* NOTREACHED */
710 return ENOTRECOVERABLE;
711
712#endif
713 }
714
740 semaphore::value (void) const
741 {
742#if !defined(OS_USE_RTOS_PORT_SEMAPHORE)
743 return (count_ > 0) ? count_ : 0;
744#else
745 return count_;
746#endif
747 }
748
760 {
761#if defined(OS_TRACE_RTOS_SEMAPHORE)
762 trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
763#endif
764
765 // Don't call this from interrupt handlers.
767
768#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
769
770 return port::semaphore::reset (this);
771
772#else
773
774 {
775 // ----- Enter critical section ---------------------------------------
777
778 internal_init_ ();
779 return result::ok;
780 // ----- Exit critical section ----------------------------------------
781 }
782
783#endif
784 }
785
786 // ------------------------------------------------------------------------
787
788 } /* namespace rtos */
789} /* namespace os */
790
791// ----------------------------------------------------------------------------
Ordered list of time stamp nodes.
Definition os-lists.h:666
const char * name(void) const
Get object name.
Definition os-decls.h:753
Double linked list node, with time stamp and thread.
Definition os-lists.h:220
Double linked list node, with thread reference.
Definition os-lists.h:59
Interrupts critical section RAII helper.
Definition os-sched.h:502
Semaphore attributes.
POSIX compliant semaphore.
result_t timed_wait(clock::duration_t timeout)
Timed wait to lock the semaphore.
result_t wait(void)
Lock the semaphore, possibly waiting.
result_t reset(void)
Reset the semaphore.
result_t post(void)
Post (unlock) the semaphore.
result_t try_wait(void)
Try to lock the semaphore.
~semaphore()
Destruct the semaphore object instance.
semaphore(const attributes &attr=initializer_binary)
Construct a semaphore object instance.
count_t initial_value(void) const
Get the semaphore initial count value.
count_t value(void) const
Get the semaphore count value.
POSIX compliant thread, using the default RTOS allocator.
Definition os-thread.h:251
bool interrupted(void)
Check if interrupted.
Definition os-thread.h:2373
#define OS_USE_RTOS_PORT_SEMAPHORE
Use a custom semaphore implementation.
int printf(const char *format,...)
Write a formatted string to the trace device.
Definition trace.cpp:59
port::clock::duration_t duration_t
Type of variables holding clock durations.
Definition os-clocks.h:78
port::clock::timestamp_t timestamp_t
Type of variables holding clock time stamps.
Definition os-clocks.h:88
clock_systick sysclock
The system clock object instance.
static const attributes_binary initializer_binary
Default binary semaphore initialiser.
int16_t count_t
Type of semaphore counter storage.
bool in_handler_mode(void)
Check if the CPU is in handler mode.
Definition os-sched.h:1101
@ ok
Function completed; no errors or events occurred.
Definition os-decls.h:179
bool locked(void)
Check if the scheduler is locked.
Definition os-sched.h:858
thread & thread(void)
Get the current running thread.
uint32_t result_t
Type of values returned by RTOS functions.
Definition os-decls.h:95
System namespace.
#define os_assert_throw(__e, __er)
Assert or throw a system error exception.
Definition os-decls.h:1122
#define os_assert_err(__e, __er)
Assert or return an error.
Definition os-decls.h:1101
Single file µOS++ RTOS definitions.