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
* Use iterator wrapper in detail::buffers_range
WebSocket:
* control callback is copied or moved
--------------------------------------------------------------------------------
Version 146:

View File

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

View File

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