调用 xSemaphoreGive 后,xSemaphoreTake 不会恢复任务
xSemaphoreTake won't resume a task after xSemaphoreGive has been called
我想在 C++ 中实现 FreeRTOS 队列,以便能够将 C++ classes 与队列一起使用,这是队列的标准 FreeRTOS 实现不可能实现的。
class 看起来像这样:
template <typename T> class os_queue
{
public:
explicit os_queue(unsigned size);
~os_queue();
template <typename U> bool send(U &&u);
T receive(TickType_t timeout);
private:
std::vector<T> m_queue;
const unsigned m_size;
SemaphoreHandle_t m_mux;
SemaphoreHandle_t m_queue_num_elems_sem;
};
构造函数和析构函数:
template <typename T> os_queue<T>::os_queue(unsigned size) : m_size(size)
{
m_queue.reserve(size);
m_mux = xSemaphoreCreateMutex();
m_queue_num_elems_sem = xSemaphoreCreateCounting(size, 0);
}
template <typename T> os_queue<T>::~os_queue()
{
vSemaphoreDelete(m_mux);
vSemaphoreDelete(m_queue_num_elems_sem);
}
而方法send
和receive
是这样实现的:
template <typename T> template <typename U> bool os_queue<T>::send(U &&u)
{
static_assert(is_equiv<T, U>::value, "The argument must be of the same type as a queue element");
bool is_full = false;
{
os_lockguard guard(m_mux);
if (m_queue.size() == m_size)
is_full = true;
else
m_queue.emplace_back(std::forward<U>(u));
}
if (!is_full)
assert(xSemaphoreGive(m_queue_num_elems_sem) == pdTRUE);
return !is_full;
}
template <typename T> T os_queue<T>::receive(TickType_t timeout)
{
if (xSemaphoreTake(m_queue_num_elems_sem, timeout) == pdFALSE)
throw std::runtime_error("Timeout when receiving element from the queue");
else
{
os_lockguard guard(m_mux);
auto res = std::move(m_queue[m_queue.size() - 1]);
m_queue.pop_back();
return res;
}
}
os_lockguard
在构建时使用 mutex/semaphore 使用 xSemaphoreTake
并在销毁时调用 xSemaphoreGive
来释放它。
计数信号量用于控制队列中元素的个数
我想测试该解决方案并使用了 FreeRTOS port for Linux。
为了将任何函数作为任务代码传递,我创建了这样一个 class:
class os_task
{
public:
template <typename TaskFuncType>
os_task(TaskFuncType &&task_code, std::string name, unsigned short stack_size, UBaseType_t priority)
: task_code(std::forward<TaskFuncType>(task_code))
{
if (xTaskCreate(
[](void *p) {
auto os_task_p = static_cast<os_task *>(p);
os_task_p->task_code();
os_wait_indefinitely();
},
name.c_str(),
stack_size,
this,
priority,
&task_handle) != pdPASS)
throw std::runtime_error("Could not create a task");
}
~os_task() { vTaskDelete(task_handle); }
private:
TaskHandle_t task_handle;
std::function<void(void)> task_code;
};
使用这个 class 我想测试一个简单的案例,当有一个 reader 和一个作者时:
static void test_single_writer_single_reader()
{
constexpr unsigned size = 16;
os_queue<int> q(size);
bool result_reader = false, result_writer = false;
auto curr_task_handle = xTaskGetCurrentTaskHandle();
os_task(std::bind(reader, std::ref(q), std::ref(result_reader), curr_task_handle), "reader", 128, 1);
os_task(std::bind(writer, std::ref(q), std::ref(result_writer), curr_task_handle), "writer", 128, 1);
try
{
wait_for_notifs(2);
}
catch (std::exception &e)
{
TEST_ASSERT_MESSAGE(false == true, e.what());
}
TEST_ASSERT(result_reader == true);
TEST_ASSERT(result_writer == true);
}
此测试是从 FreeRTOS 任务中调用的。
reader 和编写器以队列作为参考来调用 send
和 receive
方法和一个布尔变量以将操作结果保存在某处:
static void reader(os_queue<int> &queue, bool &result, TaskHandle_t task_to_notify_at_end)
{
int r;
result = true;
try
{
r = queue.receive(max_wait_time_ms);
}
catch (std::exception &e)
{
result = false;
}
xTaskNotifyGive(task_to_notify_at_end);
}
static void writer(os_queue<int> &queue, bool &result, TaskHandle_t task_to_notify_at_end)
{
int v = 0;
result = queue.send(v);
xTaskNotifyGive(task_to_notify_at_end);
}
最后wait_for_notifs
用于同步任务与reader和writer:
static void wait_for_notifs(BaseType_t notifs_num)
{
while (notifs_num)
{
auto notifs = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (notifs)
notifs_num -= notifs;
else
throw std::runtime_error("Timeout when waiting for notifications");
}
}
主要的想法是我想检查应用程序是否能够很好地处理比赛。
问题是,当 reader 首先执行并在 xSemaphoreTask
暂停时,写入器写入队列,因此它会增加信号量的计数器,reader 不会简历。
应该不行吧? xSemaphoreTake
中的超时可以很好地处理这种情况:当信号量被获取时(其计数等于 0),函数等待并希望其他任务可以提供(增加计数器)信号量。
会不会跟端口实现或者配置有关?
我使用 9.0.0 版的 FreeRTOS,启用了抢占和时间分片。
问题在这里:
os_task(std::bind(reader, std::ref(q), std::ref(result_reader), curr_task_handle), "reader", 128, 1);
os_task(std::bind(writer, std::ref(q), std::ref(result_writer), curr_task_handle), "writer", 128, 1);
创建的对象刚构造完就被删除了。应该是
os_task r(std::bind(reader, std::ref(q), std::ref(result_reader), curr_task_handle), "reader", 128, 1);
os_task w(std::bind(writer, std::ref(q), std::ref(result_writer), curr_task_handle), "writer", 128, 1);
我想在 C++ 中实现 FreeRTOS 队列,以便能够将 C++ classes 与队列一起使用,这是队列的标准 FreeRTOS 实现不可能实现的。
class 看起来像这样:
template <typename T> class os_queue
{
public:
explicit os_queue(unsigned size);
~os_queue();
template <typename U> bool send(U &&u);
T receive(TickType_t timeout);
private:
std::vector<T> m_queue;
const unsigned m_size;
SemaphoreHandle_t m_mux;
SemaphoreHandle_t m_queue_num_elems_sem;
};
构造函数和析构函数:
template <typename T> os_queue<T>::os_queue(unsigned size) : m_size(size)
{
m_queue.reserve(size);
m_mux = xSemaphoreCreateMutex();
m_queue_num_elems_sem = xSemaphoreCreateCounting(size, 0);
}
template <typename T> os_queue<T>::~os_queue()
{
vSemaphoreDelete(m_mux);
vSemaphoreDelete(m_queue_num_elems_sem);
}
而方法send
和receive
是这样实现的:
template <typename T> template <typename U> bool os_queue<T>::send(U &&u)
{
static_assert(is_equiv<T, U>::value, "The argument must be of the same type as a queue element");
bool is_full = false;
{
os_lockguard guard(m_mux);
if (m_queue.size() == m_size)
is_full = true;
else
m_queue.emplace_back(std::forward<U>(u));
}
if (!is_full)
assert(xSemaphoreGive(m_queue_num_elems_sem) == pdTRUE);
return !is_full;
}
template <typename T> T os_queue<T>::receive(TickType_t timeout)
{
if (xSemaphoreTake(m_queue_num_elems_sem, timeout) == pdFALSE)
throw std::runtime_error("Timeout when receiving element from the queue");
else
{
os_lockguard guard(m_mux);
auto res = std::move(m_queue[m_queue.size() - 1]);
m_queue.pop_back();
return res;
}
}
os_lockguard
在构建时使用 mutex/semaphore 使用 xSemaphoreTake
并在销毁时调用 xSemaphoreGive
来释放它。
计数信号量用于控制队列中元素的个数
我想测试该解决方案并使用了 FreeRTOS port for Linux。
为了将任何函数作为任务代码传递,我创建了这样一个 class:
class os_task
{
public:
template <typename TaskFuncType>
os_task(TaskFuncType &&task_code, std::string name, unsigned short stack_size, UBaseType_t priority)
: task_code(std::forward<TaskFuncType>(task_code))
{
if (xTaskCreate(
[](void *p) {
auto os_task_p = static_cast<os_task *>(p);
os_task_p->task_code();
os_wait_indefinitely();
},
name.c_str(),
stack_size,
this,
priority,
&task_handle) != pdPASS)
throw std::runtime_error("Could not create a task");
}
~os_task() { vTaskDelete(task_handle); }
private:
TaskHandle_t task_handle;
std::function<void(void)> task_code;
};
使用这个 class 我想测试一个简单的案例,当有一个 reader 和一个作者时:
static void test_single_writer_single_reader()
{
constexpr unsigned size = 16;
os_queue<int> q(size);
bool result_reader = false, result_writer = false;
auto curr_task_handle = xTaskGetCurrentTaskHandle();
os_task(std::bind(reader, std::ref(q), std::ref(result_reader), curr_task_handle), "reader", 128, 1);
os_task(std::bind(writer, std::ref(q), std::ref(result_writer), curr_task_handle), "writer", 128, 1);
try
{
wait_for_notifs(2);
}
catch (std::exception &e)
{
TEST_ASSERT_MESSAGE(false == true, e.what());
}
TEST_ASSERT(result_reader == true);
TEST_ASSERT(result_writer == true);
}
此测试是从 FreeRTOS 任务中调用的。
reader 和编写器以队列作为参考来调用 send
和 receive
方法和一个布尔变量以将操作结果保存在某处:
static void reader(os_queue<int> &queue, bool &result, TaskHandle_t task_to_notify_at_end)
{
int r;
result = true;
try
{
r = queue.receive(max_wait_time_ms);
}
catch (std::exception &e)
{
result = false;
}
xTaskNotifyGive(task_to_notify_at_end);
}
static void writer(os_queue<int> &queue, bool &result, TaskHandle_t task_to_notify_at_end)
{
int v = 0;
result = queue.send(v);
xTaskNotifyGive(task_to_notify_at_end);
}
最后wait_for_notifs
用于同步任务与reader和writer:
static void wait_for_notifs(BaseType_t notifs_num)
{
while (notifs_num)
{
auto notifs = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
if (notifs)
notifs_num -= notifs;
else
throw std::runtime_error("Timeout when waiting for notifications");
}
}
主要的想法是我想检查应用程序是否能够很好地处理比赛。
问题是,当 reader 首先执行并在 xSemaphoreTask
暂停时,写入器写入队列,因此它会增加信号量的计数器,reader 不会简历。
应该不行吧? xSemaphoreTake
中的超时可以很好地处理这种情况:当信号量被获取时(其计数等于 0),函数等待并希望其他任务可以提供(增加计数器)信号量。
会不会跟端口实现或者配置有关?
我使用 9.0.0 版的 FreeRTOS,启用了抢占和时间分片。
问题在这里:
os_task(std::bind(reader, std::ref(q), std::ref(result_reader), curr_task_handle), "reader", 128, 1);
os_task(std::bind(writer, std::ref(q), std::ref(result_writer), curr_task_handle), "writer", 128, 1);
创建的对象刚构造完就被删除了。应该是
os_task r(std::bind(reader, std::ref(q), std::ref(result_reader), curr_task_handle), "reader", 128, 1);
os_task w(std::bind(writer, std::ref(q), std::ref(result_writer), curr_task_handle), "writer", 128, 1);