Skip to content

Commit 43e3608

Browse files
committed
#9 - Added Optional Callbacks for Thread (interfaces in IThread
Also added a Garbage Collection demo
1 parent ee33222 commit 43e3608

File tree

6 files changed

+199
-12
lines changed

6 files changed

+199
-12
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
An example of ESPressio Threads' Garbage Collection system in action.
3+
*/
4+
5+
#define ESPRESSIO_THREAD_DEFAULT_STACK_SIZE = 1600 // This sets the default Stack Size for all Threads in the system.
6+
7+
#include <Arduino.h>
8+
9+
#include "ESPressio_IThread.hpp" // This gives us access to the `IThread` interface.
10+
#include "ESPressio_Thread.hpp" // This gives us access to our `Thread` base class.
11+
#include "ESPressio_ThreadManager.hpp" // This gives us access to the `ThreadManager` class.
12+
13+
using namespace ESPressio::Threads;
14+
15+
class DemoThread : public Thread {
16+
private:
17+
uint32_t _counter = 0;
18+
protected:
19+
void OnLoop() {
20+
Serial.printf("DemoThread: %u\n", _counter++);
21+
delay(1000);
22+
if (counter == 10) { // When the counter reaches 10...
23+
Terminate(); // ... terminate the thread.
24+
}
25+
}
26+
};
27+
28+
DemoThread* thread;
29+
30+
// This function will be called when the thread is destroyed.
31+
void onThreadDestroyed(IThread* thread) {
32+
Serial.printf("Thread %u has been destroyed!\n", thread->GetThreadID()); // Print a message to the Serial Monitor.
33+
}
34+
35+
void setup() {
36+
Serial.begin(115200); // Start the Serial Monitor.
37+
38+
thread = new DemoThread(true); // Create a new instance of our `DemoThread` class. The `true` parameter tells the Thread to use Garbage Collection when it's Terminated!
39+
40+
thread.SetStartOnInitialize(true); // This will start the thread as soon as it's initialized. (true is the default, but we're setting it here for clarity.)
41+
thread.SetOnDestroy(onThreadDestroyed); // This will set the `onThreadDestroyed` function as the callback for when the thread is destroyed.
42+
43+
ThreadManager::Initialize(); // This will initialize ALL Thread instances in your code!
44+
}
45+
46+
void loop() {
47+
// You can still use your main loop as normal, if you want to!
48+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
An extremely simple example of an ESPressio Thread loop that runs on either core of the ESP32.
3+
*/
4+
5+
#define ESPRESSIO_THREAD_DEFAULT_STACK_SIZE = 1600 // This sets the default Stack Size for all Threads in the system.
6+
7+
#include <Arduino.h>
8+
9+
#include "ESPressio_Thread.hpp"
10+
#include "ESPressio_ThreadManager.hpp"
11+
12+
using namespace ESPressio::Threads;
13+
14+
class DemoThread : public Thread {
15+
private:
16+
uint32_t _counter = 0;
17+
protected:
18+
void OnLoop() {
19+
Serial.printf("DemoThread: %u\n", _counter++);
20+
delay(1000);
21+
}
22+
};
23+
24+
DemoThread thread;
25+
26+
void setup() {
27+
Serial.begin(115200);
28+
29+
thread.SetStartOnInitialize(true);
30+
31+
ThreadManager::Initialize(); // This will initialize ALL Thread instances in your code!
32+
}

examples/SimpleForeverThread/SimpleForeverThread.ino

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
#include <Arduino.h>
88

9-
#include "ESPressio_Thread.hpp"
10-
#include "ESPressio_ThreadManager.hpp"
9+
#include "ESPressio_IThread.hpp" // This gives us access to the `IThread` interface.
10+
#include "ESPressio_Thread.hpp" // This gives us access to our `Thread` base class.
11+
#include "ESPressio_ThreadManager.hpp" // This gives us access to the `ThreadManager` class.
1112

1213
using namespace ESPressio::Threads;
1314

@@ -24,9 +25,13 @@ class DemoThread : public Thread {
2425
DemoThread thread;
2526

2627
void setup() {
27-
Serial.begin(115200);
28+
Serial.begin(115200); // Start the Serial Monitor.
2829

29-
thread.SetStartOnInitialize(true);
30+
thread.SetStartOnInitialize(true); // This will start the thread as soon as it's initialized. (true is the default, but we're setting it here for clarity.)
3031

3132
ThreadManager::Initialize(); // This will initialize ALL Thread instances in your code!
33+
}
34+
35+
void loop() {
36+
// You can still use your main loop as normal, if you want to!
3237
}

src/ESPressio_IThread.hpp

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// #define CORE_THREADING_DEBUG // Uncomment this to explicitly enable debugging for the threading module
44
#include <Arduino.h>
55
#include <cstdint>
6+
#include <functional>
67

78
namespace ESPressio {
89

@@ -29,16 +30,12 @@ namespace ESPressio {
2930
/// `Initialize` is invoked automatically for all Threads when the `ThreadManager` is initialized in your `main()` (or `setup()` for MCU projects) function.
3031
virtual void Initialize() = 0;
3132

32-
/*
33-
`Terminate` is invoked automatically for all Threads when the `ThreadManager` is terminated in your `main()` (or `loop()` for MCU projects) function.
34-
You can, however, invoke it manually to terminate a Thread at any time!
35-
*/
33+
/// `Terminate` is invoked automatically for all Threads when the `ThreadManager` is terminated in your `main()` (or `loop()` for MCU projects) function.
34+
/// You can, however, invoke it manually to terminate a Thread at any time!
3635
virtual void Terminate() = 0;
3736

38-
/*
39-
`Start` will start the Thread loop if it is not already running.
40-
It will also Resume the thread if it is `Paused`.
41-
*/
37+
/// `Start` will start the Thread loop if it is not already running.
38+
/// It will also Resume the thread if it is `Paused`.
4239
virtual void Start() = 0;
4340

4441
/// `Pause` will pause the Thread loop if it is running.
@@ -68,6 +65,7 @@ namespace ESPressio {
6865
virtual bool GetStartOnInitialize() = 0;
6966

7067
// Utility Getters
68+
7169
bool IsRunning() { return GetThreadState() == ThreadState::Running; }
7270

7371
bool IsPaused() { return GetThreadState() == ThreadState::Paused; }
@@ -76,7 +74,25 @@ namespace ESPressio {
7674

7775
bool IsTerminated() { return GetThreadState() == ThreadState::Terminated; }
7876

77+
// Callback Getters
78+
79+
/// `GetOnInitialized` returns the callback to be invoked when the Thread is initialized.
80+
virtual std::function<void(IThread*)> GetOnInitialized() = 0;
81+
82+
/// `GetOnStarted` returns the callback to be invoked when the Thread is started.
83+
virtual std::function<void(IThread*)> GetOnStarted() = 0;
84+
85+
/// `GetOnPaused` returns the callback to be invoked when the Thread is paused.
86+
virtual std::function<void(IThread*)> GetOnPaused() = 0;
87+
88+
/// `GetOnTerminated` returns the callback to be invoked when the Thread is terminated.
89+
virtual std::function<void(IThread*)> GetOnTerminated() = 0;
90+
91+
/// `GetOnDestroying` returns the callback to be invoked when the Thread is being destroyed.
92+
virtual std::function<void(IThread*)> GetOnDestroying() = 0;
93+
7994
// Setters
95+
8096
/// `SetCoreID` sets the ID of the Core the Thread should run on.
8197
virtual void SetCoreID(BaseType_t value) = 0;
8298

@@ -88,6 +104,28 @@ namespace ESPressio {
88104

89105
/// `SetFreeOnTerminate` defines whether this Thread should be freed from memory when it is terminated.
90106
virtual void SetFreeOnTerminate(bool value) = 0;
107+
108+
// Callback Setters
109+
110+
/// `SetOnInitialized` sets the callback to be invoked when the Thread is initialized.
111+
/// The callback function takes `IThread*` and ideally named `sender`.
112+
virtual void SetOnInitialized(std::function<void(IThread*)>) = 0;
113+
114+
/// `SetOnStarted` sets the callback to be invoked when the Thread is started.
115+
/// The callback function takes `IThread*` and ideally named `sender`.
116+
virtual void SetOnStarted(std::function<void(IThread*)>) = 0;
117+
118+
/// `SetOnPaused` sets the callback to be invoked when the Thread is paused.
119+
/// The callback function takes `IThread*` and ideally named `sender`.
120+
virtual void SetOnPaused(std::function<void(IThread*)>) = 0;
121+
122+
/// `SetOnTerminated` sets the callback to be invoked when the Thread is terminated.
123+
/// The callback function takes `IThread*` and ideally named `sender`.
124+
virtual void SetOnTerminated(std::function<void(IThread*)>) = 0;
125+
126+
/// `SetOnDestroying` sets the callback to be invoked when the Thread is being destroyed.
127+
/// The callback function takes `IThread*` and ideally named `sender`.
128+
virtual void SetOnDestroying(std::function<void(IThread*)>) = 0;
91129
};
92130

93131
}

src/ESPressio_Thread.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace ESPressio {
1414
}
1515

1616
Thread::~Thread() {
17+
if (_onDestroy != nullptr) { _onDestroy(this); }
1718
SetThreadState(ThreadState::Destroyed);
1819
ThreadManager::GetInstance()->RemoveThread(this);
1920
if (_taskHandle != nullptr) { vTaskDelete(_taskHandle); }
@@ -22,6 +23,7 @@ namespace ESPressio {
2223
// Define the Terminate method of `Thread` here
2324
void Thread::Terminate() {
2425
SetThreadState(ThreadState::Terminated);
26+
if (_onTerminate != nullptr) { _onTerminate(this); }
2527
if (GetFreeOnTerminate()) { ThreadGarbageCollector::GetInstance()->CleanUp(); } // Automatically trigger the Garbage Collector
2628
}
2729

src/ESPressio_Thread.hpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <functional>
4+
35
#include "ESPressio_IThread.hpp"
46
#include "ESPressio_ThreadSafe.hpp"
57

@@ -20,6 +22,11 @@ namespace ESPressio {
2022
*/
2123
class Thread : public IThread {
2224
private:
25+
// Type Definitions
26+
27+
/// `TOnThreadEvent` is a function type that can be used to handle Thread events.
28+
using TOnThreadEvent = std::function<void(IThread*)>;
29+
2330
// Members
2431
uint8_t _threadID; // This is idempotent so doesn't need a `Mutex` wrapper.
2532
ReadWriteMutex<ThreadState> _threadState = ReadWriteMutex<ThreadState>(ThreadState::Uninitialized);
@@ -29,6 +36,12 @@ namespace ESPressio {
2936
ReadWriteMutex<uint32_t> _stackSize = ReadWriteMutex<uint32_t>(ESPRESSIO_THREAD_DEFAULT_STACK_SIZE);
3037
ReadWriteMutex<UBaseType_t> _priority = ReadWriteMutex<UBaseType_t>(2);
3138
ReadWriteMutex<BaseType_t> _coreID = ReadWriteMutex<BaseType_t>(0);
39+
// Callbacks
40+
TOnThreadEvent _onInitialize = nullptr;
41+
TOnThreadEvent _onStarte = nullptr;
42+
TOnThreadEvent _onPause = nullptr;
43+
TOnThreadEvent _onTerminate = nullptr;
44+
TOnThreadEvent _onDestroy = nullptr;
3245

3346
// Methods
3447
void _loop() {
@@ -69,6 +82,8 @@ namespace ESPressio {
6982
_threadState.Set(state);
7083
}
7184
public:
85+
86+
7287
// Constructor/Destructor
7388
Thread();
7489

@@ -104,6 +119,7 @@ namespace ESPressio {
104119
return;
105120
}
106121
SetThreadState(GetStartOnInitialize() ? ThreadState::Running : ThreadState::Initialized);
122+
if (_onInitialize != nullptr) { _onInitialize(this); }
107123
}
108124

109125
void Terminate();
@@ -113,10 +129,12 @@ namespace ESPressio {
113129
Initialize();
114130
}
115131
SetThreadState(ThreadState::Running);
132+
if (_onStarte != nullptr) { _onStarte(this); }
116133
}
117134

118135
void Pause() {
119136
SetThreadState(ThreadState::Paused);
137+
if (_onPause != nullptr) { _onPause(this); }
120138
}
121139

122140
// Getters
@@ -149,6 +167,28 @@ namespace ESPressio {
149167
return _startOnInitialize.Get();
150168
}
151169

170+
// Callback Getters
171+
172+
std::function<void(IThread*)> GetOnInitialized() {
173+
return _onInitialize;
174+
}
175+
176+
std::function<void(IThread*)> GetOnStarted() {
177+
return _onStarte;
178+
}
179+
180+
std::function<void(IThread*)> GetOnPaused() {
181+
return _onPause;
182+
}
183+
184+
std::function<void(IThread*)> GetOnTerminated() {
185+
return _onTerminate;
186+
}
187+
188+
std::function<void(IThread*)> GetOnDestroying() {
189+
return _onDestroy;
190+
}
191+
152192
// Setters
153193

154194
void SetCoreID(BaseType_t value) {
@@ -170,6 +210,28 @@ namespace ESPressio {
170210
void SetStartOnInitialize(bool value) {
171211
_startOnInitialize.Set(value);
172212
}
213+
214+
// Callback Setters
215+
216+
void SetOnInitialized(std::function<void(IThread*)> value) {
217+
_onInitialize = value;
218+
}
219+
220+
void SetOnStarted(std::function<void(IThread*)> value) {
221+
_onStarte = value;
222+
}
223+
224+
void SetOnPaused(std::function<void(IThread*)> value) {
225+
_onPause = value;
226+
}
227+
228+
void SetOnTerminated(std::function<void(IThread*)> value) {
229+
_onTerminate = value;
230+
}
231+
232+
void SetOnDestroying(std::function<void(IThread*)> value) {
233+
_onDestroy = value;
234+
}
173235
};
174236

175237
}

0 commit comments

Comments
 (0)