Skip to content

Commit

Permalink
Update invocation examples to use executors.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriskohlhoff committed Dec 1, 2017
1 parent 5a9d072 commit e556aed
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 27 deletions.
1 change: 1 addition & 0 deletions asio/boostify.pl
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ sub copy_examples
"src/examples/cpp11/executors",
"src/examples/cpp11/futures",
"src/examples/cpp11/http/server",
"src/examples/cpp11/invocation",
"src/examples/cpp11/iostreams",
"src/examples/cpp11/spawn",
"src/examples/cpp14/executors");
Expand Down
63 changes: 36 additions & 27 deletions asio/src/examples/cpp03/invocation/prioritised_handlers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

using asio::ip::tcp;

class handler_priority_queue
class handler_priority_queue : asio::execution_context
{
public:
void add(int priority, boost::function<void()> function)
Expand All @@ -33,43 +33,60 @@ class handler_priority_queue
}
}

// A generic wrapper class for handlers to allow the invocation to be hooked.
template <typename Handler>
class wrapped_handler
class executor
{
public:
wrapped_handler(handler_priority_queue& q, int p, Handler h)
: queue_(q), priority_(p), handler_(h)
executor(handler_priority_queue& q, int p)
: context_(q), priority_(p)
{
}

handler_priority_queue& context() const
{
return context_;
}

void operator()()
template <typename Function, typename Allocator>
void dispatch(const Function& f, const Allocator&) const
{
handler_();
context_.add(priority_, f);
}

template <typename Arg1>
void operator()(Arg1 arg1)
template <typename Function, typename Allocator>
void post(const Function& f, const Allocator&) const
{
handler_(arg1);
context_.add(priority_, f);
}

template <typename Arg1, typename Arg2>
void operator()(Arg1 arg1, Arg2 arg2)
template <typename Function, typename Allocator>
void defer(const Function& f, const Allocator&) const
{
context_.add(priority_, f);
}

void on_work_started() const {}
void on_work_finished() const {}

bool operator==(const executor& other) const
{
handler_(arg1, arg2);
return &context_ == &other.context_ && priority_ == other.priority_;
}

//private:
handler_priority_queue& queue_;
bool operator!=(const executor& other) const
{
return !operator==(other);
}

private:
handler_priority_queue& context_;
int priority_;
Handler handler_;
};

template <typename Handler>
wrapped_handler<Handler> wrap(int priority, Handler handler)
asio::executor_binder<Handler, executor>
wrap(int priority, Handler handler)
{
return wrapped_handler<Handler>(*this, priority, handler);
return asio::bind_executor(executor(*this, priority), handler);
}

private:
Expand Down Expand Up @@ -100,14 +117,6 @@ class handler_priority_queue
std::priority_queue<queued_handler> handlers_;
};

// Custom invocation hook for wrapped handlers.
template <typename Function, typename Handler>
void asio_handler_invoke(Function f,
handler_priority_queue::wrapped_handler<Handler>* h)
{
h->queue_.add(h->priority_, f);
}

//----------------------------------------------------------------------

void high_priority_handler(const asio::error_code& /*ec*/)
Expand Down
2 changes: 2 additions & 0 deletions asio/src/examples/cpp11/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ noinst_PROGRAMS = \
executors/priority_scheduler \
futures/daytime_client \
http/server/http_server \
invocation/prioritised_handlers \
iostreams/http_client

if HAVE_BOOST_COROUTINE
Expand Down Expand Up @@ -67,6 +68,7 @@ http_server_http_server_SOURCES = \
http/server/request_handler.cpp \
http/server/request_parser.cpp \
http/server/server.cpp
invocation_prioritised_handlers_SOURCES = invocation/prioritised_handlers.cpp
iostreams_http_client_SOURCES = iostreams/http_client.cpp

if HAVE_BOOST_COROUTINE
Expand Down
10 changes: 10 additions & 0 deletions asio/src/examples/cpp11/invocation/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.deps
.dirstamp
*.o
*.obj
*.exe
prioritised_handlers
*.ilk
*.manifest
*.pdb
*.tds
202 changes: 202 additions & 0 deletions asio/src/examples/cpp11/invocation/prioritised_handlers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
//
// prioritised_handlers.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff 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)
//

#include "asio.hpp"
#include <iostream>
#include <memory>
#include <queue>

using asio::ip::tcp;

class handler_priority_queue : asio::execution_context
{
public:
template <typename Function>
void add(int priority, Function function)
{
std::unique_ptr<queued_handler_base> handler(
new queued_handler<Function>(
priority, std::move(function)));

handlers_.push(std::move(handler));
}

void execute_all()
{
while (!handlers_.empty())
{
handlers_.top()->execute();
handlers_.pop();
}
}

class executor
{
public:
executor(handler_priority_queue& q, int p)
: context_(q), priority_(p)
{
}

handler_priority_queue& context() const noexcept
{
return context_;
}

template <typename Function, typename Allocator>
void dispatch(Function f, const Allocator&) const
{
context_.add(priority_, std::move(f));
}

template <typename Function, typename Allocator>
void post(Function f, const Allocator&) const
{
context_.add(priority_, std::move(f));
}

template <typename Function, typename Allocator>
void defer(Function f, const Allocator&) const
{
context_.add(priority_, std::move(f));
}

void on_work_started() const noexcept {}
void on_work_finished() const noexcept {}

bool operator==(const executor& other) const noexcept
{
return &context_ == &other.context_ && priority_ == other.priority_;
}

bool operator!=(const executor& other) const noexcept
{
return !operator==(other);
}

private:
handler_priority_queue& context_;
int priority_;
};

template <typename Handler>
asio::executor_binder<Handler, executor>
wrap(int priority, Handler handler)
{
return asio::bind_executor(
executor(*this, priority), std::move(handler));
}

private:
class queued_handler_base
{
public:
queued_handler_base(int p)
: priority_(p)
{
}

virtual ~queued_handler_base()
{
}

virtual void execute() = 0;

friend bool operator<(const std::unique_ptr<queued_handler_base>& a,
const std::unique_ptr<queued_handler_base>& b) noexcept
{
return a->priority_ < b->priority_;
}

private:
int priority_;
};

template <typename Function>
class queued_handler : public queued_handler_base
{
public:
queued_handler(int p, Function f)
: queued_handler_base(p), function_(std::move(f))
{
}

void execute() override
{
function_();
}

private:
Function function_;
};

std::priority_queue<std::unique_ptr<queued_handler_base>> handlers_;
};

//----------------------------------------------------------------------

void high_priority_handler(const asio::error_code& /*ec*/)
{
std::cout << "High priority handler\n";
}

void middle_priority_handler(const asio::error_code& /*ec*/)
{
std::cout << "Middle priority handler\n";
}

struct low_priority_handler
{
// Make the handler a move-only type.
low_priority_handler() = default;
low_priority_handler(const low_priority_handler&) = delete;
low_priority_handler(low_priority_handler&&) = default;

void operator()()
{
std::cout << "Low priority handler\n";
}
};

int main()
{
asio::io_context io_context;

handler_priority_queue pri_queue;

// Post a completion handler to be run immediately.
asio::post(io_context, pri_queue.wrap(0, low_priority_handler()));

// Start an asynchronous accept that will complete immediately.
tcp::endpoint endpoint(asio::ip::address_v4::loopback(), 0);
tcp::acceptor acceptor(io_context, endpoint);
tcp::socket server_socket(io_context);
acceptor.async_accept(server_socket,
pri_queue.wrap(100, high_priority_handler));
tcp::socket client_socket(io_context);
client_socket.connect(acceptor.local_endpoint());

// Set a deadline timer to expire immediately.
asio::steady_timer timer(io_context);
timer.expires_at(asio::steady_timer::clock_type::time_point::min());
timer.async_wait(pri_queue.wrap(42, middle_priority_handler));

while (io_context.run_one())
{
// The custom invocation hook adds the handlers to the priority queue
// rather than executing them from within the poll_one() call.
while (io_context.poll_one())
;

pri_queue.execute_all();
}

return 0;
}

0 comments on commit e556aed

Please sign in to comment.