blob: eb83e5cc91a2d06f79e1b869fb2d522328799e90 [file] [log] [blame]
//
// impl/executor.hpp
// ~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2016 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)
//
#ifndef ASIO_IMPL_EXECUTOR_HPP
#define ASIO_IMPL_EXECUTOR_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include "asio/detail/atomic_count.hpp"
#include "asio/detail/executor_op.hpp"
#include "asio/detail/global.hpp"
#include "asio/detail/memory.hpp"
#include "asio/detail/recycling_allocator.hpp"
#include "asio/executor.hpp"
#include "asio/system_executor.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
#if !defined(GENERATING_DOCUMENTATION)
#if defined(ASIO_HAS_MOVE)
// Lightweight, move-only function object wrapper.
class executor::function
{
public:
template <typename F, typename Alloc>
explicit function(F f, const Alloc& a)
{
// Construct an allocator to be used for the operation.
typedef typename detail::get_recycling_allocator<Alloc>::type alloc_type;
alloc_type allocator(detail::get_recycling_allocator<Alloc>::get(a));
// Allocate and construct an operation to wrap the function.
typedef detail::executor_op<F, alloc_type> op;
typename op::ptr p = { allocator, 0, 0 };
p.v = p.a.allocate(1);
op_ = new (p.v) op(f, allocator);
p.v = 0;
}
function(function&& other)
: op_(other.op_)
{
other.op_ = 0;
}
~function()
{
if (op_)
op_->destroy();
}
void operator()()
{
if (op_)
{
detail::scheduler_operation* op = op_;
op_ = 0;
op->complete(this, asio::error_code(), 0);
}
}
private:
detail::scheduler_operation* op_;
};
#else // defined(ASIO_HAS_MOVE)
// Not so lightweight, copyable function object wrapper.
class executor::function
{
public:
template <typename F, typename Alloc>
explicit function(const F& f, const Alloc&)
: impl_(new impl<F>(f))
{
}
void operator()()
{
impl_->invoke_(impl_.get());
}
private:
// Base class for polymorphic function implementations.
struct impl_base
{
void (*invoke_)(impl_base*);
};
// Polymorphic function implementation.
template <typename F>
struct impl : impl_base
{
impl(const F& f)
: function_(f)
{
invoke_ = &function::invoke<F>;
}
F function_;
};
// Helper to invoke a function.
template <typename F>
static void invoke(impl_base* i)
{
static_cast<impl<F>*>(i)->function_();
}
detail::shared_ptr<impl_base> impl_;
};
#endif // defined(ASIO_HAS_MOVE)
// Default polymorphic allocator implementation.
template <typename Executor, typename Allocator>
class executor::impl
: public executor::impl_base
{
public:
typedef typename Allocator::template rebind<impl>::other allocator_type;
static impl_base* create(const Executor& e, Allocator a = Allocator())
{
raw_mem mem(a);
impl* p = new (mem.ptr_) impl(e, a);
mem.ptr_ = 0;
return p;
}
impl(const Executor& e, const Allocator& a) ASIO_NOEXCEPT
: impl_base(false),
ref_count_(1),
executor_(e),
allocator_(a)
{
}
impl_base* clone() const ASIO_NOEXCEPT
{
++ref_count_;
return const_cast<impl_base*>(static_cast<const impl_base*>(this));
}
void destroy() ASIO_NOEXCEPT
{
if (--ref_count_ == 0)
{
allocator_type alloc(allocator_);
impl* p = this;
p->~impl();
alloc.deallocate(p, 1);
}
}
void on_work_started() ASIO_NOEXCEPT
{
executor_.on_work_started();
}
void on_work_finished() ASIO_NOEXCEPT
{
executor_.on_work_finished();
}
execution_context& context() ASIO_NOEXCEPT
{
return executor_.context();
}
void dispatch(ASIO_MOVE_ARG(function) f)
{
executor_.dispatch(ASIO_MOVE_CAST(function)(f), allocator_);
}
void post(ASIO_MOVE_ARG(function) f)
{
executor_.post(ASIO_MOVE_CAST(function)(f), allocator_);
}
void defer(ASIO_MOVE_ARG(function) f)
{
executor_.defer(ASIO_MOVE_CAST(function)(f), allocator_);
}
type_id_result_type target_type() const ASIO_NOEXCEPT
{
return type_id<Executor>();
}
void* target() ASIO_NOEXCEPT
{
return &executor_;
}
const void* target() const ASIO_NOEXCEPT
{
return &executor_;
}
bool equals(const impl_base* e) const ASIO_NOEXCEPT
{
if (this == e)
return true;
if (target_type() != e->target_type())
return false;
return executor_ == *static_cast<const Executor*>(e->target());
}
private:
mutable detail::atomic_count ref_count_;
Executor executor_;
Allocator allocator_;
struct raw_mem
{
allocator_type allocator_;
impl* ptr_;
explicit raw_mem(const Allocator& a)
: allocator_(a),
ptr_(allocator_.allocate(1))
{
}
~raw_mem()
{
if (ptr_)
allocator_.deallocate(ptr_, 1);
}
private:
// Disallow copying and assignment.
raw_mem(const raw_mem&);
raw_mem operator=(const raw_mem&);
};
};
// Polymorphic allocator specialisation for system_executor.
template <typename Allocator>
class executor::impl<system_executor, Allocator>
: public executor::impl_base
{
public:
static impl_base* create(const system_executor&,
const Allocator& = Allocator())
{
return &detail::global<impl<system_executor, std::allocator<void> > >();
}
impl()
: impl_base(true)
{
}
impl_base* clone() const ASIO_NOEXCEPT
{
return const_cast<impl_base*>(static_cast<const impl_base*>(this));
}
void destroy() ASIO_NOEXCEPT
{
}
void on_work_started() ASIO_NOEXCEPT
{
executor_.on_work_started();
}
void on_work_finished() ASIO_NOEXCEPT
{
executor_.on_work_finished();
}
execution_context& context() ASIO_NOEXCEPT
{
return executor_.context();
}
void dispatch(ASIO_MOVE_ARG(function) f)
{
executor_.dispatch(ASIO_MOVE_CAST(function)(f), allocator_);
}
void post(ASIO_MOVE_ARG(function) f)
{
executor_.post(ASIO_MOVE_CAST(function)(f), allocator_);
}
void defer(ASIO_MOVE_ARG(function) f)
{
executor_.defer(ASIO_MOVE_CAST(function)(f), allocator_);
}
type_id_result_type target_type() const ASIO_NOEXCEPT
{
return type_id<system_executor>();
}
void* target() ASIO_NOEXCEPT
{
return &executor_;
}
const void* target() const ASIO_NOEXCEPT
{
return &executor_;
}
bool equals(const impl_base* e) const ASIO_NOEXCEPT
{
return this == e;
}
private:
system_executor executor_;
Allocator allocator_;
};
template <typename Executor>
executor::executor(Executor e)
: impl_(impl<Executor, std::allocator<void> >::create(e))
{
}
template <typename Executor, typename Allocator>
executor::executor(allocator_arg_t, const Allocator& a, Executor e)
: impl_(impl<Executor, Allocator>::create(e, a))
{
}
template <typename Function, typename Allocator>
void executor::dispatch(ASIO_MOVE_ARG(Function) f,
const Allocator& a) const
{
impl_base* i = get_impl();
if (i->fast_dispatch_)
system_executor().dispatch(ASIO_MOVE_CAST(Function)(f), a);
else
i->dispatch(function(ASIO_MOVE_CAST(Function)(f), a));
}
template <typename Function, typename Allocator>
void executor::post(ASIO_MOVE_ARG(Function) f,
const Allocator& a) const
{
get_impl()->post(function(ASIO_MOVE_CAST(Function)(f), a));
}
template <typename Function, typename Allocator>
void executor::defer(ASIO_MOVE_ARG(Function) f,
const Allocator& a) const
{
get_impl()->defer(function(ASIO_MOVE_CAST(Function)(f), a));
}
template <typename Executor>
Executor* executor::target() ASIO_NOEXCEPT
{
return impl_ && impl_->target_type() == type_id<Executor>()
? static_cast<Executor*>(impl_->target()) : 0;
}
template <typename Executor>
const Executor* executor::target() const ASIO_NOEXCEPT
{
return impl_ && impl_->target_type() == type_id<Executor>()
? static_cast<Executor*>(impl_->target()) : 0;
}
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
#include "asio/detail/pop_options.hpp"
#endif // ASIO_IMPL_EXECUTOR_HPP