db/proxy: forward "idle" events

Send "idle" to the other MPD whenever there's nothing else to do and
forward incoming "idle database" events to all our MPD clients.
This commit is contained in:
Max Kellermann 2014-01-10 23:44:03 +01:00
parent 114df1f137
commit 71d012fa61
2 changed files with 116 additions and 4 deletions

1
NEWS
View File

@ -3,6 +3,7 @@ ver 0.19 (not yet released)
- new commands "addtagid", "cleartagid"
- "lsinfo" and "readcomments" allowed for remote files
* database
- proxy: forward "idle" events
- upnp: new plugin
* playlist
- soundcloud: use https instead of http

View File

@ -20,6 +20,7 @@
#include "config.h"
#include "ProxyDatabasePlugin.hxx"
#include "DatabasePlugin.hxx"
#include "DatabaseListener.hxx"
#include "DatabaseSelection.hxx"
#include "DatabaseError.hxx"
#include "Directory.hxx"
@ -31,14 +32,21 @@
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "protocol/Ack.hxx"
#include "Main.hxx"
#include "event/SocketMonitor.hxx"
#include "event/IdleMonitor.hxx"
#include "Log.hxx"
#include <mpd/client.h>
#include <mpd/async.h>
#include <cassert>
#include <string>
#include <list>
class ProxyDatabase : public Database {
class ProxyDatabase final : public Database, SocketMonitor, IdleMonitor {
DatabaseListener &listener;
std::string host;
unsigned port;
@ -48,7 +56,23 @@ class ProxyDatabase : public Database {
/* this is mutable because GetStats() must be "const" */
mutable time_t update_stamp;
/**
* The libmpdclient idle mask that was removed from the other
* MPD. This will be handled by the next OnIdle() call.
*/
unsigned idle_received;
/**
* Is the #connection currently "idle"? That is, did we send
* the "idle" command to it?
*/
bool is_idle;
public:
ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener)
:SocketMonitor(_loop), IdleMonitor(_loop),
listener(_listener) {}
static Database *Create(EventLoop &loop, DatabaseListener &listener,
const config_param &param,
Error &error);
@ -86,6 +110,12 @@ private:
bool EnsureConnected(Error &error);
void Disconnect();
/* virtual methods from SocketMonitor */
virtual bool OnSocketReady(unsigned flags) override;
/* virtual methods from IdleMonitor */
virtual void OnIdle() override;
};
static constexpr Domain libmpdclient_domain("libmpdclient");
@ -219,11 +249,10 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection)
}
Database *
ProxyDatabase::Create(gcc_unused EventLoop &loop,
gcc_unused DatabaseListener &listener,
ProxyDatabase::Create(EventLoop &loop, DatabaseListener &listener,
const config_param &param, Error &error)
{
ProxyDatabase *db = new ProxyDatabase();
ProxyDatabase *db = new ProxyDatabase(loop, listener);
if (!db->Configure(param, error)) {
delete db;
db = nullptr;
@ -273,6 +302,12 @@ ProxyDatabase::Connect(Error &error)
return false;
}
idle_received = unsigned(-1);
is_idle = false;
SocketMonitor::Open(mpd_async_get_fd(mpd_connection_get_async(connection)));
IdleMonitor::Schedule();
if (!CheckError(connection, error)) {
if (connection != nullptr)
Disconnect();
@ -293,6 +328,18 @@ ProxyDatabase::CheckConnection(Error &error)
return Connect(error);
}
if (is_idle) {
unsigned idle = mpd_run_noidle(connection);
if (idle == 0 && !CheckError(connection, error)) {
Disconnect();
return false;
}
idle_received |= idle;
is_idle = false;
IdleMonitor::Schedule();
}
return true;
}
@ -309,10 +356,74 @@ ProxyDatabase::Disconnect()
{
assert(connection != nullptr);
IdleMonitor::Cancel();
SocketMonitor::Steal();
mpd_connection_free(connection);
connection = nullptr;
}
bool
ProxyDatabase::OnSocketReady(gcc_unused unsigned flags)
{
assert(connection != nullptr);
if (!is_idle) {
// TODO: can this happen?
IdleMonitor::Schedule();
return false;
}
unsigned idle = (unsigned)mpd_recv_idle(connection, false);
if (idle == 0) {
Error error;
if (!CheckError(connection, error)) {
LogError(error);
Disconnect();
return false;
}
}
/* let OnIdle() handle this */
idle_received |= idle;
is_idle = false;
IdleMonitor::Schedule();
return false;
}
void
ProxyDatabase::OnIdle()
{
assert(connection != nullptr);
/* handle previous idle events */
if (idle_received & MPD_IDLE_DATABASE)
listener.OnDatabaseModified();
idle_received = 0;
/* send a new idle command to the other MPD */
if (is_idle)
// TODO: can this happen?
return;
if (!mpd_send_idle_mask(connection, MPD_IDLE_DATABASE)) {
Error error;
if (!CheckError(connection, error))
LogError(error);
SocketMonitor::Steal();
mpd_connection_free(connection);
connection = nullptr;
return;
}
is_idle = true;
SocketMonitor::ScheduleRead();
}
static Song *
Convert(const struct mpd_song *song);