# HG changeset patch # User Paper # Date 1762517337 18000 # Node ID 2d3e103191125fa41c1b892ee5735d9c957fd085 # Parent 1e5d922fe82bf0e7914e9e160aba35646174c3d2 http: optimize HTTP request thread we don't need a mutex at all, in fact all we need is an atomic boolean to signify whether the thread is cancelled. curl options are now for the most part handled by a separate function to keep them in sync between non-threaded and threaded implementations diff -r 1e5d922fe82b -r 2d3e10319112 include/core/http.h --- a/include/core/http.h Thu Nov 06 12:21:35 2025 -0500 +++ b/include/core/http.h Fri Nov 07 07:08:57 2025 -0500 @@ -7,6 +7,7 @@ #include #include #include +#include namespace HTTP { @@ -52,13 +53,11 @@ std::string data_; std::vector headers_; Type type_; + void *curl_; /* these are passed to the write callback */ QByteArray array_; - bool cancelled_ = false; - - /* don't fuck this up */ - std::mutex callback_data_mutex_; + std::atomic cancelled_ = false; }; } // namespace HTTP diff -r 1e5d922fe82b -r 2d3e10319112 src/core/http.cc --- a/src/core/http.cc Thu Nov 06 12:21:35 2025 -0500 +++ b/src/core/http.cc Fri Nov 07 07:08:57 2025 -0500 @@ -65,6 +65,32 @@ return base; } +static void SetCurlOpts(CURL *curl, const std::string &url, const std::vector &headers, const std::string &data, Type type) +{ + struct curl_slist *list; + + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + + list = NULL; + for (const std::string &h : headers) + list = curl_slist_append(list, h.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + + switch (type) { + case Type::Patch: + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); + [[fallthrough]]; + case Type::Post: + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); + break; + case Type::Get: + break; + } + + curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // threading +} + static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userdata) { reinterpret_cast(userdata)->append(reinterpret_cast(contents), size * nmemb); @@ -73,25 +99,17 @@ QByteArray Request(const std::string &url, const std::vector &headers, const std::string &data, Type type) { - struct curl_slist *list = NULL; QByteArray userdata; CURL *curl = curl_easy_init(); if (curl) { - for (const std::string &h : headers) - list = curl_slist_append(list, h.c_str()); + SetCurlOpts(curl, url, headers, data, type); - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - if (type == Type::Post || type == Type::Patch) - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str()); - if (type == Type::Patch) - curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + /* Use our specific userdata & write callback + * TODO can this just be a lambda instead */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &userdata); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &WriteCallback); - /* Use system certs... useful on Windows. */ - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // threading + CURLcode res = curl_easy_perform(curl); session.IncrementRequests(); curl_easy_cleanup(curl); @@ -106,9 +124,6 @@ { RequestThread *thread = reinterpret_cast(userdata); - const std::lock_guard lock(thread->callback_data_mutex_); - - /* stop writing, then! */ if (thread->cancelled_) return CURL_WRITEFUNC_ERROR; @@ -137,6 +152,10 @@ /* block until the function can safely exit */ Stop(); wait(); + + /* Kill off any curl thing we made */ + if (curl_) + curl_easy_cleanup(reinterpret_cast(curl_)); } void RequestThread::SetUrl(const std::string &url) @@ -161,45 +180,34 @@ void RequestThread::run() { - struct curl_slist *list = NULL; - - CURL *curl = curl_easy_init(); - if (curl) { - curl_easy_setopt(curl, CURLOPT_URL, url_.c_str()); + /* If we don't have a curl object, create one */ + if (!curl_) { + curl_ = reinterpret_cast(curl_easy_init()); + } - if (type_ == Type::Post || type_ == Type::Patch) - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data_.c_str()); + CURL *curl = reinterpret_cast(curl_); - for (const std::string &h : headers_) - list = curl_slist_append(list, h.c_str()); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + if (curl) { + SetCurlOpts(curl, url_, headers_, data_, type_); curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RequestThread::WriteCallback); - /* Use system certs... useful on Windows. */ - curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); - - /* does something with threading, don't remember what though */ - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); - CURLcode res = curl_easy_perform(curl); session.IncrementRequests(); curl_easy_cleanup(curl); - callback_data_mutex_.lock(); if (res != CURLE_OK && !(res == CURLE_WRITE_ERROR && cancelled_)) session.SetStatusBar(std::string("curl_easy_perform(curl) failed!: ") + curl_easy_strerror(res)); - callback_data_mutex_.unlock(); } emit ReceivedData(array_); + /* Clear it out for any subsequent runs */ array_.clear(); } void RequestThread::Stop() { - const std::lock_guard lock(callback_data_mutex_); cancelled_ = true; }