diff --git a/NEWS b/NEWS index c6a633fad5..f3ead6c1d0 100644 --- a/NEWS +++ b/NEWS @@ -15,11 +15,14 @@ ver 0.25 (not yet released) * switch to C++23 * require Meson 1.2 -ver 0.24.9 (not yet released) +ver 0.24.9 (2026/03/11) * input - curl: fix build failure after CURL 8.19 API change +* output + - fix auto-reopen after error times out * database - upnp: refuse to build with libupnp >= 1.14.26 due to API breakage +* abort if threads were created by a library during process startup ver 0.24.8 (2026/01/26) * input diff --git a/doc/developer.rst b/doc/developer.rst index f1d6d1f632..143d81976e 100644 --- a/doc/developer.rst +++ b/doc/developer.rst @@ -32,6 +32,16 @@ Some example code: } +C++ Subset +========== + +The following C++ features are not allowed in MPD: + +* :code:`` (bloated) +* :code:`` (bloated, weird error handling) +* :code:`` (PCRE is better) + + Error handling ============== diff --git a/meson.build b/meson.build index 934cb165bd..72401faa8d 100644 --- a/meson.build +++ b/meson.build @@ -472,6 +472,7 @@ subdir('src/cmdline') subdir('src/time') subdir('src/lib/icu') subdir('src/io') +subdir('src/io/linux') subdir('src/fs') subdir('src/io/fs') subdir('src/io/uring') @@ -625,6 +626,7 @@ mpd = build_target( fs_dep, net_dep, util_dep, + io_linux_dep, event_dep, thread_dep, neighbor_glue_dep, diff --git a/src/Main.cxx b/src/Main.cxx index 9dccfc01b9..6d494aebb4 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -45,6 +45,10 @@ #include "config/PartitionConfig.hxx" #include "util/ScopeExit.hxx" +#ifdef __linux__ +#include "io/linux/ProcStatus.hxx" +#endif + #ifdef ENABLE_DAEMON #include "unix/Daemon.hxx" #endif @@ -693,6 +697,15 @@ mpd_main(int argc, char *argv[]) int main(int argc, char *argv[]) noexcept try { +#ifdef __linux__ + if (ProcStatusThreads() > 1) + /* threads created by libraries before main() can + cause all sorts of bugs that are difficult to + analyze; bail out quickly and refuse to run if we + detect such a thing */ + throw "Background threads were detected before MPD could initialize. This is likely caused by a misbehaving library. Aborting to prevent erroneous behavior. Please report."; +#endif + AtScopeExit() { log_deinit(); }; #ifdef _WIN32 diff --git a/src/decoder/plugins/SidplayDecoderPlugin.cxx b/src/decoder/plugins/SidplayDecoderPlugin.cxx index 9fb4c92603..ad4af1c402 100644 --- a/src/decoder/plugins/SidplayDecoderPlugin.cxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.cxx @@ -18,6 +18,7 @@ #include "util/AllocatedString.hxx" #include "util/CharUtil.hxx" #include "util/ByteOrder.hxx" +#include "util/StringCompare.hxx" #include "Log.hxx" #include @@ -34,8 +35,6 @@ #include #include -#include - #define SUBTUNE_PREFIX "tune_" static constexpr Domain sidplay_domain("sidplay"); @@ -136,11 +135,10 @@ struct SidplayContainerPath { static unsigned ParseSubtuneName(const char *base) noexcept { - if (memcmp(base, SUBTUNE_PREFIX, sizeof(SUBTUNE_PREFIX) - 1) != 0) + base = StringAfterPrefix(base, SUBTUNE_PREFIX); + if (base == nullptr) return 0; - base += sizeof(SUBTUNE_PREFIX) - 1; - char *endptr; auto track = strtoul(base, &endptr, 10); if (endptr == base || *endptr != '.') diff --git a/src/io/linux/ProcStatus.cxx b/src/io/linux/ProcStatus.cxx new file mode 100644 index 0000000000..3d8b9fc3fd --- /dev/null +++ b/src/io/linux/ProcStatus.cxx @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: BSD-2-Clause +// author: Max Kellermann + +#include "ProcStatus.hxx" +#include "io/UniqueFileDescriptor.hxx" +#include "util/NumberParser.hxx" +#include "util/StringSplit.hxx" + +using std::string_view_literals::operator""sv; + +unsigned +ProcStatusThreads() noexcept +{ + UniqueFileDescriptor fd; + if (!fd.OpenReadOnly("/proc/self/status")) + return 0; + + char buffer[4096]; + ssize_t nbytes = fd.Read(std::as_writable_bytes(std::span{buffer})); + if (nbytes <= 0) + return 0; + + const std::string_view contents{buffer, static_cast(nbytes)}; + + static constexpr std::string_view prefix = "\nThreads:\t"sv; + const auto position = contents.find(prefix); + if (position == contents.npos) + return 0; + + const std::string_view value_string = Split(contents.substr(position + prefix.size()), '\n').first; + unsigned value; + if (!ParseIntegerTo(value_string, value)) + return 0; + + return value; +} diff --git a/src/io/linux/ProcStatus.hxx b/src/io/linux/ProcStatus.hxx new file mode 100644 index 0000000000..ffe6bfc4c4 --- /dev/null +++ b/src/io/linux/ProcStatus.hxx @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSD-2-Clause +// author: Max Kellermann + +#pragma once + +/** + * Attempt to determine the number of threads of the current process. + * + * @return the number of threads or 0 on error + */ +[[gnu::pure]] +unsigned +ProcStatusThreads() noexcept; diff --git a/src/io/linux/meson.build b/src/io/linux/meson.build new file mode 100644 index 0000000000..d251aedf3a --- /dev/null +++ b/src/io/linux/meson.build @@ -0,0 +1,23 @@ +if not is_linux + io_linux_dep = dependency('', required: false) + subdir_done() +endif + +io_linux = static_library( + 'io_linux', + 'ProcStatus.cxx', + include_directories: inc, + dependencies: [ + io_dep, + util_dep, + ], +) + +io_linux_dep = declare_dependency( + link_with: io_linux, + dependencies: [ + fmt_dep, + io_dep, + util_dep, + ], +) diff --git a/src/output/Control.cxx b/src/output/Control.cxx index d8fa73bfe3..b936517dc3 100644 --- a/src/output/Control.cxx +++ b/src/output/Control.cxx @@ -305,7 +305,7 @@ AudioOutputControl::LockUpdate(const AudioFormat audio_format, if (enabled && really_enabled) { if (force || !fail_timer.IsDefined() || - fail_timer.Check(REOPEN_AFTER * 1000)) { + fail_timer.Check(REOPEN_AFTER)) { return Open(std::move(lock), audio_format, mp); } } else if (IsOpen()) diff --git a/subprojects/sqlite3.wrap b/subprojects/sqlite3.wrap index 77fae8e774..0bbb146e8b 100644 --- a/subprojects/sqlite3.wrap +++ b/subprojects/sqlite3.wrap @@ -1,14 +1,14 @@ [wrap-file] -directory = sqlite-amalgamation-3510200 -source_url = https://www.sqlite.org/2026/sqlite-amalgamation-3510200.zip -source_filename = sqlite-amalgamation-3510200.zip -source_hash = 6e2a845a493026bdbad0618b2b5a0cf48584faab47384480ed9f592d912f23ec -source_fallback_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.51.2-1/get_source/sqlite-amalgamation-3510200.zip -patch_filename = sqlite3_3.51.2-1_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.51.2-1/get_patch -patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.51.2-1/sqlite3_3.51.2-1_patch.zip -patch_hash = eef624e9916ec7b7c62dccbf1a04a8bc6fdcacfb5b58daaba695c204e00bec67 -wrapdb_version = 3.51.2-1 +directory = sqlite-amalgamation-3520000 +source_url = https://www.sqlite.org/2026/sqlite-amalgamation-3520000.zip +source_filename = sqlite-amalgamation-3520000.zip +source_hash = 45d0fea15971dd1300e5b509f48ca134621e10d9297713b64618fbca21b325fa +source_fallback_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.52.0-1/get_source/sqlite-amalgamation-3520000.zip +patch_filename = sqlite3_3.52.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/sqlite3_3.52.0-1/get_patch +patch_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sqlite3_3.52.0-1/sqlite3_3.52.0-1_patch.zip +patch_hash = a4b065e5bf76df313759c95aba623456744953b432b68e0c76f87b16d839293b +wrapdb_version = 3.52.0-1 [provide] dependency_names = sqlite3