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.
@@ -168,16 +148,11 @@ public:
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));