ChimeraTK-DeviceAccess  03.18.00
testableRebotSleep_testingImpl.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Deutsches Elektronen-Synchrotron DESY, MSK, ChimeraTK Project <chimeratk-support@desy.de>
2 // SPDX-License-Identifier: LGPL-3.0-or-later
3 #pragma once
4 
5 #include <boost/chrono.hpp>
6 #include <boost/thread.hpp>
7 
8 #include <atomic>
9 #include <iostream>
10 #include <mutex>
11 
12 typedef boost::chrono::steady_clock::time_point TimePoint;
13 
14 namespace ChimeraTK {
16  public:
17  static boost::chrono::steady_clock::time_point now() {
18  std::cout << "TestIMPL:: returning now = " << (_epoch - _now).count() << std::endl;
19  return _now;
20  }
21  static boost::chrono::steady_clock::time_point _now;
22  static boost::chrono::steady_clock::time_point _epoch;
23 
24  template<class Rep, class Period>
25  static void setTime(boost::chrono::duration<Rep, Period> timeSinceMyEpoch) {
26  _now = _epoch + timeSinceMyEpoch;
27  }
28  };
31 
32  // In a future implementation we might want to hold several synchronisers (one
33  // for each thread) in a loopup table. For now we the members static.
35  static std::mutex _lock;
36  // only modify this variable while holding the lock. You may read it without, that's why it's atomic.
37  static std::atomic<bool> _clientMayGetLock;
38  // may only be accessed while holding the lock.
39  static boost::chrono::steady_clock::time_point _nextRequestedWakeup;
40  // may only be modified once (set to true) by the client code (in sleep_until)
41  static std::atomic<bool> _clientHasReachedTestableMode;
42 
43  // only used in the test thread
45  };
46 
48  std::atomic<bool> RebotSleepSynchroniser::_clientMayGetLock(false);
49  boost::chrono::steady_clock::time_point RebotSleepSynchroniser::_nextRequestedWakeup = RebotTestableClock::_epoch;
52 
53  namespace testable_rebot_sleep {
54  boost::chrono::steady_clock::time_point now() {
55  std::cout << "function TestIMPL:: returning now = "
56  << (RebotTestableClock::_now - RebotTestableClock::_epoch).count() << std::endl;
58  }
59 
65  void sleep_until(boost::chrono::steady_clock::time_point t) {
67 
68  // The application is done with whatever it was doing and going to sleep.
69  // This is the synchronisation point where we hand the lock back to the
70  // test thread.
73  }
74  else {
75  // We don't hold the lock yet, so we cannot unlock it. But the next time we reach this place we will.
77  }
78 
79  std::cout << "application unlocked" << std::endl;
80 
81  // Yield the thread (give away the rest of the time slice) until we are
82  // allowed to hold the lock. The actual waiting for execution is happening in
83  // the lock. This flag is only used to avoid the race condition that the
84  // application tries to lock before the test thread had the change to aquire
85  // the lock. For a proper handshake both test code and application must have
86  // locked before the other side relocks.
87  std::cout << "application yielding..." << std::endl;
88  do {
89  boost::this_thread::interruption_point();
90  boost::this_thread::yield();
92 
93  std::cout << "application trying to lock" << std::endl;
95  // now that we are holding the lock we set _clientMayGetLock to false and
96  // wait for the test thread to signal us that we can take the lock again
98  std::cout << "application locked, mayget is false" << std::endl;
99  }
100 
101  // don't use this directly, only through advance_until
104  std::cout << "test unlocked" << std::endl;
105 
106  // the client must signal that it acquired the lock, otherwise we do not know
107  // if it excecuted its task or not. As long as the client is still allowed to
108  // get the lock, it has not had it, and we don't get it again.
109  std::cout << "test yielding..." << std::endl;
110  do {
111  boost::this_thread::yield();
113 
114  std::cout << "test trying to lock" << std::endl;
116  // Signal to the client that you were holding the lock and the synchronisation
117  // is done. So the next time it checks the lock it is allowed to hold it.
119  // The client will block, trying to get the lock and woken up by the scheduler
120  // when it is freed again.
121  std::cout << "test locked, mayget is true" << std::endl;
122  }
123 
124  template<class Rep, class Period>
125  void advance_until(boost::chrono::duration<Rep, Period> targetTimeRelativeMyEpoch) {
127  auto absoluteTargetTime = RebotTestableClock::_epoch + targetTimeRelativeMyEpoch;
128  std::cout << "advanving to " << (absoluteTargetTime - RebotTestableClock::_epoch).count() << std::endl;
129  std::cout << "next wakeup requested for "
131 
132  while(RebotTestableClock::_now < absoluteTargetTime) {
133  if(RebotSleepSynchroniser::_nextRequestedWakeup <= absoluteTargetTime) {
136  }
137  else {
138  RebotTestableClock::_now = absoluteTargetTime;
139  }
140  }
141  }
142 
143  // The client always starts without the lock because it starts a new thread and is only interacting with
144  // sleep_until(). So we have to wait in the test until the is waiting in sleep_until, then get the lock.
146  boost::this_thread::yield();
148  boost::this_thread::yield();
149  }
151  RebotSleepSynchroniser::_clientMayGetLock = true; // client may get the lock the next time the tests releases it.
152  RebotSleepSynchroniser::_testHasReachedTestableMode = true; // make sure this function is called first by an
153  // assertion in advance_until.
154  std::cout << "test locked initially , mayget is true" << std::endl;
155  }
156 
157  } // namespace testable_rebot_sleep
158 } // namespace ChimeraTK
ChimeraTK::testable_rebot_sleep::wake_up_application
void wake_up_application()
Definition: testableRebotSleep_testingImpl.h:102
ChimeraTK::testable_rebot_sleep::now
boost::chrono::steady_clock::time_point now()
Definition: testableRebotSleep.cc:7
ChimeraTK::RebotSleepSynchroniser::_clientHasReachedTestableMode
static std::atomic< bool > _clientHasReachedTestableMode
Definition: testableRebotSleep_testingImpl.h:41
ChimeraTK::testable_rebot_sleep::sleep_until
void sleep_until(boost::chrono::steady_clock::time_point t)
There are two implementations with the same signature: One that calls boost::thread::this_thread::sle...
Definition: testableRebotSleep.cc:16
ChimeraTK::RebotTestableClock::_epoch
static boost::chrono::steady_clock::time_point _epoch
Definition: testableRebotSleep_testingImpl.h:22
ChimeraTK::RebotSleepSynchroniser::_nextRequestedWakeup
static boost::chrono::steady_clock::time_point _nextRequestedWakeup
Definition: testableRebotSleep_testingImpl.h:39
ChimeraTK::RebotTestableClock::setTime
static void setTime(boost::chrono::duration< Rep, Period > timeSinceMyEpoch)
Definition: testableRebotSleep_testingImpl.h:25
ChimeraTK::RebotSleepSynchroniser
Definition: testableRebotSleep_testingImpl.h:34
ChimeraTK::testable_rebot_sleep::waitForClientTestableMode
void waitForClientTestableMode()
Definition: testableRebotSleep_testingImpl.h:145
ChimeraTK::RebotTestableClock::_now
static boost::chrono::steady_clock::time_point _now
Definition: testableRebotSleep_testingImpl.h:21
ChimeraTK::RebotTestableClock
Definition: testableRebotSleep_testingImpl.h:15
ChimeraTK::RebotTestableClock::now
static boost::chrono::steady_clock::time_point now()
Definition: testableRebotSleep_testingImpl.h:17
ChimeraTK::testable_rebot_sleep::advance_until
void advance_until(boost::chrono::duration< Rep, Period > targetTimeRelativeMyEpoch)
Definition: testableRebotSleep_testingImpl.h:125
ChimeraTK::RebotSleepSynchroniser::_lock
static std::mutex _lock
Definition: testableRebotSleep_testingImpl.h:35
ChimeraTK
Definition: DummyBackend.h:16
ChimeraTK::RebotSleepSynchroniser::_clientMayGetLock
static std::atomic< bool > _clientMayGetLock
Definition: testableRebotSleep_testingImpl.h:37
TimePoint
boost::chrono::steady_clock::time_point TimePoint
Definition: testableRebotSleep_testingImpl.h:12
ChimeraTK::RebotSleepSynchroniser::_testHasReachedTestableMode
static bool _testHasReachedTestableMode
Definition: testableRebotSleep_testingImpl.h:44