Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
4a7d596
try a windows build
kevinkreiser Jun 25, 2021
79eb865
try another target
kevinkreiser Jun 25, 2021
db7debd
leave out the build options
kevinkreiser Jun 25, 2021
8c85eee
Update win.yml
kevinkreiser Jun 25, 2021
5a6fa2b
Initial Hunterization
rbsheth Oct 14, 2020
f153eee
Windows fixes
rbsheth Jun 25, 2021
ebce2a8
Include fixup
rbsheth Jun 25, 2021
5518121
test fixes
rbsheth Jun 25, 2021
beadc09
add ifdefs to mock server
kevinkreiser Jun 29, 2021
800fcfc
Merge remote-tracking branch 'origin/kk_windows' into kk_windows
kevinkreiser Jun 29, 2021
f85c3b8
resolve conflicts
kevinkreiser Jun 29, 2021
652c002
build within the build dir
kevinkreiser Jun 29, 2021
3d029e2
coverage is disabled by default
kevinkreiser Jun 29, 2021
b31d729
Merge remote-tracking branch 'origin/master' into kk_windows
kevinkreiser Jun 30, 2021
2b74325
Update .github/workflows/win.yml
kevinkreiser Jul 2, 2021
29e9efb
Update .github/workflows/win.yml
kevinkreiser Jul 2, 2021
a38092c
Update .github/workflows/win.yml
kevinkreiser Jul 2, 2021
6fcc092
Update .github/workflows/win.yml
kevinkreiser Jul 2, 2021
ff215d6
Update .github/workflows/win.yml
kevinkreiser Jul 2, 2021
74889d3
Update .github/workflows/win.yml
kevinkreiser Jul 2, 2021
0483d3c
set warning flags differently for msvc
kevinkreiser Jul 3, 2021
a0c0695
try suppressing the warnings from external includes
kevinkreiser Jul 3, 2021
e625203
fix some warnings in test server
kevinkreiser Jul 3, 2021
17db245
undefine windows built in min/max macros
kevinkreiser Jul 4, 2021
ab42b13
no more warnings in test server
kevinkreiser Jul 4, 2021
8b86d46
move all socket defines and operations to udpsender and use from there
kevinkreiser Jul 6, 2021
295026f
avoid thread safety issues and annoyingly complex strerror
kevinkreiser Jul 6, 2021
63c6370
try removing more warnings and also lower the warning level to ignore…
kevinkreiser Jul 6, 2021
342336b
remove final warning
kevinkreiser Jul 6, 2021
7834b8e
revert hunter for now, make library easier to vendor, print exact tes…
kevinkreiser Jul 7, 2021
50d0ea9
try for more output in CI
kevinkreiser Jul 7, 2021
1337817
try more more output
kevinkreiser Jul 7, 2021
193065c
just want to see programs output..
kevinkreiser Jul 7, 2021
2998b4a
more logging of failure info
kevinkreiser Jul 7, 2021
28ea211
check if the test server binds properly
kevinkreiser Jul 7, 2021
fa1f68e
debugging windows socket recv
kevinkreiser Jul 7, 2021
312cc14
get the error number
kevinkreiser Jul 7, 2021
c15f5d2
check binding as well
kevinkreiser Jul 7, 2021
1e5a738
get tests working on windows build
kevinkreiser Jul 7, 2021
58456a4
clean up pr for review
kevinkreiser Jul 7, 2021
21a9767
disable the options if not standalone mode
kevinkreiser Jul 7, 2021
b1ea596
didnt need to fix this warning
kevinkreiser Jul 12, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/win.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Windows

on: [push, pull_request]
jobs:
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: dependencies
run: |
choco install cmake
- name: build
run: |
cmake -S . -B build -G "Visual Studio 16 2019" -A x64
cmake --build build --target ALL_BUILD --config Release
- name: test
run: |
cmake --build build --target RUN_TESTS --config Release
39 changes: 29 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@ project(cpp-statsd-client
LANGUAGES CXX
DESCRIPTION "A header-only StatsD client implemented in C++"
HOMEPAGE_URL "https://github.com/vthiery/cpp-statsd-client")

option(CPP_STATSD_STANDALONE "Allows configuration of targets for verifying library functionality" ON)
option(ENABLE_TESTS "Build tests" ON)
option(ENABLE_COVERAGE "Build with coverage instrumentalisation" OFF)

if(NOT CPP_STATSD_STANDALONE)
set(ENABLE_TESTS OFF)
set(ENABLE_COVERAGE OFF)
endif()
Comment thread
vthiery marked this conversation as resolved.

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
option(ENABLE_COVERAGE "Build with coverage instrumentalisation" OFF)

include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
find_package(Threads)
Expand All @@ -30,6 +40,9 @@ target_include_directories(
INTERFACE $<BUILD_INTERFACE:${${PROJECT_NAME}_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
target_link_libraries(${PROJECT_NAME} INTERFACE Threads::Threads)
if(WIN32)
target_link_libraries(${PROJECT_NAME} INTERFACE ws2_32)
endif()

# The installation and pkg-config-like cmake config
install(TARGETS ${PROJECT_NAME}
Expand All @@ -54,13 +67,19 @@ install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake)
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include DESTINATION include)

# The test targets
add_executable(testStatsdClient ${CMAKE_CURRENT_SOURCE_DIR}/tests/testStatsdClient.cpp)
target_compile_options(testStatsdClient PRIVATE -Wall -Wextra -pedantic -Werror)
target_include_directories(testStatsdClient PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests)
target_link_libraries(testStatsdClient ${PROJECT_NAME})
if(ENABLE_TESTS)
# The test targets
add_executable(testStatsdClient ${CMAKE_CURRENT_SOURCE_DIR}/tests/testStatsdClient.cpp)
if(WIN32)
target_compile_options(testStatsdClient PRIVATE -W4 -WX /external:W0)
Comment thread
vthiery marked this conversation as resolved.
else()
target_compile_options(testStatsdClient PRIVATE -Wall -Wextra -pedantic -Werror)
endif()
target_include_directories(testStatsdClient PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests)
target_link_libraries(testStatsdClient ${PROJECT_NAME})

# The test suite
enable_testing()
add_test(ctestTestStatsdClient testStatsdClient)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS testStatsdClient)
# The test suite
enable_testing()
add_test(ctestTestStatsdClient testStatsdClient)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS testStatsdClient)
endif()
18 changes: 18 additions & 0 deletions cmake/Config.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2016, Ruslan Baratov
#
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.

@PACKAGE_INIT@

find_package(Threads REQUIRED)

include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
check_required_components("@PROJECT_NAME@")
2 changes: 1 addition & 1 deletion include/cpp-statsd-client/StatsdClient.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ namespace Statsd {
*/
class StatsdClient {
public:
//!@name Constructor and destructor
//!@name Constructor and destructor, non-copyable
//!@{

//! Constructor
Expand Down
102 changes: 84 additions & 18 deletions include/cpp-statsd-client/UDPSender.hpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
#ifndef UDP_SENDER_HPP
#define UDP_SENDER_HPP

#ifdef _WIN32
#define NOMINMAX
#include <io.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#endif

#include <atomic>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <deque>
#include <mutex>
#include <string>
#include <thread>

namespace Statsd {

#ifdef _WIN32
using SOCKET_TYPE = SOCKET;
constexpr SOCKET_TYPE k_invalidSocket{INVALID_SOCKET};
#define SOCKET_ERRNO WSAGetLastError()
#define SOCKET_CLOSE closesocket
#else
using SOCKET_TYPE = int;
constexpr SOCKET_TYPE k_invalidSocket{-1};
#define SOCKET_ERRNO errno
#define SOCKET_CLOSE close
#endif
Comment thread
vthiery marked this conversation as resolved.

/*!
*
* UDP sender
Expand All @@ -24,7 +45,7 @@ namespace Statsd {
*/
class UDPSender final {
public:
//!@name Constructor and destructor
//!@name Constructor and destructor, non-copyable
//!@{

//! Constructor
Expand All @@ -36,6 +57,10 @@ class UDPSender final {
//! Destructor
~UDPSender();

UDPSender(const UDPSender&) = delete;
UDPSender& operator=(const UDPSender&) = delete;
UDPSender(UDPSender&&) = delete;

//!@}

//!@name Methods
Expand Down Expand Up @@ -92,7 +117,7 @@ class UDPSender final {
struct sockaddr_in m_server;

//! The socket to be used
int m_socket{-1};
SOCKET_TYPE m_socket = k_invalidSocket;

//!@}

Expand All @@ -118,10 +143,37 @@ class UDPSender final {

//! Error message (optional string)
std::string m_errorMessage;
};

namespace detail {

inline bool isValidSocket(const SOCKET_TYPE socket) {
return socket != k_invalidSocket;
}

#ifdef _WIN32
struct WinSockSingleton {
Comment thread
vthiery marked this conversation as resolved.
inline static const WinSockSingleton& getInstance() {
static const WinSockSingleton instance;
return instance;
}
inline bool ok() const {
return m_ok;
}
~WinSockSingleton() {
WSACleanup();
}

//! Bad file descriptor
static constexpr int k_invalidFd{-1};
private:
WinSockSingleton() {
WSADATA wsa;
m_ok = WSAStartup(MAKEWORD(2, 2), &wsa) == 0;
}
bool m_ok;
};
#endif

} // namespace detail

inline UDPSender::UDPSender(const std::string& host,
const uint16_t port,
Expand All @@ -138,7 +190,7 @@ inline UDPSender::UDPSender(const std::string& host,
// Define the batching thread
m_batchingThread = std::thread([this] {
// TODO: this will drop unsent stats, should we send all the unsent stats before we exit?
while (!m_mustExit.load(std::memory_order_acq_rel)) {
while (!m_mustExit.load(std::memory_order_acquire)) {
std::deque<std::string> stagedMessageQueue;

std::unique_lock<std::mutex> batchingLock(m_batchingMutex);
Expand All @@ -165,12 +217,12 @@ inline UDPSender::~UDPSender() {

// If we're running a background thread tell it to stop
if (m_batchingThread.joinable()) {
m_mustExit.store(true, std::memory_order_acq_rel);
m_mustExit.store(true, std::memory_order_release);
Comment thread
vthiery marked this conversation as resolved.
m_batchingThread.join();
}

// Cleanup the socket
close(m_socket);
SOCKET_CLOSE(m_socket);
}

inline void UDPSender::send(const std::string& message) noexcept {
Expand Down Expand Up @@ -207,18 +259,24 @@ inline const std::string& UDPSender::errorMessage() const noexcept {
}

inline bool UDPSender::initialize() noexcept {
#ifdef _WIN32
if (!detail::WinSockSingleton::getInstance().ok()) {
m_errorMessage = "WSAStartup failed: errno=" + std::to_string(SOCKET_ERRNO);
}
#endif

// Connect the socket
m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (m_socket == k_invalidFd) {
m_errorMessage = std::string("socket creation failed: err=") + std::strerror(errno);
if (!detail::isValidSocket(m_socket)) {
m_errorMessage = "socket creation failed: errno=" + std::to_string(SOCKET_ERRNO);
return false;
}

std::memset(&m_server, 0, sizeof(m_server));
m_server.sin_family = AF_INET;
m_server.sin_port = htons(m_port);

if (inet_aton(m_host.c_str(), &m_server.sin_addr) == 0) {
if (inet_pton(AF_INET, m_host.c_str(), &m_server.sin_addr) == 0) {
// An error code has been returned by inet_aton

// Specify the criteria for selecting the socket address structure
Expand All @@ -232,8 +290,8 @@ inline bool UDPSender::initialize() noexcept {
const int ret{getaddrinfo(m_host.c_str(), nullptr, &hints, &results)};
if (ret != 0) {
// An error code has been returned by getaddrinfo
close(m_socket);
m_socket = k_invalidFd;
SOCKET_CLOSE(m_socket);
m_socket = k_invalidSocket;
m_errorMessage = "getaddrinfo failed: err=" + std::to_string(ret) + ", msg=" + gai_strerror(ret);
return false;
}
Expand All @@ -251,16 +309,24 @@ inline bool UDPSender::initialize() noexcept {

inline void UDPSender::sendToDaemon(const std::string& message) noexcept {
// Try sending the message
const long int ret{
sendto(m_socket, message.data(), message.size(), 0, (struct sockaddr*)&m_server, sizeof(m_server))};
const auto ret = sendto(m_socket,
message.data(),
#ifdef _WIN32
static_cast<int>(message.size()),
#else
message.size(),
Comment thread
vthiery marked this conversation as resolved.
#endif
0,
(struct sockaddr*)&m_server,
sizeof(m_server));
if (ret == -1) {
m_errorMessage =
"sendto server failed: host=" + m_host + ":" + std::to_string(m_port) + ", err=" + std::strerror(errno);
m_errorMessage = "sendto server failed: host=" + m_host + ":" + std::to_string(m_port) +
", err=" + std::to_string(SOCKET_ERRNO);
}
}

inline bool UDPSender::initialized() const noexcept {
return m_socket != k_invalidFd;
return m_socket != k_invalidSocket;
}

inline void UDPSender::flush() noexcept {
Expand Down
Loading