summaryrefslogtreecommitdiff
path: root/src/net/timer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/timer.cpp')
-rw-r--r--src/net/timer.cpp217
1 files changed, 217 insertions, 0 deletions
diff --git a/src/net/timer.cpp b/src/net/timer.cpp
new file mode 100644
index 0000000..a85b8be
--- /dev/null
+++ b/src/net/timer.cpp
@@ -0,0 +1,217 @@
+#include "timer.hpp"
+// timer.cpp - Future event scheduler.
+//
+// Copyright © ????-2004 Athena Dev Teams
+// Copyright © 2004-2011 The Mana World Development Team
+// Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
+//
+// This file is part of The Mana World (Athena server)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <cassert>
+
+#include <algorithm>
+#include <queue>
+
+#include "../strings/zstring.hpp"
+
+#include "../poison.hpp"
+
+struct TimerData
+{
+ /// This will be reset on call, to avoid problems.
+ Timer *owner;
+
+ /// When it will be triggered
+ tick_t tick;
+ /// What will be done
+ timer_func func;
+ /// Repeat rate - 0 for oneshot
+ interval_t interval;
+
+ TimerData(Timer *o, tick_t t, timer_func f, interval_t i)
+ : owner(o)
+ , tick(t)
+ , func(std::move(f))
+ , interval(i)
+ {}
+};
+
+struct TimerCompare
+{
+ /// implement "less than"
+ bool operator() (dumb_ptr<TimerData> l, dumb_ptr<TimerData> r)
+ {
+ // C++ provides a max-heap, but we want
+ // the smallest tick to be the head (a min-heap).
+ return l->tick > r->tick;
+ }
+};
+
+static
+std::priority_queue<dumb_ptr<TimerData>, std::vector<dumb_ptr<TimerData>>, TimerCompare> timer_heap;
+
+
+tick_t gettick_cache;
+
+tick_t milli_clock::now(void) noexcept
+{
+ struct timeval tval;
+ // BUG: This will cause strange behavior if the system clock is changed!
+ // it should be reimplemented in terms of clock_gettime(CLOCK_MONOTONIC, )
+ gettimeofday(&tval, NULL);
+ return gettick_cache = tick_t(std::chrono::seconds(tval.tv_sec)
+ + std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::microseconds(tval.tv_usec)));
+}
+
+static
+void do_nothing(TimerData *, tick_t)
+{
+}
+
+void Timer::cancel()
+{
+ if (!td)
+ return;
+
+ assert (this == td->owner);
+ td->owner = nullptr;
+ td->func = do_nothing;
+ td->interval = interval_t::zero();
+ td = nullptr;
+}
+
+void Timer::detach()
+{
+ assert (this == td->owner);
+ td->owner = nullptr;
+ td = nullptr;
+}
+
+static
+void push_timer_heap(dumb_ptr<TimerData> td)
+{
+ timer_heap.push(td);
+}
+
+static
+dumb_ptr<TimerData> top_timer_heap(void)
+{
+ if (timer_heap.empty())
+ return dumb_ptr<TimerData>();
+ return timer_heap.top();
+}
+
+static
+void pop_timer_heap(void)
+{
+ timer_heap.pop();
+}
+
+Timer::Timer(tick_t tick, timer_func func, interval_t interval)
+: td(dumb_ptr<TimerData>::make(this, tick, std::move(func), interval))
+{
+ assert (interval >= interval_t::zero());
+
+ push_timer_heap(td);
+}
+
+Timer::Timer(Timer&& t)
+: td(t.td)
+{
+ t.td = nullptr;
+ if (td)
+ {
+ assert (td->owner == &t);
+ td->owner = this;
+ }
+}
+
+Timer& Timer::operator = (Timer&& t)
+{
+ std::swap(td, t.td);
+ if (td)
+ {
+ assert (td->owner == &t);
+ td->owner = this;
+ }
+ if (t.td)
+ {
+ assert (t.td->owner == this);
+ t.td->owner = &t;
+ }
+ return *this;
+}
+
+interval_t do_timer(tick_t tick)
+{
+ /// Number of milliseconds until it calls this again
+ // this says to wait 1 sec if all timers get popped
+ interval_t nextmin = std::chrono::seconds(1);
+
+ while (dumb_ptr<TimerData> td = top_timer_heap())
+ {
+ // while the heap is not empty and
+ if (td->tick > tick)
+ {
+ /// Return the time until the next timer needs to goes off
+ nextmin = td->tick - tick;
+ break;
+ }
+ pop_timer_heap();
+
+ // Prevent destroying the object we're in.
+ // Note: this would be surprising in an interval timer,
+ // but all interval timers do an immediate explicit detach().
+ if (td->owner)
+ td->owner->detach();
+ // If we are too far past the requested tick, call with
+ // the current tick instead to fix reregistration problems
+ if (td->tick + std::chrono::seconds(1) < tick)
+ td->func(td.operator->(), tick);
+ else
+ td->func(td.operator->(), td->tick);
+
+ if (td->interval == interval_t::zero())
+ {
+ td.delete_();
+ continue;
+ }
+ if (td->tick + std::chrono::seconds(1) < tick)
+ td->tick = tick + td->interval;
+ else
+ td->tick += td->interval;
+ push_timer_heap(td);
+ }
+
+ return std::max(nextmin, std::chrono::milliseconds(10));
+}
+
+tick_t file_modified(ZString name)
+{
+ struct stat buf;
+ if (stat(name.c_str(), &buf))
+ return tick_t();
+ return tick_t(std::chrono::seconds(buf.st_mtime));
+}
+
+bool has_timers()
+{
+ return !timer_heap.empty();
+}