+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<tizenModes xmlns="http://www.tizen.org" version="6.0">
- <mode name="applyNormalAsync1" type="normal" >
- <action ID="test_001" rule="test.sleep" type="async" >5</action>
- <action ID="test_002" rule="test.sleep" type="async" >5</action>
- <action ID="test_003" rule="test.sleepErrorReturn" type="async" >5</action>
- <action ID="test_004" rule="test.sleep" type="async" >5</action>
- <action ID="test_005" rule="test.sleep" type="async" >5</action>
- </mode>
-</tizenModes>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<tizenModes xmlns="http://www.tizen.org" version="6.0">
- <mode name="applyNormalSync1" type="normal">
- <action ID="test_001" rule="test.sleep" >5</action>
- <action ID="test_002" rule="test.sleep" >5</action>
- <action ID="test_003" rule="test.sleepErrorReturn" >5</action>
- <action ID="test_004" rule="test.sleep" type="sync" >5</action>
- <action ID="test_005" rule="test.sleep" type="sync" >5</action>
- </mode>
-</tizenModes>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<tizenModes xmlns="http://www.tizen.org" version="6.0">
- <mode name="applyOneshotAsync1" type="oneshot" >
- <action ID="test_001" rule="test.sleep" type="async" >5</action>
- <action ID="test_002" rule="test.sleep" type="async" >5</action>
- <action ID="test_003" rule="test.sleepErrorReturn" type="async" >5</action>
- <action ID="test_004" rule="test.sleep" type="async" >5</action>
- <action ID="test_005" rule="test.sleep" type="async" >5</action>
- </mode>
-</tizenModes>
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<tizenModes xmlns="http://www.tizen.org" version="6.0">
- <mode name="applyOneshotSync1" type="oneshot">
- <action ID="test_001" rule="test.sleep" >5</action>
- <action ID="test_002" rule="test.sleep" type="sync" >5</action>
- <action ID="test_003" rule="test.sleepErrorReturn" >5</action>
- <action ID="test_004" rule="test.sleep" >5</action>
- <action ID="test_005" rule="test.sleep" type="sync" >5</action>
- </mode>
-</tizenModes>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<tizenModes xmlns="http://www.tizen.org" version="6.0">
+ <mode name="asyncEx1" type="normal">
+ <action ID="test_001" rule="test.sleep" type="async">5</action>
+ <action ID="test_002" rule="test.sleep" type="async">5</action>
+ <action ID="test_003" rule="test.sleepErrorReturn" type="async">5</action>
+ <action ID="test_004" rule="test.sleep" type="async">5</action>
+ <action ID="test_005" rule="test.sleep" type="async">5</action>
+ </mode>
+</tizenModes>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<tizenModes xmlns="http://www.tizen.org" version="6.0">
+ <mode name="asyncEx2" type="oneshot">
+ <action ID="test_001" rule="test.sleep" type="async">5</action>
+ <action ID="test_002" rule="test.sleep" type="async">5</action>
+ <action ID="test_003" rule="test.sleepErrorReturn" type="async">5</action>
+ <action ID="test_004" rule="test.sleep" type="async">5</action>
+ <action ID="test_005" rule="test.sleep" type="async">5</action>
+ </mode>
+</tizenModes>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<tizenModes xmlns="http://www.tizen.org" version="6.0">
+ <mode name="asyncFail1" type="normal">
+ <action rule="test.sleep" type="async">5</action>
+ <action rule="test.sleep" type="async">5</action>
+ <action rule="test.sleep" type="async">5</action>
+ <action rule="test.sleep" type="async">5</action>
+ <action rule="test.sleepErrorReturn" stopOnErr="true" type="sync">1</action>
+ </mode>
+</tizenModes>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<tizenModes xmlns="http://www.tizen.org" version="6.0">
+ <mode name="asyncFail2" type="oneshot">
+ <action rule="test.sleep" type="async">5</action>
+ <action rule="test.sleep" type="async">5</action>
+ <action rule="test.sleep" type="async">5</action>
+ <action rule="test.sleep" type="async">5</action>
+ <action rule="test.sleepErrorReturn" stopOnErr="true" type="sync">5</action>
+ </mode>
+</tizenModes>
install -d -m 755 %{buildroot}%{modes_rw_dir}/custom-mode
install -d -m 755 %{buildroot}%{modes_rw_dir}/undo-info
install -m 0644 example/mode/*ex*_mode.xml %{buildroot}%{modes_ro_dir}/mode/
-install -m 0644 example/mode/*apply*_mode.xml %{buildroot}%{modes_ro_dir}/mode/
+install -m 0644 example/mode/*sync*_mode.xml %{buildroot}%{modes_ro_dir}/mode/
install -m 0644 example/rule/*ex*_rule.xml %{buildroot}%{modes_ro_dir}/rule/
install -m 0644 example/mode/*conflict*_mode.xml %{buildroot}%{modes_test_dir}/
install -m 0644 example/mode/*invalid*_mode.xml %{buildroot}%{modes_test_dir}/
int TestPlugin::set(const std::string &key, int val, PluginAction **piAction)
{
- DBG("TestPlugin::set int ( %s, %d )", key.c_str(), val);
+ DBG("set(%s, %d) Begin", key.c_str(), val);
- if (piAction) {
- TestPluginAction *testAction = new TestPluginAction();
- *piAction = testAction;
- }
+ TestPluginAction *testAction = new TestPluginAction();
- if (key.compare("sleep") == 0) {
- DBG("Sleep %d seconds", val);
+ if ("sleep" == key) {
std::this_thread::sleep_for(std::chrono::seconds(val));
- DBG("TestPlugin::set int ( %s, %d ) Wakeup", key.c_str(), val);
+ DBG("set(%s, %d) End", key.c_str(), val);
} else if (key.compare("sleepErrorReturn") == 0) {
- DBG("Sleep %d seconds", val);
std::this_thread::sleep_for(std::chrono::seconds(val));
- DBG("TestPlugin::set int ( %s, %d ) Wakeup", key.c_str(), val);
+ delete testAction;
+ DBG("set(%s, %d) End", key.c_str(), val);
return MODES_ERROR_SYSTEM;
}
+
+ if (piAction)
+ *piAction = testAction;
+ else
+ delete testAction;
+
return MODES_ERROR_NONE;
}
MODES_NAMESPACE_USE;
Action::Action()
- : isChanged(false), plugin(nullptr), piAction(nullptr), stopOnErr(false), type(SYNC), restriction(REQ_NONE)
+ : isChanged(false), type(SYNC), plugin(nullptr), piAction(nullptr), stopOnErr(false), restriction(REQ_NONE)
{
}
Action::Action(const std::string &name)
- : ruleName(name), isChanged(false), plugin(nullptr), piAction(nullptr), stopOnErr(false), type(SYNC), restriction(REQ_NONE)
+ : ruleName(name), isChanged(false), type(SYNC), plugin(nullptr), piAction(nullptr), stopOnErr(false), restriction(REQ_NONE)
{
}
static void valueChangedCallback(void *userData);
std::string ruleName;
bool isChanged;
+ ActionType type;
Plugin *plugin;
PluginAction *piAction;
private:
std::string id;
bool stopOnErr;
- ActionType type;
ActionRestrict restriction;
};
* limitations under the License.
*/
#include "Mode.h"
+
+#include <thread>
#include "modes_constants.h"
#include "ModesEx.h"
{
std::list<std::shared_ptr<Action>>::iterator it;
for (it = actionList.begin(); it != actionList.end(); it++) {
- int ret = (*it)->apply();
- if (MODES_ERROR_NONE != ret && (*it)->getStopOnErr()) {
- ERR("Action(%s) apply Fail(%d)", (*it)->getRuleName().c_str(), ret);
- return ret;
+ if (Action::ActionType::ASYNC == (*it)->getType()) {
+ std::thread(&Action::apply, *it).detach();
+ } else {
+ int ret = (*it)->apply();
+ if (MODES_ERROR_NONE != ret && (*it)->getStopOnErr()) {
+ ERR("Action(%s) apply Fail(%d)", (*it)->getRuleName().c_str(), ret);
+ return ret;
+ }
}
}
return MODES_ERROR_NONE;
{
std::list<std::shared_ptr<Action>>::iterator it;
for (it = actionList.begin(); it != actionList.end(); it++) {
- int ret = (*it)->applyOneShot();
- if (MODES_ERROR_NONE != ret && (*it)->getStopOnErr()) {
- ERR("Action(%s) applyOneShot Fail(%d)", (*it)->getRuleName().c_str(), ret);
- return ret;
+ if (Action::ActionType::ASYNC == (*it)->getType()) {
+ std::thread(&Action::applyOneShot, *it).detach();
+ } else {
+ int ret = (*it)->applyOneShot();
+ if (MODES_ERROR_NONE != ret && (*it)->getStopOnErr()) {
+ ERR("Action(%s) applyOneShot Fail(%d)", (*it)->getRuleName().c_str(), ret);
+ return ret;
+ }
}
}
return MODES_ERROR_NONE;
void addUndo(Action *action);
std::list<std::shared_ptr<Action>> getUndoList() const;
- void addUndoInfo(const std::string &rule, const std::string &info);
-
int apply();
int applyOneShot();
void undo();
class TAction : public Action {
public:
TAction(const std::string &name)
- : Action(name)
+ : Action(name), undoing(false)
{
}
+ ~TAction()
+ {
+ if (undoing)
+ undo();
+
+ if (piAction)
+ piAction->unSetChangedCallback(valueChangedCallback, this);
+ }
+
void setValueAliases(const std::map<std::string, T> &list)
{
valueAliases.clear();
int apply() override
{
- return applyImpl(false, getType() == Action::ActionType::ASYNC);
+ RETVM_IF(NULL == plugin, MODES_ERROR_NO_DATA, "Action(%s) : No plugin", ruleName.c_str());
+
+ int pos = ruleName.find_first_of(".");
+ PluginAction *tmpAction = nullptr;
+ int ret = plugin->set(ruleName.substr(pos + 1), value, &tmpAction);
+ if (MODES_ERROR_NONE != ret) {
+ ERR("plugin(%s) set() Fail(%d)", plugin->getName().c_str(), ret);
+ return ret;
+ }
+ if (tmpAction)
+ tmpAction->setChangedCallback(valueChangedCallback, this);
+
+ piAction = tmpAction;
+ return MODES_ERROR_NONE;
}
int applyOneShot() override
{
- return applyImpl(true, getType() == Action::ActionType::ASYNC);
+ RETVM_IF(NULL == plugin, MODES_ERROR_NO_DATA, "Action(%s) : No plugin", ruleName.c_str());
+
+ int pos = ruleName.find_first_of(".");
+ PluginAction *tmpAction = nullptr;
+ int ret = plugin->set(ruleName.substr(pos + 1), value, &tmpAction);
+ if (MODES_ERROR_NONE != ret) {
+ ERR("plugin(%s) set() Fail(%d)", plugin->getName().c_str(), ret);
+ return ret;
+ }
+
+ piAction = tmpAction;
+ return MODES_ERROR_NONE;
}
void undo() override
{
RETM_IF(NULL == plugin, "Action(%s) : No plugin", ruleName.c_str());
+
+ //It control multithread condition
+ if (Action::ActionType::ASYNC == type)
+ undoing = true;
+
RETM_IF(NULL == piAction, "Plugin(%s) : No piAction(%s)", plugin->getName().c_str(), ruleName.c_str());
if (isChanged) {
bool operator()(const std::string &s1, const std::string &s2) const
{
return std::lexicographical_compare(s1.begin(), s1.end(),
- s2.begin(), s2.end(), Compare());
+ s2.begin(), s2.end(), Compare());
}
};
- int applyImpl(bool isOneShot, bool asyncFlag)
- {
- RETVM_IF(NULL == plugin, MODES_ERROR_NO_DATA, "Action(%s) : No plugin", ruleName.c_str());
-
- int pos = ruleName.find_first_of(".");
- if (asyncFlag) {
- std::thread th1([](TAction * action, Plugin * plugin, std::string key, T value, bool isOneShot) {
- PluginAction *tmpAction = nullptr;
- int ret = plugin->set(key, value, &tmpAction);
- if (MODES_ERROR_NONE != ret) {
- ERR("plugin(%s) set() Fail(%d)", plugin->getName().c_str(), ret);
- return;
- }
-
- // mode class and action class are destroyed at applyOneShot.
- // so need to skip register callback function.
- if (!isOneShot) {
- if (tmpAction != nullptr)
- tmpAction->setChangedCallback(action->valueChangedCallback, action);
-
- action->piAction = tmpAction;
- }
- return;
- }, this, plugin, ruleName.substr(pos + 1), value, isOneShot);
- th1.detach();
- } else {
- PluginAction *tmpAction = nullptr;
-
- int ret = plugin->set(ruleName.substr(pos + 1), value, &tmpAction);
- if (MODES_ERROR_NONE != ret) {
- ERR("plugin(%s) set() Fail(%d)", plugin->getName().c_str(), ret);
- return ret;
- }
-
- if (!isOneShot && tmpAction != nullptr)
- tmpAction->setChangedCallback(valueChangedCallback, this);
-
- piAction = tmpAction;
- }
- return MODES_ERROR_NONE;
- }
-
std::map<std::string, T, CaseIndependentLess> valueAliases;
+ bool undoing;
T value{};
};
MODES_NAMESPACE_END
TARGET_LINK_LIBRARIES(${GTEST_CLIENT} ${CLIENT} ${gtest_pkgs_LIBRARIES})
INSTALL(TARGETS ${GTEST_CLIENT} DESTINATION ${TEST_INSTALL_DIR})
#=======================================================================================#
+SET(GTEST_CLIENT "modes-gtest-async")
+SET(GTEST_CLIENT_SRCS modes_test_async.cpp)
+ADD_EXECUTABLE(${GTEST_CLIENT} ${SRC} ${GTEST_CLIENT_SRCS})
+TARGET_LINK_LIBRARIES(${GTEST_CLIENT} ${CLIENT} ${gtest_pkgs_LIBRARIES})
+INSTALL(TARGETS ${GTEST_CLIENT} DESTINATION ${TEST_INSTALL_DIR})
+#=======================================================================================#
SET(TEST_APPLY_MODE "modes-mode-test")
SET(TEST_APPLY_MODE_SRC modes_mode_test.c)
ADD_EXECUTABLE(${TEST_APPLY_MODE} ${TEST_APPLY_MODE_SRC})
cp $DATA_DIR/schema/*.xsd ./
./modes-gtest-client
./modes-gtest-noti
+ ./modes-gtest-async
fi
./modes-gtest-rulemgr
--- /dev/null
+/*
+ * Copyright (c) 2019 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 <gtest/gtest.h>
+#include <modes.h>
+
+class AsyncTest : public ::testing::Test {
+protected:
+ void SetUp() override
+ {
+ loop = g_main_loop_new(NULL, FALSE);
+ }
+
+ void TearDown() override
+ {
+ g_main_loop_unref(loop);
+ loop = NULL;
+ }
+
+ static gboolean ModeIdler(gpointer data)
+ {
+ int ret;
+ ret = modes_apply_mode((char*)data);
+ EXPECT_EQ(MODES_ERROR_NONE, ret);
+ sleep(1);
+ ret = modes_undo_mode((char*)data);
+ EXPECT_EQ(MODES_ERROR_NONE, ret);
+
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
+ }
+
+ static gboolean failIdler(gpointer data)
+ {
+ int ret;
+ ret = modes_apply_mode((char*)data);
+ EXPECT_EQ(MODES_ERROR_SYSTEM, ret);
+ sleep(1);
+ ret = modes_undo_mode((char*)data);
+ EXPECT_EQ(MODES_ERROR_NO_DATA, ret);
+
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
+ }
+
+ static GMainLoop *loop;
+};
+
+GMainLoop *AsyncTest::loop = NULL;
+
+TEST_F(AsyncTest, normalAsync)
+{
+ const char *modeName = "asyncEx1";
+ modes_undo_mode(modeName);
+ g_idle_add(ModeIdler, (gpointer)modeName);
+ g_main_loop_run(loop);
+}
+
+TEST_F(AsyncTest, oneshotAsync)
+{
+ const char *modeName = "asyncEx2";
+ int ret = modes_apply_mode(modeName);
+ EXPECT_EQ(MODES_ERROR_NONE, ret);
+}
+
+TEST_F(AsyncTest, normalAsyncFail)
+{
+ const char *modeName = "asyncFail1";
+ modes_undo_mode(modeName);
+ g_idle_add(failIdler, (gpointer)modeName);
+ g_main_loop_run(loop);
+}
+
+TEST_F(AsyncTest, oneshotAsyncFail)
+{
+ const char *modeName = "asyncFail2";
+ g_idle_add(failIdler, (gpointer)modeName);
+ g_main_loop_run(loop);
+}
int ret = modes_undo_mode((const char*)data);
EXPECT_EQ(MODES_ERROR_NONE, ret);
- g_main_loop_quit(loop);
return G_SOURCE_REMOVE;
}
std::cout << "state : " << state << std::endl;
EXPECT_EQ(expectedState, state);
- g_main_loop_quit(loop);
+ if (0 == expectedState)
+ g_main_loop_quit(loop);
return MODES_ERROR_NONE;
}