• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15 
16 #include <cstdint>
17 #include <span>
18 
19 #include "FreeRTOS.h"
20 #include "pw_thread_freertos/config.h"
21 #include "task.h"
22 #if PW_THREAD_JOINING_ENABLED
23 #include "event_groups.h"
24 #endif  // PW_THREAD_JOINING_ENABLED
25 
26 namespace pw::thread {
27 
28 class Thread;  // Forward declare Thread which depends on Context.
29 
30 }  // namespace pw::thread
31 
32 namespace pw::thread::freertos {
33 
34 // FreeRTOS may be used for dynamic thread TCB and stack allocation, but
35 // because we need some additional context beyond that the concept of a
36 // thread's context is split into two halves:
37 //
38 //   1) Context which just contains the additional Context pw::thread::Thread
39 //      requires. This is used for both static and dynamic thread allocations.
40 //
41 //   2) StaticContext which contains the TCB and a span to the stack which is
42 //      used only for static allocations.
43 class Context {
44  public:
45   Context() = default;
46   Context(const Context&) = delete;
47   Context& operator=(const Context&) = delete;
48 
49  private:
50   friend Thread;
51 
task_handle()52   TaskHandle_t task_handle() const { return task_handle_; }
set_task_handle(const TaskHandle_t task_handle)53   void set_task_handle(const TaskHandle_t task_handle) {
54     task_handle_ = task_handle;
55   }
56 
57   using ThreadRoutine = void (*)(void* arg);
set_thread_routine(ThreadRoutine entry,void * arg)58   void set_thread_routine(ThreadRoutine entry, void* arg) {
59     entry_ = entry;
60     arg_ = arg;
61   }
62 
detached()63   bool detached() const { return detached_; }
64   void set_detached(bool value = true) { detached_ = value; }
65 
thread_done()66   bool thread_done() const { return thread_done_; }
67   void set_thread_done(bool value = true) { thread_done_ = value; }
68 
69 #if PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED
dynamically_allocated()70   bool dynamically_allocated() const { return dynamically_allocated_; }
set_dynamically_allocated()71   void set_dynamically_allocated() { dynamically_allocated_ = true; }
72 #endif  // PW_THREAD_FREERTOS_CONFIG_DYNAMIC_ALLOCATION_ENABLED
73 
74 #if PW_THREAD_JOINING_ENABLED
join_event_group()75   StaticEventGroup_t& join_event_group() { return event_group_; }
76 #endif  // PW_THREAD_JOINING_ENABLED
77 
78   static void RunThread(void* void_context_ptr);
79   static void TerminateThread(Context& context);
80 
81   TaskHandle_t task_handle_ = nullptr;
82   ThreadRoutine entry_ = nullptr;
83   void* arg_ = nullptr;
84 #if PW_THREAD_JOINING_ENABLED
85   // Note that the FreeRTOS life cycle of this event group is managed together
86   // with the task life cycle, not this object's life cycle.
87   StaticEventGroup_t event_group_;
88 #endif  // PW_THREAD_JOINING_ENABLED
89   bool detached_ = false;
90   bool dynamically_allocated_ = false;
91   bool thread_done_ = false;
92 };
93 
94 // Static thread context allocation including the TCB, an event group for
95 // joining if enabled, and an external statically allocated stack.
96 //
97 // Example usage:
98 //
99 //   std::array<StackType_t, 42> example_thread_stack;
100 //   pw::thread::freertos::Context example_thread_context(example_thread_stack);
101 //   void StartExampleThread() {
102 //      pw::thread::Thread(
103 //        pw::thread::freertos::Options()
104 //            .set_name("static_example_thread")
105 //            .set_priority(kFooPriority)
106 //            .set_static_context(example_thread_context),
107 //        example_thread_function).detach();
108 //   }
109 class StaticContext : public Context {
110  public:
StaticContext(std::span<StackType_t> stack_span)111   explicit StaticContext(std::span<StackType_t> stack_span)
112       : tcb_{}, stack_span_(stack_span) {}
113 
114  private:
115   friend Thread;
116 
tcb()117   StaticTask_t& tcb() { return tcb_; }
stack()118   std::span<StackType_t> stack() { return stack_span_; }
119 
120   StaticTask_t tcb_;
121   std::span<StackType_t> stack_span_;
122 };
123 
124 // Static thread context allocation including the stack along with the Context.
125 //
126 // Example usage:
127 //
128 //   pw::thread::freertos::ContextWithStack<42> example_thread_context;
129 //   void StartExampleThread() {
130 //      pw::thread::Thread(
131 //        pw::thread::freertos::Options()
132 //            .set_name("static_example_thread")
133 //            .set_priority(kFooPriority)
134 //            .set_static_context(example_thread_context),
135 //        example_thread_function).detach();
136 //   }
137 template <size_t kStackSizeWords = config::kDefaultStackSizeWords>
138 class StaticContextWithStack final : public StaticContext {
139  public:
StaticContextWithStack()140   constexpr StaticContextWithStack() : StaticContext(stack_storage_) {
141     static_assert(kStackSizeWords >= config::kMinimumStackSizeWords);
142   }
143 
144  private:
145   std::array<StackType_t, kStackSizeWords> stack_storage_;
146 };
147 
148 }  // namespace pw::thread::freertos
149