From 5e54969f6bf9369cde84c73be2ccc54f7f41e996 Mon Sep 17 00:00:00 2001 From: Thad House Date: Wed, 2 Nov 2016 22:17:40 -0700 Subject: [PATCH] Adds Task back into WPILibC (#320) Note that deprecation has not been added yet, since we need to add the deprecation API's for MSVC and GCC < 4.9. --- wpilibc/athena/include/Task.h | 67 +++++++++++++++ wpilibc/athena/include/Task.inc | 44 ++++++++++ wpilibc/athena/src/Task.cpp | 140 ++++++++++++++++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 wpilibc/athena/include/Task.h create mode 100644 wpilibc/athena/include/Task.inc create mode 100644 wpilibc/athena/src/Task.cpp diff --git a/wpilibc/athena/include/Task.h b/wpilibc/athena/include/Task.h new file mode 100644 index 0000000000..f759c3caaf --- /dev/null +++ b/wpilibc/athena/include/Task.h @@ -0,0 +1,67 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2008-2016. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include + +#include "ErrorBase.h" +#include "HAL/HAL.h" + +namespace frc { + +/** + * Wrapper class around std::thread that allows changing thread priority + */ +class Task : public ErrorBase { + public: + static const int kDefaultPriority = 60; + + Task() = default; + Task(const Task&) = delete; + Task& operator=(const Task&) = delete; + Task& operator=(Task&& task); + + template + Task(const std::string& name, Function&& function, Args&&... args); + + virtual ~Task(); + + bool joinable() const noexcept; + void join(); + void detach(); + std::thread::id get_id() const noexcept; + std::thread::native_handle_type native_handle(); + + bool Verify(); + + int GetPriority(); + + bool SetPriority(int priority); + + std::string GetName() const; + + private: + std::thread m_thread; + std::string m_taskName; + + typedef int32_t TASK_STATUS; + + static constexpr int32_t TASK_OK = 0; + static constexpr int32_t TASK_ERROR = -1; + static constexpr int32_t TaskLib_ILLEGAL_PRIORITY = 22; // 22 is EINVAL + + bool HandleError(TASK_STATUS results); + TASK_STATUS VerifyTaskId(); + TASK_STATUS GetTaskPriority(int32_t* priority); + TASK_STATUS SetTaskPriority(int32_t priority); +}; + +} // namespace frc + +#include "Task.inc" diff --git a/wpilibc/athena/include/Task.inc b/wpilibc/athena/include/Task.inc new file mode 100644 index 0000000000..22859aacb6 --- /dev/null +++ b/wpilibc/athena/include/Task.inc @@ -0,0 +1,44 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2016. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace frc { + +/** + * Create and launch a task. + * + * @param name The name of the task. "FRC_" will be prepended to the task name. + * @param function The address of the function to run as the new task. + * @param args A parameter pack of arguments to pass to the function. + */ +template +Task::Task(const std::string& name, Function&& function, Args&&... args) { + m_taskName = "FRC_"; + m_taskName += name; + + std::cout << "[HAL] Starting task " << m_taskName << "..." << std::endl; + + m_thread = std::thread(std::forward>(function), + std::forward(args)...); + // TODO: lvuser does not currently have permissions to set the priority. + // SetPriority(kDefaultPriority); + + static std::atomic instances{0}; + instances++; + HAL_Report(HALUsageReporting::kResourceType_Task, instances, 0, + m_taskName.c_str()); +} + +} // namespace frc diff --git a/wpilibc/athena/src/Task.cpp b/wpilibc/athena/src/Task.cpp new file mode 100644 index 0000000000..d7ea5181ed --- /dev/null +++ b/wpilibc/athena/src/Task.cpp @@ -0,0 +1,140 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) FIRST 2008-2016. All Rights Reserved. */ +/* Open Source Software - may be modified and shared by FRC teams. The code */ +/* must be accompanied by the FIRST BSD license file in the root directory of */ +/* the project. */ +/*----------------------------------------------------------------------------*/ + +#include "Task.h" + +#include + +#include + +#include "WPIErrors.h" + +namespace frc { + +const int Task::kDefaultPriority; + +Task& Task::operator=(Task&& task) { + m_thread.swap(task.m_thread); + m_taskName = std::move(task.m_taskName); + + return *this; +} + +Task::~Task() { + if (m_thread.joinable()) { + std::cout << "[HAL] Exited task " << m_taskName << std::endl; + } +} + +bool Task::joinable() const noexcept { return m_thread.joinable(); } + +void Task::join() { m_thread.join(); } + +void Task::detach() { m_thread.detach(); } + +std::thread::id Task::get_id() const noexcept { return m_thread.get_id(); } + +std::thread::native_handle_type Task::native_handle() { + return m_thread.native_handle(); +} + +/** + * Verifies a task still exists. + * + * @return true on success. + */ +bool Task::Verify() { return VerifyTaskId() == TASK_OK; } + +/** + * Gets the priority of a task. + * + * @return task priority or 0 if an error occured + */ +int Task::GetPriority() { + int priority; + if (HandleError(GetTaskPriority(&priority))) + return priority; + else + return 0; +} + +/** + * This routine changes a task's priority to a specified priority. + * Priorities range from 1, the lowest priority, to 99, the highest priority. + * Default task priority is 60. + * + * @param priority The priority at which the internal thread should run. + * @return true on success. + */ +bool Task::SetPriority(int priority) { + return HandleError(SetTaskPriority(priority)); +} + +/** + * Returns the name of the task. + * + * @return The name of the task. + */ +std::string Task::GetName() const { return m_taskName; } + +Task::TASK_STATUS Task::VerifyTaskId() { + auto task = m_thread.native_handle(); + if (pthread_kill(task, 0) == 0) { + return TASK_OK; + } else { + return TASK_ERROR; + } +} + +Task::TASK_STATUS Task::GetTaskPriority(int32_t* priority) { + auto task = m_thread.native_handle(); + int32_t policy = 0; + struct sched_param param; + + if (VerifyTaskId() == TASK_OK && + pthread_getschedparam(task, &policy, ¶m) == 0) { + *priority = param.sched_priority; + return TASK_OK; + } else { + return TASK_ERROR; + } +} + +Task::TASK_STATUS Task::SetTaskPriority(int32_t priority) { + auto task = m_thread.native_handle(); + int32_t policy = 0; + struct sched_param param; + + if (VerifyTaskId() == TASK_OK && + pthread_getschedparam(task, &policy, ¶m) == 0) { + param.sched_priority = priority; + if (pthread_setschedparam(task, SCHED_FIFO, ¶m) == 0) { + return TASK_OK; + } else { + return TASK_ERROR; + } + } else { + return TASK_ERROR; + } +} + +/** + * Handles errors generated by task related code. + */ +bool Task::HandleError(TASK_STATUS results) { + if (results != TASK_ERROR) return true; + int errsv = errno; + if (errsv == TaskLib_ILLEGAL_PRIORITY) { + wpi_setWPIErrorWithContext(TaskPriorityError, m_taskName.c_str()); + } else { + std::printf("ERROR: errno=%i", errsv); + wpi_setWPIErrorWithContext(TaskError, m_taskName.c_str()); + } + return false; +} + +} // namespace frc