100.00% Lines (62/62) 100.00% Functions (8/8)
TLA Baseline Branch
Line Hits Code Line Hits Code
  1 + //
  2 + // Copyright (c) 2026 Michael Vandeberg
  3 + //
  4 + // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5 + // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6 + //
  7 + // Official repository: https://github.com/cppalliance/capy
  8 + //
  9 +
  10 + #include <boost/capy/ex/detail/timer_service.hpp>
  11 +
  12 + namespace boost {
  13 + namespace capy {
  14 + namespace detail {
  15 +
HITGNC   16 + 20 timer_service::
HITGNC   17 + 20 timer_service(execution_context& ctx)
HITGNC   18 + 40 : thread_([this] { run(); })
  19 + {
  20 + (void)ctx;
HITGNC   21 + 20 }
  22 +
HITGNC   23 + 40 timer_service::
HITGNC   24 + 20 ~timer_service()
  25 + {
HITGNC   26 + 20 stop_and_join();
HITGNC   27 + 40 }
  28 +
  29 + timer_service::timer_id
HITGNC   30 + 135 timer_service::
  31 + schedule_at(
  32 + std::chrono::steady_clock::time_point deadline,
  33 + std::function<void()> cb)
  34 + {
HITGNC   35 + 135 std::lock_guard lock(mutex_);
HITGNC   36 + 135 auto id = ++next_id_;
HITGNC   37 + 135 active_ids_.insert(id);
HITGNC   38 + 135 queue_.push(entry{deadline, id, std::move(cb)});
HITGNC   39 + 135 cv_.notify_one();
HITGNC   40 + 135 return id;
HITGNC   41 + 135 }
  42 +
  43 + void
HITGNC   44 + 43 timer_service::
  45 + cancel(timer_id id)
  46 + {
HITGNC   47 + 43 std::unique_lock lock(mutex_);
HITGNC   48 + 43 if(!active_ids_.contains(id))
HITGNC   49 + 35 return;
HITGNC   50 + 8 if(executing_id_ == id)
  51 + {
  52 + // Callback is running — wait for it to finish.
  53 + // run() erases from active_ids_ after execution.
HITGNC   54 + 4 while(executing_id_ == id)
HITGNC   55 + 2 cancel_cv_.wait(lock);
HITGNC   56 + 2 return;
  57 + }
HITGNC   58 + 6 active_ids_.erase(id);
HITGNC   59 + 43 }
  60 +
  61 + void
HITGNC   62 + 40 timer_service::
  63 + stop_and_join()
  64 + {
  65 + {
HITGNC   66 + 40 std::lock_guard lock(mutex_);
HITGNC   67 + 40 stopped_ = true;
HITGNC   68 + 40 }
HITGNC   69 + 40 cv_.notify_one();
HITGNC   70 + 40 if(thread_.joinable())
HITGNC   71 + 20 thread_.join();
HITGNC   72 + 40 }
  73 +
  74 + void
HITGNC   75 + 20 timer_service::
  76 + shutdown()
  77 + {
HITGNC   78 + 20 stop_and_join();
HITGNC   79 + 20 }
  80 +
  81 + void
HITGNC   82 + 20 timer_service::
  83 + run()
  84 + {
HITGNC   85 + 20 std::unique_lock lock(mutex_);
  86 + for(;;)
  87 + {
HITGNC   88 + 201 if(stopped_)
HITGNC   89 + 20 return;
  90 +
HITGNC   91 + 181 if(queue_.empty())
  92 + {
HITGNC   93 + 17 cv_.wait(lock);
HITGNC   94 + 55 continue;
  95 + }
  96 +
HITGNC   97 + 164 auto deadline = queue_.top().deadline;
HITGNC   98 + 164 auto now = std::chrono::steady_clock::now();
HITGNC   99 + 164 if(deadline > now)
  100 + {
HITGNC   101 + 36 cv_.wait_until(lock, deadline);
HITGNC   102 + 36 continue;
  103 + }
  104 +
  105 + // Pop the entry (const_cast needed because priority_queue::top is const)
HITGNC   106 + 128 auto e = std::move(const_cast<entry&>(queue_.top()));
HITGNC   107 + 128 queue_.pop();
  108 +
  109 + // Skip if cancelled (no longer in active set)
HITGNC   110 + 128 if(!active_ids_.contains(e.id))
HITGNC   111 + 2 continue;
  112 +
HITGNC   113 + 126 executing_id_ = e.id;
HITGNC   114 + 126 lock.unlock();
HITGNC   115 + 126 e.callback();
HITGNC   116 + 126 lock.lock();
HITGNC   117 + 126 active_ids_.erase(e.id);
HITGNC   118 + 126 executing_id_ = 0;
HITGNC   119 + 126 cancel_cv_.notify_all();
HITGNC   120 + 309 }
HITGNC   121 + 20 }
  122 +
  123 + } // detail
  124 + } // capy
  125 + } // boost