µ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++ distribution.
3 * (https://github.com/micro-os-plus)
4 * Copyright (c) 2016-2023 Liviu Ionescu. All rights reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software
7 * for any purpose is hereby granted, under the terms of the MIT license.
8 *
9 * If a copy of the license was not distributed with this file, it can
10 * be obtained from https://opensource.org/licenses/mit/.
11 */
12
13#if defined(OS_USE_OS_APP_CONFIG_H)
14#include <cmsis-plus/os-app-config.h>
15#endif
16
17#include <cmsis-plus/rtos/os.h>
18
19// ----------------------------------------------------------------------------
20
21#if defined(__clang__)
22#pragma clang diagnostic ignored "-Wc++98-compat"
23#endif
24
25// ----------------------------------------------------------------------------
26
27namespace os
28{
29 namespace rtos
30 {
31 // ------------------------------------------------------------------------
32
86 const semaphore::attributes_binary semaphore::initializer_binary
87 { 0 };
88
89 // ------------------------------------------------------------------------
90
150 semaphore::semaphore (const char* name, const attributes& attr) :
152 { name, attr.sm_max_value, attr.sm_initial_value, attr }
153 {
154 }
155
160 semaphore::semaphore (const char* name, const count_t max_value,
161 const count_t initial_value, const attributes& attr
163 __attribute__((unused))
164#endif
165 ) :
166 object_named_system
167 { name }, //
168 max_value_ (max_value), //
169 initial_value_ (initial_value)
170 {
171#if defined(OS_TRACE_RTOS_SEMAPHORE)
172 trace::printf ("%s() @%p %s %u %u\n", __func__, this, this->name (),
173 initial_value, max_value_);
174#endif
175
176 // Don't call this from interrupt handlers.
178
179 // The CMSIS validator requires the max_value to be equal to
180 // the initial count, which can be 0, but we patch it on the way.
181 assert(max_value_ > 0);
182 assert(initial_value >= 0);
183 assert(initial_value <= max_value_);
184
185 count_ = initial_value;
186
187#if !defined(OS_USE_RTOS_PORT_SEMAPHORE)
188 clock_ = attr.clock != nullptr ? attr.clock : &sysclock;
189#endif
190
191#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
192
193 port::semaphore::create (this);
194
195#else
196
197 internal_init_ ();
198
199#endif
200 }
201
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
336 {
337
338#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
339
340#if defined(OS_TRACE_RTOS_SEMAPHORE)
341 trace::printf ("%s() @%p %s\n", __func__, this, name ());
342#endif
343
344 return port::semaphore::post (this);
345
346#else
347
348 // Don't call this from high priority interrupts.
349 assert(port::interrupts::is_priority_valid ());
350
351 {
352 // ----- Enter critical section -------------------------------------
354
355 if (count_ >= this->max_value_)
356 {
357#if defined(OS_TRACE_RTOS_SEMAPHORE)
358 trace::printf ("%s() @%p %s EAGAIN\n", __func__, this, name ());
359#endif
360 return EAGAIN;
361 }
362
363#pragma GCC diagnostic push
364#if defined(__clang__)
365#pragma clang diagnostic ignored "-Wdeprecated-volatile"
366#elif defined(__GNUC__)
367#pragma GCC diagnostic ignored "-Wvolatile"
368#endif
369 ++count_;
370#pragma GCC diagnostic pop
371
372#if defined(OS_TRACE_RTOS_SEMAPHORE)
373 trace::printf ("%s() @%p %s count %u\n", __func__, this, name (),
374 count_);
375#endif
376 // ----- Exit critical section --------------------------------------
377 }
378
379 // Wake-up one thread.
380 list_.resume_one ();
381
382 return result::ok;
383
384#endif
385 }
386
417 {
418#if defined(OS_TRACE_RTOS_SEMAPHORE)
419 trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
420#endif
421
422 // Don't call this from interrupt handlers.
424 // Don't call this from critical regions.
426
427#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
428
429 return port::semaphore::wait (this);
430
431#else
432
433 // Extra test before entering the loop, with its inherent weight.
434 // Trade size for speed.
435 {
436 // ----- Enter critical section -------------------------------------
438
439 if (internal_try_wait_ ())
440 {
441 return result::ok;
442 }
443 // ----- Exit critical section --------------------------------------
444 }
445
446 thread& crt_thread = this_thread::thread ();
447
448 // Prepare a list node pointing to the current thread.
449 // Do not worry for being on stack, it is temporarily linked to the
450 // list and guaranteed to be removed before this function returns.
452 { crt_thread };
453
454 for (;;)
455 {
456 {
457 // ----- Enter critical section ---------------------------------
459
460 if (internal_try_wait_ ())
461 {
462 return result::ok;
463 }
464
465 // Add this thread to the semaphore waiting list.
466 scheduler::internal_link_node (list_, node);
467 // state::suspended set in above link().
468 // ----- Exit critical section ----------------------------------
469 }
470
472
473 // Remove the thread from the semaphore waiting list,
474 // if not already removed by post().
475 scheduler::internal_unlink_node (node);
476
477 if (crt_thread.interrupted ())
478 {
479#if defined(OS_TRACE_RTOS_SEMAPHORE)
480 trace::printf ("%s() EINTR @%p %s\n", __func__, this, name ());
481#endif
482 return EINTR;
483 }
484 }
485
486 /* NOTREACHED */
487 return ENOTRECOVERABLE;
488
489#endif
490 }
491
514 {
515#if defined(OS_TRACE_RTOS_SEMAPHORE)
516 trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
517#endif
518
519 // Don't call this from high priority interrupts.
520 assert(port::interrupts::is_priority_valid ());
521
522#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
523
524 return port::semaphore::try_wait (this);
525
526#else
527
528 {
529 // ----- Enter critical section -------------------------------------
531
532 if (internal_try_wait_ ())
533 {
534 return result::ok;
535 }
536 else
537 {
538 return EWOULDBLOCK;
539 }
540 // ----- Exit critical section --------------------------------------
541 }
542
543#endif
544 }
545
584 {
585#if defined(OS_TRACE_RTOS_SEMAPHORE)
586#pragma GCC diagnostic push
587#if defined(__clang__)
588#elif defined(__GNUC__)
589#pragma GCC diagnostic ignored "-Wuseless-cast"
590#endif
591 trace::printf ("%s(%u) @%p %s <%u\n", __func__,
592 static_cast<unsigned int> (timeout), this, name (),
593 count_);
594#pragma GCC diagnostic pop
595#endif
596
597 // Don't call this from interrupt handlers.
599 // Don't call this from critical regions.
601
602#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
603
604 return port::semaphore::timed_wait (this, timeout);
605
606#else
607
608 // Extra test before entering the loop, with its inherent weight.
609 // Trade size for speed.
610 {
611 // ----- Enter critical section -------------------------------------
613
614 if (internal_try_wait_ ())
615 {
616 return result::ok;
617 }
618 // ----- Exit critical section --------------------------------------
619 }
620
621 thread& crt_thread = this_thread::thread ();
622
623 // Prepare a list node pointing to the current thread.
624 // Do not worry for being on stack, it is temporarily linked to the
625 // list and guaranteed to be removed before this function returns.
627 { crt_thread };
628
629 internal::clock_timestamps_list& clock_list = clock_->steady_list ();
630 clock::timestamp_t timeout_timestamp = clock_->steady_now () + timeout;
631
632 // Prepare a timeout node pointing to the current thread.
634 { timeout_timestamp, crt_thread };
635
636 for (;;)
637 {
638 {
639 // ----- Enter critical section ---------------------------------
641
642 if (internal_try_wait_ ())
643 {
644 return result::ok;
645 }
646
647 // Add this thread to the semaphore waiting list,
648 // and the clock timeout list.
649 scheduler::internal_link_node (list_, node, clock_list,
650 timeout_node);
651 // state::suspended set in above link().
652 // ----- Exit critical section ----------------------------------
653 }
654
656
657 // Remove the thread from the semaphore waiting list,
658 // if not already removed by post() and from the clock
659 // timeout list, if not already removed by the timer.
660 scheduler::internal_unlink_node (node, timeout_node);
661
662 if (crt_thread.interrupted ())
663 {
664#if defined(OS_TRACE_RTOS_SEMAPHORE)
665#pragma GCC diagnostic push
666#if defined(__clang__)
667#elif defined(__GNUC__)
668#pragma GCC diagnostic ignored "-Wuseless-cast"
669#endif
670 trace::printf ("%s(%u) EINTR @%p %s\n", __func__,
671 static_cast<unsigned int> (timeout), this,
672 name ());
673#pragma GCC diagnostic pop
674#endif
675 return EINTR;
676 }
677
678 if (clock_->steady_now () >= timeout_timestamp)
679 {
680#if defined(OS_TRACE_RTOS_SEMAPHORE)
681#pragma GCC diagnostic push
682#if defined(__clang__)
683#elif defined(__GNUC__)
684#pragma GCC diagnostic ignored "-Wuseless-cast"
685#endif
686 trace::printf ("%s(%u) ETIMEDOUT @%p %s\n", __func__,
687 static_cast<unsigned int> (timeout), this,
688 name ());
689#pragma GCC diagnostic pop
690#endif
691 return ETIMEDOUT;
692 }
693 }
694
695 /* NOTREACHED */
696 return ENOTRECOVERABLE;
697
698#endif
699 }
700
723 semaphore::value (void) const
724 {
725#if !defined(OS_USE_RTOS_PORT_SEMAPHORE)
726 return (count_ > 0) ? count_ : 0;
727#else
728 return count_;
729#endif
730 }
731
743 {
744#if defined(OS_TRACE_RTOS_SEMAPHORE)
745 trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
746#endif
747
748 // Don't call this from interrupt handlers.
750
751#if defined(OS_USE_RTOS_PORT_SEMAPHORE)
752
753 return port::semaphore::reset (this);
754
755#else
756
757 {
758 // ----- Enter critical section -------------------------------------
760
761 internal_init_ ();
762 return result::ok;
763 // ----- Exit critical section --------------------------------------
764 }
765
766#endif
767 }
768
769 // --------------------------------------------------------------------------
770
771 } /* namespace rtos */
772} /* namespace os */
773
774// ----------------------------------------------------------------------------
Ordered list of time stamp nodes.
Definition os-lists.h:671
const char * name(void) const
Get object name.
Definition os-decls.h:759
Double linked list node, with time stamp and thread.
Definition os-lists.h:223
Double linked list node, with thread reference.
Definition os-lists.h:60
Interrupts critical section RAII helper.
Definition os-sched.h:498
Semaphore attributes.
count_t sm_max_value
Semaphore max count value.
count_t sm_initial_value
Semaphore initial count value.
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 value(void) const
Get the semaphore count value.
POSIX compliant thread, using the default RTOS allocator.
Definition os-thread.h:250
bool interrupted(void)
Check if interrupted.
Definition os-thread.h:2367
#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:60
port::clock::duration_t duration_t
Type of variables holding clock durations.
Definition os-clocks.h:76
port::clock::timestamp_t timestamp_t
Type of variables holding clock time stamps.
Definition os-clocks.h:85
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:1108
@ ok
Function completed; no errors or events occurred.
Definition os-decls.h:181
bool locked(void)
Check if the scheduler is locked.
Definition os-sched.h:856
thread & thread(void)
Get the current running thread.
uint32_t result_t
Type of values returned by RTOS functions.
Definition os-decls.h:96
System namespace.
#define os_assert_throw(__e, __er)
Assert or throw a system error exception.
Definition os-decls.h:1130
#define os_assert_err(__e, __er)
Assert or return an error.
Definition os-decls.h:1115
Single file µOS++ RTOS definitions.