Skip to content

Commit 304a29a

Browse files
committed
Modernize route_params async support
- Fix spawn() to use capy::executor::post - Add capy::executor member to route_params - Add exception_ptr overload to resumer for proper error propagation - Remove post() and polling infrastructure in favor of executor
1 parent 622d39b commit 304a29a

3 files changed

Lines changed: 50 additions & 148 deletions

File tree

include/boost/http_proto/server/route_handler.hpp

Lines changed: 6 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <boost/http_proto/detail/config.hpp>
1414
#include <boost/http_proto/server/router_types.hpp>
1515
#include <boost/capy/datastore.hpp>
16+
#include <boost/capy/executor.hpp>
1617
#include <boost/capy/task.hpp>
1718
#include <boost/http_proto/request.hpp> // VFALCO forward declare?
1819
#include <boost/http_proto/request_parser.hpp> // VFALCO forward declare?
@@ -81,6 +82,10 @@ struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE
8182
*/
8283
suspender suspend;
8384

85+
/** Executor associated with the session.
86+
*/
87+
capy::executor ex;
88+
8489
/** Destructor
8590
*/
8691
BOOST_HTTP_PROTO_DECL
@@ -176,70 +181,13 @@ struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE
176181
from the route handler.
177182
*/
178183
BOOST_HTTP_PROTO_DECL
179-
virtual auto spawn(
184+
auto spawn(
180185
capy::task<route_result> coro) ->
181186
route_result;
182187

183188
#endif
184189

185-
// VFALCO this doc isn't quite right because it doesn't explain
186-
// the possibility that post will return the final result immediately,
187-
// and it needs to remind the user that calling a function which
188-
// returns route_result means they have to return the value right away
189-
// without doing anything else.
190-
//
191-
// VFALCO we have to detect calls to suspend inside `f` and throw
192-
//
193-
/** Submit cooperative work.
194-
195-
This function suspend the current handler from the session,
196-
and immediately invokes the specified function object @p f.
197-
When the function returns normally, the function object is
198-
placed into an implementation-defined work queue to be invoked
199-
again. Otherwise, if the function calls `resume(rv)` then the
200-
session is resumed and behaves as if the original route handler
201-
had returned the value `rv`.
202-
203-
When the function object is invoked, it runs in the same context
204-
as the original handler invocation. If the function object
205-
attempts to call @ref post again, or attempts to call @ref suspend
206-
an exception is thrown.
207-
208-
The function object @p f must have this equivalent signature:
209-
@code
210-
void ( resumer resume );
211-
@endcode
212-
213-
@param f The function object to invoke.
214-
@param c The continuation function to be invoked when f finishes.
215-
*/
216-
template<class F>
217-
auto
218-
post(F&& f) -> route_result;
219-
220190
protected:
221-
/** A task to be invoked later
222-
*/
223-
struct task
224-
{
225-
virtual ~task() = default;
226-
227-
/** Invoke the task.
228-
229-
@return true if the task resumed the session.
230-
*/
231-
virtual bool invoke() = 0;
232-
};
233-
234-
/** Post task_ to be invoked later
235-
236-
Subclasses must schedule task_ to be invoked at an unspecified
237-
point in the future.
238-
*/
239-
BOOST_HTTP_PROTO_DECL
240-
virtual void do_post();
241-
242-
std::unique_ptr<task> task_;
243191
std::function<void(void)> finish_;
244192
};
245193

@@ -292,79 +240,6 @@ read_body(
292240

293241
//-----------------------------------------------
294242

295-
template<class F>
296-
auto
297-
route_params::
298-
post(F&& f) -> route_result
299-
{
300-
// task already posted
301-
if(task_)
302-
detail::throw_invalid_argument();
303-
304-
struct immediate : suspender::owner
305-
{
306-
route_result rv;
307-
bool set = false;
308-
void do_resume(
309-
route_result const& rv_) override
310-
{
311-
rv = rv_;
312-
set = true;
313-
}
314-
};
315-
316-
class model: public task, suspender::owner
317-
{
318-
public:
319-
model(route_params& p,
320-
F&& f, resumer resume)
321-
: p_(p)
322-
, f_(std::forward<F>(f))
323-
, resume_(resume)
324-
{
325-
}
326-
327-
bool invoke() override
328-
{
329-
resumed_ = false;
330-
// VFALCO analyze exception safety
331-
f_(resumer(*this));
332-
return resumed_;
333-
}
334-
335-
void do_resume(
336-
route_result const& rv) override
337-
{
338-
resumed_ = true;
339-
resumer resume(resume_);
340-
p_.task_.reset(); // destroys *this
341-
resume(rv);
342-
}
343-
344-
private:
345-
route_params& p_;
346-
typename std::decay<F>::type f_;
347-
resumer resume_;
348-
bool resumed_;
349-
};
350-
351-
// first call
352-
immediate impl;
353-
f(resumer(impl));
354-
if(impl.set)
355-
return impl.rv;
356-
357-
return suspend(
358-
[&](resumer resume)
359-
{
360-
task_ = std::unique_ptr<task>(new model(
361-
*this, std::forward<F>(f), resume));
362-
do_post();
363-
});
364-
}
365-
366-
//-----------------------------------------------
367-
368243
#ifdef BOOST_HTTP_PROTO_HAS_CORO
369244

370245
/** Create a route handler from a coroutine function

include/boost/http_proto/server/router_types.hpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,10 @@ class suspender
167167
struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE
168168
owner
169169
{
170-
BOOST_HTTP_PROTO_DECL virtual resumer do_suspend();
170+
BOOST_HTTP_PROTO_DECL
171+
virtual resumer do_suspend();
171172
virtual void do_resume(route_result const&) = 0;
173+
virtual void do_resume(std::exception_ptr) = 0;
172174
};
173175

174176
suspender() = default;
@@ -253,7 +255,7 @@ class resumer
253255
When a session is resumed, routing continues as if the handler
254256
had returned the @ref route_result contained in @p rv.
255257
256-
@param ec The error code to resume with.
258+
@param rv The route result to resume with.
257259
258260
@throw std::invalid_argument If the object is empty.
259261
*/
@@ -265,6 +267,23 @@ class resumer
265267
p_->do_resume(rv);
266268
}
267269

270+
/** Resume the session with an exception
271+
272+
When a session is resumed with an exception, the exception
273+
is propagated through the router's error handling mechanism.
274+
275+
@param ep The exception to propagate.
276+
277+
@throw std::invalid_argument If the object is empty.
278+
*/
279+
void operator()(
280+
std::exception_ptr ep) const
281+
{
282+
if(! p_)
283+
detail::throw_invalid_argument();
284+
p_->do_resume(ep);
285+
}
286+
268287
private:
269288
suspender::owner* p_
270289
#if defined(__clang__)

src/server/route_handler.cpp

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
#include <boost/http_proto/server/route_handler.hpp>
1111
#include <boost/http_proto/string_body.hpp>
12-
#include <boost/assert.hpp>
13-
#include <cstring>
1412

1513
namespace boost {
1614
namespace http_proto {
@@ -56,24 +54,34 @@ set_body(std::string s)
5654
auto
5755
route_params::
5856
spawn(
59-
capy::task<route_result>) ->
57+
capy::task<route_result> t) ->
6058
route_result
6159
{
62-
detail::throw_invalid_argument();
63-
}
60+
return this->suspend(
61+
[ex = this->ex, t = std::move(t)](resumer resume) mutable
62+
{
63+
auto h = t.release();
6464

65-
#endif
65+
h.promise().on_done = [resume, h]()
66+
{
67+
auto& r = h.promise().result;
68+
if(r.index() == 2)
69+
{
70+
auto ep = std::get<2>(r);
71+
h.destroy();
72+
resume(ep);
73+
return;
74+
}
75+
auto rv = std::move(std::get<1>(r));
76+
h.destroy();
77+
resume(rv);
78+
};
6679

67-
void
68-
route_params::
69-
do_post()
70-
{
71-
BOOST_ASSERT(task_);
72-
// invoke until task resumes
73-
for(;;)
74-
if(task_->invoke())
75-
break;
80+
ex.post([h]() { h.resume(); });
81+
});
7682
}
7783

84+
#endif
85+
7886
} // http_proto
7987
} // boost

0 commit comments

Comments
 (0)