fix: stabilize linux embedded mpv startup

This commit is contained in:
4gray committed 2026-06-13 13:24:56 +02:00
1 parent 21bf695779
commit 0c4fbd0891
2 files changed
+87 -25

No files matched your search

@@ -53,27 +53,29 @@ IPTVNATOR_DECLARE_MPV_DYNAMIC_SYMBOL(mpv_wakeup)
#include <algorithm>
#include <atomic>
#include <charconv>
#include <chrono>
#include <cctype>
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <locale>
#include <memory>
#include <mutex>
#include <optional>
#include <sstream>
#include <string>
#include <system_error>
#include <thread>
#include <unordered_map>
#include <utility>
#include <vector>
#ifdef __linux__
#include <csignal>
#include <dirent.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>
@@ -204,10 +206,19 @@ std::string formatInvariantDouble(double value)
return "0";
}
std::ostringstream output;
output.imbue(std::locale::classic());
output << std::setprecision(17) << value;
return output.str();
char buffer[64]{};
const auto result = std::to_chars(
buffer,
buffer + sizeof(buffer),
value,
std::chars_format::general,
17
);
if (result.ec != std::errc()) {
return "0";
}
return std::string(buffer, result.ptr);
}
std::string nowIsoString()
@@ -594,18 +605,37 @@ std::vector<std::string> buildLinuxMpvArguments(
return arguments;
}
long inheritedFileDescriptorLimit()
void closeInheritedFileDescriptors()
{
const long maxOpenFiles = sysconf(_SC_OPEN_MAX);
return maxOpenFiles > 0 ? maxOpenFiles : 1024;
}
DIR* directory = opendir("/proc/self/fd");
if (directory) {
const int directoryFd = dirfd(directory);
while (dirent* entry = readdir(directory)) {
char* end = nullptr;
const long value = std::strtol(entry->d_name, &end, 10);
if (
!end ||
*end != '\0' ||
value <= STDERR_FILENO ||
value > INT_MAX
) {
continue;
}
void closeInheritedFileDescriptors(long fileDescriptorLimit)
{
for (long fileDescriptor = STDERR_FILENO + 1;
fileDescriptor < fileDescriptorLimit;
fileDescriptor += 1) {
const int descriptor = static_cast<int>(fileDescriptor);
const int descriptor = static_cast<int>(value);
if (descriptor == directoryFd) {
continue;
}
const int flags = fcntl(descriptor, F_GETFD);
if (flags >= 0) {
fcntl(descriptor, F_SETFD, flags | FD_CLOEXEC);
}
}
closedir(directory);
return;
}
for (int descriptor = STDERR_FILENO + 1; descriptor < 1024; descriptor += 1) {
const int flags = fcntl(descriptor, F_GETFD);
if (flags >= 0) {
fcntl(descriptor, F_SETFD, flags | FD_CLOEXEC);
@@ -945,7 +975,6 @@ pid_t spawnLinuxMpvProcess(
envp.push_back(nullptr);
const bool traceEnabled = std::getenv("IPTVNATOR_TRACE_EMBEDDED_MPV");
const long fileDescriptorLimit = inheritedFileDescriptorLimit();
const pid_t processId = fork();
if (processId != 0) {
return processId;
@@ -962,7 +991,7 @@ pid_t spawnLinuxMpvProcess(
}
}
closeInheritedFileDescriptors(fileDescriptorLimit);
closeInheritedFileDescriptors();
execvpe(argv[0], argv.data(), envp.data());
_exit(127);
}
@@ -324,14 +324,19 @@ describe('Embedded MPV native source recording invariants', () => {
expect(widCommonSource).toContain(
'arguments.push_back("--gpu-context=x11egl");'
);
expect(widCommonSource).toContain('inheritedFileDescriptorLimit()');
expect(widCommonSource).toContain(
'void closeInheritedFileDescriptors()'
);
expect(widCommonSource).toContain('opendir("/proc/self/fd")');
expect(widCommonSource).toContain('readdir(directory)');
expect(widCommonSource).toContain(
'fcntl(descriptor, F_SETFD, flags | FD_CLOEXEC)'
);
expect(widCommonSource).toContain('closeInheritedFileDescriptors();');
expect(widCommonSource).not.toContain('inheritedFileDescriptorLimit()');
expect(widCommonSource).not.toContain(
'closeInheritedFileDescriptors(fileDescriptorLimit);'
);
expect(widCommonSource).toContain('flags | FD_CLOEXEC');
expect(widCommonSource).not.toContain('opendir(');
expect(widCommonSource).not.toContain('readdir(');
expect(widCommonSource).not.toContain('closedir(');
});
it('drives Linux out-of-process MPV state and controls over JSON IPC', () => {
@@ -370,14 +375,42 @@ describe('Embedded MPV native source recording invariants', () => {
expect(refreshBody).not.toContain('clampVolumePercent(*volume)');
});
it('only marks open file descriptors close-on-exec before Linux MPV exec', () => {
const closeDescriptorsBody = sourceFunctionBody(
widCommonSource,
'void closeInheritedFileDescriptors(',
'closeInheritedFileDescriptors'
);
expect(closeDescriptorsBody).toContain('opendir("/proc/self/fd")');
expect(closeDescriptorsBody).toContain('readdir(directory)');
expect(closeDescriptorsBody).toContain('fcntl(descriptor, F_GETFD)');
expect(closeDescriptorsBody).toContain(
'fcntl(descriptor, F_SETFD, flags | FD_CLOEXEC)'
);
expect(widCommonSource).not.toContain('sysconf(_SC_OPEN_MAX)');
expect(widCommonSource).not.toContain(
'fileDescriptor < fileDescriptorLimit'
);
const spawnBody = sourceFunctionBody(
widCommonSource,
'pid_t spawnLinuxMpvProcess(',
'spawnLinuxMpvProcess'
);
expect(spawnBody).toContain('closeInheritedFileDescriptors();');
expect(spawnBody).not.toContain('inheritedFileDescriptorLimit()');
});
it('formats MPV floating-point values independently from the user locale', () => {
const formatterBody = sourceFunctionBody(
widCommonSource,
'std::string formatInvariantDouble(',
'formatInvariantDouble'
);
expect(formatterBody).toContain('std::locale::classic()');
expect(formatterBody).toContain('std::setprecision(17)');
expect(formatterBody).toContain('std::to_chars(');
expect(formatterBody).toContain('std::chars_format::general');
expect(formatterBody).not.toContain('std::locale::classic()');
expect(formatterBody).not.toContain('std::ostringstream');
const linuxArgumentsBody = sourceFunctionBody(
widCommonSource,