Rebased ref, commits from common ancestor: commit 1361da9cd5a719b32d978485a29920429a31ed25 Author: Bryce Harrington <br...@osg.samsung.com> Date: Tue Feb 21 13:27:16 2017 -0800
configure.ac: bump to version 1.13.0 for the official release diff --git a/configure.ac b/configure.ac index c50027b..fcce4ce 100644 --- a/configure.ac +++ b/configure.ac @@ -1,8 +1,8 @@ AC_PREREQ([2.64]) m4_define([wayland_major_version], [1]) -m4_define([wayland_minor_version], [12]) -m4_define([wayland_micro_version], [93]) +m4_define([wayland_minor_version], [13]) +m4_define([wayland_micro_version], [0]) m4_define([wayland_version], [wayland_major_version.wayland_minor_version.wayland_micro_version]) commit 24df0fb6b3835c3b4574847491eecae769af4c2c Author: Bryce Harrington <br...@osg.samsung.com> Date: Tue Feb 14 12:56:55 2017 -0800 configure.ac: bump to version 1.12.93 for the RC1 release diff --git a/configure.ac b/configure.ac index 2a3d5e3..c50027b 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.64]) m4_define([wayland_major_version], [1]) m4_define([wayland_minor_version], [12]) -m4_define([wayland_micro_version], [92]) +m4_define([wayland_micro_version], [93]) m4_define([wayland_version], [wayland_major_version.wayland_minor_version.wayland_micro_version]) commit 6161d7dd0efd33339a90bdfbc6f2575ff16f1b2f Author: Bryce Harrington <br...@osg.samsung.com> Date: Tue Feb 7 15:14:56 2017 -0800 configure.ac: bump to version 1.12.92 for the beta release diff --git a/configure.ac b/configure.ac index 7520b24..2a3d5e3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.64]) m4_define([wayland_major_version], [1]) m4_define([wayland_minor_version], [12]) -m4_define([wayland_micro_version], [91]) +m4_define([wayland_micro_version], [92]) m4_define([wayland_version], [wayland_major_version.wayland_minor_version.wayland_micro_version]) commit 56f2dad6d2ac04e179d43d525f6213031dcb4d62 Author: Pekka Paalanen <pekka.paala...@collabora.co.uk> Date: Wed Jan 25 14:24:44 2017 +0200 wayland-server: hide wl_priv_signal from doxygen Fix this set of warnings appearing three times during a build: /home/pq/git/wayland/src/wayland-server.c:1868: warning: class `wl_priv_signal' for related function `wl_priv_signal_init' is not documented. /home/pq/git/wayland/src/wayland-server.c:1884: warning: class `wl_priv_signal' for related function `wl_priv_signal_add' is not documented. /home/pq/git/wayland/src/wayland-server.c:1899: warning: class `wl_priv_signal' for related function `wl_priv_signal_get' is not documented. Our Wayland docbook don't include private things, so make sure these do not end up there. This removes the mention of wl_priv_signal_emit from the Server API docbook. I have no idea why the other functions did not appear there. Signed-off-by: Pekka Paalanen <pekka.paala...@collabora.co.uk> Reviewed-by: Yong Bakos <yba...@humanoriented.com> diff --git a/src/wayland-server.c b/src/wayland-server.c index cdd46fa..6a8b3e4 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -1881,6 +1881,8 @@ wl_client_for_each_resource(struct wl_client *client, wl_map_for_each(&client->objects, resource_iterator_helper, &context); } +/** \cond INTERNAL */ + /** Initialize a wl_priv_signal object * * wl_priv_signal is a safer implementation of a signal type, with the same API @@ -1972,6 +1974,8 @@ wl_priv_signal_emit(struct wl_priv_signal *signal, void *data) } } +/** \endcond INTERNAL */ + /** \cond */ /* Deprecated functions below. */ uint32_t commit 8fe8a2bb1e2b893bd39899f7481d5cf5fa303b29 Author: Yong Bakos <yba...@humanoriented.com> Date: Wed Nov 23 07:38:01 2016 -0800 tests: Test wl_argument_from_va_list connection-test.c did not cover wl_argument_from_va_list, so add one test that specifically tests this method. Signed-off-by: Yong Bakos <yba...@humanoriented.com> Reviewed-by: Daniel Stone <dani...@collabora.com> diff --git a/tests/connection-test.c b/tests/connection-test.c index 3e34f77..1c688f1 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -130,6 +130,41 @@ TEST(connection_queue) close(s[1]); } +static void +va_list_wrapper(const char *signature, union wl_argument *args, int count, ...) +{ + va_list ap; + va_start(ap, count); + wl_argument_from_va_list(signature, args, count, ap); + va_end(ap); +} + +TEST(argument_from_va_list) +{ + union wl_argument args[WL_CLOSURE_MAX_ARGS]; + struct wl_object fake_object; + struct wl_array fake_array; + + va_list_wrapper("i", args, 1, 100); + assert(args[0].i == 100); + + va_list_wrapper("is", args, 2, 101, "value"); + assert(args[0].i == 101); + assert(strcmp(args[1].s, "value") == 0); + + va_list_wrapper("?iuf?sonah", args, 8, + 102, 103, wl_fixed_from_int(104), "value", + &fake_object, 105, &fake_array, 106); + assert(args[0].i == 102); + assert(args[1].u == 103); + assert(args[2].f == wl_fixed_from_int(104)); + assert(strcmp(args[3].s, "value") == 0); + assert(args[4].o == &fake_object); + assert(args[5].n == 105); + assert(args[6].a == &fake_array); + assert(args[7].h == 106); +} + struct marshal_data { struct wl_connection *read_connection; struct wl_connection *write_connection; commit e89d0a66843eb3cb4d888c594ad87ef731e51697 Author: Yong Bakos <yba...@humanoriented.com> Date: Mon Jan 23 06:16:30 2017 -0800 dtddata: Use standard permission notice Signed-off-by: Yong Bakos <yba...@humanoriented.com> Reviewed-by: Daniel Stone <dani...@collabora.com> diff --git a/src/dtddata.S b/src/dtddata.S index ce51133..2405066 100644 --- a/src/dtddata.S +++ b/src/dtddata.S @@ -1,23 +1,26 @@ /* * Copyright � 2015 Collabora, Ltd. * - * Permission to use, copy, modify, distribute, and sell this software and - * its documentation for any purpose is hereby granted without fee, provided - * that the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of the copyright holders not be used in - * advertising or publicity pertaining to distribution of the software - * without specific, written prior permission. The copyright holders make - * no representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: * - * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS - * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF - * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ /* commit de908658945ef8e13b27b32a7a69f14b3f9356df Author: Derek Foreman <der...@osg.samsung.com> Date: Tue Jan 24 12:07:21 2017 -0600 wayland-server: log an error for events with wrong client objects Check that all the objects in an event belong to the same client as the resource posting it. This prevents a compositor from accidentally mixing client objects and posting an event that causes a client to abort with a cryptic message. Instead the client will now be disconnected as it is when the compositor tries to send a null for a non-nullable object, and a log message will be printed by the compositor. Reviewed-by: Yong Bakos <yba...@humanoriented.com> Reviewed-by: Bryce Harrington <br...@osg.samsung.com> Signed-off-by: Derek Foreman <der...@osg.samsung.com> Reviewed-by: Pekka Paalanen <pekka.paala...@collabora.co.uk> diff --git a/src/wayland-server.c b/src/wayland-server.c index 0a5eacb..cdd46fa 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -168,6 +168,36 @@ log_closure(struct wl_resource *resource, } } +static bool +verify_objects(struct wl_resource *resource, uint32_t opcode, + union wl_argument *args) +{ + struct wl_object *object = &resource->object; + const char *signature = object->interface->events[opcode].signature; + struct argument_details arg; + struct wl_resource *res; + int count, i; + + count = arg_count_for_signature(signature); + for (i = 0; i < count; i++) { + signature = get_next_argument(signature, &arg); + switch (arg.type) { + case 'n': + case 'o': + res = (struct wl_resource *) (args[i].o); + if (res && res->client != resource->client) { + wl_log("compositor bug: The compositor " + "tried to use an object from one " + "client in a '%s.%s' for a different " + "client.\n", object->interface->name, + object->interface->events[opcode].name); + return false; + } + } + } + return true; +} + static void handle_array(struct wl_resource *resource, uint32_t opcode, union wl_argument *args, @@ -179,6 +209,11 @@ handle_array(struct wl_resource *resource, uint32_t opcode, if (resource->client->error) return; + if (!verify_objects(resource, opcode, args)) { + resource->client->error = 1; + return; + } + closure = wl_closure_marshal(object, opcode, args, &object->interface->events[opcode]); commit efae9532e82e1faeb737fe0d3cf5932026ce1e6f Author: Derek Foreman <der...@osg.samsung.com> Date: Tue Jan 24 12:07:20 2017 -0600 server: Disallow sending events to clients after posting an error Until now, we haven't done anything to prevent sending additional events to clients after posting an error. Acked-by: Daniel Stone <dani...@collabora.com> Signed-off-by: Derek Foreman <der...@osg.samsung.com> Reviewed-by: Pekka Paalanen <pekka.paala...@collabora.co.uk> diff --git a/src/wayland-server.c b/src/wayland-server.c index a981fda..0a5eacb 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -176,6 +176,9 @@ handle_array(struct wl_resource *resource, uint32_t opcode, struct wl_closure *closure; struct wl_object *object = &resource->object; + if (resource->client->error) + return; + closure = wl_closure_marshal(object, opcode, args, &object->interface->events[opcode]); @@ -249,8 +252,6 @@ wl_resource_post_error(struct wl_resource *resource, vsnprintf(buffer, sizeof buffer, msg, ap); va_end(ap); - client->error = 1; - /* * When a client aborts, its resources are destroyed in id order, * which means the display resource is destroyed first. If destruction @@ -258,11 +259,12 @@ wl_resource_post_error(struct wl_resource *resource, * with a NULL display_resource. Do not try to send errors to an * already dead client. */ - if (!client->display_resource) + if (client->error || !client->display_resource) return; wl_resource_post_event(client->display_resource, WL_DISPLAY_ERROR, resource, code, buffer); + client->error = 1; } static int commit 5fbc9daa409371d15ffb9012cc21dddc934fad4a Author: Derek Foreman <der...@osg.samsung.com> Date: Tue Jan 24 12:07:19 2017 -0600 server: Refactor array send functions These have grown a little in size but are almost identical, factor out the common code. Signed-off-by: Derek Foreman <der...@osg.samsung.com> Reviewed-by: Pekka Paalanen <pekka.paala...@collabora.co.uk> diff --git a/src/wayland-server.c b/src/wayland-server.c index ac634da..a981fda 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -168,9 +168,10 @@ log_closure(struct wl_resource *resource, } } -WL_EXPORT void -wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode, - union wl_argument *args) +static void +handle_array(struct wl_resource *resource, uint32_t opcode, + union wl_argument *args, + int (*send_func)(struct wl_closure *, struct wl_connection *)) { struct wl_closure *closure; struct wl_object *object = &resource->object; @@ -183,7 +184,7 @@ wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode, return; } - if (wl_closure_send(closure, resource->client->connection)) + if (send_func(closure, resource->client->connection)) resource->client->error = 1; log_closure(resource, closure, true); @@ -192,6 +193,13 @@ wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode, } WL_EXPORT void +wl_resource_post_event_array(struct wl_resource *resource, uint32_t opcode, + union wl_argument *args) +{ + handle_array(resource, opcode, args, wl_closure_send); +} + +WL_EXPORT void wl_resource_post_event(struct wl_resource *resource, uint32_t opcode, ...) { union wl_argument args[WL_CLOSURE_MAX_ARGS]; @@ -211,23 +219,7 @@ WL_EXPORT void wl_resource_queue_event_array(struct wl_resource *resource, uint32_t opcode, union wl_argument *args) { - struct wl_closure *closure; - struct wl_object *object = &resource->object; - - closure = wl_closure_marshal(object, opcode, args, - &object->interface->events[opcode]); - - if (closure == NULL) { - resource->client->error = 1; - return; - } - - if (wl_closure_queue(closure, resource->client->connection)) - resource->client->error = 1; - - log_closure(resource, closure, true); - - wl_closure_destroy(closure); + handle_array(resource, opcode, args, wl_closure_queue); } WL_EXPORT void commit c44eed1c064999f1e0297088bacd56c602dee2eb Author: Giulio Camuffo <giuliocamu...@gmail.com> Date: Tue Jan 24 16:34:30 2017 +0200 server: use the new wl_priv_signal for wl_resource The old wl_signal is kept for backwards compatibility, as that is also present in the deprecated public wl_resource struct, and that must be kept working. Signed-off-by: Giulio Camuffo <giulio.camu...@kdab.com> Reviewed-by: Pekka Paalanen <pekka.paala...@collabora.co.uk> diff --git a/src/wayland-server.c b/src/wayland-server.c index 06f8ba2..ac634da 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -120,11 +120,16 @@ struct wl_resource { struct wl_object object; wl_resource_destroy_func_t destroy; struct wl_list link; - struct wl_signal destroy_signal; + /* Unfortunately some users of libwayland (e.g. mesa) still use the + * deprecated wl_resource struct, even if creating it with the new + * wl_resource_create(). So we cannot change the layout of the struct + * unless after the data field. */ + struct wl_signal deprecated_destroy_signal; struct wl_client *client; void *data; int version; wl_dispatcher_func_t dispatcher; + struct wl_priv_signal destroy_signal; }; struct wl_protocol_logger { @@ -600,6 +605,31 @@ wl_resource_post_no_memory(struct wl_resource *resource) WL_DISPLAY_ERROR_NO_MEMORY, "no memory"); } +/** Detect if a wl_resource uses the deprecated public definition. + * + * Before Wayland 1.2.0, the definition of struct wl_resource was public. + * It was made opaque just before 1.2.0, and later new fields were added. + * The new fields cannot be accessed if a program is using the deprecated + * defition, as there would not be memory allocated for them. + * + * The creation pattern for the deprecated definition was wl_resource_init() + * followed by wl_client_add_resource(). wl_resource_init() was an inline + * function and no longer exists, but binaries might still carry it. + * wl_client_add_resource() still exists for ABI compatiblity. + */ +static bool +resource_is_deprecated(struct wl_resource *resource) +{ + struct wl_map *map = &resource->client->objects; + int id = resource->object.id; + + /* wl_client_add_resource() marks deprecated resources with the flag. */ + if (wl_map_lookup_flags(map, id) & WL_MAP_ENTRY_LEGACY) + return true; + + return false; +} + static enum wl_iterator_result destroy_resource(void *element, void *data) { @@ -607,7 +637,11 @@ destroy_resource(void *element, void *data) struct wl_client *client = resource->client; uint32_t flags; - wl_signal_emit(&resource->destroy_signal, resource); + wl_signal_emit(&resource->deprecated_destroy_signal, resource); + /* 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); flags = wl_map_lookup_flags(&client->objects, resource->object.id); if (resource->destroy) @@ -719,14 +753,19 @@ WL_EXPORT void wl_resource_add_destroy_listener(struct wl_resource *resource, struct wl_listener * listener) { - wl_signal_add(&resource->destroy_signal, listener); + if (resource_is_deprecated(resource)) + wl_signal_add(&resource->deprecated_destroy_signal, listener); + else + wl_priv_signal_add(&resource->destroy_signal, listener); } WL_EXPORT struct wl_listener * wl_resource_get_destroy_listener(struct wl_resource *resource, wl_notify_func_t notify) { - return wl_signal_get(&resource->destroy_signal, notify); + if (resource_is_deprecated(resource)) + return wl_signal_get(&resource->deprecated_destroy_signal, notify); + return wl_priv_signal_get(&resource->destroy_signal, notify); } /** Retrieve the interface name (class) of a resource object. @@ -1559,7 +1598,8 @@ wl_resource_create(struct wl_client *client, resource->object.interface = interface; resource->object.implementation = NULL; - wl_signal_init(&resource->destroy_signal); + wl_signal_init(&resource->deprecated_destroy_signal); + wl_priv_signal_init(&resource->destroy_signal); resource->destroy = NULL; resource->client = client; @@ -1927,7 +1967,7 @@ wl_client_add_resource(struct wl_client *client, } resource->client = client; - wl_signal_init(&resource->destroy_signal); + wl_signal_init(&resource->deprecated_destroy_signal); return resource->object.id; } commit 7454aa9bb9569a1d63f6bcb1938e165ea370a4a5 Author: Giulio Camuffo <giuliocamu...@gmail.com> Date: Tue Jan 24 16:34:29 2017 +0200 server: use the new wl_priv_signal in wl_client Signed-off-by: Giulio Camuffo <giulio.camu...@kdab.com> Reviewed-by: Pekka Paalanen <pekka.paala...@collabora.co.uk> diff --git a/src/wayland-server.c b/src/wayland-server.c index 1482d5e..06f8ba2 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -78,10 +78,10 @@ struct wl_client { uint32_t mask; struct wl_list link; struct wl_map objects; - struct wl_signal destroy_signal; + struct wl_priv_signal destroy_signal; struct ucred ucred; int error; - struct wl_signal resource_created_signal; + struct wl_priv_signal resource_created_signal; }; struct wl_display { @@ -460,7 +460,7 @@ wl_client_create(struct wl_display *display, int fd) if (client == NULL) return NULL; - wl_signal_init(&client->resource_created_signal); + wl_priv_signal_init(&client->resource_created_signal); client->display = display; client->source = wl_event_loop_add_fd(display->loop, fd, WL_EVENT_READABLE, @@ -483,7 +483,7 @@ wl_client_create(struct wl_display *display, int fd) if (wl_map_insert_at(&client->objects, 0, 0, NULL) < 0) goto err_map; - wl_signal_init(&client->destroy_signal); + wl_priv_signal_init(&client->destroy_signal); if (bind_display(client, display) < 0) goto err_map; @@ -745,14 +745,14 @@ WL_EXPORT void wl_client_add_destroy_listener(struct wl_client *client, struct wl_listener *listener) { - wl_signal_add(&client->destroy_signal, listener); + wl_priv_signal_add(&client->destroy_signal, listener); } WL_EXPORT struct wl_listener * wl_client_get_destroy_listener(struct wl_client *client, wl_notify_func_t notify) { - return wl_signal_get(&client->destroy_signal, notify); + return wl_priv_signal_get(&client->destroy_signal, notify); } WL_EXPORT void @@ -760,7 +760,7 @@ wl_client_destroy(struct wl_client *client) { uint32_t serial = 0; - wl_signal_emit(&client->destroy_signal, client); + wl_priv_signal_emit(&client->destroy_signal, client); wl_client_flush(client); wl_map_for_each(&client->objects, destroy_resource, &serial); @@ -1575,7 +1575,7 @@ wl_resource_create(struct wl_client *client, return NULL; } - wl_signal_emit(&client->resource_created_signal, resource); + wl_priv_signal_emit(&client->resource_created_signal, resource); return resource; } @@ -1763,7 +1763,7 @@ WL_EXPORT void wl_client_add_resource_created_listener(struct wl_client *client, struct wl_listener *listener) { - wl_signal_add(&client->resource_created_signal, listener); + wl_priv_signal_add(&client->resource_created_signal, listener); } struct wl_resource_iterator_context { commit 5e6eb032294ecdee889600c604dfcaab0ffb9398 Author: Giulio Camuffo <giuliocamu...@gmail.com> Date: Tue Jan 24 16:34:28 2017 +0200 server: add a safer signal type and port wl_display to it wl_list_for_each_safe, which is used by wl_signal_emit is not really safe. If a signal has two listeners, and the first one removes and re-inits the second one, it would enter an infinite loop, which was hit in weston on resource destruction, which emits a signal. This commit adds a new version of wl_signal, called wl_priv_signal, which is private in wayland-server.c and which does not have this problem. The old wl_signal cannot be improved without breaking backwards compatibility. Signed-off-by: Giulio Camuffo <giulio.camu...@kdab.com> Reviewed-by: Pekka Paalanen <pekka.paala...@collabora.co.uk> diff --git a/Makefile.am b/Makefile.am index d78a0ca..d0c8bd3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -159,6 +159,7 @@ built_test_programs = \ socket-test \ queue-test \ signal-test \ + newsignal-test \ resources-test \ message-test \ headers-test \ @@ -226,6 +227,9 @@ queue_test_SOURCES = tests/queue-test.c queue_test_LDADD = libtest-runner.la signal_test_SOURCES = tests/signal-test.c signal_test_LDADD = libtest-runner.la +# wayland-server.c is needed here to access wl_priv_* functions +newsignal_test_SOURCES = tests/newsignal-test.c src/wayland-server.c +newsignal_test_LDADD = libtest-runner.la resources_test_SOURCES = tests/resources-test.c resources_test_LDADD = libtest-runner.la message_test_SOURCES = tests/message-test.c diff --git a/src/wayland-private.h b/src/wayland-private.h index 676b181..434cb04 100644 --- a/src/wayland-private.h +++ b/src/wayland-private.h @@ -35,6 +35,7 @@ #define WL_HIDE_DEPRECATED 1 #include "wayland-util.h" +#include "wayland-server-core.h" /* Invalid memory address */ #define WL_ARRAY_POISON_PTR (void *) 4 @@ -233,4 +234,21 @@ zalloc(size_t s) return calloc(1, s); } +struct wl_priv_signal { + struct wl_list listener_list; + struct wl_list emit_list; +}; + +void +wl_priv_signal_init(struct wl_priv_signal *signal); + +void +wl_priv_signal_add(struct wl_priv_signal *signal, struct wl_listener *listener); + +struct wl_listener * +wl_priv_signal_get(struct wl_priv_signal *signal, wl_notify_func_t notify); + +void +wl_priv_signal_emit(struct wl_priv_signal *signal, void *data); + #endif diff --git a/src/wayland-server.c b/src/wayland-server.c index 4360874..1482d5e 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -97,8 +97,8 @@ struct wl_display { struct wl_list client_list; struct wl_list protocol_loggers; - struct wl_signal destroy_signal; - struct wl_signal create_client_signal; + struct wl_priv_signal destroy_signal; + struct wl_priv_signal create_client_signal; struct wl_array additional_shm_formats; @@ -489,7 +489,7 @@ wl_client_create(struct wl_display *display, int fd) wl_list_insert(display->client_list.prev, &client->link); - wl_signal_emit(&display->create_client_signal, client); + wl_priv_signal_emit(&display->create_client_signal, client); return client; @@ -942,8 +942,8 @@ wl_display_create(void) wl_list_init(&display->registry_resource_list); wl_list_init(&display->protocol_loggers); - wl_signal_init(&display->destroy_signal); - wl_signal_init(&display->create_client_signal); + wl_priv_signal_init(&display->destroy_signal); + wl_priv_signal_init(&display->create_client_signal); display->id = 1; display->serial = 0; @@ -1008,7 +1008,7 @@ wl_display_destroy(struct wl_display *display) struct wl_socket *s, *next; struct wl_global *global, *gnext; - wl_signal_emit(&display->destroy_signal, display); + wl_priv_signal_emit(&display->destroy_signal, display); wl_list_for_each_safe(s, next, &display->socket_list, link) { wl_socket_destroy(s); @@ -1478,7 +1478,7 @@ WL_EXPORT void wl_display_add_destroy_listener(struct wl_display *display, struct wl_listener *listener) { - wl_signal_add(&display->destroy_signal, listener); + wl_priv_signal_add(&display->destroy_signal, listener); } /** Registers a listener for the client connection signal. @@ -1496,14 +1496,14 @@ WL_EXPORT void wl_display_add_client_created_listener(struct wl_display *display, struct wl_listener *listener) { - wl_signal_add(&display->create_client_signal, listener); + wl_priv_signal_add(&display->create_client_signal, listener); } WL_EXPORT struct wl_listener * wl_display_get_destroy_listener(struct wl_display *display, wl_notify_func_t notify) { - return wl_signal_get(&display->destroy_signal, notify); + return wl_priv_signal_get(&display->destroy_signal, notify); } WL_EXPORT void @@ -1812,6 +1812,97 @@ wl_client_for_each_resource(struct wl_client *client, wl_map_for_each(&client->objects, resource_iterator_helper, &context); } +/** Initialize a wl_priv_signal object + * + * wl_priv_signal is a safer implementation of a signal type, with the same API + * as wl_signal, but kept as a private utility of libwayland-server. + * It is safer because listeners can be removed from within wl_priv_signal_emit() + * without corrupting the signal's list. + * + * Before passing a wl_priv_signal object to any other function it must be + * initialized by useing wl_priv_signal_init(). + * + * \memberof wl_priv_signal + */ +void +wl_priv_signal_init(struct wl_priv_signal *signal) +{ + wl_list_init(&signal->listener_list); + wl_list_init(&signal->emit_list); +} + +/** Add a listener to a signal + * + * The new listener will be called when calling wl_signal_emit(). If a listener is + * added to the signal while wl_signal_emit() is running it will be called from + * the next time wl_priv_signal_emit() is called. + * To remove a listener call wl_list_remove() on its link member. + * + * \memberof wl_priv_signal + */ +void +wl_priv_signal_add(struct wl_priv_signal *signal, struct wl_listener *listener) +{ + wl_list_insert(signal->listener_list.prev, &listener->link); +} + +/** Get a listener added to a signal + * + * Returns the listener added to the given \a signal and with the given + * \a notify function, or NULL if there isn't any. + * Calling this function from withing wl_priv_signal_emit() is safe and will + * return the correct value. + * + * \memberof wl_priv_signal + */ +struct wl_listener * +wl_priv_signal_get(struct wl_priv_signal *signal, wl_notify_func_t notify) +{ + struct wl_listener *l; + + wl_list_for_each(l, &signal->listener_list, link) + if (l->notify == notify) + return l; + wl_list_for_each(l, &signal->emit_list, link) + if (l->notify == notify) + return l; + + return NULL; +} + +/** Emit the signal, 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. + */ +void +wl_priv_signal_emit(struct wl_priv_signal *signal, void *data) +{ + struct wl_listener *l; + struct wl_list *pos; + + wl_list_insert_list(&signal->emit_list, &signal->listener_list); + wl_list_init(&signal->listener_list); + + /* Take every element out of the list and put them in a temporary list. + * This way, the 'it' func can remove any element it wants from the list + * without troubles, because we always get the first element, not the + * one after the current, which may be invalid. + * wl_list_for_each_safe tries to be safe but it fails: it works fine + * if the current item is removed, but not if the next one is. */ + while (!wl_list_empty(&signal->emit_list)) { + pos = signal->emit_list.next; + l = wl_container_of(pos, l, link); + + wl_list_remove(pos); + wl_list_insert(&signal->listener_list, pos); + + l->notify(l, data); + } +} + /** \cond */ /* Deprecated functions below. */ uint32_t diff --git a/tests/newsignal-test.c b/tests/newsignal-test.c new file mode 100644 index 0000000..47c429b --- /dev/null +++ b/tests/newsignal-test.c @@ -0,0 +1,337 @@ +/* + * Copyright © 2013 Marek Chalupa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <assert.h> + +#include "test-runner.h" +#include "wayland-private.h" + +static void +signal_notify(struct wl_listener *listener, void *data) +{ + /* only increase counter*/ + ++(*((int *) data)); +} + +TEST(signal_init) +{ + struct wl_priv_signal signal; + + wl_priv_signal_init(&signal); + + /* Test if listeners' list is initialized */ + assert(&signal.listener_list == signal.listener_list.next + && "Maybe wl_priv_signal implementation changed?"); + assert(signal.listener_list.next == signal.listener_list.prev + && "Maybe wl_priv_signal implementation changed?"); +} + +TEST(signal_add_get) +{ + struct wl_priv_signal signal; + + /* we just need different values of notify */ + struct wl_listener l1 = {.notify = (wl_notify_func_t) 0x1}; + struct wl_listener l2 = {.notify = (wl_notify_func_t) 0x2}; + struct wl_listener l3 = {.notify = (wl_notify_func_t) 0x3}; + /* one real, why not */ + struct wl_listener l4 = {.notify = signal_notify}; + + wl_priv_signal_init(&signal); + + wl_priv_signal_add(&signal, &l1); + wl_priv_signal_add(&signal, &l2); + wl_priv_signal_add(&signal, &l3); + wl_priv_signal_add(&signal, &l4); + + assert(wl_priv_signal_get(&signal, signal_notify) == &l4); + assert(wl_priv_signal_get(&signal, (wl_notify_func_t) 0x3) == &l3); + assert(wl_priv_signal_get(&signal, (wl_notify_func_t) 0x2) == &l2); + assert(wl_priv_signal_get(&signal, (wl_notify_func_t) 0x1) == &l1); + + /* get should not be destructive */ + assert(wl_priv_signal_get(&signal, signal_notify) == &l4); + assert(wl_priv_signal_get(&signal, (wl_notify_func_t) 0x3) == &l3); + assert(wl_priv_signal_get(&signal, (wl_notify_func_t) 0x2) == &l2); + assert(wl_priv_signal_get(&signal, (wl_notify_func_t) 0x1) == &l1); +} + +TEST(signal_emit_to_one_listener) +{ + int count = 0; + int counter; + + struct wl_priv_signal signal; + struct wl_listener l1 = {.notify = signal_notify}; + + wl_priv_signal_init(&signal); + wl_priv_signal_add(&signal, &l1); + + for (counter = 0; counter < 100; counter++) + wl_priv_signal_emit(&signal, &count); + + assert(counter == count); +} + +TEST(signal_emit_to_more_listeners) +{ + int count = 0; + int counter; + + struct wl_priv_signal signal; + struct wl_listener l1 = {.notify = signal_notify}; + struct wl_listener l2 = {.notify = signal_notify}; + struct wl_listener l3 = {.notify = signal_notify}; + + wl_priv_signal_init(&signal); + wl_priv_signal_add(&signal, &l1); + wl_priv_signal_add(&signal, &l2);