Skip to content

Conversation

@bagder
Copy link
Member

@bagder bagder commented Oct 10, 2025

As this is in the tool shutdown the impact of it was nothing.

Also, move two global variables to local.

Follow-up to 9a26633

Reported-by: Joshua Rogers

As this is in the tool shutdown the impact of it was nothing.

Also, move two global variables to local.

Follow-up to 9a26633

Reported-by: Joshua Rogers
@github-actions github-actions bot added the Windows Windows-specific label Oct 10, 2025
@bagder bagder marked this pull request as ready for review October 10, 2025 08:20
@vszakats
Copy link
Member

vszakats commented Oct 10, 2025

The patch is fine, though zooming out, the use of TerminateThread() is strongly not recommended by the documentation. It's also the single place where this is done in the codebase:
https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminatethread#remarks

(Up until recently, all other places used the CRT API for thread creation, and that API doesn't have a terminate API.)

Earlier discussion: #18451
Initial commit: 9a26633 #17572

edit: Meant to say: If there is a way, this error handling should probably be solved differently, without killing the thread.

@bagder
Copy link
Member Author

bagder commented Oct 10, 2025

If there is a way, this error handling should probably be solved differently, without killing the thread.

It certainly sounds so, but I don't think I am the person to do that as it probably should involve running tests on Windows to work out what works and what doesn't work. It's not clear to me what the alternative is.

@jay
Copy link
Member

jay commented Oct 11, 2025

I don't think we need to terminate the thread on error, it looks like it is written in a way that it would abort on its own? @denandz Though I notice that there's similar cleanup in both the main thread and the worker thread and that doesn't make a lot of sense to me. Couldn't it lead to handles being closed twice etc if the worker thread has started but there is an error in the main thread?

main thread cleanup of tdata, sockets,etc:

curl/src/tool_doswin.c

Lines 920 to 948 in 11b9912

if(rc != 1) {
if(socket_r != CURL_SOCKET_BAD && tdata) {
if(GetStdHandle(STD_INPUT_HANDLE) == (HANDLE)socket_r &&
tdata->stdin_handle) {
/* restore STDIN */
SetStdHandle(STD_INPUT_HANDLE, tdata->stdin_handle);
tdata->stdin_handle = NULL;
}
sclose(socket_r);
socket_r = CURL_SOCKET_BAD;
}
if(stdin_thread) {
TerminateThread(stdin_thread, 1);
stdin_thread = NULL;
}
if(tdata) {
if(tdata->stdin_handle)
CloseHandle(tdata->stdin_handle);
if(tdata->socket_l != CURL_SOCKET_BAD)
sclose(tdata->socket_l);
free(tdata);
}
return CURL_SOCKET_BAD;
}

worker thread cleanup of tdata, sockets,etc :

curl/src/tool_doswin.c

Lines 794 to 806 in 11b9912

ThreadCleanup:
CloseHandle(tdata->stdin_handle);
tdata->stdin_handle = NULL;
if(tdata->socket_l != CURL_SOCKET_BAD) {
sclose(tdata->socket_l);
tdata->socket_l = CURL_SOCKET_BAD;
}
if(socket_w != CURL_SOCKET_BAD)
sclose(socket_w);
if(tdata) {
free(tdata);
}

Should the worker thread really be doing any of that?

@denandz
Copy link
Contributor

denandz commented Oct 11, 2025

That block in the main thread is cleaning up if there's any issue spawning the worker and its various handles. The in-worker-cleanup closes the socket handles after the worker main processing loop is completed.

IIRC without the in-worker cleanup, I had the test suite catching unclosed-sockets on exit. The other bit was we need to call the socket close (along with the SO_DONTLINGER socket option) to avoid a case where Windows holds the socket in TIMEWAIT after curl exits (because we didn't close the socket) and subsequent execution of curl -T . fails until the kernel cleans up that socket.

Regarding TerminateThread - understood it's not recommended by Microsoft - the idea here was that if there's an issue during worker setup and win32_stdin_read_thread is going to return an error, we want to be sure there are no dangling threads kicking around and thus take the nuclear option to terminate the thread prior to returning error.

This could all probably be done in a neater way - like passing the tdata structure ref back to the main curl process and having it do all of the cleanup as part of exiting.

@vszakats
Copy link
Member

Would it be workable to setup the sockets first, and only create the thread if those succeeded?

@denandz
Copy link
Contributor

denandz commented Oct 12, 2025

Would it be workable to setup the sockets first, and only create the thread if those succeeded?

Possibly - the connect() blocks in the main thread with the worker doing the accept() - so there would need to be some trickery to have connect() and accept() happen in the same main thread, then get loaded into the tdata struct and the thread spawned.

I think there's two areas for improvement here - Moving away from TerminateThread and having the main thread explicitly clean up the sockets and handles used by the worker thread on exit.

Did we want to track this in a separate issue? I just tested this branch on a Windows host and the additional CloseHandle and global variable cleanup works fine.

@bagder
Copy link
Member Author

bagder commented Oct 12, 2025

Did we want to track this in a separate issue? I just tested this branch on a Windows host and the additional CloseHandle and global variable cleanup works fine.

Yes, I think we should do it in a separate PR to keep it clean. I can merge this as-is in the mean time.

@bagder bagder closed this in 142d61a Oct 12, 2025
@bagder bagder deleted the bagder/doswin-thread-close branch October 12, 2025 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cmdline tool Windows Windows-specific

Development

Successfully merging this pull request may close these issues.

4 participants