Simple question, I think.
I have one thread that responds to a callback that is called when a user connects via TCP. That callback wants an answer if I accept or reject the login. Problem is I have to send a login request to a security server via asynchronous message passing and wait for a response.
What is the best way to handle this? Right now I have some code that just loops in the callback testing to se开发者_如何学运维e if the security server has sent a reply and when it comes in I read it and return the appropriate boolean. It just seems kind of gross.
TIA
First of all, you need a locking library that contains a monitor class with the ability to:
- acquire a lock, guaranteeing mutual exclusion, i.e. that only one thread can hold the lock at any time
- sleep on the lock, which means releasing the lock temporarily, and sleeping until the lock can be reacquired
- signal on the lock, notifying sleeping threads that they should wake up and reacquire the lock. It is only possible to signal on the lock while holding the lock. This means that the signal will never have the immediate effect of waking up other threads. Typically the signaling thread will signal and then immediately release the lock, allowing the threads that have just been signaled to wake up. Waking up has the effect of a return of the blocking call to sleep, with the lock reacquired.
So with that functionality available from some library, you need to implement a security server proxy that uses the asynchronous security server to implement a synchronous service. When the synchronous authenticate()
function is called in some thread (denoted thread 1), this is what should happen:
- proxy acquires the lock
- proxy sends a request message to the security server
- proxy goes to sleep, waiting for the result, thread 1 is now blocked and the lock is available
- security server computes a result
- security server sends a message with the result to the proxy
- the proxy message handler function is called in thread 2, thread 1 is still blocked
- the proxy acquires the lock in thread 2
- the proxy retrieves the result from the message and stores it in a member variable
- the proxy signals on the lock, causing thread 1 blocking on sleep to try to wake up, but it can't, because thread 2 still holds the lock (inside the
sleep()
function, thread 2 is now blocked on a call to acquire the lock) - the proxy message handler releases its lock
- the sleep call in thread 1 reacquires the lock and returns
- the synchronous function in thread 1 then immediately releases its lock and returns the result
The last part with thread 1 reacquiring the lock only to immediately release it may seem pointless, but it matters because this ensures that the message handler is done before the synchronous function proceeds.
In pseudocode, it actually looks a lot simpler that you might expect:
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;
};
Note that this implementation cannot handle more than one request at a time! In order to handle multiple concurrent requests, you need to be able to store multiple results. You also need to store the thread handles of the threads that correspond to each request. In the message handler, you need to figure out which thread is blocking on any given request, and then just wake up the relevant thread in the signal()
call, the locking library must support this.
Note also that it is highly recommended to implement a RAII class to handle the lock()
and unlock()
calls on the monitor.
Presumably, you would block on your asynchronous call to the security server to effectively make it synchronous.
In the function that initiates the login check, after sending the message to request the check block on something. A Windows Event would work, as would a boolean flag and a boost::condition_variable
or a boost::unique_future
.
In the code that receives the response message from the security server set the event, or future or flag/condition variable. This will then wake up the initial function and allow it to return the appropriate response to the initial caller.
精彩评论