Skip to content

Commit 7535d9a

Browse files
authored
fix: prevent batching thread to run indefinitely when UDPSender is destroyed (#16)
Make m_mustExit an std::atomic<bool> and use std::memory_order_acq_rel ordering so that other threads are guaranteed to see the change of value of m_mustExit.
1 parent 42f02b4 commit 7535d9a

File tree

2 files changed

+19
-13
lines changed

2 files changed

+19
-13
lines changed

include/cpp-statsd-client/StatsdClient.hpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ class StatsdClient {
7171
};
7272

7373
inline StatsdClient::StatsdClient(const std::string& host,
74-
const uint16_t port,
75-
const std::string& prefix,
76-
const std::optional<uint64_t> batchsize) noexcept
74+
const uint16_t port,
75+
const std::string& prefix,
76+
const std::optional<uint64_t> batchsize) noexcept
7777
: m_prefix(prefix), m_sender(host, port, batchsize) {
7878
// Initialize the randorm generator to be used for sampling
7979
std::srand(time(nullptr));
@@ -100,16 +100,19 @@ inline void StatsdClient::count(const std::string& key, const int delta, const f
100100
return send(key, delta, "c", frequency);
101101
}
102102

103-
inline void StatsdClient::gauge(const std::string& key, const unsigned int value, const float frequency) const noexcept {
103+
inline void StatsdClient::gauge(const std::string& key, const unsigned int value, const float frequency) const
104+
noexcept {
104105
return send(key, value, "g", frequency);
105106
}
106107

107108
inline void StatsdClient::timing(const std::string& key, const unsigned int ms, const float frequency) const noexcept {
108109
return send(key, ms, "ms", frequency);
109110
}
110111

111-
inline void StatsdClient::send(const std::string& key, const int value, const std::string& type, const float frequency) const
112-
noexcept {
112+
inline void StatsdClient::send(const std::string& key,
113+
const int value,
114+
const std::string& type,
115+
const float frequency) const noexcept {
113116
const auto isFrequencyOne = [](const float frequency) noexcept {
114117
constexpr float epsilon{0.0001f};
115118
return std::fabs(frequency - 1.0f) < epsilon;

include/cpp-statsd-client/UDPSender.hpp

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <sys/socket.h>
77
#include <sys/types.h>
88
#include <unistd.h>
9+
#include <atomic>
910
#include <cmath>
1011
#include <cstring>
1112
#include <deque>
@@ -73,7 +74,7 @@ class UDPSender final {
7374
bool m_isInitialized{false};
7475

7576
//! Shall we exit?
76-
bool m_mustExit{false};
77+
std::atomic<bool> m_mustExit{false};
7778

7879
//!@}
7980

@@ -118,7 +119,9 @@ class UDPSender final {
118119
std::optional<std::string> m_errorMessage;
119120
};
120121

121-
inline UDPSender::UDPSender(const std::string& host, const uint16_t port, const std::optional<uint64_t> batchsize) noexcept
122+
inline UDPSender::UDPSender(const std::string& host,
123+
const uint16_t port,
124+
const std::optional<uint64_t> batchsize) noexcept
122125
: m_host(host), m_port(port) {
123126
// If batching is on, use a dedicated thread to send every now and then
124127
if (batchsize) {
@@ -131,7 +134,7 @@ inline UDPSender::UDPSender(const std::string& host, const uint16_t port, const
131134

132135
// Define the batching thread
133136
m_batchingThread = std::thread([this, batchingWait] {
134-
while (!m_mustExit) {
137+
while (!m_mustExit.load(std::memory_order_acq_rel)) {
135138
std::deque<std::string> stagedMessageQueue;
136139

137140
std::unique_lock<std::mutex> batchingLock(m_batchingMutex);
@@ -153,7 +156,7 @@ inline UDPSender::UDPSender(const std::string& host, const uint16_t port, const
153156

154157
inline UDPSender::~UDPSender() {
155158
if (m_batching) {
156-
m_mustExit = true;
159+
m_mustExit.store(true, std::memory_order_acq_rel);
157160
m_batchingThread.join();
158161
}
159162

@@ -221,7 +224,7 @@ inline bool UDPSender::initialize() noexcept {
221224
hints.ai_socktype = SOCK_DGRAM;
222225

223226
// Get the address info using the hints
224-
struct addrinfo *results = nullptr;
227+
struct addrinfo* results = nullptr;
225228
const int ret{getaddrinfo(m_host.c_str(), nullptr, &hints, &results)};
226229
if (ret != 0) {
227230
// An error code has been returned by getaddrinfo
@@ -232,7 +235,7 @@ inline bool UDPSender::initialize() noexcept {
232235
}
233236

234237
// Copy the results in m_server
235-
struct sockaddr_in *host_addr = (struct sockaddr_in *)results->ai_addr;
238+
struct sockaddr_in* host_addr = (struct sockaddr_in*)results->ai_addr;
236239
std::memcpy(&m_server.sin_addr, &host_addr->sin_addr, sizeof(struct in_addr));
237240

238241
// Free the memory allocated
@@ -251,7 +254,7 @@ inline void UDPSender::sendToDaemon(const std::string& message) noexcept {
251254

252255
// Try sending the message
253256
const long int ret{
254-
sendto(m_socket, message.data(), message.size(), 0, (struct sockaddr *)&m_server, sizeof(m_server))};
257+
sendto(m_socket, message.data(), message.size(), 0, (struct sockaddr*)&m_server, sizeof(m_server))};
255258
if (ret == -1) {
256259
using namespace std::string_literals;
257260
m_errorMessage =

0 commit comments

Comments
 (0)