From b9c9fc9ef9da273212e1e55bf27fc57e432c70b2 Mon Sep 17 00:00:00 2001 From: turtledreams <62231246+turtledreams@users.noreply.github.com> Date: Mon, 30 Oct 2023 22:27:51 +0900 Subject: [PATCH 01/33] Update README.md (#130) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5338d2..a272183 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/f3268a85b0034b68aa4fc47c9dce596c)](https://www.codacy.com/gh/Countly/countly-sdk-cpp/dashboard?utm_source=github.com&utm_medium=referral&utm_content=Countly/countly-sdk-cpp&utm_campaign=Badge_Grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/f3268a85b0034b68aa4fc47c9dce596c)](https://app.codacy.com/gh/Countly/countly-sdk-cpp/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) # Countly C++ SDK From 6537dadd4f67ddfb59b58bb826a423b11c3534d7 Mon Sep 17 00:00:00 2001 From: turtledreams <62231246+turtledreams@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:10:41 +0900 Subject: [PATCH 02/33] Release notice (#131) --- .github/workflows/release_notice.yml | 39 ++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/release_notice.yml diff --git a/.github/workflows/release_notice.yml b/.github/workflows/release_notice.yml new file mode 100644 index 0000000..e17315a --- /dev/null +++ b/.github/workflows/release_notice.yml @@ -0,0 +1,39 @@ +name: Release Notice +on: + release: + types: [published] + workflow_dispatch: +jobs: + build: + runs-on: ubuntu-latest + steps: + # To check the github context + - name: Dump Github context + env: + GITHUB_CONTEXT: ${{ toJSON(github) }} + run: echo "$GITHUB_CONTEXT" + - name: Send custom JSON data to Slack workflow + id: slack + uses: slackapi/slack-github-action@v1.23.0 + with: + # This data can be any valid JSON from a previous step in the GitHub Action + payload: | + { + "repository": "${{ github.repository }}", + "tag_name": "${{ github.event.release.tag_name }}", + "actor": "${{ github.actor }}", + "body": ${{ toJSON(github.event.release.body) }}, + "html_url": "${{ github.event.release.html_url }}" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_RELEASE }} + - name: Send custom JSON data to Discord + uses: sarisia/actions-status-discord@v1.13.0 + with: + webhook: ${{ secrets.DISCORD_WEBHOOK_URL }} + nodetail: true + title: New ${{ github.repository }} version ${{ github.event.release.tag_name }} published by ${{ github.actor }} + description: | + Release URL: ${{ github.event.release.html_url }} + Click [here](https://github.com/Countly/countly-server/blob/master/CHANGELOG.md) to view the change log. + `${{ github.event.release.body }}` From 74ae31cd350c36c50283dbde4fd2c5af640541dc Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray <57103426+arifBurakDemiray@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:35:43 +0000 Subject: [PATCH 03/33] [CPP] Add warnings to all SDK example apps that would check if propper credentials have been set or it still has the defaults. (#132) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: warn for default * Update example_integration.cpp * Update example_integration.cpp --------- Co-authored-by: Artūrs Kadiķis --- examples/example_integration.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/example_integration.cpp b/examples/example_integration.cpp index 1497337..e8306aa 100644 --- a/examples/example_integration.cpp +++ b/examples/example_integration.cpp @@ -47,7 +47,14 @@ int main() { ct.SetMetrics("Windows 10", "10.22", "Mac", "800x600", "Carrier", "1.0"); // start the SDK (initialize the SDK) - ct.start("YOUR_APP_KEY", "https://try.count.ly", 443, true); + string _appKey = "YOUR_APP_KEY"; + string _serverUrl = "https://your.server.ly"; + + if(_appKey.compare("YOUR_APP_KEY") == 0 || _serverUrl.compare("https://your.server.ly") == 0) { + cerr << "Please do not use default set of app key and server url" << endl; + } + + ct.start(_appKey, _serverUrl, 443, true); ct.setAutomaticSessionUpdateInterval(5);// The value is set so low just for internal validation. ct.setMaxRQProcessingBatchSize(2); // in most cases not needed to be set. The value is set so low just for internal validation From ac5f2c7ee6f0c68c299dab8915f6f7e0303c9f2f Mon Sep 17 00:00:00 2001 From: turtledreams Date: Thu, 14 Nov 2024 20:17:39 +0900 Subject: [PATCH 04/33] Http client example --- examples/example_integration.cpp | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/examples/example_integration.cpp b/examples/example_integration.cpp index e8306aa..f606bff 100644 --- a/examples/example_integration.cpp +++ b/examples/example_integration.cpp @@ -2,8 +2,8 @@ #include #include #include +#include #include - using namespace std; using namespace cly; @@ -30,6 +30,27 @@ void printLog(LogLevel level, const string &msg) { cout << lvl << msg << endl; } +// // Custom HTTP client +// HTTPResponse customClient(bool use_post, const std::string &path, const std::string &data) { +// HTTPResponse response; +// response.success = false; +// cout << "Will send the request!!" << endl; + +// // asynchronous operation +// std::thread requestThread([&response]() { +// std::this_thread::sleep_for(std::chrono::seconds(2)); // network delay +// // you should add extra logic to check if request is successful normally +// response.success = true; +// response.data = R"({"some":"data"})"; // response data +// }); + +// // Wait for the request to complete +// requestThread.join(); +// cout << "Got RESPONSE:[" + response.data.dump() + "]" << endl; + +// return response; +// } + int main() { cout << "Sample App" << endl; Countly &ct = Countly::getInstance(); @@ -38,13 +59,17 @@ int main() { // Please refer to the documentation for more information: // https://support.count.ly/hc/en-us/articles/4416163384857-C- - ct.alwaysUsePost(true); + // Custom HTTP client + // HTTPClientFunction clientPtr = customClient; + // ct.setHTTPClient(clientPtr); + // ct.alwaysUsePost(true); + ct.setLogger(printLog); ct.SetPath("databaseFileName.db"); // this will be only built into account if the correct configurations are set ct.setDeviceID("test-device-id"); // ct.setSalt("test-salt"); // OS, OS_version, device, resolution, carrier, app_version); - ct.SetMetrics("Windows 10", "10.22", "Mac", "800x600", "Carrier", "1.0"); + // ct.SetMetrics("Windows 10", "10.22", "Mac", "800x600", "Carrier", "1.0"); // start the SDK (initialize the SDK) string _appKey = "YOUR_APP_KEY"; From ccc97db8234cdfaea159bd138b85adfba5c71e5a Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 13:12:13 +0300 Subject: [PATCH 05/33] feat: add changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d51c0f2..028e8da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## XX.XX.XX +- Added "checkRQSize" function to return the current number of requests in the queue. + ## 23.2.0 - Request queue processing now is limited to 100 requests at a time - Added 'setEventsToRQThreshold' method that sets the number of events after which all events will be sent to the RQ. Default value is set to 100. From b2fcfe9587ec904400fa3255a6ddf34fc5585a8a Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 13:12:38 +0300 Subject: [PATCH 06/33] feat: function to header files --- include/countly.hpp | 5 +++++ include/countly/request_module.hpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/include/countly.hpp b/include/countly.hpp index 9c95583..610940b 100644 --- a/include/countly.hpp +++ b/include/countly.hpp @@ -105,6 +105,11 @@ class Countly : public cly::CountlyDelegates { */ int checkEQSize(); + /* + * Checks and returns the size of the request queue in memory or persistent storage. + */ + int checkRQSize(); + /** * Checks and returns the size of the event queue in persistent storage. */ diff --git a/include/countly/request_module.hpp b/include/countly/request_module.hpp index 5b013e6..7d494a1 100644 --- a/include/countly/request_module.hpp +++ b/include/countly/request_module.hpp @@ -35,6 +35,8 @@ class RequestModule { */ void clearRequestQueue(); + long long RQSize(); + private: class RequestModuleImpl; std::unique_ptr impl; From 9326a3b5122888937d0e4bef359986fe16886495 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 13:13:06 +0300 Subject: [PATCH 07/33] feat: function impl --- src/countly.cpp | 18 +++++++++++++++--- src/request_module.cpp | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/countly.cpp b/src/countly.cpp index bb2188f..fabbbe2 100644 --- a/src/countly.cpp +++ b/src/countly.cpp @@ -520,7 +520,7 @@ void Countly::checkAndSendEventToRQ() { void Countly::setMaxEvents(size_t value) { log(LogLevel::WARNING, "[Countly][setMaxEvents/SetMaxEventsPerMessage] These calls are deprecated. Use 'setEventsToRQThreshold' instead."); - setEventsToRQThreshold(value); + setEventsToRQThreshold(static_cast(value)); } void Countly::setEventsToRQThreshold(int value) { @@ -824,12 +824,24 @@ int Countly::checkEQSize() { return event_count; } +int Countly::checkRQSize() { + log(LogLevel::DEBUG, "[Countly][checkRQSize]"); + int request_count = -1; + if (!is_sdk_initialized) { + log(LogLevel::DEBUG, "[Countly][checkRQSize] SDK is not initialized."); + return request_count; + } + + request_count = static_cast(requestModule->RQSize()); + return request_count; +} + #ifndef COUNTLY_USE_SQLITE int Countly::checkMemoryEQSize() { log(LogLevel::DEBUG, "[Countly][checkMemoryEQSize] Checking event queue size in memory"); int result = 0; mutex->lock(); - result = event_queue.size(); + result = static_cast(event_queue.size()); mutex->unlock(); return result; } @@ -1205,4 +1217,4 @@ void Countly::updateRemoteConfigExcept(std::string *keys, size_t key_count) { std::thread _thread(&Countly::_updateRemoteConfigWithSpecificValues, this, data); _thread.detach(); } -} // namespace cly \ No newline at end of file +} // namespace cly diff --git a/src/request_module.cpp b/src/request_module.cpp index 7b23996..1770c86 100644 --- a/src/request_module.cpp +++ b/src/request_module.cpp @@ -341,4 +341,5 @@ HTTPResponse RequestModule::sendHTTP(std::string path, std::string data) { return response; #endif } +long long RequestModule::RQSize() { return impl->_storageModule->RQCount(); } } // namespace cly From 4d4ff54c44105c79d4ce8386a7e7c10606a883f0 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 13:26:34 +0300 Subject: [PATCH 08/33] feat: add manual session control --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d51c0f2..50e36d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## XX.XX.XX +- Added manual session control, which can be enabled via "Countly::enableManualSessionControl". + ## 23.2.0 - Request queue processing now is limited to 100 requests at a time - Added 'setEventsToRQThreshold' method that sets the number of events after which all events will be sent to the RQ. Default value is set to 100. From 1a212201f19688f34d2aaafea5f828ca00199275 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 13:26:54 +0300 Subject: [PATCH 09/33] feat: add manual session control impl --- include/countly.hpp | 2 ++ include/countly/countly_configuration.hpp | 2 ++ src/countly.cpp | 14 ++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/include/countly.hpp b/include/countly.hpp index 9c95583..029a35b 100644 --- a/include/countly.hpp +++ b/include/countly.hpp @@ -56,6 +56,8 @@ class Countly : public cly::CountlyDelegates { void setSha256(cly::SHA256Function fun); + void enableManualSessionControl(); + void setHTTPClient(HTTPClientFunction fun); void setMetrics(const std::string &os, const std::string &os_version, const std::string &device, const std::string &resolution, const std::string &carrier, const std::string &app_version); diff --git a/include/countly/countly_configuration.hpp b/include/countly/countly_configuration.hpp index 0b4aa4f..c98d500 100644 --- a/include/countly/countly_configuration.hpp +++ b/include/countly/countly_configuration.hpp @@ -68,6 +68,8 @@ struct CountlyConfiguration { SHA256Function sha256_function = nullptr; + bool manualSessionControl = false; + HTTPClientFunction http_client_function = nullptr; nlohmann::json metrics; diff --git a/src/countly.cpp b/src/countly.cpp index bb2188f..a669273 100644 --- a/src/countly.cpp +++ b/src/countly.cpp @@ -128,6 +128,20 @@ void Countly::setSha256(SHA256Function fun) { mutex->unlock(); } +/** + * Enable manual session handling. + */ +void Countly::enableManualSessionControl() { + if (is_sdk_initialized) { + log(LogLevel::WARNING, "[Countly][enableManualSessionControl] You can not enable manual session control after SDK initialization."); + return; + } + + mutex->lock(); + configuration->manualSessionControl = true; + mutex->unlock(); +} + void Countly::setMetrics(const std::string &os, const std::string &os_version, const std::string &device, const std::string &resolution, const std::string &carrier, const std::string &app_version) { if (is_sdk_initialized) { log(LogLevel::WARNING, "[Countly][setMetrics] You can not set metrics after SDK initialization."); From 82f10eb281a46e0be1ccedb18ab4e4c6d3d3e99d Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:25:26 +0300 Subject: [PATCH 10/33] feat: introduce pack events --- include/countly.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/countly.hpp b/include/countly.hpp index 029a35b..b0bc6a1 100644 --- a/include/countly.hpp +++ b/include/countly.hpp @@ -321,6 +321,7 @@ class Countly : public cly::CountlyDelegates { std::chrono::system_clock::duration getSessionDuration(); void updateLoop(); + void packEvents(); bool began_session = false; bool is_being_disposed = false; bool is_sdk_initialized = false; From afdcd28db2f0484d99bc1c3e55919fc6b48a79aa Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:26:25 +0300 Subject: [PATCH 11/33] feat: manual sessions impl --- src/countly.cpp | 94 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/src/countly.cpp b/src/countly.cpp index a669273..204fa10 100644 --- a/src/countly.cpp +++ b/src/countly.cpp @@ -346,17 +346,19 @@ void Countly::_changeDeviceIdWithoutMerge(const std::string &value) { // send all event to server and end current session of old user flushEvents(); - if (began_session) { + if(!configuration->manualSessionControl){ endSession(); - mutex->lock(); - session_params["device_id"] = value; - mutex->unlock(); + } + + mutex->lock(); + session_params["device_id"] = value; + mutex->unlock(); + + // start a new session for new user + if(!configuration->manualSessionControl){ beginSession(); - } else { - mutex->lock(); - session_params["device_id"] = value; - mutex->unlock(); } + } #pragma endregion Device Id @@ -401,7 +403,7 @@ void Countly::start(const std::string &app_key, const std::string &host, int por log(LogLevel::INFO, "[Countly][start] '_WIN32' is not defined"); #endif - enable_automatic_session = start_thread; + enable_automatic_session = start_thread && !configuration->manualSessionControl; start_thread = true; if (port < 0 || port > 65535) { @@ -436,9 +438,11 @@ void Countly::start(const std::string &app_key, const std::string &host, int por if (!running) { - mutex->unlock(); - beginSession(); - mutex->lock(); + if(!configuration->manualSessionControl){ + mutex->unlock(); + beginSession(); + mutex->lock(); + } if (start_thread) { stop_thread = false; @@ -465,7 +469,7 @@ void Countly::startOnCloud(const std::string &app_key) { void Countly::stop() { _deleteThread(); - if (began_session) { + if (!configuration->manualSessionControl) { endSession(); } } @@ -607,8 +611,16 @@ bool Countly::attemptSessionUpdateEQ() { return false; } #endif - - return !updateSession(); + bool result; + if(!configuration->manualSessionControl){ + result = !updateSession(); + } else { + log(LogLevel::WARNING, "[Countly][attemptSessionUpdateEQ] SDK is in manual session control mode. Please start a session first."); + result = false; + } + + packEvents(); + return result; } void Countly::clearEQInternal() { @@ -670,6 +682,7 @@ bool Countly::beginSession() { log(LogLevel::INFO, "[Countly][beginSession]"); if (began_session) { mutex->unlock(); + log(LogLevel::DEBUG, "[Countly][beginSession] Session is already active."); return true; } @@ -725,6 +738,10 @@ bool Countly::updateSession() { mutex->lock(); if (!began_session) { mutex->unlock(); + if(configuration->manualSessionControl){ + log(LogLevel::WARNING, "[Countly][updateSession] SDK is in manual session control mode and there is no active session. Please start a session first."); + return false; + } if (!beginSession()) { // if beginSession fails, we should not try to update session return false; @@ -732,8 +749,31 @@ bool Countly::updateSession() { mutex->lock(); began_session = true; } + + mutex->unlock(); + auto duration = std::chrono::duration_cast(getSessionDuration()); + mutex->lock(); - // events array + // report session duration if it is greater than the configured session duration value + if (duration.count() >= configuration->sessionDuration) { + log(LogLevel::DEBUG, "[Countly][updateSession] sending session update."); + std::map data = {{"app_key", session_params["app_key"].get()}, {"device_id", session_params["device_id"].get()}, {"session_duration", std::to_string(duration.count())}}; + requestModule->addRequestToQueue(data); + + last_sent_session_request += duration; + } + } catch (const std::system_error &e) { + std::ostringstream log_message; + log_message << "update session, error: " << e.what(); + log(LogLevel::FATAL, log_message.str()); + } + mutex->unlock(); + return true; +} + +void Countly::packEvents() { + try { + // events array nlohmann::json events = nlohmann::json::array(); std::string event_ids; mutex->unlock(); @@ -754,20 +794,7 @@ bool Countly::updateSession() { } else { log(LogLevel::DEBUG, "[Countly][updateSession] EQ empty."); } - mutex->unlock(); - auto duration = std::chrono::duration_cast(getSessionDuration()); - mutex->lock(); - - // report session duration if it is greater than the configured session duration value - if (duration.count() >= configuration->sessionDuration) { - log(LogLevel::DEBUG, "[Countly][updateSession] sending session update."); - std::map data = {{"app_key", session_params["app_key"].get()}, {"device_id", session_params["device_id"].get()}, {"session_duration", std::to_string(duration.count())}}; - requestModule->addRequestToQueue(data); - - last_sent_session_request += duration; - } - - // report events if there are any to request queue + // report events if there are any to request queue if (!no_events) { sendEventsToRQ(events); } @@ -788,9 +815,9 @@ bool Countly::updateSession() { log(LogLevel::FATAL, log_message.str()); } mutex->unlock(); - return true; } + void Countly::sendEventsToRQ(const nlohmann::json &events) { log(LogLevel::DEBUG, "[Countly][sendEventsToRQ] Sending events to RQ."); std::map data = {{"app_key", session_params["app_key"].get()}, {"device_id", session_params["device_id"].get()}, {"events", events.dump()}}; @@ -799,6 +826,10 @@ void Countly::sendEventsToRQ(const nlohmann::json &events) { bool Countly::endSession() { log(LogLevel::INFO, "[Countly][endSession]"); + if(!began_session) { + log(LogLevel::DEBUG, "[Countly][endSession] There is no active session to end."); + return true; + } const std::chrono::system_clock::time_point now = Countly::getTimestamp(); const auto timestamp = std::chrono::duration_cast(now.time_since_epoch()); const auto duration = std::chrono::duration_cast(getSessionDuration(now)); @@ -1127,6 +1158,7 @@ void Countly::updateLoop() { if (enable_automatic_session) { updateSession(); } + packEvents(); requestModule->processQueue(mutex); } mutex->lock(); From 417944da2df7b3ef2877e0eca3e997d975fd1e28 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:29:40 +0300 Subject: [PATCH 12/33] feat: update runners --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b498382..9bf03d0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,8 +14,8 @@ jobs: fail-fast: false matrix: os: - - ubuntu-20.04 - - macos-11.0 + - ubuntu-24.04 + - macos-15 - windows-2019 include: From 53c168557592ed54e0d64ad3a4a935373451d1a9 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:34:22 +0300 Subject: [PATCH 13/33] feat: update runner scripts --- .github/workflows/tests.yml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9bf03d0..0d82d46 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,14 +27,23 @@ jobs: choco install -y visualstudio2017-workload-vctools choco upgrade -y visualstudio2017-workload-vctools make: msbuild countly-tests.vcxproj -t:rebuild -verbosity:diag -property:Configuration=Release && .\Release\countly-tests.exe - - os: macos-11.0 + - os: macos-15 cmake-install: "brew install cmake" dependencies: "brew install openssl" make: make ./countly-tests && ./countly-tests - - os: ubuntu-20.04 - cmake-install: "sudo apt-get update && sudo apt-get install -y cmake" - dependencies: "sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev" - make: make ./countly-tests && ./countly-tests + - os: ubuntu-24.04 + cmake-install: | + sudo apt-get update && sudo apt-get install -y \ + cmake \ + g++ \ + make \ + libcurl4-openssl-dev \ + libssl-dev + build-and-test: | + mkdir -p build && cd build + cmake .. + make countly-tests + ./countly-tests steps: - name: Checkout code From c8897f18c858a6ac32de6f99de78174e49232d7f Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:36:38 +0300 Subject: [PATCH 14/33] feat: update runner scripts for windws --- .github/workflows/tests.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0d82d46..397fd7d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,16 +16,16 @@ jobs: os: - ubuntu-24.04 - macos-15 - - windows-2019 + - windows-2022 include: - - os: windows-2019 - cmake-generator: -G "Visual Studio 16 2019" -A x64 + - os: windows-2022 + cmake-generator: -G "Visual Studio 17 2022" -A x64 cmake-install: "choco install -y cmake" dependencies: | choco install -y openssl - choco install -y visualstudio2017-workload-vctools - choco upgrade -y visualstudio2017-workload-vctools + choco install -y visualstudio2022-workload-vctools + choco upgrade -y visualstudio2022-workload-vctools make: msbuild countly-tests.vcxproj -t:rebuild -verbosity:diag -property:Configuration=Release && .\Release\countly-tests.exe - os: macos-15 cmake-install: "brew install cmake" From f1d4ea48160e287b4e70b801bd174ef12a3ee07f Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:43:32 +0300 Subject: [PATCH 15/33] feat: update runner scripts --- .github/workflows/tests.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 397fd7d..b29a99b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,7 +28,10 @@ jobs: choco upgrade -y visualstudio2022-workload-vctools make: msbuild countly-tests.vcxproj -t:rebuild -verbosity:diag -property:Configuration=Release && .\Release\countly-tests.exe - os: macos-15 - cmake-install: "brew install cmake" + cmake-install: | + curl -LO https://github.com/Kitware/CMake/releases/download/v3.31.9/cmake-3.31.9-macos-universal.tar.gz + tar -xzf cmake-3.31.9-macos-universal.tar.gz + sudo cp -R cmake-3.31.9-macos-universal/CMake.app/Contents/bin/* /usr/local/bin/ dependencies: "brew install openssl" make: make ./countly-tests && ./countly-tests - os: ubuntu-24.04 @@ -39,7 +42,7 @@ jobs: make \ libcurl4-openssl-dev \ libssl-dev - build-and-test: | + make: | mkdir -p build && cd build cmake .. make countly-tests @@ -61,7 +64,7 @@ jobs: run: ${{ matrix.dependencies }} - name: Set up MSVC - if: matrix.os == 'windows-2019' + if: matrix.os == 'windows-2022' uses: microsoft/setup-msbuild@v1 - name: Build and run tests From 4a7f5c8c894543583cf72ce43c0033c08bbc2daa Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:46:58 +0300 Subject: [PATCH 16/33] feat: update runner scripts --- .github/workflows/tests.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b29a99b..61484eb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,10 +28,7 @@ jobs: choco upgrade -y visualstudio2022-workload-vctools make: msbuild countly-tests.vcxproj -t:rebuild -verbosity:diag -property:Configuration=Release && .\Release\countly-tests.exe - os: macos-15 - cmake-install: | - curl -LO https://github.com/Kitware/CMake/releases/download/v3.31.9/cmake-3.31.9-macos-universal.tar.gz - tar -xzf cmake-3.31.9-macos-universal.tar.gz - sudo cp -R cmake-3.31.9-macos-universal/CMake.app/Contents/bin/* /usr/local/bin/ + cmake-install: "brew install cmake" dependencies: "brew install openssl" make: make ./countly-tests && ./countly-tests - os: ubuntu-24.04 @@ -72,3 +69,5 @@ jobs: cmake -DCOUNTLY_BUILD_TESTS=1 -B build . ${{ matrix.cmake-generator }} cd build ${{ matrix.make }} + env: + CMAKE_POLICY_VERSION_MINIMUM: 3.0 From 7c68875f966c6ddb503be4b0171ec4d1605b3720 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:48:17 +0300 Subject: [PATCH 17/33] feat: update runner scripts --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 61484eb..4c77b47 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -70,4 +70,4 @@ jobs: cd build ${{ matrix.make }} env: - CMAKE_POLICY_VERSION_MINIMUM: 3.0 + CMAKE_POLICY_VERSION_MINIMUM: 3.31 From 2a0ad42dda3333130687538ee5b667f6deb0fe83 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:52:53 +0300 Subject: [PATCH 18/33] fix: a 86 interrupt instruction forapple in doctest --- tests/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/main.cpp b/tests/main.cpp index 613ea1e..c299c8b 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -6,6 +6,7 @@ #include #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#define DOCTEST_CONFIG_NO_BREAK_INTO_DEBUGGER #include "doctest.h" From 0e93449634e3bbf1c4733246b0b0185448c80360 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:57:45 +0300 Subject: [PATCH 19/33] fix: a 86 interrupt instruction forapple in doctest --- .github/workflows/tests.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4c77b47..29289f6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,7 +30,11 @@ jobs: - os: macos-15 cmake-install: "brew install cmake" dependencies: "brew install openssl" - make: make ./countly-tests && ./countly-tests + make: | + mkdir -p build && cd build + cmake -DCOUNTLY_BUILD_TESTS=1 .. + cmake --build . --config Release + ctest --output-on-failure - os: ubuntu-24.04 cmake-install: | sudo apt-get update && sudo apt-get install -y \ @@ -41,9 +45,9 @@ jobs: libssl-dev make: | mkdir -p build && cd build - cmake .. - make countly-tests - ./countly-tests + cmake -DCOUNTLY_BUILD_TESTS=1 .. + cmake --build . --config Release + ctest --output-on-failure steps: - name: Checkout code From 8c05027a394f8eb8dc9ec73af941a98be1c72a87 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 14:59:08 +0300 Subject: [PATCH 20/33] fix: a 86 interrupt instruction forapple in doctest --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 29289f6..a7040d9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,7 +32,7 @@ jobs: dependencies: "brew install openssl" make: | mkdir -p build && cd build - cmake -DCOUNTLY_BUILD_TESTS=1 .. + cmake -DCOUNTLY_BUILD_TESTS=1 cmake --build . --config Release ctest --output-on-failure - os: ubuntu-24.04 @@ -45,7 +45,7 @@ jobs: libssl-dev make: | mkdir -p build && cd build - cmake -DCOUNTLY_BUILD_TESTS=1 .. + cmake -DCOUNTLY_BUILD_TESTS=1 cmake --build . --config Release ctest --output-on-failure From e74b615ba3fb34e7530868f7f8ae275d292fb953 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 15:01:33 +0300 Subject: [PATCH 21/33] fix: a 86 interrupt instruction forapple in doctest --- .github/workflows/tests.yml | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a7040d9..aab3cf6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,11 +30,7 @@ jobs: - os: macos-15 cmake-install: "brew install cmake" dependencies: "brew install openssl" - make: | - mkdir -p build && cd build - cmake -DCOUNTLY_BUILD_TESTS=1 - cmake --build . --config Release - ctest --output-on-failure + make: make ./countly-tests && ./countly-tests - os: ubuntu-24.04 cmake-install: | sudo apt-get update && sudo apt-get install -y \ @@ -43,11 +39,7 @@ jobs: make \ libcurl4-openssl-dev \ libssl-dev - make: | - mkdir -p build && cd build - cmake -DCOUNTLY_BUILD_TESTS=1 - cmake --build . --config Release - ctest --output-on-failure + make: make ./countly-tests && ./countly-tests steps: - name: Checkout code From b8b050d50eb42738c3bb5f5f69206276c81f0803 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 15:06:37 +0300 Subject: [PATCH 22/33] feat: update doctest v --- vendor/doctest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/doctest b/vendor/doctest index f13a00c..1da23a3 160000 --- a/vendor/doctest +++ b/vendor/doctest @@ -1 +1 @@ -Subproject commit f13a00cc27ed3c1ec4f755572ab7556c4cb01716 +Subproject commit 1da23a3e8119ec5cce4f9388e91b065e20bf06f5 From 816a5d00e7c95cec523d861afc269ca68d487244 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 15:17:04 +0300 Subject: [PATCH 23/33] feat: update windows runner --- .github/workflows/tests.yml | 13 ++++++------- tests/main.cpp | 2 ++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aab3cf6..7d334f5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -21,12 +21,9 @@ jobs: include: - os: windows-2022 cmake-generator: -G "Visual Studio 17 2022" -A x64 - cmake-install: "choco install -y cmake" - dependencies: | - choco install -y openssl - choco install -y visualstudio2022-workload-vctools - choco upgrade -y visualstudio2022-workload-vctools - make: msbuild countly-tests.vcxproj -t:rebuild -verbosity:diag -property:Configuration=Release && .\Release\countly-tests.exe + cmake-install: "" #Already installed on hosted runner + dependencies: "" #Already installed on hosted runner + make: msbuild countly-tests.vcxproj -t:rebuild -verbosity:diag -property:Configuration=Release && .\Release\countly-tests.exe - os: macos-15 cmake-install: "brew install cmake" dependencies: "brew install openssl" @@ -36,7 +33,9 @@ jobs: sudo apt-get update && sudo apt-get install -y \ cmake \ g++ \ - make \ + make + dependencies: | + sudo apt-get update && sudo apt-get install -y \ libcurl4-openssl-dev \ libssl-dev make: make ./countly-tests && ./countly-tests diff --git a/tests/main.cpp b/tests/main.cpp index c299c8b..499eb8b 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -6,7 +6,9 @@ #include #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#ifdef __APPLE__ #define DOCTEST_CONFIG_NO_BREAK_INTO_DEBUGGER +#endif #include "doctest.h" From 007bd15e891b8fc83901f1aee4bca2d1f777b474 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 15:21:54 +0300 Subject: [PATCH 24/33] fix: reduce ubuntu v because latest break --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7d334f5..17cfc74 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: - - ubuntu-24.04 + - ubuntu-22.04 - macos-15 - windows-2022 @@ -28,7 +28,7 @@ jobs: cmake-install: "brew install cmake" dependencies: "brew install openssl" make: make ./countly-tests && ./countly-tests - - os: ubuntu-24.04 + - os: ubuntu-22.04 cmake-install: | sudo apt-get update && sudo apt-get install -y \ cmake \ From 3a5e7f13d76f958304a7fa6d93ca650fd7c3aab7 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 15:24:48 +0300 Subject: [PATCH 25/33] refactor: remove unnecessary downlaods --- .github/workflows/tests.yml | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 17cfc74..51e737b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,19 +25,12 @@ jobs: dependencies: "" #Already installed on hosted runner make: msbuild countly-tests.vcxproj -t:rebuild -verbosity:diag -property:Configuration=Release && .\Release\countly-tests.exe - os: macos-15 - cmake-install: "brew install cmake" - dependencies: "brew install openssl" + cmake-install: "" #Already installed on hosted runner + dependencies: "" #Already installed on hosted runner make: make ./countly-tests && ./countly-tests - os: ubuntu-22.04 - cmake-install: | - sudo apt-get update && sudo apt-get install -y \ - cmake \ - g++ \ - make - dependencies: | - sudo apt-get update && sudo apt-get install -y \ - libcurl4-openssl-dev \ - libssl-dev + cmake-install: "" #Already installed on hosted runner + dependencies: "" #Already installed on hosted runner make: make ./countly-tests && ./countly-tests steps: From 8dcf3350cbfe726ff33700c0fb5f8c9f7da93487 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 15:26:09 +0300 Subject: [PATCH 26/33] refactor: remove unnecessary downlaods --- .github/workflows/tests.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 51e737b..80cc101 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,7 +30,10 @@ jobs: make: make ./countly-tests && ./countly-tests - os: ubuntu-22.04 cmake-install: "" #Already installed on hosted runner - dependencies: "" #Already installed on hosted runner + dependencies: | + sudo apt-get update && sudo apt-get install -y \ + libcurl4-openssl-dev \ + libssl-dev make: make ./countly-tests && ./countly-tests steps: From e9465efb655b1be6996be8ba637e62b27e7bc067 Mon Sep 17 00:00:00 2001 From: turtledreams <62231246+turtledreams@users.noreply.github.com> Date: Tue, 30 Sep 2025 22:28:46 +0900 Subject: [PATCH 27/33] Update example_integration.cpp --- examples/example_integration.cpp | 117 +++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 15 deletions(-) diff --git a/examples/example_integration.cpp b/examples/example_integration.cpp index f606bff..5e7410d 100644 --- a/examples/example_integration.cpp +++ b/examples/example_integration.cpp @@ -2,8 +2,8 @@ #include #include #include -#include #include + using namespace std; using namespace cly; @@ -30,24 +30,112 @@ void printLog(LogLevel level, const string &msg) { cout << lvl << msg << endl; } -// // Custom HTTP client +// // Callback function to write response data +// static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { +// ((std::string*)userp)->append((char*)contents, size * nmemb); +// return size * nmemb; +// } + +// // Custom HTTP client for macOS // HTTPResponse customClient(bool use_post, const std::string &path, const std::string &data) { // HTTPResponse response; // response.success = false; -// cout << "Will send the request!!" << endl; +// cout << "Making real HTTP request to: " << path << endl; -// // asynchronous operation -// std::thread requestThread([&response]() { -// std::this_thread::sleep_for(std::chrono::seconds(2)); // network delay -// // you should add extra logic to check if request is successful normally -// response.success = true; -// response.data = R"({"some":"data"})"; // response data -// }); +// CURL *curl; +// CURLcode res; +// std::string readBuffer; -// // Wait for the request to complete -// requestThread.join(); -// cout << "Got RESPONSE:[" + response.data.dump() + "]" << endl; +// curl = curl_easy_init(); +// if(curl) { +// // Set URL +// curl_easy_setopt(curl, CURLOPT_URL, path.c_str()); + +// // Set callback function to write data +// curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); +// curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + +// // Set timeout +// curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); + +// // Follow redirects +// curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + +// // SSL verification (set to 0 for testing, 1 for production) +// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); +// curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + +// // Set User-Agent +// curl_easy_setopt(curl, CURLOPT_USERAGENT, "Countly-SDK-CPP/1.0"); + +// if (use_post) { +// // Set POST method +// curl_easy_setopt(curl, CURLOPT_POST, 1L); + +// if (!data.empty()) { +// // Set POST data +// curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); +// curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.length()); +// } + +// // Set content type for POST +// struct curl_slist *headers = NULL; +// headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded"); +// curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); +// } else { +// // For GET requests, append data as query parameters +// if (!data.empty()) { +// std::string fullUrl = path; +// fullUrl += (path.find('?') != std::string::npos) ? "&" : "?"; +// fullUrl += data; +// curl_easy_setopt(curl, CURLOPT_URL, fullUrl.c_str()); +// } +// } + +// // Perform the request +// res = curl_easy_perform(curl); + +// if(res != CURLE_OK) { +// cout << "curl_easy_perform() failed: " << curl_easy_strerror(res) << endl; +// } else { +// // Get response code +// long response_code; +// curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + +// cout << "HTTP Response Code: " << response_code << endl; +// cout << "Raw response: " << readBuffer << endl; + +// // Check if HTTP request was successful (2xx status codes) +// if (response_code >= 200 && response_code < 300) { +// // Check if response contains { result: 'Success' } or {"result":"Success"} +// if (readBuffer.find("\"result\"") != std::string::npos && +// (readBuffer.find("\"Success\"") != std::string::npos || +// readBuffer.find("'Success'") != std::string::npos)) { +// response.success = true; +// cout << "Success response detected!" << endl; +// } else { +// cout << "Response does not indicate success" << endl; +// } + +// // Parse as JSON +// try { +// response.data = nlohmann::json::parse(readBuffer); +// } catch (const std::exception& e) { +// cout << "Failed to parse JSON response: " << e.what() << endl; +// response.data = nlohmann::json::object(); +// } +// } else { +// cout << "HTTP request failed with code: " << response_code << endl; +// } +// } + +// // Cleanup +// curl_easy_cleanup(curl); +// } else { +// cout << "Failed to initialize curl" << endl; +// } +// cout << "Request completed. Success: " << (response.success ? "true" : "false") << endl; // return response; // } @@ -63,13 +151,12 @@ int main() { // HTTPClientFunction clientPtr = customClient; // ct.setHTTPClient(clientPtr); // ct.alwaysUsePost(true); - ct.setLogger(printLog); ct.SetPath("databaseFileName.db"); // this will be only built into account if the correct configurations are set ct.setDeviceID("test-device-id"); // ct.setSalt("test-salt"); // OS, OS_version, device, resolution, carrier, app_version); - // ct.SetMetrics("Windows 10", "10.22", "Mac", "800x600", "Carrier", "1.0"); + ct.SetMetrics("Windows 10", "10.22", "Mac", "800x600", "Carrier", "1.0"); // start the SDK (initialize the SDK) string _appKey = "YOUR_APP_KEY"; From 4434b54c299be204cdb1a18e54aee3a768fa5df8 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 30 Sep 2025 16:31:01 +0300 Subject: [PATCH 28/33] feat: 23.2.1 --- CHANGELOG.md | 4 ++-- include/countly/constants.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87f54c1..f4b0214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -## XX.XX.XX -- Added manual session control, which can be enabled via "Countly::enableManualSessionControl". +## 23.2.1 +- Added manual session control via "Countly::enableManualSessionControl". When enabled, automatic session calls are ignored, while manual calls remain usable for finer control. - Added "checkRQSize" function to return the current number of requests in the queue. ## 23.2.0 diff --git a/include/countly/constants.hpp b/include/countly/constants.hpp index 99471ad..ec80ee2 100644 --- a/include/countly/constants.hpp +++ b/include/countly/constants.hpp @@ -13,7 +13,7 @@ #include #define COUNTLY_SDK_NAME "cpp-native-unknown" -#define COUNTLY_SDK_VERSION "23.2.0" +#define COUNTLY_SDK_VERSION "23.2.1" #define COUNTLY_POST_THRESHOLD 2000 #define COUNTLY_KEEPALIVE_INTERVAL 3000 #define COUNTLY_MAX_EVENTS_DEFAULT 200 From 92f795e907828a3786423d38a1a6445758fe5c45 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Wed, 1 Oct 2025 09:18:51 +0300 Subject: [PATCH 29/33] revert: some changes --- src/countly.cpp | 60 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/src/countly.cpp b/src/countly.cpp index 4e55887..4fce47e 100644 --- a/src/countly.cpp +++ b/src/countly.cpp @@ -403,7 +403,7 @@ void Countly::start(const std::string &app_key, const std::string &host, int por log(LogLevel::INFO, "[Countly][start] '_WIN32' is not defined"); #endif - enable_automatic_session = start_thread && !configuration->manualSessionControl; + enable_automatic_session = start_thread; start_thread = true; if (port < 0 || port > 65535) { @@ -611,16 +611,12 @@ bool Countly::attemptSessionUpdateEQ() { return false; } #endif - bool result; if(!configuration->manualSessionControl){ - result = !updateSession(); + return !updateSession(); } else { - log(LogLevel::WARNING, "[Countly][attemptSessionUpdateEQ] SDK is in manual session control mode. Please start a session first."); - result = false; + packEvents(); + return false; } - - packEvents(); - return result; } void Countly::clearEQInternal() { @@ -749,7 +745,28 @@ bool Countly::updateSession() { mutex->lock(); began_session = true; } - + + // events array + nlohmann::json events = nlohmann::json::array(); + std::string event_ids; + mutex->unlock(); + bool no_events = checkEQSize() > 0 ? false : true; + mutex->lock(); + + if (!no_events) { +#ifndef COUNTLY_USE_SQLITE + for (const auto &event_json : event_queue) { + events.push_back(nlohmann::json::parse(event_json)); + } +#else + // TODO: If database_path was empty there was return false here + mutex->unlock(); + fillEventsIntoJson(events, event_ids); + mutex->lock(); +#endif + } else { + log(LogLevel::DEBUG, "[Countly][updateSession] EQ empty."); + } mutex->unlock(); auto duration = std::chrono::duration_cast(getSessionDuration()); mutex->lock(); @@ -762,6 +779,22 @@ bool Countly::updateSession() { last_sent_session_request += duration; } + + // report events if there are any to request queue + if (!no_events) { + sendEventsToRQ(events); + } + +// clear event queue +// TODO: check if we want to totally wipe the event queue in memory but not in database +#ifndef COUNTLY_USE_SQLITE + event_queue.clear(); +#else + if (!event_ids.empty()) { + // this is a partial clearance, we only remove the events that were sent + removeEventWithId(event_ids); + } +#endif } catch (const std::system_error &e) { std::ostringstream log_message; log_message << "update session, error: " << e.what(); @@ -792,7 +825,7 @@ void Countly::packEvents() { mutex->lock(); #endif } else { - log(LogLevel::DEBUG, "[Countly][updateSession] EQ empty."); + log(LogLevel::DEBUG, "[Countly][packEvents] EQ empty."); } // report events if there are any to request queue if (!no_events) { @@ -811,7 +844,7 @@ void Countly::packEvents() { #endif } catch (const std::system_error &e) { std::ostringstream log_message; - log_message << "update session, error: " << e.what(); + log_message << "packEvents, error: " << e.what(); log(LogLevel::FATAL, log_message.str()); } mutex->unlock(); @@ -1167,10 +1200,11 @@ void Countly::updateLoop() { size_t last_wait_milliseconds = wait_milliseconds; mutex->unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(last_wait_milliseconds)); - if (enable_automatic_session) { + if (enable_automatic_session && !configuration->manualSessionControl) { updateSession(); + } else { + packEvents(); } - packEvents(); requestModule->processQueue(mutex); } mutex->lock(); From 7add13081a6a7affe137f8170717640b3e477675 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Wed, 1 Oct 2025 10:04:56 +0300 Subject: [PATCH 30/33] fix: behavior changes --- src/countly.cpp | 22 +++++++++++----------- tests/config.cpp | 3 +++ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/countly.cpp b/src/countly.cpp index 4fce47e..4e32d7e 100644 --- a/src/countly.cpp +++ b/src/countly.cpp @@ -346,7 +346,7 @@ void Countly::_changeDeviceIdWithoutMerge(const std::string &value) { // send all event to server and end current session of old user flushEvents(); - if(!configuration->manualSessionControl){ + if(configuration->manualSessionControl == false){ endSession(); } @@ -355,7 +355,7 @@ void Countly::_changeDeviceIdWithoutMerge(const std::string &value) { mutex->unlock(); // start a new session for new user - if(!configuration->manualSessionControl){ + if(configuration->manualSessionControl == false){ beginSession(); } @@ -438,7 +438,7 @@ void Countly::start(const std::string &app_key, const std::string &host, int por if (!running) { - if(!configuration->manualSessionControl){ + if(configuration->manualSessionControl == false){ mutex->unlock(); beginSession(); mutex->lock(); @@ -469,7 +469,7 @@ void Countly::startOnCloud(const std::string &app_key) { void Countly::stop() { _deleteThread(); - if (!configuration->manualSessionControl) { + if (configuration->manualSessionControl == false) { endSession(); } } @@ -611,7 +611,7 @@ bool Countly::attemptSessionUpdateEQ() { return false; } #endif - if(!configuration->manualSessionControl){ + if(configuration->manualSessionControl == false){ return !updateSession(); } else { packEvents(); @@ -676,7 +676,7 @@ std::vector Countly::debugReturnStateOfEQ() { bool Countly::beginSession() { mutex->lock(); log(LogLevel::INFO, "[Countly][beginSession]"); - if (began_session) { + if (began_session == true) { mutex->unlock(); log(LogLevel::DEBUG, "[Countly][beginSession] Session is already active."); return true; @@ -732,9 +732,9 @@ bool Countly::updateSession() { try { // Check if there was a session, if not try to start one mutex->lock(); - if (!began_session) { + if (began_session == false) { mutex->unlock(); - if(configuration->manualSessionControl){ + if(configuration->manualSessionControl == true){ log(LogLevel::WARNING, "[Countly][updateSession] SDK is in manual session control mode and there is no active session. Please start a session first."); return false; } @@ -859,7 +859,7 @@ void Countly::sendEventsToRQ(const nlohmann::json &events) { bool Countly::endSession() { log(LogLevel::INFO, "[Countly][endSession]"); - if(!began_session) { + if(began_session == false) { log(LogLevel::DEBUG, "[Countly][endSession] There is no active session to end."); return true; } @@ -1200,9 +1200,9 @@ void Countly::updateLoop() { size_t last_wait_milliseconds = wait_milliseconds; mutex->unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(last_wait_milliseconds)); - if (enable_automatic_session && !configuration->manualSessionControl) { + if (enable_automatic_session == true && configuration->manualSessionControl == false) { updateSession(); - } else { + } else if (configuration->manualSessionControl == true) { packEvents(); } requestModule->processQueue(mutex); diff --git a/tests/config.cpp b/tests/config.cpp index 8949dff..002a590 100644 --- a/tests/config.cpp +++ b/tests/config.cpp @@ -51,6 +51,7 @@ TEST_CASE("Validate setting configuration values") { CHECK(config.breadcrumbsThreshold == 100); CHECK(config.forcePost == false); CHECK(config.port == 443); + CHECK(config.manualSessionControl == false); CHECK(config.sha256_function == nullptr); CHECK(config.http_client_function == nullptr); CHECK(config.metrics.empty()); @@ -76,6 +77,7 @@ TEST_CASE("Validate setting configuration values") { ct.setMaxRequestQueueSize(10); ct.SetPath(TEST_DATABASE_NAME); ct.setMaxRQProcessingBatchSize(10); + ct.enableManualSessionControl(); ct.start("YOUR_APP_KEY", "https://try.count.ly", -1, false); // Get configuration values using Countly getters @@ -94,6 +96,7 @@ TEST_CASE("Validate setting configuration values") { CHECK(config.breadcrumbsThreshold == 100); CHECK(config.forcePost == true); CHECK(config.port == 443); + CHECK(config.manualSessionControl == true); CHECK(config.sha256_function("custom SHA256") == customSha_1_returnValue); HTTPResponse response = config.http_client_function(true, "", ""); From 9e3d4266d987720426deb10c3cdd8bceaf6af139 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray <57103426+arifBurakDemiray@users.noreply.github.com> Date: Thu, 2 Oct 2025 11:52:39 +0300 Subject: [PATCH 31/33] Correct header formatting for version 23.2.1 Fixed formatting issue in CHANGELOG.md header. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4b0214..d06f931 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 23.2.1 +## 23.2.1 - Added manual session control via "Countly::enableManualSessionControl". When enabled, automatic session calls are ignored, while manual calls remain usable for finer control. - Added "checkRQSize" function to return the current number of requests in the queue. From 0354e630431b9b3eeb3f7c7feacfd2dbeb7bcd5e Mon Sep 17 00:00:00 2001 From: turtledreams Date: Wed, 8 Oct 2025 00:17:51 +0900 Subject: [PATCH 32/33] mutex fix --- CHANGELOG.md | 3 +++ src/countly.cpp | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d06f931..8286588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## X.X.X +- Mitigated a mutex issue that can happen during update loop. + ## 23.2.1 - Added manual session control via "Countly::enableManualSessionControl". When enabled, automatic session calls are ignored, while manual calls remain usable for finer control. - Added "checkRQSize" function to return the current number of requests in the queue. diff --git a/src/countly.cpp b/src/countly.cpp index 4e32d7e..e5b321a 100644 --- a/src/countly.cpp +++ b/src/countly.cpp @@ -373,6 +373,7 @@ void Countly::start(const std::string &app_key, const std::string &host, int por #ifdef COUNTLY_USE_SQLITE if (configuration->databasePath == "" || configuration->databasePath == " ") { log(LogLevel::ERROR, "[Countly][start] Database path can not be empty or blank."); + mutex->unlock(); return; } #endif @@ -806,7 +807,8 @@ bool Countly::updateSession() { void Countly::packEvents() { try { - // events array + mutex->lock(); + // events array nlohmann::json events = nlohmann::json::array(); std::string event_ids; mutex->unlock(); From 797cfe042f9b239385b9916bbf9c75653bbd790e Mon Sep 17 00:00:00 2001 From: turtledreams Date: Wed, 8 Oct 2025 18:25:25 +0900 Subject: [PATCH 33/33] version --- CHANGELOG.md | 2 +- include/countly/constants.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8286588..654f754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## X.X.X +## 23.2.2 - Mitigated a mutex issue that can happen during update loop. ## 23.2.1 diff --git a/include/countly/constants.hpp b/include/countly/constants.hpp index ec80ee2..300e09e 100644 --- a/include/countly/constants.hpp +++ b/include/countly/constants.hpp @@ -13,7 +13,7 @@ #include #define COUNTLY_SDK_NAME "cpp-native-unknown" -#define COUNTLY_SDK_VERSION "23.2.1" +#define COUNTLY_SDK_VERSION "23.2.2" #define COUNTLY_POST_THRESHOLD 2000 #define COUNTLY_KEEPALIVE_INTERVAL 3000 #define COUNTLY_MAX_EVENTS_DEFAULT 200