Skeleton of job lifecycle handling state machine 13/138513/5
authorMu-Woong Lee <muwoong.lee@samsung.com>
Wed, 12 Jul 2017 10:31:05 +0000 (19:31 +0900)
committerMu-Woong Lee <muwoong.lee@samsung.com>
Fri, 14 Jul 2017 08:04:27 +0000 (17:04 +0900)
Change-Id: Iae69eae781684ce3794048b4747c03f6c1d734db
Signed-off-by: Mu-Woong Lee <muwoong.lee@samsung.com>
src/server/JobManager.cpp
src/server/JobManager.h
src/server/JobRunner.cpp
src/server/JobRunner.h
src/server/JobState.cpp [new file with mode: 0644]
src/server/JobState.h [new file with mode: 0644]
src/server/SchedTimer.cpp
src/server/SchedTimer.h

index 35be0bf..9967d1f 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <ServerUtil.h>
 #include <JobAction.h>
+#include "SchedTimer.h"
 #include "JobRunner.h"
 #include "JobManager.h"
 
@@ -37,6 +38,11 @@ JobManager::~JobManager()
        __release();
 }
 
+uid_t JobManager::getUid() const
+{
+       return __uid;
+}
+
 bool JobManager::isSupported(JobContext::Type type, const std::string& uri)
 {
        //TODO: availability check
@@ -78,7 +84,7 @@ int JobManager::__addPeriodicJob(PeriodicJobInfo* jobInfo, IClient* owner)
 
        jobInfo->setId(__generateJobId());
 
-       unsigned int cnt = __addRunner(new JobRunner(owner->getName(), jobInfo));
+       unsigned int cnt = __addRunner(new PeriodicJobRunner(this, owner->getName(), jobInfo));
        _D("%u jobs have been registered", cnt);
 
        return jobInfo->getId();
@@ -90,7 +96,7 @@ int JobManager::__addOnDemandJob(OnDemandJobInfo* jobInfo, IClient* owner)
 
        jobInfo->setId(__generateJobId());
 
-       unsigned int cnt = __addRunner(new JobRunner(owner->getName(), jobInfo));
+       unsigned int cnt = __addRunner(new OnDemandJobRunner(this, owner->getName(), jobInfo));
        _D("%u jobs have been registered", cnt);
 
        return jobInfo->getId();
@@ -137,9 +143,6 @@ int JobManager::removeJob(int jobId, IClient* owner)
        JobRunner* runner = __getRunner(owner->getName(), jobId);
        IF_FAIL_RETURN_TAG(runner, E_PARAM, _W, "Not found");
 
-       if (runner->isStarted())
-               runner->stop();
-
        if (runner->isPersistent())
                __jobInfoDatabase.remove(jobId);
 
@@ -148,6 +151,14 @@ int JobManager::removeJob(int jobId, IClient* owner)
        return E_NONE;
 }
 
+void JobManager::removeRunner(JobRunner* runner)
+{
+       if (runner->isPersistent())
+               __jobInfoDatabase.remove(runner->getJobId());
+
+       __removeRunner(runner);
+}
+
 JobInfo* JobManager::getJobInfo(int jobId, IClient* owner)
 {
        JobRunner* jobRunner = __getRunner(owner->getName(), jobId);
@@ -289,11 +300,10 @@ unsigned int JobManager::__addRunner(JobRunner* runner)
        return __jobRunners.size();
 }
 
-bool JobManager::__removeRunner(JobRunner* runner)
+void JobManager::__removeRunner(JobRunner* runner)
 {
        __jobRunners.remove(runner);
        delete runner;
-       return true;
 }
 
 JobRunner* JobManager::__getRunner(const std::string& ownerId, int jobId)
@@ -314,28 +324,38 @@ void JobManager::__restore()
 
        _I("Restoring %u jobs", storedJobs.size());
 
+       SchedTimer::pause();
+
        for (auto& storedJob : storedJobs) {
                const std::string& owner = storedJob.first;
                JobInfo* jobInfo = storedJob.second;
 
                maxJobId = MAX(maxJobId, jobInfo->getId());
 
-               JobRunner* runner = new JobRunner(owner, jobInfo);
+               JobRunner* runner = NULL;
+               if (jobInfo->getType() == JobInfo::Type::PERIODIC)
+                       runner = new PeriodicJobRunner(this, owner, jobInfo);
+               else
+                       runner = new OnDemandJobRunner(this, owner, jobInfo);
+
                if (jobInfo->isStarted())
                        runner->start();
 
                __addRunner(runner);
        }
 
+       SchedTimer::resume();
+
        __lastJobId = maxJobId;
 }
 
 void JobManager::__release()
 {
+       SchedTimer::pause();
+
        for (auto& runner : __jobRunners) {
-               runner->stop();
                delete runner;
        }
 
-       __jobRunners.clear();
+       SchedTimer::resume();
 }
index 2962995..31815a7 100644 (file)
@@ -32,12 +32,15 @@ namespace ctx {
                JobManager(uid_t uid);
                ~JobManager();
 
+               uid_t getUid() const;
+
                bool isSupported(JobContext::Type type, const std::string& uri);
 
                int addJob(JobInfo* jobInfo, IClient* owner);
                int startJob(int jobId, IClient* owner);
                int stopJob(int jobId, IClient* owner);
                int removeJob(int jobId, IClient* owner);
+               void removeRunner(JobRunner* runner);
 
                JobInfo* getJobInfo(int jobId, IClient* owner);
                std::vector<JobInfo*> getAllJobInfo(IClient* owner);
@@ -59,7 +62,7 @@ namespace ctx {
                int __generateJobId();
 
                unsigned int __addRunner(JobRunner* runner);
-               bool __removeRunner(JobRunner* runner);
+               void __removeRunner(JobRunner* runner);
                JobRunner* __getRunner(const std::string& ownerId, int jobId);
                std::vector<JobRunner*> __getRunners(const std::string& ownerId);
 
index 643b390..617c214 100644 (file)
  * limitations under the License.
  */
 
+#include <stdexcept>
+#include "SchedTimer.h"
+#include "JobManager.h"
+#include "JobState.h"
 #include "JobRunner.h"
 
 using namespace ctx;
 
-JobRunner::JobRunner(const std::string& owner, JobInfo* jobInfo) :
+unsigned int PeriodicJobRunner::__periodicJobRunnerCnt = 0;
+
+JobRunner::JobRunner(JobManager* mgr, const std::string& owner, JobInfo* info) :
+       __jobManager(mgr),
+       __jobInfo(info),
+       __client(NULL),
        __owner(owner),
-       __jobInfo(jobInfo)
+       __state(NULL)
+{
+}
+
+JobRunner::~JobRunner()
+{
+       delete __state;
+       delete __jobInfo;
+}
+
+uid_t JobRunner::getUid() const
 {
+       return __jobManager->getUid();
 }
 
-const std::string& JobRunner::getOwner()
+const std::string& JobRunner::getOwner() const
 {
        return __owner;
 }
 
-int JobRunner::getJobId()
+int JobRunner::getJobId() const
 {
        return __jobInfo->getId();
 }
 
-bool JobRunner::isStarted()
+bool JobRunner::isStarted() const
 {
        return __jobInfo->isStarted();
 }
 
-bool JobRunner::isPersistent()
+bool JobRunner::isPersistent() const
 {
        return __jobInfo->isPersistent();
 }
@@ -49,12 +69,109 @@ JobInfo* JobRunner::getJobInfo()
        return __jobInfo;
 }
 
-void JobRunner::start()
+void JobRunner::setState(JobState* state)
+{
+       delete __state;
+       __state = state;
+
+       IF_FAIL_VOID(__state);
+
+       try {
+               if (__state->execute())
+                       __state->setClient(__client);
+       } catch (const std::runtime_error& e) {
+               _E("State transtion failed: %s", e.what());
+               _I("Terminating Job-%d of '%s'", getJobId(), getOwner().c_str());
+               terminate();
+       }
+}
+
+void JobRunner::setClient(IClient* client)
+{
+       __client = client;
+
+       if (__state)
+               __state->setClient(client);
+}
+
+void JobRunner::jobFinished()
+{
+       if (__state)
+               __state->jobFinished();
+}
+
+
+PeriodicJobRunner::PeriodicJobRunner(JobManager* mgr, const std::string& owner, JobInfo* info) :
+       JobRunner(mgr, owner, info)
+{
+       ++__periodicJobRunnerCnt;
+}
+
+PeriodicJobRunner::~PeriodicJobRunner()
+{
+       --__periodicJobRunnerCnt;
+}
+
+void PeriodicJobRunner::start()
+{
+       __jobInfo->setStarted(true);
+       setState(new TimerStandbyState(this));
+
+       if (__periodicJobRunnerCnt == TimerStandbyState::getCount())
+               SchedTimer::reset();
+}
+
+void PeriodicJobRunner::stop()
+{
+       __jobInfo->setStarted(false);
+       setState(NULL);
+
+       if (__periodicJobRunnerCnt == TimerStandbyState::getCount())
+               SchedTimer::reset();
+}
+
+void PeriodicJobRunner::restart()
+{
+       setState(new TimerStandbyState(this));
+
+       if (__periodicJobRunnerCnt == TimerStandbyState::getCount())
+               SchedTimer::reset();
+}
+
+void PeriodicJobRunner::terminate()
+{
+       setState(NULL);
+       __jobManager->removeRunner(this);
+
+       if (__periodicJobRunnerCnt == TimerStandbyState::getCount())
+               SchedTimer::reset();
+}
+
+
+OnDemandJobRunner::OnDemandJobRunner(JobManager* mgr, const std::string& owner, JobInfo* info) :
+       JobRunner(mgr, owner, info)
+{
+}
+
+void OnDemandJobRunner::start()
 {
        __jobInfo->setStarted(true);
+       setState(new TriggerStandbyState(this));
 }
 
-void JobRunner::stop()
+void OnDemandJobRunner::stop()
 {
        __jobInfo->setStarted(false);
+       setState(NULL);
+}
+
+void OnDemandJobRunner::restart()
+{
+       setState(new TriggerStandbyState(this));
+}
+
+void OnDemandJobRunner::terminate()
+{
+       setState(NULL);
+       __jobManager->removeRunner(this);
 }
index b190d5a..dfedcae 100644 (file)
 #ifndef __CONTEXT_JOB_SCHEDULER_JOB_RUNNER_H__
 #define __CONTEXT_JOB_SCHEDULER_JOB_RUNNER_H__
 
+#include <IClient.h>
 #include <JobInfo.h>
 
 namespace ctx {
 
+       class JobManager;
+       class JobState;
+
        class JobRunner {
        public:
-               JobRunner(const std::string& owner, JobInfo* jobInfo);
+               virtual ~JobRunner();
 
-               const std::string& getOwner();
-               int getJobId();
-               bool isStarted();
-               bool isPersistent();
+               uid_t getUid() const;
+               const std::string& getOwner() const;
+               int getJobId() const;
+               bool isStarted() const;
+               bool isPersistent() const;
 
                JobInfo* getJobInfo();
 
+               void setState(JobState* state);
+               void setClient(IClient* client);
+               void jobFinished();
+
+               virtual void start() = 0;
+               virtual void stop() = 0;
+               virtual void restart() = 0;
+               virtual void terminate() = 0;
+
+       protected:
+               JobRunner(JobManager* mgr, const std::string& owner, JobInfo* info);
+
+               JobManager* __jobManager;
+               JobInfo* __jobInfo;
+               IClient* __client;
+
+       private:
+               std::string __owner;
+               JobState* __state;
+       };
+
+
+       class PeriodicJobRunner : public JobRunner {
+       public:
+               PeriodicJobRunner(JobManager* mgr, const std::string& owner, JobInfo* info);
+               ~PeriodicJobRunner();
+
                void start();
                void stop();
+               void restart();
+               void terminate();
 
        private:
-               std::string __owner;
-               JobInfo* __jobInfo;
+               static unsigned int __periodicJobRunnerCnt;
+       };
+
+
+       class OnDemandJobRunner : public JobRunner {
+       public:
+               OnDemandJobRunner(JobManager* mgr, const std::string& owner, JobInfo* info);
+
+               void start();
+               void stop();
+               void restart();
+               void terminate();
        };
 
 }
diff --git a/src/server/JobState.cpp b/src/server/JobState.cpp
new file mode 100644 (file)
index 0000000..7a08ae3
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * 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 "JobRunner.h"
+#include "JobState.h"
+
+#define ENTER  _I("ENTER: Job-%d of '%s'", getJobId(), getOwner().c_str())
+#define EXIT   _I("EXIT : Job-%d of '%s'", getJobId(), getOwner().c_str())
+
+using namespace ctx;
+
+unsigned int TimerStandbyState::__timerStandbyStateCnt = 0;
+
+JobState::JobState(JobRunner* runner) :
+       __jobRunner(runner)
+{
+}
+
+JobState::JobState(JobState* prevState) :
+       __jobRunner(prevState->__jobRunner)
+{
+}
+
+JobState::~JobState()
+{
+}
+
+void JobState::setClient(IClient* client)
+{
+}
+
+void JobState::jobFinished()
+{
+}
+
+uid_t JobState::getUid() const
+{
+       return __jobRunner->getUid();
+}
+
+const std::string& JobState::getOwner() const
+{
+       return __jobRunner->getOwner();
+}
+
+int JobState::getJobId() const
+{
+       return __jobRunner->getJobId();
+}
+
+JobInfo* JobState::getJobInfo()
+{
+       return __jobRunner->getJobInfo();
+}
+
+JobRunner* JobState::getJobRunner()
+{
+       return __jobRunner;
+}
+
+void JobState::transit(JobState* nextState)
+{
+       __jobRunner->setState(nextState);
+}
+
+
+TimerStandbyState::TimerStandbyState(JobRunner* runner) :
+       JobState(runner)
+{
+       ENTER;
+       ++__timerStandbyStateCnt;
+}
+
+TimerStandbyState::~TimerStandbyState()
+{
+       EXIT;
+       --__timerStandbyStateCnt;
+}
+
+bool TimerStandbyState::execute()
+{
+       //TODO: Set a timer.
+       return true;
+}
+
+void TimerStandbyState::onSchedTimerExpired()
+{
+       //TODO: Transit to ReqVerificationState.
+}
+
+unsigned int TimerStandbyState::getCount()
+{
+       return __timerStandbyStateCnt;
+}
+
+
+TriggerStandbyState::TriggerStandbyState(JobRunner* runner) :
+       JobState(runner)
+{
+       ENTER;
+}
+
+TriggerStandbyState::~TriggerStandbyState()
+{
+       EXIT;
+}
+
+bool TriggerStandbyState::execute()
+{
+       //TODO: Subscribe the trigger contexts.
+       // If a context expires, verify its attributes, and transit to ReqVerificationState.
+       return true;
+}
+
+
+ReqVerificationState::ReqVerificationState(JobState* prevState) :
+       JobState(prevState)
+{
+       ENTER;
+}
+
+ReqVerificationState::~ReqVerificationState()
+{
+       EXIT;
+}
+
+bool ReqVerificationState::execute()
+{
+       //TODO: Read the requirement contexts.
+       // If all satisfied, goto ActionState.
+       // If the RequirementTimeout == 0, goto restart the machine.
+       // Set the timeout.
+       // If the timeout expires, read the requirements again.
+       // If all mandatory satisfied, goto ActionState, restart otherwise.
+       return true;
+}
+
+
+ActionState::ActionState(JobState* prevState) :
+       JobState(prevState)
+{
+       ENTER;
+       //TODO: Check whether the timeout expired.
+}
+
+ActionState::~ActionState()
+{
+       EXIT;
+}
+
+bool ActionState::execute()
+{
+       //TODO: Set a timeout for waiting the client finishes the job.
+       // If the timeout expires, let the client know that the device is about to suspend.
+       // And goto ClosingState.
+       return true;
+}
+
+void ActionState::setClient(IClient* client)
+{
+       //TODO: Notify the client which job has been expired (once).
+       // Notify the client if the timeout already expired (once).
+}
+
+void ActionState::jobFinished()
+{
+       //TODO: cancel the timeout and goto ClosingState.
+}
+
+
+ClosingState::ClosingState(JobRunner* runner) :
+       JobState(runner)
+{
+       ENTER;
+}
+
+ClosingState::ClosingState(JobState* prevState) :
+       JobState(prevState)
+{
+       ENTER;
+}
+
+ClosingState::~ClosingState()
+{
+       EXIT;
+}
+
+bool ClosingState::execute()
+{
+       //TODO: restart or terminate the job.
+       return false;
+}
diff --git a/src/server/JobState.h b/src/server/JobState.h
new file mode 100644 (file)
index 0000000..a03891a
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * 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 __CONTEXT_JOB_SCHEDULER_JOB_STATE_H__
+#define __CONTEXT_JOB_SCHEDULER_JOB_STATE_H__
+
+#include <string>
+#include <IClient.h>
+#include <JobInfo.h>
+#include "SchedTimer.h"
+
+namespace ctx {
+
+       class JobRunner;
+
+
+       class JobState {
+       public:
+               virtual ~JobState();
+
+               virtual bool execute() = 0;
+
+               virtual void setClient(IClient* client);
+               virtual void jobFinished();
+
+       protected:
+               JobState(JobRunner* runner);
+               JobState(JobState* prevState);
+
+               uid_t getUid() const;
+               const std::string& getOwner() const;
+               int getJobId() const;
+               JobInfo* getJobInfo();
+               JobRunner* getJobRunner();
+
+               void transit(JobState* nextState);
+
+       private:
+               JobRunner* __jobRunner;
+       };
+
+
+       class TimerStandbyState : public JobState, public ISchedTimerListener {
+       public:
+               TimerStandbyState(JobRunner* runner);
+               ~TimerStandbyState();
+
+               bool execute();
+
+               void onSchedTimerExpired();
+
+               static unsigned int getCount();
+
+       private:
+               static unsigned int __timerStandbyStateCnt;
+       };
+
+
+       class TriggerStandbyState : public JobState {
+       public:
+               TriggerStandbyState(JobRunner* runner);
+               ~TriggerStandbyState();
+
+               bool execute();
+       };
+
+
+       class ReqVerificationState : public JobState {
+       public:
+               ReqVerificationState(JobState* prevState);
+               ~ReqVerificationState();
+
+               bool execute();
+       };
+
+
+       class ActionState : public JobState {
+       public:
+               ActionState(JobState* prevState);
+               ~ActionState();
+
+               bool execute();
+
+               void setClient(IClient* client);
+               void jobFinished();
+       };
+
+
+       class ClosingState : public JobState {
+       public:
+               ClosingState(JobRunner* runner);
+               ClosingState(JobState* prevState);
+               ~ClosingState();
+
+               bool execute();
+       };
+
+}
+
+#endif /* __CONTEXT_JOB_SCHEDULER_JOB_STATE_H__ */
index 04401c2..96a1107 100644 (file)
@@ -27,7 +27,8 @@ using namespace ctx;
 SchedTimer* SchedTimer::__instance = NULL;
 
 SchedTimer::SchedTimer() :
-       __scheduledTime(0)
+       __scheduledTime(0),
+       __paused(false)
 {
 }
 
@@ -67,6 +68,18 @@ void SchedTimer::reset()
        __instance->__reset();
 }
 
+void SchedTimer::pause()
+{
+       IF_FAIL_VOID(__instance);
+       __instance->__pause();
+}
+
+void SchedTimer::resume()
+{
+       IF_FAIL_VOID(__instance);
+       __instance->__resume();
+}
+
 bool SchedTimer::onTimerExpired(unsigned int timerId, unsigned int intervalMs, void* userData)
 {
        time_t limit = time(NULL);
@@ -92,6 +105,8 @@ void SchedTimer::__remove(ISchedTimerListener* listener)
 
 void SchedTimer::__reset()
 {
+       IF_FAIL_VOID(!__paused);
+
        time_t next = __timeLine.getNextScheduledTime();
        IF_FAIL_VOID(DIFF(next, __scheduledTime) >= DIFF_THRESHOLD);
 
@@ -111,6 +126,19 @@ void SchedTimer::__reset()
        //TODO: g_timeout to catch the device wakeups caused by other reasons.
 }
 
+void SchedTimer::__pause()
+{
+       __scheduledTime = 0;
+       __timer.cancelAll();
+       __paused = true;
+}
+
+void SchedTimer::__resume()
+{
+       __paused = false;
+       __reset();
+}
+
 SchedTimer::TimeLineEntry::TimeLineEntry(ISchedTimerListener* l, time_t t, unsigned int m) :
        listener(l),
        scheduledTime(t),
index 3b03fd4..6f2b82e 100644 (file)
@@ -41,6 +41,8 @@ namespace ctx {
                // Reset the timer when the all periodic jobs enter to the interval waiting state,
                // or a job has been removed and all others are in the waiting state.
                static void reset();
+               static void pause();
+               static void resume();
 
                bool onTimerExpired(unsigned int timerId, unsigned int intervalMs, void* userData);
 
@@ -51,6 +53,8 @@ namespace ctx {
                bool __set(ISchedTimerListener* listener, time_t scheduledTime, unsigned int margin);
                void __remove(ISchedTimerListener* listener);
                void __reset();
+               void __pause();
+               void __resume();
 
                struct TimeLineEntry {
                        ISchedTimerListener* listener;
@@ -83,6 +87,7 @@ namespace ctx {
                TimeLine __timeLine;
                Timer __timer;
                time_t __scheduledTime;
+               bool __paused;
 
                static SchedTimer* __instance;
        };