Merge branch 'v0.20.x'

This commit is contained in:
Max Kellermann 2017-02-09 21:24:20 +01:00
commit 67a958a326
9 changed files with 83 additions and 18 deletions

4
NEWS
View File

@ -6,6 +6,10 @@ ver 0.21 (not yet released)
ver 0.20.5 (not yet released) ver 0.20.5 (not yet released)
* tags * tags
- id3: fix memory leak on corrupt ID3 tags - id3: fix memory leak on corrupt ID3 tags
* decoder
- sidplay: don't require libsidutils when building with libsidplayfp
* mixer
- alsa: fix crash bug
ver 0.20.4 (2017/02/01) ver 0.20.4 (2017/02/01)
* input * input

View File

@ -988,7 +988,7 @@ AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enab
dnl --------------------------------- sidplay --------------------------------- dnl --------------------------------- sidplay ---------------------------------
if test x$enable_sidplay != xno; then if test x$enable_sidplay != xno; then
dnl Check for libsidplayfp first dnl Check for libsidplayfp first
PKG_CHECK_MODULES([SIDPLAY], [libsidplayfp libsidutils], PKG_CHECK_MODULES([SIDPLAY], [libsidplayfp],
[found_sidplayfp=yes], [found_sidplayfp=yes],
[found_sidplayfp=no]) [found_sidplayfp=no])
found_sidplay=$found_sidplayfp found_sidplay=$found_sidplayfp

View File

@ -79,7 +79,7 @@ private:
void void
BlockingCall(EventLoop &loop, std::function<void()> &&f) BlockingCall(EventLoop &loop, std::function<void()> &&f)
{ {
if (loop.IsInside()) { if (loop.IsInsideOrNull()) {
/* we're already inside the loop - we can simply call /* we're already inside the loop - we can simply call
the function */ the function */
f(); f();

View File

@ -222,8 +222,9 @@ EventLoop::Run()
#ifndef NDEBUG #ifndef NDEBUG
assert(busy); assert(busy);
assert(thread.IsInside()); assert(thread.IsInside());
thread = ThreadId::Null();
#endif #endif
thread = ThreadId::Null();
} }
void void

View File

@ -212,12 +212,16 @@ public:
} }
#endif #endif
#ifndef NDEBUG /**
* Like IsInside(), but also returns true if the thread has
* already ended (or was not started yet). This is useful for
* code which may run during startup or shutdown, when events
* are not yet/anymore handled.
*/
gcc_pure gcc_pure
bool IsInsideOrNull() const { bool IsInsideOrNull() const {
return thread.IsNull() || thread.IsInside(); return thread.IsNull() || thread.IsInside();
} }
#endif
}; };
#endif /* MAIN_NOTIFY_H */ #endif /* MAIN_NOTIFY_H */

View File

@ -28,12 +28,18 @@
#endif #endif
MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop) MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
:IdleMonitor(_loop), TimeoutMonitor(_loop), ready(false) { :IdleMonitor(_loop), TimeoutMonitor(_loop) {
} }
MultiSocketMonitor::~MultiSocketMonitor() void
MultiSocketMonitor::Reset()
{ {
// TODO assert(GetEventLoop().IsInsideOrNull());
fds.clear();
IdleMonitor::Cancel();
TimeoutMonitor::Cancel();
ready = refresh = false;
} }
void void

View File

@ -96,7 +96,19 @@ class MultiSocketMonitor : IdleMonitor, TimeoutMonitor
friend class SingleFD; friend class SingleFD;
bool ready, refresh; /**
* DispatchSockets() should be called.
*/
bool ready = false;
/**
* PrepareSockets() should be called.
*
* Note that this doesn't need to be initialized by the
* constructor; this class is activated with the first
* InvalidateSockets() call, which initializes this flag.
*/
bool refresh;
std::forward_list<SingleFD> fds; std::forward_list<SingleFD> fds;
@ -107,11 +119,26 @@ public:
static constexpr unsigned HANGUP = SocketMonitor::HANGUP; static constexpr unsigned HANGUP = SocketMonitor::HANGUP;
MultiSocketMonitor(EventLoop &_loop); MultiSocketMonitor(EventLoop &_loop);
~MultiSocketMonitor();
using IdleMonitor::GetEventLoop; using IdleMonitor::GetEventLoop;
public: /**
* Clear the socket list and disable all #EventLoop
* registrations. Run this in the #EventLoop thread before
* destroying this object.
*
* Later, this object can be reused and reactivated by calling
* InvalidateSockets().
*
* Note that this class doesn't have a destructor which calls
* this method, because this would be racy and thus pointless:
* at the time ~MultiSocketMonitor() is called, our virtual
* methods have been morphed to be pure again, and in the
* meantime the #EventLoop thread could invoke those pure
* methods.
*/
void Reset();
/** /**
* Invalidate the socket list. A call to PrepareSockets() is * Invalidate the socket list. A call to PrepareSockets() is
* scheduled which will then update the list. * scheduled which will then update the list.
@ -121,12 +148,19 @@ public:
IdleMonitor::Schedule(); IdleMonitor::Schedule();
} }
/**
* Add one socket to the list of monitored sockets.
*
* May only be called from PrepareSockets().
*/
void AddSocket(int fd, unsigned events) { void AddSocket(int fd, unsigned events) {
fds.emplace_front(*this, fd, events); fds.emplace_front(*this, fd, events);
} }
/** /**
* Remove all sockets. * Remove all sockets.
*
* May only be called from PrepareSockets().
*/ */
void ClearSocketList(); void ClearSocketList();
@ -135,6 +169,8 @@ public:
* each one; its return value is the events bit mask. A * each one; its return value is the events bit mask. A
* return value of 0 means the socket will be removed from the * return value of 0 means the socket will be removed from the
* list. * list.
*
* May only be called from PrepareSockets().
*/ */
template<typename E> template<typename E>
void UpdateSocketList(E &&e) { void UpdateSocketList(E &&e) {
@ -157,15 +193,26 @@ public:
/** /**
* Replace the socket list with the given file descriptors. * Replace the socket list with the given file descriptors.
* The given pollfd array will be modified by this method. * The given pollfd array will be modified by this method.
*
* May only be called from PrepareSockets().
*/ */
void ReplaceSocketList(pollfd *pfds, unsigned n); void ReplaceSocketList(pollfd *pfds, unsigned n);
#endif #endif
protected: protected:
/** /**
* Override this method and update the socket registrations.
* To do that, call AddSocket(), ClearSocketList(),
* UpdateSocketList() and ReplaceSocketList().
*
* @return timeout or a negative value for no timeout * @return timeout or a negative value for no timeout
*/ */
virtual std::chrono::steady_clock::duration PrepareSockets() = 0; virtual std::chrono::steady_clock::duration PrepareSockets() = 0;
/**
* At least one socket is ready or the timeout has expired.
* This method should be used to perform I/O.
*/
virtual void DispatchSockets() = 0; virtual void DispatchSockets() = 0;
private: private:

View File

@ -99,12 +99,8 @@ public:
} }
~AlsaInputStream() { ~AlsaInputStream() {
/* ClearSocketList must be called from within the
IOThread; if we don't do it manually here, the
~MultiSocketMonitor() will do it in the current
thread */
BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){ BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){
ClearSocketList(); MultiSocketMonitor::Reset();
}); });
snd_pcm_close(capture_handle); snd_pcm_close(capture_handle);
@ -182,7 +178,7 @@ AlsaInputStream::PrepareSockets()
} }
int count = snd_pcm_poll_descriptors_count(capture_handle); int count = snd_pcm_poll_descriptors_count(capture_handle);
if (count < 0) { if (count <= 0) {
ClearSocketList(); ClearSocketList();
return std::chrono::steady_clock::duration(-1); return std::chrono::steady_clock::duration(-1);
} }

View File

@ -23,6 +23,7 @@
#include "output/OutputAPI.hxx" #include "output/OutputAPI.hxx"
#include "event/MultiSocketMonitor.hxx" #include "event/MultiSocketMonitor.hxx"
#include "event/DeferredMonitor.hxx" #include "event/DeferredMonitor.hxx"
#include "event/Call.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/ReusableArray.hxx" #include "util/ReusableArray.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
@ -53,6 +54,12 @@ public:
DeferredMonitor::Schedule(); DeferredMonitor::Schedule();
} }
~AlsaMixerMonitor() {
BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){
MultiSocketMonitor::Reset();
});
}
private: private:
virtual void RunDeferred() override { virtual void RunDeferred() override {
InvalidateSockets(); InvalidateSockets();
@ -102,7 +109,7 @@ AlsaMixerMonitor::PrepareSockets()
} }
int count = snd_mixer_poll_descriptors_count(mixer); int count = snd_mixer_poll_descriptors_count(mixer);
if (count < 0) if (count <= 0)
count = 0; count = 0;
struct pollfd *pfds = pfd_buffer.Get(count); struct pollfd *pfds = pfd_buffer.Get(count);