123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- #ifndef CPPTIME_H_
- #define CPPTIME_H_
- #include <algorithm>
- #include <chrono>
- #include <condition_variable>
- #include <functional>
- #include <mutex>
- #include <set>
- #include <stack>
- #include <string>
- #include <thread>
- #include <utility>
- #include <vector>
- namespace cnstream {
- using std::chrono::duration_cast;
- using std::chrono::microseconds;
- using std::chrono::milliseconds;
- using std::chrono::steady_clock;
- using std::chrono::time_point;
- using timer_id = std::size_t;
- using handler_t = std::function<void(timer_id)>;
- using clock = std::chrono::steady_clock;
- using timestamp = std::chrono::time_point<clock>;
- using duration = std::chrono::microseconds;
- namespace detail {
- struct Event {
- timer_id id;
- timestamp start;
- duration period;
- handler_t handler;
- bool valid;
- Event() : id(0), start(duration::zero()), period(duration::zero()), handler(nullptr), valid(false) {}
- template <typename Func>
- Event(timer_id id, timestamp start, duration period, Func &&handler)
- : id(id), start(start), period(period), handler(std::forward<Func>(handler)), valid(true) {}
- Event(Event &&r) = default;
- Event &operator=(Event &&ev) = default;
- Event(const Event &r) = delete;
- Event &operator=(const Event &r) = delete;
- };
- struct Time_event {
- timestamp next;
- timer_id ref;
- };
- inline bool operator<(const Time_event &l, const Time_event &r) { return l.next < r.next; }
- }
- class Timer {
- using scoped_m = std::unique_lock<std::mutex>;
-
- std::mutex m;
- std::condition_variable cond;
- std::thread worker;
-
- bool done = false;
-
- std::vector<detail::Event> events;
-
- std::multiset<detail::Time_event> time_events;
-
- std::stack<cnstream::timer_id> free_ids;
- public:
- Timer() : m{}, cond{}, worker{}, events{}, time_events{}, free_ids{} {
- scoped_m lock(m);
- done = false;
- worker = std::thread([this] { run(); });
- }
- ~Timer() {
- scoped_m lock(m);
- done = true;
- lock.unlock();
- cond.notify_all();
- worker.join();
- events.clear();
- time_events.clear();
- while (!free_ids.empty()) {
- free_ids.pop();
- }
- }
-
- timer_id add(const timestamp &when, handler_t &&handler, const duration &period = duration::zero()) {
- scoped_m lock(m);
- timer_id id = 0;
-
-
- if (free_ids.empty()) {
- id = events.size();
- detail::Event e(id, when, period, std::move(handler));
- events.push_back(std::move(e));
- } else {
- id = free_ids.top();
- free_ids.pop();
- detail::Event e(id, when, period, std::move(handler));
- events[id] = std::move(e);
- }
- time_events.insert(detail::Time_event{when, id});
- lock.unlock();
- cond.notify_all();
- return id;
- }
-
- template <class Rep, class Period>
- inline timer_id add(const std::chrono::duration<Rep, Period> &when, handler_t &&handler,
- const duration &period = duration::zero()) {
- return add(clock::now() + std::chrono::duration_cast<std::chrono::microseconds>(when), std::move(handler), period);
- }
-
- inline timer_id add(const uint64_t when, handler_t &&handler, const uint64_t period = 0) {
- return add(duration(when), std::move(handler), duration(period));
- }
-
- bool remove(timer_id id) {
- scoped_m lock(m);
- if (events.size() == 0 || events.size() < id) {
- return false;
- }
- events[id].valid = false;
- auto it = std::find_if(time_events.begin(), time_events.end(),
- [&](const detail::Time_event &te) { return te.ref == id; });
- if (it != time_events.end()) {
- free_ids.push(it->ref);
- time_events.erase(it);
- }
- lock.unlock();
- cond.notify_all();
- return true;
- }
- private:
- void run() {
- scoped_m lock(m);
- while (!done) {
- if (time_events.empty()) {
-
- cond.wait(lock);
- } else {
- detail::Time_event te = *time_events.begin();
- if (cnstream::clock::now() >= te.next) {
-
- time_events.erase(time_events.begin());
-
- lock.unlock();
- events[te.ref].handler(te.ref);
- lock.lock();
- if (events[te.ref].valid && events[te.ref].period.count() > 0) {
-
- te.next += events[te.ref].period;
- time_events.insert(te);
- } else {
-
-
- events[te.ref].valid = false;
- free_ids.push(te.ref);
- }
- } else {
- cond.wait_until(lock, te.next);
- }
- }
- }
- }
- };
- template <typename precision = microseconds>
- class TimeStampBase {
- public:
-
- static uint64_t Current() {
- return duration_cast<precision>(std::chrono::system_clock::now().time_since_epoch()).count();
- }
-
- static std::string CurrentToString() { return std::to_string(Current()); }
-
- static std::string CurrentToDate() {
- uint64_t now = Current();
- time_t now_in_sec = now / 1e6;
- uint64_t remainder = now % 1000000;
- struct tm now_time;
- char buf[80];
- localtime_r(&now_in_sec, &now_time);
- strftime(buf, sizeof(buf), "%Y-%m-%d-%H.%M.%S", &now_time);
- std::string result = buf;
- return result + "." + std::to_string(remainder);
- }
- };
- using TimeStamp = TimeStampBase<>;
- enum class ClockType {
- Tick,
- TickTock,
- };
- template <ClockType type, typename precision = std::micro>
- class ClockBase {
- public:
- using Elapsed_t = std::chrono::duration<double, precision>;
-
- Elapsed_t ElapsedTotal() const { return total_; }
-
- double ElapsedTotalAsDouble() const { return total_.count(); }
-
- Elapsed_t ElapsedAverage() const { return times_ == 0 ? Elapsed_t::zero() : total_ / times_; }
-
- double ElapsedAverageAsDouble() const { return ElapsedAverage().count(); }
-
- void Clear() {
- total_ = Elapsed_t::zero();
- times_ = 0;
- }
- protected:
- Elapsed_t total_ = Elapsed_t::zero();
- uint32_t times_ = 0;
- };
- class TickClock final : public ClockBase<ClockType::Tick> {
- public:
-
- void Tick() {
- curr_ = steady_clock::now();
- if (!started_) {
- started_ = true;
- } else {
- total_ += curr_ - prev_;
- ++times_;
- }
- prev_ = curr_;
- }
- private:
- time_point<steady_clock> prev_, curr_;
- bool started_ = false;
- };
- class TickTockClock final : public ClockBase<ClockType::TickTock> {
- public:
-
- void Tick() { start_ = steady_clock::now(); }
-
- void Tock() {
- end_ = steady_clock::now();
- total_ += end_ - start_;
- ++times_;
- }
- private:
- time_point<steady_clock> start_, end_;
- };
- }
- #endif
|