Repository: streamlink/streamlink

Issue:
SegmentedStreamWriter.close() does not reliably finish before CLI exits (race condition)
### Checklist

- [X] This is a bug report and not a different kind of issue
- [X] [I have read the contribution guidelines](https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink)
- [X] [I have checked the list of open and recently closed bug reports](https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22bug%22)
- [X] [I have checked the commit log of the master branch](https://github.com/streamlink/streamlink/commits/master)

### Streamlink version

Latest build from the master branch

### Description

### Background
I was doing some works on latest `master` branch, adding some feature on Segmented/HLS streams, and it requires some cleanup, so I added them in the end of `SegmentedStreamWriter.close()`, like

```python
        self.closed = True
        self.reader.close()
        self.executor.shutdown(wait=True, cancel_futures=True)

        __my_extra_cleanup()
```

And I found my cleanup code does not always being executed, when HLS streams end normally.

### Problem
When an HLS stream ends normally, the whole shutdown process is triggered by the last line of `SegmentedStreamWriter.run()`, `self.close()`. Then `SegmentedStreamWriter.close()` calls `SegmentedStreamReader.close()`, then the iteration loop of `stream_cli.main:read_stream()` will reach its end, then `main()` exits.

The problem is, `SegmentedStreamWriter.run() -> SegmentedStreamWriter.close()` was run in a separated thread, which means `SegmentedStreamWriter.close()` cannot reliably finish its work before main thread exits.

### To reproduce
To reliably trigger the problem, adding a sleep to original `SegmentedStreamWriter.close()`, like

```python
        self.closed = True
        self.reader.close()
        self.executor.shutdown(wait=True, cancel_futures=True)

        time.sleep(3)
        log.debug("SegmentedStreamWriter.close() ends")
```

Then run the CLI with a short HLS stream, the `SegmentedStreamWriter.close() ends` message never appears.


### Debug log

```text
......

[cli][info] Stream ended
[cli][info] Closing currently open stream...
```

### Expected result
```text
......

[cli][info] Stream ended
[cli][info] Closing currently open stream...
[stream.segmented][debug] SegmentedStreamWriter.close() ends
```



Discussion:

Resolving patch:
diff --git a/src/streamlink/stream/segmented.py b/src/streamlink/stream/segmented.py
--- a/src/streamlink/stream/segmented.py
+++ b/src/streamlink/stream/segmented.py
@@ -3,7 +3,7 @@
 from concurrent import futures
 from concurrent.futures import Future, ThreadPoolExecutor
 from sys import version_info
-from threading import Event, Thread
+from threading import Event, Thread, current_thread
 from typing import Any, Optional
 
 from streamlink.buffers import RingBuffer
@@ -228,6 +228,12 @@ def close(self):
         self.writer.close()
         self.buffer.close()
 
+        current = current_thread()
+        if current is not self.worker:  # pragma: no branch
+            self.worker.join(timeout=self.timeout)
+        if current is not self.writer:  # pragma: no branch
+            self.writer.join(timeout=self.timeout)
+
     def read(self, size):
         return self.buffer.read(
             size,


Test patch: