int attr_mask;
syscommon_resman_resource_event_cb callback;
void *user_data;
+ int removed;
+ GList *l;
};
static int set_resource_attr_interest(struct syscommon_resman_resource *resource, u_int64_t interest_mask);
notify_resource_event(int resource_type, int resource_id, u_int64_t attr_id, const void *data, int count);
static GList *g_resource_driver_head;
+
static GList *g_resource_event_data_list;
+static int is_notify_underway = 0;
+static bool need_event_data_cleanup = false;
/**
* key: resource_id, value: struct syscommon_resman_resource
return is_resource_attr_interested(resource, interest_mask);
}
+static void add_resource_event_data(struct resource_event_data *event_data)
+{
+ GList *new_list;
+
+ assert(event_data);
+
+ new_list = g_list_alloc();
+ new_list->data = event_data;
+
+ event_data->l = g_steal_pointer(&new_list);
+
+ g_resource_event_data_list = g_list_concat(g_resource_event_data_list, event_data->l);
+}
+
+static void remove_resource_event_data(struct resource_event_data *event_data)
+{
+ assert(event_data);
+
+ if (is_notify_underway > 0) {
+ event_data->removed = 1;
+ need_event_data_cleanup = true;
+ return;
+ }
+
+ g_resource_event_data_list = g_list_remove_link(g_resource_event_data_list, event_data->l);
+ g_list_free(g_steal_pointer(&event_data->l));
+ free(event_data);
+}
+
+static void cleanup_removed_event_data(gpointer data, gpointer user_data)
+{
+ struct resource_event_data *event_data = (struct resource_event_data *) data;
+
+ if (event_data->removed)
+ remove_resource_event_data(event_data);
+}
+
static void
notify_resource_event(int resource_type, int resource_id, u_int64_t attr_id, const void *data, int count)
{
GList *elem;
struct resource_event_data *event_data;
+ ++is_notify_underway;
+
for (elem = g_resource_event_data_list; elem; elem = elem->next) {
event_data = (struct resource_event_data *) elem->data;
assert(event_data);
+ if (event_data->removed)
+ continue;
+
if ((event_data->resource_type != RESOURCE_TYPE_ALL) && (event_data->resource_type != resource_type))
continue;
event_data->callback(resource_type, resource_id, attr_id, data, event_data->user_data, count);
}
+
+ if (--is_notify_underway > 0)
+ return;
+
+ if (!need_event_data_cleanup)
+ return;
+
+ /* cleanup removed event_data during callback */
+ g_list_foreach(g_resource_event_data_list, cleanup_removed_event_data, NULL);
+
+ need_event_data_cleanup = false;
}
static gboolean find_resource_event_data(gconstpointer data, gconstpointer id)
.user_data = user_data,
};
- g_resource_event_data_list = g_list_append(g_resource_event_data_list, event_data);
+ add_resource_event_data(event_data);
*id = event_data->id;
if (!list)
return;
- g_resource_event_data_list = g_list_remove_link(g_resource_event_data_list, list);
-
- free(list->data);
- list->data = NULL;
- g_list_free(list);
+ remove_resource_event_data(list->data);
}
const char *
#include "../test-main.h"
+#define MAGIC_UNSUBSCRIBE (2147483629)
+
static int setup_resource_drivers(void **state)
{
setup_display_resource_driver();
check_expected(count);
}
+static void test_event_callback_unsubscribe(int resource_type, int resource_id, int attr_id, const void *data, void *user_data, int count)
+{
+ int callback_id;
+ int value;
+
+ check_expected(resource_type);
+ check_expected(resource_id);
+ check_expected(attr_id);
+ check_expected_ptr(data);
+ check_expected(count);
+
+ value = *(const int *) data;
+ if (value == MAGIC_UNSUBSCRIBE) {
+ callback_id = *(int *) user_data;
+ syscommon_resman_unsubscribe_event(callback_id);
+ }
+}
+
static void test_subscribe_event_pass(void **state)
{
int resource_id1;
assert_int_equal(ret, -EINVAL);
}
+static void test_unsubscribe_event_during_callback_pass(void **state)
+{
+ int display_resource_id;
+ int callback_id1;
+ int callback_id2;
+ int int_value = 478478;
+ int ret;
+
+ display_resource_id = create_display_resource();
+
+ /**
+ * The callback test_event_callback_unsubscribe() will unsubscribe itself
+ * on setting DISPLAY_ATTR_INT to MAGIC_UNSUBSCRIBE
+ */
+ ret = syscommon_resman_subscribe_resource_attribute_event(TEST_RESOURCE_TYPE_DISPLAY,
+ DISPLAY_ATTR_INT, test_event_callback_unsubscribe,
+ &callback_id1, /* unsubscribed on setting DISPLAY_ATTR_INT to MAGIC_UNSUBSCRIBE */
+ &callback_id1);
+ assert_int_equal(ret, 0);
+
+ ret = syscommon_resman_subscribe_resource_attribute_event(TEST_RESOURCE_TYPE_DISPLAY,
+ DISPLAY_ATTR_INT, test_event_callback2, NULL, &callback_id2);
+ assert_int_equal(ret, 0);
+
+ /* configure mock setter, success */
+ expect_memory(test_display_set, data, &int_value, sizeof(int));
+ will_return(test_display_set, 0);
+ /* configure test_event_callback_unsubscribe() */
+ expect_value(test_event_callback_unsubscribe, resource_type, TEST_RESOURCE_TYPE_DISPLAY);
+ expect_value(test_event_callback_unsubscribe, resource_id, display_resource_id);
+ expect_value(test_event_callback_unsubscribe, attr_id, DISPLAY_ATTR_INT);
+ expect_memory(test_event_callback_unsubscribe, data, &int_value, sizeof(int));
+ expect_value(test_event_callback_unsubscribe, count, 1);
+ /* configure test_event_callback2() */
+ expect_value(test_event_callback2, resource_type, TEST_RESOURCE_TYPE_DISPLAY);
+ expect_value(test_event_callback2, resource_id, display_resource_id);
+ expect_value(test_event_callback2, attr_id, DISPLAY_ATTR_INT);
+ expect_memory(test_event_callback2, data, &int_value, sizeof(int));
+ expect_value(test_event_callback2, count, 1);
+
+ ret = syscommon_resman_set_resource_attr_int(display_resource_id, DISPLAY_ATTR_INT, int_value);
+ assert_int_equal(ret, 0);
+
+ /* now we expect the test_event_callback_unsubscribe() to remove iteslf */
+ int_value = MAGIC_UNSUBSCRIBE;
+
+ /* configure mock setter, success */
+ expect_memory(test_display_set, data, &int_value, sizeof(int));
+ will_return(test_display_set, 0);
+ /* configure test_event_callback_unsubscribe() */
+ expect_value(test_event_callback_unsubscribe, resource_type, TEST_RESOURCE_TYPE_DISPLAY);
+ expect_value(test_event_callback_unsubscribe, resource_id, display_resource_id);
+ expect_value(test_event_callback_unsubscribe, attr_id, DISPLAY_ATTR_INT);
+ expect_memory(test_event_callback_unsubscribe, data, &int_value, sizeof(int));
+ expect_value(test_event_callback_unsubscribe, count, 1);
+ /* configure test_event_callback2() */
+ expect_value(test_event_callback2, resource_type, TEST_RESOURCE_TYPE_DISPLAY);
+ expect_value(test_event_callback2, resource_id, display_resource_id);
+ expect_value(test_event_callback2, attr_id, DISPLAY_ATTR_INT);
+ expect_memory(test_event_callback2, data, &int_value, sizeof(int));
+ expect_value(test_event_callback2, count, 1);
+
+ ret = syscommon_resman_set_resource_attr_int(display_resource_id, DISPLAY_ATTR_INT, int_value);
+ assert_int_equal(ret, 0);
+
+ int_value = -10;
+
+ /* configure mock setter, success */
+ expect_memory(test_display_set, data, &int_value, sizeof(int));
+ will_return(test_display_set, 0);
+ /* do not configure test_event_callback_unsubscribe() as it must have been removed */
+ /* configure test_event_callback2() */
+ expect_value(test_event_callback2, resource_type, TEST_RESOURCE_TYPE_DISPLAY);
+ expect_value(test_event_callback2, resource_id, display_resource_id);
+ expect_value(test_event_callback2, attr_id, DISPLAY_ATTR_INT);
+ expect_memory(test_event_callback2, data, &int_value, sizeof(int));
+ expect_value(test_event_callback2, count, 1);
+
+ ret = syscommon_resman_set_resource_attr_int(display_resource_id, DISPLAY_ATTR_INT, int_value);
+ assert_int_equal(ret, 0);
+
+ syscommon_resman_unsubscribe_event(callback_id1);
+ delete_display_resource(display_resource_id);
+}
+
static const struct CMUnitTest resource_manager_testsuite[] = {
cmocka_unit_test(test_create_resource_pass),
cmocka_unit_test(test_create_resource_fail_driver_ops_create),
cmocka_unit_test(test_subscribe_resource_attribute_event_pass),
cmocka_unit_test(test_subscribe_resource_attribute_event_fail_invalid_parameter_1),
cmocka_unit_test(test_subscribe_resource_attribute_event_fail_invalid_parameter_2),
+
+ cmocka_unit_test(test_unsubscribe_event_during_callback_pass),
};
TESTSUITE_FIXTURE(resource_manager_testsuite, setup_resource_drivers, NULL);