Я работаю над дизайном, который использует задачу привратника для доступа к общему ресурсу. Базовый дизайн, который у меня есть сейчас, - это одна очередь, от которой получает задание привратника, и несколько заданий, помещающих в него запросы.
Это ограниченная память, и я использую FreeRTOS (порт Cortex M3).
Проблема заключается в следующем: асинхронно обрабатывать эти запросы довольно просто. Запрашивающая задача ставит в очередь свой запрос и выполняет свою работу, опрашивает, обрабатывает или ожидает других событий. Чтобы обрабатывать эти запросы синхронно, мне нужен механизм для блокирования запрашивающей задачи, чтобы после обработки запроса привратник мог активировать задачу, вызвавшую этот запрос.
Самым простым проектом, который я могу придумать, было бы включение семафора в каждый запрос, но, учитывая ограничения памяти и довольно большой размер семафора во FreeRTOS, это не практично.
То, что я придумал, - это использование функции приостановки и возобновления задачи для ручной блокировки задачи, передачи дескриптора привратнику, с помощью которого он может возобновить задачу, когда запрос завершен. Однако есть некоторые проблемы с приостановкой / возобновлением, и я бы очень хотел их избежать. Один вызов возобновит выполнение задачи независимо от того, сколько раз оно было приостановлено другими вызовами, и это может привести к нежелательному поведению.
Несколько простых псевдо-C для демонстрации метода приостановки / возобновления.
void gatekeeper_blocking_request(void)
{
put_request_in_queue(request);
task_suspend(this_task);
}
void gatekeeper_request_complete_callback(request)
{
task_resume(request->task);
}
Обходной путь, который я планирую использовать в это время, заключается в использовании асинхронных вызовов и полной блокировке в каждой запрашивающей задаче. Гейткипер выполнит предоставленный обратный вызов, когда операция завершится, и затем он может выполнить передачу в основную очередь задачи или в определенный семафор, или что угодно. Наличие блокирующих вызовов для запросов по существу является удобной функцией, поэтому каждая запрашивающая задача не должна реализовывать это.
Псевдо-C для демонстрации блокирования для конкретной задачи, но это необходимо реализовать в каждой задаче.
void requesting_task(void)
{
while(1)
{
gatekeeper_async_request(callback);
pend_on_sempahore(sem);
}
}
void callback(request)
{
post_to_semaphore(sem);
}
Возможно, лучшее решение - просто не реализовывать блокировку в привратнике и API и заставлять каждую задачу справляться с этим. Это увеличит сложность потока каждой задачи, и я надеялся, что смогу избежать этого. По большей части все вызовы будут блокироваться до завершения операции.
Есть какая-то конструкция, которую я пропускаю, или даже просто лучший термин для проблемы такого типа, которую я могу гуглить? Я не встречал ничего подобного в своих поисках.
Дополнительные замечания - Две причины для задачи привратника:
Требуется большое пространство в стеке. Вместо того, чтобы добавлять это требование к каждой задаче, привратник может иметь один стек со всей необходимой памятью.
Ресурс не всегда доступен в ЦП. Он синхронизирует не только задачи в ЦП, но и задачи вне ЦП.