PVA server avoid lock order violation
There is a lock order violation when a server force disconnects it's clients while sending a final message to it (or concurrent close, or probably others). The entry points are channelStateChange() and operation send(). Fix is not to hold the channel mutex when destroy()ing operations. epics::pvAccess::ServerChannelRPCRequesterImpl::destroy locks RPC requester mutex epics::pvAccess::ServerChannel::destroy locks channel mutex epics::pvAccess::ServerChannelRequesterImpl::channelStateChange epics::pvAccess::ServerChannel::unregisterRequest locks channel mutex epics::pvAccess::ServerChannelRPCRequesterImpl::destroy locks RPC requester mutex epics::pvAccess::ServerChannelRPCRequesterImpl::send
This commit is contained in:
@@ -64,16 +64,29 @@ Destroyable::shared_pointer ServerChannel::getRequest(const pvAccessID id)
|
||||
|
||||
void ServerChannel::destroy()
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
|
||||
if (_destroyed) return;
|
||||
_destroyed = true;
|
||||
|
||||
// destroy all requests
|
||||
// take ownership of _requests locally to prevent
|
||||
// removal via unregisterRequest() during iteration
|
||||
_requests_t reqs;
|
||||
_requests.swap(reqs);
|
||||
{
|
||||
Lock guard(_mutex);
|
||||
|
||||
if (_destroyed) return;
|
||||
_destroyed = true;
|
||||
|
||||
// destroy all requests
|
||||
// take ownership of _requests locally to prevent
|
||||
// removal via unregisterRequest() during iteration
|
||||
_requests.swap(reqs);
|
||||
|
||||
// close channel security session
|
||||
// TODO try catch
|
||||
_channelSecuritySession->close();
|
||||
|
||||
// ... and the channel
|
||||
// TODO try catch
|
||||
_channel->destroy();
|
||||
}
|
||||
// unlock our before destroy.
|
||||
// our mutex is subordinate to operation mutex
|
||||
|
||||
for(_requests_t::const_iterator it=reqs.begin(), end=reqs.end(); it!=end; ++it)
|
||||
{
|
||||
const _requests_t::mapped_type& req = it->second;
|
||||
@@ -81,14 +94,6 @@ void ServerChannel::destroy()
|
||||
req->destroy();
|
||||
// May still be in the send queue
|
||||
}
|
||||
|
||||
// close channel security session
|
||||
// TODO try catch
|
||||
_channelSecuritySession->close();
|
||||
|
||||
// ... and the channel
|
||||
// TODO try catch
|
||||
_channel->destroy();
|
||||
}
|
||||
|
||||
ServerChannel::~ServerChannel()
|
||||
|
||||
Reference in New Issue
Block a user