Compare commits

...

36 Commits

Author SHA1 Message Date
Max Kellermann
975a4ae871 release v0.20.16 2018-02-03 19:55:07 +01:00
Max Kellermann
56aaf3c73e python/build/libs: upgrade CURL to 7.58.0 2018-02-03 19:46:31 +01:00
Max Kellermann
12fd1cad0c archive/iso9660: libcdio 2.0 compatibility
Closes #173
2018-02-03 19:32:31 +01:00
Max Kellermann
e573cbf032 db/update/Queue: work around GCC7 -Wuninitialized 2018-02-01 19:53:42 +01:00
Max Kellermann
dead461542 lib/upnp/Init: enable IPv6 2018-01-31 18:15:46 +01:00
Max Kellermann
3d5da1ac73 lib/upnp/Init: use nullptr instead of 0 2018-01-31 18:14:26 +01:00
Max Kellermann
ec408ca6a6 output/pulse: fix crash during auto-detection
The PulseOutput needs to be "enabled" before WaitConnection() may be
called.

Closes #207
2018-01-30 10:06:36 +01:00
Max Kellermann
ea66cdd6a5 test/read_mixer: another kludge to work around -Wnull-dereference 2018-01-23 16:42:25 +01:00
Max Kellermann
f762e8034f test/NullMixerListener: new class to fix -Wnull-dereference 2018-01-23 16:28:56 +01:00
Max Kellermann
bb1e369f30 playlist/SoundCloud: fix -Wunused-lambda-capture 2018-01-23 09:57:52 +01:00
Max Kellermann
8376578921 db/simple/Mount: drop mount point prefix from LOCATE_TAG_BASE_TYPE
Fixes search within mount points, resulting in error "No such
directory".

Closes #190
2018-01-19 23:52:57 +01:00
Max Kellermann
ed2354cd9d SongFilter: allow copying items 2018-01-19 23:52:03 +01:00
Max Kellermann
386688b87a SongFilter: use std::string instead of AllocatedString 2018-01-19 23:51:42 +01:00
Max Kellermann
38d56dddf1 lib/icu/Compare: allow copying 2018-01-19 23:49:50 +01:00
Max Kellermann
e8975942ec Makefile.am: link libicu.a before libutil.a
libicu.a depends on libutil.a.
2018-01-19 23:38:24 +01:00
Max Kellermann
3ca80a7336 util/RefCount, db/simple/Mount: remove obsolete libc++ workarounds
No longer a problem with NDK r16.
2018-01-19 23:19:46 +01:00
Max Kellermann
d029dae7ad Makefile.am: use Android SDK build-tools 27.0.0 2018-01-19 23:04:54 +01:00
Max Kellermann
9e058732ee android/build.py: add -fpic
Android native code should be position-independent.

The NDK build scripts use "-fpic" instead of "-fPIC" for ARM, but that
doesn't work with FFmpeg's assembly code, because it requires
R_ARM_MOVW_ABS_NC which is unavailable with "-fpic".
2018-01-19 22:40:59 +01:00
Max Kellermann
cad5d11261 android/build.py: simplify libc++ flags
By telling clang which implementation to use, we avoid the dependency
on libstdc++.so.
2018-01-19 22:36:19 +01:00
Max Kellermann
fcaedec2ab {android,win32}/build.py: move "-O* -g" to common_flags 2018-01-19 12:33:28 +01:00
Max Kellermann
ead9d59e88 python/build/libs.py: build only libFLAC, no programs 2018-01-19 12:33:03 +01:00
Max Kellermann
34b8a17ccd python/build/autotools.py: add "subdir" parameter 2018-01-19 11:39:36 +01:00
Max Kellermann
a53d081c39 python/build/libs.py: disable libFLAC API documentation 2018-01-19 11:38:24 +01:00
Max Kellermann
823134e4ba python/build/libs.py: disable Opus documentation and extra programs 2018-01-19 11:32:40 +01:00
Max Kellermann
272167b4fc python/build/libs.py: update LAME to 3.100 2018-01-18 22:07:28 +01:00
Max Kellermann
92f09bba94 Makefile.am: rename JAVA_SOURCES to JAVA_SOURCE_PATHS
Work around automake warning:

    Makefile.am:310: warning: variable 'JAVA_SOURCES' is defined but no program or
    Makefile.am:310: library has 'JAVA' as canonical name (possible typo)

Closes #195
2018-01-18 22:05:04 +01:00
Max Kellermann
1f50bdb230 event/Loop: use std::atomic_bool for the "quit" variable
Fixes thread sanitizer warnings.
2018-01-08 10:06:23 +01:00
Max Kellermann
2eef4e6716 thread/Thread: add debug attribute "inside_handle"
This attribute shall be used only for IsInside() to make this safe
against a race condition described in #188:

> There is no requirement on the implementation that the ID of the
> created thread be available before the newly created thread starts
> executing.

http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_create.html):

This means that on some pthread implementations (e.g. Haiku), the
assert(thread.IsInside()) could fail.

Closes #188
2018-01-08 09:58:18 +01:00
Max Kellermann
d989dbfec4 thread/Thread: make IsInside() debug-only
This method is only used inside assert().
2018-01-08 09:56:39 +01:00
Max Kellermann
ca9fcec364 thread/Thread: fix indent 2018-01-08 09:49:08 +01:00
Max Kellermann
354104f9a9 thread/{Thread,Id}: use defaul-initialized pthread_t as "undefined" value
Use the "==" operator instead of pthread_equal().

This allows us to eliminate two boolean flags which are there to avoid
race conditions, and made the thing so fragile that I got tons of
(correct) thread sanitizer warnings.
2018-01-07 17:20:26 +01:00
Max Kellermann
8649ea3d6f thread/Thread: use BoundMethod 2018-01-07 17:20:26 +01:00
Max Kellermann
752ff12c37 thread/Thread: move code to Run() 2018-01-07 17:20:26 +01:00
Max Kellermann
4bb89b1755 MusicPipe: lock the mutex in Peek() and GetSize() 2018-01-07 17:20:22 +01:00
Max Kellermann
0ef553d30e increment version number to 0.20.16 2018-01-06 13:15:47 +01:00
Max Kellermann
43a62aef07 android: release 0.20.15 2018-01-05 18:09:56 +01:00
43 changed files with 337 additions and 223 deletions

View File

@@ -61,8 +61,8 @@ src_mpd_LDADD = \
libnet.a \ libnet.a \
$(FS_LIBS) \ $(FS_LIBS) \
libsystem.a \ libsystem.a \
libutil.a \
$(ICU_LDADD) \ $(ICU_LDADD) \
libutil.a \
$(SYSTEMD_DAEMON_LIBS) $(SYSTEMD_DAEMON_LIBS)
src_mpd_SOURCES = \ src_mpd_SOURCES = \
@@ -292,7 +292,7 @@ clean-local:
libmpd.so: $(filter %.a,$(src_mpd_LDADD)) libmain.a libmpd.so: $(filter %.a,$(src_mpd_LDADD)) libmain.a
$(AM_V_CXXLD)$(CXXLD) -shared -Wl,--no-undefined,-shared,-Bsymbolic -llog -lz -o $@ $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) src/libmain_a-Main.o $(src_mpd_LDADD) $(LIBS) $(AM_V_CXXLD)$(CXXLD) -shared -Wl,--no-undefined,-shared,-Bsymbolic -llog -lz -o $@ $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) src/libmain_a-Main.o $(src_mpd_LDADD) $(LIBS)
ANDROID_SDK_BUILD_TOOLS_VERSION = 20.0.0 ANDROID_SDK_BUILD_TOOLS_VERSION = 27.0.0
ANDROID_SDK_PLATFORM = android-17 ANDROID_SDK_PLATFORM = android-17
ANDROID_BUILD_TOOLS_DIR = $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION) ANDROID_BUILD_TOOLS_DIR = $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)
@@ -307,7 +307,7 @@ ANDROID_XML_RES := $(wildcard $(srcdir)/android/res/*/*.xml)
ANDROID_XML_RES_COPIES := $(patsubst $(srcdir)/android/%,android/build/%,$(ANDROID_XML_RES)) ANDROID_XML_RES_COPIES := $(patsubst $(srcdir)/android/%,android/build/%,$(ANDROID_XML_RES))
JAVA_SOURCE_NAMES = Bridge.java Loader.java Main.java JAVA_SOURCE_NAMES = Bridge.java Loader.java Main.java
JAVA_SOURCES = $(addprefix $(srcdir)/android/src/,$(JAVA_SOURCE_NAMES)) JAVA_SOURCE_PATHS = $(addprefix $(srcdir)/android/src/,$(JAVA_SOURCE_NAMES))
JAVA_CLASSFILES_DIR = android/build/classes JAVA_CLASSFILES_DIR = android/build/classes
@@ -328,7 +328,7 @@ android/build/resources.apk: $(ANDROID_XML_RES_COPIES) android/build/res/drawabl
# R.java is generated by aapt, when resources.apk is generated # R.java is generated by aapt, when resources.apk is generated
android/build/gen/org/musicpd/R.java: android/build/resources.apk android/build/gen/org/musicpd/R.java: android/build/resources.apk
android/build/classes.dex: $(JAVA_SOURCES) android/build/gen/org/musicpd/R.java android/build/classes.dex: $(JAVA_SOURCE_PATHS) android/build/gen/org/musicpd/R.java
@$(MKDIR_P) $(JAVA_CLASSFILES_DIR) @$(MKDIR_P) $(JAVA_CLASSFILES_DIR)
$(JAVAC) -source 1.5 -target 1.5 -Xlint:-options \ $(JAVAC) -source 1.5 -target 1.5 -Xlint:-options \
-cp $(ANDROID_SDK_PLATFORM_DIR)/android.jar:$(JAVA_CLASSFILES_DIR) \ -cp $(ANDROID_SDK_PLATFORM_DIR)/android.jar:$(JAVA_CLASSFILES_DIR) \
@@ -2159,6 +2159,7 @@ test_run_output_LDADD = $(MPD_LIBS) \
libutil.a libutil.a
test_run_output_SOURCES = test/run_output.cxx \ test_run_output_SOURCES = test/run_output.cxx \
test/ScopeIOThread.hxx \ test/ScopeIOThread.hxx \
test/NullMixerListener.hxx \
src/Log.cxx src/LogBackend.cxx \ src/Log.cxx src/LogBackend.cxx \
src/IOThread.cxx \ src/IOThread.cxx \
src/output/Domain.cxx \ src/output/Domain.cxx \
@@ -2182,6 +2183,7 @@ test_read_mixer_LDADD = \
libsystem.a \ libsystem.a \
libutil.a libutil.a
test_read_mixer_SOURCES = test/read_mixer.cxx \ test_read_mixer_SOURCES = test/read_mixer.cxx \
test/NullMixerListener.hxx \
src/Log.cxx src/LogBackend.cxx \ src/Log.cxx src/LogBackend.cxx \
src/mixer/MixerControl.cxx \ src/mixer/MixerControl.cxx \
src/filter/FilterPlugin.cxx \ src/filter/FilterPlugin.cxx \

10
NEWS
View File

@@ -1,3 +1,13 @@
ver 0.20.16 (2018/02/03)
* output
- pulse: fix crash during auto-detection
* database
- simple: fix search within mount points
- upnp: enable IPv6
* archive
- iso9660: libcdio 2.0 compatibility
* fix crash in debug build on Haiku and other operating systems
ver 0.20.15 (2018/01/05) ver 0.20.15 (2018/01/05)
* queue: fix crash after seek failure * queue: fix crash after seek failure
* resampler * resampler

View File

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd" package="org.musicpd"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="13" android:versionCode="15"
android:versionName="0.19.9"> android:versionName="0.20.16">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/>

View File

@@ -65,7 +65,9 @@ class AndroidNdkToolchain:
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch) llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
llvm_triple = 'armv7-none-linux-androideabi' llvm_triple = 'armv7-none-linux-androideabi'
common_flags = '-march=armv7-a -mfloat-abi=softfp' common_flags = '-Os -g'
common_flags += ' -fPIC'
common_flags += ' -march=armv7-a -mfloat-abi=softfp'
toolchain_bin = os.path.join(toolchain_path, 'bin') toolchain_bin = os.path.join(toolchain_path, 'bin')
llvm_bin = os.path.join(llvm_path, 'bin') llvm_bin = os.path.join(llvm_path, 'bin')
@@ -80,8 +82,8 @@ class AndroidNdkToolchain:
self.nm = os.path.join(toolchain_bin, arch + '-nm') self.nm = os.path.join(toolchain_bin, arch + '-nm')
self.strip = os.path.join(toolchain_bin, arch + '-strip') self.strip = os.path.join(toolchain_bin, arch + '-strip')
self.cflags = '-Os -g ' + common_flags self.cflags = common_flags
self.cxxflags = '-Os -g ' + common_flags self.cxxflags = common_flags
self.cppflags = '--sysroot=' + sysroot + \ self.cppflags = '--sysroot=' + sysroot + \
' -isystem ' + os.path.join(install_prefix, 'include') + \ ' -isystem ' + os.path.join(install_prefix, 'include') + \
' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \ ' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \
@@ -100,15 +102,13 @@ class AndroidNdkToolchain:
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++') libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi) libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi)
libstdcxx_cppflags = '-nostdinc++ -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include') libstdcxx_flags = '-stdlib=libc++'
libstdcxx_ldadd = os.path.join(libcxx_libs_path, 'libc++_static.a') + ' ' + os.path.join(libcxx_libs_path, 'libc++abi.a') libstdcxx_cxxflags = libstdcxx_flags + ' -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
libstdcxx_ldflags = libstdcxx_flags + ' -static-libstdc++ -L' + libcxx_libs_path
if self.is_armv7:
libstdcxx_ldadd += ' ' + os.path.join(libcxx_libs_path, 'libunwind.a')
if use_cxx: if use_cxx:
self.libs += ' ' + libstdcxx_ldadd self.cxxflags += ' ' + libstdcxx_cxxflags
self.cppflags += ' ' + libstdcxx_cppflags self.ldflags += ' ' + libstdcxx_ldflags
self.env = dict(os.environ) self.env = dict(os.environ)

View File

@@ -1,10 +1,10 @@
AC_PREREQ(2.60) AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.15, musicpd-dev-team@lists.sourceforge.net) AC_INIT(mpd, 0.20.16, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0 VERSION_MAJOR=0
VERSION_MINOR=20 VERSION_MINOR=20
VERSION_REVISION=15 VERSION_REVISION=16
VERSION_EXTRA=0 VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx]) AC_CONFIG_SRCDIR([src/Main.cxx])

View File

@@ -8,6 +8,7 @@ class AutotoolsProject(MakeProject):
cppflags='', cppflags='',
ldflags='', ldflags='',
libs='', libs='',
subdirs=None,
**kwargs): **kwargs):
MakeProject.__init__(self, url, md5, installed, **kwargs) MakeProject.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args self.configure_args = configure_args
@@ -15,6 +16,7 @@ class AutotoolsProject(MakeProject):
self.cppflags = cppflags self.cppflags = cppflags
self.ldflags = ldflags self.ldflags = ldflags
self.libs = libs self.libs = libs
self.subdirs = subdirs
def configure(self, toolchain): def configure(self, toolchain):
src = self.unpack(toolchain) src = self.unpack(toolchain)
@@ -51,4 +53,8 @@ class AutotoolsProject(MakeProject):
def build(self, toolchain): def build(self, toolchain):
build = self.configure(toolchain) build = self.configure(toolchain)
MakeProject.build(self, toolchain, build) if self.subdirs is not None:
for subdir in self.subdirs:
MakeProject.build(self, toolchain, os.path.join(build, subdir))
else:
MakeProject.build(self, toolchain, build)

View File

@@ -29,6 +29,8 @@ opus = AutotoolsProject(
'lib/libopus.a', 'lib/libopus.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
'--disable-doc',
'--disable-extra-programs',
], ],
# suppress "visibility default" from opus_defines.h # suppress "visibility default" from opus_defines.h
@@ -42,7 +44,9 @@ flac = AutotoolsProject(
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
'--disable-xmms-plugin', '--disable-cpplibs', '--disable-xmms-plugin', '--disable-cpplibs',
'--disable-doxygen-docs',
], ],
subdirs=['include', 'src/libFLAC'],
) )
zlib = ZlibProject( zlib = ZlibProject(
@@ -83,8 +87,8 @@ libmad = AutotoolsProject(
) )
liblame = AutotoolsProject( liblame = AutotoolsProject(
'http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz', 'http://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz',
'24346b4158e4af3bd9f2e194bb23eb473c75fb7377011523353196b19b9a23ff', 'ddfe36cab873794038ae2c1210557ad34857a4b6bdc515785d1da9e175b1da1e',
'lib/libmp3lame.a', 'lib/libmp3lame.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
@@ -335,8 +339,8 @@ ffmpeg = FfmpegProject(
) )
curl = AutotoolsProject( curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.57.0.tar.xz', 'http://curl.haxx.se/download/curl-7.58.0.tar.xz',
'f5f6fd3c72b7b8389969f4fb671ed8532fa9b5bb7a5cae7ca89bc1cea45c7878', '6a813875243609eb75f37fa72044e4ad618b55ec15a4eafdac2df6a7e800e3e3',
'lib/libcurl.a', 'lib/libcurl.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',

View File

@@ -27,12 +27,17 @@
#include <assert.h> #include <assert.h>
static struct { static struct IOThread {
Mutex mutex; Mutex mutex;
Cond cond; Cond cond;
EventLoop *loop; EventLoop *loop;
Thread thread; Thread thread;
IOThread():thread(BIND_THIS_METHOD(Run)) {}
private:
void Run() noexcept;
} io; } io;
void void
@@ -44,15 +49,15 @@ io_thread_run(void)
io.loop->Run(); io.loop->Run();
} }
static void inline void
io_thread_func(gcc_unused void *arg) IOThread::Run() noexcept
{ {
SetThreadName("io"); SetThreadName("io");
/* lock+unlock to synchronize with io_thread_start(), to be /* lock+unlock to synchronize with io_thread_start(), to be
sure that io.thread is set */ sure that io.thread is set */
io.mutex.lock(); mutex.lock();
io.mutex.unlock(); mutex.unlock();
io_thread_run(); io_thread_run();
} }
@@ -73,7 +78,7 @@ io_thread_start()
assert(!io.thread.IsDefined()); assert(!io.thread.IsDefined());
const std::lock_guard<Mutex> protect(io.mutex); const std::lock_guard<Mutex> protect(io.mutex);
io.thread.Start(io_thread_func, nullptr); io.thread.Start();
} }
void void
@@ -103,8 +108,12 @@ io_thread_get() noexcept
return *io.loop; return *io.loop;
} }
#ifndef NDEBUG
bool bool
io_thread_inside() noexcept io_thread_inside() noexcept
{ {
return io.thread.IsInside(); return io.thread.IsInside();
} }
#endif

View File

@@ -20,6 +20,7 @@
#ifndef MPD_IO_THREAD_HXX #ifndef MPD_IO_THREAD_HXX
#define MPD_IO_THREAD_HXX #define MPD_IO_THREAD_HXX
#include "check.h"
#include "Compiler.h" #include "Compiler.h"
class EventLoop; class EventLoop;
@@ -53,6 +54,8 @@ gcc_const
EventLoop & EventLoop &
io_thread_get() noexcept; io_thread_get() noexcept;
#ifndef NDEBUG
/** /**
* Is the current thread the I/O thread? * Is the current thread the I/O thread?
*/ */
@@ -61,3 +64,5 @@ bool
io_thread_inside() noexcept; io_thread_inside() noexcept;
#endif #endif
#endif

View File

@@ -95,6 +95,7 @@ public:
*/ */
gcc_pure gcc_pure
const MusicChunk *Peek() const noexcept { const MusicChunk *Peek() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return head; return head;
} }
@@ -120,6 +121,7 @@ public:
*/ */
gcc_pure gcc_pure
unsigned GetSize() const noexcept { unsigned GetSize() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return size; return size;
} }

View File

@@ -24,6 +24,8 @@
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/StringAPI.hxx" #include "util/StringAPI.hxx"
#include "util/StringCompare.hxx"
#include "util/StringView.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/TimeParser.hxx" #include "util/TimeParser.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
@@ -59,7 +61,7 @@ locate_parse_type(const char *str) noexcept
SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case) SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
:tag(_tag), :tag(_tag),
value(AllocatedString<>::Duplicate(_value)), value(_value),
fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare()) fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare())
{ {
} }
@@ -274,3 +276,33 @@ SongFilter::GetBase() const noexcept
return nullptr; return nullptr;
} }
SongFilter
SongFilter::WithoutBasePrefix(const char *_prefix) const noexcept
{
const StringView prefix(_prefix);
SongFilter result;
for (const auto &i : items) {
if (i.GetTag() == LOCATE_TAG_BASE_TYPE) {
const char *s = StringAfterPrefix(i.GetValue(), prefix);
if (s != nullptr) {
if (*s == 0)
continue;
if (*s == '/') {
++s;
if (*s != 0)
result.items.emplace_back(LOCATE_TAG_BASE_TYPE, s);
continue;
}
}
}
result.items.emplace_back(i);
}
return result;
}

View File

@@ -21,9 +21,9 @@
#define MPD_SONG_FILTER_HXX #define MPD_SONG_FILTER_HXX
#include "lib/icu/Compare.hxx" #include "lib/icu/Compare.hxx"
#include "util/AllocatedString.hxx"
#include "Compiler.h" #include "Compiler.h"
#include <string>
#include <list> #include <list>
#include <stdint.h> #include <stdint.h>
@@ -49,7 +49,7 @@ public:
class Item { class Item {
uint8_t tag; uint8_t tag;
AllocatedString<> value; std::string value;
/** /**
* This value is only set if case folding is enabled. * This value is only set if case folding is enabled.
@@ -66,11 +66,6 @@ public:
Item(unsigned tag, const char *value, bool fold_case=false); Item(unsigned tag, const char *value, bool fold_case=false);
Item(unsigned tag, time_t time); Item(unsigned tag, time_t time);
Item(const Item &other) = delete;
Item(Item &&) = default;
Item &operator=(const Item &other) = delete;
unsigned GetTag() const { unsigned GetTag() const {
return tag; return tag;
} }
@@ -157,6 +152,13 @@ public:
*/ */
gcc_pure gcc_pure
const char *GetBase() const noexcept; const char *GetBase() const noexcept;
/**
* Create a copy of the filter with the given prefix stripped
* from all #LOCATE_TAG_BASE_TYPE items. This is used to
* filter songs in mounted databases.
*/
SongFilter WithoutBasePrefix(const char *prefix) const noexcept;
}; };
/** /**

View File

@@ -115,7 +115,12 @@ Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
visitor.VisitArchiveEntry(path + 1); visitor.VisitArchiveEntry(path + 1);
} }
} }
#if LIBCDIO_VERSION_NUM >= 20000
iso9660_filelist_free(entlist);
#else
_cdio_list_free (entlist, true); _cdio_list_free (entlist, true);
#endif
} }
static ArchiveFile * static ArchiveFile *

View File

@@ -20,18 +20,12 @@
#include "config.h" #include "config.h"
#include "Mount.hxx" #include "Mount.hxx"
#include "PrefixedLightSong.hxx" #include "PrefixedLightSong.hxx"
#include "SongFilter.hxx"
#include "db/Selection.hxx" #include "db/Selection.hxx"
#include "db/LightDirectory.hxx" #include "db/LightDirectory.hxx"
#include "db/Interface.hxx" #include "db/Interface.hxx"
#include "fs/Traits.hxx" #include "fs/Traits.hxx"
#ifdef _LIBCPP_VERSION
/* workaround for "error: incomplete type 'PlaylistInfo' used in type
trait expression" with libc++ version 3900 (from Android NDK
r13b) */
#include "db/PlaylistInfo.hxx"
#endif
#include <string> #include <string>
struct PrefixedLightDirectory : LightDirectory { struct PrefixedLightDirectory : LightDirectory {
@@ -93,5 +87,16 @@ WalkMount(const char *base, const Database &db,
vp = std::bind(PrefixVisitPlaylist, vp = std::bind(PrefixVisitPlaylist,
base, std::ref(visit_playlist), _1, _2); base, std::ref(visit_playlist), _1, _2);
SongFilter prefix_filter;
if (base != nullptr && filter != nullptr) {
/* if the SongFilter contains a LOCATE_TAG_BASE_TYPE
item, copy the SongFilter and drop the mount point
from the filter, because the mounted database
doesn't know its own location within MPD's VFS */
prefix_filter = filter->WithoutBasePrefix(base);
filter = &prefix_filter;
}
db.Visit(DatabaseSelection(uri, recursive, filter), vd, vs, vp); db.Visit(DatabaseSelection(uri, recursive, filter), vd, vs, vp);
} }

View File

@@ -49,6 +49,10 @@ struct UpdateQueueItem {
bool IsDefined() const { bool IsDefined() const {
return id != 0; return id != 0;
} }
void Clear() {
id = 0;
}
}; };
class UpdateQueue { class UpdateQueue {

View File

@@ -43,6 +43,7 @@ UpdateService::UpdateService(EventLoop &_loop, SimpleDatabase &_db,
:DeferredMonitor(_loop), :DeferredMonitor(_loop),
db(_db), storage(_storage), db(_db), storage(_storage),
listener(_listener), listener(_listener),
update_thread(BIND_THIS_METHOD(Task)),
update_task_id(0), update_task_id(0),
walk(nullptr) walk(nullptr)
{ {
@@ -140,13 +141,6 @@ UpdateService::Task()
DeferredMonitor::Schedule(); DeferredMonitor::Schedule();
} }
void
UpdateService::Task(void *ctx)
{
UpdateService &service = *(UpdateService *)ctx;
return service.Task();
}
void void
UpdateService::StartThread(UpdateQueueItem &&i) UpdateService::StartThread(UpdateQueueItem &&i)
{ {
@@ -158,7 +152,7 @@ UpdateService::StartThread(UpdateQueueItem &&i)
next = std::move(i); next = std::move(i);
walk = new UpdateWalk(GetEventLoop(), listener, *next.storage); walk = new UpdateWalk(GetEventLoop(), listener, *next.storage);
update_thread.Start(Task, this); update_thread.Start();
FormatDebug(update_domain, FormatDebug(update_domain,
"spawned thread for update job id %i", next.id); "spawned thread for update job id %i", next.id);
@@ -258,7 +252,7 @@ UpdateService::RunDeferred()
delete walk; delete walk;
walk = nullptr; walk = nullptr;
next = UpdateQueueItem(); next.Clear();
idle_add(IDLE_UPDATE); idle_add(IDLE_UPDATE);

View File

@@ -98,7 +98,6 @@ private:
/* the update thread */ /* the update thread */
void Task(); void Task();
static void Task(void *ctx);
void StartThread(UpdateQueueItem &&i); void StartThread(UpdateQueueItem &&i);

View File

@@ -30,7 +30,8 @@
DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond, DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond,
const AudioFormat _configured_audio_format, const AudioFormat _configured_audio_format,
const ReplayGainConfig &_replay_gain_config) const ReplayGainConfig &_replay_gain_config)
:mutex(_mutex), client_cond(_client_cond), :thread(BIND_THIS_METHOD(RunThread)),
mutex(_mutex), client_cond(_client_cond),
configured_audio_format(_configured_audio_format), configured_audio_format(_configured_audio_format),
replay_gain_config(_replay_gain_config) {} replay_gain_config(_replay_gain_config) {}

View File

@@ -415,6 +415,9 @@ public:
* mixramp_start/mixramp_end. * mixramp_start/mixramp_end.
*/ */
void CycleMixRamp(); void CycleMixRamp();
private:
void RunThread();
}; };
#endif #endif

View File

@@ -513,30 +513,28 @@ try {
dc.client_cond.signal(); dc.client_cond.signal();
} }
static void void
decoder_task(void *arg) DecoderControl::RunThread()
{ {
DecoderControl &dc = *(DecoderControl *)arg;
SetThreadName("decoder"); SetThreadName("decoder");
const std::lock_guard<Mutex> protect(dc.mutex); const std::lock_guard<Mutex> protect(mutex);
do { do {
assert(dc.state == DecoderState::STOP || assert(state == DecoderState::STOP ||
dc.state == DecoderState::ERROR); state == DecoderState::ERROR);
switch (dc.command) { switch (command) {
case DecoderCommand::START: case DecoderCommand::START:
dc.CycleMixRamp(); CycleMixRamp();
dc.replay_gain_prev_db = dc.replay_gain_db; replay_gain_prev_db = replay_gain_db;
dc.replay_gain_db = 0; replay_gain_db = 0;
decoder_run(dc); decoder_run(*this);
if (dc.state == DecoderState::ERROR) { if (state == DecoderState::ERROR) {
try { try {
std::rethrow_exception(dc.error); std::rethrow_exception(error);
} catch (const std::exception &e) { } catch (const std::exception &e) {
LogError(e); LogError(e);
} catch (...) { } catch (...) {
@@ -552,20 +550,20 @@ decoder_task(void *arg)
/* we need to clear the pipe here; usually the /* we need to clear the pipe here; usually the
PlayerThread is responsible, but it is not PlayerThread is responsible, but it is not
aware that the decoder has finished */ aware that the decoder has finished */
dc.pipe->Clear(*dc.buffer); pipe->Clear(*buffer);
decoder_run(dc); decoder_run(*this);
break; break;
case DecoderCommand::STOP: case DecoderCommand::STOP:
dc.CommandFinishedLocked(); CommandFinishedLocked();
break; break;
case DecoderCommand::NONE: case DecoderCommand::NONE:
dc.Wait(); Wait();
break; break;
} }
} while (dc.command != DecoderCommand::NONE || !dc.quit); } while (command != DecoderCommand::NONE || !quit);
} }
void void
@@ -574,5 +572,5 @@ decoder_thread_start(DecoderControl &dc)
assert(!dc.thread.IsDefined()); assert(!dc.thread.IsDefined());
dc.quit = false; dc.quit = false;
dc.thread.Start(decoder_task, &dc); dc.thread.Start();
} }

View File

@@ -27,7 +27,7 @@
#include <algorithm> #include <algorithm>
EventLoop::EventLoop() EventLoop::EventLoop()
:SocketMonitor(*this) :SocketMonitor(*this), quit(false)
{ {
SocketMonitor::Open(wake_fd.Get()); SocketMonitor::Open(wake_fd.Get());
SocketMonitor::Schedule(SocketMonitor::READ); SocketMonitor::Schedule(SocketMonitor::READ);
@@ -46,7 +46,9 @@ EventLoop::~EventLoop()
void void
EventLoop::Break() EventLoop::Break()
{ {
quit = true; if (quit.exchange(true))
return;
wake_fd.Write(); wake_fd.Write();
} }

View File

@@ -30,6 +30,7 @@
#include "SocketMonitor.hxx" #include "SocketMonitor.hxx"
#include <chrono> #include <chrono>
#include <atomic>
#include <list> #include <list>
#include <set> #include <set>
@@ -82,7 +83,7 @@ class EventLoop final : SocketMonitor
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
bool quit = false; std::atomic_bool quit;
/** /**
* True when the object has been modified and another check is * True when the object has been modified and another check is

View File

@@ -54,10 +54,10 @@ ThreadInputStream::Start()
assert(p != nullptr); assert(p != nullptr);
buffer = new CircularBuffer<uint8_t>((uint8_t *)p, buffer_size); buffer = new CircularBuffer<uint8_t>((uint8_t *)p, buffer_size);
thread.Start(ThreadFunc, this); thread.Start();
} }
inline void void
ThreadInputStream::ThreadFunc() ThreadInputStream::ThreadFunc()
{ {
FormatThreadName("input:%s", plugin); FormatThreadName("input:%s", plugin);
@@ -107,13 +107,6 @@ ThreadInputStream::ThreadFunc()
Close(); Close();
} }
void
ThreadInputStream::ThreadFunc(void *ctx)
{
ThreadInputStream &tis = *(ThreadInputStream *)ctx;
tis.ThreadFunc();
}
void void
ThreadInputStream::Check() ThreadInputStream::Check()
{ {

View File

@@ -73,6 +73,7 @@ public:
size_t _buffer_size) size_t _buffer_size)
:InputStream(_uri, _mutex, _cond), :InputStream(_uri, _mutex, _cond),
plugin(_plugin), plugin(_plugin),
thread(BIND_THIS_METHOD(ThreadFunc)),
buffer_size(_buffer_size) {} buffer_size(_buffer_size) {}
virtual ~ThreadInputStream(); virtual ~ThreadInputStream();
@@ -138,7 +139,6 @@ protected:
private: private:
void ThreadFunc(); void ThreadFunc();
static void ThreadFunc(void *ctx);
}; };
#endif #endif

View File

@@ -37,6 +37,18 @@ public:
explicit IcuCompare(const char *needle) noexcept; explicit IcuCompare(const char *needle) noexcept;
IcuCompare(const IcuCompare &src) noexcept
:needle(src
? AllocatedString<>::Duplicate(src.needle.c_str())
: nullptr) {}
IcuCompare &operator=(const IcuCompare &src) noexcept {
needle = src
? AllocatedString<>::Duplicate(src.needle.c_str())
: nullptr;
return *this;
}
IcuCompare(IcuCompare &&) = default; IcuCompare(IcuCompare &&) = default;
IcuCompare &operator=(IcuCompare &&) = default; IcuCompare &operator=(IcuCompare &&) = default;

View File

@@ -34,7 +34,11 @@ static unsigned upnp_ref;
static void static void
DoInit() DoInit()
{ {
auto code = UpnpInit(0, 0); #ifdef UPNP_ENABLE_IPV6
auto code = UpnpInit2(nullptr, 0);
#else
auto code = UpnpInit(nullptr, 0);
#endif
if (code != UPNP_E_SUCCESS) if (code != UPNP_E_SUCCESS)
throw FormatRuntimeError("UpnpInit() failed: %s", throw FormatRuntimeError("UpnpInit() failed: %s",
UpnpGetErrorMessage(code)); UpnpGetErrorMessage(code));

View File

@@ -69,7 +69,8 @@ class SmbclientNeighborExplorer final : public NeighborExplorer {
public: public:
SmbclientNeighborExplorer(NeighborListener &_listener) SmbclientNeighborExplorer(NeighborListener &_listener)
:NeighborExplorer(_listener) {} :NeighborExplorer(_listener),
thread(BIND_THIS_METHOD(ThreadFunc)) {}
/* virtual methods from class NeighborExplorer */ /* virtual methods from class NeighborExplorer */
void Open() override; void Open() override;
@@ -79,14 +80,13 @@ public:
private: private:
void Run(); void Run();
void ThreadFunc(); void ThreadFunc();
static void ThreadFunc(void *ctx);
}; };
void void
SmbclientNeighborExplorer::Open() SmbclientNeighborExplorer::Open()
{ {
quit = false; quit = false;
thread.Start(ThreadFunc, this); thread.Start();
} }
void void
@@ -239,6 +239,8 @@ SmbclientNeighborExplorer::Run()
inline void inline void
SmbclientNeighborExplorer::ThreadFunc() SmbclientNeighborExplorer::ThreadFunc()
{ {
SetThreadName("smbclient");
mutex.lock(); mutex.lock();
while (!quit) { while (!quit) {
@@ -257,15 +259,6 @@ SmbclientNeighborExplorer::ThreadFunc()
mutex.unlock(); mutex.unlock();
} }
void
SmbclientNeighborExplorer::ThreadFunc(void *ctx)
{
SetThreadName("smbclient");
SmbclientNeighborExplorer &e = *(SmbclientNeighborExplorer *)ctx;
e.ThreadFunc();
}
static NeighborExplorer * static NeighborExplorer *
smbclient_neighbor_create(gcc_unused EventLoop &loop, smbclient_neighbor_create(gcc_unused EventLoop &loop,
NeighborListener &listener, NeighborListener &listener,

View File

@@ -51,7 +51,8 @@
AudioOutput::AudioOutput(const AudioOutputPlugin &_plugin, AudioOutput::AudioOutput(const AudioOutputPlugin &_plugin,
const ConfigBlock &block) const ConfigBlock &block)
:plugin(_plugin) :plugin(_plugin),
thread(BIND_THIS_METHOD(Task))
{ {
assert(plugin.finish != nullptr); assert(plugin.finish != nullptr);
assert(plugin.open != nullptr); assert(plugin.open != nullptr);

View File

@@ -515,7 +515,6 @@ private:
* The OutputThread. * The OutputThread.
*/ */
void Task(); void Task();
static void Task(void *arg);
}; };
/** /**

View File

@@ -396,7 +396,7 @@ AudioOutput::Pause()
pause = false; pause = false;
} }
inline void void
AudioOutput::Task() AudioOutput::Task()
{ {
FormatThreadName("output:%s", name); FormatThreadName("output:%s", name);
@@ -512,17 +512,10 @@ AudioOutput::Task()
} }
} }
void
AudioOutput::Task(void *arg)
{
AudioOutput *ao = (AudioOutput *)arg;
ao->Task();
}
void void
AudioOutput::StartThread() AudioOutput::StartThread()
{ {
assert(command == Command::NONE); assert(command == Command::NONE);
thread.Start(Task, this); thread.Start();
} }

View File

@@ -27,6 +27,7 @@
#include "../Wrapper.hxx" #include "../Wrapper.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "mixer/plugins/PulseMixerPlugin.hxx" #include "mixer/plugins/PulseMixerPlugin.hxx"
#include "util/ScopeExit.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <pulse/thread-mainloop.h> #include <pulse/thread-mainloop.h>
@@ -854,7 +855,10 @@ PulseOutput::TestDefaultDevice()
try { try {
const ConfigBlock empty; const ConfigBlock empty;
PulseOutput po(empty); PulseOutput po(empty);
po.Enable();
AtScopeExit(&po) { po.Disable(); };
po.WaitConnection(); po.WaitConnection();
return true; return true;
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
return false; return false;

View File

@@ -37,6 +37,7 @@ PlayerControl::PlayerControl(PlayerListener &_listener,
buffer_chunks(_buffer_chunks), buffer_chunks(_buffer_chunks),
buffered_before_play(_buffered_before_play), buffered_before_play(_buffered_before_play),
configured_audio_format(_configured_audio_format), configured_audio_format(_configured_audio_format),
thread(BIND_THIS_METHOD(RunThread)),
replay_gain_config(_replay_gain_config) replay_gain_config(_replay_gain_config)
{ {
} }

View File

@@ -537,6 +537,9 @@ public:
void ApplyEnabled() override { void ApplyEnabled() override {
LockUpdateAudio(); LockUpdateAudio();
} }
private:
void RunThread();
}; };
#endif #endif

View File

@@ -1147,91 +1147,89 @@ do_play(PlayerControl &pc, DecoderControl &dc,
player.Run(); player.Run();
} }
static void void
player_task(void *arg) PlayerControl::RunThread()
{ {
PlayerControl &pc = *(PlayerControl *)arg;
SetThreadName("player"); SetThreadName("player");
DecoderControl dc(pc.mutex, pc.cond, DecoderControl dc(mutex, cond,
pc.configured_audio_format, configured_audio_format,
pc.replay_gain_config); replay_gain_config);
decoder_thread_start(dc); decoder_thread_start(dc);
MusicBuffer buffer(pc.buffer_chunks); MusicBuffer buffer(buffer_chunks);
pc.Lock(); Lock();
while (1) { while (1) {
switch (pc.command) { switch (command) {
case PlayerCommand::SEEK: case PlayerCommand::SEEK:
case PlayerCommand::QUEUE: case PlayerCommand::QUEUE:
assert(pc.next_song != nullptr); assert(next_song != nullptr);
pc.Unlock(); Unlock();
do_play(pc, dc, buffer); do_play(*this, dc, buffer);
pc.listener.OnPlayerSync(); listener.OnPlayerSync();
pc.Lock(); Lock();
break; break;
case PlayerCommand::STOP: case PlayerCommand::STOP:
pc.Unlock(); Unlock();
pc.outputs.Cancel(); outputs.Cancel();
pc.Lock(); Lock();
/* fall through */ /* fall through */
case PlayerCommand::PAUSE: case PlayerCommand::PAUSE:
delete pc.next_song; delete next_song;
pc.next_song = nullptr; next_song = nullptr;
pc.CommandFinished(); CommandFinished();
break; break;
case PlayerCommand::CLOSE_AUDIO: case PlayerCommand::CLOSE_AUDIO:
pc.Unlock(); Unlock();
pc.outputs.Release(); outputs.Release();
pc.Lock(); Lock();
pc.CommandFinished(); CommandFinished();
assert(buffer.IsEmptyUnsafe()); assert(buffer.IsEmptyUnsafe());
break; break;
case PlayerCommand::UPDATE_AUDIO: case PlayerCommand::UPDATE_AUDIO:
pc.Unlock(); Unlock();
pc.outputs.EnableDisable(); outputs.EnableDisable();
pc.Lock(); Lock();
pc.CommandFinished(); CommandFinished();
break; break;
case PlayerCommand::EXIT: case PlayerCommand::EXIT:
pc.Unlock(); Unlock();
dc.Quit(); dc.Quit();
pc.outputs.Close(); outputs.Close();
pc.LockCommandFinished(); LockCommandFinished();
return; return;
case PlayerCommand::CANCEL: case PlayerCommand::CANCEL:
delete pc.next_song; delete next_song;
pc.next_song = nullptr; next_song = nullptr;
pc.CommandFinished(); CommandFinished();
break; break;
case PlayerCommand::REFRESH: case PlayerCommand::REFRESH:
/* no-op when not playing */ /* no-op when not playing */
pc.CommandFinished(); CommandFinished();
break; break;
case PlayerCommand::NONE: case PlayerCommand::NONE:
pc.Wait(); Wait();
break; break;
} }
} }
@@ -1242,5 +1240,5 @@ StartPlayerThread(PlayerControl &pc)
{ {
assert(!pc.thread.IsDefined()); assert(!pc.thread.IsDefined());
pc.thread.Start(player_task, &pc); pc.thread.Start();
} }

View File

@@ -313,7 +313,7 @@ soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond)
SoundCloudJsonData data; SoundCloudJsonData data;
yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data); yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data);
AtScopeExit(hand, &data) { yajl_free(hand); }; AtScopeExit(hand) { yajl_free(hand); };
int ret = soundcloud_parse_json(u, hand, mutex, cond); int ret = soundcloud_parse_json(u, hand, mutex, cond);

View File

@@ -52,13 +52,11 @@ public:
constexpr ThreadId(pthread_t _id):id(_id) {} constexpr ThreadId(pthread_t _id):id(_id) {}
#endif #endif
gcc_const static constexpr ThreadId Null() noexcept {
static ThreadId Null() noexcept {
#ifdef _WIN32 #ifdef _WIN32
return 0; return 0;
#else #else
static ThreadId null; return pthread_t();
return null;
#endif #endif
} }
@@ -81,11 +79,13 @@ public:
gcc_pure gcc_pure
bool operator==(const ThreadId &other) const noexcept { bool operator==(const ThreadId &other) const noexcept {
#ifdef _WIN32 /* note: not using pthread_equal() because that
function "is undefined if either thread ID is not
valid so we can't safely use it on
default-constructed values" (comment from
libstdc++) - and if both libstdc++ and libc++ get
away with this, we can do it as well */
return id == other.id; return id == other.id;
#else
return pthread_equal(id, other.id);
#endif
} }
/** /**

View File

@@ -25,39 +25,21 @@
#include "java/Global.hxx" #include "java/Global.hxx"
#endif #endif
bool void
Thread::Start(void (*_f)(void *ctx), void *_ctx) Thread::Start()
{ {
assert(!IsDefined()); assert(!IsDefined());
f = _f;
ctx = _ctx;
#ifdef _WIN32 #ifdef _WIN32
handle = ::CreateThread(nullptr, 0, ThreadProc, this, 0, &id); handle = ::CreateThread(nullptr, 0, ThreadProc, this, 0, &id);
if (handle == nullptr) if (handle == nullptr)
throw MakeLastError("Failed to create thread"); throw MakeLastError("Failed to create thread");
#else #else
#ifndef NDEBUG
creating = true;
#endif
int e = pthread_create(&handle, nullptr, ThreadProc, this); int e = pthread_create(&handle, nullptr, ThreadProc, this);
if (e != 0) { if (e != 0)
#ifndef NDEBUG
creating = false;
#endif
throw MakeErrno(e, "Failed to create thread"); throw MakeErrno(e, "Failed to create thread");
}
defined = true;
#ifndef NDEBUG
creating = false;
#endif #endif
#endif
return true;
} }
void void
@@ -72,7 +54,17 @@ Thread::Join()
handle = nullptr; handle = nullptr;
#else #else
pthread_join(handle, nullptr); pthread_join(handle, nullptr);
defined = false; handle = pthread_t();
#endif
}
inline void
Thread::Run()
{
f();
#ifdef ANDROID
Java::DetachCurrentThread();
#endif #endif
} }
@@ -83,7 +75,7 @@ Thread::ThreadProc(LPVOID ctx)
{ {
Thread &thread = *(Thread *)ctx; Thread &thread = *(Thread *)ctx;
thread.f(thread.ctx); thread.Run();
return 0; return 0;
} }
@@ -95,18 +87,10 @@ Thread::ThreadProc(void *ctx)
Thread &thread = *(Thread *)ctx; Thread &thread = *(Thread *)ctx;
#ifndef NDEBUG #ifndef NDEBUG
/* this works around a race condition that causes an assertion thread.inside_handle = pthread_self();
failure due to IsInside() spuriously returning false right
after the thread has been created, and the calling thread
hasn't initialised "defined" yet */
thread.defined = true;
#endif #endif
thread.f(thread.ctx); thread.Run();
#ifdef ANDROID
Java::DetachCurrentThread();
#endif
return nullptr; return nullptr;
} }

View File

@@ -21,6 +21,7 @@
#define MPD_THREAD_HXX #define MPD_THREAD_HXX
#include "check.h" #include "check.h"
#include "util/BindMethod.hxx"
#include "Compiler.h" #include "Compiler.h"
#ifdef _WIN32 #ifdef _WIN32
@@ -32,28 +33,28 @@
#include <assert.h> #include <assert.h>
class Thread { class Thread {
typedef BoundMethod<void()> Function;
const Function f;
#ifdef _WIN32 #ifdef _WIN32
HANDLE handle = nullptr; HANDLE handle = nullptr;
DWORD id; DWORD id;
#else #else
pthread_t handle; pthread_t handle = pthread_t();
bool defined = false;
#ifndef NDEBUG #ifndef NDEBUG
/** /**
* The thread is currently being created. This is a workaround for * This handle is only used by IsInside(), and is set by the
* IsInside(), which may return false until pthread_create() has * thread function. Since #handle is set by pthread_create()
* initialised the #handle. * which is racy, we need this attribute for early checks
* inside the thread function.
*/ */
bool creating = false; pthread_t inside_handle = pthread_t();
#endif #endif
#endif #endif
void (*f)(void *ctx);
void *ctx;
public: public:
Thread() = default; explicit Thread(Function _f):f(_f) {}
Thread(const Thread &) = delete; Thread(const Thread &) = delete;
@@ -69,10 +70,11 @@ public:
#ifdef _WIN32 #ifdef _WIN32
return handle != nullptr; return handle != nullptr;
#else #else
return defined; return handle != pthread_t();
#endif #endif
} }
#ifndef NDEBUG
/** /**
* Check if this thread is the current thread. * Check if this thread is the current thread.
*/ */
@@ -81,18 +83,23 @@ public:
#ifdef _WIN32 #ifdef _WIN32
return GetCurrentThreadId() == id; return GetCurrentThreadId() == id;
#else #else
#ifdef NDEBUG /* note: not using pthread_equal() because that
constexpr bool creating = false; function "is undefined if either thread ID is not
#endif valid so we can't safely use it on
return IsDefined() && (creating || default-constructed values" (comment from
pthread_equal(pthread_self(), handle)); libstdc++) - and if both libstdc++ and libc++ get
away with this, we can do it as well */
return pthread_self() == inside_handle;
#endif #endif
} }
#endif
bool Start(void (*f)(void *ctx), void *ctx); void Start();
void Join(); void Join();
private: private:
void Run();
#ifdef _WIN32 #ifdef _WIN32
static DWORD WINAPI ThreadProc(LPVOID ctx); static DWORD WINAPI ThreadProc(LPVOID ctx);
#else #else

View File

@@ -42,11 +42,6 @@ class RefCount {
std::atomic_uint n; std::atomic_uint n;
public: public:
#ifndef _LIBCPP_VERSION
/* the "constexpr" is missing in libc++'s "atomic"
implementation */
constexpr
#endif
RefCount():n(1) {} RefCount():n(1) {}
void Increment() { void Increment() {

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2003-2018 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef NULL_MIXER_LISTENER_HXX
#define NULL_MIXER_LISTENER_HXX
#include "mixer/Listener.hxx"
class NullMixerListener : public MixerListener {
public:
void OnMixerVolumeChanged(Mixer &, int) override {}
};
#endif

View File

@@ -18,6 +18,7 @@
*/ */
#include "config.h" #include "config.h"
#include "NullMixerListener.hxx"
#include "mixer/MixerControl.hxx" #include "mixer/MixerControl.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "filter/FilterRegistry.hxx" #include "filter/FilterRegistry.hxx"
@@ -50,9 +51,14 @@ try {
EventLoop event_loop; EventLoop event_loop;
NullMixerListener mixer_listener;
Mixer *mixer = mixer_new(event_loop, alsa_mixer_plugin, Mixer *mixer = mixer_new(event_loop, alsa_mixer_plugin,
*(AudioOutput *)nullptr, /* ugly dangerous dummy pointer to
*(MixerListener *)nullptr, make the compiler happy; this
works with most mixers, because
they don't need the AudioOutput */
*(AudioOutput *)0x1,
mixer_listener,
ConfigBlock()); ConfigBlock());
mixer_open(mixer); mixer_open(mixer);

View File

@@ -18,6 +18,7 @@
*/ */
#include "config.h" #include "config.h"
#include "NullMixerListener.hxx"
#include "output/Internal.hxx" #include "output/Internal.hxx"
#include "output/OutputPlugin.hxx" #include "output/OutputPlugin.hxx"
#include "output/Client.hxx" #include "output/Client.hxx"
@@ -44,6 +45,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
void AudioOutput::Task() {}
class DummyAudioOutputClient final : public AudioOutputClient { class DummyAudioOutputClient final : public AudioOutputClient {
public: public:
/* virtual methods from AudioOutputClient */ /* virtual methods from AudioOutputClient */
@@ -62,7 +65,9 @@ filter_plugin_by_name(gcc_unused const char *name) noexcept
} }
static AudioOutput * static AudioOutput *
load_audio_output(EventLoop &event_loop, AudioOutputClient &client, load_audio_output(EventLoop &event_loop,
NullMixerListener &mixer_listener,
AudioOutputClient &client,
const char *name) const char *name)
{ {
const auto *param = config_find_block(ConfigBlockOption::AUDIO_OUTPUT, const auto *param = config_find_block(ConfigBlockOption::AUDIO_OUTPUT,
@@ -72,7 +77,7 @@ load_audio_output(EventLoop &event_loop, AudioOutputClient &client,
name); name);
return audio_output_new(event_loop, ReplayGainConfig(), *param, return audio_output_new(event_loop, ReplayGainConfig(), *param,
*(MixerListener *)nullptr, mixer_listener,
client); client);
} }
@@ -142,8 +147,10 @@ try {
/* initialize the audio output */ /* initialize the audio output */
NullMixerListener mixer_listener;
DummyAudioOutputClient client; DummyAudioOutputClient client;
AudioOutput *ao = load_audio_output(event_loop, client, argv[2]); AudioOutput *ao = load_audio_output(event_loop, mixer_listener,
client, argv[2]);
/* parse the audio format */ /* parse the audio format */

View File

@@ -50,14 +50,14 @@ class CrossGccToolchain:
self.nm = os.path.join(toolchain_bin, arch + '-nm') self.nm = os.path.join(toolchain_bin, arch + '-nm')
self.strip = os.path.join(toolchain_bin, arch + '-strip') self.strip = os.path.join(toolchain_bin, arch + '-strip')
common_flags = '' common_flags = '-O2 -g'
if not x64: if not x64:
# enable SSE support which is required for LAME # enable SSE support which is required for LAME
common_flags += ' -march=pentium3' common_flags += ' -march=pentium3'
self.cflags = '-O2 -g ' + common_flags self.cflags = common_flags
self.cxxflags = '-O2 -g ' + common_flags self.cxxflags = common_flags
self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include') self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include')
self.ldflags = '-L' + os.path.join(install_prefix, 'lib') self.ldflags = '-L' + os.path.join(install_prefix, 'lib')
self.libs = '' self.libs = ''