1
0
mirror of https://github.com/solemnwarning/directplay-lite synced 2024-12-30 16:45:37 +01:00

Remove code duplication in disconnect/cleanup paths.

This commit is contained in:
Daniel Collins 2018-10-15 18:13:10 +01:00
parent b74b018ff2
commit 9fc69d67ea
2 changed files with 139 additions and 269 deletions

View File

@ -1475,23 +1475,7 @@ HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
ei->second.cancel();
}
if(discovery_socket != -1)
{
closesocket(discovery_socket);
discovery_socket = -1;
}
if(listener_socket != -1)
{
closesocket(listener_socket);
listener_socket = -1;
}
if(udp_socket != -1)
{
closesocket(udp_socket);
udp_socket = -1;
}
close_main_sockets();
if(state == STATE_CONNECTING_TO_HOST || state == STATE_CONNECTING_TO_PEERS)
{
@ -1513,66 +1497,16 @@ HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
if(was_hosting)
{
/* Raise a DPNMSG_DESTROY_PLAYER for ourself. */
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = local_player_id;
dp.pvPlayerContext = local_player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_NORMAL;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_NORMAL);
}
if(dwFlags & DPNCLOSE_IMMEDIATE)
{
while(!peers.empty())
{
unsigned int peer_id = peers.begin()->first;
peer_destroy(l, peer_id, DPNERR_USERCANCEL, DPNDESTROYPLAYERREASON_NORMAL);
}
peer_destroy_all(l, DPNERR_USERCANCEL, DPNDESTROYPLAYERREASON_NORMAL);
}
else{
for(auto pi = peers.begin(); pi != peers.end();)
{
unsigned int peer_id = pi->first;
Peer *peer = pi->second;
if(peer->state == Peer::PS_CONNECTED)
{
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(dp);
dp.dpnidPlayer = peer->player_id;
dp.pvPlayerContext = peer->player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_NORMAL;
peer->state = Peer::PS_CLOSING;
/* Wake up a worker to deal with closing the connection. */
SetEvent(peer->event);
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
pi = peers.begin();
}
else if(peer->state == Peer::PS_CLOSING)
{
/* Do nothing. We're waiting for this peer to go away. */
++pi;
}
else{
peer_destroy(l, peer_id, DPNERR_USERCANCEL, DPNDESTROYPLAYERREASON_NORMAL);
pi = peers.begin();
}
}
/* Initiate graceful shutdown of all peers. */
peer_shutdown_all(l, DPNERR_USERCANCEL, DPNDESTROYPLAYERREASON_NORMAL);
/* Wait for remaining peers to finish disconnecting. */
peer_destroyed.wait(l, [this]() { return peers.empty(); });
@ -1581,18 +1515,7 @@ HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
if(was_connected)
{
/* Raise a DPNMSG_DESTROY_PLAYER for ourself. */
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = local_player_id;
dp.pvPlayerContext = local_player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_NORMAL;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_NORMAL);
}
/* Wait for outstanding EnumHosts() calls. */
@ -1719,6 +1642,9 @@ HRESULT DirectPlay8Peer::DestroyPeer(CONST DPNID dpnidClient, CONST void* CONST
return DPNERR_INVALIDPLAYER;
}
/* dpnidClient must be present in player_to_peer_id for the above to have succeeded. */
unsigned int peer_id = player_to_peer_id[dpnidClient];
PacketSerialiser destroy_peer_base(DPLITE_MSGID_DESTROY_PEER);
destroy_peer_base.append_dword(peer->player_id);
@ -1729,22 +1655,7 @@ HRESULT DirectPlay8Peer::DestroyPeer(CONST DPNID dpnidClient, CONST void* CONST
/* Notify the peer we are destroying it and initiate the connection shutdown. */
peer->sq.send(SendQueue::SEND_PRI_HIGH, destroy_peer_full, NULL, [](std::unique_lock<std::mutex> &l, HRESULT result) {});
peer->state = Peer::PS_CLOSING;
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(dp);
dp.dpnidPlayer = peer->player_id;
dp.pvPlayerContext = peer->player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_HOSTDESTROYEDPLAYER;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
player_to_peer_id.erase(dpnidClient);
peer_shutdown(l, peer_id, DPNERR_HOSTTERMINATEDSESSION, DPNDESTROYPLAYERREASON_HOSTDESTROYEDPLAYER);
/* Notify the other peers, in case the other peer is malfunctioning and doesn't remove
* itself from the session gracefully.
@ -1975,23 +1886,7 @@ HRESULT DirectPlay8Peer::TerminateSession(void* CONST pvTerminateData, CONST DWO
case STATE_TERMINATED: return DPNERR_HOSTTERMINATEDSESSION;
}
if(discovery_socket != -1)
{
closesocket(discovery_socket);
discovery_socket = -1;
}
if(listener_socket != -1)
{
closesocket(listener_socket);
listener_socket = -1;
}
if(udp_socket != -1)
{
closesocket(udp_socket);
udp_socket = -1;
}
close_main_sockets();
/* First, we iterate over all the peers.
*
@ -2051,36 +1946,13 @@ HRESULT DirectPlay8Peer::TerminateSession(void* CONST pvTerminateData, CONST DWO
}
/* Raise a DPNMSG_DESTROY_PLAYER for ourself. */
{
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = local_player_id;
dp.pvPlayerContext = local_player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_SESSIONTERMINATED;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
}
dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_SESSIONTERMINATED);
/* Raise a DPNMSG_DESTROY_PLAYER for each connected peer. */
for(auto cp = closing_peers.begin(); cp != closing_peers.end(); ++cp)
{
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(dp);
dp.dpnidPlayer = cp->first;
dp.pvPlayerContext = cp->second;
dp.dwReason = DPNDESTROYPLAYERREASON_SESSIONTERMINATED;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
dispatch_destroy_player(l, cp->first, cp->second, DPNDESTROYPLAYERREASON_SESSIONTERMINATED);
}
/* Destroy any peers which weren't fully connected. */
@ -2838,14 +2710,6 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock<std::mutex> &l, unsigned int
{
DPNID killed_player_id = peer->player_id;
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(dp);
dp.dpnidPlayer = peer->player_id;
dp.pvPlayerContext = peer->player_ctx;
dp.dwReason = destroy_player_reason;
/* Bodge to prevent io_peer_send() initiating a graceful shutdown
* while the application is handling the DPNMSG_DESTROY_PLAYER.
*/
@ -2853,9 +2717,7 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock<std::mutex> &l, unsigned int
peer->state = Peer::PS_CLOSING;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
dispatch_destroy_player(l, peer->player_id, peer->player_ctx, destroy_player_reason);
player_to_peer_id.erase(killed_player_id);
@ -2883,17 +2745,7 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock<std::mutex> &l, unsigned int
message_handler(message_handler_ctx, DPN_MSGID_TERMINATE_SESSION, &ts);
l.lock();
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = local_player_id;
dp.pvPlayerContext = local_player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_NORMAL;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_NORMAL);
while(!peers.empty())
{
@ -2925,34 +2777,91 @@ void DirectPlay8Peer::peer_destroy(std::unique_lock<std::mutex> &l, unsigned int
peer_destroyed.notify_all();
}
/* Immediately close all sockets and erase all peers. */
void DirectPlay8Peer::close_everything_now(std::unique_lock<std::mutex> &l, HRESULT outstanding_op_result, DWORD destroy_player_reason)
void DirectPlay8Peer::peer_destroy_all(std::unique_lock<std::mutex> &l, HRESULT outstanding_op_result, DWORD destroy_player_reason)
{
while(!peers.empty())
{
peer_destroy(l, peers.begin()->first, outstanding_op_result, destroy_player_reason);
unsigned int peer_id = peers.begin()->first;
peer_destroy(l, peer_id, outstanding_op_result, destroy_player_reason);
}
}
void DirectPlay8Peer::peer_shutdown(std::unique_lock<std::mutex> &l, unsigned int peer_id, HRESULT outstanding_op_result, DWORD destroy_player_reason)
{
Peer *peer = get_peer_by_peer_id(peer_id);
assert(peer != NULL);
if(peer->state == Peer::PS_CONNECTED)
{
/* Peer is a fully connected player, initiate a graceful shutdown and raise a
* DPNMSG_DESTROY_PLAYER message.
*/
peer->state = Peer::PS_CLOSING;
SetEvent(peer->event);
dispatch_destroy_player(l, peer->player_id, peer->player_ctx, destroy_player_reason);
player_to_peer_id.erase(peer_id);
}
else if(peer->state == Peer::PS_CLOSING)
{
/* We're waiting for this peer to go away. Do nothing. */
}
else{
/* Peer is not a fully fledged player, just destroy it. */
peer_destroy(l, peer_id, outstanding_op_result, destroy_player_reason);
}
}
void DirectPlay8Peer::peer_shutdown_all(std::unique_lock<std::mutex> &l, HRESULT outstanding_op_result, DWORD destroy_player_reason)
{
for(auto p = peers.begin(); p != peers.end();)
{
unsigned int peer_id = p->first;
Peer *peer = p->second;
if(peer->state == Peer::PS_CLOSING)
{
/* Peer is already shutting down. Do nothing. */
++p;
}
else{
/* Gracefully shutdown or destroy the peer as appropriate. Restart the
* loop as any iterator into peers may have been invalidated within the
* peer_shutdown() call.
*/
peer_shutdown(l, peer_id, outstanding_op_result, destroy_player_reason);
p = peers.begin();
#ifndef NDEBUG
peer = get_peer_by_peer_id(peer_id);
if(peer != NULL)
{
assert(peer->state == Peer::PS_CLOSING);
}
#endif
}
}
}
void DirectPlay8Peer::close_main_sockets()
{
if(discovery_socket != -1)
{
WSAEventSelect(discovery_socket, other_socket_event, 0);
closesocket(discovery_socket);
discovery_socket = -1;
}
if(listener_socket != -1)
{
WSAEventSelect(listener_socket, other_socket_event, 0);
closesocket(listener_socket);
listener_socket = -1;
}
if(udp_socket != -1)
{
WSAEventSelect(udp_socket, udp_socket_event, 0);
closesocket(udp_socket);
udp_socket = -1;
}
@ -3863,56 +3772,27 @@ void DirectPlay8Peer::handle_destroy_peer(std::unique_lock<std::mutex> &l, unsig
message_handler(message_handler_ctx, DPN_MSGID_TERMINATE_SESSION, &ts);
l.lock();
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_SESSIONTERMINATED);
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = local_player_id;
dp.pvPlayerContext = local_player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_SESSIONTERMINATED;
/* Forward destroyed player notification to all peers so they may raise
* DPNMSG_DESTROY_PLAYER with the correct dwReason.
*/
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
for(auto pi = peers.begin(); pi != peers.end();)
for(auto pi = peers.begin(); pi != peers.end(); ++pi)
{
unsigned int peer_id = pi->first;
Peer *peer = pi->second;
if(peer->state == Peer::PS_CONNECTED)
{
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(dp);
dp.dpnidPlayer = peer->player_id;
dp.pvPlayerContext = peer->player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_SESSIONTERMINATED;
PacketSerialiser destroy_peer(DPLITE_MSGID_DESTROY_PEER);
destroy_peer.append_dword(local_player_id);
peer->sq.send(SendQueue::SEND_PRI_HIGH, destroy_peer, NULL, [](std::unique_lock<std::mutex> &l, HRESULT result) {});
peer->state = Peer::PS_CLOSING;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
pi = peers.begin();
}
else if(peer->state == Peer::PS_CLOSING)
{
/* Do nothing. We're waiting for this peer to go away. */
++pi;
}
else{
peer_destroy(l, peer_id, DPNERR_HOSTTERMINATEDSESSION, DPNDESTROYPLAYERREASON_NORMAL);
pi = peers.begin();
}
}
peer_shutdown_all(l, DPNERR_HOSTTERMINATEDSESSION, DPNDESTROYPLAYERREASON_SESSIONTERMINATED);
}
else{
/* The host called DestroyPeer() on another peer in the session.
@ -3976,55 +3856,9 @@ void DirectPlay8Peer::handle_terminate_session(std::unique_lock<std::mutex> &l,
message_handler(message_handler_ctx, DPN_MSGID_TERMINATE_SESSION, &ts);
l.lock();
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_SESSIONTERMINATED);
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = local_player_id;
dp.pvPlayerContext = local_player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_SESSIONTERMINATED;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
for(auto pi = peers.begin(); pi != peers.end();)
{
unsigned int peer_id = pi->first;
Peer *peer = pi->second;
if(peer->state == Peer::PS_CONNECTED)
{
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(dp);
dp.dpnidPlayer = peer->player_id;
dp.pvPlayerContext = peer->player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_SESSIONTERMINATED;
peer->state = Peer::PS_CLOSING;
/* Wake up a worker to deal with closing the connection. */
SetEvent(peer->event);
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
pi = peers.begin();
}
else if(peer->state == Peer::PS_CLOSING)
{
/* Do nothing. We're waiting for this peer to go away. */
++pi;
}
else{
peer_destroy(l, peer_id, DPNERR_USERCANCEL, DPNDESTROYPLAYERREASON_NORMAL);
pi = peers.begin();
}
}
peer_shutdown_all(l, DPNERR_HOSTTERMINATEDSESSION, DPNDESTROYPLAYERREASON_SESSIONTERMINATED);
}
catch(const PacketDeserialiser::Error &e)
{
@ -4106,7 +3940,8 @@ void DirectPlay8Peer::connect_fail(std::unique_lock<std::mutex> &l, HRESULT hRes
state = STATE_CONNECT_FAILED;
close_everything_now(l, DPNERR_GENERIC, DPNDESTROYPLAYERREASON_CONNECTIONLOST);
close_main_sockets();
peer_destroy_all(l, DPNERR_GENERIC, DPNDESTROYPLAYERREASON_CONNECTIONLOST);
if(old_state == STATE_CONNECTING_TO_PEERS)
{
@ -4114,17 +3949,7 @@ void DirectPlay8Peer::connect_fail(std::unique_lock<std::mutex> &l, HRESULT hRes
* raised DPNMSG_CREATE_PLAYER for the local player. Undo it.
*/
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = local_player_id;
dp.pvPlayerContext = local_player_ctx;
dp.dwReason = DPNDESTROYPLAYERREASON_NORMAL;
l.unlock();
message_handler(message_handler_ctx, DPN_MSGID_DESTROY_PLAYER, &dp);
l.lock();
dispatch_destroy_player(l, local_player_id, local_player_ctx, DPNDESTROYPLAYERREASON_NORMAL);
}
DPNMSG_CONNECT_COMPLETE cc;
@ -4149,6 +3974,44 @@ void DirectPlay8Peer::connect_fail(std::unique_lock<std::mutex> &l, HRESULT hRes
connect_cv.notify_all();
}
HRESULT DirectPlay8Peer::dispatch_message(std::unique_lock<std::mutex> &l, DWORD dwMessageType, PVOID pvMessage)
{
l.unlock();
HRESULT result = message_handler(message_handler_ctx, dwMessageType, pvMessage);
l.lock();
return result;
}
HRESULT DirectPlay8Peer::dispatch_create_player(std::unique_lock<std::mutex> &l, DPNID dpnidPlayer, void **ppvPlayerContext)
{
DPNMSG_CREATE_PLAYER cp;
memset(&cp, 0, sizeof(cp));
cp.dwSize = sizeof(cp);
cp.dpnidPlayer = dpnidPlayer;
cp.pvPlayerContext = *ppvPlayerContext;
HRESULT result = dispatch_message(l, DPN_MSGID_CREATE_PLAYER, &cp);
*ppvPlayerContext = cp.pvPlayerContext;
return result;
}
HRESULT DirectPlay8Peer::dispatch_destroy_player(std::unique_lock<std::mutex> &l, DPNID dpnidPlayer, void *pvPlayerContext, DWORD dwReason)
{
DPNMSG_DESTROY_PLAYER dp;
memset(&dp, 0, sizeof(dp));
dp.dwSize = sizeof(DPNMSG_DESTROY_PLAYER);
dp.dpnidPlayer = dpnidPlayer;
dp.pvPlayerContext = pvPlayerContext;
dp.dwReason = dwReason;
return dispatch_message(l, DPN_MSGID_DESTROY_PLAYER, &dp);
}
DirectPlay8Peer::Peer::Peer(enum PeerState state, int sock, uint32_t ip, uint16_t port):
state(state), sock(sock), ip(ip), port(port), recv_busy(false), recv_buf_cur(0), events(0), sq(event), send_open(true), next_ack_id(1)
{}

View File

@ -197,8 +197,11 @@ class DirectPlay8Peer: public IDirectPlay8Peer
void peer_accept(std::unique_lock<std::mutex> &l);
bool peer_connect(Peer::PeerState initial_state, uint32_t remote_ip, uint16_t remote_port, DPNID player_id = 0);
void peer_destroy(std::unique_lock<std::mutex> &l, unsigned int peer_id, HRESULT outstanding_op_result, DWORD destroy_player_reason);
void peer_destroy_all(std::unique_lock<std::mutex> &l, HRESULT outstanding_op_result, DWORD destroy_player_reason);
void peer_shutdown(std::unique_lock<std::mutex> &l, unsigned int peer_id, HRESULT outstanding_op_result, DWORD destroy_player_reason);
void peer_shutdown_all(std::unique_lock<std::mutex> &l, HRESULT outstanding_op_result, DWORD destroy_player_reason);
void close_everything_now(std::unique_lock<std::mutex> &l, HRESULT outstanding_op_result, DWORD destroy_player_reason);
void close_main_sockets();
void handle_host_enum_request(std::unique_lock<std::mutex> &l, const PacketDeserialiser &pd, const struct sockaddr_in *from_addr);
void handle_host_connect_request(std::unique_lock<std::mutex> &l, unsigned int peer_id, const PacketDeserialiser &pd);
@ -217,6 +220,10 @@ class DirectPlay8Peer: public IDirectPlay8Peer
void connect_check(std::unique_lock<std::mutex> &l);
void connect_fail(std::unique_lock<std::mutex> &l, HRESULT hResultCode, const void *pvApplicationReplyData, DWORD dwApplicationReplyDataSize);
HRESULT dispatch_message(std::unique_lock<std::mutex> &l, DWORD dwMessageType, PVOID pvMessage);
HRESULT dispatch_create_player(std::unique_lock<std::mutex> &l, DPNID dpnidPlayer, void **ppvPlayerContext);
HRESULT dispatch_destroy_player(std::unique_lock<std::mutex> &l, DPNID dpnidPlayer, void *pvPlayerContext, DWORD dwReason);
public:
DirectPlay8Peer(std::atomic<unsigned int> *global_refcount);
virtual ~DirectPlay8Peer();