Прежде всего, вам нужна библиотека блокировки, которая содержит класс монитора с возможностью:
- получить блокировку, гарантирующую взаимное исключение, то есть, что только один поток может удерживать блокировку в любойвремя
- сна на блокировке, что означает временное снятие блокировки и сна до тех пор, пока блокировка не может быть восстановлена
- сигнал на блокировке, уведомляющий спящие потоки, что они должны проснуться и снова захватить блокировку,Сигнал о блокировке возможен только при удерживании блокировки.Это означает, что сигнал никогда не будет иметь немедленного эффекта от пробуждения других потоков.Обычно сигнальный поток сигнализирует, а затем немедленно снимает блокировку, позволяя просыпаться только что сигнализированным потокам.Пробуждение приводит к возврату блокирующего вызова в спящий режим с повторной блокировкой.
Таким образом, с помощью этой функции, доступной в некоторой библиотеке, вам необходимо реализовать прокси-сервер безопасности, использующий асинхронный режим.Сервер безопасности для реализации синхронного сервиса.Когда синхронная функция authenticate()
вызывается в каком-то потоке (обозначается как поток 1), это должно происходить следующим образом:
- прокси получает блокировку
- прокси отправляет сообщение запросасервер безопасности
- прокси переходит в спящий режим, ожидая результата, поток 1 теперь заблокирован и доступна блокировка
- сервер безопасности вычисляет результат
- сервер безопасности отправляет сообщениес результатом для прокси
- функция обработчика сообщений прокси вызывается в потоке 2, поток 1 все еще блокируется
- прокси получает блокировку в потоке 2
- проксиизвлекает результат из сообщения и сохраняет его в переменной-члене
- , прокси сигнализирует о блокировке, в результате чего поток 1, блокирующий в спящем режиме, пытается пробудиться, но не может, потому что поток 2 по-прежнему удерживаетблокировка (внутри функции
sleep()
поток 2 теперь блокируется при вызове для получения блокировки) - обработчик сообщений прокси снимает блокировку
- режим ожиданияll в потоке 1 вновь получает блокировку и возвращает
- синхронную функцию в потоке 1, а затем немедленно снимает блокировку и возвращает результат
Последняя часть с потоком 1, повторно запрашивающая блокировку только длянемедленное освобождение может показаться бессмысленным, но это важно, потому что это гарантирует, что обработчик сообщений будет выполнен до того, как синхронная функция продолжит работу.
В псевдокоде это выглядит намного проще, чем вы могли бы ожидать:
class SecutityProxy
{
public:
SecutityProxy( SecurityServer& server ) : m_server(server)
{}
Result authenticate( username, password )
{
m_monitor.lock();
m_server.send_message( username, password );
m_monitor.sleep();
m_monitor.unlock();
return m_result;
}
void message_received( message )
{
m_monitor.lock();
m_result = message;
m_monitor->signal();
m_monitor.unlock();
}
private:
SecurityServer& m_server;
Monitor m_monitor;
Result m_result;
};
Обратите внимание, что эта реализация не может обрабатывать более одного запроса одновременно!Чтобы обрабатывать несколько одновременных запросов, вы должны иметь возможность хранить несколько результатов.Вам также необходимо хранить дескрипторы потоков, которые соответствуют каждому запросу.В обработчике сообщений вам нужно выяснить, какой поток блокирует какой-либо запрос, а затем просто разбудить соответствующий поток в вызове signal()
, библиотека блокировки должна это поддерживать.
Обратите внимание, чтоНастоятельно рекомендуется реализовать класс RAII для обработки вызовов lock()
и unlock()
на мониторе.