After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 690400 - Cannot unsubscribe from notification in notify handler
Cannot unsubscribe from notification in notify handler
Status: RESOLVED FIXED
Product: GUPnP
Classification: Other
Component: gupnp
0.11.x
Other Linux
: Normal major
: ---
Assigned To: GUPnP Maintainers
GUPnP Maintainers
Depends on:
Blocks:
 
 
Reported: 2012-12-18 08:34 UTC by Jens Georg
Modified: 2014-01-21 13:09 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
tests: Add test for #690400 (7.38 KB, patch)
2014-01-20 15:05 UTC, Jussi Kukkonen
accepted-commit_now Details | Review
Survive ServiceProxy callback-list changes from callbacks (2.57 KB, patch)
2014-01-20 15:05 UTC, Jussi Kukkonen
needs-work Details | Review
tests: refactor main loop handling to one function (3.75 KB, patch)
2014-01-20 15:05 UTC, Jussi Kukkonen
accepted-commit_now Details | Review

Description Jens Georg 2012-12-18 08:34:31 UTC
Quoting https://mail.gnome.org/archives/gupnp-list/2012-December/msg00010.html

we've identified a problem with the GUPnPServiceProxy API that I would
like to bring to your attention. The current implementation of
notifications in GUPnPServiceProxy does not deal nicely with code that
calls gupnp_service_proxy_remove_notify() from a notification callback.
Actually it will most certainly crash if you attempt to do this.

What happens is that we are inside this emit_notification() looping over
registered callbacks that want to get notified about a change of a state
variable:

        NotifyData *data;
        GList *l;

        data = g_hash_table_lookup (proxy->priv->notify_hash,
                                    var_node->name);
        if (data == NULL)
                return;

        /* some not relevant code removed here... */

        /* Call callbacks */
        for (l = data->callbacks; l; l = l->next) {
                CallbackData *callback_data;

                callback_data = l->data;

                callback_data->callback (proxy,
                                         (const char *) var_node->name,
                                         &value,
                                         callback_data->user_data);
        }

Now if the callback uses gupnp_service_proxy_add_notify() or
gupnp_service_proxy_remove_notify() it will change the list of callbacks
in NotifyData while we are iterating it. Probably the most common case
(and the one that we hit in our code) is that the callback removes
itself from the service-proxy. In that case the GList struct that we are
holding a pointer to will be released. This is likely going to cause a
crash when the for-loop accesses l->next.
Comment 1 Jussi Kukkonen 2014-01-19 11:24:20 UTC
I have a patch for this but I'll wait until the test changes in bug 678701 are in so I can add a test as well.
Comment 2 Jussi Kukkonen 2014-01-20 15:05:22 UTC
Created attachment 266747 [details] [review]
tests: Add test for #690400

Test that removing a notify-callback from the callback itself works.
Comment 3 Jussi Kukkonen 2014-01-20 15:05:27 UTC
Created attachment 266748 [details] [review]
Survive ServiceProxy callback-list changes from callbacks

Make notify-callback code more robust, so that removing a
notify-callback from a notify-callback works.
Comment 4 Jussi Kukkonen 2014-01-20 15:05:31 UTC
Created attachment 266749 [details] [review]
tests: refactor main loop handling to one function
Comment 5 Jens Georg 2014-01-21 08:15:28 UTC
Review of attachment 266748 [details] [review]:

This patch should also remove the doc paragraph in gupnp_service_proxy_remove_notify that says that you cannot do this or up until which version it wasn't possible to do this or sth like that.

Otherwise, +1

::: libgupnp/gupnp-service-proxy.c
@@ +1632,3 @@
+        if (data->next_emit == NULL)
+                data->next_emit = g_list_last (data->callbacks);
+

Why is next_emit set to the newest notify added?
Comment 6 Jens Georg 2014-01-21 08:16:45 UTC
Review of attachment 266747 [details] [review]:

+1
Comment 7 Jens Georg 2014-01-21 08:17:30 UTC
Review of attachment 266749 [details] [review]:

+1
Comment 8 Jussi Kukkonen 2014-01-21 10:01:26 UTC
(In reply to comment #5)
> Review of attachment 266748 [details] [review]:
> 
> This patch should also remove the doc paragraph in
> gupnp_service_proxy_remove_notify that says that you cannot do this or up until
> which version it wasn't possible to do this or sth like that.

Good catch.

> ::: libgupnp/gupnp-service-proxy.c
> @@ +1632,3 @@
> +        if (data->next_emit == NULL)
> +                data->next_emit = g_list_last (data->callbacks);
> +
> 
> Why is next_emit set to the newest notify added?

It makes sure we do things consistently: If next_emit is NULL it means either 
 A) we're not in emit_notification() (in which case setting next_emit does not affect anything) or 
 B) we're in emit_notification() calling the last callback, in which case we want to call the new callback too -- not because it makes so much sense but because it's consistent with what happens when callbacks are added from callbacks earlier in the list.
Comment 9 Jens Georg 2014-01-21 10:39:44 UTC
Ah. Thanks for the explanation.
Comment 10 Jussi Kukkonen 2014-01-21 13:09:06 UTC
Committed as 
17442e3876fa85a861b5624d51a9211fef15112d
bf151a712020250461ceb1f43e7bcf949466800c
4ad119c7186c690aa96aa34c64199c874ec33dbb