Skip to content

[cyberRT][cyber_time] Time jumps when using a timer can cause the thread sleep duration to be incorrect. #15763

@CPPMemorialAlbum

Description

@CPPMemorialAlbum

Describe the bug
Time jumps when using a timer can cause the thread sleep duration to be incorrect.

To Reproduce
Steps to reproduce the behavior:

  1. start cyber timer or timer_component
  2. date -s xxxx-xx-xx(not now)
  3. Monitor the execution of the timer task
  4. The timer task is no longer executed

Expected behavior
Timer execution is not affected by time jumps

Additional context
timing wheel use Rate Sleep fun :

void TimingWheel::TickFunc() {
  Rate rate(TIMER_RESOLUTION_MS * 1000000);  // ms to ns
  while (running_) {
    Tick();
    // AINFO_EVERY(1000) << "Tick " << TickCount();
    tick_count_++;
    rate.Sleep();
    {
      std::lock_guard<std::mutex> lock(current_work_wheel_index_mutex_);
      current_work_wheel_index_ =
          GetWorkWheelIndex(current_work_wheel_index_ + 1);
    }
    if (current_work_wheel_index_ == 0) {
      {
        std::lock_guard<std::mutex> lock(current_assistant_wheel_index_mutex_);
        current_assistant_wheel_index_ =
            GetAssistantWheelIndex(current_assistant_wheel_index_ + 1);
      }
      Cascade(current_assistant_wheel_index_);
    }
  }
}

and Rate::Sleep used Time::SleepUntil perform thread sleep operation

void Rate::Sleep() {
  Time expected_end = start_ + expected_cycle_time_;

  Time actual_end = Time::Now();

  // detect backward jumps in time
  if (actual_end < start_) {
    AWARN << "Detect backward jumps in time";
    expected_end = actual_end + expected_cycle_time_;
  }

  // calculate the time we'll sleep for
  Duration sleep_time = expected_end - actual_end;

  // set the actual amount of time the loop took in case the user wants to kNow
  actual_cycle_time_ = actual_end - start_;

  // make sure to reset our start time
  start_ = expected_end;

  // if we've taken too much time we won't sleep
  if (sleep_time < Duration(0.0)) {
    AWARN << "Detect forward jumps in time";
    // if we've jumped forward in time, or the loop has taken more than a full
    // extra cycle, reset our cycle
    if (actual_end > expected_end + expected_cycle_time_) {
      start_ = actual_end;
    }
    // return false to show that the desired rate was not met
    return;
  }

  Time::SleepUntil(expected_end);
}

but, Time::SleepUntil use system_clock, When a system time jump occurs, the sleep time may be calculated incorrectly.

void Time::SleepUntil(const Time& time) {
  auto nano = std::chrono::nanoseconds(time.ToNanosecond());
  system_clock::time_point tp(nano);
  std::this_thread::sleep_until(tp);
}

Why Rate not use steady_clock replace system_clock, and use Time::MonoTime() replace 'Time::Now()'

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions