µ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-lists.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 #include <cmsis-plus/diag/trace.h>
31 
32 namespace os
33 {
34  namespace rtos
35  {
36  namespace internal
37  {
38  // ======================================================================
39 
40  void
42  {
43  // Add thread intrusive node at the end of the list.
44  insert_after (thread.child_links_,
45  const_cast<utils::static_double_list_links*> (tail ()));
46  }
47 
48  // ======================================================================
49 
50  void
52  {
53  if (head_.prev () == nullptr)
54  {
55  // If this is the first time, initialise the list to empty.
56  clear ();
57  }
58 
59  thread::priority_t prio = node.thread_->priority ();
60 
61  waiting_thread_node* after =
62  static_cast<waiting_thread_node*> (const_cast<utils::static_double_list_links *> (tail ()));
63 
64  if (empty ())
65  {
66  // Insert at the end of the list.
67 #if defined(OS_TRACE_RTOS_LISTS)
68  trace::printf ("ready %s() empty +%u\n", __func__, prio);
69 #endif
70  }
71  else if (prio <= after->thread_->priority ())
72  {
73  // Insert at the end of the list.
74 #if defined(OS_TRACE_RTOS_LISTS)
75  trace::printf ("ready %s() back %u +%u \n", __func__,
76  after->thread_->priority (), prio);
77 #endif
78  }
79  else if (prio > head ()->thread_->priority ())
80  {
81  // Insert at the beginning of the list.
82  after =
83  static_cast<waiting_thread_node*> (const_cast<utils::static_double_list_links *> (&head_));
84 #if defined(OS_TRACE_RTOS_LISTS)
85  trace::printf ("ready %s() front +%u %u \n", __func__, prio,
86  head ()->thread_->priority ());
87 #endif
88  }
89  else
90  {
91  // Insert in the middle of the list.
92  // The loop is guaranteed to terminate, and not hit the head.
93  // The weight is relatively small, priority() is not heavy.
94  while (prio > after->thread_->priority ())
95  {
96  after =
97  static_cast<waiting_thread_node*> (const_cast<utils::static_double_list_links *> (after->prev ()));
98  }
99 #if defined(OS_TRACE_RTOS_LISTS)
100  trace::printf ("ready %s() middle %u +%u \n", __func__,
101  after->thread_->priority (), prio);
102 #endif
103  }
104 
105  insert_after (node, after);
106 
107  node.thread_->state_ = thread::state::ready;
108  }
109 
114  thread*
116  {
117  assert (!empty ());
118 
119  thread* th = head ()->thread_;
120 
121 #if defined(OS_TRACE_RTOS_LISTS)
122  trace::printf ("ready %s() %p %s\n", __func__, th, th->name ());
123 #endif
124 
125  const_cast<waiting_thread_node*> (head ())->unlink ();
126 
127  assert (th != nullptr);
128 
129  // Unlinking is immediately followed by a context switch,
130  // so in order to guarantee that the thread is marked as
131  // running, it is saver to do it here.
132 
133  th->state_ = thread::state::running;
134  return th;
135  }
136 
137  // ======================================================================
138 
181  void
183  {
184  thread::priority_t prio = node.thread_->priority ();
185 
186  waiting_thread_node* after =
187  static_cast<waiting_thread_node*> (const_cast<utils::static_double_list_links *> (tail ()));
188 
189  if (empty ())
190  {
191  // Insert at the end of the list.
192 #if defined(OS_TRACE_RTOS_LISTS)
193  trace::printf ("wait %s() empty +%u\n", __func__, prio);
194 #endif
195  }
196  else if (prio <= after->thread_->priority ())
197  {
198  // Insert at the end of the list.
199 #if defined(OS_TRACE_RTOS_LISTS)
200  trace::printf ("wait %s() back %u +%u \n", __func__,
201  after->thread_->priority (), prio);
202 #endif
203  }
204  else if (prio > head ()->thread_->priority ())
205  {
206  // Insert at the beginning of the list.
207  after =
208  static_cast<waiting_thread_node*> (const_cast<utils::static_double_list_links *> (&head_));
209 #if defined(OS_TRACE_RTOS_LISTS)
210  trace::printf ("wait %s() front +%u %u \n", __func__, prio,
211  head ()->thread_->priority ());
212 #endif
213  }
214  else
215  {
216  // Insert in the middle of the list.
217  // The loop is guaranteed to terminate, and not hit the head.
218  // The weight is relatively small, priority() is not heavy.
219  while (prio > after->thread_->priority ())
220  {
221  after =
222  static_cast<waiting_thread_node*> (const_cast<utils::static_double_list_links *> (after->prev ()));
223  }
224 #if defined(OS_TRACE_RTOS_LISTS)
225  trace::printf ("wait %s() middle %u +%u \n", __func__,
226  after->thread_->priority (), prio);
227 #endif
228  }
229 
230  insert_after (node, after);
231  }
232 
238  bool
240  {
241  thread* th;
242  {
243  // ----- Enter critical section -----------------------------------
245 
246  // If the list is empty, silently return.
247  if (empty ())
248  {
249  return false;
250  }
251 
252  // The top priority is to remove the entry from the list
253  // so that subsequent wakeups to address different threads.
254  th = head ()->thread_;
255  const_cast<waiting_thread_node*> (head ())->unlink ();
256  // ----- Exit critical section ------------------------------------
257  }
258  assert (th != nullptr);
259 
260  thread::state_t state = th->state ();
261  if (state != thread::state::destroyed)
262  {
263  th->resume ();
264  }
265  else
266  {
267 #if defined(OS_TRACE_RTOS_LISTS)
268  trace::printf ("%s() gone \n", __func__);
269 #endif
270  }
271 
272  return true;
273  }
274 
275  void
277  {
278  while (resume_one ())
279  ;
280  }
281 
282  // ======================================================================
283 
285  timestamp (ts)
286  {
287 #if defined(OS_TRACE_RTOS_LISTS_CONSTRUCT)
288  trace::printf ("%s() %p \n", __func__, this);
289 #endif
290  }
291 
293  {
294 #if defined(OS_TRACE_RTOS_LISTS_CONSTRUCT)
295  trace::printf ("%s() %p \n", __func__, this);
296 #endif
297  }
298 
299  // ======================================================================
300 
302  rtos::thread& th) :
304  { ts }, //
305  thread (th)
306  {
307 #if defined(OS_TRACE_RTOS_LISTS_CONSTRUCT)
308  trace::printf ("%s() %p \n", __func__, this);
309 #endif
310  }
311 
313  {
314 #if defined(OS_TRACE_RTOS_LISTS_CONSTRUCT)
315  trace::printf ("%s() %p \n", __func__, this);
316 #endif
317  }
318 
319  // Must be called in a critical section.
320  void
322  {
323  rtos::thread* th = &this->thread;
324  this->unlink ();
325 
326  thread::state_t state = th->state ();
327  if (state != thread::state::destroyed)
328  {
329  th->resume ();
330  }
331  }
332 
333  // ======================================================================
334 
335 #if !defined(OS_USE_RTOS_PORT_TIMER)
336 
339  { ts }, //
340  tmr (tm)
341  {
342 #if defined(OS_TRACE_RTOS_LISTS_CONSTRUCT)
343  trace::printf ("%s() %p \n", __func__, this);
344 #endif
345  }
346 
348  {
349 #if defined(OS_TRACE_RTOS_LISTS_CONSTRUCT)
350  trace::printf ("%s() %p \n", __func__, this);
351 #endif
352  }
353 
358  void
360  {
361  this->unlink ();
362  tmr.internal_interrupt_service_routine ();
363  }
364 
365 #endif
366 
367  // ======================================================================
368 
383  void
385  {
387 
388  timeout_thread_node* after =
389  static_cast<timeout_thread_node*> (const_cast<utils::static_double_list_links *> (tail ()));
390 
391  if (empty ())
392  {
393  // Insert at the end of the list.
394 #if defined(OS_TRACE_RTOS_LISTS_CLOCKS)
395  trace::printf ("clock %s() empty +%u\n", __func__,
396  static_cast<uint32_t> (timestamp));
397 #endif
398  }
399  else if (timestamp >= after->timestamp)
400  {
401  // Insert at the end of the list.
402 #if defined(OS_TRACE_RTOS_LISTS_CLOCKS)
403  trace::printf ("clock %s() back %u +%u\n", __func__,
404  static_cast<uint32_t> (after->timestamp),
405  static_cast<uint32_t> (timestamp));
406 #endif
407  }
408  else if (timestamp < head ()->timestamp)
409  {
410  // Insert at the beginning of the list
411  // and update the new head.
412  after =
413  static_cast<timeout_thread_node*> (const_cast<utils::static_double_list_links *> (&head_));
414 #if defined(OS_TRACE_RTOS_LISTS_CLOCKS)
415  trace::printf ("clock %s() front +%u %u\n", __func__,
416  static_cast<uint32_t> (timestamp),
417  static_cast<uint32_t> (head ()->timestamp));
418 #endif
419  }
420  else
421  {
422  // Insert in the middle of the list.
423  // The loop is guaranteed to terminate.
424  while (timestamp < after->timestamp)
425  {
426  after =
427  static_cast<timeout_thread_node*> (const_cast<utils::static_double_list_links *> (after->prev ()));
428  }
429 #if defined(OS_TRACE_RTOS_LISTS_CLOCKS)
430  trace::printf ("clock %s() middle %u +%u\n", __func__,
431  static_cast<uint32_t> (after->timestamp),
432  static_cast<uint32_t> (timestamp));
433 #endif
434  }
435 
436  insert_after (node, after);
437  }
438 
446  void
448  {
449  if (head_.next () == nullptr)
450  {
451  // This happens before the constructors are executed.
452  return;
453  }
454 
455  // Multiple threads can wait for the same time stamp, so
456  // iterate until a node with future time stamp is identified.
457  for (;;)
458  {
459  // ----- Enter critical section -----------------------------------
461 
462  if (empty ())
463  {
464  break;
465  }
466  clock::timestamp_t head_ts = head ()->timestamp;
467  if (now >= head_ts)
468  {
469 #if defined(OS_TRACE_RTOS_LISTS_CLOCKS)
470  trace::printf ("%s() %u \n", __func__,
471  static_cast<uint32_t> (sysclock.now ()));
472 #endif
473  const_cast<timestamp_node*> (head ())->action ();
474  }
475  else
476  {
477  break;
478  }
479  // ----- Exit critical section ------------------------------------
480  }
481  }
482 
483  // ======================================================================
484 
485  void
487  {
488  if (head_.prev () == nullptr)
489  {
490  // If this is the first time, initialise the list to empty.
491  clear ();
492  }
493 
494  waiting_thread_node* after =
495  static_cast<waiting_thread_node*> (const_cast<utils::static_double_list_links *> (tail ()));
496 
497 #if defined(OS_TRACE_RTOS_THREAD)
498  trace::printf ("terminated %s() %p %s\n", __func__, &node.thread_,
499  node.thread_->name ());
500 #endif
501 
502  node.thread_->state_ = thread::state::terminated;
503 
504  insert_after (node, after);
505  }
506 
507  // ------------------------------------------------------------------------
508  } /* namespace internal */
509  } /* namespace rtos */
510 } /* namespace os */
Terminated and resources (like stack) released.
Definition: os-thread.h:378
port::clock::timestamp_t timestamp
Time stamp when the next action will be performed.
Definition: os-lists.h:195
port::clock::timestamp_t timestamp_t
Type of variables holding clock time stamps.
Definition: os-clocks.h:81
void link(timestamp_node &node)
Add a new thread node to the list.
Definition: os-lists.cpp:384
timestamp_node(port::clock::timestamp_t ts)
Construct a node with a time stamp.
Definition: os-lists.cpp:284
rtos::thread & thread
Reference to thread who initiated the timeout.
Definition: os-lists.h:285
void resume_all(void)
Wake-up all threads in the list.
Definition: os-lists.cpp:276
Ask for flags to be cleared after read.
Definition: os-decls.h:303
Double linked list node, with thread reference.
Definition: os-lists.h:58
uint8_t state_t
Type of variables holding thread states.
Definition: os-thread.h:337
virtual ~timestamp_node()
Destruct the node.
Definition: os-lists.cpp:292
Interrupts critical section RAII helper.
Definition: os-sched.h:498
volatile static_double_list_links * head(void) const
Get the list head.
Definition: lists.h:1039
timeout_thread_node(port::clock::timestamp_t ts, thread &th)
Construct a clock timeout node.
Definition: os-lists.cpp:301
timer_node(port::clock::timestamp_t ts, timer &tm)
Construct a clock timer node.
Definition: os-lists.cpp:337
System namespace.
Double linked list node, with time stamp and thread.
Definition: os-lists.h:213
void link(thread &thread)
Add a new thread node to the list.
Definition: os-lists.cpp:41
User single-shot or periodic timer.
Definition: os-timer.h:53
rtos::thread * thread_
Pointer to waiting thread.
Definition: os-lists.h:107
void insert_after(static_double_list_links &node, static_double_list_links *after)
Insert a new node after existing node.
Definition: lists.cpp:115
void link(waiting_thread_node &node)
Add a new thread node to the list.
Definition: os-lists.cpp:51
void clear(void)
Clear the list.
Definition: lists.cpp:108
volatile static_double_list_links * tail(void) const
Get the list tail.
Definition: lists.h:1045
Present in the READY list and competing for CPU.
Definition: os-thread.h:362
virtual ~timeout_thread_node() override
Destruct the node.
Definition: os-lists.cpp:312
virtual ~timer_node() override
Destruct the node.
Definition: os-lists.cpp:347
const char * name(void) const
Get object name.
Definition: os-decls.h:760
uint8_t priority_t
Type of variables holding thread priorities.
Definition: os-thread.h:252
POSIX compliant thread, using the default RTOS allocator.
Definition: os-thread.h:230
void link(waiting_thread_node &node)
Add a new thread node to the list.
Definition: os-lists.cpp:486
state_t state(void) const
Get thread scheduler state.
Definition: os-thread.h:2304
bool resume_one(void)
Wake-up one thread (the oldest with the highest priority)
Definition: os-lists.cpp:239
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
int unlink(const char *name)
virtual void action(void) override
Action to perform when the time stamp is reached.
Definition: os-lists.cpp:359
static_double_list_links head_
A list node used to point to head and tail.
Definition: lists.h:486
thread * unlink_head(void)
Remove the top node from the list.
Definition: os-lists.cpp:115
virtual void action(void) override
Action to perform when the time stamp is reached.
Definition: os-lists.cpp:321
bool empty(void) const
Check if the list is empty.
Definition: lists.h:1032
result_t priority(priority_t prio)
Set the assigned scheduling priority.
Definition: os-thread.cpp:658
void check_timestamp(port::clock::timestamp_t now)
Check list time stamps.
Definition: os-lists.cpp:447
timer & tmr
Reference to waiting timer.
Definition: os-lists.h:372
Double linked list node, with time stamp.
Definition: os-lists.h:124
No longer usable, but resources not yet released.
Definition: os-thread.h:374
void resume(void)
Resume the thread.
Definition: os-thread.cpp:551
void link(waiting_thread_node &node)
Add a new thread node to the list.
Definition: os-lists.cpp:182
Has the CPU and runs.
Definition: os-thread.h:366
virtual timestamp_t now(void)
Tell the current time, possibly adjusted for epoch.
Definition: os-clocks.cpp:169