Я работаю над дизайном, который использует задачу привратника для доступа к общему ресурсу. Базовый дизайн, который у меня сейчас есть, - это одна очередь, которую получает задача привратника, и несколько задач, помещающих в нее запросы.
Это система с ограничением памяти, и я использую 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 и заставлять каждую задачу обрабатывать ее. Однако это повысит сложность потока каждой задачи, и я надеялся, что смогу этого избежать. По большей части все вызовы нужно блокировать до завершения операции.
Есть ли какая-то конструкция, которую мне не хватает, или просто лучший термин для этого типа проблемы, который я могу найти в Google? Ничего подобного в поисках не встречал.
Дополнительные примечания - две причины для использования привратника:
Требуется большой стек. Вместо того, чтобы добавлять это требование к каждой задаче, привратник может иметь единственный стек со всей необходимой памятью.
Ресурс не всегда доступен в ЦП. Он синхронизирует не только задачи в ЦП, но и задачи вне ЦП.