Program Listing for File dynamic_sleeper.h

Return to documentation for file (contrib/ecaltime/include/dynamic_sleeper.h)

/* ========================= eCAL LICENSE =================================
 *
 * Copyright (C) 2016 - 2019 Continental Corporation
 *
 * 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.
 *
 * ========================= eCAL LICENSE =================================
*/

#pragma once

#include <chrono>
#include <mutex>
#include <condition_variable>
#include <atomic>

class CDynamicSleeper
{
public:

  CDynamicSleeper() :
    rate(1.0),
    lastSimTime(0)
  {
    lastSimTimeLocalTimestamp = std::chrono::steady_clock::now();
  }

  void sleepFor(long long durationNsecs_) {
    auto nowReal = std::chrono::steady_clock::now();
    long long nowSim;
    {
      std::unique_lock<std::mutex> modifyTimeLock(modifyTimeMutex);
      nowSim = (long long)((double)((nowReal - lastSimTimeLocalTimestamp).count()) * rate) + lastSimTime;
    }
    sleepUntil(nowSim + durationNsecs_);
  }

  void sleepUntil(long long sleepUntilTimeNsecs_) {
    auto      startRealtime = std::chrono::steady_clock::now();
    long long loopStartSimTime;
    double    originalRate;
    long long originalLastSimtime;

    {
      std::unique_lock<std::mutex> modifyTimeLock(modifyTimeMutex);
      loopStartSimTime = (long long)((double)((startRealtime - lastSimTimeLocalTimestamp).count()) * rate) + lastSimTime;
      originalRate = rate;
      originalLastSimtime = lastSimTime;
    }

    while (loopStartSimTime < sleepUntilTimeNsecs_) {
      std::unique_lock<std::mutex> waitLck(waitMutex);
      if (originalRate > 0) {
        std::chrono::duration<long long, std::nano> realTimeToSleep((long long)((double)(sleepUntilTimeNsecs_ - loopStartSimTime) / originalRate));
        if (waitCv.wait_for(waitLck, realTimeToSleep) == std::cv_status::timeout) {
          return;
        }
      }
      else {
        waitCv.wait(waitLck);
      }

      {
        std::unique_lock<std::mutex> modifyTimeLock(modifyTimeMutex);
        if (lastSimTime < originalLastSimtime) {
          return;
        }
        startRealtime = std::chrono::steady_clock::now();
        loopStartSimTime = (long long)((double)((startRealtime - lastSimTimeLocalTimestamp).count()) * rate) + lastSimTime;
        originalLastSimtime = lastSimTime;
        originalRate = rate;
      }
    }
  }

  void setRate(double rate_) {
    auto now = std::chrono::steady_clock::now();
    std::unique_lock<std::mutex> modifyTimeLock(modifyTimeMutex);
    long long passedSystemNsecs = (now - lastSimTimeLocalTimestamp).count();
    long long passedSimtimeNsecs = (long long)((double)passedSystemNsecs * rate);
    lastSimTimeLocalTimestamp = now;
    lastSimTime += passedSimtimeNsecs;
    rate = rate_;
    waitCv.notify_all();
  }

  void setTime(long long nsecs_) {
    auto now = std::chrono::steady_clock::now();
    std::unique_lock<std::mutex> modifyTimeLock(modifyTimeMutex);
    lastSimTimeLocalTimestamp = now;
    lastSimTime = nsecs_;
    waitCv.notify_all();
  }

  void setTimeAndRate(long long time_nsecs_, double rate_) {
    auto now = std::chrono::steady_clock::now();
    std::unique_lock<std::mutex> modifyTimeLock(modifyTimeMutex);
    lastSimTimeLocalTimestamp = now;
    lastSimTime = time_nsecs_;
    rate = rate_;
    waitCv.notify_all();
  }

  long long getCurrentInternalSimTime() {
    std::unique_lock<std::mutex> modifyTimeLock(modifyTimeMutex);
    auto now = std::chrono::steady_clock::now();
    return (long long)((double)((now - lastSimTimeLocalTimestamp).count()) * rate) + lastSimTime;
  }

  ~CDynamicSleeper() {}

private:
  std::mutex waitMutex;
  std::condition_variable waitCv;
  std::mutex modifyTimeMutex;
  double rate;
  long long lastSimTime;
  std::chrono::time_point<std::chrono::steady_clock> lastSimTimeLocalTimestamp;
};