I'm trying to run an idle loop using libmpdclient
, but already on the first idle call, I get to an apparently unrecoverable error state.
I'm passing false
to the disable_timeout
parameter of mpd_recv_idle
, so that I can stop the loop from the outside (it will be run in a background thread), to ensure a clean shutdown procedure.
Here is my test code:
#include <string>
#include <stdexcept>
#include <memory>
#include <mpd/client.h>
typedef std::unique_ptr<mpd_connection, decltype(&mpd_connection_free)>
mpd_connection_ptr;
void
check_error (const mpd_connection_ptr &c, const std::string &s)
{
if (mpd_connection_get_error (c.get ()) != MPD_ERROR_SUCCESS)
{
throw std::runtime_error (s);
}
}
int
main (void)
{
const std::string host = "127.0.0.1";
const uint16_t port = 7701;
const int timeout = 1 * 1000;
mpd_connection_ptr c {mpd_connection_new (host.c_str (), port, timeout),
&mpd_connection_free
};
if (c == nullptr)
{
throw std::runtime_error ("connection_new returned nullptr");
}
if (!mpd_send_idle (c.get ()))
{
throw std::runtime_error ("mpd_send_idle returned false");
}
check_error (c, "mpd_send_idle caused error status");
auto idle = mpd_recv_idle (c.get (), false);
if (idle == 0)
{
if (mpd_connection_get_error (c.get ()) == MPD_ERROR_TIMEOUT)
{
if (!mpd_connection_clear_error (c.get ()))
{
throw std::runtime_error ("mpd_connection_clear_error returned false");
}
}
else
{
check_error (c, "mpd_recv_idle caused error status");
}
}
return 0;
}
When I run this code (mpd is running on 127.0.0.1:7701, I checked with netstat
), I get this result:
terminate called after throwing an instance of 'std::runtime_error'
what(): mpd_connection_clear_error returned false
Why can't I clear the timeout error here, it seems like a recoverable situation or not?
From studying the
libmpdclient
source code I think I can answer that myself.A timeout is an unrecoverable error in the library design. That's why the
disable_timeout
parameter formpd_recv_idle ()
is there in the first place.Synchronous idle requests are expected to block "forever" (until MPD answers the request). This is incompatible with what I want, I will probably have to use the low level async interface to achieve what I want.
And here is my solution (with minimal error checking).
The program waits for the user to press ENTER and processes MPD idle messages in the background which can be interrupted every 200 ms.
What's missing:
Here is the code:
An improved version can be interrupted "as fast as possible", by calling
select ()
without atimeval
and watching for a shutdownpipe ()
instead:An even better solution, if you're willing to write your code in an event based style (using
libuv
anduvw
):