Add SchedTimer 94/138194/3
authorMu-Woong Lee <muwoong.lee@samsung.com>
Tue, 11 Jul 2017 11:18:02 +0000 (20:18 +0900)
committerMu-Woong Lee <muwoong.lee@samsung.com>
Wed, 12 Jul 2017 07:22:42 +0000 (16:22 +0900)
SchedTimer summarizes the periodic intervals of all periodic jobs and sets a single alarm.
When the alarm expires (or the device wake-ups by any reasons),
SchedTimer notifies the corresponding periodic job runners.

Change-Id: Ib791c3a90e57d3600c04ad45967908749c69d8db
Signed-off-by: Mu-Woong Lee <muwoong.lee@samsung.com>
src/server/JobSchedulerService.cpp
src/server/SchedTimer.cpp [new file with mode: 0644]
src/server/SchedTimer.h [new file with mode: 0644]

index 62f0ddcb08f354b81c31e74db1dd51b81dab1c66..0f10b94a6200130fab703e4e30a2c07ee322e1fb 100644 (file)
@@ -20,6 +20,7 @@
 #include <JobSchedulerService.h>
 #include "JobManager.h"
 #include "MethodCallHandler.h"
+#include "SchedTimer.h"
 
 #define ROOT_UID       0
 
@@ -57,12 +58,14 @@ IMethodCallHandler* JobSchedulerService::createMethodCallHandler(IClient* client
 
 bool JobSchedulerService::prepare()
 {
+       SchedTimer::init();
        __jobManagers.emplace(ROOT_UID, new JobManager(ROOT_UID));
        return true;
 }
 
 void JobSchedulerService::cleanup()
 {
+       SchedTimer::release();
        for (auto& it : __jobManagers) {
                delete it.second;
        }
diff --git a/src/server/SchedTimer.cpp b/src/server/SchedTimer.cpp
new file mode 100644 (file)
index 0000000..04401c2
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * 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 <ctime>
+#include <algorithm>
+#include <limits>
+#include "SchedTimer.h"
+
+#define DIFF(X, Y)             ((X) > (Y) ? (X) - (Y) : (Y) - (X))
+#define DIFF_THRESHOLD 60
+
+using namespace ctx;
+
+SchedTimer* SchedTimer::__instance = NULL;
+
+SchedTimer::SchedTimer() :
+       __scheduledTime(0)
+{
+}
+
+SchedTimer::~SchedTimer()
+{
+}
+
+bool SchedTimer::init()
+{
+       if (!__instance)
+               __instance = new SchedTimer();
+
+       return true;
+}
+
+void SchedTimer::release()
+{
+       delete __instance;
+       __instance = NULL;
+}
+
+bool SchedTimer::set(ISchedTimerListener* listener, time_t scheduledTime, unsigned int margin)
+{
+       IF_FAIL_RETURN(__instance, false);
+       return __instance->__set(listener, scheduledTime, margin);
+}
+
+void SchedTimer::remove(ISchedTimerListener* listener)
+{
+       IF_FAIL_VOID(__instance);
+       __instance->__remove(listener);
+}
+
+void SchedTimer::reset()
+{
+       IF_FAIL_VOID(__instance);
+       __instance->__reset();
+}
+
+bool SchedTimer::onTimerExpired(unsigned int timerId, unsigned int intervalMs, void* userData)
+{
+       time_t limit = time(NULL);
+       ISchedTimerListener* listener = NULL;
+
+       while ((listener = __timeLine.pop(limit))) {
+               listener->onSchedTimerExpired();
+       }
+
+       return true;
+}
+
+bool SchedTimer::__set(ISchedTimerListener* listener, time_t scheduledTime, unsigned int margin)
+{
+       __timeLine.push(listener, scheduledTime, margin);
+       return true;
+}
+
+void SchedTimer::__remove(ISchedTimerListener* listener)
+{
+       __timeLine.remove(listener);
+}
+
+void SchedTimer::__reset()
+{
+       time_t next = __timeLine.getNextScheduledTime();
+       IF_FAIL_VOID(DIFF(next, __scheduledTime) >= DIFF_THRESHOLD);
+
+       __scheduledTime = next;
+       __timer.cancelAll();
+
+       IF_FAIL_VOID(next != 0);
+
+       unsigned int intervalMin = (next - time(NULL) + 59) / 60;
+       if (intervalMin < 1) {
+               _W("Invalid time: %d", static_cast<int>(next));
+               intervalMin = 1;
+       }
+
+       __timer.addAlarm(intervalMin, this, NULL);
+
+       //TODO: g_timeout to catch the device wakeups caused by other reasons.
+}
+
+SchedTimer::TimeLineEntry::TimeLineEntry(ISchedTimerListener* l, time_t t, unsigned int m) :
+       listener(l),
+       scheduledTime(t),
+       margin(m)
+{
+}
+
+SchedTimer::TimeLine::TimeLine()
+{
+}
+
+SchedTimer::TimeLine::~TimeLine()
+{
+       std::for_each(__timeLineEntries.begin(), __timeLineEntries.end(), [](TimeLineEntry*& e){ delete e; });
+}
+
+void SchedTimer::TimeLine::push(ISchedTimerListener* listener, time_t scheduledTime, unsigned int margin)
+{
+       push(new TimeLineEntry(listener, scheduledTime, margin));
+}
+
+void SchedTimer::TimeLine::push(TimeLineEntry* entry)
+{
+       auto it = __timeLineEntries.rbegin();
+
+       while (it != __timeLineEntries.rend()) {
+               if (__compensateTime(entry) > __compensateTime(*it))
+                       break;
+
+               if (__compensateTime(entry) == __compensateTime(*it) &&
+                               entry->scheduledTime > (*it)->scheduledTime)
+                       break;
+
+               ++it;
+       }
+
+       __timeLineEntries.insert(it.base(), entry);
+}
+
+ISchedTimerListener* SchedTimer::TimeLine::pop(time_t limit)
+{
+       TimeLineEntry* entry = popEntry(limit);
+
+       if (!entry)
+               return NULL;
+
+       ISchedTimerListener* listener = entry->listener;
+       delete entry;
+
+       return listener;
+}
+
+SchedTimer::TimeLineEntry* SchedTimer::TimeLine::popEntry(time_t limit)
+{
+       if (__timeLineEntries.empty())
+               return NULL;
+
+       TimeLineEntry* entry = __timeLineEntries.front();
+
+       if (__compensateTime(entry) < limit) {
+               __timeLineEntries.pop_front();
+               return entry;
+       }
+
+       return NULL;
+}
+
+void SchedTimer::TimeLine::remove(ISchedTimerListener* listener)
+{
+       for (auto it = __timeLineEntries.begin(); it != __timeLineEntries.end(); ++it) {
+               if ((*it)->listener == listener) {
+                       delete *it;
+                       __timeLineEntries.erase(it);
+                       break;
+               }
+       }
+}
+
+time_t SchedTimer::TimeLine::getNextScheduledTime()
+{
+       if (__timeLineEntries.empty())
+               return 0;
+
+       auto it = __timeLineEntries.begin();
+       time_t next = (*it)->scheduledTime;
+
+       while (++it != __timeLineEntries.end()) {
+               if (next <= __compensateTime(*it))
+                       break;
+
+               next = MIN(next, (*it)->scheduledTime);
+       }
+
+       return next;
+}
+
+time_t SchedTimer::TimeLine::__compensateTime(TimeLineEntry*& entry)
+{
+       return entry->scheduledTime - static_cast<time_t>(entry->margin);
+}
diff --git a/src/server/SchedTimer.h b/src/server/SchedTimer.h
new file mode 100644 (file)
index 0000000..3b03fd4
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * 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_SCHED_TIMER_H__
+#define __CONTEXT_JOB_SCHEDULER_SCHED_TIMER_H__
+
+#include <list>
+#include <Timer.h>
+#include <ITimerListener.h>
+
+namespace ctx {
+
+       class ISchedTimerListener {
+       public:
+               virtual ~ISchedTimerListener() {}
+               virtual void onSchedTimerExpired() = 0;
+       };
+
+
+       class SchedTimer : public ITimerListener {
+       public:
+               static bool init();
+               static void release();
+
+               static bool set(ISchedTimerListener* listener, time_t scheduledTime, unsigned int margin);
+               static void remove(ISchedTimerListener* listener);
+
+               // 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();
+
+               bool onTimerExpired(unsigned int timerId, unsigned int intervalMs, void* userData);
+
+       private:
+               SchedTimer();
+               ~SchedTimer();
+
+               bool __set(ISchedTimerListener* listener, time_t scheduledTime, unsigned int margin);
+               void __remove(ISchedTimerListener* listener);
+               void __reset();
+
+               struct TimeLineEntry {
+                       ISchedTimerListener* listener;
+                       time_t scheduledTime;
+                       unsigned int margin;
+                       TimeLineEntry(ISchedTimerListener* l, time_t t, unsigned int m);
+               };
+
+               class TimeLine {
+               public:
+                       TimeLine();
+                       ~TimeLine();
+
+                       void push(ISchedTimerListener* listener, time_t scheduledTime, unsigned int margin);
+                       void push(TimeLineEntry* entry);
+
+                       ISchedTimerListener* pop(time_t limit);
+                       TimeLineEntry* popEntry(time_t limit);
+
+                       void remove(ISchedTimerListener* listener);
+
+                       time_t getNextScheduledTime();
+
+               private:
+                       inline time_t __compensateTime(TimeLineEntry*& entry);
+
+                       std::list<TimeLineEntry*> __timeLineEntries;
+               };
+
+               TimeLine __timeLine;
+               Timer __timer;
+               time_t __scheduledTime;
+
+               static SchedTimer* __instance;
+       };
+
+}
+
+#endif /* __CONTEXT_JOB_SCHEDULER_SCHED_TIMER_H__ */