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

DirectPlay8Peer: Support cancelling in-progress connections.

This commit is contained in:
Daniel Collins 2018-10-14 01:13:57 +01:00
parent f753f42d44
commit c88355efb9
2 changed files with 310 additions and 6 deletions

View File

@ -185,6 +185,8 @@ HRESULT DirectPlay8Peer::EnumServiceProviders(CONST GUID* CONST pguidServiceProv
HRESULT DirectPlay8Peer::CancelAsyncOperation(CONST DPNHANDLE hAsyncHandle, CONST DWORD dwFlags)
{
std::unique_lock<std::mutex> l(lock);
if(dwFlags & DPNCANCEL_PLAYER_SENDS)
{
/* Cancel sends to player ID in hAsyncHandle */
@ -196,8 +198,6 @@ HRESULT DirectPlay8Peer::CancelAsyncOperation(CONST DPNHANDLE hAsyncHandle, CONS
if(dwFlags & (DPNCANCEL_ENUM | DPNCANCEL_ALL_OPERATIONS))
{
std::unique_lock<std::mutex> l(lock);
for(auto ei = host_enums.begin(); ei != host_enums.end(); ++ei)
{
ei->second.cancel();
@ -206,7 +206,11 @@ HRESULT DirectPlay8Peer::CancelAsyncOperation(CONST DPNHANDLE hAsyncHandle, CONS
if(dwFlags & (DPNCANCEL_CONNECT | DPNCANCEL_ALL_OPERATIONS))
{
/* TODO: Cancel in-progress connect. */
if((state == STATE_CONNECTING_TO_HOST || state == STATE_CONNECTING_TO_PEERS) && connect_handle != 0)
{
/* We have an ongoing asynchronous connection. */
connect_fail(l, DPNERR_USERCANCEL, NULL, 0);
}
}
if(dwFlags & DPNCANCEL_ALL_OPERATIONS)
@ -218,8 +222,6 @@ HRESULT DirectPlay8Peer::CancelAsyncOperation(CONST DPNHANDLE hAsyncHandle, CONS
}
else if((hAsyncHandle & AsyncHandleAllocator::TYPE_MASK) == AsyncHandleAllocator::TYPE_ENUM)
{
std::unique_lock<std::mutex> l(lock);
auto ei = host_enums.find(hAsyncHandle);
if(ei == host_enums.end())
{
@ -233,7 +235,20 @@ HRESULT DirectPlay8Peer::CancelAsyncOperation(CONST DPNHANDLE hAsyncHandle, CONS
}
else if((hAsyncHandle & AsyncHandleAllocator::TYPE_MASK) == AsyncHandleAllocator::TYPE_CONNECT)
{
UNIMPLEMENTED("DirectPlay8Peer::CancelAsyncOperation");
if(hAsyncHandle == connect_handle)
{
if(state == STATE_CONNECTING_TO_HOST || state == STATE_CONNECTING_TO_PEERS)
{
connect_fail(l, DPNERR_USERCANCEL, NULL, 0);
return S_OK;
}
else{
return DPNERR_CANNOTCANCEL;
}
}
else{
return DPNERR_INVALIDHANDLE;
}
}
else if((hAsyncHandle & AsyncHandleAllocator::TYPE_MASK) == AsyncHandleAllocator::TYPE_SEND)
{
@ -1415,6 +1430,15 @@ HRESULT DirectPlay8Peer::Close(CONST DWORD dwFlags)
udp_socket = -1;
}
if(state == STATE_CONNECTING_TO_HOST || state == STATE_CONNECTING_TO_PEERS)
{
/* connect_fail() will change the state to STATE_CONNECT_FAILED, then finally to
* STATE_INITIALISED before it returns, this doesn't matter as we return it to
* STATE_CLOSING before the lock is released again.
*/
connect_fail(l, DPNERR_NOCONNECTION, NULL, 0);
}
state = STATE_CLOSING;
/* When Close() is called as a host, the DPNMSG_DESTROY_PLAYER for the local player comes

View File

@ -1873,6 +1873,286 @@ TEST(DirectPlay8Peer, ConnectAsyncFail)
EXPECT_EQ(p1_seq, 1);
}
TEST(DirectPlay8Peer, ConnectAsyncCancelByHandle)
{
/* We set up a host which blocks when processing DPN_MSGID_INDICATE_CONNECT, causing any
* attempted Connect() by a peer to take a while, giving us time to cancel it.
*/
SessionHost host(APP_GUID_1, L"Session 1", PORT,
[]
(DWORD dwMessageType, PVOID pMessage)
{
if(dwMessageType == DPN_MSGID_INDICATE_CONNECT)
{
Sleep(2000);
}
return DPN_OK;
});
TestPeer peer1("peer1");
DPN_APPLICATION_DESC connect_to_app;
memset(&connect_to_app, 0, sizeof(connect_to_app));
connect_to_app.dwSize = sizeof(connect_to_app);
connect_to_app.guidApplication = APP_GUID_1;
IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT);
DPNHANDLE p1_connect_handle;
ASSERT_EQ(peer1->Connect(
&connect_to_app, /* pdnAppDesc */
connect_to_addr, /* pHostAddr */
NULL, /* pDeviceInfo */
NULL, /* pdnSecurity */
NULL, /* pdnCredentials */
NULL, /* pvUserConnectData */
0, /* dwUserConnectDataSize */
NULL, /* pvPlayerContext */
(void*)(0xABCD), /* pvAsyncContext */
&p1_connect_handle, /* phAsyncHandle */
0 /* dwFlags */
), DPNSUCCESS_PENDING);
peer1.expect_begin();
peer1.expect_push([&p1_connect_handle](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_CONNECT_COMPLETE);
if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE)
{
DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage);
EXPECT_EQ(cc->dwSize, sizeof(DPNMSG_CONNECT_COMPLETE));
EXPECT_EQ(cc->hAsyncOp, p1_connect_handle);
EXPECT_EQ(cc->pvUserContext, (void*)(0xABCD));
EXPECT_EQ(cc->hResultCode, DPNERR_USERCANCEL);
EXPECT_EQ(cc->pvApplicationReplyData, (PVOID)(NULL));
EXPECT_EQ(cc->dwApplicationReplyDataSize, 0);
}
return DPN_OK;
});
ASSERT_EQ(peer1->CancelAsyncOperation(p1_connect_handle, 0), S_OK);
Sleep(250);
peer1.expect_end();
}
TEST(DirectPlay8Peer, ConnectAsyncCancelAllConnects)
{
/* We set up a host which blocks when processing DPN_MSGID_INDICATE_CONNECT, causing any
* attempted Connect() by a peer to take a while, giving us time to cancel it.
*/
SessionHost host(APP_GUID_1, L"Session 1", PORT,
[]
(DWORD dwMessageType, PVOID pMessage)
{
if(dwMessageType == DPN_MSGID_INDICATE_CONNECT)
{
Sleep(2000);
}
return DPN_OK;
});
TestPeer peer1("peer1");
DPN_APPLICATION_DESC connect_to_app;
memset(&connect_to_app, 0, sizeof(connect_to_app));
connect_to_app.dwSize = sizeof(connect_to_app);
connect_to_app.guidApplication = APP_GUID_1;
IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT);
DPNHANDLE p1_connect_handle;
ASSERT_EQ(peer1->Connect(
&connect_to_app, /* pdnAppDesc */
connect_to_addr, /* pHostAddr */
NULL, /* pDeviceInfo */
NULL, /* pdnSecurity */
NULL, /* pdnCredentials */
NULL, /* pvUserConnectData */
0, /* dwUserConnectDataSize */
NULL, /* pvPlayerContext */
(void*)(0xABCD), /* pvAsyncContext */
&p1_connect_handle, /* phAsyncHandle */
0 /* dwFlags */
), DPNSUCCESS_PENDING);
peer1.expect_begin();
peer1.expect_push([&p1_connect_handle](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_CONNECT_COMPLETE);
if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE)
{
DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage);
EXPECT_EQ(cc->dwSize, sizeof(DPNMSG_CONNECT_COMPLETE));
EXPECT_EQ(cc->hAsyncOp, p1_connect_handle);
EXPECT_EQ(cc->pvUserContext, (void*)(0xABCD));
EXPECT_EQ(cc->hResultCode, DPNERR_USERCANCEL);
EXPECT_EQ(cc->pvApplicationReplyData, (PVOID)(NULL));
EXPECT_EQ(cc->dwApplicationReplyDataSize, 0);
}
return DPN_OK;
});
ASSERT_EQ(peer1->CancelAsyncOperation(NULL, DPNCANCEL_CONNECT), S_OK);
Sleep(250);
peer1.expect_end();
}
TEST(DirectPlay8Peer, ConnectAsyncCancelAllOperations)
{
/* We set up a host which blocks when processing DPN_MSGID_INDICATE_CONNECT, causing any
* attempted Connect() by a peer to take a while, giving us time to cancel it.
*/
SessionHost host(APP_GUID_1, L"Session 1", PORT,
[]
(DWORD dwMessageType, PVOID pMessage)
{
if(dwMessageType == DPN_MSGID_INDICATE_CONNECT)
{
Sleep(2000);
}
return DPN_OK;
});
TestPeer peer1("peer1");
DPN_APPLICATION_DESC connect_to_app;
memset(&connect_to_app, 0, sizeof(connect_to_app));
connect_to_app.dwSize = sizeof(connect_to_app);
connect_to_app.guidApplication = APP_GUID_1;
IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT);
DPNHANDLE p1_connect_handle;
ASSERT_EQ(peer1->Connect(
&connect_to_app, /* pdnAppDesc */
connect_to_addr, /* pHostAddr */
NULL, /* pDeviceInfo */
NULL, /* pdnSecurity */
NULL, /* pdnCredentials */
NULL, /* pvUserConnectData */
0, /* dwUserConnectDataSize */
NULL, /* pvPlayerContext */
(void*)(0xABCD), /* pvAsyncContext */
&p1_connect_handle, /* phAsyncHandle */
0 /* dwFlags */
), DPNSUCCESS_PENDING);
peer1.expect_begin();
peer1.expect_push([&p1_connect_handle](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_CONNECT_COMPLETE);
if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE)
{
DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage);
EXPECT_EQ(cc->dwSize, sizeof(DPNMSG_CONNECT_COMPLETE));
EXPECT_EQ(cc->hAsyncOp, p1_connect_handle);
EXPECT_EQ(cc->pvUserContext, (void*)(0xABCD));
EXPECT_EQ(cc->hResultCode, DPNERR_USERCANCEL);
EXPECT_EQ(cc->pvApplicationReplyData, (PVOID)(NULL));
EXPECT_EQ(cc->dwApplicationReplyDataSize, 0);
}
return DPN_OK;
});
ASSERT_EQ(peer1->CancelAsyncOperation(NULL, DPNCANCEL_ALL_OPERATIONS), S_OK);
Sleep(250);
peer1.expect_end();
}
TEST(DirectPlay8Peer, ConnectAsyncCancelByClose)
{
/* We set up a host which blocks when processing DPN_MSGID_INDICATE_CONNECT, causing any
* attempted Connect() by a peer to take a while, giving us time to cancel it.
*/
SessionHost host(APP_GUID_1, L"Session 1", PORT,
[]
(DWORD dwMessageType, PVOID pMessage)
{
if(dwMessageType == DPN_MSGID_INDICATE_CONNECT)
{
Sleep(2000);
}
return DPN_OK;
});
TestPeer peer1("peer1");
DPN_APPLICATION_DESC connect_to_app;
memset(&connect_to_app, 0, sizeof(connect_to_app));
connect_to_app.dwSize = sizeof(connect_to_app);
connect_to_app.guidApplication = APP_GUID_1;
IDP8AddressInstance connect_to_addr(L"127.0.0.1", PORT);
DPNHANDLE p1_connect_handle;
ASSERT_EQ(peer1->Connect(
&connect_to_app, /* pdnAppDesc */
connect_to_addr, /* pHostAddr */
NULL, /* pDeviceInfo */
NULL, /* pdnSecurity */
NULL, /* pdnCredentials */
NULL, /* pvUserConnectData */
0, /* dwUserConnectDataSize */
NULL, /* pvPlayerContext */
(void*)(0xABCD), /* pvAsyncContext */
&p1_connect_handle, /* phAsyncHandle */
0 /* dwFlags */
), DPNSUCCESS_PENDING);
peer1.expect_begin();
peer1.expect_push([&p1_connect_handle](DWORD dwMessageType, PVOID pMessage)
{
EXPECT_EQ(dwMessageType, DPN_MSGID_CONNECT_COMPLETE);
if(dwMessageType == DPN_MSGID_CONNECT_COMPLETE)
{
DPNMSG_CONNECT_COMPLETE *cc = (DPNMSG_CONNECT_COMPLETE*)(pMessage);
EXPECT_EQ(cc->dwSize, sizeof(DPNMSG_CONNECT_COMPLETE));
EXPECT_EQ(cc->hAsyncOp, p1_connect_handle);
EXPECT_EQ(cc->pvUserContext, (void*)(0xABCD));
EXPECT_EQ(cc->hResultCode, DPNERR_NOCONNECTION);
EXPECT_EQ(cc->pvApplicationReplyData, (PVOID)(NULL));
EXPECT_EQ(cc->dwApplicationReplyDataSize, 0);
}
return DPN_OK;
});
peer1->Close(DPNCLOSE_IMMEDIATE);
Sleep(250);
peer1.expect_end();
}
TEST(DirectPlay8Peer, ConnectToIPX)
{
std::atomic<bool> testing(true);