mirror of
https://github.com/boostorg/fiber.git
synced 2026-07-21 13:13:32 +00:00
examples added
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
// Copyright Nat Goodspeed 2015.
|
||||
// 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 <boost/fiber/all.hpp>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <tuple> // std::tie()
|
||||
#include <cassert>
|
||||
|
||||
/*****************************************************************************
|
||||
* example async API
|
||||
*****************************************************************************/
|
||||
//[AsyncAPI
|
||||
class AsyncAPI {
|
||||
public:
|
||||
// constructor acquires some resource that can be read and written
|
||||
AsyncAPI();
|
||||
|
||||
// callbacks accept an int error code; 0 == success
|
||||
typedef int errorcode;
|
||||
|
||||
// write callback only needs to indicate success or failure
|
||||
void init_write( std::string const& data,
|
||||
std::function< void( errorcode) > const& callback);
|
||||
|
||||
// read callback needs to accept both errorcode and data
|
||||
void init_read( std::function< void( errorcode, std::string const&) > const&);
|
||||
|
||||
// ... other operations ...
|
||||
//<-
|
||||
void inject_error( errorcode ec);
|
||||
|
||||
private:
|
||||
std::string data_;
|
||||
errorcode injected_;
|
||||
//->
|
||||
};
|
||||
//]
|
||||
|
||||
/*****************************************************************************
|
||||
* fake AsyncAPI implementation... pay no attention to the little man behind
|
||||
* the curtain...
|
||||
*****************************************************************************/
|
||||
AsyncAPI::AsyncAPI() :
|
||||
injected_( 0) {
|
||||
}
|
||||
|
||||
void AsyncAPI::inject_error( errorcode ec) {
|
||||
injected_ = ec;
|
||||
}
|
||||
|
||||
void AsyncAPI::init_write( std::string const& data,
|
||||
std::function< void( errorcode) > const& callback) {
|
||||
// make a local copy of injected_
|
||||
errorcode injected( injected_);
|
||||
// reset it synchronously with caller
|
||||
injected_ = 0;
|
||||
// update data_ (this might be an echo service)
|
||||
if ( ! injected) {
|
||||
data_ = data;
|
||||
}
|
||||
// Simulate an asynchronous I/O operation by launching a detached thread
|
||||
// that sleeps a bit before calling completion callback. Echo back to
|
||||
// caller any previously-injected errorcode.
|
||||
std::thread( [injected, callback](){
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds(100) );
|
||||
callback( injected);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void AsyncAPI::init_read( std::function< void( errorcode, std::string const&) > const& callback) {
|
||||
// make a local copy of injected_
|
||||
errorcode injected( injected_);
|
||||
// reset it synchronously with caller
|
||||
injected_ = 0;
|
||||
// local copy of data_ so we can capture in lambda
|
||||
std::string data( data_);
|
||||
// Simulate an asynchronous I/O operation by launching a detached thread
|
||||
// that sleeps a bit before calling completion callback. Echo back to
|
||||
// caller any previously-injected errorcode.
|
||||
std::thread( [injected, callback, data](){
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds(100) );
|
||||
callback( injected, data);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* adapters
|
||||
*****************************************************************************/
|
||||
// helper function used in a couple of the adapters
|
||||
std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode);
|
||||
|
||||
//[callbacks_write_ec
|
||||
AsyncAPI::errorcode write_ec( AsyncAPI & api, std::string const& data) {
|
||||
boost::fibers::promise< AsyncAPI::errorcode > promise;
|
||||
boost::fibers::future< AsyncAPI::errorcode > future( promise.get_future() );
|
||||
// We can confidently bind a reference to local variable 'promise' into
|
||||
// the lambda callback because we know for a fact we're going to suspend
|
||||
// (preserving the lifespan of both 'promise' and 'future') until the
|
||||
// callback has fired.
|
||||
api.init_write( data,
|
||||
[&promise]( AsyncAPI::errorcode ec){
|
||||
promise.set_value( ec);
|
||||
});
|
||||
return future.get();
|
||||
}
|
||||
//]
|
||||
|
||||
//[callbacks_write
|
||||
void write( AsyncAPI & api, std::string const& data) {
|
||||
AsyncAPI::errorcode ec = write_ec( api, data);
|
||||
if ( ec) {
|
||||
throw make_exception("write", ec);
|
||||
}
|
||||
}
|
||||
//]
|
||||
|
||||
//[callbacks_read_ec
|
||||
std::pair< AsyncAPI::errorcode, std::string > read_ec( AsyncAPI & api) {
|
||||
typedef std::pair< AsyncAPI::errorcode, std::string > result_pair;
|
||||
boost::fibers::promise< result_pair > promise;
|
||||
boost::fibers::future< result_pair > future( promise.get_future() );
|
||||
// We promise that both 'promise' and 'future' will survive until our
|
||||
// lambda has been called.
|
||||
api.init_read( [&promise]( AsyncAPI::errorcode ec, std::string const& data){
|
||||
promise.set_value( result_pair( ec, data) );
|
||||
});
|
||||
return future.get();
|
||||
}
|
||||
//]
|
||||
|
||||
//[callbacks_read
|
||||
std::string read( AsyncAPI & api) {
|
||||
boost::fibers::promise< std::string > promise;
|
||||
boost::fibers::future< std::string > future( promise.get_future() );
|
||||
// Both 'promise' and 'future' will survive until our lambda has been
|
||||
// called.
|
||||
api.init_read( [&promise]( AsyncAPI::errorcode ec, std::string const& data){
|
||||
if ( ! ec) {
|
||||
promise.set_value( data);
|
||||
} else {
|
||||
promise.set_exception(
|
||||
std::make_exception_ptr(
|
||||
make_exception("read", ec) ) );
|
||||
}
|
||||
});
|
||||
return future.get();
|
||||
}
|
||||
//]
|
||||
|
||||
/*****************************************************************************
|
||||
* helpers
|
||||
*****************************************************************************/
|
||||
std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode ec) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "Error in AsyncAPI::" << desc << "(): " << ec;
|
||||
return std::runtime_error( buffer.str() );
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* driving logic
|
||||
*****************************************************************************/
|
||||
int main( int argc, char *argv[]) {
|
||||
AsyncAPI api;
|
||||
|
||||
// successful write(): prime AsyncAPI with some data
|
||||
write( api, "abcd");
|
||||
// successful read(): retrieve it
|
||||
std::string data( read( api) );
|
||||
assert( data == "abcd");
|
||||
|
||||
// successful write_ec()
|
||||
AsyncAPI::errorcode ec( write_ec( api, "efgh") );
|
||||
assert( ec == 0);
|
||||
|
||||
// write_ec() with error
|
||||
api.inject_error(1);
|
||||
ec = write_ec( api, "ijkl");
|
||||
assert( ec == 1);
|
||||
|
||||
// write() with error
|
||||
std::string thrown;
|
||||
api.inject_error(2);
|
||||
try {
|
||||
write(api, "mnop");
|
||||
} catch ( std::exception const& e) {
|
||||
thrown = e.what();
|
||||
}
|
||||
assert( thrown == make_exception("write", 2).what() );
|
||||
|
||||
// successful read_ec()
|
||||
//[callbacks_read_ec_call
|
||||
std::tie( ec, data) = read_ec( api);
|
||||
//]
|
||||
assert( ! ec);
|
||||
assert( data == "efgh"); // last successful write_ec()
|
||||
|
||||
// read_ec() with error
|
||||
api.inject_error(3);
|
||||
std::tie( ec, data) = read_ec( api);
|
||||
assert( ec == 3);
|
||||
// 'data' in unspecified state, don't test
|
||||
|
||||
// read() with error
|
||||
thrown.clear();
|
||||
api.inject_error(4);
|
||||
try {
|
||||
data = read(api);
|
||||
} catch ( std::exception const& e) {
|
||||
thrown = e.what();
|
||||
}
|
||||
assert( thrown == make_exception("read", 4).what() );
|
||||
|
||||
std::cout << "done." << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright Nat Goodspeed 2015.
|
||||
// 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 <boost/fiber/all.hpp>
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <cassert>
|
||||
|
||||
/*****************************************************************************
|
||||
* example async API
|
||||
*****************************************************************************/
|
||||
// introduce class-scope typedef
|
||||
struct AsyncAPIBase {
|
||||
// error callback accepts an int error code; 0 == success
|
||||
typedef int errorcode;
|
||||
};
|
||||
|
||||
//[Response
|
||||
// every async operation receives a subclass instance of this abstract base
|
||||
// class through which to communicate its result
|
||||
struct Response {
|
||||
typedef std::shared_ptr< Response > ptr;
|
||||
|
||||
// called if the operation succeeds
|
||||
virtual void success( std::string const& data) = 0;
|
||||
|
||||
// called if the operation fails
|
||||
virtual void error( AsyncAPIBase::errorcode ec) = 0;
|
||||
};
|
||||
//]
|
||||
|
||||
// the actual async API
|
||||
class AsyncAPI: public AsyncAPIBase {
|
||||
public:
|
||||
// constructor acquires some resource that can be read
|
||||
AsyncAPI( std::string const& data);
|
||||
|
||||
//[method_init_read
|
||||
// derive Response subclass, instantiate, pass Response::ptr
|
||||
void init_read( Response::ptr);
|
||||
//]
|
||||
|
||||
// ... other operations ...
|
||||
void inject_error( errorcode ec);
|
||||
|
||||
private:
|
||||
std::string data_;
|
||||
errorcode injected_;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* fake AsyncAPI implementation... pay no attention to the little man behind
|
||||
* the curtain...
|
||||
*****************************************************************************/
|
||||
AsyncAPI::AsyncAPI( std::string const& data) :
|
||||
data_( data),
|
||||
injected_( 0) {
|
||||
}
|
||||
|
||||
void AsyncAPI::inject_error( errorcode ec) {
|
||||
injected_ = ec;
|
||||
}
|
||||
|
||||
void AsyncAPI::init_read( Response::ptr response) {
|
||||
// make a local copy of injected_
|
||||
errorcode injected( injected_);
|
||||
// reset it synchronously with caller
|
||||
injected_ = 0;
|
||||
// local copy of data_ so we can capture in lambda
|
||||
std::string data( data_);
|
||||
// Simulate an asynchronous I/O operation by launching a detached thread
|
||||
// that sleeps a bit before calling either completion method.
|
||||
std::thread( [injected, response, data](){
|
||||
std::this_thread::sleep_for( std::chrono::milliseconds(100) );
|
||||
if ( ! injected) {
|
||||
// no error, call success()
|
||||
response->success( data);
|
||||
} else {
|
||||
// injected error, call error()
|
||||
response->error( injected);
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* adapters
|
||||
*****************************************************************************/
|
||||
// helper function
|
||||
std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode);
|
||||
|
||||
//[PromiseResponse
|
||||
class PromiseResponse: public Response {
|
||||
public:
|
||||
// called if the operation succeeds
|
||||
virtual void success( std::string const& data) {
|
||||
promise_.set_value( data);
|
||||
}
|
||||
|
||||
// called if the operation fails
|
||||
virtual void error( AsyncAPIBase::errorcode ec) {
|
||||
promise_.set_exception(
|
||||
std::make_exception_ptr(
|
||||
make_exception("read", ec) ) );
|
||||
}
|
||||
|
||||
boost::fibers::future< std::string > get_future() {
|
||||
return promise_.get_future();
|
||||
}
|
||||
|
||||
private:
|
||||
boost::fibers::promise< std::string > promise_;
|
||||
};
|
||||
//]
|
||||
|
||||
//[method_read
|
||||
std::string read( AsyncAPI & api) {
|
||||
// Because init_read() requires a shared_ptr, we must allocate our
|
||||
// ResponsePromise on the heap, even though we know its lifespan.
|
||||
auto promisep( std::make_shared< PromiseResponse >() );
|
||||
boost::fibers::future< std::string > future( promisep->get_future() );
|
||||
// Both 'promisep' and 'future' will survive until our lambda has been
|
||||
// called.
|
||||
api.init_read( promisep);
|
||||
return future.get();
|
||||
}
|
||||
//]
|
||||
|
||||
/*****************************************************************************
|
||||
* helpers
|
||||
*****************************************************************************/
|
||||
std::runtime_error make_exception( std::string const& desc, AsyncAPI::errorcode ec) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "Error in AsyncAPI::" << desc << "(): " << ec;
|
||||
return std::runtime_error( buffer.str() );
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* driving logic
|
||||
*****************************************************************************/
|
||||
int main(int argc, char *argv[]) {
|
||||
// prime AsyncAPI with some data
|
||||
AsyncAPI api("abcd");
|
||||
|
||||
// successful read(): retrieve it
|
||||
std::string data( read( api) );
|
||||
assert(data == "abcd");
|
||||
|
||||
// read() with error
|
||||
std::string thrown;
|
||||
api.inject_error(1);
|
||||
try {
|
||||
data = read( api);
|
||||
} catch ( std::exception const& e) {
|
||||
thrown = e.what();
|
||||
}
|
||||
assert(thrown == make_exception("read", 1).what() );
|
||||
|
||||
std::cout << "done." << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
// Copyright Nat Goodspeed 2015.
|
||||
// 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 <boost/fiber/all.hpp>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <algorithm> // std::min()
|
||||
#include <errno.h> // EWOULDBLOCK
|
||||
#include <cassert>
|
||||
|
||||
/*****************************************************************************
|
||||
* example nonblocking API
|
||||
*****************************************************************************/
|
||||
//[NonblockingAPI
|
||||
class NonblockingAPI {
|
||||
public:
|
||||
NonblockingAPI();
|
||||
|
||||
// nonblocking operation: may return EWOULDBLOCK
|
||||
int read( std::string & data, std::size_t desired);
|
||||
|
||||
/*= ...*/
|
||||
//<-
|
||||
// for simulating a real nonblocking API
|
||||
void set_data( std::string const& data, std::size_t chunksize);
|
||||
void inject_error( int ec);
|
||||
|
||||
private:
|
||||
std::string data_;
|
||||
int injected_;
|
||||
unsigned tries_;
|
||||
std::size_t chunksize_;
|
||||
//->
|
||||
};
|
||||
//]
|
||||
|
||||
/*****************************************************************************
|
||||
* fake NonblockingAPI implementation... pay no attention to the little man
|
||||
* behind the curtain...
|
||||
*****************************************************************************/
|
||||
NonblockingAPI::NonblockingAPI() :
|
||||
injected_( 0),
|
||||
tries_( 0),
|
||||
chunksize_( 9999) {
|
||||
}
|
||||
|
||||
void NonblockingAPI::set_data( std::string const& data, std::size_t chunksize) {
|
||||
data_ = data;
|
||||
chunksize_ = chunksize;
|
||||
// This delimits the start of a new test. Reset state.
|
||||
injected_ = 0;
|
||||
tries_ = 0;
|
||||
}
|
||||
|
||||
void NonblockingAPI::inject_error( int ec) {
|
||||
injected_ = ec;
|
||||
}
|
||||
|
||||
int NonblockingAPI::read( std::string & data, std::size_t desired) {
|
||||
// in case of error
|
||||
data.clear();
|
||||
|
||||
if ( injected_) {
|
||||
// copy injected_ because we're about to reset it
|
||||
auto injected( injected_);
|
||||
injected_ = 0;
|
||||
// after an error situation, restart success count
|
||||
tries_ = 0;
|
||||
return injected;
|
||||
}
|
||||
|
||||
if ( ++tries_ < 5) {
|
||||
// no injected error, but the resource isn't yet ready
|
||||
return EWOULDBLOCK;
|
||||
}
|
||||
|
||||
// tell caller there's nothing left
|
||||
if ( data_.empty() ) {
|
||||
return EOF;
|
||||
}
|
||||
|
||||
// okay, finally have some data
|
||||
// but return minimum of desired and chunksize_
|
||||
std::size_t size( ( std::min)( desired, chunksize_) );
|
||||
data = data_.substr( 0, size);
|
||||
// strip off what we just returned
|
||||
data_ = data_.substr( size);
|
||||
// reset I/O retries count for next time
|
||||
tries_ = 0;
|
||||
// success
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* adapters
|
||||
*****************************************************************************/
|
||||
//[nonblocking_read_chunk
|
||||
// guaranteed not to return EWOULDBLOCK
|
||||
int read_chunk( NonblockingAPI & api, std::string & data, std::size_t desired) {
|
||||
int error;
|
||||
while ( EWOULDBLOCK == ( error = api.read( data, desired) ) ) {
|
||||
// not ready yet -- try again on the next iteration of the
|
||||
// application's main loop
|
||||
boost::this_fiber::yield();
|
||||
}
|
||||
return error;
|
||||
}
|
||||
//]
|
||||
|
||||
//[nonblocking_read_desired
|
||||
// keep reading until desired length, EOF or error
|
||||
// may return both partial data and nonzero error
|
||||
int read_desired( NonblockingAPI & api, std::string & data, std::size_t desired) {
|
||||
// we're going to accumulate results into 'data'
|
||||
data.clear();
|
||||
std::string chunk;
|
||||
int error = 0;
|
||||
while ( data.length() < desired &&
|
||||
( error = read_chunk( api, chunk, desired - data.length() ) ) == 0) {
|
||||
data.append( chunk);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
//]
|
||||
|
||||
//[nonblocking_IncompleteRead
|
||||
// exception class augmented with both partially-read data and errorcode
|
||||
class IncompleteRead : public std::runtime_error {
|
||||
public:
|
||||
IncompleteRead( std::string const& what, std::string const& partial, int ec) :
|
||||
std::runtime_error( what),
|
||||
partial_( partial),
|
||||
ec_( ec) {
|
||||
}
|
||||
|
||||
std::string get_partial() const {
|
||||
return partial_;
|
||||
}
|
||||
|
||||
int get_errorcode() const {
|
||||
return ec_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string partial_;
|
||||
int ec_;
|
||||
};
|
||||
//]
|
||||
|
||||
//[nonblocking_read
|
||||
// read all desired data or throw IncompleteRead
|
||||
std::string read( NonblockingAPI & api, std::size_t desired) {
|
||||
std::string data;
|
||||
int ec( read_desired( api, data, desired) );
|
||||
|
||||
// for present purposes, EOF isn't a failure
|
||||
if ( 0 == ec || EOF == ec) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// oh oh, partial read
|
||||
std::ostringstream msg;
|
||||
msg << "NonblockingAPI::read() error " << ec << " after "
|
||||
<< data.length() << " of " << desired << " characters";
|
||||
throw IncompleteRead( msg.str(), data, ec);
|
||||
}
|
||||
//]
|
||||
|
||||
int main( int argc, char *argv[]) {
|
||||
NonblockingAPI api;
|
||||
const std::string sample_data("abcdefghijklmnopqrstuvwxyz");
|
||||
|
||||
// Try just reading directly from NonblockingAPI
|
||||
api.set_data( sample_data, 5);
|
||||
std::string data;
|
||||
int ec = api.read( data, 13);
|
||||
// whoops, underlying resource not ready
|
||||
assert(ec == EWOULDBLOCK);
|
||||
assert(data.empty());
|
||||
|
||||
// successful read()
|
||||
api.set_data( sample_data, 5);
|
||||
data = read( api, 13);
|
||||
assert(data == "abcdefghijklm");
|
||||
|
||||
// read() with error
|
||||
api.set_data( sample_data, 5);
|
||||
// don't accidentally pick either EOF or EWOULDBLOCK
|
||||
assert(EOF != 1);
|
||||
assert(EWOULDBLOCK != 1);
|
||||
api.inject_error(1);
|
||||
int thrown = 0;
|
||||
try {
|
||||
data = read( api, 13);
|
||||
} catch ( IncompleteRead const& e) {
|
||||
thrown = e.get_errorcode();
|
||||
}
|
||||
assert(thrown == 1);
|
||||
|
||||
std::cout << "done." << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// daytime_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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)
|
||||
//
|
||||
// modified by Oliver Kowalke
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/ip/udp.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#include "loop.hpp"
|
||||
#include "use_future.hpp"
|
||||
|
||||
using boost::asio::ip::udp;
|
||||
|
||||
void get_daytime(boost::asio::io_service& io_service, const char* hostname)
|
||||
{
|
||||
try
|
||||
{
|
||||
udp::resolver resolver(io_service);
|
||||
|
||||
boost::fibers::future<udp::resolver::iterator> iter =
|
||||
resolver.async_resolve(
|
||||
udp::resolver::query( udp::v4(), hostname, "daytime"),
|
||||
boost::fibers::asio::use_future);
|
||||
|
||||
// The async_resolve operation above returns the endpoint iterator as a
|
||||
// future value that is not retrieved ...
|
||||
|
||||
udp::socket socket(io_service, udp::v4());
|
||||
|
||||
boost::array<char, 1> send_buf = {{ 0 }};
|
||||
boost::fibers::future<std::size_t> send_length =
|
||||
socket.async_send_to(boost::asio::buffer(send_buf),
|
||||
*iter.get(), // ... until here. This call may block.
|
||||
boost::fibers::asio::use_future);
|
||||
|
||||
// Do other things here while the send completes.
|
||||
|
||||
send_length.get(); // Blocks until the send is complete. Throws any errors.
|
||||
|
||||
boost::array<char, 128> recv_buf;
|
||||
udp::endpoint sender_endpoint;
|
||||
boost::fibers::future<std::size_t> recv_length =
|
||||
socket.async_receive_from(
|
||||
boost::asio::buffer(recv_buf),
|
||||
sender_endpoint,
|
||||
boost::fibers::asio::use_future);
|
||||
|
||||
// Do other things here while the receive completes.
|
||||
|
||||
std::cout.write(
|
||||
recv_buf.data(),
|
||||
recv_length.get()); // Blocks until receive is complete.
|
||||
}
|
||||
catch (boost::system::system_error& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
io_service.stop();
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[])
|
||||
{
|
||||
boost::asio::io_service io_service;
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: daytime_client <host>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::fibers::fiber(
|
||||
boost::bind( get_daytime,
|
||||
boost::ref( io_service), argv[1]) ).detach();
|
||||
|
||||
boost::fibers::asio::run_service( io_service);
|
||||
}
|
||||
catch ( std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// daytime_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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)
|
||||
//
|
||||
// modified by Oliver Kowalke
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/asio/io_service.hpp>
|
||||
#include <boost/asio/ip/udp.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/system/system_error.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#include "loop.hpp"
|
||||
#include "yield.hpp"
|
||||
|
||||
using boost::asio::ip::udp;
|
||||
|
||||
void get_daytime(boost::asio::io_service& io_service, const char* hostname)
|
||||
{
|
||||
try
|
||||
{
|
||||
udp::resolver resolver(io_service);
|
||||
|
||||
udp::resolver::iterator iter =
|
||||
resolver.async_resolve(
|
||||
udp::resolver::query( udp::v4(), hostname, "daytime"),
|
||||
boost::fibers::asio::yield);
|
||||
|
||||
udp::socket socket(io_service, udp::v4());
|
||||
|
||||
boost::array<char, 1> send_buf = {{ 0 }};
|
||||
std::size_t send_length =
|
||||
socket.async_send_to(boost::asio::buffer(send_buf),
|
||||
*iter, boost::fibers::asio::yield);
|
||||
(void)send_length;
|
||||
|
||||
boost::array<char, 128> recv_buf;
|
||||
udp::endpoint sender_endpoint;
|
||||
std::size_t recv_length =
|
||||
socket.async_receive_from(
|
||||
boost::asio::buffer(recv_buf),
|
||||
sender_endpoint,
|
||||
boost::fibers::asio::yield);
|
||||
|
||||
std::cout.write(
|
||||
recv_buf.data(),
|
||||
recv_length);
|
||||
}
|
||||
catch (boost::system::system_error& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
io_service.stop();
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[])
|
||||
{
|
||||
boost::asio::io_service io_service;
|
||||
try
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: daytime_client <host>" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::fibers::fiber(
|
||||
boost::bind( get_daytime,
|
||||
boost::ref( io_service), argv[1]) ).detach();
|
||||
|
||||
boost::fibers::asio::run_service( io_service);
|
||||
}
|
||||
catch ( std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
//
|
||||
// promise_handler.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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)
|
||||
//
|
||||
// modified by Oliver Kowalke and Nat Goodspeed
|
||||
//
|
||||
|
||||
#ifndef BOOST_FIBERS_ASIO_DETAIL_PROMISE_HANDLER_HPP
|
||||
#define BOOST_FIBERS_ASIO_DETAIL_PROMISE_HANDLER_HPP
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
#include <boost/exception/all.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// Completion handler to adapt a promise as a completion handler.
|
||||
//[fibers_asio_promise_handler_base
|
||||
template< typename T >
|
||||
class promise_handler_base {
|
||||
public:
|
||||
typedef std::shared_ptr< boost::fibers::promise< T > > promise_ptr;
|
||||
|
||||
// Construct from any promise_completion_token subclass special value.
|
||||
template< typename Allocator >
|
||||
promise_handler_base( boost::fibers::asio::promise_completion_token< Allocator > const& pct) :
|
||||
promise_( std::make_shared< boost::fibers::promise< T > >(
|
||||
std::allocator_arg, pct.get_allocator() ) )
|
||||
//<-
|
||||
, ecp_( pct.ec_)
|
||||
//->
|
||||
{}
|
||||
|
||||
bool should_set_value( boost::system::error_code const& ec) {
|
||||
if ( ! ec) {
|
||||
// whew, success
|
||||
return true;
|
||||
}
|
||||
|
||||
//<-
|
||||
// ec indicates error
|
||||
if ( ecp_) {
|
||||
// promise_completion_token bound an error_code variable: set it
|
||||
* ecp_ = ec;
|
||||
// This is the odd case: although there's an error, user code
|
||||
// expressly forbid us to call set_exception(). We've set the
|
||||
// bound error code -- but future::get() will wait forever unless
|
||||
// we kick the promise SOMEHOW. Tell subclass to call set_value()
|
||||
// anyway.
|
||||
return true;
|
||||
}
|
||||
//->
|
||||
// no bound error_code: cause promise_ to throw an exception
|
||||
promise_->set_exception(
|
||||
std::make_exception_ptr(
|
||||
boost::system::system_error( ec) ) );
|
||||
// caller should NOT call set_value()
|
||||
return false;
|
||||
}
|
||||
|
||||
promise_ptr get_promise() const {
|
||||
return promise_;
|
||||
}
|
||||
|
||||
private:
|
||||
promise_ptr promise_;
|
||||
//<-
|
||||
boost::system::error_code * ecp_;
|
||||
//->
|
||||
};
|
||||
//]
|
||||
|
||||
// generic promise_handler for arbitrary value
|
||||
//[fibers_asio_promise_handler
|
||||
template< typename T >
|
||||
class promise_handler : public promise_handler_base< T > {
|
||||
private:
|
||||
//<-
|
||||
using promise_handler_base< T >::should_set_value;
|
||||
|
||||
//->
|
||||
public:
|
||||
// Construct from any promise_completion_token subclass special value.
|
||||
template< typename Allocator >
|
||||
promise_handler( boost::fibers::asio::promise_completion_token< Allocator > const& pct) :
|
||||
promise_handler_base< T >( pct) {
|
||||
}
|
||||
|
||||
//<-
|
||||
void operator()( T t) {
|
||||
get_promise()->set_value( t);
|
||||
}
|
||||
//->
|
||||
void operator()( boost::system::error_code const& ec, T t) {
|
||||
if ( should_set_value( ec) ) {
|
||||
get_promise()->set_value( t);
|
||||
}
|
||||
}
|
||||
//<-
|
||||
using typename promise_handler_base< T >::promise_ptr;
|
||||
using promise_handler_base< T >::get_promise;
|
||||
//->
|
||||
};
|
||||
//]
|
||||
|
||||
// specialize promise_handler for void
|
||||
template<>
|
||||
class promise_handler< void > : public promise_handler_base< void > {
|
||||
private:
|
||||
using promise_handler_base< void >::should_set_value;
|
||||
|
||||
public:
|
||||
// Construct from any promise_completion_token subclass special value.
|
||||
template< typename Allocator >
|
||||
promise_handler( boost::fibers::asio::promise_completion_token< Allocator > const& pct) :
|
||||
promise_handler_base< void >( pct) {
|
||||
}
|
||||
|
||||
void operator()() {
|
||||
get_promise()->set_value();
|
||||
}
|
||||
|
||||
void operator()( boost::system::error_code const& ec) {
|
||||
if ( should_set_value( ec) ) {
|
||||
get_promise()->set_value();
|
||||
}
|
||||
}
|
||||
|
||||
using promise_handler_base< void >::promise_ptr;
|
||||
using promise_handler_base< void >::get_promise;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// Specialize asio_handler_invoke hook to ensure that any exceptions thrown
|
||||
// from the handler are propagated back to the caller via the future.
|
||||
template< typename Function, typename T >
|
||||
void asio_handler_invoke( Function f, fibers::asio::detail::promise_handler< T > * h) {
|
||||
typename fibers::asio::detail::promise_handler< T >::promise_ptr
|
||||
p( h->get_promise() );
|
||||
try {
|
||||
f();
|
||||
} catch (...) {
|
||||
p->set_exception( std::current_exception() );
|
||||
}
|
||||
}
|
||||
|
||||
}}}
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
#endif
|
||||
|
||||
#endif // BOOST_FIBERS_ASIO_DETAIL_PROMISE_HANDLER_HPP
|
||||
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// use_future.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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)
|
||||
//
|
||||
// modified by Oliver Kowalke and Nat Goodspeed
|
||||
//
|
||||
|
||||
#ifndef BOOST_FIBERS_ASIO_DETAIL_USE_FUTURE_HPP
|
||||
#define BOOST_FIBERS_ASIO_DETAIL_USE_FUTURE_HPP
|
||||
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#include "promise_handler.hpp"
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// use_future_handler is just an alias for promise_handler -- but we must
|
||||
// distinguish this case to specialize async_result below.
|
||||
template< typename T >
|
||||
using use_future_handler = promise_handler< T >;
|
||||
|
||||
}}}
|
||||
|
||||
namespace asio {
|
||||
|
||||
// Handler traits specialisation for use_future_handler.
|
||||
template< typename T >
|
||||
class async_result< fibers::asio::detail::use_future_handler< T > > {
|
||||
public:
|
||||
// The initiating function will return a future.
|
||||
typedef boost::fibers::future< T > type;
|
||||
|
||||
// Constructor creates a new promise for the async operation, and obtains the
|
||||
// corresponding future.
|
||||
explicit async_result( fibers::asio::detail::use_future_handler< T > & h) {
|
||||
value_ = h.get_promise()->get_future();
|
||||
}
|
||||
|
||||
// Obtain the future to be returned from the initiating function.
|
||||
type get() {
|
||||
return boost::move( value_);
|
||||
}
|
||||
|
||||
private:
|
||||
type value_;
|
||||
};
|
||||
|
||||
// Handler type specialisation for use_future for a nullary callback.
|
||||
template< typename Allocator, typename ReturnType >
|
||||
struct handler_type< boost::fibers::asio::use_future_t< Allocator >, ReturnType() > {
|
||||
typedef fibers::asio::detail::use_future_handler< void > type;
|
||||
};
|
||||
|
||||
// Handler type specialisation for use_future for a single-argument callback.
|
||||
template< typename Allocator, typename ReturnType, typename Arg1 >
|
||||
struct handler_type< boost::fibers::asio::use_future_t< Allocator >, ReturnType( Arg1) > {
|
||||
typedef fibers::asio::detail::use_future_handler< Arg1 > type;
|
||||
};
|
||||
|
||||
// Handler type specialisation for use_future for a callback passed only
|
||||
// boost::system::error_code. Note the use of use_future_handler<void>: an
|
||||
// error_code indicating error will be conveyed to consumer code via
|
||||
// set_exception().
|
||||
template< typename Allocator, typename ReturnType >
|
||||
struct handler_type< boost::fibers::asio::use_future_t< Allocator >, ReturnType( boost::system::error_code) > {
|
||||
typedef fibers::asio::detail::use_future_handler< void > type;
|
||||
};
|
||||
|
||||
// Handler type specialisation for use_future for a callback passed
|
||||
// boost::system::error_code plus an arbitrary value. Note the use of a
|
||||
// single-argument use_future_handler: an error_code indicating error will be
|
||||
// conveyed to consumer code via set_exception().
|
||||
template< typename Allocator, typename ReturnType, typename Arg2 >
|
||||
struct handler_type< boost::fibers::asio::use_future_t< Allocator >, ReturnType( boost::system::error_code, Arg2) > {
|
||||
typedef fibers::asio::detail::use_future_handler< Arg2 > type;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
#endif
|
||||
|
||||
#endif // BOOST_FIBERS_ASIO_DETAIL_USE_FUTURE_HPP
|
||||
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// yield.hpp
|
||||
// ~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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)
|
||||
//
|
||||
// modified by Oliver Kowalke and Nat Goodspeed
|
||||
//
|
||||
|
||||
#ifndef BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
|
||||
#define BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
|
||||
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/handler_type.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#include "promise_handler.hpp"
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
namespace asio {
|
||||
namespace detail {
|
||||
|
||||
// yield_handler is just an alias for promise_handler -- but we must
|
||||
// distinguish this case to specialize async_result below.
|
||||
//[fibers_asio_yield_handler
|
||||
template< typename T >
|
||||
using yield_handler = promise_handler< T >;
|
||||
//]
|
||||
|
||||
}}}
|
||||
|
||||
namespace asio {
|
||||
|
||||
// Handler traits specialisation for yield_handler.
|
||||
template< typename T >
|
||||
class async_result< fibers::asio::detail::yield_handler< T > > {
|
||||
public:
|
||||
// The initiating function will return a value of type T.
|
||||
typedef T type;
|
||||
|
||||
// Constructor creates a new promise for the async operation, and obtains the
|
||||
// corresponding future.
|
||||
explicit async_result( fibers::asio::detail::yield_handler< T > & h) {
|
||||
future_ = h.get_promise()->get_future();
|
||||
}
|
||||
|
||||
// This blocks the calling fiber until the handler sets either a value or
|
||||
// an exception.
|
||||
type get() {
|
||||
return future_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
fibers::future< T > future_;
|
||||
};
|
||||
|
||||
// Handler type specialisation for yield for a nullary callback.
|
||||
template< typename Allocator, typename ReturnType >
|
||||
struct handler_type< boost::fibers::asio::yield_t< Allocator >,
|
||||
ReturnType() > {
|
||||
typedef boost::fibers::asio::detail::yield_handler< void > type;
|
||||
};
|
||||
|
||||
// Handler type specialisation for yield for a single-argument callback.
|
||||
template< typename Allocator, typename ReturnType, typename Arg1 >
|
||||
struct handler_type< boost::fibers::asio::yield_t< Allocator >,
|
||||
ReturnType( Arg1) > {
|
||||
typedef fibers::asio::detail::yield_handler< Arg1 > type;
|
||||
};
|
||||
|
||||
// Handler type specialisation for yield for a callback passed only
|
||||
// boost::system::error_code. Note the use of yield_handler<void>: an
|
||||
// error_code indicating error will be conveyed to consumer code via an
|
||||
// exception. Normal return implies (! error_code).
|
||||
template< typename Allocator, typename ReturnType >
|
||||
struct handler_type< boost::fibers::asio::yield_t< Allocator >,
|
||||
ReturnType( boost::system::error_code) > {
|
||||
typedef fibers::asio::detail::yield_handler< void > type;
|
||||
};
|
||||
|
||||
// Handler type specialisation for yield for a callback passed
|
||||
// boost::system::error_code plus an arbitrary value. Note the use of a
|
||||
// single-argument yield_handler: an error_code indicating error will be
|
||||
// conveyed to consumer code via an exception. Normal return implies (!
|
||||
// error_code).
|
||||
//[asio_handler_type
|
||||
template< typename Allocator, typename ReturnType, typename Arg2 >
|
||||
struct handler_type< boost::fibers::asio::yield_t< Allocator >,
|
||||
ReturnType( boost::system::error_code, Arg2) > {
|
||||
typedef fibers::asio::detail::yield_handler< Arg2 > type;
|
||||
};
|
||||
//]
|
||||
|
||||
}}
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
#endif
|
||||
|
||||
#endif // BOOST_FIBERS_ASIO_DETAIL_YIELD_HPP
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// echo_client.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// This source is effectively identical to
|
||||
// http://www.boost.org/doc/libs/release/doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_client.cpp
|
||||
// It does not use Boost.Fiber. It is copied here only for completeness. A
|
||||
// server needs a client.
|
||||
//
|
||||
// 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: echo_client <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
tcp::resolver resolver(io_service);
|
||||
tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);
|
||||
tcp::resolver::iterator iterator = resolver.resolve(query);
|
||||
|
||||
tcp::socket s(io_service);
|
||||
boost::asio::connect(s, iterator);
|
||||
|
||||
using namespace std; // For strlen.
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t request_length = strlen(request);
|
||||
boost::asio::write(s, boost::asio::buffer(request, request_length));
|
||||
|
||||
char reply[max_length];
|
||||
size_t reply_length = boost::asio::read(s,
|
||||
boost::asio::buffer(reply, request_length));
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << "\n";
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// echo_client2.cpp
|
||||
// ~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// This source is almost identical to
|
||||
// http://www.boost.org/doc/libs/release/doc/html/boost_asio/example/cpp03/echo/blocking_tcp_echo_client.cpp
|
||||
// save that it deliberately introduces a timeout.
|
||||
// It does not use Boost.Fiber. It is copied here only for completeness. A
|
||||
// server needs a client.
|
||||
//
|
||||
// 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: echo_client2 <host> <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
tcp::resolver resolver(io_service);
|
||||
tcp::resolver::query query(tcp::v4(), argv[1], argv[2]);
|
||||
tcp::resolver::iterator iterator = resolver.resolve(query);
|
||||
|
||||
tcp::socket s(io_service);
|
||||
boost::asio::connect(s, iterator);
|
||||
|
||||
using namespace std; // For strlen.
|
||||
std::cout << "Enter message: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
size_t request_length = strlen(request);
|
||||
boost::asio::write(s, boost::asio::buffer(request, request_length));
|
||||
|
||||
char reply[max_length];
|
||||
size_t reply_length = boost::asio::read(s,
|
||||
boost::asio::buffer(reply, request_length));
|
||||
std::cout << "Reply is: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << "\nWe block for 10 seconds in order to let session timeout\n";
|
||||
boost::this_thread::sleep( boost::posix_time::seconds( 10) );
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// echo_server.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// 2013 Oliver Kowalke
|
||||
//
|
||||
// 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 <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#include "loop.hpp" // run_service()
|
||||
#include "yield.hpp"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
const int max_length = 1024;
|
||||
|
||||
typedef boost::shared_ptr< tcp::socket > socket_ptr;
|
||||
|
||||
void session( socket_ptr sock)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
char data[max_length];
|
||||
|
||||
boost::system::error_code ec;
|
||||
std::size_t length = sock->async_read_some(
|
||||
boost::asio::buffer( data),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ec == boost::asio::error::eof)
|
||||
break; //connection closed cleanly by peer
|
||||
else if ( ec)
|
||||
throw boost::system::system_error( ec); //some other error
|
||||
|
||||
boost::asio::async_write(
|
||||
* sock,
|
||||
boost::asio::buffer( data, length),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ec == boost::asio::error::eof)
|
||||
break; //connection closed cleanly by peer
|
||||
else if ( ec)
|
||||
throw boost::system::system_error( ec); //some other error
|
||||
}
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{ std::cerr << "Exception in fiber: " << e.what() << "\n"; }
|
||||
}
|
||||
|
||||
void server( boost::asio::io_service & io_service, unsigned short port)
|
||||
{
|
||||
tcp::acceptor a( io_service, tcp::endpoint( tcp::v4(), port) );
|
||||
for (;;)
|
||||
{
|
||||
socket_ptr socket( new tcp::socket( io_service) );
|
||||
boost::system::error_code ec;
|
||||
std::cout << "wait for accept" << std::endl;
|
||||
a.async_accept(
|
||||
* socket,
|
||||
boost::fibers::asio::yield[ec]);
|
||||
std::cout << "accepted" << std::endl;
|
||||
if ( ! ec) {
|
||||
boost::fibers::fiber(
|
||||
boost::bind( session, socket) ).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
boost::fibers::fiber(
|
||||
boost::bind( server, boost::ref( io_service), std::atoi( argv[1]) ) ).detach();
|
||||
|
||||
boost::fibers::asio::run_service( io_service);
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{ std::cerr << "Exception: " << e.what() << "\n"; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// echo_server2.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
// 2013 Oliver Kowalke
|
||||
//
|
||||
// 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 <cstdlib>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#include "loop.hpp"
|
||||
#include "yield.hpp"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
const int max_length = 1024;
|
||||
|
||||
class session : public boost::enable_shared_from_this< session >
|
||||
{
|
||||
public:
|
||||
explicit session( boost::asio::io_service & io_service) :
|
||||
strand_( io_service),
|
||||
socket_( io_service),
|
||||
timer_( io_service)
|
||||
{}
|
||||
|
||||
tcp::socket& socket()
|
||||
{ return socket_; }
|
||||
|
||||
void go()
|
||||
{
|
||||
boost::fibers::fiber(
|
||||
boost::bind(&session::echo,
|
||||
shared_from_this())).detach();
|
||||
boost::fibers::fiber(
|
||||
boost::bind(&session::timeout,
|
||||
shared_from_this())).detach();
|
||||
}
|
||||
|
||||
private:
|
||||
void echo()
|
||||
{
|
||||
try
|
||||
{
|
||||
char data[max_length];
|
||||
for (;;)
|
||||
{
|
||||
timer_.expires_from_now(
|
||||
boost::posix_time::seconds( 3) );
|
||||
std::size_t n = socket_.async_read_some(
|
||||
boost::asio::buffer( data),
|
||||
boost::fibers::asio::yield);
|
||||
boost::asio::async_write(
|
||||
socket_,
|
||||
boost::asio::buffer( data, n),
|
||||
boost::fibers::asio::yield);
|
||||
}
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{
|
||||
socket_.close();
|
||||
timer_.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void timeout()
|
||||
{
|
||||
while ( socket_.is_open() )
|
||||
{
|
||||
boost::system::error_code ignored_ec;
|
||||
timer_.async_wait( boost::fibers::asio::yield[ignored_ec]);
|
||||
if ( timer_.expires_from_now() <= boost::posix_time::seconds( 0) ) {
|
||||
std::cout << "session to " << socket_.remote_endpoint() << " timed out" << std::endl;
|
||||
socket_.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::io_service::strand strand_;
|
||||
tcp::socket socket_;
|
||||
boost::asio::deadline_timer timer_;
|
||||
};
|
||||
|
||||
void do_accept(boost::asio::io_service& io_service,
|
||||
unsigned short port)
|
||||
{
|
||||
tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), port) );
|
||||
|
||||
for (;;)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
boost::shared_ptr< session > new_session( new session( io_service) );
|
||||
acceptor.async_accept(
|
||||
new_session->socket(),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ! ec) {
|
||||
boost::fibers::fiber( boost::bind( & session::go, new_session) ).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if ( argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: echo_server <port>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
using namespace std; // For atoi.
|
||||
boost::fibers::fiber(
|
||||
boost::bind( do_accept,
|
||||
boost::ref( io_service), atoi( argv[1])) ).detach();
|
||||
|
||||
boost::fibers::asio::run_service( io_service);
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{ std::cerr << "Exception: " << e.what() << "\n"; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
|
||||
// Copyright Eugene Yakubovich 2014.
|
||||
// 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 <chrono>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/high_resolution_timer.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
namespace asio {
|
||||
|
||||
//[timer_handler
|
||||
inline void timer_handler( boost::asio::high_resolution_timer & timer) {
|
||||
boost::this_fiber::yield();
|
||||
timer.expires_from_now( boost::fibers::wait_interval() );
|
||||
timer.async_wait( std::bind( timer_handler, std::ref( timer) ) );
|
||||
}
|
||||
//]
|
||||
|
||||
//[run_service
|
||||
inline void run_service( boost::asio::io_service & io_service) {
|
||||
boost::asio::high_resolution_timer timer( io_service, std::chrono::seconds(0) );
|
||||
timer.async_wait( std::bind( timer_handler, std::ref( timer) ) );
|
||||
io_service.run();
|
||||
}
|
||||
//]
|
||||
|
||||
}}}
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
#endif
|
||||
@@ -0,0 +1,88 @@
|
||||
//
|
||||
// promise_completion_token.hpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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)
|
||||
//
|
||||
// modified by Oliver Kowalke and Nat Goodspeed
|
||||
//
|
||||
|
||||
#ifndef BOOST_FIBERS_ASIO_PROMISE_COMPLETION_TOKEN_HPP
|
||||
#define BOOST_FIBERS_ASIO_PROMISE_COMPLETION_TOKEN_HPP
|
||||
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
namespace asio {
|
||||
|
||||
/// Common base class for yield_t and use_future_t. See also yield.hpp and
|
||||
/// use_future.hpp.
|
||||
/**
|
||||
* The awkward name of this class is because it's not intended to be used
|
||||
* directly in user code: it's the common base class for a couple of user-
|
||||
* facing placeholder classes <tt>yield_t</tt> and <tt>use_future_t</tt>. They
|
||||
* share a common handler class <tt>promise_handler</tt>.
|
||||
*
|
||||
* Each subclass (e.g. <tt>use_future_t</tt>) has a canonical instance
|
||||
* (<tt>use_future</tt>). These may be used in the following ways as a
|
||||
* Boost.Asio asynchronous operation completion token:
|
||||
*
|
||||
* <dl>
|
||||
* <dt><tt>boost::fibers::asio::use_future</tt></dt>
|
||||
* <dd>This is the canonical instance of <tt>use_future_t</tt>, provided
|
||||
* solely for convenience. It causes <tt>promise_handler</tt> to allocate its
|
||||
* internal <tt>boost::fibers::promise</tt> using a default-constructed
|
||||
* default allocator (<tt>std::allocator<void></tt>).</dd>
|
||||
* <dt><tt>boost::fibers::asio::use_future::with(alloc_instance)</tt></dt>
|
||||
* <dd>This usage specifies an alternate allocator instance
|
||||
* <tt>alloc_instance</tt>. It causes <tt>promise_handler</tt> to allocate its
|
||||
* internal <tt>boost::fibers::promise</tt> using the specified
|
||||
* allocator.</dd>
|
||||
* </dl>
|
||||
*/
|
||||
//[fibers_asio_promise_completion_token
|
||||
template< typename Allocator >
|
||||
class promise_completion_token {
|
||||
public:
|
||||
typedef Allocator allocator_type;
|
||||
|
||||
/// Construct using default-constructed allocator.
|
||||
BOOST_CONSTEXPR promise_completion_token() :
|
||||
ec_( nullptr) {
|
||||
}
|
||||
|
||||
/// Construct using specified allocator.
|
||||
explicit promise_completion_token( Allocator const& allocator) :
|
||||
ec_( nullptr),
|
||||
allocator_( allocator) {
|
||||
}
|
||||
|
||||
/// Obtain allocator.
|
||||
allocator_type get_allocator() const {
|
||||
return allocator_;
|
||||
}
|
||||
|
||||
//private:
|
||||
// used by some subclasses to bind an error_code to suppress exceptions
|
||||
boost::system::error_code * ec_;
|
||||
|
||||
private:
|
||||
Allocator allocator_;
|
||||
};
|
||||
//]
|
||||
|
||||
}}}
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
#endif
|
||||
|
||||
#endif // BOOST_FIBERS_ASIO_PROMISE_COMPLETION_TOKEN_HPP
|
||||
@@ -0,0 +1,62 @@
|
||||
//
|
||||
// blocking_tcp_echo_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: publisher <host> <channel>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
tcp::resolver resolver(io_service);
|
||||
tcp::resolver::query query(tcp::v4(), argv[1], "9997");
|
||||
tcp::resolver::iterator iterator = resolver.resolve(query);
|
||||
|
||||
tcp::socket s(io_service);
|
||||
boost::asio::connect(s, iterator);
|
||||
|
||||
char msg[max_length];
|
||||
std::string channel(argv[2]);
|
||||
std::memset(msg, '\0', max_length);
|
||||
std::memcpy(msg, channel.c_str(), channel.size() );
|
||||
boost::asio::write(s, boost::asio::buffer(msg, max_length));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
std::cout << "publish: ";
|
||||
char request[max_length];
|
||||
std::cin.getline(request, max_length);
|
||||
boost::asio::write(s, boost::asio::buffer(request, max_length));
|
||||
}
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
//
|
||||
// server.cpp
|
||||
// ~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2013 Oliver Kowalke
|
||||
//
|
||||
// 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 <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
|
||||
#include "../loop.hpp"
|
||||
#include "../yield.hpp"
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
const std::size_t max_length = 1024;
|
||||
|
||||
class subscriber_session;
|
||||
typedef boost::shared_ptr<subscriber_session> subscriber_session_ptr;
|
||||
|
||||
// a channel has n subscribers (subscriptions)
|
||||
// this class holds a list of subcribers for one channel
|
||||
class subscriptions
|
||||
{
|
||||
public:
|
||||
~subscriptions();
|
||||
|
||||
// subscribe to this channel
|
||||
void subscribe( subscriber_session_ptr const& s)
|
||||
{ subscribers_.insert( s); }
|
||||
|
||||
// unsubscribe from this channel
|
||||
void unsubscribe( subscriber_session_ptr const& s)
|
||||
{ subscribers_.erase(s); }
|
||||
|
||||
// publish a message, e.g. push this message to all subscribers
|
||||
void publish( std::string const& msg);
|
||||
|
||||
private:
|
||||
// list of subscribers
|
||||
std::set<subscriber_session_ptr> subscribers_;
|
||||
};
|
||||
|
||||
// a class to register channels and to subsribe clients to this channels
|
||||
class registry : private boost::noncopyable
|
||||
{
|
||||
private:
|
||||
typedef std::map< std::string, boost::shared_ptr< subscriptions > > channels_cont;
|
||||
typedef channels_cont::iterator channels_iter;
|
||||
|
||||
boost::fibers::mutex mtx_;
|
||||
channels_cont channels_;
|
||||
|
||||
void register_channel_( std::string const& channel)
|
||||
{
|
||||
if ( channels_.end() != channels_.find( channel) )
|
||||
throw std::runtime_error("channel already exists");
|
||||
channels_[channel] = boost::make_shared< subscriptions >();
|
||||
std::cout << "new channel '" << channel << "' registered" << std::endl;
|
||||
}
|
||||
|
||||
void unregister_channel_( std::string const& channel)
|
||||
{
|
||||
channels_.erase( channel);
|
||||
std::cout << "channel '" << channel << "' unregistered" << std::endl;
|
||||
}
|
||||
|
||||
void subscribe_( std::string const& channel, subscriber_session_ptr s)
|
||||
{
|
||||
channels_iter iter = channels_.find( channel);
|
||||
if ( channels_.end() == iter )
|
||||
throw std::runtime_error("channel does not exist");
|
||||
iter->second->subscribe( s);
|
||||
std::cout << "new subscription to channel '" << channel << "'" << std::endl;
|
||||
}
|
||||
|
||||
void unsubscribe_( std::string const& channel, subscriber_session_ptr s)
|
||||
{
|
||||
channels_iter iter = channels_.find( channel);
|
||||
if ( channels_.end() != iter )
|
||||
iter->second->unsubscribe( s);
|
||||
}
|
||||
|
||||
void publish_( std::string const& channel, std::string const& msg)
|
||||
{
|
||||
channels_iter iter = channels_.find( channel);
|
||||
if ( channels_.end() == iter )
|
||||
throw std::runtime_error("channel does not exist");
|
||||
iter->second->publish( msg);
|
||||
std::cout << "message '" << msg << "' to publish on channel '" << channel << "'" << std::endl;
|
||||
}
|
||||
|
||||
public:
|
||||
// add a channel to registry
|
||||
void register_channel( std::string const& channel)
|
||||
{
|
||||
std::unique_lock< boost::fibers::mutex > lk( mtx_);
|
||||
register_channel_( channel);
|
||||
}
|
||||
|
||||
// remove a channel from registry
|
||||
void unregister_channel( std::string const& channel)
|
||||
{
|
||||
std::unique_lock< boost::fibers::mutex > lk( mtx_);
|
||||
unregister_channel_( channel);
|
||||
}
|
||||
|
||||
// subscribe to a channel
|
||||
void subscribe( std::string const& channel, subscriber_session_ptr s)
|
||||
{
|
||||
std::unique_lock< boost::fibers::mutex > lk( mtx_);
|
||||
subscribe_( channel, s);
|
||||
}
|
||||
|
||||
// unsubscribe from a channel
|
||||
void unsubscribe( std::string const& channel, subscriber_session_ptr s)
|
||||
{
|
||||
std::unique_lock< boost::fibers::mutex > lk( mtx_);
|
||||
unsubscribe_( channel, s);
|
||||
}
|
||||
|
||||
// publish a message to all subscribers registerd to the channel
|
||||
void publish( std::string const& channel, std::string const& msg)
|
||||
{
|
||||
std::unique_lock< boost::fibers::mutex > lk( mtx_);
|
||||
publish_( channel, msg);
|
||||
}
|
||||
};
|
||||
|
||||
// a subscriber subscribes to a given channel in order to receive messages published on this channel
|
||||
class subscriber_session : public boost::enable_shared_from_this< subscriber_session >
|
||||
{
|
||||
public:
|
||||
explicit subscriber_session( boost::asio::io_service & io_service, registry & reg) :
|
||||
socket_( io_service),
|
||||
reg_( reg)
|
||||
{}
|
||||
|
||||
tcp::socket& socket()
|
||||
{ return socket_; }
|
||||
|
||||
// this function is executed inside the fiber
|
||||
void run()
|
||||
{
|
||||
std::string channel;
|
||||
try
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
// read first message == channel name
|
||||
// async_ready() returns if the the complete message is read
|
||||
// until this the fiber is suspended until the complete message
|
||||
// is read int the given buffer 'data'
|
||||
boost::asio::async_read(
|
||||
socket_,
|
||||
boost::asio::buffer( data_),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ec) throw std::runtime_error("no channel from subscriber");
|
||||
// first message ist equal to the channel name the publisher
|
||||
// publishes to
|
||||
channel = data_;
|
||||
|
||||
// subscribe to new channel
|
||||
reg_.subscribe( channel, shared_from_this() );
|
||||
|
||||
// read published messages
|
||||
for (;;)
|
||||
{
|
||||
// wait for a conditon-variable for new messages
|
||||
// the fiber will be suspended until the condtion
|
||||
// becomes true and the fiber is resumed
|
||||
// published message is stored in buffer 'data_'
|
||||
std::unique_lock< boost::fibers::mutex > lk( mtx_);
|
||||
cond_.wait( lk);
|
||||
std::string data( data_);
|
||||
lk.unlock();
|
||||
std::cout << "subscriber::run(): '" << data << std::endl;
|
||||
|
||||
// message '<fini>' terminates subscription
|
||||
if ( "<fini>" == data) break;
|
||||
|
||||
// async. write message to socket connected with
|
||||
// subscriber
|
||||
// async_write() returns if the complete message was writen
|
||||
// the fiber is suspended in the meanwhile
|
||||
boost::asio::async_write(
|
||||
socket_,
|
||||
boost::asio::buffer( data, data.size() ),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ec == boost::asio::error::eof)
|
||||
break; //connection closed cleanly by peer
|
||||
else if ( ec)
|
||||
throw boost::system::system_error( ec); //some other error
|
||||
std::cout << "subscriber::run(): '" << data << " written" << std::endl;
|
||||
}
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{ std::cerr << "subscriber [" << channel << "] failed: " << e.what() << std::endl; }
|
||||
|
||||
// close socket
|
||||
socket_.close();
|
||||
// unregister channel
|
||||
reg_.unsubscribe( channel, shared_from_this() );
|
||||
}
|
||||
|
||||
// called from publisher_session (running in other fiber)
|
||||
void publish( std::string const& msg)
|
||||
{
|
||||
std::unique_lock< boost::fibers::mutex > lk( mtx_);
|
||||
std::memset(data_, '\0', sizeof( data_));
|
||||
std::memcpy(data_, msg.c_str(), (std::min)(max_length, msg.size()));
|
||||
cond_.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
tcp::socket socket_;
|
||||
registry & reg_;
|
||||
boost::fibers::mutex mtx_;
|
||||
boost::fibers::condition_variable cond_;
|
||||
// fixed size message
|
||||
char data_[max_length];
|
||||
};
|
||||
|
||||
|
||||
subscriptions::~subscriptions()
|
||||
{
|
||||
BOOST_FOREACH( subscriber_session_ptr s, subscribers_)
|
||||
{ s->publish("<fini>"); }
|
||||
}
|
||||
|
||||
void
|
||||
subscriptions::publish( std::string const& msg)
|
||||
{
|
||||
BOOST_FOREACH( subscriber_session_ptr s, subscribers_)
|
||||
{ s->publish( msg); }
|
||||
}
|
||||
|
||||
// a publisher publishes messages on its channel
|
||||
// subscriber might register to this channel to get the published messages
|
||||
class publisher_session : public boost::enable_shared_from_this< publisher_session >
|
||||
{
|
||||
public:
|
||||
explicit publisher_session( boost::asio::io_service & io_service, registry & reg) :
|
||||
socket_( io_service),
|
||||
reg_( reg)
|
||||
{}
|
||||
|
||||
tcp::socket& socket()
|
||||
{ return socket_; }
|
||||
|
||||
// this function is executed inside the fiber
|
||||
void run()
|
||||
{
|
||||
std::string channel;
|
||||
try
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
|
||||
// fixed size message
|
||||
char data[max_length];
|
||||
|
||||
// read first message == channel name
|
||||
// async_ready() returns if the the complete message is read
|
||||
// until this the fiber is suspended until the complete message
|
||||
// is read int the given buffer 'data'
|
||||
boost::asio::async_read(
|
||||
socket_,
|
||||
boost::asio::buffer( data),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ec) throw std::runtime_error("no channel from publisher");
|
||||
// first message ist equal to the channel name the publisher
|
||||
// publishes to
|
||||
channel = data;
|
||||
|
||||
// register the new channel
|
||||
reg_.register_channel( channel);
|
||||
|
||||
// start publishing messages
|
||||
for (;;)
|
||||
{
|
||||
// read message from publisher asyncronous
|
||||
// async_read() suspends this fiber until the complete emssage is read
|
||||
// and stored in the given buffer 'data'
|
||||
boost::asio::async_read(
|
||||
socket_,
|
||||
boost::asio::buffer( data),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ec == boost::asio::error::eof)
|
||||
break; //connection closed cleanly by peer
|
||||
else if ( ec)
|
||||
throw boost::system::system_error( ec); //some other error
|
||||
|
||||
// publish message to all subscribers
|
||||
reg_.publish( channel, std::string( data) );
|
||||
}
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{ std::cerr << "publisher [" << channel << "] failed: " << e.what() << std::endl; }
|
||||
|
||||
// close socket
|
||||
socket_.close();
|
||||
// unregister channel
|
||||
reg_.unregister_channel( channel);
|
||||
}
|
||||
|
||||
private:
|
||||
tcp::socket socket_;
|
||||
registry & reg_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr< publisher_session > publisher_session_ptr;
|
||||
|
||||
// function accepts connections requests from clients acting as a publisher
|
||||
void accept_publisher( boost::asio::io_service& io_service,
|
||||
unsigned short port,
|
||||
registry & reg)
|
||||
{
|
||||
// create TCP-acceptor
|
||||
tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), port) );
|
||||
|
||||
// loop for accepting connection requests
|
||||
for (;;)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
// create new publisher-session
|
||||
// this instance will be associated with one publisher
|
||||
publisher_session_ptr new_publisher_session =
|
||||
boost::make_shared<publisher_session>( boost::ref( io_service), boost::ref( reg) );
|
||||
// async. accept of new connection request
|
||||
// this function will suspend this execution context (fiber) until a
|
||||
// connection was established, after returning from this function a new client (publisher)
|
||||
// is connected
|
||||
acceptor.async_accept(
|
||||
new_publisher_session->socket(),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ! ec) {
|
||||
// run the new publisher in its own fiber (one fiber for one client)
|
||||
boost::fibers::fiber(
|
||||
boost::bind( & publisher_session::run, new_publisher_session) ).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// function accepts connections requests from clients acting as a subscriber
|
||||
void accept_subscriber( boost::asio::io_service& io_service,
|
||||
unsigned short port,
|
||||
registry & reg)
|
||||
{
|
||||
// create TCP-acceptor
|
||||
tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), port) );
|
||||
|
||||
// loop for accepting connection requests
|
||||
for (;;)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
// create new subscriber-session
|
||||
// this instance will be associated with one subscriber
|
||||
subscriber_session_ptr new_subscriber_session =
|
||||
boost::make_shared<subscriber_session>( boost::ref( io_service), boost::ref( reg) );
|
||||
// async. accept of new connection request
|
||||
// this function will suspend this execution context (fiber) until a
|
||||
// connection was established, after returning from this function a new client (subscriber)
|
||||
// is connected
|
||||
acceptor.async_accept(
|
||||
new_subscriber_session->socket(),
|
||||
boost::fibers::asio::yield[ec]);
|
||||
if ( ! ec) {
|
||||
// run the new subscriber in its own fiber (one fiber for one client)
|
||||
boost::fibers::fiber(
|
||||
boost::bind( & subscriber_session::run, new_subscriber_session) ).detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// create io_service for async. I/O
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
// registry for channels and its subscription
|
||||
registry reg;
|
||||
|
||||
// create an acceptor for publishers, run it as fiber
|
||||
boost::fibers::fiber(
|
||||
boost::bind( accept_publisher,
|
||||
boost::ref( io_service), 9997, boost::ref( reg)) ).detach();
|
||||
|
||||
// create an acceptor for subscribers, run it as fiber
|
||||
boost::fibers::fiber(
|
||||
boost::bind( accept_subscriber,
|
||||
boost::ref( io_service), 9998, boost::ref( reg)) ).detach();
|
||||
|
||||
boost::fibers::asio::run_service( io_service);
|
||||
}
|
||||
catch ( std::exception const& e)
|
||||
{ std::cerr << "Exception: " << e.what() << "\n"; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// blocking_tcp_echo_client.cpp
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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 <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
enum { max_length = 1024 };
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
std::cerr << "Usage: subscriber <host> <channel>\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
tcp::resolver resolver(io_service);
|
||||
tcp::resolver::query query(tcp::v4(), argv[1], "9998");
|
||||
tcp::resolver::iterator iterator = resolver.resolve(query);
|
||||
|
||||
tcp::socket s(io_service);
|
||||
boost::asio::connect(s, iterator);
|
||||
|
||||
char msg[max_length];
|
||||
std::string channel(argv[2]);
|
||||
std::memset(msg, '\0', max_length);
|
||||
std::memcpy(msg, channel.c_str(), channel.size() );
|
||||
boost::asio::write(s, boost::asio::buffer(msg, max_length));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char reply[max_length];
|
||||
size_t reply_length = s.read_some(
|
||||
boost::asio::buffer(reply, max_length));
|
||||
std::cout << "published: ";
|
||||
std::cout.write(reply, reply_length);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
std::cerr << "Exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// use_future.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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)
|
||||
//
|
||||
// modified by Oliver Kowalke and Nat Goodspeed
|
||||
//
|
||||
|
||||
#ifndef BOOST_FIBERS_ASIO_USE_FUTURE_HPP
|
||||
#define BOOST_FIBERS_ASIO_USE_FUTURE_HPP
|
||||
|
||||
#include <memory> // std::allocator
|
||||
#include <boost/config.hpp>
|
||||
#include "promise_completion_token.hpp"
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
namespace asio {
|
||||
|
||||
/// Class used to specify that a Boost.Asio asynchronous operation should
|
||||
/// return a future.
|
||||
/**
|
||||
* The use_future_t class is used to indicate that a Boost.Asio asynchronous
|
||||
* operation should return a boost::fibers::future object. A use_future_t
|
||||
* object may be passed as a handler to an asynchronous operation, typically
|
||||
* using the special value @c boost::fibers::asio::use_future. For example:
|
||||
*
|
||||
* @code boost::fibers::future<std::size_t> my_future
|
||||
* = my_socket.async_read_some(my_buffer, boost::fibers::asio::use_future); @endcode
|
||||
*
|
||||
* The initiating function (async_read_some in the above example) returns a
|
||||
* future that will receive the result of the operation. If the operation
|
||||
* completes with an error_code indicating failure, it is converted into a
|
||||
* system_error and passed back to the caller via the future.
|
||||
*/
|
||||
template< typename Allocator = std::allocator< void > >
|
||||
class use_future_t : public promise_completion_token< Allocator > {
|
||||
public:
|
||||
/// Construct using default-constructed allocator.
|
||||
BOOST_CONSTEXPR use_future_t() {
|
||||
}
|
||||
|
||||
/// Construct using specified allocator.
|
||||
explicit use_future_t( Allocator const& allocator) :
|
||||
promise_completion_token<Allocator>( allocator) {
|
||||
}
|
||||
|
||||
/// Specify an alternate allocator.
|
||||
template< typename OtherAllocator >
|
||||
use_future_t< OtherAllocator > operator[]( OtherAllocator const& allocator) const {
|
||||
return use_future_t< OtherAllocator >( allocator);
|
||||
}
|
||||
};
|
||||
|
||||
/// A special value, similar to std::nothrow.
|
||||
BOOST_CONSTEXPR_OR_CONST use_future_t<> use_future;
|
||||
|
||||
}}} // namespace asio
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
#endif
|
||||
|
||||
#include "detail/use_future.hpp"
|
||||
|
||||
#endif // BOOST_FIBERS_ASIO_USE_FUTURE_HPP
|
||||
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// yield.hpp
|
||||
// ~~~~~~~~~~~~~~
|
||||
//
|
||||
// Copyright (c) 2003-2013 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)
|
||||
//
|
||||
// modified by Oliver Kowalke and Nat Goodspeed
|
||||
//
|
||||
|
||||
#ifndef BOOST_FIBERS_ASIO_YIELD_HPP
|
||||
#define BOOST_FIBERS_ASIO_YIELD_HPP
|
||||
|
||||
#include <memory> // std::allocator
|
||||
#include <boost/config.hpp>
|
||||
#include "promise_completion_token.hpp"
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_PREFIX
|
||||
#endif
|
||||
|
||||
namespace boost {
|
||||
namespace fibers {
|
||||
namespace asio {
|
||||
|
||||
/// Class used to specify that a Boost.Asio asynchronous operation should
|
||||
/// suspend the calling fiber until completion.
|
||||
/**
|
||||
* The yield_t class is used to indicate that a Boost.Asio asynchronous
|
||||
* operation should suspend the calling fiber until its completion. The
|
||||
* asynchronous function will either return a suitable value, or will throw an
|
||||
* exception indicating the error. A yield_t object may be passed as a handler
|
||||
* to an asynchronous operation, typically using the special value @c
|
||||
* boost::fibers::asio::yield. For example:
|
||||
*
|
||||
* @code std::size_t length_read
|
||||
* = my_socket.async_read_some(my_buffer, boost::fibers::asio::yield); @endcode
|
||||
*
|
||||
* The initiating function (async_read_some in the above example) does not
|
||||
* return to the calling fiber until the asynchronous read has completed. Like
|
||||
* its synchronous counterpart, it returns the result of the operation. If the
|
||||
* operation completes with an error_code indicating failure, it is converted
|
||||
* into a system_error and thrown as an exception.
|
||||
*
|
||||
* To suppress a possible error exception:
|
||||
* @code
|
||||
* boost::system::error_code ec;
|
||||
* std::size_t length_read =
|
||||
* my_socket.async_read_some(my_buffer, boost::fibers::asio::yield[ec]);
|
||||
* // test ec for success
|
||||
* @endcode
|
||||
*
|
||||
* The crucial distinction between
|
||||
* @code
|
||||
* std::size_t length_read = my_socket.read_some(my_buffer);
|
||||
* @endcode
|
||||
* and
|
||||
* @code
|
||||
* std::size_t length_read =
|
||||
* my_socket.async_read_some(my_buffer, boost::fibers::asio::yield);
|
||||
* @code
|
||||
* is that <tt>read_some()</tt> blocks the entire calling @em thread, whereas
|
||||
* <tt>async_read_some(..., boost::fibers::asio::yield)</tt> blocks only the
|
||||
* calling @em fiber, permitting other fibers on the same thread to continue
|
||||
* running.
|
||||
*
|
||||
* To specify an alternate allocator for the internal
|
||||
* <tt>boost::fibers::promise</tt>:
|
||||
* @code
|
||||
* boost::fibers::asio::yield.with(alloc_instance)
|
||||
* @endcode
|
||||
*
|
||||
* To bind a <tt>boost::system::error_code</tt> @a ec as well as using an
|
||||
* alternate allocator:
|
||||
* @code
|
||||
* boost::fibers::asio::yield.with(alloc_instance)[ec]
|
||||
* @endcode
|
||||
*/
|
||||
//[fibers_asio_yield_t
|
||||
template< typename Allocator = std::allocator< void > >
|
||||
class yield_t : public promise_completion_token< Allocator > {
|
||||
public:
|
||||
/// Construct with default-constructed allocator.
|
||||
BOOST_CONSTEXPR yield_t() {
|
||||
}
|
||||
/*= // ... ways to use an alternate allocator or bind an error_code ...*/
|
||||
/*=};*/
|
||||
//]
|
||||
|
||||
/// Construct using specified allocator.
|
||||
explicit yield_t( Allocator const& allocator) :
|
||||
promise_completion_token< Allocator >( allocator) {
|
||||
}
|
||||
|
||||
/// Specify an alternate allocator.
|
||||
template< typename OtherAllocator >
|
||||
yield_t< OtherAllocator > with( OtherAllocator const& allocator) const {
|
||||
return yield_t< OtherAllocator >( allocator);
|
||||
}
|
||||
|
||||
/// Bind an error_code to suppress error exception.
|
||||
yield_t operator[]( boost::system::error_code & ec) const {
|
||||
// Return a copy because typical usage will be on our canonical
|
||||
// instance. Don't leave the canonical instance with a dangling
|
||||
// binding to a transient error_code!
|
||||
yield_t tmp;
|
||||
tmp.ec_ = & ec;
|
||||
return tmp;
|
||||
}
|
||||
};
|
||||
|
||||
//[fibers_asio_yield
|
||||
/// A special value, similar to std::nothrow.
|
||||
BOOST_CONSTEXPR_OR_CONST yield_t<> yield;
|
||||
//]
|
||||
|
||||
}}}
|
||||
|
||||
#ifdef BOOST_HAS_ABI_HEADERS
|
||||
# include BOOST_ABI_SUFFIX
|
||||
#endif
|
||||
|
||||
#include "detail/yield.hpp"
|
||||
|
||||
#endif // BOOST_FIBERS_ASIO_YIELD_HPP
|
||||
@@ -0,0 +1,337 @@
|
||||
// Copyright Nat Goodspeed 2014.
|
||||
// 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 <iostream>
|
||||
|
||||
#include <boost/fiber/all.hpp>
|
||||
#include <boost/fiber/scheduler.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
class Verbose: public boost::noncopyable {
|
||||
public:
|
||||
Verbose( std::string const& d, std::string const& s="stop") :
|
||||
desc( d),
|
||||
stop( s) {
|
||||
std::cout << desc << " start" << std::endl;
|
||||
}
|
||||
|
||||
~Verbose() {
|
||||
std::cout << desc << ' ' << stop << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string desc;
|
||||
std::string stop;
|
||||
};
|
||||
|
||||
//[priority_props
|
||||
class priority_props : public boost::fibers::fiber_properties {
|
||||
public:
|
||||
priority_props( boost::fibers::context * ctx):
|
||||
fiber_properties( ctx), /*< Your subclass constructor must accept a
|
||||
[^[class_link context]*] and pass it to
|
||||
the `fiber_properties` constructor. >*/
|
||||
priority_( 0) {
|
||||
}
|
||||
|
||||
int get_priority() const {
|
||||
return priority_; /*< Provide read access methods at your own discretion. >*/
|
||||
}
|
||||
|
||||
// Call this method to alter priority, because we must notify
|
||||
// priority_scheduler of any change.
|
||||
void set_priority( int p) {
|
||||
/*< It's important to call notify() on any
|
||||
change in a property that can affect the
|
||||
scheduler's behavior. Therefore, such
|
||||
modifications should only be performed
|
||||
through an access method. >*/
|
||||
// Of course, it's only worth reshuffling the queue and all if we're
|
||||
// actually changing the priority.
|
||||
if ( p != priority_) {
|
||||
priority_ = p;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
// The fiber name of course is solely for purposes of this example
|
||||
// program; it has nothing to do with implementing scheduler priority.
|
||||
// This is a public data member -- not requiring set/get access methods --
|
||||
// because we need not inform the scheduler of any change.
|
||||
std::string name; /*< A property that does not affect the scheduler does
|
||||
not need access methods. >*/
|
||||
private:
|
||||
int priority_;
|
||||
};
|
||||
//]
|
||||
|
||||
//[priority_scheduler
|
||||
class priority_scheduler : public boost::fibers::sched_algorithm_with_properties< priority_props > {
|
||||
private:
|
||||
typedef boost::fibers::scheduler::ready_queue_t rqueue_t;
|
||||
|
||||
rqueue_t rqueue_;
|
||||
|
||||
public:
|
||||
priority_scheduler() :
|
||||
rqueue_() {
|
||||
}
|
||||
|
||||
// For a subclass of sched_algorithm_with_properties<>, it's important to
|
||||
// override the correct awakened() overload.
|
||||
/*<< You must override the [member_link sched_algorithm_with_properties..awakened]
|
||||
method. This is how your scheduler receives notification of a
|
||||
fiber that has become ready to run. >>*/
|
||||
virtual void awakened( boost::fibers::context * ctx, priority_props & props) {
|
||||
int f_priority = props.get_priority(); /*< `props` is the instance of
|
||||
priority_props associated
|
||||
with the passed fiber `f`. >*/
|
||||
// With this scheduler, fibers with higher priority values are
|
||||
// preferred over fibers with lower priority values. But fibers with
|
||||
// equal priority values are processed in round-robin fashion. So when
|
||||
// we're handed a new context*, put it at the end of the fibers
|
||||
// with that same priority. In other words: search for the first fiber
|
||||
// in the queue with LOWER priority, and insert before that one.
|
||||
rqueue_t::iterator i( rqueue_.begin() ), e( rqueue_.end() );
|
||||
for ( ; i != e; ++i) {
|
||||
if ( properties( & ( * i) ).get_priority() < f_priority) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Now, whether or not we found a fiber with lower priority,
|
||||
// insert this new fiber here.
|
||||
rqueue_.insert( i, * ctx);
|
||||
//<-
|
||||
|
||||
std::cout << "awakened(" << props.name << "): ";
|
||||
describe_ready_queue();
|
||||
//->
|
||||
}
|
||||
|
||||
/*<< You must override the [member_link sched_algorithm_with_properties..pick_next]
|
||||
method. This is how your scheduler actually advises the fiber manager
|
||||
of the next fiber to run. >>*/
|
||||
virtual boost::fibers::context * pick_next() {
|
||||
// if ready queue is empty, just tell caller
|
||||
if ( rqueue_.empty() ) {
|
||||
return nullptr;
|
||||
}
|
||||
boost::fibers::context * ctx( & rqueue_.front() );
|
||||
rqueue_.pop_front();
|
||||
//<-
|
||||
std::cout << "pick_next() resuming " << properties( ctx).name << ": ";
|
||||
describe_ready_queue();
|
||||
//->
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*<< You must override [member_link sched_algorithm_with_properties..ready_fibers]
|
||||
to inform the fiber manager of the size of your ready queue. >>*/
|
||||
virtual bool has_ready_fibers() const noexcept {
|
||||
return ! rqueue_.empty();
|
||||
}
|
||||
|
||||
/*<< Overriding [member_link sched_algorithm_with_properties..property_change]
|
||||
is optional. This override handles the case in which the running
|
||||
fiber changes the priority of another ready fiber: a fiber already in
|
||||
our queue. In that case, move the updated fiber within the queue. >>*/
|
||||
virtual void property_change( boost::fibers::context * ctx, priority_props & props) {
|
||||
// Although our priority_props class defines multiple properties, only
|
||||
// one of them (priority) actually calls notify() when changed. The
|
||||
// point of a property_change() override is to reshuffle the ready
|
||||
// queue according to the updated priority value.
|
||||
//<-
|
||||
std::cout << "property_change(" << props.name << '(' << props.get_priority()
|
||||
<< ")): ";
|
||||
//->
|
||||
|
||||
// Find 'f' in the queue. Note that it might not be in our queue at
|
||||
// all, if caller is changing the priority of (say) the running fiber.
|
||||
bool found = false;
|
||||
rqueue_t::iterator e( rqueue_.end() );
|
||||
for ( rqueue_t::iterator i( rqueue_.begin() ); i != e; ++i) {
|
||||
if ( & ( * i) == ctx) {
|
||||
// found the passed fiber in our list -- unlink it
|
||||
found = true;
|
||||
rqueue_.erase( i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// It's possible to get a property_change() call for a fiber that is
|
||||
// not on our ready queue. If it's not there, no need to move it:
|
||||
// we'll handle it next time it hits awakened().
|
||||
if ( ! found) {
|
||||
/*< Your `property_change()` override must be able to
|
||||
handle the case in which the passed `f` is not in
|
||||
your ready queue. It might be running, or it might be
|
||||
blocked. >*/
|
||||
//<-
|
||||
// hopefully user will distinguish this case by noticing that
|
||||
// the fiber with which we were called does not appear in the
|
||||
// ready queue at all
|
||||
describe_ready_queue();
|
||||
//->
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we know that f was in our ready queue, but we've unlinked it.
|
||||
// We happen to have a method that will (re-)add a context* to
|
||||
// the ready queue.
|
||||
awakened( ctx, props);
|
||||
}
|
||||
//<-
|
||||
|
||||
void describe_ready_queue() {
|
||||
if ( rqueue_.empty() ) {
|
||||
std::cout << "[empty]";
|
||||
} else {
|
||||
const char * delim = "";
|
||||
for ( boost::fibers::context & f : rqueue_) {
|
||||
priority_props & props( properties( & f) );
|
||||
std::cout << delim << props.name << '(' << props.get_priority() << ')';
|
||||
delim = ", ";
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
//->
|
||||
};
|
||||
//]
|
||||
|
||||
//[launch
|
||||
template< typename Fn >
|
||||
boost::fibers::fiber launch( Fn && func, std::string const& name, int priority) {
|
||||
boost::fibers::fiber fiber( func);
|
||||
priority_props & props( fiber.properties< priority_props >() );
|
||||
props.name = name;
|
||||
props.set_priority( priority);
|
||||
return fiber;
|
||||
}
|
||||
//]
|
||||
|
||||
void yield_fn() {
|
||||
std::string name( boost::this_fiber::properties< priority_props >().name);
|
||||
Verbose v( std::string("fiber ") + name);
|
||||
for ( int i = 0; i < 3; ++i) {
|
||||
std::cout << "fiber " << name << " yielding" << std::endl;
|
||||
boost::this_fiber::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void barrier_fn( boost::fibers::barrier & barrier) {
|
||||
std::string name( boost::this_fiber::properties< priority_props >().name);
|
||||
Verbose v( std::string("fiber ") + name);
|
||||
std::cout << "fiber " << name << " waiting on barrier" << std::endl;
|
||||
barrier.wait();
|
||||
std::cout << "fiber " << name << " yielding" << std::endl;
|
||||
boost::this_fiber::yield();
|
||||
}
|
||||
|
||||
//[change_fn
|
||||
void change_fn( boost::fibers::fiber & other,
|
||||
int other_priority,
|
||||
boost::fibers::barrier& barrier) {
|
||||
std::string name( boost::this_fiber::properties< priority_props >().name);
|
||||
Verbose v( std::string("fiber ") + name);
|
||||
|
||||
//<-
|
||||
std::cout << "fiber " << name << " waiting on barrier" << std::endl;
|
||||
//->
|
||||
barrier.wait();
|
||||
// We assume a couple things about 'other':
|
||||
// - that it was also waiting on the same barrier
|
||||
// - that it has lower priority than this fiber.
|
||||
// If both are true, 'other' is now ready to run but is sitting in
|
||||
// priority_scheduler's ready queue. Change its priority.
|
||||
priority_props & other_props(
|
||||
other.properties< priority_props >() );
|
||||
//<-
|
||||
std::cout << "fiber " << name << " changing priority of " << other_props.name
|
||||
<< " to " << other_priority << std::endl;
|
||||
//->
|
||||
other_props.set_priority( other_priority);
|
||||
}
|
||||
//]
|
||||
|
||||
//[main
|
||||
int main( int argc, char *argv[]) {
|
||||
// make sure we use our priority_scheduler rather than default round_robin
|
||||
boost::fibers::use_scheduling_algorithm< priority_scheduler >();
|
||||
/*= ...*/
|
||||
/*=}*/
|
||||
//]
|
||||
Verbose v("main()");
|
||||
|
||||
// for clarity
|
||||
std::cout << "main() setting name" << std::endl;
|
||||
//[main_name
|
||||
boost::this_fiber::properties< priority_props >().name = "main";
|
||||
//]
|
||||
std::cout << "main() running tests" << std::endl;
|
||||
|
||||
{
|
||||
Verbose v("high-priority first", "stop\n");
|
||||
// verify that high-priority fiber always gets scheduled first
|
||||
boost::fibers::fiber low( launch( yield_fn, "low", 1) );
|
||||
boost::fibers::fiber med( launch( yield_fn, "medium", 2) );
|
||||
boost::fibers::fiber hi( launch( yield_fn, "high", 3) );
|
||||
std::cout << "main: high.join()" << std::endl;
|
||||
hi.join();
|
||||
std::cout << "main: medium.join()" << std::endl;
|
||||
med.join();
|
||||
std::cout << "main: low.join()" << std::endl;
|
||||
low.join();
|
||||
}
|
||||
|
||||
{
|
||||
Verbose v("same priority round-robin", "stop\n");
|
||||
// fibers of same priority are scheduled in round-robin order
|
||||
boost::fibers::fiber a( launch( yield_fn, "a", 0) );
|
||||
boost::fibers::fiber b( launch( yield_fn, "b", 0) );
|
||||
boost::fibers::fiber c( launch( yield_fn, "c", 0) );
|
||||
std::cout << "main: a.join()" << std::endl;
|
||||
a.join();
|
||||
std::cout << "main: b.join()" << std::endl;
|
||||
b.join();
|
||||
std::cout << "main: c.join()" << std::endl;
|
||||
c.join();
|
||||
}
|
||||
|
||||
{
|
||||
Verbose v("barrier wakes up all", "stop\n");
|
||||
// using a barrier wakes up all waiting fibers at the same time
|
||||
boost::fibers::barrier barrier( 3);
|
||||
boost::fibers::fiber low( launch( [&barrier](){ barrier_fn( barrier); }, "low", 1) );
|
||||
boost::fibers::fiber med( launch( [&barrier](){ barrier_fn( barrier); }, "medium", 2) );
|
||||
boost::fibers::fiber hi( launch( [&barrier](){ barrier_fn( barrier); }, "high", 3) );
|
||||
std::cout << "main: low.join()" << std::endl;
|
||||
low.join();
|
||||
std::cout << "main: medium.join()" << std::endl;
|
||||
med.join();
|
||||
std::cout << "main: high.join()" << std::endl;
|
||||
hi.join();
|
||||
}
|
||||
|
||||
{
|
||||
Verbose v("change priority", "stop\n");
|
||||
// change priority of a fiber in priority_scheduler's ready queue
|
||||
boost::fibers::barrier barrier( 3);
|
||||
boost::fibers::fiber c( launch( [&barrier](){ barrier_fn( barrier); }, "c", 1) );
|
||||
boost::fibers::fiber a( launch( [&c,&barrier]() { change_fn( c, 3, barrier); }, "a", 3) );
|
||||
boost::fibers::fiber b( launch( [&barrier](){ barrier_fn( barrier); }, "b", 2) );
|
||||
std::cout << "main: a.join()" << std::endl;
|
||||
std::cout << "main: a.join()" << std::endl;
|
||||
a.join();
|
||||
std::cout << "main: b.join()" << std::endl;
|
||||
b.join();
|
||||
std::cout << "main: c.join()" << std::endl;
|
||||
c.join();
|
||||
}
|
||||
|
||||
std::cout << "done." << std::endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,178 @@
|
||||
// Copyright Nat Goodspeed 2015.
|
||||
// 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 <boost/fiber/all.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
|
||||
/*****************************************************************************
|
||||
* shared_ready_queue scheduler
|
||||
*****************************************************************************/
|
||||
// This simple scheduler is like round_robin, except that it shares a common
|
||||
// ready queue among all participating threads. A thread participates in this
|
||||
// pool by executing use_scheduling_algorithm<shared_ready_queue>() before any
|
||||
// other Boost.Fiber operation.
|
||||
class shared_ready_queue : public boost::fibers::sched_algorithm {
|
||||
private:
|
||||
typedef std::unique_lock< std::mutex > lock_t;
|
||||
typedef std::queue< boost::fibers::context * > rqueue_t;
|
||||
|
||||
// The important point about this ready queue is that it's a class static,
|
||||
// common to all instances of shared_ready_queue.
|
||||
static rqueue_t rqueue_;
|
||||
|
||||
// so is this mutex
|
||||
static std::mutex mtx_;
|
||||
|
||||
// Reserve a separate, scheduler-specific slot for this thread's main
|
||||
// fiber. When we're passed the main fiber, stash it there instead of in
|
||||
// the shared queue: it would be Bad News for thread B to retrieve and
|
||||
// attempt to execute thread A's main fiber. This slot might be empty
|
||||
// (nullptr) or full: pick_next() must only return the main fiber's
|
||||
// context* after it has been passed to awakened().
|
||||
boost::fibers::context * main_ctx_;
|
||||
boost::fibers::context * dispatcher_ctx_;
|
||||
|
||||
public:
|
||||
shared_ready_queue() :
|
||||
main_ctx_( nullptr),
|
||||
dispatcher_ctx_( nullptr) {
|
||||
}
|
||||
|
||||
virtual void awakened( boost::fibers::context * ctx) {
|
||||
BOOST_ASSERT( nullptr != ctx);
|
||||
|
||||
// recognize when we're passed this thread's main fiber
|
||||
if ( ctx->is_main_context() ) {
|
||||
// never put this thread's main fiber on the queue
|
||||
// stash it in separate slot
|
||||
main_ctx_ = ctx;
|
||||
// recognize when we're passed this thread's dispatcher fiber
|
||||
} else if ( ctx->is_dispatcher_context() ) {
|
||||
// never put this thread's main fiber on the queue
|
||||
// stash it in separate slot
|
||||
dispatcher_ctx_ = ctx;
|
||||
} else {
|
||||
ctx->worker_unlink();
|
||||
ctx->set_scheduler( nullptr);
|
||||
// ordinary fiber, enqueue on shared queue
|
||||
lock_t lock( mtx_);
|
||||
rqueue_.push( ctx);
|
||||
}
|
||||
}
|
||||
|
||||
virtual boost::fibers::context * pick_next() {
|
||||
lock_t lock( mtx_);
|
||||
boost::fibers::context * victim( nullptr);
|
||||
if ( ! rqueue_.empty() ) {
|
||||
// good, we have an item in the ready queue, pop it
|
||||
victim = rqueue_.front();
|
||||
rqueue_.pop();
|
||||
BOOST_ASSERT( nullptr != victim);
|
||||
} else if ( nullptr != main_ctx_) {
|
||||
// nothing in the ready queue, return main_ctx_
|
||||
victim = main_ctx_;
|
||||
// once we've returned main_ctx_, clear the slot
|
||||
main_ctx_ = nullptr;
|
||||
} else if ( nullptr != dispatcher_ctx_) {
|
||||
// nothing in the ready queue, return dispatcher_ctx_
|
||||
victim = dispatcher_ctx_;
|
||||
// once we've returned dispatcher_ctx_, clear the slot
|
||||
dispatcher_ctx_ = nullptr;
|
||||
}
|
||||
return victim;
|
||||
}
|
||||
|
||||
virtual bool has_ready_fibers() const noexcept {
|
||||
lock_t lock( mtx_);
|
||||
return ! rqueue_.empty() || nullptr != main_ctx_;
|
||||
}
|
||||
};
|
||||
|
||||
shared_ready_queue::rqueue_t shared_ready_queue::rqueue_;
|
||||
std::mutex shared_ready_queue::mtx_;
|
||||
|
||||
/*****************************************************************************
|
||||
* example fiber function
|
||||
*****************************************************************************/
|
||||
void whatevah( char me) {
|
||||
std::thread::id my_thread = std::this_thread::get_id();
|
||||
{
|
||||
std::ostringstream buffer;
|
||||
//buffer << "fiber " << me << " started on thread " << my_thread << '\n';
|
||||
std::cout << buffer.str() << std::flush;
|
||||
}
|
||||
for ( unsigned i = 0; i < 10; ++i) {
|
||||
boost::this_fiber::yield();
|
||||
std::thread::id new_thread = std::this_thread::get_id();
|
||||
if ( new_thread != my_thread) {
|
||||
my_thread = new_thread;
|
||||
std::ostringstream buffer;
|
||||
//buffer << "fiber " << me << " switched to thread " << my_thread << '\n';
|
||||
std::cout << buffer.str() << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* example thread function
|
||||
*****************************************************************************/
|
||||
// Wait until all running fibers have completed. This works because we happen
|
||||
// to know that all example fibers use yield(), which leaves them in ready
|
||||
// state. A fiber blocked on a synchronization object is invisible to
|
||||
// ready_fibers().
|
||||
void drain() {
|
||||
// THIS fiber is running, so won't be counted among "ready" fibers
|
||||
while ( boost::fibers::has_ready_fibers() ) {
|
||||
boost::this_fiber::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void thread() {
|
||||
boost::fibers::use_scheduling_algorithm< shared_ready_queue >();
|
||||
drain();
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* main()
|
||||
*****************************************************************************/
|
||||
int main( int argc, char *argv[]) {
|
||||
// use shared_ready_queue for main thread too, so we launch new fibers
|
||||
// into shared pool
|
||||
boost::fibers::use_scheduling_algorithm< shared_ready_queue >();
|
||||
|
||||
for ( int i = 0; i < 10; ++i) {
|
||||
// launch a number of fibers
|
||||
for ( char c : std::string("abcdefghijklmnopqrstuvwxyz")) {
|
||||
boost::fibers::fiber([c](){ whatevah( c); }).detach();
|
||||
}
|
||||
|
||||
// launch a couple threads to help process them
|
||||
std::thread threads[] = {
|
||||
std::thread( thread),
|
||||
std::thread( thread),
|
||||
std::thread( thread),
|
||||
std::thread( thread),
|
||||
std::thread( thread)
|
||||
};
|
||||
|
||||
// drain running fibers
|
||||
drain();
|
||||
|
||||
// wait for threads to terminate
|
||||
for ( std::thread & t : threads) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user