diff --git a/src/libinput-private.h b/src/libinput-private.h index e3d9bb1e..00901b4d 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -60,6 +60,7 @@ struct libinput { libinput_log_handler log_handler; enum libinput_log_priority log_priority; void *user_data; + int refcount; }; typedef void (*libinput_seat_destroy_func) (struct libinput_seat *seat); diff --git a/src/libinput.c b/src/libinput.c index 91557776..44f4f23d 100644 --- a/src/libinput.c +++ b/src/libinput.c @@ -496,6 +496,7 @@ libinput_init(struct libinput *libinput, libinput->interface = interface; libinput->interface_backend = interface_backend; libinput->user_data = user_data; + libinput->refcount = 1; list_init(&libinput->source_destroy_list); list_init(&libinput->seat_list); @@ -524,15 +525,27 @@ libinput_drop_destroyed_sources(struct libinput *libinput) list_init(&libinput->source_destroy_list); } -LIBINPUT_EXPORT void -libinput_destroy(struct libinput *libinput) +LIBINPUT_EXPORT struct libinput * +libinput_ref(struct libinput *libinput) +{ + libinput->refcount++; + return libinput; +} + +LIBINPUT_EXPORT struct libinput * +libinput_unref(struct libinput *libinput) { struct libinput_event *event; struct libinput_device *device, *next_device; struct libinput_seat *seat, *next_seat; if (libinput == NULL) - return; + return NULL; + + assert(libinput->refcount > 0); + libinput->refcount--; + if (libinput->refcount > 0) + return libinput; libinput_suspend(libinput); @@ -556,6 +569,8 @@ libinput_destroy(struct libinput *libinput) libinput_drop_destroyed_sources(libinput); close(libinput->epoll_fd); free(libinput); + + return NULL; } LIBINPUT_EXPORT void @@ -601,10 +616,11 @@ libinput_seat_init(struct libinput_seat *seat, list_insert(&libinput->seat_list, &seat->link); } -LIBINPUT_EXPORT void +LIBINPUT_EXPORT struct libinput_seat * libinput_seat_ref(struct libinput_seat *seat) { seat->refcount++; + return seat; } static void @@ -616,13 +632,17 @@ libinput_seat_destroy(struct libinput_seat *seat) seat->destroy(seat); } -LIBINPUT_EXPORT void +LIBINPUT_EXPORT struct libinput_seat * libinput_seat_unref(struct libinput_seat *seat) { assert(seat->refcount > 0); seat->refcount--; - if (seat->refcount == 0) + if (seat->refcount == 0) { libinput_seat_destroy(seat); + return NULL; + } else { + return seat; + } } LIBINPUT_EXPORT void @@ -657,10 +677,11 @@ libinput_device_init(struct libinput_device *device, device->refcount = 1; } -LIBINPUT_EXPORT void +LIBINPUT_EXPORT struct libinput_device * libinput_device_ref(struct libinput_device *device) { device->refcount++; + return device; } static void @@ -669,13 +690,17 @@ libinput_device_destroy(struct libinput_device *device) evdev_device_destroy((struct evdev_device *) device); } -LIBINPUT_EXPORT void +LIBINPUT_EXPORT struct libinput_device * libinput_device_unref(struct libinput_device *device) { assert(device->refcount > 0); device->refcount--; - if (device->refcount == 0) + if (device->refcount == 0) { libinput_device_destroy(device); + return NULL; + } else { + return device; + } } LIBINPUT_EXPORT int diff --git a/src/libinput.h b/src/libinput.h index 5e75cc2c..96da0e0b 100644 --- a/src/libinput.h +++ b/src/libinput.h @@ -837,6 +837,9 @@ libinput_udev_assign_seat(struct libinput *libinput, * device are ignored. Such devices and those that failed to open * ignored until the next call to libinput_resume(). * + * The reference count of the context is initialized to 1. See @ref + * libinput_unref. + * * @param interface The callback interface * @param user_data Caller-specific data passed to the various callback * interfaces. @@ -867,6 +870,9 @@ libinput_udev_create_for_seat(const struct libinput_interface *interface, * The context is fully initialized but will not generate events until at * least one device has been added. * + * The reference count of the context is initialized to 1. See @ref + * libinput_unref. + * * @param interface The callback interface * @param user_data Caller-specific data passed to the various callback * interfaces. @@ -1016,13 +1022,27 @@ libinput_suspend(struct libinput *libinput); /** * @ingroup base * - * Destroy the libinput context. After this, object references associated with - * the destroyed context are invalid and may not be interacted with. + * Add a reference to the context. A context is destroyed whenever the + * reference count reaches 0. See @ref libinput_unref. + * + * @param libinput A previously initialized valid libinput context + * @return The passed libinput context + */ +struct libinput * +libinput_ref(struct libinput *libinput); + +/** + * @ingroup base + * + * Dereference the libinput context. After this, the context may have been + * destroyed, if the last reference was dereferenced. If so, the context is + * invalid and may not be interacted with. * * @param libinput A previously initialized libinput context + * @return NULL if context was destroyed otherwise the passed context */ -void -libinput_destroy(struct libinput *libinput); +struct libinput * +libinput_unref(struct libinput *libinput); /** * @ingroup base @@ -1132,8 +1152,9 @@ libinput_log_set_handler(struct libinput *libinput, * the seat correctly to avoid dangling pointers. * * @param seat A previously obtained seat + * @return The passed seat */ -void +struct libinput_seat * libinput_seat_ref(struct libinput_seat *seat); /** @@ -1145,8 +1166,9 @@ libinput_seat_ref(struct libinput_seat *seat); * the seat correctly to avoid dangling pointers. * * @param seat A previously obtained seat + * @return NULL if seat was destroyed, otherwise the passed seat */ -void +struct libinput_seat * libinput_seat_unref(struct libinput_seat *seat); /** @@ -1219,8 +1241,9 @@ libinput_seat_get_logical_name(struct libinput_seat *seat); * the device correctly to avoid dangling pointers. * * @param device A previously obtained device + * @return The passed device */ -void +struct libinput_device * libinput_device_ref(struct libinput_device *device); /** @@ -1232,8 +1255,9 @@ libinput_device_ref(struct libinput_device *device); * the device correctly to avoid dangling pointers. * * @param device A previously obtained device + * @return NULL if device was destroyed, otherwise the passed device */ -void +struct libinput_device * libinput_device_unref(struct libinput_device *device); /** diff --git a/src/udev-seat.c b/src/udev-seat.c index 9082697f..17cb3cb0 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -351,6 +351,7 @@ libinput_udev_create_context(const struct libinput_interface *interface, if (libinput_init(&input->base, interface, &interface_backend, user_data) != 0) { + libinput_unref(&input->base); free(input); return NULL; } @@ -400,7 +401,7 @@ libinput_udev_create_for_seat(const struct libinput_interface *interface, return NULL; if (libinput_udev_assign_seat(libinput, seat_id) != 0) { - libinput_destroy(libinput); + libinput_unref(libinput); libinput = NULL; } diff --git a/test/keyboard.c b/test/keyboard.c index e1af05f7..bf5d55af 100644 --- a/test/keyboard.c +++ b/test/keyboard.c @@ -108,7 +108,7 @@ START_TEST(keyboard_seat_key_count) for (i = 0; i < num_devices; ++i) litest_delete_device(devices[i]); - libinput_destroy(libinput); + libinput_unref(libinput); } END_TEST diff --git a/test/litest.c b/test/litest.c index 793d72f7..320a26d6 100644 --- a/test/litest.c +++ b/test/litest.c @@ -577,7 +577,7 @@ litest_delete_device(struct litest_device *d) libinput_device_unref(d->libinput_device); if (d->owns_context) - libinput_destroy(d->libinput); + libinput_unref(d->libinput); libevdev_free(d->evdev); libevdev_uinput_destroy(d->uinput); memset(d,0, sizeof(*d)); diff --git a/test/log.c b/test/log.c index 02ed4f08..6ce5e7f4 100644 --- a/test/log.c +++ b/test/log.c @@ -72,7 +72,7 @@ START_TEST(log_default_priority) ck_assert_int_eq(pri, LIBINPUT_LOG_PRIORITY_ERROR); - libinput_destroy(li); + libinput_unref(li); } END_TEST @@ -91,7 +91,7 @@ START_TEST(log_handler_invoked) ck_assert_int_gt(log_handler_called, 0); log_handler_called = 0; - libinput_destroy(li); + libinput_unref(li); log_handler_context = NULL; } @@ -110,7 +110,7 @@ START_TEST(log_handler_NULL) ck_assert_int_eq(log_handler_called, 0); log_handler_called = 0; - libinput_destroy(li); + libinput_unref(li); } END_TEST @@ -133,7 +133,7 @@ START_TEST(log_priority) log_handler_called = 0; - libinput_destroy(li); + libinput_unref(li); log_handler_context = NULL; } END_TEST diff --git a/test/misc.c b/test/misc.c index 133bdb60..bea7e889 100644 --- a/test/misc.c +++ b/test/misc.c @@ -133,7 +133,7 @@ START_TEST(event_conversion_device_notify) libinput_event_destroy(event); } - libinput_destroy(li); + libinput_unref(li); libevdev_uinput_destroy(uinput); ck_assert_int_gt(device_added, 0); @@ -194,7 +194,7 @@ START_TEST(event_conversion_pointer) libinput_event_destroy(event); } - libinput_destroy(li); + libinput_unref(li); libevdev_uinput_destroy(uinput); ck_assert_int_gt(motion, 0); @@ -254,7 +254,7 @@ START_TEST(event_conversion_pointer_abs) libinput_event_destroy(event); } - libinput_destroy(li); + libinput_unref(li); libevdev_uinput_destroy(uinput); ck_assert_int_gt(motion, 0); @@ -304,7 +304,7 @@ START_TEST(event_conversion_key) libinput_event_destroy(event); } - libinput_destroy(li); + libinput_unref(li); libevdev_uinput_destroy(uinput); ck_assert_int_gt(key, 0); @@ -364,19 +364,39 @@ START_TEST(event_conversion_touch) libinput_event_destroy(event); } - libinput_destroy(li); + libinput_unref(li); libevdev_uinput_destroy(uinput); ck_assert_int_gt(touch, 0); } END_TEST +START_TEST(context_ref_counting) +{ + struct libinput *li; + + /* These tests rely on valgrind to detect memory leak and use after + * free errors. */ + + li = libinput_path_create_context(&simple_interface, NULL); + ck_assert_notnull(li); + ck_assert_ptr_eq(libinput_unref(li), NULL); + + li = libinput_path_create_context(&simple_interface, NULL); + ck_assert_notnull(li); + ck_assert_ptr_eq(libinput_ref(li), li); + ck_assert_ptr_eq(libinput_unref(li), li); + ck_assert_ptr_eq(libinput_unref(li), NULL); +} +END_TEST + int main (int argc, char **argv) { litest_add_no_device("events:conversion", event_conversion_device_notify); litest_add_no_device("events:conversion", event_conversion_pointer); litest_add_no_device("events:conversion", event_conversion_pointer_abs); litest_add_no_device("events:conversion", event_conversion_key); litest_add_no_device("events:conversion", event_conversion_touch); + litest_add_no_device("context:refcount", context_ref_counting); return litest_run(argc, argv); } diff --git a/test/path.c b/test/path.c index 24f60e01..99b474eb 100644 --- a/test/path.c +++ b/test/path.c @@ -65,7 +65,7 @@ START_TEST(path_create_NULL) ck_assert(li == NULL); li = libinput_path_create_context(&simple_interface, NULL); ck_assert(li != NULL); - libinput_destroy(li); + libinput_unref(li); ck_assert_int_eq(open_func_count, 0); ck_assert_int_eq(close_func_count, 0); @@ -92,7 +92,7 @@ START_TEST(path_create_invalid) ck_assert_int_eq(open_func_count, 0); ck_assert_int_eq(close_func_count, 0); - libinput_destroy(li); + libinput_unref(li); ck_assert_int_eq(close_func_count, 0); open_func_count = 0; @@ -126,7 +126,7 @@ START_TEST(path_create_destroy) ck_assert_int_eq(open_func_count, 1); libevdev_uinput_destroy(uinput); - libinput_destroy(li); + libinput_unref(li); ck_assert_int_eq(close_func_count, 1); open_func_count = 0; @@ -372,7 +372,7 @@ START_TEST(path_suspend) libinput_resume(li); libevdev_uinput_destroy(uinput); - libinput_destroy(li); + libinput_unref(li); open_func_count = 0; close_func_count = 0; @@ -406,7 +406,7 @@ START_TEST(path_double_suspend) libinput_resume(li); libevdev_uinput_destroy(uinput); - libinput_destroy(li); + libinput_unref(li); open_func_count = 0; close_func_count = 0; @@ -440,7 +440,7 @@ START_TEST(path_double_resume) libinput_resume(li); libevdev_uinput_destroy(uinput); - libinput_destroy(li); + libinput_unref(li); open_func_count = 0; close_func_count = 0; @@ -523,7 +523,7 @@ START_TEST(path_add_device_suspend_resume) libevdev_uinput_destroy(uinput1); libevdev_uinput_destroy(uinput2); - libinput_destroy(li); + libinput_unref(li); open_func_count = 0; close_func_count = 0; @@ -614,7 +614,7 @@ START_TEST(path_add_device_suspend_resume_fail) ck_assert_int_eq(nevents, 2); libevdev_uinput_destroy(uinput2); - libinput_destroy(li); + libinput_unref(li); open_func_count = 0; close_func_count = 0; @@ -704,7 +704,7 @@ START_TEST(path_add_device_suspend_resume_remove_device) ck_assert_int_eq(nevents, 1); libevdev_uinput_destroy(uinput1); - libinput_destroy(li); + libinput_unref(li); open_func_count = 0; close_func_count = 0; @@ -790,7 +790,7 @@ START_TEST(path_seat_recycle) ck_assert(found == 1); - libinput_destroy(li); + libinput_unref(li); libevdev_uinput_destroy(uinput); } diff --git a/test/pointer.c b/test/pointer.c index 346e59b7..7d5668f8 100644 --- a/test/pointer.c +++ b/test/pointer.c @@ -292,7 +292,7 @@ START_TEST(pointer_seat_button_count) for (i = 0; i < num_devices; ++i) litest_delete_device(devices[i]); - libinput_destroy(libinput); + libinput_unref(libinput); } END_TEST diff --git a/test/udev.c b/test/udev.c index d13c6e24..f07decb6 100644 --- a/test/udev.c +++ b/test/udev.c @@ -68,7 +68,8 @@ START_TEST(udev_create_NULL) li = libinput_udev_create_context(&interface, NULL, udev); ck_assert(li != NULL); ck_assert_int_eq(libinput_udev_assign_seat(li, NULL), -1); - libinput_destroy(li); + + libinput_unref(li); udev_unref(udev); } END_TEST @@ -96,7 +97,7 @@ START_TEST(udev_create_seat0) ck_assert(event != NULL); libinput_event_destroy(event); - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); } END_TEST @@ -124,7 +125,7 @@ START_TEST(udev_create_empty_seat) ck_assert(event == NULL); libinput_event_destroy(event); - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); } END_TEST @@ -170,7 +171,7 @@ START_TEST(udev_added_seat_default) ck_assert(default_seat_found); - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); } END_TEST @@ -202,7 +203,7 @@ START_TEST(udev_double_suspend) libinput_resume(li); libinput_event_destroy(event); - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); } END_TEST @@ -234,7 +235,7 @@ START_TEST(udev_double_resume) libinput_resume(li); libinput_event_destroy(event); - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); } END_TEST @@ -293,7 +294,7 @@ START_TEST(udev_suspend_resume) process_events_count_devices(li, &num_devices); ck_assert_int_gt(num_devices, 0); - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); } END_TEST @@ -327,7 +328,7 @@ START_TEST(udev_device_sysname) libinput_event_destroy(ev); } - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); } END_TEST @@ -402,7 +403,7 @@ START_TEST(udev_seat_recycle) ck_assert(found == 1); - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); } END_TEST diff --git a/tools/event-debug.c b/tools/event-debug.c index 824c6b20..3eade366 100644 --- a/tools/event-debug.c +++ b/tools/event-debug.c @@ -162,7 +162,7 @@ open_udev(struct libinput **li) if (libinput_udev_assign_seat(*li, seat)) { fprintf(stderr, "Failed to set seat\n"); - libinput_destroy(*li); + libinput_unref(*li); return 1; } @@ -188,7 +188,7 @@ open_device(struct libinput **li, const char *path) device = libinput_path_add_device(*li, path); if (!device) { fprintf(stderr, "Failed to initialized device %s\n", path); - libinput_destroy(*li); + libinput_unref(*li); return 1; } @@ -489,7 +489,7 @@ main(int argc, char **argv) mainloop(li); - libinput_destroy(li); + libinput_unref(li); if (udev) udev_unref(udev); diff --git a/tools/event-gui.c b/tools/event-gui.c index df86a520..9475e778 100644 --- a/tools/event-gui.c +++ b/tools/event-gui.c @@ -467,7 +467,7 @@ main(int argc, char *argv[]) gtk_main(); - libinput_destroy(li); + libinput_unref(li); udev_unref(udev); return 0;