diff options
| author | Eli Zaretskii | 2022-07-17 15:44:50 +0300 |
|---|---|---|
| committer | Eli Zaretskii | 2022-07-17 15:44:50 +0300 |
| commit | 637436970f34f860d50f73a514b3bafd0c5cace7 (patch) | |
| tree | f9f1fba2434ad07a84eecade8f0306d2be4111e2 /src | |
| parent | 202c12a24b89a3b8a923adba4d6bab0894b1a16e (diff) | |
| download | emacs-637436970f34f860d50f73a514b3bafd0c5cace7.tar.gz emacs-637436970f34f860d50f73a514b3bafd0c5cace7.zip | |
Fix leaking of file descriptors due to pipe processes on MS-Windows
* src/w32proc.c (reader_thread): Wait for 'sys_close' to finish
processing the pipe read descriptor, before trying to close it.
* src/w32.c (sys_close): Attempt to detect when the reader thread
already exited, so that it would be possible to close descriptors
open by pipe processes for reading from the pipe. (Bug#56606)
Diffstat (limited to 'src')
| -rw-r--r-- | src/w32.c | 23 | ||||
| -rw-r--r-- | src/w32proc.c | 17 |
2 files changed, 32 insertions, 8 deletions
| @@ -8573,6 +8573,7 @@ int | |||
| 8573 | sys_close (int fd) | 8573 | sys_close (int fd) |
| 8574 | { | 8574 | { |
| 8575 | int rc = -1; | 8575 | int rc = -1; |
| 8576 | bool reader_thread_exited = false; | ||
| 8576 | 8577 | ||
| 8577 | if (fd < 0) | 8578 | if (fd < 0) |
| 8578 | { | 8579 | { |
| @@ -8583,6 +8584,13 @@ sys_close (int fd) | |||
| 8583 | if (fd < MAXDESC && fd_info[fd].cp) | 8584 | if (fd < MAXDESC && fd_info[fd].cp) |
| 8584 | { | 8585 | { |
| 8585 | child_process * cp = fd_info[fd].cp; | 8586 | child_process * cp = fd_info[fd].cp; |
| 8587 | DWORD thrd_status = STILL_ACTIVE; | ||
| 8588 | |||
| 8589 | /* Thread handle will be NULL if we already called delete_child. */ | ||
| 8590 | if (cp->thrd != NULL | ||
| 8591 | && GetExitCodeThread (cp->thrd, &thrd_status) | ||
| 8592 | && thrd_status != STILL_ACTIVE) | ||
| 8593 | reader_thread_exited = true; | ||
| 8586 | 8594 | ||
| 8587 | fd_info[fd].cp = NULL; | 8595 | fd_info[fd].cp = NULL; |
| 8588 | 8596 | ||
| @@ -8633,7 +8641,11 @@ sys_close (int fd) | |||
| 8633 | because socket handles are fully fledged kernel handles. */ | 8641 | because socket handles are fully fledged kernel handles. */ |
| 8634 | if (fd < MAXDESC) | 8642 | if (fd < MAXDESC) |
| 8635 | { | 8643 | { |
| 8636 | if ((fd_info[fd].flags & FILE_DONT_CLOSE) == 0) | 8644 | if ((fd_info[fd].flags & FILE_DONT_CLOSE) == 0 |
| 8645 | /* If the reader thread already exited, close the descriptor, | ||
| 8646 | since otherwise no one will close it, and we will be | ||
| 8647 | leaking descriptors. */ | ||
| 8648 | || reader_thread_exited) | ||
| 8637 | { | 8649 | { |
| 8638 | fd_info[fd].flags = 0; | 8650 | fd_info[fd].flags = 0; |
| 8639 | rc = _close (fd); | 8651 | rc = _close (fd); |
| @@ -8641,10 +8653,11 @@ sys_close (int fd) | |||
| 8641 | else | 8653 | else |
| 8642 | { | 8654 | { |
| 8643 | /* We don't close here descriptors open by pipe processes | 8655 | /* We don't close here descriptors open by pipe processes |
| 8644 | for reading from the pipe, because the reader thread | 8656 | for reading from the pipe when the reader thread might |
| 8645 | might be stuck in _sys_read_ahead, and then we will hang | 8657 | still be running, since that thread might be stuck in |
| 8646 | here. If the reader thread exits normally, it will close | 8658 | _sys_read_ahead, and then we will hang here. If the |
| 8647 | the descriptor; otherwise we will leave a zombie thread | 8659 | reader thread exits normally, it will close the |
| 8660 | descriptor; otherwise we will leave a zombie thread | ||
| 8648 | hanging around. */ | 8661 | hanging around. */ |
| 8649 | rc = 0; | 8662 | rc = 0; |
| 8650 | /* Leave the flag set for the reader thread to close the | 8663 | /* Leave the flag set for the reader thread to close the |
diff --git a/src/w32proc.c b/src/w32proc.c index 7acfba64d70..f771ebc8511 100644 --- a/src/w32proc.c +++ b/src/w32proc.c | |||
| @@ -1287,10 +1287,21 @@ reader_thread (void *arg) | |||
| 1287 | } | 1287 | } |
| 1288 | /* If this thread was reading from a pipe process, close the | 1288 | /* If this thread was reading from a pipe process, close the |
| 1289 | descriptor used for reading, as sys_close doesn't in that case. */ | 1289 | descriptor used for reading, as sys_close doesn't in that case. */ |
| 1290 | if (fd_info[fd].flags == FILE_DONT_CLOSE) | 1290 | if ((fd_info[fd].flags & FILE_DONT_CLOSE) == FILE_DONT_CLOSE) |
| 1291 | { | 1291 | { |
| 1292 | fd_info[fd].flags = 0; | 1292 | int i; |
| 1293 | _close (fd); | 1293 | /* If w32.c:sys_close is still processing this descriptor, wait |
| 1294 | for a while for it to finish. */ | ||
| 1295 | for (i = 0; i < 5; i++) | ||
| 1296 | { | ||
| 1297 | if (fd_info[fd].flags == FILE_DONT_CLOSE) | ||
| 1298 | { | ||
| 1299 | fd_info[fd].flags = 0; | ||
| 1300 | _close (fd); | ||
| 1301 | break; | ||
| 1302 | } | ||
| 1303 | Sleep (5); | ||
| 1304 | } | ||
| 1294 | } | 1305 | } |
| 1295 | return 0; | 1306 | return 0; |
| 1296 | } | 1307 | } |