Merge branch 'http-download-threaded' into 'next'

Put HTTP downloading on a background thread

See merge request STJr/SRB2!2322
This commit is contained in:
LJ Sonic 2024-12-31 16:14:06 +00:00
commit 3e6862b1c2
3 changed files with 90 additions and 71 deletions

View file

@ -546,6 +546,7 @@ static void AbortConnection(void)
{ {
Snake_Free(&snake); Snake_Free(&snake);
CURLAbortFile();
D_QuitNetGame(); D_QuitNetGame();
CL_Reset(); CL_Reset();
D_StartTitle(); D_StartTitle();
@ -1062,10 +1063,6 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
} }
} }
// Rusty TODO: multithread
if (filedownload.http_running)
CURLGetFile();
if (waitmore) if (waitmore)
break; // exit the case break; // exit the case

View file

@ -95,6 +95,7 @@ static filetran_t transfer[MAXNETNODES];
INT32 fileneedednum; // Number of files needed to join the server INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t *fileneeded; // List of needed files fileneeded_t *fileneeded; // List of needed files
static tic_t lasttimeackpacketsent = 0; static tic_t lasttimeackpacketsent = 0;
static I_mutex downloadmutex;
char downloaddir[512] = "DOWNLOAD"; char downloaddir[512] = "DOWNLOAD";
// For resuming failed downloads // For resuming failed downloads
@ -1609,11 +1610,13 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
I_Error("Attempted to download files in -nodownload mode"); I_Error("Attempted to download files in -nodownload mode");
#endif #endif
curl_global_init(CURL_GLOBAL_ALL); if (!multi_handle)
{
curl_global_init(CURL_GLOBAL_ALL);
multi_handle = curl_multi_init();
}
http_handle = curl_easy_init(); http_handle = curl_easy_init();
multi_handle = curl_multi_init();
if (http_handle && multi_handle) if (http_handle && multi_handle)
{ {
I_mkdir(downloaddir, 0755); I_mkdir(downloaddir, 0755);
@ -1672,6 +1675,8 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
filedownload.current = dfilenum; filedownload.current = dfilenum;
filedownload.http_running = true; filedownload.http_running = true;
I_spawn_thread("http-download", (I_thread_fn)CURLGetFile, NULL);
return true; return true;
} }
@ -1680,103 +1685,119 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
return false; return false;
} }
void CURLAbortFile(void)
{
filedownload.http_running = false;
// lock and unlock to wait for the download thread to exit
I_lock_mutex(&downloadmutex);
I_unlock_mutex(downloadmutex);
}
void CURLGetFile(void) void CURLGetFile(void)
{ {
I_lock_mutex(&downloadmutex);
CURLMcode mc; /* return code used by curl_multi_wait() */ CURLMcode mc; /* return code used by curl_multi_wait() */
CURLcode easyres; /* Return from easy interface */ CURLcode easyres; /* Return from easy interface */
int numfds;
CURLMsg *m; /* for picking up messages with the transfer status */ CURLMsg *m; /* for picking up messages with the transfer status */
CURL *e; CURL *e;
int msgs_left; /* how many messages are left */ int msgs_left; /* how many messages are left */
const char *easy_handle_error; const char *easy_handle_error;
boolean running = true;
if (curl_runninghandles) while (running && filedownload.http_running)
{ {
curl_multi_perform(multi_handle, &curl_runninghandles); if (curl_runninghandles)
/* wait for activity, timeout or "nothing" */
mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds);
if (mc != CURLM_OK)
{ {
CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc); curl_multi_perform(multi_handle, &curl_runninghandles);
return;
}
curl_curfile->currentsize = curl_dlnow;
curl_curfile->totalsize = curl_dltotal;
}
/* See how the transfers went */ /* wait for activity, timeout or "nothing" */
while ((m = curl_multi_info_read(multi_handle, &msgs_left))) mc = curl_multi_wait(multi_handle, NULL, 0, 1000, NULL);
{
if (m && (m->msg == CURLMSG_DONE))
{
e = m->easy_handle;
easyres = m->data.result;
char *filename = Z_StrDup(curl_realname); if (mc != CURLM_OK)
nameonly(filename);
if (easyres != CURLE_OK)
{ {
long response_code = 0; CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc);
continue;
if (easyres == CURLE_HTTP_RETURNED_ERROR)
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code);
if (response_code == 404)
curl_curfile->failed = FDOWNLOAD_FAIL_NOTFOUND;
else
curl_curfile->failed = FDOWNLOAD_FAIL_OTHER;
easy_handle_error = (response_code) ? va("HTTP response code %ld", response_code) : curl_easy_strerror(easyres);
curl_curfile->status = FS_FALLBACK;
curl_curfile->currentsize = curl_origfilesize;
curl_curfile->totalsize = curl_origtotalfilesize;
filedownload.http_failed = true;
fclose(curl_curfile->file);
remove(curl_curfile->filename);
CONS_Alert(CONS_ERROR, M_GetText("Failed to download addon \"%s\" (%s)\n"), filename, easy_handle_error);
} }
else curl_curfile->currentsize = curl_dlnow;
curl_curfile->totalsize = curl_dltotal;
}
/* See how the transfers went */
while ((m = curl_multi_info_read(multi_handle, &msgs_left)))
{
if (m && (m->msg == CURLMSG_DONE))
{ {
fclose(curl_curfile->file); running = false;
e = m->easy_handle;
easyres = m->data.result;
CONS_Printf(M_GetText("Finished download of \"%s\"\n"), filename); char *filename = Z_StrDup(curl_realname);
nameonly(filename);
if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD) if (easyres != CURLE_OK)
{ {
CONS_Alert(CONS_WARNING, M_GetText("File \"%s\" does not match the version used by the server\n"), filename); long response_code = 0;
if (easyres == CURLE_HTTP_RETURNED_ERROR)
curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &response_code);
if (response_code == 404)
curl_curfile->failed = FDOWNLOAD_FAIL_NOTFOUND;
else
curl_curfile->failed = FDOWNLOAD_FAIL_OTHER;
easy_handle_error = (response_code) ? va("HTTP response code %ld", response_code) : curl_easy_strerror(easyres);
curl_curfile->status = FS_FALLBACK; curl_curfile->status = FS_FALLBACK;
curl_curfile->failed = FDOWNLOAD_FAIL_MD5SUMBAD; curl_curfile->currentsize = curl_origfilesize;
curl_curfile->totalsize = curl_origtotalfilesize;
filedownload.http_failed = true; filedownload.http_failed = true;
fclose(curl_curfile->file);
remove(curl_curfile->filename);
CONS_Alert(CONS_ERROR, M_GetText("Failed to download addon \"%s\" (%s)\n"), filename, easy_handle_error);
} }
else else
{ {
filedownload.completednum++; fclose(curl_curfile->file);
filedownload.completedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND; CONS_Printf(M_GetText("Finished download of \"%s\"\n"), filename);
if (checkfilemd5(curl_curfile->filename, curl_curfile->md5sum) == FS_MD5SUMBAD)
{
CONS_Alert(CONS_WARNING, M_GetText("File \"%s\" does not match the version used by the server\n"), filename);
curl_curfile->status = FS_FALLBACK;
curl_curfile->failed = FDOWNLOAD_FAIL_MD5SUMBAD;
filedownload.http_failed = true;
}
else
{
filedownload.completednum++;
filedownload.completedsize += curl_curfile->totalsize;
curl_curfile->status = FS_FOUND;
}
} }
Z_Free(filename);
curl_curfile->file = NULL;
filedownload.remaining--;
curl_multi_remove_handle(multi_handle, e);
curl_easy_cleanup(e);
if (!filedownload.remaining)
break;
} }
Z_Free(filename);
curl_curfile->file = NULL;
filedownload.http_running = false;
filedownload.remaining--;
curl_multi_remove_handle(multi_handle, e);
curl_easy_cleanup(e);
if (!filedownload.remaining)
break;
} }
} }
if (!filedownload.remaining) if (!filedownload.remaining || !filedownload.http_running)
{ {
curl_multi_cleanup(multi_handle); curl_multi_cleanup(multi_handle);
curl_global_cleanup(); curl_global_cleanup();
multi_handle = NULL;
} }
filedownload.http_running = false;
I_unlock_mutex(downloadmutex);
} }
HTTP_login * HTTP_login *

View file

@ -140,6 +140,7 @@ boolean CL_SendFileRequest(void);
void PT_RequestFile(SINT8 node); void PT_RequestFile(SINT8 node);
boolean CURLPrepareFile(const char* url, int dfilenum); boolean CURLPrepareFile(const char* url, int dfilenum);
void CURLAbortFile(void);
void CURLGetFile(void); void CURLGetFile(void);
HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next); HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next);