Skip to content

Commit 01d1ab1

Browse files
committed
Merge pull request microsoft#393 from ChristophAlbert/cpprestsdk
Race condition in ws_client_wspp.cpp
2 parents c327a61 + 42b7a93 commit 01d1ab1

File tree

6 files changed

+98
-70
lines changed

6 files changed

+98
-70
lines changed

Release/src/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,14 @@ elseif(CPPREST_WEBSOCKETS_IMPL STREQUAL "winrt")
4545
websockets/client/ws_msg.cpp
4646
websockets/client/ws_client.cpp
4747
websockets/client/ws_client_winrt.cpp
48+
websockets/client/ws_client_impl.h
4849
)
4950
elseif(CPPREST_WEBSOCKETS_IMPL STREQUAL "wspp")
5051
list(APPEND SOURCES
5152
websockets/client/ws_msg.cpp
5253
websockets/client/ws_client.cpp
5354
websockets/client/ws_client_wspp.cpp
55+
websockets/client/ws_client_impl.h
5456
)
5557
endif()
5658

Release/src/build/common.vcxitems

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
<ClInclude Include="$(MSBuildThisFileDirectory)..\http\client\http_client_impl.h" />
8787
<ClInclude Include="$(MSBuildThisFileDirectory)..\http\listener\http_server_impl.h" />
8888
<ClInclude Include="$(MSBuildThisFileDirectory)..\http\common\internal_http_helpers.h" />
89+
<ClInclude Include="$(MSBuildThisFileDirectory)..\websockets\client\ws_client_impl.h" />
8990
<ClInclude Include="$(MSBuildThisFileDirectory)..\pch\stdafx.h" />
9091
</ItemGroup>
9192
<ItemGroup>

Release/src/build/common.vcxitems.filters

+6-1
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,12 @@
218218
<ClInclude Include="$(MSBuildThisFileDirectory)..\http\listener\http_server_impl.h">
219219
<Filter>Header Files\private</Filter>
220220
</ClInclude>
221-
<ClInclude Include="$(MSBuildThisFileDirectory)..\http\common\internal_http_helpers.h" />
221+
<ClInclude Include="$(MSBuildThisFileDirectory)..\websockets\client\ws_client_impl.h">
222+
<Filter>Header Files\private</Filter>
223+
</ClInclude>
224+
<ClInclude Include="$(MSBuildThisFileDirectory)..\http\common\internal_http_helpers.h">
225+
<Filter>Header Files\private</Filter>
226+
</ClInclude>
222227
</ItemGroup>
223228
<ItemGroup>
224229
<None Include="$(MSBuildThisFileDirectory)..\..\include\cpprest\details\http_constants.dat">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#pragma once
2+
3+
#include <queue>
4+
#include <mutex>
5+
#include "cpprest/ws_client.h"
6+
#include "cpprest/ws_msg.h"
7+
8+
namespace web
9+
{
10+
namespace websockets
11+
{
12+
namespace client
13+
{
14+
namespace details
15+
{
16+
17+
struct outgoing_msg_queue
18+
{
19+
enum class state
20+
{
21+
was_empty,
22+
was_not_empty,
23+
};
24+
25+
state push(websocket_outgoing_message& msg)
26+
{
27+
state ret = state::was_not_empty;
28+
std::lock_guard<std::mutex> lock(m_lock);
29+
if (m_queue.empty())
30+
{
31+
ret = state::was_empty;
32+
}
33+
34+
m_queue.push(msg);
35+
return ret;
36+
}
37+
38+
bool pop_and_peek(websocket_outgoing_message& msg)
39+
{
40+
std::lock_guard<std::mutex> lock(m_lock);
41+
42+
m_queue.pop();
43+
44+
if (m_queue.empty())
45+
{
46+
return false;
47+
}
48+
msg = m_queue.front();
49+
return true;
50+
}
51+
52+
private:
53+
std::mutex m_lock;
54+
std::queue<websocket_outgoing_message> m_queue;
55+
};
56+
57+
58+
}}}}

Release/src/websockets/client/ws_client_winrt.cpp

+6-30
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
#if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
1717

18+
#include "ws_client_impl.h"
19+
1820
using namespace ::Windows::Foundation;
1921
using namespace ::Windows::Storage;
2022
using namespace ::Windows::Storage::Streams;
@@ -230,19 +232,10 @@ class winrt_callback_client : public websocket_client_callback_impl, public std:
230232
return pplx::task_from_exception<void>(websocket_exception("Message size too large. Ensure message length is less than UINT_MAX."));
231233
}
232234

233-
bool msg_pending = false;
234-
{
235-
std::lock_guard<std::mutex> lock(m_send_lock);
236-
if (m_outgoing_msg_queue.size() > 0)
237-
{
238-
msg_pending = true;
239-
}
240-
241-
m_outgoing_msg_queue.push(msg);
242-
}
235+
auto msg_pending = m_out_queue.push(msg);
243236

244237
// No sends in progress
245-
if (msg_pending == false)
238+
if (msg_pending == outgoing_msg_queue::state::was_empty)
246239
{
247240
// Start sending the message
248241
send_msg(msg);
@@ -379,22 +372,8 @@ class winrt_callback_client : public websocket_client_callback_impl, public std:
379372
msg.signal_body_sent();
380373
}
381374

382-
bool msg_pending = false;
383375
websocket_outgoing_message next_msg;
384-
{
385-
// Only hold the lock when actually touching the queue.
386-
std::lock_guard<std::mutex> lock(this_client->m_send_lock);
387-
388-
// First message in queue has been sent
389-
this_client->m_outgoing_msg_queue.pop();
390-
391-
if (this_client->m_outgoing_msg_queue.size() > 0)
392-
{
393-
next_msg = this_client->m_outgoing_msg_queue.front();
394-
msg_pending = true;
395-
}
396-
}
397-
376+
bool msg_pending = this_client->m_out_queue.pop_and_peek(next_msg);
398377
if (msg_pending)
399378
{
400379
this_client->send_msg(next_msg);
@@ -443,11 +422,8 @@ class winrt_callback_client : public websocket_client_callback_impl, public std:
443422
std::function<void(websocket_incoming_message)> m_external_message_handler;
444423
std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)> m_external_close_handler;
445424

446-
// The implementation has to ensure ordering of send requests
447-
std::mutex m_send_lock;
448-
449425
// Queue to track pending sends
450-
std::queue<websocket_outgoing_message> m_outgoing_msg_queue;
426+
outgoing_msg_queue m_out_queue;
451427
};
452428

453429
void ReceiveContext::OnReceive(MessageWebSocket^ sender, MessageWebSocketMessageReceivedEventArgs^ args)

Release/src/websockets/client/ws_client_wspp.cpp

+25-39
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include "cpprest/details/x509_cert_utilities.h"
1919
#include "pplx/threadpool.h"
2020

21+
#include "ws_client_impl.h"
22+
2123
// Force websocketpp to use C++ std::error_code instead of Boost.
2224
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
2325
#if defined(_MSC_VER)
@@ -117,8 +119,7 @@ class wspp_callback_client : public websocket_client_callback_impl, public std::
117119
public:
118120
wspp_callback_client(websocket_client_config config) :
119121
websocket_client_callback_impl(std::move(config)),
120-
m_state(CREATED),
121-
m_num_sends(0)
122+
m_state(CREATED)
122123
#if defined(__APPLE__) || (defined(ANDROID) || defined(__ANDROID__)) || defined(_WIN32)
123124
, m_openssl_failed(false)
124125
#endif
@@ -402,10 +403,10 @@ class wspp_callback_client : public websocket_client_callback_impl, public std::
402403
{
403404
case websocket_message_type::text_message:
404405
case websocket_message_type::binary_message:
405-
case websocket_message_type::pong:
406+
case websocket_message_type::pong:
406407
break;
407408
default:
408-
return pplx::task_from_exception<void>(websocket_exception("Invalid message type"));
409+
return pplx::task_from_exception<void>(websocket_exception("Message Type not supported."));
409410
}
410411

411412
const auto length = msg.m_length;
@@ -418,18 +419,13 @@ class wspp_callback_client : public websocket_client_callback_impl, public std::
418419
return pplx::task_from_exception<void>(websocket_exception("Message size too large. Ensure message length is less than UINT_MAX."));
419420
}
420421

422+
auto msg_pending = m_out_queue.push(msg);
423+
424+
// No sends in progress
425+
if (msg_pending == outgoing_msg_queue::state::was_empty)
421426
{
422-
if (++m_num_sends == 1) // No sends in progress
423-
{
424-
// Start sending the message
425-
send_msg(msg);
426-
}
427-
else
428-
{
429-
// Only actually have to take the lock if touching the queue.
430-
std::lock_guard<std::mutex> lock(m_send_lock);
431-
m_outgoing_msg_queue.push(msg);
432-
}
427+
// Start sending the message
428+
send_msg(msg);
433429
}
434430

435431
return pplx::create_task(msg.body_sent());
@@ -565,16 +561,12 @@ class wspp_callback_client : public websocket_client_callback_impl, public std::
565561
msg.signal_body_sent();
566562
}
567563

568-
if (--this_client->m_num_sends > 0)
564+
websocket_outgoing_message next_msg;
565+
bool msg_pending = this_client->m_out_queue.pop_and_peek(next_msg);
566+
567+
if (msg_pending)
569568
{
570-
// Only hold the lock when actually touching the queue.
571-
websocket_outgoing_message next_msg;
572-
{
573-
std::lock_guard<std::mutex> lock(this_client->m_send_lock);
574-
next_msg = this_client->m_outgoing_msg_queue.front();
575-
this_client->m_outgoing_msg_queue.pop();
576-
}
577-
this_client->send_msg(next_msg);
569+
this_client->send_msg(next_msg);
578570
}
579571
});
580572
}
@@ -669,19 +661,19 @@ class wspp_callback_client : public websocket_client_callback_impl, public std::
669661
ec);
670662
break;
671663
case websocket_message_type::binary_message:
672-
client.send(
664+
client.send(
673665
this_client->m_con,
674666
sp_allocated.get(),
675667
length,
676668
websocketpp::frame::opcode::binary,
677669
ec);
678670
break;
679-
case websocket_message_type::pong:
680-
client.pong(
681-
this_client->m_con,
682-
"",
683-
ec);
684-
break;
671+
case websocket_message_type::pong:
672+
client.pong(
673+
this_client->m_con,
674+
"",
675+
ec);
676+
break;
685677
default:
686678
// This case should have already been filtered above.
687679
std::abort();
@@ -763,14 +755,8 @@ class wspp_callback_client : public websocket_client_callback_impl, public std::
763755
State m_state;
764756
std::unique_ptr<websocketpp_client_base> m_client;
765757

766-
// Guards access to m_outgoing_msg_queue
767-
std::mutex m_send_lock;
768-
769-
// Queue to order the sends
770-
std::queue<websocket_outgoing_message> m_outgoing_msg_queue;
771-
772-
// Number of sends in progress and queued up.
773-
std::atomic<int> m_num_sends;
758+
// Queue to track pending sends
759+
outgoing_msg_queue m_out_queue;
774760

775761
// External callback for handling received and close event
776762
std::function<void(websocket_incoming_message)> m_external_message_handler;

0 commit comments

Comments
 (0)