diff --git a/CHANGELOG.md b/CHANGELOG.md index 279ea3f0..3af80c66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project are documented below. The format is based on [keep a changelog](http://keepachangelog.com/) and this project uses [semantic versioning](http://semver.org/). +### [2.8.2] - [2023-10-31] +### Fixed +- Fixed a race condition that could happen during socket connection which would cause `std::future_error` to be thrown. + ### [2.8.1] - [2023-05-25] ### Fixed - Fixed a memory issue with retrieving HTTP response codes. diff --git a/core/core-rt/NRtClient.cpp b/core/core-rt/NRtClient.cpp index d37479f1..ab917b37 100644 --- a/core/core-rt/NRtClient.cpp +++ b/core/core-rt/NRtClient.cpp @@ -114,10 +114,10 @@ std::future NRtClient::connectAsync(NSessionPtr session, bool createStatus // if old promise not ready, just complete it for the user. _connectPromise->set_value(); } - catch(const std::future_error& e) + catch(const std::future_error&) { // if we get an exception here, it means the connect promise has completed already from a previous connect. - // this is very expected. + // this is very expected. there is no way to check from the promise itself if it has completed already. } // stomp the old promise @@ -196,7 +196,16 @@ void NRtClient::onTransportConnected() _listener->onConnect(); } - _connectPromise->set_value(); + try + { + // signal to the user's future that the connection has completed. + _connectPromise->set_value(); + } + catch (const std::future_error&) + { + // if we get an exception here, it means the connect promise has completed already from a previous connect. + // this can happen if the transport double fires or some other unexpected cases, like the user disconnecting while a connection is being made. + } } void NRtClient::onTransportDisconnected(const NRtClientDisconnectInfo& info) diff --git a/test/src/realtime/test_lifecycle.cpp b/test/src/realtime/test_lifecycle.cpp index afedfc23..39ef812c 100644 --- a/test/src/realtime/test_lifecycle.cpp +++ b/test/src/realtime/test_lifecycle.cpp @@ -76,7 +76,7 @@ namespace Nakama { bool threadedTick = true; NTest test(__func__, threadedTick); test.setTestTimeoutMs(15000); - bool hadConnectCallback = false; + test.runTest(); NSessionPtr session = test.client->authenticateCustomAsync(TestGuid::newGuid(), std::string(), true).get(); @@ -92,5 +92,29 @@ namespace Nakama { test.stopTest(true); } + + void test_rt_connect_callback() + { + bool threadedTick = true; + + NTest test(__func__, true); + test.runTest(); + + NSessionPtr session = test.client->authenticateCustomAsync(TestGuid::newGuid(), std::string(), true).get(); + + bool connected = false; + + test.listener.setConnectCallback([&connected](){ + connected = true; + }); + + test.rtClient->connect(session, true); + + // try to trigger any issues with the underlying promise. + std::this_thread::sleep_for(std::chrono::milliseconds(5000)); + + test.stopTest(connected); + + } } } diff --git a/test/src/realtime/test_realtime.cpp b/test/src/realtime/test_realtime.cpp index 11e990fe..121406cc 100644 --- a/test/src/realtime/test_realtime.cpp +++ b/test/src/realtime/test_realtime.cpp @@ -34,7 +34,7 @@ void test_rt_joinGroupChat(); void test_rt_quickdestroy(); void test_rt_rapiddisconnect(); void test_rt_reconnect(); - +void test_rt_connect_callback(); void run_realtime_tests() { @@ -52,6 +52,7 @@ void test_realtime() { // These tests are not protocol specific test_rt_rapiddisconnect(); + test_rt_connect_callback(); /// change to 10 iterations to trigger https://github.com/microsoft/libHttpClient/issues/698 bug for (int i = 0; i < 1; i++) { test_rt_reconnect(); diff --git a/version.cmake b/version.cmake index 7c31b188..21655931 100644 --- a/version.cmake +++ b/version.cmake @@ -1,4 +1,4 @@ # Easy to update by CI scripts file containing our version as well as # dependent git repos to achieve reproducible builds -set(LIBNAKAMA_VERSION 2.8.0) +set(LIBNAKAMA_VERSION 2.8.2)