Code

screen_utils: remove redundant doupdate() call
[ncmpc.git] / src / gidle.c
index 9c720866770f415bfe504470c57dbf2cd2d39941..0d18cf384612cdc30009442439d31e7f3b7c32c8 100644 (file)
@@ -1,5 +1,5 @@
 /* ncmpc (Ncurses MPD Client)
-   (c) 2004-2009 The Music Player Daemon Project
+   (c) 2004-2017 The Music Player Daemon Project
    Project homepage: http://musicpd.org
 
    Redistribution and use in source and binary forms, with or without
 */
 
 #include "gidle.h"
+#include "Compiler.h"
 
 #include <mpd/async.h>
 #include <mpd/parser.h>
 
 #include <glib.h>
+
 #include <assert.h>
 #include <string.h>
-#include <sys/select.h>
 #include <errno.h>
 
 struct mpd_glib_source {
@@ -52,6 +53,22 @@ struct mpd_glib_source {
        guint id;
 
        enum mpd_idle idle_events;
+
+       /**
+        * This flag is a hack: it is set while mpd_glib_leave() is
+        * executed.  mpd_glib_leave() might invoke the callback, and
+        * the callback might invoke mpd_glib_enter(), awkwardly
+        * leaving mpd_glib_leave() in idle mode.  As long as this
+        * flag is set, mpd_glib_enter() is a no-op to prevent this.
+        */
+       bool leaving;
+
+       /**
+        * This flag is true when mpd_glib_free() has been called
+        * during a callback invoked from mpd_glib_leave().
+        * mpd_glib_leave() will do the real g_free() call then.
+        */
+       bool destroyed;
 };
 
 struct mpd_glib_source *
@@ -71,6 +88,8 @@ mpd_glib_new(struct mpd_connection *connection,
        source->channel = g_io_channel_unix_new(mpd_async_get_fd(source->async));
        source->io_events = 0;
        source->id = 0;
+       source->leaving = false;
+       source->destroyed = false;
 
        return source;
 }
@@ -78,16 +97,26 @@ mpd_glib_new(struct mpd_connection *connection,
 void
 mpd_glib_free(struct mpd_glib_source *source)
 {
+       assert(!source->destroyed);
+
        if (source->id != 0)
                g_source_remove(source->id);
 
        g_io_channel_unref(source->channel);
+
+       mpd_parser_free(source->parser);
+
+       if (source->leaving)
+               source->destroyed = true;
+       else
+               g_free(source);
 }
 
 static void
 mpd_glib_invoke(const struct mpd_glib_source *source)
 {
        assert(source->id == 0);
+       assert(!source->destroyed);
 
        if (source->idle_events != 0)
                source->callback(MPD_ERROR_SUCCESS, 0, NULL,
@@ -100,6 +129,7 @@ mpd_glib_invoke_error(const struct mpd_glib_source *source,
                      const char *message)
 {
        assert(source->id == 0);
+       assert(!source->destroyed);
 
        source->callback(error, server_error, message,
                         0, source->callback_ctx);
@@ -217,11 +247,8 @@ static bool
 mpd_glib_recv(struct mpd_glib_source *source)
 {
        char *line;
-       bool success;
-
        while ((line = mpd_async_recv_line(source->async)) != NULL) {
-               success = mpd_glib_feed(source, line);
-               if (!success)
+               if (!mpd_glib_feed(source, line))
                        return false;
        }
 
@@ -237,21 +264,18 @@ mpd_glib_recv(struct mpd_glib_source *source)
 }
 
 static gboolean
-mpd_glib_source_callback(G_GNUC_UNUSED GIOChannel *_source,
+mpd_glib_source_callback(gcc_unused GIOChannel *_source,
                         GIOCondition condition, gpointer data)
 {
        struct mpd_glib_source *source = data;
-       bool success;
-       enum mpd_async_event events;
 
        assert(source->id != 0);
        assert(source->io_events != 0);
 
        /* let libmpdclient do some I/O */
 
-       success = mpd_async_io(source->async,
-                              g_io_condition_to_mpd_async_event(condition));
-       if (!success) {
+       if (!mpd_async_io(source->async,
+                         g_io_condition_to_mpd_async_event(condition))) {
                source->id = 0;
                source->io_events = 0;
 
@@ -262,14 +286,13 @@ mpd_glib_source_callback(G_GNUC_UNUSED GIOChannel *_source,
        /* receive the response */
 
        if ((condition & G_IO_IN) != 0) {
-               success = mpd_glib_recv(source);
-               if (!success)
+               if (!mpd_glib_recv(source))
                        return false;
        }
 
        /* continue polling? */
 
-       events = mpd_async_events(source->async);
+       enum mpd_async_event events = mpd_async_events(source->async);
        if (events == 0) {
                /* no events - disable watch */
                source->id = 0;
@@ -296,54 +319,56 @@ static void
 mpd_glib_add_watch(struct mpd_glib_source *source)
 {
        enum mpd_async_event events = mpd_async_events(source->async);
-       GIOCondition condition;
 
        assert(source->io_events == 0);
        assert(source->id == 0);
 
-       condition = mpd_async_events_to_g_io_condition(events);
-
+       GIOCondition condition = mpd_async_events_to_g_io_condition(events);
        source->id = g_io_add_watch(source->channel, condition,
                                    mpd_glib_source_callback, source);
        source->io_events = events;
 }
 
-void
+bool
 mpd_glib_enter(struct mpd_glib_source *source)
 {
-       bool success;
-
        assert(source->io_events == 0);
        assert(source->id == 0);
+       assert(!source->destroyed);
+
+       if (source->leaving)
+               return false;
 
        source->idle_events = 0;
 
-       success = mpd_async_send_command(source->async, "idle", NULL);
-       if (!success) {
+       if (!mpd_async_send_command(source->async, "idle", NULL)) {
                mpd_glib_invoke_async_error(source);
-               return;
+               return false;
        }
 
        mpd_glib_add_watch(source);
+       return true;
 }
 
-void
+bool
 mpd_glib_leave(struct mpd_glib_source *source)
 {
-       enum mpd_idle events;
+       assert(!source->destroyed);
 
        if (source->id == 0)
                /* already left, callback was invoked */
-               return;
+               return true;
 
        g_source_remove(source->id);
        source->id = 0;
        source->io_events = 0;
 
-       events = source->idle_events == 0
+       enum mpd_idle events = source->idle_events == 0
                ? mpd_run_noidle(source->connection)
                : mpd_recv_idle(source->connection, false);
 
+       source->leaving = true;
+
        if (events == 0 &&
            mpd_connection_get_error(source->connection) != MPD_ERROR_SUCCESS) {
                enum mpd_error error =
@@ -355,9 +380,24 @@ mpd_glib_leave(struct mpd_glib_source *source)
 
                mpd_glib_invoke_error(source, error, server_error,
                                      mpd_connection_get_error_message(source->connection));
-               return;
+
+               if (source->destroyed) {
+                       g_free(source);
+                       return false;
+               }
+
+               source->leaving = false;
+               return true;
        }
 
        source->idle_events |= events;
        mpd_glib_invoke(source);
+
+       if (source->destroyed) {
+               g_free(source);
+               return false;
+       }
+
+       source->leaving = false;
+       return true;
 }