µ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-core.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
33 {
34 #if defined(OS_HAS_INTERRUPTS_STACK)
35 // Object used to manage the interrupts stack.
36  class os::rtos::thread::stack interrupts_stack;
37 #endif /* defined(OS_HAS_INTERRUPTS_STACK) */
38  ;
39 // Avoid formatter bug
40 }
41 
42 namespace os
43 {
49  namespace rtos
50  {
51 
57  namespace scheduler
58  {
68  bool is_started_ = false;
69 
70 #if 0
71 
76  state_t lock_state_ = state::init;
77 #endif
78 
79 #pragma GCC diagnostic push
80 #if defined(__clang__)
81 #pragma clang diagnostic ignored "-Wglobal-constructors"
82 #pragma clang diagnostic ignored "-Wexit-time-destructors"
83 #endif
84 
90  // top_threads_list top_threads_list_;
91  thread::threads_list top_threads_list_;
92 
93 #pragma GCC diagnostic pop
94 
95 #if !defined(OS_USE_RTOS_PORT_SCHEDULER)
96 
97  bool is_preemptive_ = false;
98 
99 #pragma GCC diagnostic push
100 #pragma GCC diagnostic ignored "-Wpadded"
101  // A small kludge to provide a temporary errno before
102  // the first real thread is created.
103  typedef struct {
104  void* vtbl;
105  void* name_;
106  // errno is the first thread member, so right after the name.
107  int errno_;
108  } tiny_thread_t;
109 #pragma GCC diagnostic pop
110 
111  // Ensure the tiny thread is large enough to have the errno
112  // member in the same location.
113 #pragma GCC diagnostic push
114 #pragma GCC diagnostic ignored "-Winvalid-offsetof"
115  static_assert(offsetof(tiny_thread_t, errno_) == offsetof(thread, errno_), "adjust tiny_thread_t members");
116 #pragma GCC diagnostic pop
117 
118 #pragma GCC diagnostic push
119 #if defined(__clang__)
120 #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
121 #endif
122  tiny_thread_t tiny_thread;
123 #pragma GCC diagnostic pop
124 
125  thread* volatile current_thread_ = reinterpret_cast<thread*>(&tiny_thread);
126 
127 #pragma GCC diagnostic push
128 #if defined(__clang__)
129 #pragma clang diagnostic ignored "-Wglobal-constructors"
130 #pragma clang diagnostic ignored "-Wexit-time-destructors"
131 #endif
132  internal::ready_threads_list ready_threads_list_;
133 #pragma GCC diagnostic pop
134 #endif
135 
136 #pragma GCC diagnostic push
137 #if defined(__clang__)
138 #pragma clang diagnostic ignored "-Wglobal-constructors"
139 #pragma clang diagnostic ignored "-Wexit-time-destructors"
140 #endif
141  internal::terminated_threads_list terminated_threads_list_;
142 #pragma GCC diagnostic pop
143 
156  result_t
157  initialize (void)
158  {
159 #if defined(OS_TRACE_RTOS_SCHEDULER)
160  trace::printf ("scheduler::%s() \n", __func__);
161 #endif
162 
163  // Don't call this from interrupt handlers.
165 
166 #if defined(OS_USE_RTOS_PORT_SCHEDULER)
167 
168  return port::scheduler::initialize ();
169 
170 #else
171 
173 
174  return result::ok;
175 #endif
176  }
177 
185  [[noreturn]] void
186  start (void)
187  {
188  trace::printf ("scheduler::%s() \n", __func__);
189 
190  // Don't call this from interrupt handlers.
192 
193  sysclock.start ();
194  hrclock.start ();
195 
196  rtclock.start ();
197 
198 #if defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CONTEXT_SWITCHES)
199 
200  scheduler::statistics::context_switches_ = 0;
201 
202 #endif /* defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CONTEXT_SWITCHES) */
203 
204 #if defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CPU_CYCLES)
205 
206  scheduler::statistics::cpu_cycles_ = 0;
207  scheduler::statistics::switch_timestamp_ = hrclock.now ();
208 
209 #endif /* defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CPU_CYCLES) */
210 
211 #if !defined(OS_USE_RTOS_PORT_SCHEDULER)
212  is_preemptive_ = OS_BOOL_RTOS_SCHEDULER_PREEMPTIVE;
213 #endif /* defined(OS_USE_RTOS_PORT_SCHEDULER) */
214 
215  is_started_ = true;
216 
218  }
219 
225  bool
226  preemptive (bool state)
227  {
228 #if defined(OS_TRACE_RTOS_SCHEDULER)
229  trace::printf ("scheduler::%s(%d) \n", __func__, state);
230 #endif
231  // Don't call this from interrupt handlers.
233 
234 #if defined(OS_USE_RTOS_PORT_SCHEDULER)
235 
236  return port::scheduler::preemptive(state);
237 
238 #else
239  bool tmp;
240 
241  {
242  // ----- Enter critical section -----------------------------------
244 
245  tmp = is_preemptive_;
246  is_preemptive_ = state;
247  // ----- Exit critical section ------------------------------------
248  }
249 
250  return tmp;
251 #endif
252  }
253 
259  thread::threads_list&
261  {
262  if (th == nullptr)
263  {
264  return top_threads_list_;
265  }
266  else
267  {
268  return th->children_;
269  }
270  }
271 
307  /*
308  * @var const state_t critical_section::state_
309  * @details
310  * The variable is constant, after being set by the constructor no
311  * further changes are possible.
312  *
313  * The variable type usually is a `bool`, but a counter is also
314  * possible if the scheduler uses a recursive lock.
315  */
316 
323  } /* namespace scheduler */
324 
325  namespace scheduler
326  {
331  void
332  internal_link_node (internal::waiting_threads_list& list,
334  {
335  // Remove this thread from the ready list, if there.
337 
338  // Add this thread to the node waiting list.
339  list.link (node);
340  node.thread_->waiting_node_ = &node;
341 
342  node.thread_->state_ = thread::state::suspended;
343  }
344 
345  void
346  internal_unlink_node (internal::waiting_thread_node& node)
347  {
348  {
349  // ----- Enter critical section -----------------------------------
351 
352  // Remove the thread from the node waiting list,
353  // if not already removed.
354  node.thread_->waiting_node_ = nullptr;
355  node.unlink ();
356  // ----- Exit critical section ------------------------------------
357  }
358  }
359 
360  void
361  internal_link_node (internal::waiting_threads_list& list,
363  internal::clock_timestamps_list& timeout_list,
364  internal::timeout_thread_node& timeout_node)
365  {
366  // Remove this thread from the ready list, if there.
368 
369  // Add this thread to the node waiting list.
370  list.link (node);
371  node.thread_->waiting_node_ = &node;
372 
373  node.thread_->state_ = thread::state::suspended;
374 
375  // Add this thread to the clock timeout list.
376  timeout_list.link (timeout_node);
377  timeout_node.thread.clock_node_ = &timeout_node;
378  }
379 
380  void
381  internal_unlink_node (internal::waiting_thread_node& node,
382  internal::timeout_thread_node& timeout_node)
383  {
384  // ----- Enter critical section ---------------------------------------
386 
387  // Remove the thread from the clock timeout list,
388  // if not already removed by the timer.
389  timeout_node.thread.clock_node_ = nullptr;
390  timeout_node.unlink ();
391 
392  // Remove the thread from the node waiting list,
393  // if not already removed.
394  node.thread_->waiting_node_ = nullptr;
395  node.unlink ();
396  // ----- Exit critical section ----------------------------------------
397  }
398 
399  // ----------------------------------------------------------------------
400 
401 #if !defined(OS_USE_RTOS_PORT_SCHEDULER)
402 
403  void
404  internal_switch_threads (void)
405  {
406 #if defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CPU_CYCLES)
407 
408  // Get the high resolution timestamp.
409  clock::timestamp_t now = hrclock.now ();
410 
411  // Compute duration since previous context switch.
412  // Assume scheduler is not disabled for very long.
414  static_cast<rtos::statistics::duration_t> (now
415  - scheduler::statistics::switch_timestamp_);
416 
417  // Accumulate durations to scheduler total.
418  scheduler::statistics::cpu_cycles_ += delta;
419 
420  // Accumulate durations to old thread.
421  scheduler::current_thread_->statistics_.cpu_cycles_ += delta;
422 
423  // Remember the timestamp for the next context switch.
424  scheduler::statistics::switch_timestamp_ = now;
425 
426 #endif /* defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CPU_CYCLES) */
427 
428  // The very core of the scheduler, if not locked, re-link the
429  // current thread and return the top priority thread.
430  if (!locked ())
431  {
432  // Normally the old running thread must be re-linked to ready.
433  scheduler::current_thread_->internal_relink_running_ ();
434 
435  // The top of the ready list gives the next thread to run.
436  scheduler::current_thread_ =
437  scheduler::ready_threads_list_.unlink_head ();
438  }
439 
440  // ***** Pointer switched to new thread! *****
441 
442  // The new thread was marked as running in unlink_head(),
443  // so in case the handler is re-entered immediately,
444  // the relink_running() will simply reschedule it,
445  // otherwise the thread will be lost.
446 
447 #if defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CONTEXT_SWITCHES)
448 
449  // Increment global context switches.
450  scheduler::statistics::context_switches_++;
451 
452  // Increment new thread context switches.
453  scheduler::current_thread_->statistics_.context_switches_++;
454 
455 #endif /* defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CONTEXT_SWITCHES) */
456 
457  }
458 
459 #endif /* !defined(OS_USE_RTOS_PORT_SCHEDULER) */
460 
461  namespace statistics
462  {
463 #if defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CONTEXT_SWITCHES)
464 
465  rtos::statistics::counter_t context_switches_;
466 
467 #endif /* defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CONTEXT_SWITCHES) */
468 
469 #if defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CPU_CYCLES)
470 
471  clock::timestamp_t switch_timestamp_;
472  rtos::statistics::duration_t cpu_cycles_;
473 
474 #endif /* defined(OS_INCLUDE_RTOS_STATISTICS_THREAD_CPU_CYCLES) */
475 
476  } /* namespace statistics */
477 
482  } /* namespace scheduler */
483 
489  namespace interrupts
490  {
526  /*
527  * @var const state_t critical_section::state_
528  * @details
529  * The variable is constant, after being set by the constructor no
530  * further changes are possible.
531  *
532  * The variable type usually is an unsigned integer where
533  * the priorities register is saved.
534  */
535 
542  /*
543  * @var state_t lockable::state_
544  * @details
545  * The variable type usually is an unsigned integer where
546  * the priorities register is saved.
547  */
548 
549 #if defined(OS_HAS_INTERRUPTS_STACK) || defined(__DOXYGEN__)
550 
561  class thread::stack*
562  stack (void)
563  {
564  return &interrupts_stack;
565  }
566 
567 #endif /* defined(OS_HAS_INTERRUPTS_STACK) */
568 
569  ;
570  // Avoid formatter bug.
571  }
572  /* namespace interrupts */
573 
574  // ========================================================================
575  namespace internal
576  {
586  /*
587  * @var const char* const object_named::name_
588  * @details
589  * To save space, the null terminated string passed to the
590  * constructor is not copied locally. Instead, the pointer to
591  * the string is copied, so the
592  * caller must ensure that the pointer life cycle
593  * is at least as long as the object life cycle. A constant
594  * string (stored in flash) is preferred.
595  */
596 
600  object_named::object_named ()
601  {
602  ;
603  }
604 
616  object_named::object_named (const char* name) :
617  name_ (name != nullptr ? name : "-")
618  {
619  ;
620  }
621 
622  } /* namespace internal */
623 
624  // ==========================================================================
625  } /* namespace rtos */
626 } /* namespace os */
627 
628 int*
629 __errno (void);
630 
645 int*
646 __errno (void)
647 {
649 }
650 
#define OS_BOOL_RTOS_SCHEDULER_PREEMPTIVE
Default definition for the preemption flag.
#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
Thread stack.
Definition: os-thread.h:406
void link(timestamp_node &node)
Add a new thread node to the list.
Definition: os-lists.cpp:384
class thread::stack * stack(void)
Get the interrupts stack.
Definition: os-core.cpp:562
void start(void)
Start the RTOS scheduler.
Definition: os-core.cpp:186
rtos::thread & thread
Reference to thread who initiated the timeout.
Definition: os-lists.h:285
virtual void start(void) override
Definition: os-clocks.cpp:584
state_t locked(state_t state)
Lock/unlock the scheduler.
Definition: os-sched.h:899
Priority ordered list of threads.
Definition: os-lists.h:536
Double linked list node, with thread reference.
Definition: os-lists.h:58
int * __errno(void)
Implementation of the library __errno() function.
Definition: os-thread.h:2054
Interrupts critical section RAII helper.
Definition: os-sched.h:498
System namespace.
Double linked list node, with time stamp and thread.
Definition: os-lists.h:213
rtos::thread * thread_
Pointer to waiting thread.
Definition: os-lists.h:107
clock_highres hrclock
The high resolution clock object instance.
Definition: os-clocks.cpp:717
Standard thread.
virtual void start(void) override
Definition: os-clocks.cpp:750
void start(void)
Start the RTOS scheduler.
Definition: os-core.cpp:186
clock_rtc rtclock
The real time clock object instance.
Definition: os-clocks.cpp:660
POSIX compliant thread, using the default RTOS allocator.
Definition: os-thread.h:230
result_t initialize(void)
Initialise the RTOS scheduler.
Definition: trace.cpp:50
port::scheduler::state_t state_t
Type of variables holding scheduler state codes.
Definition: os-decls.h:202
bool preemptive(void)
Check if the scheduler is in preemptive mode.
Definition: os-sched.h:839
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
bool preemptive(void)
Check if the scheduler is in preemptive mode.
Definition: os-sched.h:839
Function completed; no errors or events occurred.
Definition: os-decls.h:181
Not present in the READY list, waiting for an event.
Definition: os-thread.h:370
int * __errno(void)
Per-thread error support.
Definition: os-core.cpp:646
virtual void start(void) override
Initialise and make the RTC tick.
Definition: os-clocks.cpp:696
uint64_t counter_t
Type of variables holding context switches counters.
Definition: os-decls.h:217
virtual timestamp_t now(void) override
Tell the current time.
Definition: os-clocks.cpp:760
Ordered list of time stamp nodes.
Definition: os-lists.h:657
uint32_t result_t
Type of values returned by RTOS functions.
Definition: os-decls.h:96
result_t initialize(void)
Initialise the RTOS scheduler.
Definition: os-core.cpp:157
void link(waiting_thread_node &node)
Add a new thread node to the list.
Definition: os-lists.cpp:182
thread::threads_list & children_threads(thread *th)
Get the children threads.
Definition: os-core.cpp:260
uint64_t duration_t
Type of variables holding durations in CPU cycles.
Definition: os-decls.h:222
#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