--- /dev/null
+/* MIT License
+ *
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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 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. */
+
+#ifndef __SYSCOMMON_NOTIFIER_H__
+#define __SYSCOMMON_NOTIFIER_H__
+
+typedef int (*syscommon_notifier_cb) (void *notify_data);
+typedef int (*syscommon_notifier_udata_cb) (void *notify_data, void *user_data);
+typedef void (*syscommon_notifier_destroy_cb) (void *user_data);
+
+/**
+ * Subscribe notify without user_data callback.
+ * Higher number of priority will be notified first.
+ */
+int syscommon_notifier_subscribe_notify_priority(int type, syscommon_notifier_cb func, int priority);
+
+/* Subscribe notify without user_data callback, default priority 0 */
+static inline int syscommon_notifier_subscribe_notify(int type, syscommon_notifier_cb func)
+{
+ return syscommon_notifier_subscribe_notify_priority(type, func, 0);
+}
+
+/* Unsubscribe notify callback without user_data */
+int syscommon_notifier_unsubscribe_notify(int type, syscommon_notifier_cb func);
+
+/**
+ * Subscribe notify with user_data callback, destroy function, and priority.
+ * Higher number of priority will be notified first.
+ * Return notify id and can it be used for unsubscribing.
+ */
+int syscommon_notifier_subscribe_notify_udata_priority(int type,
+ syscommon_notifier_udata_cb func_udata, void *user_data,
+ syscommon_notifier_destroy_cb func_destroy_udata, int priority);
+
+/**
+ * Subscribe notify with user_data callback, and default priority 0.
+ * Return notify id and it can be used for unsubscribing.
+ */
+static inline int syscommon_notifier_subscribe_notify_udata(int type,
+ syscommon_notifier_udata_cb func_udata, void *user_data, syscommon_notifier_destroy_cb func_destroy)
+{
+ return syscommon_notifier_subscribe_notify_udata_priority(type, func_udata, user_data, func_destroy, 0);
+}
+
+/**
+ * Unsubscribe notify callback with user_data.
+ * Invoke destroy function with user_data.
+ */
+int syscommon_notifier_unsubscribe_notify_udata(int id);
+
+/* Emit notify. */
+void syscommon_notifier_emit_notify(int type, void *notify_data);
+
+/* Emit notify only once.
+ * Notification after the first one is ignored. */
+void syscommon_notifier_emit_notify_once(int type, void *notify_data);
+
+#endif /* __SYSCOMMON_NOTIFIER_H__ */
--- /dev/null
+/* MIT License
+ *
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ *
+ * 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 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <glib.h>
+
+#include "libsyscommon/notifier.h"
+#include "libsyscommon/list.h"
+#include "shared/log.h"
+
+struct syscommon_notifier {
+ int id;
+ int priority; /* descending order */
+ bool deleted;
+
+ int type;
+ int (*func)(void *data);
+
+ int (*func_udata)(void *data, void *user_data);
+ void (*destroyer)(void *user_data);
+ void *user_data;
+};
+
+static GList *syscommon_notifier_list;
+static guint idl;
+
+#define FIND_NOTIFIER(a, b, d, e, f) \
+ SYS_G_LIST_FOREACH(a, b, d) \
+ if (e == d->e && f == (d->f) && !d->deleted)
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+ /* descending order */
+ return ((const struct syscommon_notifier *)b)->priority - ((const struct syscommon_notifier *)a)->priority + 1;
+}
+
+int syscommon_notifier_subscribe_notify_priority(int type, syscommon_notifier_cb func, int priority)
+{
+ GList *n;
+ struct syscommon_notifier *notifier;
+
+ _I("notifier type=%d, func=%p", type, func);
+
+ if (!func) {
+ _E("Invalid func address.");
+ return -EINVAL;
+ }
+
+ FIND_NOTIFIER(syscommon_notifier_list, n, notifier, type, func) {
+ _E("Function has already been registered, [%d, %p]", type, func);
+ return -EINVAL;
+ }
+
+ notifier = calloc(1, sizeof(struct syscommon_notifier));
+ if (!notifier) {
+ _E("Fail to malloc for %d notifier.", type);
+ return -ENOMEM;
+ }
+
+ notifier->type = type;
+ notifier->priority = priority;
+ notifier->func = func;
+
+ syscommon_notifier_list = g_list_insert_sorted(syscommon_notifier_list, notifier, compare_priority);
+
+ return 0;
+}
+
+
+int syscommon_notifier_subscribe_notify_udata_priority(int type,
+ syscommon_notifier_udata_cb func_udata, void *user_data,
+ syscommon_notifier_destroy_cb func_destroy_udata, int priority)
+{
+ struct syscommon_notifier *notifier;
+ static int id = 1;
+
+ _I("notifier type=%d, func=%p", type, func_udata);
+
+ if (!func_udata) {
+ _E("Invalid func address.");
+ return -EINVAL;
+ }
+
+ notifier = calloc(1, sizeof(struct syscommon_notifier));
+ if (!notifier) {
+ _E("Fail to malloc for %d notifier.", type);
+ return -ENOMEM;
+ }
+
+ notifier->id = id;
+ notifier->priority = priority;
+ notifier->type = type;
+ notifier->func_udata = func_udata;
+ notifier->user_data = user_data;
+ notifier->destroyer = func_destroy_udata;
+
+ syscommon_notifier_list = g_list_insert_sorted(syscommon_notifier_list, notifier, compare_priority);
+
+ ++id;
+
+ return notifier->id;
+}
+
+int syscommon_notifier_unsubscribe_notify(int type, syscommon_notifier_cb func)
+{
+ GList *n;
+ struct syscommon_notifier *notifier;
+
+ if (!func) {
+ _E("Invalid func address.");
+ return -EINVAL;
+ }
+
+ FIND_NOTIFIER(syscommon_notifier_list, n, notifier, type, func) {
+ _I("notifier type=%d, func=%p", type, func);
+ notifier->deleted = true;
+ }
+
+ return 0;
+}
+
+int syscommon_notifier_unsubscribe_notify_udata(int id)
+{
+ GList *n;
+ struct syscommon_notifier *notifier;
+
+ SYS_G_LIST_FOREACH(syscommon_notifier_list, n, notifier) {
+ if (notifier->id == id) {
+ if (notifier->destroyer)
+ notifier->destroyer(notifier->user_data);
+ notifier->deleted = true;
+ }
+ }
+
+ return 0;
+}
+
+static gboolean delete_unused_notifier_cb(void *data)
+{
+ GList *n;
+ GList *next;
+ struct syscommon_notifier *notifier;
+
+ SYS_G_LIST_FOREACH_SAFE(syscommon_notifier_list, n, next, notifier) {
+ if (notifier->deleted) {
+ SYS_G_LIST_REMOVE_LIST(syscommon_notifier_list, n);
+ free(notifier);
+ }
+ }
+
+ idl = 0;
+ return G_SOURCE_REMOVE;
+}
+
+void syscommon_notifier_emit_notify(int type, void *data)
+{
+ GList *n;
+ struct syscommon_notifier *notifier;
+
+ SYS_G_LIST_FOREACH(syscommon_notifier_list, n, notifier) {
+ if (!notifier->deleted && type == notifier->type) {
+ if (notifier->func)
+ notifier->func(data);
+ else if (notifier->func_udata)
+ notifier->func_udata(data, notifier->user_data);
+ }
+ }
+
+ if (!idl)
+ idl = g_idle_add(delete_unused_notifier_cb, NULL);
+}
+
+void syscommon_notifier_emit_notify_once(int type, void *data)
+{
+ GList *n;
+ struct syscommon_notifier *notifier;
+
+ SYS_G_LIST_FOREACH(syscommon_notifier_list, n, notifier) {
+ if (!notifier->deleted && type == notifier->type) {
+ if (notifier->func) {
+ notifier->func(data);
+ } else if (notifier->func_udata) {
+ notifier->func_udata(data, notifier->user_data);
+ if (notifier->destroyer)
+ notifier->destroyer(notifier->user_data);
+ }
+
+ notifier->deleted = true;
+ }
+ }
+
+ if (!idl)
+ idl = g_idle_add(delete_unused_notifier_cb, NULL);
+}
#include "libsyscommon/list.h"
#include "libsyscommon/ini-parser.h"
#include "libsyscommon/bitmap.h"
+#include "libsyscommon/notifier.h"
#include "../test-main.h"
#include "../test-mock.h"
#define MOCK_FILE_PATH "testfile"
#define MOCK_FILE_LENGTH 100
+#define SYSCOMMON_NOTIFIER_1 (1)
+#define SYSCOMMON_NOTIFIER_2 (2)
+#define SYSCOMMON_NOTIFIER_3 (3)
+#define SYSCOMMON_NOTIFIER_4 (4)
+#define SYSCOMMON_NOTIFIER_5 (5)
+#define SYSCOMMON_NOTIFIER_6 (6)
+#define SYSCOMMON_NOTIFIER_7 (7)
+#define SYSCOMMON_NOTIFIER_8 (8)
+
static int setup(void **state)
{
FILE *fp;
syscommon_bitmap_deinit_bitmap(bm);
}
+static int notify_callback(void *data)
+{
+ check_expected(data);
+
+ return 0;
+}
+
+static int notify_callback_udata(void *data, void *udata)
+{
+ check_expected(data);
+ check_expected(udata);
+
+ return 0;
+}
+
+static void destroy_callback_udata(void *udata)
+{
+ check_expected(udata);
+}
+
+static void test_syscommon_notifier_emit_notify_p1(void **state)
+{
+ int retval;
+
+ retval = syscommon_notifier_subscribe_notify(SYSCOMMON_NOTIFIER_1, notify_callback);
+ assert_int_equal(retval, 0);
+ expect_value(notify_callback, data, (void *)(intptr_t) 0x3f3f3f3f);
+
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_1, (void *)(intptr_t) 0x3f3f3f3f);
+}
+
+static void test_syscommon_notifier_emit_notify_p2(void **state)
+{
+ int retval;
+ int notify_data;
+ const int udata2 = 0xaeaeaeae;
+ const int udata3 = 0x99997777;
+
+ notify_data = 0x12456321;
+
+ /* 1st notifier */
+ retval = syscommon_notifier_subscribe_notify(SYSCOMMON_NOTIFIER_2, notify_callback);
+ assert_int_equal(retval, 0);
+
+ /* 2nd notifier */
+ retval = syscommon_notifier_subscribe_notify_udata(SYSCOMMON_NOTIFIER_2, notify_callback_udata, (void *)(intptr_t) udata2, NULL);
+ assert_in_range(retval, 1, INT_MAX);
+
+ /* expect invocation of 1st callback and check parameter */
+ expect_value(notify_callback, data, (void *)(intptr_t) notify_data);
+ /* expect invocation of 2nd callback and check parameter */
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) notify_data);
+ expect_value(notify_callback_udata, udata,(void *)(intptr_t) udata2);
+
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_2, (void *)(intptr_t) notify_data);
+
+ notify_data = 0x888899dd;
+
+ /* 3rd notifier */
+ retval = syscommon_notifier_subscribe_notify_udata(SYSCOMMON_NOTIFIER_2, notify_callback_udata, (void *)(intptr_t) udata3, NULL);
+ assert_in_range(retval, 1, INT_MAX);
+
+ /* expect invocation of 1st callback and check parameter */
+ expect_value(notify_callback, data, (void *)(intptr_t) notify_data);
+ /* expect invocation of 2nd callback and check parameter */
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) notify_data);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) udata2);
+ /* expect invocation of 3rd callback and check parameter */
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) notify_data);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) udata3);
+
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_2, (void *)(intptr_t) notify_data);
+}
+
+static void test_syscommon_notifier_emit_notify_p3(void **state)
+{
+ int notify_data;
+ int id1, id2;
+ const int udata1 = 0x41a41a41;
+ const int udata2 = 0x77777777;
+
+ /* first run */
+ notify_data = 0x8575ddff;
+
+ /* 1st notifier */
+ id1 = syscommon_notifier_subscribe_notify_udata(SYSCOMMON_NOTIFIER_3, notify_callback_udata, (void *)(intptr_t) udata1, NULL);
+ assert_in_range(id1, 1, INT_MAX);
+
+ /* 2nd notifier */
+ id2 = syscommon_notifier_subscribe_notify_udata(SYSCOMMON_NOTIFIER_3, notify_callback_udata, (void *)(intptr_t) udata2, NULL);
+ assert_in_range(id2, 1, INT_MAX);
+
+ /* expect invocation of 1st callback and check parameter */
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) notify_data);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) udata1);
+ /* expect invocation of 2nd callback and check parameter */
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) notify_data);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) udata2);
+
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_3, (void *)(intptr_t) notify_data);
+
+ /* second run. at this time syscommon_notifier_emit_notify() after unregistering 1st notifier */
+ notify_data = 0x345;
+
+ syscommon_notifier_unsubscribe_notify_udata(id1);
+
+ /* only expect invocation of 2nd callback and check parameter */
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) notify_data);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) udata2);
+
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_3, (void *)(intptr_t) notify_data);
+}
+
+static void test_syscommon_notifier_emit_notify_once_p(void **state)
+{
+ int retval;
+
+ retval = syscommon_notifier_subscribe_notify(SYSCOMMON_NOTIFIER_4, notify_callback);
+ assert_int_equal(retval, 0);
+
+ expect_value(notify_callback, data, (void *)(intptr_t) 0xabcdabcd);
+ syscommon_notifier_emit_notify_once(SYSCOMMON_NOTIFIER_4, (void *)(intptr_t) 0xabcdabcd);
+
+ /* Don't add expect_value() for callback at this time.
+ * Therefore if the callback is invoked, check_expected() returns error */
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_4, (void *)(intptr_t) 0xabcdabcd);
+
+ retval = syscommon_notifier_subscribe_notify_udata(SYSCOMMON_NOTIFIER_5, notify_callback_udata, (void *)(intptr_t) 0xfefefefe, NULL);
+ assert_in_range(retval, 1, INT_MAX);
+
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) 0x34343434);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) 0xfefefefe);
+ syscommon_notifier_emit_notify_once(SYSCOMMON_NOTIFIER_5, (void *)(intptr_t) 0x34343434);
+
+ /* Don't add expect_value() for callback at this time.
+ * Therefore if the callback is invoked, check_expected() returns error */
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_5, (void *)(intptr_t) 0x34343434);
+}
+
+static void test_destroy_callback_p1(void **state)
+{
+ int id;
+
+ id = syscommon_notifier_subscribe_notify_udata(SYSCOMMON_NOTIFIER_6, notify_callback_udata,
+ (void *)(intptr_t) 0x4848, destroy_callback_udata);
+ assert_in_range(id, 1, INT_MAX);
+
+ expect_value(destroy_callback_udata, udata, (void *)(intptr_t) 0x4848);
+
+ syscommon_notifier_unsubscribe_notify_udata(id);
+}
+
+static void test_destroy_callback_p2(void **state)
+{
+ int id;
+
+ id = syscommon_notifier_subscribe_notify_udata(SYSCOMMON_NOTIFIER_6, notify_callback_udata,
+ (void *)(intptr_t) 0x1177, destroy_callback_udata);
+ assert_in_range(id, 1, INT_MAX);
+
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) 0x9a9a9a9a);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) 0x1177);
+ expect_value(destroy_callback_udata, udata, (void *)(intptr_t) 0x1177);
+
+ syscommon_notifier_emit_notify_once(SYSCOMMON_NOTIFIER_6, (void *)(intptr_t) 0x9a9a9a9a);
+}
+
+static void test_destroy_callback_p3(void **state)
+{
+ int retval;
+
+ retval = syscommon_notifier_subscribe_notify(SYSCOMMON_NOTIFIER_7, notify_callback);
+ assert_int_equal(retval, 0);
+ expect_value(notify_callback, data, (void *)(intptr_t) 0x3f3f3f3f);
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_7, (void *)(intptr_t) 0x3f3f3f3f);
+
+ /* notify_callback() should not be invoked */
+ syscommon_notifier_unsubscribe_notify(SYSCOMMON_NOTIFIER_7, notify_callback);
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_7, (void *)(intptr_t) 0x34ab34ab);
+
+ /* if a notifier have successfully been deleted,
+ * re-registering the notifier must be successful */
+ retval = syscommon_notifier_subscribe_notify(SYSCOMMON_NOTIFIER_7, notify_callback);
+ assert_int_equal(retval, 0);
+
+ expect_value(notify_callback, data, (void *)(intptr_t) 0x7878444);
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_7, (void *)(intptr_t) 0x7878444);
+}
+
+static void test_syscommon_notifier_emit_notify_priority_p1(void **state)
+{
+ int id1, id2, id3;
+
+ id1 = syscommon_notifier_subscribe_notify_udata_priority(SYSCOMMON_NOTIFIER_8, notify_callback_udata,
+ (void *)(intptr_t) 0x11111111, NULL, -300);
+ assert_in_range(id1, 1, INT_MAX);
+
+ id2 = syscommon_notifier_subscribe_notify_udata_priority(SYSCOMMON_NOTIFIER_8, notify_callback_udata,
+ (void *)(intptr_t) 0x22222222, NULL, 500);
+ assert_in_range(id2, 1, INT_MAX);
+
+ id3 = syscommon_notifier_subscribe_notify_udata_priority(SYSCOMMON_NOTIFIER_8, notify_callback_udata,
+ (void *)(intptr_t) 0x33333333, NULL, -300);
+ assert_in_range(id3, 1, INT_MAX);
+
+ /* id2 will be invoked first */
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) 0x1234);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) 0x22222222);
+ /* id1, id3 invocation follows it,
+ * and those are invoked in the order in which they were registered */
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) 0x1234);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) 0x11111111);
+ expect_value(notify_callback_udata, data, (void *)(intptr_t) 0x1234);
+ expect_value(notify_callback_udata, udata, (void *)(intptr_t) 0x33333333);
+
+ syscommon_notifier_emit_notify(SYSCOMMON_NOTIFIER_8, (void *)(intptr_t)0x1234);
+}
+
int run_test_suite(void)
{
const struct CMUnitTest testsuite[] = {
cmocka_unit_test(test_init_bitmap_n),
cmocka_unit_test(test_test_bit),
cmocka_unit_test(test_all_bit),
+ cmocka_unit_test(test_syscommon_notifier_emit_notify_p1),
+ cmocka_unit_test(test_syscommon_notifier_emit_notify_p2),
+ cmocka_unit_test(test_syscommon_notifier_emit_notify_p3),
+ cmocka_unit_test(test_syscommon_notifier_emit_notify_once_p),
+ cmocka_unit_test(test_destroy_callback_p1),
+ cmocka_unit_test(test_destroy_callback_p2),
+ cmocka_unit_test(test_destroy_callback_p3),
+ cmocka_unit_test(test_syscommon_notifier_emit_notify_priority_p1),
};
return cmocka_run_group_tests(testsuite, NULL, NULL);