Skip to content

Commit cdaddfa

Browse files
authored
feat(task): Allow task thread to call stop(); add support for getting task thread id (#309)
* feat(task): Allow task thread to call stop(); add support for getting task thread id * update to ensure task_handle_ is unset when joined * fix printing of id on other platforms * update how id is managed for cleaner / more maintainable code * only use task_handle_ on esp
1 parent dffe2ca commit cdaddfa

File tree

3 files changed

+96
-11
lines changed

3 files changed

+96
-11
lines changed

Diff for: components/task/example/main/task_example.cpp

+44-2
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,9 @@ extern "C" void app_main(void) {
367367
task.start();
368368
auto stop_fn = [&task]() {
369369
std::this_thread::sleep_for(1s);
370-
auto thread = xTaskGetCurrentTaskHandle();
371-
fmt::print("Stopping task from thread {}...\n", fmt::ptr(thread));
370+
// NOTE: on ESP-IDF, this is the same as xTaskGetCurrentTaskHandle();
371+
auto thread = espp::Task::get_current_id();
372+
fmt::print("Stopping task from thread {}...\n", thread);
372373
task.stop();
373374
};
374375
// make vector of threads to stop the task
@@ -387,6 +388,47 @@ extern "C" void app_main(void) {
387388
//! [Task Request Stop From Multiple Threads example]
388389
}
389390

391+
/**
392+
* Show an example of a task which calls stop on itself from within the task
393+
*/
394+
test_start = std::chrono::high_resolution_clock::now();
395+
{
396+
fmt::print("Task Request Stop From Within Task example\n");
397+
//! [Task Request Stop From Within Task example]
398+
espp::Task task = espp::Task({.name = "Self Stopping Task",
399+
.callback =
400+
[&num_seconds_to_run, &task](std::mutex &m, std::condition_variable &cv) {
401+
static auto begin = std::chrono::high_resolution_clock::now();
402+
auto now = std::chrono::high_resolution_clock::now();
403+
auto elapsed = std::chrono::duration<float>(now - begin).count();
404+
fmt::print("Task has run for {:.03f} seconds\n", elapsed);
405+
// NOTE: sleeping in this way allows the sleep to exit early when the
406+
// task is being stopped / destroyed
407+
{
408+
std::unique_lock<std::mutex> lk(m);
409+
cv.wait_for(lk, 100ms);
410+
}
411+
if (elapsed > num_seconds_to_run) {
412+
fmt::print("Stopping task from within task...\n");
413+
task.stop();
414+
}
415+
// do some other work here which can't be preempted, this helps force the
416+
// stopping threads to try to contend on the thread join within the stop
417+
// call
418+
std::this_thread::sleep_for(50ms);
419+
// we don't want to stop yet, so return false
420+
return false;
421+
},
422+
423+
.log_level = espp::Logger::Verbosity::DEBUG});
424+
task.start();
425+
while (task.is_started()) {
426+
std::this_thread::sleep_for(50ms);
427+
}
428+
fmt::print("Task successfully stopped by itself!\n");
429+
//! [Task Request Stop From Within Task example]
430+
}
431+
390432
{
391433
//! [run on core example]
392434
fmt::print("Example main running on core {}\n", xPortGetCoreID());

Diff for: components/task/include/task.hpp

+38-3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ namespace espp {
4444
*/
4545
class Task : public espp::BaseComponent {
4646
public:
47+
#if defined(ESP_PLATFORM)
48+
typedef void* task_id_t;
49+
#else
50+
typedef std::thread::id task_id_t;
51+
#endif
52+
4753
/**
4854
* @brief Task callback function signature.
4955
*
@@ -191,10 +197,12 @@ class Task : public espp::BaseComponent {
191197
bool start();
192198

193199
/**
194-
* @brief Stop the task execution, blocking until it stops.
195-
*
200+
* @brief Stop the task execution.
201+
* @details This will request the task to stop, notify the condition variable,
202+
* and (if this calling context is not the task context) join the
203+
* thread.
196204
* @return true if the task stopped, false if it was not started / already
197-
* stopped.
205+
* stopped.
198206
*/
199207
bool stop();
200208

@@ -231,6 +239,30 @@ class Task : public espp::BaseComponent {
231239
static std::string get_info(const Task &task);
232240
#endif
233241

242+
/**
243+
* @brief Get the ID for this Task's thread / task context.
244+
* @return ID for this Task's thread / task context.
245+
*/
246+
task_id_t get_id() const {
247+
#if defined(ESP_PLATFORM)
248+
return task_handle_;
249+
#else
250+
return thread_.get_id();
251+
#endif
252+
}
253+
254+
/**
255+
* @brief Get the ID for the current thread / task context.
256+
* @return ID for the current thread / task context.
257+
*/
258+
static task_id_t get_current_id() {
259+
#if defined(ESP_PLATFORM)
260+
return static_cast<task_id_t>(xTaskGetCurrentTaskHandle());
261+
#else
262+
return std::this_thread::get_id();
263+
#endif
264+
}
265+
234266
protected:
235267
/**
236268
* @brief Function that is run in the task thread.
@@ -256,6 +288,9 @@ class Task : public espp::BaseComponent {
256288
std::mutex cv_m_;
257289
std::mutex thread_mutex_;
258290
std::thread thread_;
291+
#if defined(ESP_PLATFORM)
292+
task_id_t task_handle_;
293+
#endif
259294
};
260295
} // namespace espp
261296

Diff for: components/task/src/task.cpp

+14-6
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,16 @@ void Task::notify_and_join() {
108108
std::lock_guard<std::mutex> lock(cv_m_);
109109
cv_.notify_all();
110110
}
111-
{
112-
std::lock_guard<std::mutex> lock(thread_mutex_);
113-
if (thread_.joinable()) {
114-
thread_.join();
115-
}
111+
auto thread_id = get_id();
112+
auto current_id = get_current_id();
113+
logger_.debug("Thread id: {}, current id: {}", thread_id, current_id);
114+
// check to ensure we're not the same thread
115+
std::lock_guard<std::mutex> lock(thread_mutex_);
116+
if (thread_.joinable() && current_id != thread_id) {
117+
thread_.join();
118+
#if defined(ESP_PLATFORM)
119+
task_handle_ = nullptr;
120+
#endif
116121
}
117122
}
118123

@@ -127,14 +132,17 @@ std::string Task::get_info() {
127132
}
128133

129134
std::string Task::get_info(const Task &task) {
130-
TaskHandle_t freertos_handle = xTaskGetHandle(task.name_.c_str());
135+
TaskHandle_t freertos_handle = static_cast<TaskHandle_t>(task.get_id());
131136
return fmt::format("[T] '{}',{},{},{}\n", pcTaskGetName(freertos_handle), xPortGetCoreID(),
132137
uxTaskPriorityGet(freertos_handle),
133138
uxTaskGetStackHighWaterMark(freertos_handle));
134139
}
135140
#endif
136141

137142
void Task::thread_function() {
143+
#if defined(ESP_PLATFORM)
144+
task_handle_ = get_current_id();
145+
#endif
138146
while (started_) {
139147
if (callback_) {
140148
bool should_stop = callback_(cv_m_, cv_);

0 commit comments

Comments
 (0)