--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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__ */