Skip to content

Commit 4a00611

Browse files
committed
Fixing a potential race in obtaining a connection from boost asio connection pool.
1 parent 23357c2 commit 4a00611

File tree

1 file changed

+21
-34
lines changed

1 file changed

+21
-34
lines changed

Release/src/http/client/http_client_asio.cpp

Lines changed: 21 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -242,72 +242,59 @@ class asio_connection_pool
242242
// This will destroy and remove the connection from pool after the set timeout.
243243
// We use 'this' because async calls to timer handler only occur while the pool exists.
244244
connection->start_pool_timer(m_timeout_secs, boost::bind(&asio_connection_pool::handle_pool_timer, this, boost::asio::placeholders::error, connection));
245-
246-
put_to_pool(connection);
245+
246+
std::lock_guard<std::mutex> lock(m_connections_mutex);
247+
m_connections.push_back(connection);
247248
}
248249
// Otherwise connection is not put to the pool and it will go out of scope.
249250
}
250251

251252
std::shared_ptr<asio_connection> obtain()
252253
{
253-
if (is_pool_empty())
254+
std::unique_lock<std::mutex> lock(m_connections_mutex);
255+
if (m_connections.empty())
254256
{
257+
lock.unlock();
258+
255259
// No connections in pool => create a new connection instance.
256260
return std::make_shared<asio_connection>(m_io_service, m_use_ssl);
257261
}
258262
else
259263
{
260264
// Reuse connection from pool.
261-
auto connection(get_head());
265+
auto connection = m_connections.back();
266+
m_connections.pop_back();
267+
lock.unlock();
268+
262269
connection->start_reuse();
263270
return connection;
264271
}
265272
}
266273

267274
private:
268-
269-
bool is_pool_empty()
270-
{
271-
std::lock_guard<std::mutex> lock(m_connections_mutex);
272-
return m_connections.empty();
273-
}
274-
275-
std::shared_ptr<asio_connection> get_head()
276-
{
277-
std::lock_guard<std::mutex> lock(m_connections_mutex);
278-
auto connection(*m_connections.begin());
279-
m_connections.erase(m_connections.begin());
280-
return connection;
281-
}
282-
283-
void put_to_pool(const std::shared_ptr<asio_connection> &connection)
284-
{
285-
std::lock_guard<std::mutex> lock(m_connections_mutex);
286-
m_connections.insert(connection);
287-
}
288-
289-
void remove(const std::shared_ptr<asio_connection> &connection)
290-
{
291-
std::lock_guard<std::mutex> lock(m_connections_mutex);
292-
m_connections.erase(connection);
293-
}
294-
275+
295276
// Using weak_ptr here ensures bind() to this handler will not prevent the connection object from going out of scope.
296277
void handle_pool_timer(const boost::system::error_code& ec, const std::weak_ptr<asio_connection> &connection)
297278
{
298279
if (!ec)
299280
{
300-
if (auto connection_shared = connection.lock())
281+
auto connection_shared = connection.lock();
282+
if (connection_shared)
301283
{
302-
remove(connection_shared);
284+
std::lock_guard<std::mutex> lock(m_connections_mutex);
285+
const auto &iter = std::find(m_connections.begin(), m_connections.end(), connection_shared);
286+
if (iter != m_connections.end())
287+
{
288+
m_connections.erase(iter);
289+
}
303290
}
304291
}
305292
}
306293

307294
boost::asio::io_service& m_io_service;
308295
const int m_timeout_secs;
309296
const bool m_use_ssl;
310-
std::unordered_set<std::shared_ptr<asio_connection> > m_connections;
297+
std::vector<std::shared_ptr<asio_connection> > m_connections;
311298
std::mutex m_connections_mutex;
312299
};
313300

0 commit comments

Comments
 (0)