aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/wayland-private.h3
-rw-r--r--src/wayland-server.c46
2 files changed, 46 insertions, 3 deletions
diff --git a/src/wayland-private.h b/src/wayland-private.h
index 12b9032..29516ec 100644
--- a/src/wayland-private.h
+++ b/src/wayland-private.h
@@ -254,6 +254,9 @@ void
wl_priv_signal_emit(struct wl_priv_signal *signal, void *data);
void
+wl_priv_signal_final_emit(struct wl_priv_signal *signal, void *data);
+
+void
wl_connection_close_fds_in(struct wl_connection *connection, int max);
#endif
diff --git a/src/wayland-server.c b/src/wayland-server.c
index eab2ee4..eae8d2e 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -682,7 +682,7 @@ destroy_resource(void *element, void *data, uint32_t flags)
/* Don't emit the new signal for deprecated resources, as that would
* access memory outside the bounds of the deprecated struct */
if (!resource_is_deprecated(resource))
- wl_priv_signal_emit(&resource->destroy_signal, resource);
+ wl_priv_signal_final_emit(&resource->destroy_signal, resource);
if (resource->destroy)
resource->destroy(resource);
@@ -841,7 +841,7 @@ wl_client_destroy(struct wl_client *client)
{
uint32_t serial = 0;
- wl_priv_signal_emit(&client->destroy_signal, client);
+ wl_priv_signal_final_emit(&client->destroy_signal, client);
wl_client_flush(client);
wl_map_for_each(&client->objects, destroy_resource, &serial);
@@ -1089,7 +1089,7 @@ wl_display_destroy(struct wl_display *display)
struct wl_socket *s, *next;
struct wl_global *global, *gnext;
- wl_priv_signal_emit(&display->destroy_signal, display);
+ wl_priv_signal_final_emit(&display->destroy_signal, display);
wl_list_for_each_safe(s, next, &display->socket_list, link) {
wl_socket_destroy(s);
@@ -2025,6 +2025,46 @@ wl_priv_signal_emit(struct wl_priv_signal *signal, void *data)
}
}
+/** Emit the signal for the last time, calling all the installed listeners
+ *
+ * Iterate over all the listeners added to this \a signal and call
+ * their \a notify function pointer, passing on the given \a data.
+ * Removing or adding a listener from within wl_priv_signal_emit()
+ * is safe, as is freeing the structure containing the listener.
+ *
+ * A large body of external code assumes it's ok to free a destruction
+ * listener without removing that listener from the list. Mixing code
+ * that acts like this and code that doesn't will result in list
+ * corruption.
+ *
+ * We resolve this by removing each item from the list and isolating it
+ * in another list. We discard it completely after firing the notifier.
+ * This should allow interoperability between code that unlinks its
+ * destruction listeners and code that just frees structures they're in.
+ *
+ */
+void
+wl_priv_signal_final_emit(struct wl_priv_signal *signal, void *data)
+{
+ struct wl_listener *l;
+ struct wl_list *pos;
+
+ /* During a destructor notifier isolate every list item before
+ * notifying. This renders harmless the long standing misuse
+ * of freeing listeners without removing them, but allows
+ * callers that do choose to remove them to interoperate with
+ * ones that don't. */
+ while (!wl_list_empty(&signal->listener_list)) {
+ pos = signal->listener_list.next;
+ l = wl_container_of(pos, l, link);
+
+ wl_list_remove(pos);
+ wl_list_init(pos);
+
+ l->notify(l, data);
+ }
+}
+
/** \endcond INTERNAL */
/** \cond */ /* Deprecated functions below. */