diff --git a/lib/http/HttpClientManager.cpp b/lib/http/HttpClientManager.cpp index 58fa5fb4a..0de14e085 100644 --- a/lib/http/HttpClientManager.cpp +++ b/lib/http/HttpClientManager.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #ifdef linux #include @@ -149,11 +151,23 @@ namespace MAT_NS_BEGIN { void HttpClientManager::cancelAllRequests() { cancelAllRequestsAsync(); - while (!m_httpCallbacks.empty()) - std::this_thread::yield(); + + // Wait for callbacks to drain before shutdown can destroy state that + // those callbacks still use. Keep the list check synchronized and sleep + // between polls so a slow adapter does not burn CPU while draining. + for (;;) + { + { + LOCKGUARD(m_httpCallbacksMtx); + if (m_httpCallbacks.empty()) + { + return; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } } // start async cancellation } MAT_NS_END - diff --git a/lib/http/HttpClientManager.hpp b/lib/http/HttpClientManager.hpp index 646435b00..e8214d631 100644 --- a/lib/http/HttpClientManager.hpp +++ b/lib/http/HttpClientManager.hpp @@ -32,6 +32,12 @@ class HttpClientManager size_t requestCount() const { + // Access to m_httpCallbacks must be serialized via m_httpCallbacksMtx. + // Without the lock this is a std::list data race vs onHttpResponse, + // handleSendRequest, and cancelAllRequests (same UB class as the + // empty()-check bug fixed in cancelAllRequests). The mutex is + // declared mutable below so a const observer can take it. + LOCKGUARD(m_httpCallbacksMtx); return m_httpCallbacks.size(); } @@ -54,7 +60,7 @@ class HttpClientManager ILogManager& m_logManager; IHttpClient& m_httpClient; ITaskDispatcher& m_taskDispatcher; - std::recursive_mutex m_httpCallbacksMtx; + mutable std::recursive_mutex m_httpCallbacksMtx; std::list m_httpCallbacks; }; diff --git a/lib/http/HttpClient_Apple.mm b/lib/http/HttpClient_Apple.mm index 05817087a..449b4c9af 100644 --- a/lib/http/HttpClient_Apple.mm +++ b/lib/http/HttpClient_Apple.mm @@ -132,23 +132,6 @@ void HandleResponse(NSData* data, NSURLResponse* response, NSError* error) void Cancel() { [m_dataTask cancel]; - [session getTasksWithCompletionHandler:^(NSArray* dataTasks, NSArray* uploadTasks, NSArray* downloadTasks) - { - for (NSURLSessionTask* _task in dataTasks) - { - [_task cancel]; - } - - for (NSURLSessionTask* _task in downloadTasks) - { - [_task cancel]; - } - - for (NSURLSessionTask* _task in uploadTasks) - { - [_task cancel]; - } - }]; } private: @@ -214,10 +197,16 @@ void Cancel() for (const auto &id : ids) CancelRequestAsync(id); - while (!m_requests.empty()) + for (;;) { + { + std::lock_guard lock(m_requestsMtx); + if (m_requests.empty()) + { + return; + } + } PAL::sleep(100); - std::this_thread::yield(); } }