µOS++ IIIe Reference  v6.3.15
“Perfekt ist nicht gut genug”
The third edition of µOS++, a POSIX inspired open source system, written in C++.
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 Liviu Ionescu.
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or
11  * sell copies of the Software, and to permit persons to whom
12  * the Software is furnished to do so, subject to the following
13  * conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #include <cmsis-plus/rtos/os.h>
29 
30 // ----------------------------------------------------------------------------
31 
32 namespace os
33 {
34  namespace rtos
35  {
36  // ------------------------------------------------------------------------
37 
88  const semaphore::attributes_binary semaphore::initializer_binary
89  { 0 };
90 
91  // ------------------------------------------------------------------------
92 
149  semaphore::semaphore (const char* name, const attributes& attr) :
150  semaphore
151  { name, attr.sm_max_value, attr.sm_initial_value, attr }
152  {
153  ;
154  }
155 
160  semaphore::semaphore (const char* name, const count_t max_value,
161  const count_t initial_value, const attributes& attr
162 #if defined(OS_USE_RTOS_PORT_SEMAPHORE)
163  __attribute__((unused))
164 #endif
165  ) :
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  --count_;
272 #if defined(OS_TRACE_RTOS_SEMAPHORE)
273  trace::printf ("%s() @%p %s >%u\n", __func__, this, name (), count_);
274 #endif
275  return true;
276  }
277 
278  // Count may be 0.
279 #if defined(OS_TRACE_RTOS_SEMAPHORE)
280  trace::printf ("%s() @%p %s false\n", __func__, this, name ());
281 #endif
282  return false;
283  }
284 
326  result_t
328  {
329 
330 #if defined(OS_USE_RTOS_PORT_SEMAPHORE)
331 
332 #if defined(OS_TRACE_RTOS_SEMAPHORE)
333  trace::printf ("%s() @%p %s\n", __func__, this, name ());
334 #endif
335 
336  return port::semaphore::post (this);
337 
338 #else
339 
340  // Don't call this from high priority interrupts.
341  assert(port::interrupts::is_priority_valid ());
342 
343  {
344  // ----- Enter critical section -------------------------------------
346 
347  if (count_ >= this->max_value_)
348  {
349 #if defined(OS_TRACE_RTOS_SEMAPHORE)
350  trace::printf ("%s() @%p %s EAGAIN\n", __func__, this, name ());
351 #endif
352  return EAGAIN;
353  }
354 
355  ++count_;
356 #if defined(OS_TRACE_RTOS_SEMAPHORE)
357  trace::printf ("%s() @%p %s count %u\n", __func__, this, name (),
358  count_);
359 #endif
360  // ----- Exit critical section --------------------------------------
361  }
362 
363  // Wake-up one thread.
364  list_.resume_one ();
365 
366  return result::ok;
367 
368 #endif
369  }
370 
399  result_t
401  {
402 #if defined(OS_TRACE_RTOS_SEMAPHORE)
403  trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
404 #endif
405 
406  // Don't call this from interrupt handlers.
408  // Don't call this from critical regions.
409  os_assert_err(!scheduler::locked (), EPERM);
410 
411 #if defined(OS_USE_RTOS_PORT_SEMAPHORE)
412 
413  return port::semaphore::wait (this);
414 
415 #else
416 
417  // Extra test before entering the loop, with its inherent weight.
418  // Trade size for speed.
419  {
420  // ----- Enter critical section -------------------------------------
422 
423  if (internal_try_wait_ ())
424  {
425  return result::ok;
426  }
427  // ----- Exit critical section --------------------------------------
428  }
429 
430  thread& crt_thread = this_thread::thread ();
431 
432  // Prepare a list node pointing to the current thread.
433  // Do not worry for being on stack, it is temporarily linked to the
434  // list and guaranteed to be removed before this function returns.
436  { crt_thread };
437 
438  for (;;)
439  {
440  {
441  // ----- Enter critical section ---------------------------------
443 
444  if (internal_try_wait_ ())
445  {
446  return result::ok;
447  }
448 
449  // Add this thread to the semaphore waiting list.
450  scheduler::internal_link_node (list_, node);
451  // state::suspended set in above link().
452  // ----- Exit critical section ----------------------------------
453  }
454 
456 
457  // Remove the thread from the semaphore waiting list,
458  // if not already removed by post().
459  scheduler::internal_unlink_node (node);
460 
461  if (crt_thread.interrupted ())
462  {
463 #if defined(OS_TRACE_RTOS_SEMAPHORE)
464  trace::printf ("%s() EINTR @%p %s\n", __func__, this, name ());
465 #endif
466  return EINTR;
467  }
468  }
469 
470  /* NOTREACHED */
471  return ENOTRECOVERABLE;
472 
473 #endif
474  }
475 
496  result_t
498  {
499 #if defined(OS_TRACE_RTOS_SEMAPHORE)
500  trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
501 #endif
502 
503  // Don't call this from high priority interrupts.
504  assert(port::interrupts::is_priority_valid ());
505 
506 #if defined(OS_USE_RTOS_PORT_SEMAPHORE)
507 
508  return port::semaphore::try_wait (this);
509 
510 #else
511 
512  {
513  // ----- Enter critical section -------------------------------------
515 
516  if (internal_try_wait_ ())
517  {
518  return result::ok;
519  }
520  else
521  {
522  return EWOULDBLOCK;
523  }
524  // ----- Exit critical section --------------------------------------
525  }
526 
527 #endif
528  }
529 
566  result_t
568  {
569 #if defined(OS_TRACE_RTOS_SEMAPHORE)
570  trace::printf ("%s(%u) @%p %s <%u\n", __func__,
571  static_cast<unsigned int> (timeout), this, name (),
572  count_);
573 #endif
574 
575  // Don't call this from interrupt handlers.
577  // Don't call this from critical regions.
578  os_assert_err(!scheduler::locked (), EPERM);
579 
580 #if defined(OS_USE_RTOS_PORT_SEMAPHORE)
581 
582  return port::semaphore::timed_wait (this, timeout);
583 
584 #else
585 
586  // Extra test before entering the loop, with its inherent weight.
587  // Trade size for speed.
588  {
589  // ----- Enter critical section -------------------------------------
591 
592  if (internal_try_wait_ ())
593  {
594  return result::ok;
595  }
596  // ----- Exit critical section --------------------------------------
597  }
598 
599  thread& crt_thread = this_thread::thread ();
600 
601  // Prepare a list node pointing to the current thread.
602  // Do not worry for being on stack, it is temporarily linked to the
603  // list and guaranteed to be removed before this function returns.
605  { crt_thread };
606 
607  internal::clock_timestamps_list& clock_list = clock_->steady_list ();
608  clock::timestamp_t timeout_timestamp = clock_->steady_now () + timeout;
609 
610  // Prepare a timeout node pointing to the current thread.
611  internal::timeout_thread_node timeout_node
612  { timeout_timestamp, crt_thread };
613 
614  for (;;)
615  {
616  {
617  // ----- Enter critical section ---------------------------------
619 
620  if (internal_try_wait_ ())
621  {
622  return result::ok;
623  }
624 
625  // Add this thread to the semaphore waiting list,
626  // and the clock timeout list.
627  scheduler::internal_link_node (list_, node, clock_list,
628  timeout_node);
629  // state::suspended set in above link().
630  // ----- Exit critical section ----------------------------------
631  }
632 
634 
635  // Remove the thread from the semaphore waiting list,
636  // if not already removed by post() and from the clock
637  // timeout list, if not already removed by the timer.
638  scheduler::internal_unlink_node (node, timeout_node);
639 
640  if (crt_thread.interrupted ())
641  {
642 #if defined(OS_TRACE_RTOS_SEMAPHORE)
643  trace::printf ("%s(%u) EINTR @%p %s\n", __func__,
644  static_cast<unsigned int> (timeout), this,
645  name ());
646 #endif
647  return EINTR;
648  }
649 
650  if (clock_->steady_now () >= timeout_timestamp)
651  {
652 #if defined(OS_TRACE_RTOS_SEMAPHORE)
653  trace::printf ("%s(%u) ETIMEDOUT @%p %s\n", __func__,
654  static_cast<unsigned int> (timeout), this,
655  name ());
656 #endif
657  return ETIMEDOUT;
658  }
659  }
660 
661  /* NOTREACHED */
662  return ENOTRECOVERABLE;
663 
664 #endif
665  }
666 
689  semaphore::value (void) const
690  {
691 #if !defined(OS_USE_RTOS_PORT_SEMAPHORE)
692  return (count_ > 0) ? count_ : 0;
693 #else
694  return count_;
695 #endif
696  }
697 
707  result_t
709  {
710 #if defined(OS_TRACE_RTOS_SEMAPHORE)
711  trace::printf ("%s() @%p %s <%u\n", __func__, this, name (), count_);
712 #endif
713 
714  // Don't call this from interrupt handlers.
716 
717 #if defined(OS_USE_RTOS_PORT_SEMAPHORE)
718 
719  return port::semaphore::reset (this);
720 
721 #else
722 
723  {
724  // ----- Enter critical section -------------------------------------
726 
727  internal_init_ ();
728  return result::ok;
729  // ----- Exit critical section --------------------------------------
730  }
731 
732 #endif
733  }
734 
735  // --------------------------------------------------------------------------
736 
737  } /* namespace rtos */
738 } /* namespace os */
#define os_assert_throw(__e, __er)
Assert or throw a system error exception.
Definition: os-decls.h:1127
port::clock::timestamp_t timestamp_t
Type of variables holding clock time stamps.
Definition: os-clocks.h:81
count_t sm_initial_value
Semaphore initial count value.
Definition: os-semaphore.h:147
result_t post(void)
Post (unlock) the semaphore.
state_t locked(state_t state)
Lock/unlock the scheduler.
Definition: os-sched.h:899
result_t reset(void)
Reset the semaphore.
POSIX compliant semaphore.
Definition: os-semaphore.h:53
Double linked list node, with thread reference.
Definition: os-lists.h:58
Interrupts critical section RAII helper.
Definition: os-sched.h:498
semaphore(const attributes &attr=initializer_binary)
Construct a semaphore object instance.
Definition: os-semaphore.h:737
pid_t wait(int *stat_loc)
System namespace.
Double linked list node, with time stamp and thread.
Definition: os-lists.h:213
static const attributes_binary initializer_binary
Default binary semaphore initialiser.
Definition: os-semaphore.h:202
object_named_system()
Construct a named system object instance.
Definition: os-decls.h:768
const char * name(void) const
Get object name.
Definition: os-decls.h:760
count_t sm_max_value
Semaphore max count value.
Definition: os-semaphore.h:142
POSIX compliant thread, using the default RTOS allocator.
Definition: os-thread.h:230
port::clock::duration_t duration_t
Type of variables holding clock durations.
Definition: os-clocks.h:72
result_t wait(void)
Lock the semaphore, possibly waiting.
Single file µOS++ RTOS definitions.
clock_systick sysclock
The system clock object instance.
Definition: os-clocks.cpp:551
int printf(const char *format,...)
Write a formatted string to the trace device.
Definition: trace.cpp:74
Function completed; no errors or events occurred.
Definition: os-decls.h:181
count_t initial_value(void) const
Get the semaphore initial count value.
Definition: os-semaphore.h:762
int16_t count_t
Type of semaphore counter storage.
Definition: os-semaphore.h:64
~semaphore()
Destruct the semaphore object instance.
Semaphore attributes.
Definition: os-semaphore.h:81
result_t try_wait(void)
Try to lock the semaphore.
#define OS_USE_RTOS_PORT_SEMAPHORE
Use a custom semaphore implementation.
Ordered list of time stamp nodes.
Definition: os-lists.h:657
thread & thread(void)
Get the current running thread.
Definition: os-thread.cpp:1543
count_t value(void) const
Get the semaphore count value.
uint32_t result_t
Type of values returned by RTOS functions.
Definition: os-decls.h:96
result_t timed_wait(clock::duration_t timeout)
Timed wait to lock the semaphore.
bool interrupted(void)
Check if interrupted.
Definition: os-thread.h:2326
count_t max_value(void) const
Get the semaphore maximum count value.
Definition: os-semaphore.h:775
USB switch to High Speed occurred.
Definition: usb-device.h:142
#define os_assert_err(__e, __er)
Assert or return an error.
Definition: os-decls.h:1112
bool in_handler_mode(void)
Check if the CPU is in handler mode.
Definition: os-sched.h:1091