Commit 71d012fa authored by Max Kellermann's avatar Max Kellermann

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.
parent 114df1f1
...@@ -3,6 +3,7 @@ ver 0.19 (not yet released) ...@@ -3,6 +3,7 @@ ver 0.19 (not yet released)
- new commands "addtagid", "cleartagid" - new commands "addtagid", "cleartagid"
- "lsinfo" and "readcomments" allowed for remote files - "lsinfo" and "readcomments" allowed for remote files
* database * database
- proxy: forward "idle" events
- upnp: new plugin - upnp: new plugin
* playlist * playlist
- soundcloud: use https instead of http - soundcloud: use https instead of http
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "config.h" #include "config.h"
#include "ProxyDatabasePlugin.hxx" #include "ProxyDatabasePlugin.hxx"
#include "DatabasePlugin.hxx" #include "DatabasePlugin.hxx"
#include "DatabaseListener.hxx"
#include "DatabaseSelection.hxx" #include "DatabaseSelection.hxx"
#include "DatabaseError.hxx" #include "DatabaseError.hxx"
#include "Directory.hxx" #include "Directory.hxx"
...@@ -31,14 +32,21 @@ ...@@ -31,14 +32,21 @@
#include "util/Error.hxx" #include "util/Error.hxx"
#include "util/Domain.hxx" #include "util/Domain.hxx"
#include "protocol/Ack.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/client.h>
#include <mpd/async.h>
#include <cassert> #include <cassert>
#include <string> #include <string>
#include <list> #include <list>
class ProxyDatabase : public Database { class ProxyDatabase final : public Database, SocketMonitor, IdleMonitor {
DatabaseListener &listener;
std::string host; std::string host;
unsigned port; unsigned port;
...@@ -48,7 +56,23 @@ class ProxyDatabase : public Database { ...@@ -48,7 +56,23 @@ class ProxyDatabase : public Database {
/* this is mutable because GetStats() must be "const" */ /* this is mutable because GetStats() must be "const" */
mutable time_t update_stamp; 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: public:
ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener)
:SocketMonitor(_loop), IdleMonitor(_loop),
listener(_listener) {}
static Database *Create(EventLoop &loop, DatabaseListener &listener, static Database *Create(EventLoop &loop, DatabaseListener &listener,
const config_param &param, const config_param &param,
Error &error); Error &error);
...@@ -86,6 +110,12 @@ private: ...@@ -86,6 +110,12 @@ private:
bool EnsureConnected(Error &error); bool EnsureConnected(Error &error);
void Disconnect(); 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"); static constexpr Domain libmpdclient_domain("libmpdclient");
...@@ -219,11 +249,10 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection) ...@@ -219,11 +249,10 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection)
} }
Database * Database *
ProxyDatabase::Create(gcc_unused EventLoop &loop, ProxyDatabase::Create(EventLoop &loop, DatabaseListener &listener,
gcc_unused DatabaseListener &listener,
const config_param &param, Error &error) const config_param &param, Error &error)
{ {
ProxyDatabase *db = new ProxyDatabase(); ProxyDatabase *db = new ProxyDatabase(loop, listener);
if (!db->Configure(param, error)) { if (!db->Configure(param, error)) {
delete db; delete db;
db = nullptr; db = nullptr;
...@@ -273,6 +302,12 @@ ProxyDatabase::Connect(Error &error) ...@@ -273,6 +302,12 @@ ProxyDatabase::Connect(Error &error)
return false; 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 (!CheckError(connection, error)) {
if (connection != nullptr) if (connection != nullptr)
Disconnect(); Disconnect();
...@@ -293,6 +328,18 @@ ProxyDatabase::CheckConnection(Error &error) ...@@ -293,6 +328,18 @@ ProxyDatabase::CheckConnection(Error &error)
return Connect(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; return true;
} }
...@@ -309,10 +356,74 @@ ProxyDatabase::Disconnect() ...@@ -309,10 +356,74 @@ ProxyDatabase::Disconnect()
{ {
assert(connection != nullptr); assert(connection != nullptr);
IdleMonitor::Cancel();
SocketMonitor::Steal();
mpd_connection_free(connection); mpd_connection_free(connection);
connection = nullptr; 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 * static Song *
Convert(const struct mpd_song *song); Convert(const struct mpd_song *song);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment