µ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-thread.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#include <memory>
19#include <stdexcept>
20
21// ----------------------------------------------------------------------------
22
23#if defined(__clang__)
24#pragma clang diagnostic ignored "-Wc++98-compat"
25#endif
26
27// ----------------------------------------------------------------------------
28
29namespace os
30{
31 namespace rtos
32 {
33 // ------------------------------------------------------------------------
34
39 std::size_t thread::stack::min_size_bytes_ = port::stack::min_size_bytes;
40
41 std::size_t thread::stack::default_size_bytes_
42 = port::stack::default_size_bytes;
43
48 // ------------------------------------------------------------------------
49 using mutexes_list = utils::intrusive_list<mutex, utils::double_list_links,
50 &mutex::owner_links_>;
51
52 // ========================================================================
104 const thread::attributes thread::initializer;
105
158 void
160 {
161 // Align the bottom of the stack.
162 void* pa = bottom_address_;
163 bottom_address_ = static_cast<stack::element_t*> (
164 std::align (sizeof (stack::allocation_element_t), stack::min_size (),
165 pa, size_bytes_));
166
167 // If there is not enough space for the minimal stack, fail.
168 os_assert_throw (bottom_address_ != nullptr, ENOMEM);
169
170#pragma GCC diagnostic push
171#if defined(__clang__)
172#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
173#endif
174 element_t* p = bottom_address_;
175 element_t* pend = top ();
176
177 // Initialise the entire stack with the magic word.
178 for (; p < pend; ++p)
179 {
180 *p = magic;
181 }
182#pragma GCC diagnostic pop
183
184 // Compute the actual size. The -1 is to leave space for the magic.
185 size_bytes_ = ((static_cast<std::size_t> (p - bottom_address_) - 1)
186 * sizeof (element_t));
187 }
188
201 std::size_t
203 {
204#pragma GCC diagnostic push
205#if defined(__clang__)
206#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
207#endif
208 element_t* p = bottom_address_;
209 std::size_t count = 0;
210 while (*p == magic)
211 {
212 count += sizeof (element_t);
213 ++p;
214 }
215#pragma GCC diagnostic pop
216
217 return count;
218 }
219
232 void
233 thread::internal_invoke_with_exit_ (thread* thread)
234 {
235#if defined(OS_TRACE_RTOS_THREAD)
236 trace::printf ("%s() @%p %s\n", __func__, thread, thread->name ());
237#endif
238
239 void* exit_ptr;
240#if defined(__EXCEPTIONS)
241 try
242 {
243 exit_ptr = thread->func_ (thread->func_args_);
244 }
245 catch (std::exception const& e)
246 {
247 trace::printf ("%s() @%p %s top exception \"%s\"\n", __func__,
248 thread, thread->name (), e.what ());
249 exit_ptr = nullptr;
250 }
251 catch (...)
252 {
253 trace::printf ("%s() @%p %s top exception\n", __func__, thread,
254 thread->name ());
255 exit_ptr = nullptr;
256 }
257#else
258 exit_ptr = thread->func_ (thread->func_args_);
259#endif
260 thread->internal_exit_ (exit_ptr);
261 }
262
264 {
265#if defined(OS_TRACE_RTOS_THREAD)
266 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
267#endif
268 // Must be explicit here, since they are not done in the members
269 // declarations to allow th_enable_assert_reuse.
270 state_ = state::initializing;
271 func_ = nullptr;
272 }
273
275 {
276#if defined(OS_TRACE_RTOS_THREAD)
277 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
278#endif
279 // Must be explicit here, since they are not done in the members
280 // declarations to allow th_enable_assert_reuse.
281 state_ = state::initializing;
282 }
283
306 bool
308 {
309 return ((thread.state_ == state::ready || thread.state_ == state::running
310 || thread.state_ == state::suspended
311 || thread.state_ == state::terminated));
312 }
313
361 thread::thread (func_t function, func_args_t args, const attributes& attr,
362 const allocator_type& allocator)
363 : thread{ nullptr, function, args, attr, allocator }
364 {
365 }
366
414 thread::thread (const char* name, func_t function, func_args_t args,
415 const attributes& attr, const allocator_type& allocator)
416 : object_named_system{ name }
417 {
418#if defined(OS_TRACE_RTOS_THREAD)
419 trace::printf ("%s() @%p %s\n", __func__, this, this->name ());
420#endif
421
422#if defined(DEBUG)
423 if (attr.th_enable_assert_reuse)
424 {
425 // Expect either statically initialised (undefined), or destroyed.
426 assert ((state_ == state::undefined)
427 || (state_ == state::destroyed));
428 }
429#endif /* DEBUG */
430
431 state_ = state::initializing;
432
433 allocator_ = &allocator;
434
435 if (attr.th_stack_address != nullptr
437 {
438 internal_construct_ (function, args, attr, nullptr, 0);
439 }
440 else
441 {
442 using allocator_type2
444
446 {
447 allocated_stack_size_elements_
448 = (attr.th_stack_size_bytes
449 + sizeof (stack::allocation_element_t) - 1)
451 }
452 else
453 {
454 allocated_stack_size_elements_
456 + sizeof (stack::allocation_element_t) - 1)
458 }
459
460#pragma GCC diagnostic push
461#if defined(__clang__)
462#elif defined(__GNUC__)
463#pragma GCC diagnostic ignored "-Wuseless-cast"
464#endif
465 allocated_stack_address_ = reinterpret_cast<stack::element_t*> (
466 const_cast<allocator_type2&> (allocator).allocate (
467 allocated_stack_size_elements_));
468#pragma GCC diagnostic pop
469
470 // Stack allocation failed.
471 assert (allocated_stack_address_ != nullptr);
472
473 internal_construct_ (function, args, attr, allocated_stack_address_,
474 allocated_stack_size_elements_
475 * sizeof (stack::allocation_element_t));
476 }
477 }
478
483 void
484 thread::internal_construct_ (func_t function, func_args_t args,
485 const attributes& attr, void* stack_address,
486 std::size_t stack_size_bytes)
487 {
488 // Don't call this from interrupt handlers.
490
491 // The thread function must be real.
492 assert (function != nullptr);
493 // Don't forget to set the thread priority.
494 assert (attr.th_priority != priority::none);
495
496 clock_ = attr.clock != nullptr ? attr.clock : &sysclock;
497
498 if (stack_address != nullptr)
499 {
500 // The attributes should not define any storage in this case.
501 if (attr.th_stack_size_bytes > stack::min_size ())
502 {
503 // The stack address must be real.
504 assert (attr.th_stack_address == nullptr);
505 }
506
507 stack ().set (static_cast<stack::element_t*> (stack_address),
508 stack_size_bytes);
509 }
510 else
511 {
512 stack ().set (static_cast<stack::element_t*> (attr.th_stack_address),
513 attr.th_stack_size_bytes);
514 }
515
516#if defined(OS_TRACE_RTOS_THREAD)
517 trace::printf ("%s() @%p %s p%u stack{%p,%u}\n", __func__, this, name (),
518 attr.th_priority, stack ().bottom_address_,
519 stack ().size_bytes_);
520#endif
521
522 {
523 // Prevent the new thread to execute before all members are set.
524 // ----- Enter critical section ---------------------------------------
525 scheduler::critical_section scs;
526
527 // Get attributes from user structure.
528 prio_assigned_ = attr.th_priority;
529
530 func_ = function;
531 func_args_ = args;
532
533 parent_ = this_thread::_thread ();
534 if (scheduler::started () && (parent_ != nullptr))
535 {
536 parent_->children_.link (*this);
537 }
538 else
539 {
540 scheduler::top_threads_list_.link (*this);
541 }
542
543 stack ().initialize ();
544
545#if defined(OS_USE_RTOS_PORT_SCHEDULER)
546
547 port::thread::create (this);
548 state_ = state::ready;
549
550#else
551
552#pragma GCC diagnostic push
553#if defined(__clang__)
554#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
555#endif
556 // Create the port specific context.
558 &context_, reinterpret_cast<void*> (internal_invoke_with_exit_),
559 this);
560#pragma GCC diagnostic pop
561
562 if (!scheduler::started ())
563 {
564 scheduler::current_thread_ = this;
565 }
566
567 // Add to ready list, but do not yield yet.
568 resume ();
569
570#endif
571 // ----- Exit critical section ----------------------------------------
572 }
573 // For just in case the new thread has higher priority.
575 }
576
596 {
597#if defined(OS_TRACE_RTOS_THREAD)
598 trace::printf ("%s() @%p %s \n", __func__, this, name ());
599#endif
600
601 // Prevent the main thread to destroy itself while running
602 // the exit cleanup code.
603 if (this != &this_thread::thread ())
604 {
605 // Extra test to avoid the 'already gone' message.
606 if (state_ != state::destroyed)
607 {
608 kill ();
609 }
610 }
611 else
612 {
613#if defined(OS_TRACE_RTOS_THREAD)
614 trace::printf ("%s() @%p %s nop, cannot commit suicide\n", __func__,
615 this, name ());
616#endif
617 }
618 }
619
629 void
631 {
632#if defined(OS_TRACE_RTOS_THREAD_CONTEXT)
633 trace::printf ("%s() @%p %s %u\n", __func__, this, name (),
634 prio_assigned_);
635#endif
636
637#if defined(OS_USE_RTOS_PORT_SCHEDULER)
638
639 {
640 // ----- Enter critical section ---------------------------------------
642
643 state_ = state::ready;
644 port::thread::resume (this);
645 // ----- Exit critical section ----------------------------------------
646 }
647
648#else
649
650 // Don't call this from high priority interrupts.
651 assert (port::interrupts::is_priority_valid ());
652
653 {
654 // ----- Enter critical section ---------------------------------------
656
657 // If the thread is not already in the ready list, enqueue it.
658 if (ready_node_.next () == nullptr)
659 {
660 scheduler::ready_threads_list_.link (ready_node_);
661 // state::ready set in above link().
662 }
663 // ----- Exit critical section ----------------------------------------
664 }
665
667
668#endif
669 }
670
679 {
680 // trace::printf ("%s() @%p %s\n", __func__, this, name ());
681
682 if (prio_inherited_ == priority::none)
683 {
684 // The common case is to have no inherited priority;
685 // return the assigned one.
686 return prio_assigned_;
687 }
688 else
689 {
690 // Return the maximum between inherited and assigned.
691 return (prio_inherited_ >= prio_assigned_) ? prio_inherited_
692 : prio_assigned_;
693 }
694 }
695
704 {
705 // Don't call this from interrupt handlers.
707
708 return prio_inherited_;
709 }
710
735 {
736#if defined(OS_TRACE_RTOS_THREAD)
737 trace::printf ("%s(%u) @%p %s\n", __func__, prio, this, name ());
738#endif
739
740 // Don't call this from interrupt handlers.
742 // Check the priority, it is not in the allowed range.
743 os_assert_err (prio < priority::error, EINVAL);
744 os_assert_err (prio != priority::none, EINVAL);
745
746 if (prio_assigned_ == prio)
747 {
748 // Optimise, if priority did not change.
749 return result::ok;
750 }
751
752 prio_assigned_ = prio;
753
754 result_t res = result::ok;
755
756#if defined(OS_USE_RTOS_PORT_SCHEDULER)
757
758 // The port must perform a context switch.
759 res = port::thread::priority (this, prio);
760
761#else
762
763 if (state_ == state::ready)
764 {
765 // ----- Enter critical section -------------------------------------
767
768 // Remove from initial location and reinsert according
769 // to new priority.
770 ready_node_.unlink ();
771 scheduler::ready_threads_list_.link (ready_node_);
772 // ----- Exit critical section --------------------------------------
773 }
774
775 // Mandatory, the priority might have been raised, the
776 // task must be scheduled to run.
778
779#endif
780
781 return res;
782 }
783
803 {
804#if defined(OS_TRACE_RTOS_THREAD)
805 trace::printf ("%s(%u) @%p %s\n", __func__, prio, this, name ());
806#endif
807
808 // Don't call this from interrupt handlers.
810 // Check the priority, it is not in the allowed range.
811 os_assert_err (prio < priority::error, EINVAL);
812
813 // Warning: do not check for `priority::none`, since
814 // `mutex::unlock()` sets it when the list of mutexes owned
815 // by a thread is empty.
816
817 if (prio == prio_inherited_)
818 {
819 // Optimise, if priority did not change.
820 return result::ok;
821 }
822
823 prio_inherited_ = prio;
824
825 if (prio_inherited_ < prio_assigned_)
826 {
827 // Optimise, no need to reschedule.
828 return result::ok;
829 }
830
831 result_t res = result::ok;
832
833#if defined(OS_USE_RTOS_PORT_SCHEDULER)
834
835 // The port must perform a context switch.
836 res = port::thread::priority (this, prio);
837
838#else
839
840 if (state_ == state::ready)
841 {
842 // ----- Enter critical section -------------------------------------
844
845 // Remove from initial location and reinsert according
846 // to new priority.
847 ready_node_.unlink ();
848 scheduler::ready_threads_list_.link (ready_node_);
849 // ----- Exit critical section --------------------------------------
850 }
851
852 // Mandatory, the priority might have been raised, the
853 // task must be scheduled to run.
855
856#endif
857
858 return res;
859 }
860
884 {
885#if defined(OS_TRACE_RTOS_THREAD)
886 trace::printf ("%s() @%p %s\n", __func__, this, name ());
887#endif
888
889 // Don't call this from interrupt handlers.
891
892#if defined(OS_USE_RTOS_PORT_SCHEDULER)
893
894 result_t res = port::thread::detach (this);
895 if (res != result::ok)
896 {
897 return res;
898 }
899
900#else
901
902 // TODO: implement
903
904#endif
905
906 return result::ok;
907 }
908
941 thread::join (void** exit_ptr)
942 {
943#if defined(OS_TRACE_RTOS_THREAD)
944 trace::printf ("%s() @%p %s\n", __func__, this, name ());
945#endif
946
947 // Don't call this from interrupt handlers.
949 // Don't call this from critical regions.
950 os_assert_err (!scheduler::locked (), EPERM);
951
952 // Fail if current thread
953 assert (this != this_thread::_thread ());
954
955 while (state_ != state::destroyed)
956 {
957 joiner_ = this_thread::_thread ();
958 this_thread::_thread ()->internal_suspend_ ();
959 }
960
961#if defined(OS_TRACE_RTOS_THREAD)
962 trace::printf ("%s() @%p %s joined\n", __func__, this, name ());
963#endif
964
965 if (exit_ptr != nullptr)
966 {
967 *exit_ptr = func_result_;
968 }
969
970 return result::ok;
971 }
972
996 {
997#if defined(OS_TRACE_RTOS_THREAD)
998 trace::printf ("%s() @%p %s\n", __func__, this, name ());
999#endif
1000
1001 // Don't call this from interrupt handlers.
1003
1004 // TODO: implement according to POSIX specs.
1005 return result::ok;
1006 }
1007
1019 bool
1020 thread::interrupt (bool interrupt)
1021 {
1022#if defined(OS_TRACE_RTOS_THREAD)
1023 trace::printf ("%s() @%p %s\n", __func__, this, name ());
1024#endif
1025
1026 bool tmp = interrupted_;
1027 interrupted_ = interrupt;
1028
1029 resume ();
1030 return tmp;
1031 }
1032
1041 void
1042 thread::internal_suspend_ (void)
1043 {
1044#if defined(OS_TRACE_RTOS_THREAD)
1045 trace::printf ("%s() @%p %s\n", __func__, this, name ());
1046#endif
1047
1048 {
1049 // ----- Enter critical section ---------------------------------------
1051
1052 // Remove this thread from the ready list, if there.
1054
1055 state_ = state::suspended;
1056 // ----- Exit critical section ----------------------------------------
1057 }
1058
1060 }
1061
1062 void
1063 thread::internal_exit_ (void* exit_ptr)
1064 {
1065#if defined(OS_TRACE_RTOS_THREAD)
1066 trace::printf ("%s() @%p %s\n", __func__, this, name ());
1067#endif
1068
1069 // Don't call this from interrupt handlers.
1070 assert (!interrupts::in_handler_mode ());
1071
1072 {
1073 // ----- Enter critical section ---------------------------------------
1074 scheduler::critical_section scs;
1075
1076 {
1077 // ----- Enter critical section -------------------------------------
1078 interrupts::critical_section ics;
1079
1080 ready_node_.unlink ();
1081
1082 child_links_.unlink ();
1083 // ----- Exit critical section --------------------------------------
1084 }
1085
1086 // There must be no children threads still alive.
1087 assert (children_.empty ());
1088 parent_ = nullptr;
1089
1090 func_ = nullptr;
1091 func_args_ = nullptr;
1092
1093 // There must be no more mutexes locked by this thread.
1094 assert (mutexes_.empty ());
1095 assert (acquired_mutexes_ == 0);
1096
1097 func_result_ = exit_ptr;
1098 // ----- Exit critical section ----------------------------------------
1099 }
1100
1101 {
1102 // ----- Enter critical section ---------------------------------------
1103 interrupts::critical_section ics;
1104
1105 // Add to a list of threads to be destroyed by the idle thread.
1106 // Also set state::terminated.
1107 scheduler::terminated_threads_list_.link (ready_node_);
1108 // ----- Exit critical section ----------------------------------------
1109 }
1110
1111#if defined(OS_USE_RTOS_PORT_SCHEDULER)
1112
1113 port::thread::destroy_this (this);
1114 // Does not return if the current thread.
1115
1116#else
1117
1118 // At this point, since the thread state is no longer 'running',
1119 // the thread is no longer linked in the READY list.
1121
1122#endif
1123
1124 assert (true);
1125 while (true)
1126 ;
1127
1128 // Definitely does not return.
1129 }
1130
1131 void
1132 thread::internal_check_stack_ (void)
1133 {
1134 if (stack ().size () > 0)
1135 {
1136 if (!stack ().check_bottom_magic () || !stack ().check_top_magic ())
1137 {
1138 trace::printf ("%s() @%p %s\n", __func__, this, name ());
1139 assert (stack ().check_bottom_magic ());
1140 assert (stack ().check_top_magic ());
1141 }
1142
1143#if defined(OS_TRACE_RTOS_THREAD)
1145 "%s() @%p %s stack: %u/%u bytes used\n", __func__, this, name (),
1146 stack ().size () - stack ().available (), stack ().size ());
1147#endif
1148
1149 // Clear stack to avoid further checks
1150 stack ().clear ();
1151 }
1152 }
1153
1154#pragma GCC diagnostic push
1155#if defined(__clang__)
1156#elif defined(__GNUC__)
1157#pragma GCC diagnostic ignored "-Wsuggest-final-methods"
1158#endif
1159 // Called from kill() and from idle thread.
1160 void
1161 thread::internal_destroy_ (void)
1162 {
1163#if defined(OS_TRACE_RTOS_THREAD)
1164 trace::printf ("%s() @%p %s\n", __func__, this, name ());
1165#endif
1166
1167 internal_check_stack_ ();
1168
1169 if (allocated_stack_address_ != nullptr)
1170 {
1171 typedef
1172 typename std::allocator_traits<allocator_type>::pointer pointer;
1173
1174#pragma GCC diagnostic push
1175#if defined(__clang__)
1176#elif defined(__GNUC__)
1177#pragma GCC diagnostic ignored "-Wuseless-cast"
1178#pragma GCC diagnostic ignored "-Wcast-align"
1179#endif
1180 static_cast<allocator_type*> (const_cast<void*> (allocator_))
1181 ->deallocate (
1182 reinterpret_cast<pointer> (allocated_stack_address_),
1183 allocated_stack_size_elements_);
1184#pragma GCC diagnostic pop
1185
1186 allocated_stack_address_ = nullptr;
1187 }
1188
1189 {
1190 // ----- Enter critical section ---------------------------------------
1191 scheduler::critical_section scs;
1192
1193 mutexes_list& mx_list = reinterpret_cast<mutexes_list&> (mutexes_);
1194 while (not mx_list.empty ())
1195 {
1196 auto* mx = mx_list.unlink_head ();
1197
1198 mx->internal_mark_owner_dead_ ();
1199
1200 // Unlock the mutex as owned by the thread itself.
1201 mx->internal_unlock_ (this);
1202 }
1203 // ----- Exit critical section ----------------------------------------
1204 }
1205
1206 state_ = state::destroyed;
1207
1208 if (joiner_ != nullptr)
1209 {
1210 joiner_->resume ();
1211 }
1212 }
1213#pragma GCC diagnostic pop
1214
1230 result_t
1232 {
1233#if defined(OS_TRACE_RTOS_THREAD)
1234 trace::printf ("%s() @%p %s\n", __func__, this, name ());
1235#endif
1236
1237 // Don't call this from interrupt handlers.
1239
1240 {
1241 // ----- Enter critical section ---------------------------------------
1243
1244 if (state_ == state::destroyed)
1245 {
1246#if defined(OS_TRACE_RTOS_THREAD)
1247 trace::printf ("%s() @%p %s already gone\n", __func__, this,
1248 name ());
1249#endif
1250 return result::ok; // Already exited itself
1251 }
1252
1253 {
1254 // ----- Enter critical section -------------------------------------
1256
1257 // Remove thread from the funeral list and kill it here.
1258 ready_node_.unlink ();
1259
1260 // If the thread is waiting on an event, remove it from the list.
1261 if (waiting_node_ != nullptr)
1262 {
1263 waiting_node_->unlink ();
1264 }
1265
1266 // If the thread is waiting on a timeout, remove it from the list.
1267 if (clock_node_ != nullptr)
1268 {
1269 clock_node_->unlink ();
1270 }
1271
1272 child_links_.unlink ();
1273 // ----- Exit critical section --------------------------------------
1274 }
1275
1276 // The must be no more children threads alive.
1277 assert (children_.empty ());
1278 parent_ = nullptr;
1279
1280#if defined(OS_USE_RTOS_PORT_SCHEDULER)
1281
1282 port::thread::destroy_other (this);
1283
1284#endif
1285
1286 func_result_ = nullptr;
1287
1288 func_ = nullptr;
1289 func_args_ = nullptr;
1290
1291 internal_destroy_ ();
1292
1293 // There must be no mutexes locked by this thread.
1294 // Must have been cleaned before.
1295 assert (mutexes_.empty ());
1296 assert (acquired_mutexes_ == 0);
1297
1298 // ----- Exit critical section ----------------------------------------
1299 }
1300
1301 return result::ok;
1302 }
1303
1312 result_t
1314 {
1315#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1316 trace::printf ("%s(0x%X) @%p %s <0x%X\n", __func__, mask, this, name (),
1317 event_flags_.mask ());
1318#endif
1319
1320 result_t res = event_flags_.raise (mask, oflags);
1321
1322 this->resume ();
1323
1324#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1325 trace::printf ("%s(0x%X) @%p %s >0x%X\n", __func__, mask, this, name (),
1326 event_flags_.mask ());
1327#endif
1328
1329 return res;
1330 }
1331
1336 result_t
1337 thread::internal_flags_wait_ (flags::mask_t mask, flags::mask_t* oflags,
1338 flags::mode_t mode)
1339 {
1340#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1341 trace::printf ("%s(0x%X,%u) @%p %s <0x%X\n", __func__, mask, mode, this,
1342 name (), event_flags_.mask ());
1343#endif
1344
1345 // Don't call this from interrupt handlers.
1347 // Don't call this from critical regions.
1348 os_assert_err (!scheduler::locked (), EPERM);
1349
1350 {
1351 // ----- Enter critical section ---------------------------------------
1353
1354 if (event_flags_.check_raised (mask, oflags, mode))
1355 {
1356#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1357 trace::printf ("%s(0x%X,%u) @%p %s >0x%X\n", __func__, mask, mode,
1358 this, name (), event_flags_.mask ());
1359#endif
1360 return result::ok;
1361 }
1362 // ----- Exit critical section ----------------------------------------
1363 }
1364
1365#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1366 clock::timestamp_t begin_timestamp = clock_->now ();
1367#endif
1368 for (;;)
1369 {
1370 {
1371 // ----- Enter critical section -----------------------------------
1372 interrupts::critical_section ics;
1373
1374 if (event_flags_.check_raised (mask, oflags, mode))
1375 {
1376#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1377 clock::duration_t slept_ticks
1378 = static_cast<clock::duration_t> (clock_->now ()
1379 - begin_timestamp);
1380 trace::printf ("%s(0x%X,%u) in %d @%p %s >0x%X\n", __func__,
1381 mask, mode, slept_ticks, this, name (),
1382 event_flags_.mask ());
1383#endif
1384 return result::ok;
1385 }
1386 // ----- Exit critical section ------------------------------------
1387 }
1388
1389 internal_suspend_ ();
1390
1391 if (interrupted ())
1392 {
1393#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1394 trace::printf ("%s(0x%X,%u) EINTR @%p %s\n", __func__, mask,
1395 mode, this, name ());
1396#endif
1397 return EINTR;
1398 }
1399 }
1400
1401 /* NOTREACHED */
1402 return ENOTRECOVERABLE;
1403 }
1404
1405 result_t
1406 thread::internal_flags_try_wait_ (flags::mask_t mask,
1407 flags::mask_t* oflags,
1408 flags::mode_t mode)
1409 {
1410#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1411 trace::printf ("%s(0x%X,%u) @%p %s <0x%X\n", __func__, mask, mode, this,
1412 name (), event_flags_.mask ());
1413#endif
1414
1415 // Don't call this from interrupt handlers.
1417
1418 {
1419 // ----- Enter critical section ---------------------------------------
1420 interrupts::critical_section ics;
1421
1422 if (event_flags_.check_raised (mask, oflags, mode))
1423 {
1424#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1425 trace::printf ("%s(0x%X,%u) @%p %s >0x%X\n", __func__, mask, mode,
1426 this, name (), event_flags_.mask ());
1427#endif
1428 return result::ok;
1429 }
1430 else
1431 {
1432#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1433 trace::printf ("%s(0x%X,%u) EWOULDBLOCK @%p %s \n", __func__, mask,
1434 mode, this, name ());
1435#endif
1436 return EWOULDBLOCK;
1437 }
1438 // ----- Exit critical section ----------------------------------------
1439 }
1440 }
1441
1442 result_t
1443 thread::internal_flags_timed_wait_ (flags::mask_t mask,
1444 clock::duration_t timeout,
1445 flags::mask_t* oflags,
1446 flags::mode_t mode)
1447 {
1448#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1449 trace::printf ("%s(0x%X,%u,%u) @%p %s <0x%X\n", __func__, mask, timeout,
1450 mode, this, name (), event_flags_.mask ());
1451#endif
1452
1453 // Don't call this from interrupt handlers.
1455 // Don't call this from critical regions.
1456 os_assert_err (!scheduler::locked (), EPERM);
1457
1458 {
1459 // ----- Enter critical section ---------------------------------------
1460 interrupts::critical_section ics;
1461
1462 if (event_flags_.check_raised (mask, oflags, mode))
1463 {
1464#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1465 trace::printf ("%s(0x%X,%u,%u) @%p %s >0x%X\n", __func__, mask,
1466 timeout, mode, this, name (), event_flags_.mask ());
1467#endif
1468 return result::ok;
1469 }
1470 // ----- Exit critical section ----------------------------------------
1471 }
1472
1473 internal::clock_timestamps_list& clock_list = clock_->steady_list ();
1474 clock::timestamp_t timeout_timestamp = clock_->steady_now () + timeout;
1475
1476#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1477 clock::timestamp_t begin_timestamp = clock_->steady_now ();
1478#endif
1479
1480 // Prepare a timeout node pointing to the current thread.
1481 internal::timeout_thread_node timeout_node{ timeout_timestamp, *this };
1482
1483 for (;;)
1484 {
1485 {
1486 // ----- Enter critical section -----------------------------------
1487 interrupts::critical_section ics;
1488
1489 if (event_flags_.check_raised (mask, oflags, mode))
1490 {
1491#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1492 clock::duration_t slept_ticks
1493 = static_cast<clock::duration_t> (clock_->steady_now ()
1494 - begin_timestamp);
1495#pragma GCC diagnostic push
1496#if defined(__clang__)
1497#elif defined(__GNUC__)
1498#pragma GCC diagnostic ignored "-Wuseless-cast"
1499#endif
1500 trace::printf ("%s(0x%X,%u,%u) in %u @%p %s >0x%X\n", __func__,
1501 mask, timeout, mode,
1502 static_cast<unsigned int> (slept_ticks), this,
1503 name (), event_flags_.mask ());
1504#pragma GCC diagnostic pop
1505#endif
1506 return result::ok;
1507 }
1508
1509 // Remove this thread from the ready list, if there.
1511
1512 // Add this thread to the clock timeout list.
1513 clock_list.link (timeout_node);
1514 timeout_node.thread.clock_node_ = &timeout_node;
1515
1516 state_ = state::suspended;
1517 // ----- Exit critical section ------------------------------------
1518 }
1519
1521
1522 {
1523 // ----- Enter critical section -----------------------------------
1524 interrupts::critical_section ics;
1525
1526 // Remove the thread from the clock timeout list,
1527 // if not already removed by the timer.
1528 timeout_node.thread.clock_node_ = nullptr;
1529 timeout_node.unlink ();
1530 // ----- Exit critical section ------------------------------------
1531 }
1532
1533 if (interrupted ())
1534 {
1535#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1536 trace::printf ("%s(0x%X,%u,%u) EINTR @%p %s\n", __func__, mask,
1537 timeout, mode, this, name ());
1538#endif
1539 return EINTR;
1540 }
1541
1542 if (clock_->steady_now () >= timeout_timestamp)
1543 {
1544#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1545 trace::printf ("%s(0x%X,%u,%u) ETIMEDOUT @%p %s\n", __func__,
1546 mask, timeout, mode, this, name ());
1547#endif
1548 return ETIMEDOUT;
1549 }
1550 }
1551
1552 return ENOTRECOVERABLE;
1553 }
1554
1567 thread::internal_flags_get_ (flags::mask_t mask, flags::mode_t mode)
1568 {
1569#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1570 trace::printf ("%s(0x%X) @%p %s\n", __func__, mask, this, name ());
1571#endif
1572
1573 // Don't call this from interrupt handlers.
1575
1576 flags::mask_t ret = event_flags_.get (mask, mode);
1577
1578#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1579 trace::printf ("%s(0x%X)=0x%X @%p %s\n", __func__, mask,
1580 event_flags_.mask (), this, name ());
1581#endif
1582 // Return the selected bits.
1583 return ret;
1584 }
1585
1589 result_t
1590 thread::internal_flags_clear_ (flags::mask_t mask, flags::mask_t* oflags)
1591 {
1592#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1593 trace::printf ("%s(0x%X) @%p %s <0x%X\n", __func__, mask, this, name (),
1594 event_flags_.mask ());
1595#endif
1596
1597 // Don't call this from interrupt handlers.
1599
1600 result_t res = event_flags_.clear (mask, oflags);
1601
1602#if defined(OS_TRACE_RTOS_THREAD_FLAGS)
1603 trace::printf ("%s(0x%X) @%p %s >0x%X\n", __func__, mask, this, name (),
1604 event_flags_.mask ());
1605#endif
1606 return res;
1607 }
1608
1613 // ------------------------------------------------------------------------
1619 namespace this_thread
1620 {
1621
1626 rtos::thread*
1627 _thread (void)
1628 {
1629 rtos::thread* th;
1630
1631#if defined(OS_USE_RTOS_PORT_SCHEDULER)
1632
1633 th = port::this_thread::thread ();
1634
1635#else
1636
1637 th = scheduler::current_thread_;
1638
1639#endif
1640 return th;
1641 }
1642
1650 rtos::thread&
1651 thread (void)
1652 {
1653 // Don't call this from interrupt handlers.
1655
1656 rtos::thread* th;
1657
1658 th = _thread ();
1659
1660 // Could not get the current thread.
1661 assert (th != nullptr);
1662 return (*th);
1663 }
1664
1671 void
1672 yield (void)
1673 {
1674 // Don't call this from interrupt handlers.
1676
1677 if (!scheduler::started ())
1678 {
1679#if defined(OS_TRACE_RTOS_THREAD_CONTEXT)
1680 trace::printf ("%s() nop %s \n", __func__, _thread ()->name ());
1681#endif
1682 return;
1683 }
1684
1685#if defined(OS_TRACE_RTOS_THREAD_CONTEXT)
1686 trace::printf ("%s() from %s\n", __func__, _thread ()->name ());
1687#endif
1688
1689#if defined(OS_USE_RTOS_PORT_SCHEDULER)
1690
1692
1693#else
1694
1696
1697#endif
1698
1699#if defined(OS_TRACE_RTOS_THREAD_CONTEXT)
1700 trace::printf ("%s() to %s\n", __func__, _thread ()->name ());
1701#endif
1702 }
1703
1704 } /* namespace this_thread */
1705
1706 // ------------------------------------------------------------------------
1707 } /* namespace rtos */
1708} /* namespace os */
1709
1710// ----------------------------------------------------------------------------
object_named_system()
Construct a named system object instance.
Definition os-decls.h:760
const char * name(void) const
Get object name.
Definition os-decls.h:753
Interrupts critical section RAII helper.
Definition os-sched.h:502
Standard allocator based on the RTOS system default memory manager.
Definition os-memory.h:538
static void create(void *context, void *func, void *args)
Scheduler critical section RAII helper.
Definition os-sched.h:171
Thread attributes.
Definition os-thread.h:800
std::size_t th_stack_size_bytes
Size of the user defined storage for the thread stack, in bytes.
Definition os-thread.h:861
void * th_stack_address
Address of the user defined storage for the thread stack.
Definition os-thread.h:847
void initialize(void)
Align the pointers and initialise to a known pattern.
os::rtos::port::stack::element_t element_t
Type of a stack element.
Definition os-thread.h:442
static std::size_t default_size(void)
Get the default stack size.
Definition os-thread.h:2222
static std::size_t min_size(void)
Get the min stack size.
Definition os-thread.h:2202
os::rtos::port::stack::allocation_element_t allocation_element_t
Type of a stack allocation element.
Definition os-thread.h:452
void clear(void)
Clear the stack pointer and size.
Definition os-thread.h:2131
std::size_t available(void)
Compute how much available stack remains.
static const element_t magic
Definition os-thread.h:454
stack::element_t * top(void)
Get the top stack address.
Definition os-thread.h:2161
void set(stack::element_t *address, std::size_t size_bytes)
Set the stack address and size.
Definition os-thread.h:2141
POSIX compliant thread, using the default RTOS allocator.
Definition os-thread.h:251
result_t flags_raise(flags::mask_t mask, flags::mask_t *oflags=nullptr)
Raise thread event flags.
result_t kill(void)
Force thread termination.
virtual ~thread()
Destruct the thread object instance.
void resume(void)
Resume the thread.
static const attributes initializer
Default thread initialiser.
Definition os-thread.h:1011
thread::stack & stack(void)
Get the thread context stack.
Definition os-thread.h:2451
priority_t priority_inherited(void)
Get the inherited scheduling priority.
bool interrupted(void)
Check if interrupted.
Definition os-thread.h:2373
memory::allocator< stack::allocation_element_t > allocator_type
Default RTOS allocator.
Definition os-thread.h:1018
result_t detach(void)
Detach a thread.
result_t cancel(void)
Cancel thread execution.
void *(*)(func_args_t args) func_t
Type of thread function.
Definition os-thread.h:423
bool interrupt(bool interrupt=true)
Set the interrupt flag, possibly interrupting the thread.
_func_args_t func_args_t
Type of thread function arguments.
Definition os-thread.h:414
static bool is_constructed(const thread &thread)
Check if the thread is constructed.
Standard thread.
thread() noexcept=default
void join(void)
Definition thread-cpp.h:80
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.
uint8_t priority_t
Type of variables holding thread priorities.
Definition os-thread.h:271
@ all
Special mask to represent all flags.
Definition os-decls.h:322
uint32_t mode_t
Type of variables holding flags modes.
Definition os-decls.h:277
uint32_t mask_t
Type of variables holding flags masks.
Definition os-decls.h:266
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 started(void)
Check if the scheduler was started.
Definition os-sched.h:829
bool locked(void)
Check if the scheduler is locked.
Definition os-sched.h:858
thread & thread(void)
Get the current running thread.
void yield(void)
Yield execution to the next ready thread.
utils::intrusive_list< mutex, utils::double_list_links, &mutex::owner_links_ > mutexes_list
Definition os-mutex.cpp:328
uint32_t result_t
Type of values returned by RTOS functions.
Definition os-decls.h:95
System namespace.
A namespace for functions applying to the current thread.
#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.
Thread priorities.
Definition os-thread.h:283
@ running
Has the CPU and runs.
Definition os-thread.h:386
@ destroyed
Terminated and resources (like stack) released.
Definition os-thread.h:398
@ initializing
Used to check reused threads.
Definition os-thread.h:402
@ terminated
No longer usable, but resources not yet released.
Definition os-thread.h:394
@ ready
Present in the READY list and competing for CPU.
Definition os-thread.h:382
@ undefined
Used to catch uninitialised threads.
Definition os-thread.h:378
@ suspended
Not present in the READY list, waiting for an event.
Definition os-thread.h:390