--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIZEN_CORE_UTIL_TIZEN_CORE_THREAD_CALLBACK_H_
+#define TIZEN_CORE_UTIL_TIZEN_CORE_THREAD_CALLBACK_H_
+
+#include <tizen_core.h>
+#include <unistd.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <tuple>
+
+namespace tizen_core_util {
+
+template <class HANDLE, class SETTER, class UNSETTER, class CB,
+ std::size_t USER_DATA_INDEX, std::copyable... TYPES>
+class ThreadCallbackContext {
+ public:
+ ThreadCallbackContext(const ThreadCallbackContext&) = delete;
+ ThreadCallbackContext(ThreadCallbackContext&&) noexcept = delete;
+
+ ThreadCallbackContext& operator=(const ThreadCallbackContext&) = delete;
+ ThreadCallbackContext& operator=(ThreadCallbackContext&&) = delete;
+
+ static auto& GetInst() {
+ static ThreadCallbackContext<HANDLE, SETTER, UNSETTER, CB, USER_DATA_INDEX,
+ TYPES...>
+ tcb;
+ return tcb;
+ }
+
+ struct HandleContext {
+ ~HandleContext() {
+ for (auto& [core, fields] : data_) {
+ auto* source = std::get<2>(fields);
+ if (source) tizen_core_remove_source(core, source);
+ }
+ }
+
+ std::map<tizen_core_h, std::tuple<CB, void*, tizen_core_source_h>> data_;
+ };
+
+ int Add(HANDLE handle, SETTER cb_setter, CB cb, void* data) {
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
+ tizen_core_h core = nullptr;
+ int ret = tizen_core_find_from_this_thread(&core);
+ if (ret != 0) return ret;
+ bool first = false;
+ if (contexts_[handle].data_.empty()) first = true;
+ contexts_[handle].data_[core] = {cb, data, nullptr};
+ if (!first) return 0;
+ cb_setter(
+ handle,
+ [](TYPES... args) {
+ auto& obj =
+ ThreadCallbackContext<HANDLE, SETTER, UNSETTER, CB,
+ USER_DATA_INDEX, TYPES...>::GetInst();
+ std::lock_guard<std::recursive_mutex> lock(obj.mutex_);
+ auto params = std::make_shared<std::tuple<TYPES...>>(args...);
+ auto* cxt =
+ static_cast<HandleContext*>(std::get<USER_DATA_INDEX>(*params));
+
+ if (obj.on_copy_)
+ params = std::shared_ptr<std::tuple<TYPES...>>(
+ new std::tuple<TYPES...>(obj.on_copy_(*std::move(params))));
+
+ for (auto& [core, fields] : cxt->data_) {
+ auto* pair =
+ new std::pair<std::tuple<CB, void*, tizen_core_source_h>&,
+ std::shared_ptr<std::tuple<TYPES...>>>{fields,
+ params};
+ tizen_core_add_idle_job(
+ core,
+ [](void* data) {
+ auto& obj = ThreadCallbackContext<HANDLE, SETTER, UNSETTER,
+ CB, USER_DATA_INDEX,
+ TYPES...>::GetInst();
+ std::lock_guard<std::recursive_mutex> lock(obj.GetLock());
+
+ auto* pair = static_cast<
+ std::pair<std::tuple<CB, void*, HANDLE>&,
+ std::shared_ptr<std::tuple<TYPES...>>>*>(data);
+ auto& fields = (*pair).first;
+ CB cb = std::get<0>(fields);
+ void* org_data = std::get<1>(fields);
+ std::tuple<TYPES...> params = *(*pair).second;
+ std::get<USER_DATA_INDEX>(params) = org_data;
+ std::apply([cb](auto&&... args) { cb(args...); }, params);
+ std::get<2>(fields) = nullptr;
+
+ delete pair;
+ return false;
+ },
+ pair, &std::get<2>(fields));
+ }
+ },
+ &contexts_[handle]);
+ return 0;
+ }
+
+ int Remove(HANDLE handle, UNSETTER cb_unsetter) {
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
+ tizen_core_h core = nullptr;
+ int ret = tizen_core_find_from_this_thread(&core);
+ if (ret != 0) return ret;
+ contexts_[handle].data_.erase(core);
+
+ if (contexts_[handle].data_.empty()) cb_unsetter(handle);
+ return 0;
+ }
+
+ void SetOnCopy(
+ std::function<std::tuple<TYPES...>(std::tuple<TYPES...>)> on_copy) {
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
+ on_copy_ = std::move(on_copy);
+ }
+
+ void Clear() {
+ contexts_.clear();
+ on_copy_ = nullptr;
+ }
+
+ std::recursive_mutex& GetLock() { return mutex_; }
+
+ private:
+ ThreadCallbackContext() = default;
+ std::map<HANDLE, HandleContext> contexts_;
+
+ std::function<std::tuple<TYPES...>(std::tuple<TYPES...>)> on_copy_;
+ std::recursive_mutex mutex_;
+};
+
+} // namespace tizen_core_util
+
+/**
+ * @brief Creates a thread-specific callback registration API using existing
+setter APIs.
+ * @code
+#include <tizen_core_thread_callback.hpp>
+
+TCORE_MAKE_THREAD_CALLBACK_SETTER(package_manager_set_event_cb,
+package_manager_h, package_manager_event_cb, 6, const char*, const char*,
+package_manager_event_type_e, package_manager_event_state_e, int,
+package_manager_error_e, void*)
+
+void __event_cb(const char*, const char*,
+ package_manager_event_type_e, package_manager_event_state_e, int,
+ package_manager_error_e, void*)
+{
+}
+
+int main()
+{
+ package_manager_h handle;
+ ...
+
+ package_manager_set_event_cb(handle, __event_cb, nullptr);
+}
+* @endcode
+ */
+#define TCORE_MAKE_THREAD_CALLBACK_SETTER(ORG_SETTER, HANDLE_TYPE, CB_TYPE, \
+ USER_DATA_INDEX, ...) \
+ int ORG_SETTER##_tcc(HANDLE_TYPE handle, CB_TYPE cb, void* user_data) { \
+ auto setter = [](HANDLE_TYPE handle, CB_TYPE cb, void* user_data) { \
+ ORG_SETTER(handle, cb, user_data); \
+ }; \
+ auto& tcc = tizen_core_util::ThreadCallbackContext< \
+ HANDLE_TYPE, std::function<void(HANDLE_TYPE, CB_TYPE, void*)>, \
+ std::function<void(HANDLE_TYPE)>, CB_TYPE, USER_DATA_INDEX, \
+ __VA_ARGS__>::GetInst(); \
+ return tcc.Add(handle, setter, cb, user_data); \
+ }
+
+/**
+ * @brief Creates a thread-specific callback registration API using existing
+setter APIs.
+ * @code
+#include <tizen_core_thread_callback.hpp>
+
+TCORE_MAKE_THREAD_CALLBACK_SETTER_NO_HANDLE(app_manager_set_app_context_event_cb,
+app_manager_app_context_event_cb, 2, app_context_h, app_context_event_e, void*)
+
+void __event_cb(app_context_h app_context, app_context_event_e event, void
+*user_data)
+{
+}
+
+int main()
+{
+ app_manager_set_app_context_event_cb_tcc(app_context_event_cb, nullptr);
+}
+* @endcode
+ */
+#define TCORE_MAKE_THREAD_CALLBACK_SETTER_NO_HANDLE(ORG_SETTER, CB_TYPE, \
+ USER_DATA_INDEX, ...) \
+ int ORG_SETTER##_tcc(CB_TYPE cb, void* user_data) { \
+ auto setter = [](void*, CB_TYPE cb, void* user_data) { \
+ ORG_SETTER(cb, user_data); \
+ }; \
+ auto& tcc = tizen_core_util::ThreadCallbackContext< \
+ void*, std::function<void(void*, CB_TYPE, void*)>, \
+ std::function<void(void*)>, CB_TYPE, USER_DATA_INDEX, \
+ __VA_ARGS__>::GetInst(); \
+ return tcc.Add(nullptr, setter, cb, user_data); \
+ }
+
+/**
+ * @brief Creates a thread-specific callback unregistration API using existing
+unsetter APIs.
+ * @code
+#include <tizen_core_thread_callback.hpp>
+
+TCORE_MAKE_THREAD_CALLBACK_UNSETTER(package_manager_unset_event_cb,
+package_manager_h, package_manager_event_cb, 6, const char*, const char*,
+package_manager_event_type_e, package_manager_event_state_e, int,
+package_manager_error_e, void*)
+
+int main()
+{
+ package_manager_h handle;
+ ...
+
+ package_manager_unset_event_cb_tcc(handle);
+}
+* @endcode
+ */
+#define TCORE_MAKE_THREAD_CALLBACK_UNSETTER(ORG_UNSETTER, HANDLE_TYPE, \
+ CB_TYPE, USER_DATA_INDEX, ...) \
+ int ORG_UNSETTER##_tcc(HANDLE_TYPE handle) { \
+ auto unsetter = [](HANDLE_TYPE handle) { ORG_UNSETTER(handle); }; \
+ auto& tcc = tizen_core_util::ThreadCallbackContext< \
+ HANDLE_TYPE, std::function<void(HANDLE_TYPE, CB_TYPE, void*)>, \
+ std::function<void(HANDLE_TYPE)>, CB_TYPE, USER_DATA_INDEX, \
+ __VA_ARGS__>::GetInst(); \
+ return tcc.Remove(handle, unsetter); \
+ }
+
+/**
+ * @brief Creates a thread-specific callback unregistration API using existing
+unsetter APIs.
+ * @code
+#include <tizen_core_thread_callback.hpp>
+
+TCORE_MAKE_THREAD_CALLBACK_UNSETTER_NO_HANDLE(app_manager_unset_app_context_event_cb,
+app_manager_app_context_event_cb, 2, app_context_h, app_context_event_e, void*)
+
+int main()
+{
+ app_manager_unset_app_context_event_cb_tcc();
+}
+* @endcode
+ */
+#define TCORE_MAKE_THREAD_CALLBACK_UNSETTER_NO_HANDLE(ORG_UNSETTER, CB_TYPE, \
+ USER_DATA_INDEX, ...) \
+ int ORG_UNSETTER##_tcc(void) { \
+ auto unsetter = [](void*) { ORG_UNSETTER(); }; \
+ auto& tcc = tizen_core_util::ThreadCallbackContext< \
+ void*, std::function<void(void*, CB_TYPE, void*)>, \
+ std::function<void(void*)>, CB_TYPE, USER_DATA_INDEX, \
+ __VA_ARGS__>::GetInst(); \
+ return tcc.Remove(nullptr, unsetter); \
+ }
+
+#endif // TIZEN_CORE_UTIL_TIZEN_CORE_THREAD_CALLBACK_H_
--- /dev/null
+/*
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <tizen_core.h>
+#include <unistd.h>
+
+#include "src/tizen-core-util/tizen_core_thread_callback.hpp"
+#include "src/tizen-core/include/tizen_core.h"
+
+// LCOV_EXCL_START
+
+namespace {
+int hit_cnt = 0;
+
+using simple_cb = void (*)(void* user_data);
+int sc_source = 0;
+std::pair<simple_cb, void*> sc_context;
+
+void invoke_simple_callback() {
+ sc_source = g_idle_add(
+ [](gpointer data) -> gboolean {
+ sc_context.first(sc_context.second); // invoke callback
+ sc_source = 0;
+ return G_SOURCE_REMOVE;
+ },
+ nullptr);
+}
+
+void set_simple_callback(simple_cb cb, void* data) { sc_context = {cb, data}; }
+
+void unset_simple_callback() {
+ if (sc_source > 0) {
+ g_source_remove(sc_source);
+ sc_source = 0;
+ }
+}
+
+TCORE_MAKE_THREAD_CALLBACK_SETTER_NO_HANDLE(set_simple_callback, simple_cb, 0,
+ void*)
+TCORE_MAKE_THREAD_CALLBACK_UNSETTER_NO_HANDLE(unset_simple_callback, simple_cb,
+ 0, void*)
+
+class HandleCallbackContext;
+using handle_callback_h = HandleCallbackContext*;
+using handle_cb = void (*)(handle_callback_h h, int state, void* user_data);
+
+struct HandleCallbackContext {
+ ~HandleCallbackContext() {
+ if (source_ > 0) g_source_remove(source_);
+ }
+
+ int source_ = 0;
+ std::pair<handle_cb, void*> context_;
+};
+
+handle_callback_h create_handle_callback() {
+ return new HandleCallbackContext();
+}
+
+void destory_handle_callback(handle_callback_h handle) { delete handle; }
+
+void invoke_handle_callback(handle_callback_h handle) {
+ handle->source_ = g_idle_add(
+ [](gpointer data) -> gboolean {
+ auto* self = static_cast<HandleCallbackContext*>(data);
+ self->context_.first(self, 3,
+ self->context_.second); // invoke callback
+ self->source_ = 0;
+ return G_SOURCE_REMOVE;
+ },
+ handle);
+}
+
+void set_handle_callback(handle_callback_h handle, handle_cb cb,
+ void* user_data) {
+ handle->context_ = {cb, user_data};
+}
+
+void unset_handle_callback(handle_callback_h handle) {
+ if (handle->source_ > 0) {
+ g_source_remove(handle->source_);
+ handle->source_ = 0;
+ }
+}
+
+TCORE_MAKE_THREAD_CALLBACK_SETTER(set_handle_callback, handle_callback_h,
+ handle_cb, 2, handle_callback_h, int, void*)
+TCORE_MAKE_THREAD_CALLBACK_UNSETTER(unset_handle_callback, handle_callback_h,
+ handle_cb, 2, handle_callback_h, int, void*)
+
+} // namespace
+
+using namespace tizen_core_util;
+
+class TizenCoreUtilTest : public ::testing::Test {
+ public:
+ TizenCoreUtilTest() {}
+ virtual ~TizenCoreUtilTest() {}
+
+ void SetUp() override {
+ TCoreSetUp();
+ SetTimeout();
+ handle_cb1_ = create_handle_callback();
+ }
+
+ void TearDown() override {
+ if (timeout_source) {
+ tizen_core_remove_source(main_task_, timeout_source);
+ timeout_source = nullptr;
+ }
+
+ TCoreTearDown();
+ ClearThreadCallbackContext();
+ ref_count_ = 1;
+ destory_handle_callback(handle_cb1_);
+ }
+
+ void Run() { tizen_core_task_run(main_task_); }
+
+ void RunWorker() {
+ tizen_core_task_run(task1_);
+ tizen_core_task_run(task2_);
+ tizen_core_task_run(task3_);
+ sleep(1);
+ }
+
+ void Stop() {
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
+ if (--ref_count_ == 0) {
+ tizen_core_task_quit(task1_);
+ tizen_core_task_quit(task2_);
+ tizen_core_task_quit(task3_);
+ tizen_core_task_quit(main_task_);
+ }
+ }
+
+ void SendEvent() { invoke_simple_callback(); }
+ void SendEvent(handle_callback_h h) { invoke_handle_callback(h); }
+
+ void SetTimeout() {
+ tizen_core_add_timer(
+ main_task_, 2000,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ tizen_core_task_quit(self->task1_);
+ tizen_core_task_quit(self->task2_);
+ tizen_core_task_quit(self->task3_);
+ tizen_core_task_quit(self->main_task_);
+ EXPECT_TRUE(false);
+ return false;
+ },
+ this, &timeout_source);
+ }
+
+ void ClearThreadCallbackContext() {
+ ThreadCallbackContext<void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0,
+ void*>::GetInst()
+ .Clear();
+ ThreadCallbackContext<handle_callback_h,
+ std::function<void(void*, handle_cb, void*)>,
+ std::function<void(handle_cb)>, handle_cb, 2,
+ handle_callback_h, int, void*>::GetInst()
+ .Clear();
+ }
+
+ void SetStopRef(int cnt) {
+ std::lock_guard<std::recursive_mutex> lock(mutex_);
+ ref_count_ = cnt;
+ }
+
+ tizen_core_task_h task1_ = nullptr;
+ tizen_core_task_h task2_ = nullptr;
+ tizen_core_task_h task3_ = nullptr;
+ tizen_core_task_h main_task_ = nullptr;
+ tizen_core_source_h timeout_source = nullptr;
+ int tid_ = 0;
+ int ref_count_ = 1;
+ std::recursive_mutex mutex_;
+ handle_callback_h handle_cb1_ = nullptr;
+
+ private:
+ void TCoreSetUp() {
+ tizen_core_init();
+ tizen_core_task_create("test_task1", true, &task1_);
+ tizen_core_task_create("test_task2", true, &task2_);
+ tizen_core_task_create("test_task3", true, &task3_);
+ tizen_core_task_create("main", false, &main_task_);
+ tid_ = gettid();
+ }
+
+ void TCoreTearDown() {
+ if (task1_) {
+ tizen_core_task_destroy(task1_);
+ task1_ = nullptr;
+ }
+
+ if (task2_) {
+ tizen_core_task_destroy(task2_);
+ task2_ = nullptr;
+ }
+
+ if (task3_) {
+ tizen_core_task_destroy(task3_);
+ task3_ = nullptr;
+ }
+
+ if (main_task_) {
+ tizen_core_task_destroy(main_task_);
+ main_task_ = nullptr;
+ }
+
+ tizen_core_shutdown();
+ }
+};
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Add_MainThread_P) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+
+ auto& tcc =
+ ThreadCallbackContext<void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0,
+ void*>::GetInst();
+ tcc.Add(
+ nullptr, setter,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ self->Stop();
+ },
+ this);
+
+ SendEvent();
+ Run();
+}
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Add_WorkerThread_P) {
+ tizen_core_source_h source = nullptr;
+ hit_cnt = 0;
+ tizen_core_add_idle_job(
+ task1_,
+ [](void* data) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+
+ auto& tcc = ThreadCallbackContext<
+ void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0, void*>::GetInst();
+
+ tcc.Add(
+ nullptr, setter,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+
+ return false;
+ },
+ this, &source);
+
+ RunWorker();
+ SendEvent();
+ Run();
+ EXPECT_EQ(hit_cnt, 1);
+}
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Add_WorkerThread_Multi_P) {
+ tizen_core_source_h source = nullptr;
+ SetStopRef(2);
+ hit_cnt = 0;
+ tizen_core_add_idle_job(
+ task1_,
+ [](void* data) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+
+ auto& tcc = ThreadCallbackContext<
+ void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0, void*>::GetInst();
+
+ tcc.Add(
+ nullptr, setter,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+
+ return false;
+ },
+ this, &source);
+
+ tizen_core_add_idle_job(
+ task2_,
+ [](void* data) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+
+ auto& tcc = ThreadCallbackContext<
+ void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0, void*>::GetInst();
+
+ tcc.Add(
+ nullptr, setter,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+
+ return false;
+ },
+ this, &source);
+
+ RunWorker();
+ SendEvent();
+ Run();
+ EXPECT_EQ(hit_cnt, 2);
+}
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Add_WorkerThread_Multi_P2) {
+ tizen_core_source_h source = nullptr;
+ SetStopRef(3);
+ hit_cnt = 0;
+
+ tizen_core_add_idle_job(
+ task1_,
+ [](void* data) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+
+ auto& tcc = ThreadCallbackContext<
+ void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0, void*>::GetInst();
+
+ tcc.Add(
+ nullptr, setter,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+
+ return false;
+ },
+ this, &source);
+
+ tizen_core_add_idle_job(
+ task2_,
+ [](void* data) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+
+ auto& tcc = ThreadCallbackContext<
+ void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0, void*>::GetInst();
+
+ tcc.Add(
+ nullptr, setter,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+
+ return false;
+ },
+ this, &source);
+
+ tizen_core_add_idle_job(
+ task3_,
+ [](void* data) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+
+ auto& tcc = ThreadCallbackContext<
+ void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0, void*>::GetInst();
+
+ tcc.Add(
+ nullptr, setter,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+
+ return false;
+ },
+ this, &source);
+
+ RunWorker();
+ SendEvent();
+ Run();
+ EXPECT_EQ(hit_cnt, 3);
+}
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Remove_P) {
+ tizen_core_source_h source = nullptr;
+ hit_cnt = 0;
+ tizen_core_add_idle_job(
+ task1_,
+ [](void* data) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+
+ auto& tcc = ThreadCallbackContext<
+ void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0, void*>::GetInst();
+
+ tcc.Add(
+ nullptr, setter,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+
+ return false;
+ },
+ this, &source);
+ tizen_core_add_idle_job(
+ task2_,
+ [](void* data) {
+ auto setter = [](void* handle, simple_cb cb, void* data) {
+ set_simple_callback(cb, data);
+ };
+ auto unsetter = [](void* handle) { unset_simple_callback(); };
+
+ auto& tcc = ThreadCallbackContext<
+ void*, std::function<void(void*, simple_cb, void*)>,
+ std::function<void(void*)>, simple_cb, 0, void*>::GetInst();
+
+ tcc.Add(
+ nullptr, setter, [](void* data) { hit_cnt++; }, data);
+ tcc.Remove(nullptr, unsetter);
+
+ return false;
+ },
+ this, &source);
+
+ RunWorker();
+ SendEvent();
+ Run();
+ EXPECT_EQ(hit_cnt, 1);
+}
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Macro_Set_P) {
+ tizen_core_source_h source = nullptr;
+ hit_cnt = 0;
+ tizen_core_add_idle_job(
+ task1_,
+ [](void* data) {
+ set_simple_callback_tcc(
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+ return false;
+ },
+ this, &source);
+
+ RunWorker();
+ SendEvent();
+ Run();
+ EXPECT_EQ(hit_cnt, 1);
+}
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Macro_Set_P2) {
+ tizen_core_source_h source = nullptr;
+ hit_cnt = 0;
+ tizen_core_add_idle_job(
+ task1_,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ set_handle_callback_tcc(
+ self->handle_cb1_,
+ [](handle_callback_h h, int state, void* user_data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(user_data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+ return false;
+ },
+ this, &source);
+
+ RunWorker();
+ SendEvent(handle_cb1_);
+ Run();
+ EXPECT_EQ(hit_cnt, 1);
+}
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Macro_Unset_P) {
+ tizen_core_source_h source = nullptr;
+ hit_cnt = 0;
+ tizen_core_add_idle_job(
+ task1_,
+ [](void* data) {
+ set_simple_callback_tcc(
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+ return false;
+ },
+ this, &source);
+ tizen_core_add_idle_job(
+ task2_,
+ [](void* data) {
+ set_simple_callback_tcc([](void* data) { hit_cnt++; }, data);
+ unset_simple_callback_tcc();
+ return false;
+ },
+ this, &source);
+
+ RunWorker();
+ SendEvent();
+ Run();
+ EXPECT_EQ(hit_cnt, 1);
+}
+
+TEST_F(TizenCoreUtilTest, ThreadCallbackContext_Macro_Unset_P2) {
+ tizen_core_source_h source = nullptr;
+ hit_cnt = 0;
+ tizen_core_add_idle_job(
+ task1_,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ set_handle_callback_tcc(
+ self->handle_cb1_,
+ [](handle_callback_h h, int state, void* user_data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(user_data);
+ EXPECT_NE(gettid(), self->tid_);
+ hit_cnt++;
+ self->Stop();
+ },
+ data);
+ return false;
+ },
+ this, &source);
+ tizen_core_add_idle_job(
+ task2_,
+ [](void* data) {
+ auto* self = static_cast<TizenCoreUtilTest*>(data);
+ set_handle_callback_tcc(
+ self->handle_cb1_,
+ [](handle_callback_h h, int state, void* user_data) { hit_cnt++; },
+ data);
+ unset_handle_callback_tcc(self->handle_cb1_);
+ return false;
+ },
+ this, &source);
+
+ RunWorker();
+ SendEvent(handle_cb1_);
+ Run();
+ EXPECT_EQ(hit_cnt, 1);
+}
+
+// LCOV_EXCL_STOP