Skip to content

Commit 131a96a

Browse files
committed
fix(harmony): harden CURL event loop reuse
- switch Harmony HTTPFileSource to share the CURLEventLoop so requests hold a stable reference - capture the shared loop inside HTTPRequest and guard handle acquisition with explicit errors - drop handles through the request-scoped loop reference before releasing it during teardown
1 parent f82de77 commit 131a96a

1 file changed

Lines changed: 26 additions & 10 deletions

File tree

platform/harmony/maplibre_harmony/src/main/cpp/network/http_file_source_harmony.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <cstdio>
2424
#include <cstdlib>
2525
#include <optional>
26+
#include <memory>
2627
#include <mutex>
2728
#include <atomic>
2829
#include <thread>
@@ -96,7 +97,7 @@ class HTTPFileSource::Impl {
9697
void checkMultiInfo();
9798

9899
// Use the dedicated CURL event loop
99-
std::unique_ptr<harmony::CURLEventLoop> curlEventLoop;
100+
std::shared_ptr<harmony::CURLEventLoop> curlEventLoop;
100101

101102
// CURL multi handle — now managed by CURLEventLoop
102103
CURLM *multi = nullptr;
@@ -114,6 +115,10 @@ class HTTPFileSource::Impl {
114115
void setClientOptions(ClientOptions options);
115116
ClientOptions getClientOptions();
116117

118+
std::shared_ptr<harmony::CURLEventLoop> getEventLoop() const {
119+
return curlEventLoop;
120+
}
121+
117122
private:
118123
mutable std::mutex resourceOptionsMutex;
119124
mutable std::mutex clientOptionsMutex;
@@ -133,6 +138,7 @@ class HTTPRequest : public AsyncRequest {
133138
static size_t writeCallback(void *contents, size_t nmemb, size_t size, void *userp);
134139

135140
HTTPFileSource::Impl *context = nullptr;
141+
std::shared_ptr<harmony::CURLEventLoop> eventLoop;
136142
Resource resource;
137143
FileSource::Callback callback;
138144

@@ -190,7 +196,7 @@ HTTPFileSource::Impl::Impl(const ResourceOptions &resourceOptions_, const Client
190196
mode = harmony::CURLEventLoop::Mode::EventDriven;
191197
}
192198

193-
curlEventLoop = std::make_unique<harmony::CURLEventLoop>(mode);
199+
curlEventLoop = std::make_shared<harmony::CURLEventLoop>(mode);
194200
curlEventLoop->start();
195201

196202
// Obtain the multi handle (owned by CURLEventLoop)
@@ -219,8 +225,8 @@ HTTPFileSource::Impl::~Impl() {
219225
// but ANRDetector helps surface long-running operations.
220226
if (curlEventLoop) {
221227
ANRDetector stopDetector("curlEventLoop->stop", 50, 200);
222-
curlEventLoop->stop();
223-
curlEventLoop.reset();
228+
auto loopRef = std::move(curlEventLoop);
229+
loopRef->stop();
224230
}
225231

226232
// Clean up the CURL handle queue (typically quick)
@@ -305,10 +311,18 @@ ClientOptions HTTPFileSource::Impl::getClientOptions() {
305311

306312
HTTPRequest::HTTPRequest(HTTPFileSource::Impl *context_, Resource resource_, FileSource::Callback callback_)
307313
: context(context_),
314+
eventLoop(context_ ? context_->getEventLoop() : nullptr),
308315
resource(std::move(resource_)),
309316
callback(std::move(callback_)),
310-
handle(context->getHandle()) {
311-
317+
handle(context_ ? context_->getHandle() : nullptr) {
318+
if (!context) {
319+
throw std::runtime_error("HTTPFileSource context is null");
320+
}
321+
322+
if (!handle) {
323+
throw std::runtime_error("Failed to acquire CURL easy handle");
324+
}
325+
312326
// Apply URL transforms when a callback is provided.
313327
// Reference Android: platform/android/.../file_source.cpp:122-124
314328
// Reference iOS: platform/darwin/src/MLNOfflineStorage.mm:138-141
@@ -377,8 +391,8 @@ HTTPRequest::HTTPRequest(HTTPFileSource::Impl *context_, Resource resource_, Fil
377391
handleError(curl_easy_setopt(handle, CURLOPT_SHARE, context->share));
378392

379393
// Start requesting the information using CURLEventLoop
380-
if (context->curlEventLoop) {
381-
bool success = context->curlEventLoop->addHandle(handle);
394+
if (eventLoop) {
395+
bool success = eventLoop->addHandle(handle);
382396
if (!success) {
383397
Logger::error("Network", "❌ Failed to add handle to CURLEventLoop");
384398
throw std::runtime_error("Failed to add handle to CURLEventLoop");
@@ -407,8 +421,8 @@ HTTPRequest::~HTTPRequest() {
407421
// 3. Clear userp and release resources.
408422

409423
// Step 1: remove the CURL handle from CURLEventLoop (stop new callbacks)
410-
if (context && context->curlEventLoop && handle) {
411-
bool success = context->curlEventLoop->removeHandle(handle);
424+
if (eventLoop && handle) {
425+
bool success = eventLoop->removeHandle(handle);
412426

413427
// Step 2: short wait to ensure in-flight callbacks finish
414428
// Note: keep it brief to avoid ANR while still accommodating callbacks
@@ -433,6 +447,8 @@ HTTPRequest::~HTTPRequest() {
433447
curl_slist_free_all(headers);
434448
headers = nullptr;
435449
}
450+
451+
eventLoop.reset();
436452
}
437453

438454
size_t HTTPRequest::writeCallback(void *const contents, const size_t size, const size_t nmemb, void *userp) {

0 commit comments

Comments
 (0)