Skip to content

Commit 2c21661

Browse files
committed
Allow async_resolve to use a configurable number of internal threads.
The number of threads used to emulate asynchronous address resolution is now adjustable via the "resolver" / "threads" configuration option. If this is set to a non-zero value, that number of threads will be created when the first resolver object is constructed. Otherwise, it defaults to a single thread that is created on the first call to async_resolve.
1 parent e3d2c61 commit 2c21661

13 files changed

Lines changed: 363 additions & 164 deletions

asio/include/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ nobase_include_HEADERS = \
140140
asio/detail/impl/reactive_descriptor_service.ipp \
141141
asio/detail/impl/reactive_socket_service_base.ipp \
142142
asio/detail/impl/resolver_service_base.ipp \
143+
asio/detail/impl/resolver_thread_pool.ipp \
143144
asio/detail/impl/scheduler.ipp \
144145
asio/detail/impl/select_reactor.hpp \
145146
asio/detail/impl/select_reactor.ipp \
@@ -254,6 +255,7 @@ nobase_include_HEADERS = \
254255
asio/detail/resolve_op.hpp \
255256
asio/detail/resolve_query_op.hpp \
256257
asio/detail/resolver_service_base.hpp \
258+
asio/detail/resolver_thread_pool.hpp \
257259
asio/detail/resolver_service.hpp \
258260
asio/detail/scheduler.hpp \
259261
asio/detail/scheduler_operation.hpp \

asio/include/asio/detail/impl/resolver_service_base.ipp

Lines changed: 4 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -17,71 +17,21 @@
1717

1818
#include "asio/detail/config.hpp"
1919
#include "asio/config.hpp"
20+
#include "asio/detail/memory.hpp"
2021
#include "asio/detail/resolver_service_base.hpp"
2122

2223
#include "asio/detail/push_options.hpp"
2324

2425
namespace asio {
2526
namespace detail {
2627

27-
class resolver_service_base::work_scheduler_runner
28-
{
29-
public:
30-
work_scheduler_runner(scheduler_impl& work_scheduler)
31-
: work_scheduler_(work_scheduler)
32-
{
33-
}
34-
35-
void operator()()
36-
{
37-
asio::error_code ec;
38-
work_scheduler_.run(ec);
39-
}
40-
41-
private:
42-
scheduler_impl& work_scheduler_;
43-
};
44-
4528
resolver_service_base::resolver_service_base(execution_context& context)
46-
: scheduler_(asio::use_service<scheduler_impl>(context)),
47-
work_scheduler_(new scheduler_impl(context)),
48-
work_thread_(),
49-
scheduler_locking_(config(context).get("scheduler", "locking", true))
29+
: thread_pool_(asio::use_service<resolver_thread_pool>(context))
5030
{
51-
work_scheduler_->work_started();
5231
}
5332

5433
resolver_service_base::~resolver_service_base()
5534
{
56-
base_shutdown();
57-
}
58-
59-
void resolver_service_base::base_shutdown()
60-
{
61-
if (work_scheduler_.get())
62-
{
63-
work_scheduler_->work_finished();
64-
work_scheduler_->stop();
65-
work_thread_.join();
66-
work_scheduler_.reset();
67-
}
68-
}
69-
70-
void resolver_service_base::base_notify_fork(
71-
execution_context::fork_event fork_ev)
72-
{
73-
if (work_thread_.joinable())
74-
{
75-
if (fork_ev == execution_context::fork_prepare)
76-
{
77-
work_scheduler_->stop();
78-
work_thread_.join();
79-
}
80-
}
81-
else if (fork_ev != execution_context::fork_prepare)
82-
{
83-
work_scheduler_->restart();
84-
}
8535
}
8636

8737
void resolver_service_base::construct(
@@ -93,7 +43,7 @@ void resolver_service_base::construct(
9343
void resolver_service_base::destroy(
9444
resolver_service_base::implementation_type& impl)
9545
{
96-
ASIO_HANDLER_OPERATION((scheduler_.context(),
46+
ASIO_HANDLER_OPERATION((thread_pool_.context(),
9747
"resolver", &impl, 0, "cancel"));
9848

9949
impl.reset();
@@ -115,34 +65,12 @@ void resolver_service_base::move_assign(implementation_type& impl,
11565
void resolver_service_base::cancel(
11666
resolver_service_base::implementation_type& impl)
11767
{
118-
ASIO_HANDLER_OPERATION((scheduler_.context(),
68+
ASIO_HANDLER_OPERATION((thread_pool_.context(),
11969
"resolver", &impl, 0, "cancel"));
12070

12171
impl.reset(static_cast<void*>(0), socket_ops::noop_deleter());
12272
}
12373

124-
void resolver_service_base::start_resolve_op(resolve_op* op)
125-
{
126-
if (scheduler_locking_)
127-
{
128-
start_work_thread();
129-
scheduler_.work_started();
130-
work_scheduler_->post_immediate_completion(op, false);
131-
}
132-
else
133-
{
134-
op->ec_ = asio::error::operation_not_supported;
135-
scheduler_.post_immediate_completion(op, false);
136-
}
137-
}
138-
139-
void resolver_service_base::start_work_thread()
140-
{
141-
asio::detail::mutex::scoped_lock lock(mutex_);
142-
if (!work_thread_.joinable())
143-
work_thread_ = thread(work_scheduler_runner(*work_scheduler_));
144-
}
145-
14674
} // namespace detail
14775
} // namespace asio
14876

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
//
2+
// detail/impl/resolver_thread_pool.ipp
3+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4+
//
5+
// Copyright (c) 2003-2025 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6+
//
7+
// Distributed under the Boost Software License, Version 1.0. (See accompanying
8+
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9+
//
10+
11+
#ifndef ASIO_DETAIL_IMPL_RESOLVER_THREAD_POOL_IPP
12+
#define ASIO_DETAIL_IMPL_RESOLVER_THREAD_POOL_IPP
13+
14+
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
15+
# pragma once
16+
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17+
18+
#include "asio/detail/config.hpp"
19+
#include "asio/config.hpp"
20+
#include "asio/detail/memory.hpp"
21+
#include "asio/detail/resolver_thread_pool.hpp"
22+
#include "asio/error.hpp"
23+
24+
#include "asio/detail/push_options.hpp"
25+
26+
namespace asio {
27+
namespace detail {
28+
29+
class resolver_thread_pool::work_scheduler_runner
30+
{
31+
public:
32+
work_scheduler_runner(scheduler_impl& work_scheduler)
33+
: work_scheduler_(work_scheduler)
34+
{
35+
}
36+
37+
void operator()()
38+
{
39+
asio::error_code ec;
40+
work_scheduler_.run(ec);
41+
}
42+
43+
private:
44+
scheduler_impl& work_scheduler_;
45+
};
46+
47+
resolver_thread_pool::resolver_thread_pool(execution_context& context)
48+
: execution_context_service_base<resolver_thread_pool>(context),
49+
scheduler_(asio::use_service<scheduler_impl>(context)),
50+
work_scheduler_(scheduler_impl::internal(), context),
51+
work_threads_(execution_context::allocator<void>(context)),
52+
num_work_threads_(config(context).get("resolver", "threads", 0U)),
53+
scheduler_locking_(config(context).get("scheduler", "locking", true)),
54+
shutdown_(false)
55+
{
56+
work_scheduler_.work_started();
57+
if (num_work_threads_ > 0)
58+
start_work_threads();
59+
else
60+
num_work_threads_ = 1;
61+
}
62+
63+
resolver_thread_pool::~resolver_thread_pool()
64+
{
65+
shutdown();
66+
}
67+
68+
void resolver_thread_pool::shutdown()
69+
{
70+
if (!shutdown_)
71+
{
72+
work_scheduler_.work_finished();
73+
work_scheduler_.stop();
74+
work_threads_.join();
75+
work_scheduler_.shutdown();
76+
shutdown_ = true;
77+
}
78+
}
79+
80+
void resolver_thread_pool::notify_fork(execution_context::fork_event fork_ev)
81+
{
82+
if (!work_threads_.empty())
83+
{
84+
if (fork_ev == execution_context::fork_prepare)
85+
{
86+
work_scheduler_.stop();
87+
work_threads_.join();
88+
}
89+
}
90+
else if (fork_ev != execution_context::fork_prepare)
91+
{
92+
work_scheduler_.restart();
93+
}
94+
}
95+
96+
void resolver_thread_pool::start_resolve_op(resolve_op* op)
97+
{
98+
if (scheduler_locking_)
99+
{
100+
start_work_threads();
101+
scheduler_.work_started();
102+
work_scheduler_.post_immediate_completion(op, false);
103+
}
104+
else
105+
{
106+
op->ec_ = asio::error::operation_not_supported;
107+
scheduler_.post_immediate_completion(op, false);
108+
}
109+
}
110+
111+
void resolver_thread_pool::start_work_threads()
112+
{
113+
asio::detail::mutex::scoped_lock lock(mutex_);
114+
if (work_threads_.empty())
115+
for (unsigned int i = 0; i < num_work_threads_; ++i)
116+
work_threads_.create_thread(work_scheduler_runner(work_scheduler_));
117+
}
118+
119+
} // namespace detail
120+
} // namespace asio
121+
122+
#include "asio/detail/pop_options.hpp"
123+
124+
#endif // ASIO_DETAIL_IMPL_RESOLVER_THREAD_POOL_IPP

asio/include/asio/detail/impl/scheduler.ipp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,22 @@ scheduler::scheduler(asio::execution_context& ctx,
126126
ASIO_HANDLER_TRACKING_INIT;
127127
}
128128

129+
scheduler::scheduler(scheduler::internal, asio::execution_context& ctx)
130+
: asio::detail::execution_context_service_base<scheduler>(ctx),
131+
one_thread_(false),
132+
mutex_(true, 0),
133+
task_(0),
134+
get_task_(&scheduler::get_default_task),
135+
task_interrupted_(true),
136+
stopped_(false),
137+
shutdown_(false),
138+
outstanding_work_(0),
139+
task_usec_(-1L),
140+
wait_usec_(-1L)
141+
{
142+
ASIO_HANDLER_TRACKING_INIT;
143+
}
144+
129145
scheduler::~scheduler()
130146
{
131147
}

asio/include/asio/detail/impl/win_iocp_io_context.ipp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,32 @@ win_iocp_io_context::win_iocp_io_context(
104104
}
105105
}
106106

107+
win_iocp_io_context::win_iocp_io_context(
108+
win_iocp_io_context::internal, asio::execution_context& ctx)
109+
: execution_context_service_base<win_iocp_io_context>(ctx),
110+
iocp_(),
111+
outstanding_work_(0),
112+
stopped_(0),
113+
stop_event_posted_(0),
114+
shutdown_(0),
115+
gqcs_timeout_(get_gqcs_timeout()),
116+
dispatch_required_(0),
117+
concurrency_hint_(-1)
118+
{
119+
ASIO_HANDLER_TRACKING_INIT;
120+
121+
iocp_.handle = ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0,
122+
static_cast<DWORD>(concurrency_hint_ >= 0
123+
? concurrency_hint_ : DWORD(~0)));
124+
if (!iocp_.handle)
125+
{
126+
DWORD last_error = ::GetLastError();
127+
asio::error_code ec(last_error,
128+
asio::error::get_system_category());
129+
asio::detail::throw_error(ec, "iocp");
130+
}
131+
}
132+
107133
win_iocp_io_context::~win_iocp_io_context()
108134
{
109135
}

asio/include/asio/detail/resolver_service.hpp

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,6 @@ class resolver_service :
6060
// Destroy all user-defined handler objects owned by the service.
6161
void shutdown()
6262
{
63-
this->base_shutdown();
64-
}
65-
66-
// Perform any fork-related housekeeping.
67-
void notify_fork(execution_context::fork_event fork_ev)
68-
{
69-
this->base_notify_fork(fork_ev);
7063
}
7164

7265
// Resolve a query to a list of entries.
@@ -93,12 +86,12 @@ class resolver_service :
9386
typedef resolve_query_op<Protocol, Handler, IoExecutor> op;
9487
typename op::ptr p = { asio::detail::addressof(handler),
9588
op::ptr::allocate(handler), 0 };
96-
p.p = new (p.v) op(impl, qry, scheduler_, handler, io_ex);
89+
p.p = new (p.v) op(impl, qry, thread_pool_.scheduler(), handler, io_ex);
9790

98-
ASIO_HANDLER_CREATION((scheduler_.context(),
91+
ASIO_HANDLER_CREATION((thread_pool_.context(),
9992
*p.p, "resolver", &impl, 0, "async_resolve"));
10093

101-
start_resolve_op(p.p);
94+
thread_pool_.start_resolve_op(p.p);
10295
p.v = p.p = 0;
10396
}
10497

@@ -126,12 +119,13 @@ class resolver_service :
126119
typedef resolve_endpoint_op<Protocol, Handler, IoExecutor> op;
127120
typename op::ptr p = { asio::detail::addressof(handler),
128121
op::ptr::allocate(handler), 0 };
129-
p.p = new (p.v) op(impl, endpoint, scheduler_, handler, io_ex);
122+
p.p = new (p.v) op(impl, endpoint,
123+
thread_pool_.scheduler(), handler, io_ex);
130124

131-
ASIO_HANDLER_CREATION((scheduler_.context(),
125+
ASIO_HANDLER_CREATION((thread_pool_.context(),
132126
*p.p, "resolver", &impl, 0, "async_resolve"));
133127

134-
start_resolve_op(p.p);
128+
thread_pool_.start_resolve_op(p.p);
135129
p.v = p.p = 0;
136130
}
137131
};

0 commit comments

Comments
 (0)