control callback is copied or moved:

The function stream::control_callback now copies or moves
the callback. In some cases this may require a dynamic
allocation.

To avoid the possibility of a dynamic allocation, callers
may wrap their callback using `std::ref` before setting it.
This commit is contained in:
Vinnie Falco
2017-11-27 15:05:58 -08:00
parent ab91844039
commit 8181851719
4 changed files with 29 additions and 33 deletions

View File

@@ -3,6 +3,10 @@ Version 147:
* Don't use boost::string_ref * Don't use boost::string_ref
* Use iterator wrapper in detail::buffers_range * Use iterator wrapper in detail::buffers_range
WebSocket:
* control callback is copied or moved
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
Version 146: Version 146:

View File

@@ -580,9 +580,9 @@ public:
string_view payload // The payload in the frame string_view payload // The payload in the frame
); );
@endcode @endcode
The implementation type-erases the callback without requiring The implementation type-erases the callback which may require
a dynamic allocation. For this reason, the callback object is a dynamic allocation. To prevent the possiblity of a dynamic
passed by a non-constant reference. allocation, use `std::ref` to wrap the callback.
If the read operation which receives the control frame is If the read operation which receives the control frame is
an asynchronous operation, the callback will be invoked using an asynchronous operation, the callback will be invoked using
the same method as that used to invoke the final handler. the same method as that used to invoke the final handler.
@@ -592,15 +592,10 @@ public:
Attempting to send a close frame after a close frame is Attempting to send a close frame after a close frame is
received will result in undefined behavior. received will result in undefined behavior.
*/ */
template<class Callback>
void void
control_callback(Callback& cb) control_callback(std::function<void(frame_type, string_view)> cb)
{ {
// Callback may not be constant, caller is responsible for ctrl_cb_ = std::move(cb);
// managing the lifetime of the callback. Copies are not made.
BOOST_STATIC_ASSERT(! std::is_const<Callback>::value);
ctrl_cb_ = std::ref(cb);
} }
/** Reset the control frame callback. /** Reset the control frame callback.

View File

@@ -126,13 +126,13 @@ public:
put(ws.next_layer().buffer(), cbuf( put(ws.next_layer().buffer(), cbuf(
0x89, 0x00)); 0x89, 0x00));
bool invoked = false; bool invoked = false;
auto cb = [&](frame_type kind, string_view) ws.control_callback(
{ [&](frame_type kind, string_view)
BEAST_EXPECT(! invoked); {
BEAST_EXPECT(kind == frame_type::ping); BEAST_EXPECT(! invoked);
invoked = true; BEAST_EXPECT(kind == frame_type::ping);
}; invoked = true;
ws.control_callback(cb); });
w.write(ws, sbuf("Hello")); w.write(ws, sbuf("Hello"));
multi_buffer b; multi_buffer b;
w.read(ws, b); w.read(ws, b);
@@ -147,13 +147,13 @@ public:
put(ws.next_layer().buffer(), cbuf( put(ws.next_layer().buffer(), cbuf(
0x88, 0x00)); 0x88, 0x00));
bool invoked = false; bool invoked = false;
auto cb = [&](frame_type kind, string_view) ws.control_callback(
{ [&](frame_type kind, string_view)
BEAST_EXPECT(! invoked); {
BEAST_EXPECT(kind == frame_type::close); BEAST_EXPECT(! invoked);
invoked = true; BEAST_EXPECT(kind == frame_type::close);
}; invoked = true;
ws.control_callback(cb); });
w.write(ws, sbuf("Hello")); w.write(ws, sbuf("Hello"));
doReadTest(w, ws, close_code::none); doReadTest(w, ws, close_code::none);
}); });
@@ -162,15 +162,14 @@ public:
doTest(pmd, [&](ws_type& ws) doTest(pmd, [&](ws_type& ws)
{ {
bool once = false; bool once = false;
auto cb = ws.control_callback(
[&](frame_type kind, string_view s) [&](frame_type kind, string_view s)
{ {
BEAST_EXPECT(kind == frame_type::pong); BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once); BEAST_EXPECT(! once);
once = true; once = true;
BEAST_EXPECT(s == ""); BEAST_EXPECT(s == "");
}; });
ws.control_callback(cb);
w.ping(ws, ""); w.ping(ws, "");
ws.binary(true); ws.binary(true);
w.write(ws, sbuf("Hello")); w.write(ws, sbuf("Hello"));
@@ -185,15 +184,14 @@ public:
doTest(pmd, [&](ws_type& ws) doTest(pmd, [&](ws_type& ws)
{ {
bool once = false; bool once = false;
auto cb = ws.control_callback(
[&](frame_type kind, string_view s) [&](frame_type kind, string_view s)
{ {
BEAST_EXPECT(kind == frame_type::pong); BEAST_EXPECT(kind == frame_type::pong);
BEAST_EXPECT(! once); BEAST_EXPECT(! once);
once = true; once = true;
BEAST_EXPECT(s == "payload"); BEAST_EXPECT(s == "payload");
}; });
ws.control_callback(cb);
ws.ping("payload"); ws.ping("payload");
w.write_some(ws, false, sbuf("Hello, ")); w.write_some(ws, false, sbuf("Hello, "));
w.write_some(ws, false, sbuf("")); w.write_some(ws, false, sbuf(""));

View File

@@ -190,13 +190,12 @@ boost::asio::ip::tcp::socket sock{ioc};
{ {
stream<boost::asio::ip::tcp::socket> ws{ioc}; stream<boost::asio::ip::tcp::socket> ws{ioc};
//[ws_snippet_17 //[ws_snippet_17
auto cb = ws.control_callback(
[](frame_type kind, string_view payload) [](frame_type kind, string_view payload)
{ {
// Do something with the payload // Do something with the payload
boost::ignore_unused(kind, payload); boost::ignore_unused(kind, payload);
}; });
ws.control_callback(cb);
//] //]
//[ws_snippet_18 //[ws_snippet_18