203 lines
5.5 KiB
C++
203 lines
5.5 KiB
C++
//
|
|
// detail/impl/strand_service.ipp
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
// Copyright (c) 2003-2023 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_DETAIL_IMPL_STRAND_SERVICE_IPP
|
|
#define ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP
|
|
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
|
# pragma once
|
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
|
|
|
#include "asio/detail/config.hpp"
|
|
#include "asio/detail/call_stack.hpp"
|
|
#include "asio/detail/strand_service.hpp"
|
|
|
|
#include "asio/detail/push_options.hpp"
|
|
|
|
namespace asio {
|
|
namespace detail {
|
|
|
|
struct strand_service::on_do_complete_exit
|
|
{
|
|
io_context_impl* owner_;
|
|
strand_impl* impl_;
|
|
|
|
~on_do_complete_exit()
|
|
{
|
|
impl_->mutex_.lock();
|
|
impl_->ready_queue_.push(impl_->waiting_queue_);
|
|
bool more_handlers = impl_->locked_ = !impl_->ready_queue_.empty();
|
|
impl_->mutex_.unlock();
|
|
|
|
if (more_handlers)
|
|
owner_->post_immediate_completion(impl_, true);
|
|
}
|
|
};
|
|
|
|
strand_service::strand_service(asio::io_context& io_context)
|
|
: asio::detail::service_base<strand_service>(io_context),
|
|
io_context_(io_context),
|
|
io_context_impl_(asio::use_service<io_context_impl>(io_context)),
|
|
mutex_(),
|
|
salt_(0)
|
|
{
|
|
}
|
|
|
|
void strand_service::shutdown()
|
|
{
|
|
op_queue<operation> ops;
|
|
|
|
asio::detail::mutex::scoped_lock lock(mutex_);
|
|
|
|
for (std::size_t i = 0; i < num_implementations; ++i)
|
|
{
|
|
if (strand_impl* impl = implementations_[i].get())
|
|
{
|
|
ops.push(impl->waiting_queue_);
|
|
ops.push(impl->ready_queue_);
|
|
}
|
|
}
|
|
}
|
|
|
|
void strand_service::construct(strand_service::implementation_type& impl)
|
|
{
|
|
asio::detail::mutex::scoped_lock lock(mutex_);
|
|
|
|
std::size_t salt = salt_++;
|
|
#if defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION)
|
|
std::size_t index = salt;
|
|
#else // defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION)
|
|
std::size_t index = reinterpret_cast<std::size_t>(&impl);
|
|
index += (reinterpret_cast<std::size_t>(&impl) >> 3);
|
|
index ^= salt + 0x9e3779b9 + (index << 6) + (index >> 2);
|
|
#endif // defined(ASIO_ENABLE_SEQUENTIAL_STRAND_ALLOCATION)
|
|
index = index % num_implementations;
|
|
|
|
if (!implementations_[index].get())
|
|
implementations_[index].reset(new strand_impl);
|
|
impl = implementations_[index].get();
|
|
}
|
|
|
|
bool strand_service::running_in_this_thread(
|
|
const implementation_type& impl) const
|
|
{
|
|
return call_stack<strand_impl>::contains(impl) != 0;
|
|
}
|
|
|
|
struct strand_service::on_dispatch_exit
|
|
{
|
|
io_context_impl* io_context_impl_;
|
|
strand_impl* impl_;
|
|
|
|
~on_dispatch_exit()
|
|
{
|
|
impl_->mutex_.lock();
|
|
impl_->ready_queue_.push(impl_->waiting_queue_);
|
|
bool more_handlers = impl_->locked_ = !impl_->ready_queue_.empty();
|
|
impl_->mutex_.unlock();
|
|
|
|
if (more_handlers)
|
|
io_context_impl_->post_immediate_completion(impl_, false);
|
|
}
|
|
};
|
|
|
|
void strand_service::do_dispatch(implementation_type& impl, operation* op)
|
|
{
|
|
// If we are running inside the io_context, and no other handler already
|
|
// holds the strand lock, then the handler can run immediately.
|
|
bool can_dispatch = io_context_impl_.can_dispatch();
|
|
impl->mutex_.lock();
|
|
if (can_dispatch && !impl->locked_)
|
|
{
|
|
// Immediate invocation is allowed.
|
|
impl->locked_ = true;
|
|
impl->mutex_.unlock();
|
|
|
|
// Indicate that this strand is executing on the current thread.
|
|
call_stack<strand_impl>::context ctx(impl);
|
|
|
|
// Ensure the next handler, if any, is scheduled on block exit.
|
|
on_dispatch_exit on_exit = { &io_context_impl_, impl };
|
|
(void)on_exit;
|
|
|
|
op->complete(&io_context_impl_, asio::error_code(), 0);
|
|
return;
|
|
}
|
|
|
|
if (impl->locked_)
|
|
{
|
|
// Some other handler already holds the strand lock. Enqueue for later.
|
|
impl->waiting_queue_.push(op);
|
|
impl->mutex_.unlock();
|
|
}
|
|
else
|
|
{
|
|
// The handler is acquiring the strand lock and so is responsible for
|
|
// scheduling the strand.
|
|
impl->locked_ = true;
|
|
impl->mutex_.unlock();
|
|
impl->ready_queue_.push(op);
|
|
io_context_impl_.post_immediate_completion(impl, false);
|
|
}
|
|
}
|
|
|
|
void strand_service::do_post(implementation_type& impl,
|
|
operation* op, bool is_continuation)
|
|
{
|
|
impl->mutex_.lock();
|
|
if (impl->locked_)
|
|
{
|
|
// Some other handler already holds the strand lock. Enqueue for later.
|
|
impl->waiting_queue_.push(op);
|
|
impl->mutex_.unlock();
|
|
}
|
|
else
|
|
{
|
|
// The handler is acquiring the strand lock and so is responsible for
|
|
// scheduling the strand.
|
|
impl->locked_ = true;
|
|
impl->mutex_.unlock();
|
|
impl->ready_queue_.push(op);
|
|
io_context_impl_.post_immediate_completion(impl, is_continuation);
|
|
}
|
|
}
|
|
|
|
void strand_service::do_complete(void* owner, operation* base,
|
|
const asio::error_code& ec, std::size_t /*bytes_transferred*/)
|
|
{
|
|
if (owner)
|
|
{
|
|
strand_impl* impl = static_cast<strand_impl*>(base);
|
|
|
|
// Indicate that this strand is executing on the current thread.
|
|
call_stack<strand_impl>::context ctx(impl);
|
|
|
|
// Ensure the next handler, if any, is scheduled on block exit.
|
|
on_do_complete_exit on_exit;
|
|
on_exit.owner_ = static_cast<io_context_impl*>(owner);
|
|
on_exit.impl_ = impl;
|
|
|
|
// Run all ready handlers. No lock is required since the ready queue is
|
|
// accessed only within the strand.
|
|
while (operation* o = impl->ready_queue_.front())
|
|
{
|
|
impl->ready_queue_.pop();
|
|
o->complete(owner, ec, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace asio
|
|
|
|
#include "asio/detail/pop_options.hpp"
|
|
|
|
#endif // ASIO_DETAIL_IMPL_STRAND_SERVICE_IPP
|