Remove http Writer suspend and resume feature (API Change):

fix #154

The resume_context parameter passed to instances
of Writer during HTTP serialization is removed.
This commit is contained in:
Vinnie Falco
2017-03-31 11:15:27 -04:00
parent 36027ba844
commit ed906adc35
16 changed files with 53 additions and 248 deletions

View File

@@ -2,6 +2,10 @@
* Add io_service completion invariants test * Add io_service completion invariants test
API Changes:
* Remove http Writer suspend and resume feature
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
1.0.0-b31 1.0.0-b31

View File

@@ -42,7 +42,6 @@
<member><link linkend="beast.ref.http__request_header">request_header</link></member> <member><link linkend="beast.ref.http__request_header">request_header</link></member>
<member><link linkend="beast.ref.http__response">response</link></member> <member><link linkend="beast.ref.http__response">response</link></member>
<member><link linkend="beast.ref.http__response_header">response_header</link></member> <member><link linkend="beast.ref.http__response_header">response_header</link></member>
<member><link linkend="beast.ref.http__resume_context">resume_context</link></member>
<member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member> <member><link linkend="beast.ref.http__streambuf_body">streambuf_body</link></member>
<member><link linkend="beast.ref.http__string_body">string_body</link></member> <member><link linkend="beast.ref.http__string_body">string_body</link></member>
</simplelist> </simplelist>
@@ -190,6 +189,7 @@
<member><link linkend="beast.ref.buffer_cat">buffer_cat</link></member> <member><link linkend="beast.ref.buffer_cat">buffer_cat</link></member>
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member> <member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member> <member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
<member><link linkend="beast.ref.system_category">system_category</link></member>
<member><link linkend="beast.ref.to_string">to_string</link></member> <member><link linkend="beast.ref.to_string">to_string</link></member>
<member><link linkend="beast.ref.write">write</link></member> <member><link linkend="beast.ref.write">write</link></member>
</simplelist> </simplelist>

View File

@@ -28,8 +28,6 @@ In this table:
* `m` denotes a value of type `message const&` where * `m` denotes a value of type `message const&` where
`std::is_same<decltype(m.body), Body::value_type>:value == true`. `std::is_same<decltype(m.body), Body::value_type>:value == true`.
* `rc` is an object of type [link beast.ref.http__resume_context `resume_context`].
* `ec` is a value of type [link beast.ref.error_code `error_code&`] * `ec` is a value of type [link beast.ref.error_code `error_code&`]
* `wf` is a [*write function]: a function object of unspecified type provided * `wf` is a [*write function]: a function object of unspecified type provided
@@ -72,8 +70,8 @@ In this table:
] ]
] ]
[ [
[`a.write(rc, ec, wf)`] [`a.write(ec, wf)`]
[`boost::tribool`] [`bool`]
[ [
Called repeatedly after `init` succeeds. `wf` is a function object Called repeatedly after `init` succeeds. `wf` is a function object
which takes as its single parameter any value meeting the requirements which takes as its single parameter any value meeting the requirements
@@ -81,12 +79,7 @@ In this table:
must remain valid until the next member function of `writer` is must remain valid until the next member function of `writer` is
invoked (which may be the destructor). This function returns `true` invoked (which may be the destructor). This function returns `true`
to indicate all message body data has been written, or `false` if to indicate all message body data has been written, or `false` if
there is more body data. If the return value is `boost::indeterminate`, there is more body data.
the implementation will suspend the operation until the writer invokes
`rc`. It is the writers responsibility when returning
`boost::indeterminate`, to acquire ownership of `rc` via move
construction and eventually call it or else undefined behavior
results. This function must be `noexcept`.
] ]
] ]
] ]
@@ -139,7 +132,6 @@ public:
Postconditions: Postconditions:
If return value is `true`: If return value is `true`:
* Callee does not take ownership of resume.
* Callee made zero or one calls to `write`. * Callee made zero or one calls to `write`.
* There is no more data remaining to write. * There is no more data remaining to write.
@@ -147,18 +139,6 @@ public:
* Callee does not take ownership of resume. * Callee does not take ownership of resume.
* Callee made one call to `write`. * Callee made one call to `write`.
If return value is boost::indeterminate:
* Callee takes ownership of `resume`.
* Caller suspends the write operation
until `resume` is invoked.
When the caller takes ownership of resume, the
asynchronous operation will not complete until the
caller destroys the object.
@param resume A functor to call to resume the write operation
after the writer has returned boost::indeterminate.
@param ec Set to indicate an error. This will cause an @param ec Set to indicate an error. This will cause an
asynchronous write operation to complete with the error. asynchronous write operation to complete with the error.
@@ -167,17 +147,12 @@ public:
the writer must guarantee that the buffers remain valid until the the writer must guarantee that the buffers remain valid until the
next member function is invoked, which may be the destructor. next member function is invoked, which may be the destructor.
@return `true` if there is no more data to send, @return `true` if there is no more data to send,
`false` when there may be more data, `false` when there may be more data.
boost::indeterminate to suspend.
@note Undefined behavior if the callee takes ownership
of resume but does not return boost::indeterminate.
*/ */
template<class WriteFunction> template<class WriteFunction>
boost::tribool bool
write( write(
resume_context&&,
error_code&, error_code&,
WriteFunction&& wf) noexcept; WriteFunction&& wf) noexcept;
}; };

View File

@@ -10,10 +10,8 @@
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/resume_context.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/logic/tribool.hpp>
#include <cstdio> #include <cstdio>
#include <cstdint> #include <cstdint>
@@ -38,7 +36,8 @@ struct file_body
writer& operator=(writer const&) = delete; writer& operator=(writer const&) = delete;
template<bool isRequest, class Fields> template<bool isRequest, class Fields>
writer(message<isRequest, file_body, Fields> const& m) noexcept writer(message<isRequest,
file_body, Fields> const& m) noexcept
: path_(m.body) : path_(m.body)
{ {
} }
@@ -54,8 +53,8 @@ struct file_body
{ {
file_ = fopen(path_.c_str(), "rb"); file_ = fopen(path_.c_str(), "rb");
if(! file_) if(! file_)
ec = boost::system::errc::make_error_code( ec = error_code{errno,
static_cast<boost::system::errc::errc_t>(errno)); system_category()};
else else
size_ = boost::filesystem::file_size(path_); size_ = boost::filesystem::file_size(path_);
} }
@@ -67,9 +66,8 @@ struct file_body
} }
template<class WriteFunction> template<class WriteFunction>
boost::tribool bool
write(resume_context&&, error_code&, write(error_code& ec, WriteFunction&& wf) noexcept
WriteFunction&& wf) noexcept
{ {
if(size_ - offset_ < sizeof(buf_)) if(size_ - offset_ < sizeof(buf_))
buf_len_ = static_cast<std::size_t>( buf_len_ = static_cast<std::size_t>(
@@ -77,7 +75,12 @@ struct file_body
else else
buf_len_ = sizeof(buf_); buf_len_ = sizeof(buf_);
auto const nread = fread(buf_, 1, sizeof(buf_), file_); auto const nread = fread(buf_, 1, sizeof(buf_), file_);
(void)nread; if(ferror(file_))
{
ec = error_code(errno,
system_category());
return true;
}
offset_ += buf_len_; offset_ += buf_len_;
wf(boost::asio::buffer(buf_, buf_len_)); wf(boost::asio::buffer(buf_, buf_len_));
return offset_ >= size_; return offset_ >= size_;

View File

@@ -22,6 +22,14 @@ using system_error = boost::system::system_error;
/// The type of error category used by the library /// The type of error category used by the library
using error_category = boost::system::error_category; using error_category = boost::system::error_category;
/// A function to return the system error category used by the library
#if GENERATING_DOCS
error_category const&
system_category();
#else
using boost::system::system_category;
#endif
/// The type of error condition used by the library /// The type of error condition used by the library
using error_condition = boost::system::error_condition; using error_condition = boost::system::error_condition;

View File

@@ -19,7 +19,6 @@
#include <beast/http/parser_v1.hpp> #include <beast/http/parser_v1.hpp>
#include <beast/http/read.hpp> #include <beast/http/read.hpp>
#include <beast/http/reason.hpp> #include <beast/http/reason.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/http/rfc7230.hpp> #include <beast/http/rfc7230.hpp>
#include <beast/http/streambuf_body.hpp> #include <beast/http/streambuf_body.hpp>
#include <beast/http/string_body.hpp> #include <beast/http/string_body.hpp>

View File

@@ -10,10 +10,8 @@
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/core/detail/type_traits.hpp> #include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
namespace beast { namespace beast {
namespace http { namespace http {
@@ -87,9 +85,8 @@ private:
} }
template<class WriteFunction> template<class WriteFunction>
boost::tribool bool
write(resume_context&&, error_code&, write(error_code&, WriteFunction&& wf) noexcept
WriteFunction&& wf) noexcept
{ {
wf(body_.data()); wf(body_.data());
return true; return true;

View File

@@ -10,9 +10,7 @@
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/core/detail/type_traits.hpp> #include <beast/core/detail/type_traits.hpp>
#include <beast/http/resume_context.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@@ -50,38 +48,6 @@ struct has_content_length<T, beast::detail::void_t<decltype(
"Writer::content_length requirements not met"); "Writer::content_length requirements not met");
}; };
#if 0
template<class T, class M, class = beast::detail::void_t<>>
struct is_Writer : std::false_type {};
template<class T, class M>
struct is_Writer<T, M, beast::detail::void_t<decltype(
std::declval<T>().init(
std::declval<error_code&>())
// VFALCO This is unfortunate, we have to provide the template
// argument type because this is not a deduced context?
//
,std::declval<T>().template write<detail::write_function>(
std::declval<resume_context>(),
std::declval<error_code&>(),
std::declval<detail::write_function>())
)> > : std::integral_constant<bool,
std::is_nothrow_constructible<T, M const&>::value &&
std::is_convertible<decltype(
std::declval<T>().template write<detail::write_function>(
std::declval<resume_context>(),
std::declval<error_code&>(),
std::declval<detail::write_function>())),
boost::tribool>::value
>
{
static_assert(std::is_same<
typename M::body_type::writer, T>::value,
"Mismatched writer and message");
};
#else
template<class T, class M> template<class T, class M>
class is_Writer class is_Writer
{ {
@@ -99,10 +65,9 @@ class is_Writer
template<class U, class R = template<class U, class R =
std::is_convertible<decltype( std::is_convertible<decltype(
std::declval<U>().template write<detail::write_function>( std::declval<U>().template write<detail::write_function>(
std::declval<resume_context>(),
std::declval<error_code&>(), std::declval<error_code&>(),
std::declval<detail::write_function>())) std::declval<detail::write_function>()))
, boost::tribool>> , bool>>
static R check2(int); static R check2(int);
template<class> template<class>
static std::false_type check2(...); static std::false_type check2(...);
@@ -120,8 +85,6 @@ public:
>; >;
}; };
#endif
template<class T> template<class T>
class is_Parser class is_Parser
{ {

View File

@@ -10,10 +10,8 @@
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/core/detail/type_traits.hpp> #include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -59,9 +57,8 @@ private:
} }
template<class WriteFunction> template<class WriteFunction>
boost::tribool bool
write(resume_context&&, error_code&, write(error_code&, WriteFunction&& wf) noexcept
WriteFunction&& wf) noexcept
{ {
wf(boost::asio::null_buffers{}); wf(boost::asio::null_buffers{});
return true; return true;

View File

@@ -9,7 +9,6 @@
#define BEAST_HTTP_IMPL_WRITE_IPP #define BEAST_HTTP_IMPL_WRITE_IPP
#include <beast/http/concepts.hpp> #include <beast/http/concepts.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/http/chunk_encode.hpp> #include <beast/http/chunk_encode.hpp>
#include <beast/core/buffer_cat.hpp> #include <beast/core/buffer_cat.hpp>
#include <beast/core/bind_handler.hpp> #include <beast/core/bind_handler.hpp>
@@ -21,7 +20,6 @@
#include <beast/core/write_dynabuf.hpp> #include <beast/core/write_dynabuf.hpp>
#include <beast/core/detail/sync_ostream.hpp> #include <beast/core/detail/sync_ostream.hpp>
#include <boost/asio/write.hpp> #include <boost/asio/write.hpp>
#include <boost/logic/tribool.hpp>
#include <condition_variable> #include <condition_variable>
#include <mutex> #include <mutex>
#include <ostream> #include <ostream>
@@ -296,8 +294,6 @@ class write_op
// VFALCO How do we use handler_alloc in write_preparation? // VFALCO How do we use handler_alloc in write_preparation?
write_preparation< write_preparation<
isRequest, Body, Fields> wp; isRequest, Body, Fields> wp;
resume_context resume;
resume_context copy;
int state = 0; int state = 0;
data(Handler& handler, Stream& s_, data(Handler& handler, Stream& s_,
@@ -375,27 +371,9 @@ public:
: d_(std::forward<DeducedHandler>(h), : d_(std::forward<DeducedHandler>(h),
s, std::forward<Args>(args)...) s, std::forward<Args>(args)...)
{ {
auto& d = *d_;
auto sp = d_;
d.resume = {
[sp]() mutable
{
write_op self{std::move(sp)};
self.d_->cont = false;
auto& ios = self.d_->s.get_io_service();
ios.dispatch(bind_handler(std::move(self),
error_code{}, 0, false));
}};
d.copy = d.resume;
(*this)(error_code{}, 0, false); (*this)(error_code{}, 0, false);
} }
explicit
write_op(handler_ptr<data, Handler> d)
: d_(std::move(d))
{
}
void void
operator()(error_code ec, operator()(error_code ec,
std::size_t bytes_transferred, bool again = true); std::size_t bytes_transferred, bool again = true);
@@ -460,8 +438,9 @@ operator()(error_code ec, std::size_t, bool again)
case 1: case 1:
{ {
boost::tribool const result = d.wp.w.write( auto const result =
std::move(d.copy), ec, writef0_lambda{*this}); d.wp.w.write(ec,
writef0_lambda{*this});
if(ec) if(ec)
{ {
// call handler // call handler
@@ -470,12 +449,6 @@ operator()(error_code ec, std::size_t, bool again)
std::move(*this), ec, false)); std::move(*this), ec, false));
return; return;
} }
if(boost::indeterminate(result))
{
// suspend
d.copy = d.resume;
return;
}
if(result) if(result)
d.state = d.wp.chunked ? 4 : 5; d.state = d.wp.chunked ? 4 : 5;
else else
@@ -491,20 +464,15 @@ operator()(error_code ec, std::size_t, bool again)
case 3: case 3:
{ {
boost::tribool result = d.wp.w.write( auto const result =
std::move(d.copy), ec, writef_lambda{*this}); d.wp.w.write(ec,
writef_lambda{*this});
if(ec) if(ec)
{ {
// call handler // call handler
d.state = 99; d.state = 99;
break; break;
} }
if(boost::indeterminate(result))
{
// suspend
d.copy = d.resume;
return;
}
if(result) if(result)
d.state = d.wp.chunked ? 4 : 5; d.state = d.wp.chunked ? 4 : 5;
else else
@@ -533,8 +501,6 @@ operator()(error_code ec, std::size_t, bool again)
break; break;
} }
} }
d.copy = {};
d.resume = {};
d_.invoke(ec); d_.invoke(ec);
} }
@@ -640,37 +606,12 @@ write(SyncWriteStream& stream,
wp.init(ec); wp.init(ec);
if(ec) if(ec)
return; return;
std::mutex m; auto result = wp.w.write(
std::condition_variable cv; ec, detail::writef0_lambda<
bool ready = false; SyncWriteStream, decltype(wp.sb)>{
resume_context resume{ stream, wp.sb, wp.chunked, ec});
[&]
{
std::lock_guard<std::mutex> lock(m);
ready = true;
cv.notify_one();
}};
auto copy = resume;
boost::tribool result =
wp.w.write(std::move(copy), ec,
detail::writef0_lambda<SyncWriteStream,
decltype(wp.sb)>{stream,
wp.sb, wp.chunked, ec});
if(ec) if(ec)
return; return;
if(boost::indeterminate(result))
{
copy = resume;
{
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
}
boost::asio::write(stream, wp.sb.data(), ec);
if(ec)
return;
result = false;
}
wp.sb.consume(wp.sb.size()); wp.sb.consume(wp.sb.size());
if(! result) if(! result)
{ {
@@ -678,17 +619,11 @@ write(SyncWriteStream& stream,
stream, wp.chunked, ec}; stream, wp.chunked, ec};
for(;;) for(;;)
{ {
result = wp.w.write(std::move(copy), ec, wf); result = wp.w.write(ec, wf);
if(ec) if(ec)
return; return;
if(result) if(result)
break; break;
if(! result)
continue;
copy = resume;
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return ready; });
ready = false;
} }
} }
if(wp.chunked) if(wp.chunked)

View File

@@ -1,34 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_HTTP_RESUME_CONTEXT_HPP
#define BEAST_HTTP_RESUME_CONTEXT_HPP
#include <functional>
namespace beast {
namespace http {
/** A functor that resumes a write operation.
An rvalue reference to an object of this type is provided by the
write implementation to the `writer` associated with the body of
a message being sent.
If it is desired that the `writer` suspend the write operation (for
example, to wait until data is ready), it can take ownership of
the resume context using a move. Then, it returns `boost::indeterminate`
to indicate that the write operation should suspend. Later, the calling
code invokes the resume function and the write operation continues
from where it left off.
*/
using resume_context = std::function<void(void)>;
} // http
} // beast
#endif

View File

@@ -10,10 +10,8 @@
#include <beast/core/error.hpp> #include <beast/core/error.hpp>
#include <beast/http/message.hpp> #include <beast/http/message.hpp>
#include <beast/http/resume_context.hpp>
#include <beast/core/detail/type_traits.hpp> #include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp> #include <boost/asio/buffer.hpp>
#include <boost/logic/tribool.hpp>
#include <memory> #include <memory>
#include <string> #include <string>
@@ -87,9 +85,8 @@ private:
} }
template<class WriteFunction> template<class WriteFunction>
boost::tribool bool
write(resume_context&&, error_code&, write(error_code&, WriteFunction&& wf) noexcept
WriteFunction&& wf) noexcept
{ {
wf(boost::asio::buffer(body_)); wf(boost::asio::buffer(body_));
return true; return true;

View File

@@ -58,7 +58,6 @@ unit-test http-tests :
http/parser_v1.cpp http/parser_v1.cpp
http/read.cpp http/read.cpp
http/reason.cpp http/reason.cpp
http/resume_context.cpp
http/rfc7230.cpp http/rfc7230.cpp
http/streambuf_body.cpp http/streambuf_body.cpp
http/string_body.cpp http/string_body.cpp

View File

@@ -23,7 +23,6 @@ add_executable (http-tests
parser_v1.cpp parser_v1.cpp
read.cpp read.cpp
reason.cpp reason.cpp
resume_context.cpp
rfc7230.cpp rfc7230.cpp
streambuf_body.cpp streambuf_body.cpp
string_body.cpp string_body.cpp

View File

@@ -1,9 +0,0 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Test that header file is self-contained.
#include <beast/http/resume_context.hpp>

View File

@@ -55,9 +55,8 @@ public:
} }
template<class WriteFunction> template<class WriteFunction>
boost::tribool bool
write(resume_context&&, error_code&, write(error_code&, WriteFunction&& wf) noexcept
WriteFunction&& wf) noexcept
{ {
wf(boost::asio::buffer(body_)); wf(boost::asio::buffer(body_));
return true; return true;
@@ -103,8 +102,6 @@ public:
{ {
std::size_t n_ = 0; std::size_t n_ = 0;
value_type const& body_; value_type const& body_;
bool suspend_ = false;
enable_yield_to yt_;
public: public:
template<bool isRequest, class Allocator> template<bool isRequest, class Allocator>
@@ -120,37 +117,12 @@ public:
body_.fc_.fail(ec); body_.fc_.fail(ec);
} }
class do_resume
{
resume_context rc_;
public:
explicit
do_resume(resume_context&& rc)
: rc_(std::move(rc))
{
}
void
operator()()
{
rc_();
}
};
template<class WriteFunction> template<class WriteFunction>
boost::tribool bool
write(resume_context&& rc, error_code& ec, write(error_code& ec, WriteFunction&& wf) noexcept
WriteFunction&& wf) noexcept
{ {
if(body_.fc_.fail(ec)) if(body_.fc_.fail(ec))
return false; return false;
suspend_ = ! suspend_;
if(suspend_)
{
yt_.get_io_service().post(do_resume{std::move(rc)});
return boost::indeterminate;
}
if(n_ >= body_.s_.size()) if(n_ >= body_.s_.size())
return true; return true;
wf(boost::asio::buffer(body_.s_.data() + n_, 1)); wf(boost::asio::buffer(body_.s_.data() + n_, 1));