diff --git a/Pcap++/header/PcapLiveDevice.h b/Pcap++/header/PcapLiveDevice.h index 941f6cb33..0c4d69ee1 100644 --- a/Pcap++/header/PcapLiveDevice.h +++ b/Pcap++/header/PcapLiveDevice.h @@ -82,44 +82,6 @@ namespace pcpp bool isLoopback; }; - /// @brief A worker thread that periodically calls the provided callback with updated statistics. - class StatisticsUpdateWorker - { - public: - /// @brief Constructs and starts a worker thread that periodically calls the provided callback with updated - /// statistics. - /// @param pcapDevice A pointer to the PcapLiveDevice instance to be monitored. - /// @param onStatsUpdateCallback A callback function to be called with updated statistics. - /// @param onStatsUpdateUserCookie A user-defined pointer that is passed to the callback function. - /// @param updateIntervalMs The interval in milliseconds between each callback invocation. - StatisticsUpdateWorker(PcapLiveDevice const& pcapDevice, OnStatsUpdateCallback onStatsUpdateCallback, - void* onStatsUpdateUserCookie = nullptr, unsigned int updateIntervalMs = 1000); - - /// @brief Stops the worker thread. - void stopWorker(); - - private: - struct ThreadData - { - PcapLiveDevice const* pcapDevice = nullptr; - OnStatsUpdateCallback cbOnStatsUpdate; - void* cbOnStatsUpdateUserCookie = nullptr; - unsigned int updateIntervalMs = 1000; // Default update interval is 1 second - }; - - struct SharedThreadData - { - std::atomic_bool stopRequested{ false }; - }; - - /// @brief Main function for the worker thread. - /// @remarks This function is static to allow the worker class to be movable. - static void workerMain(std::shared_ptr sharedThreadData, ThreadData threadData); - - std::shared_ptr m_SharedThreadData; - std::thread m_WorkerThread; - }; - bool m_DeviceOpened = false; // This is a second descriptor for the same device. It is needed because of a bug @@ -134,9 +96,7 @@ namespace pcpp MacAddress m_MacAddress; IPv4Address m_DefaultGateway; std::thread m_CaptureThread; - - // TODO: Cpp17 Using std::optional might be better here - std::unique_ptr m_StatisticsUpdateWorker; + std::thread m_StatsThread; // Should be set to true by the Caller for the Callee std::atomic m_StopThread; diff --git a/Pcap++/src/PcapLiveDevice.cpp b/Pcap++/src/PcapLiveDevice.cpp index 973b6de8a..42244a74c 100644 --- a/Pcap++/src/PcapLiveDevice.cpp +++ b/Pcap++/src/PcapLiveDevice.cpp @@ -230,67 +230,49 @@ namespace pcpp } } - PcapLiveDevice::StatisticsUpdateWorker::StatisticsUpdateWorker(PcapLiveDevice const& pcapDevice, - OnStatsUpdateCallback onStatsUpdateCallback, - void* onStatsUpdateUserCookie, - unsigned int updateIntervalMs) - { - // Setup thread data - m_SharedThreadData = std::make_shared(); - - ThreadData threadData; - threadData.pcapDevice = &pcapDevice; - threadData.cbOnStatsUpdate = onStatsUpdateCallback; - threadData.cbOnStatsUpdateUserCookie = onStatsUpdateUserCookie; - threadData.updateIntervalMs = updateIntervalMs; - - // Start the thread - m_WorkerThread = std::thread(&StatisticsUpdateWorker::workerMain, m_SharedThreadData, std::move(threadData)); - } - - void PcapLiveDevice::StatisticsUpdateWorker::stopWorker() - { - m_SharedThreadData->stopRequested = true; - if (m_WorkerThread.joinable()) - { - m_WorkerThread.join(); - } - } - - void PcapLiveDevice::StatisticsUpdateWorker::workerMain(std::shared_ptr sharedThreadData, - ThreadData threadData) + namespace { - if (sharedThreadData == nullptr) - { - PCPP_LOG_ERROR("Shared thread data is null"); - return; - } - - if (threadData.pcapDevice == nullptr) + struct StatisticsUpdateContext { - PCPP_LOG_ERROR("Pcap device is null"); - return; - } + OnStatsUpdateCallback cbOnStatsUpdate; + void* cbOnStatsUpdateUserCookie = nullptr; + std::chrono::milliseconds updateInterval = std::chrono::seconds(1); + }; - if (threadData.cbOnStatsUpdate == nullptr) + void statsThreadMain(std::atomic& stopFlag, internal::PcapHandle const& pcapDescriptor, + StatisticsUpdateContext context) { - PCPP_LOG_ERROR("Statistics Callback is null"); - return; - } + if (context.cbOnStatsUpdate == nullptr) + { + PCPP_LOG_ERROR("No callback provided for statistics updates"); + return; + } - PCPP_LOG_DEBUG("Started statistics thread"); + PCPP_LOG_DEBUG("Begin periodic statistics update procedure"); - PcapStats stats; - auto sleepDuration = std::chrono::milliseconds(threadData.updateIntervalMs); - while (!sharedThreadData->stopRequested) - { - threadData.pcapDevice->getStatistics(stats); - threadData.cbOnStatsUpdate(stats, threadData.cbOnStatsUpdateUserCookie); - std::this_thread::sleep_for(sleepDuration); + IPcapDevice::PcapStats stats; + while (!stopFlag.load()) + { + try + { + pcapDescriptor.getStatistics(stats); + context.cbOnStatsUpdate(stats, context.cbOnStatsUpdateUserCookie); + } + catch (const std::exception& ex) + { + PCPP_LOG_ERROR("Exception occurred while invoking statistics update callback: " << ex.what()); + break; + } + catch (...) + { + PCPP_LOG_ERROR("Unknown exception occurred while invoking statistics update callback"); + break; + } + std::this_thread::sleep_for(context.updateInterval); + } + PCPP_LOG_DEBUG("Ended periodic statistics update procedure"); } - - PCPP_LOG_DEBUG("Stopped statistics thread"); - } + } // namespace PcapLiveDevice::PcapLiveDevice(DeviceInterfaceDetails interfaceDetails, bool calculateMTU, bool calculateMacAddress, bool calculateDefaultGateway) @@ -686,10 +668,14 @@ namespace pcpp if (onStatsUpdate != nullptr && intervalInSecondsToUpdateStats > 0) { - // Due to passing a 'this' pointer, the current device object shouldn't be relocated, while the worker is - // active. - m_StatisticsUpdateWorker = std::make_unique( - *this, std::move(onStatsUpdate), onStatsUpdateUserCookie, intervalInSecondsToUpdateStats * 1000); + StatisticsUpdateContext statsContext; + + statsContext.cbOnStatsUpdate = std::move(onStatsUpdate); + statsContext.cbOnStatsUpdateUserCookie = onStatsUpdateUserCookie; + statsContext.updateInterval = std::chrono::seconds(intervalInSecondsToUpdateStats); + + m_StatsThread = std::thread(statsThreadMain, std::ref(m_StopThread), std::ref(m_PcapDescriptor), + std::move(statsContext)); PCPP_LOG_DEBUG("Successfully created stats thread for device '" << m_InterfaceDetails.name << "'."); } @@ -890,11 +876,10 @@ namespace pcpp } PCPP_LOG_DEBUG("Capture thread stopped for device '" << m_InterfaceDetails.name << "'"); - if (m_StatisticsUpdateWorker != nullptr) + if (m_StatsThread.joinable()) { PCPP_LOG_DEBUG("Stopping stats thread, waiting for it to join..."); - m_StatisticsUpdateWorker->stopWorker(); - m_StatisticsUpdateWorker.reset(); + m_StatsThread.join(); PCPP_LOG_DEBUG("Stats thread stopped for device '" << m_InterfaceDetails.name << "'"); }