YARP
Yet Another Robot Platform
Time.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  * Copyright (C) 2006, 2011 Anne van Rossum <anne@almende.com>
4  * All rights reserved.
5  *
6  * This software may be modified and distributed under the terms of the
7  * BSD-3-Clause license. See the accompanying LICENSE file for details.
8  */
9 
10 #include <yarp/os/Time.h>
11 
12 #include <yarp/os/Network.h>
13 #include <yarp/os/NetworkClock.h>
14 #include <yarp/os/SystemClock.h>
15 #include <yarp/os/Thread.h>
18 #include <yarp/os/impl/TimeImpl.h>
19 
20 #include <mutex>
21 
22 #if defined(_WIN32)
23 // for WIN32 MM functions
24 # include <mmsystem.h>
25 #endif
26 
27 using namespace yarp::os;
28 
29 namespace {
30 YARP_OS_LOG_COMPONENT(TIME, "yarp.os.Time")
31 } // namespace
32 
33 namespace {
34 
35 bool clock_owned = false;
36 bool network_clock_ok = false;
37 Clock* pclock = nullptr;
38 yarpClockType yarp_clock_type = YARP_CLOCK_UNINITIALIZED;
39 
40 std::mutex& getTimeMutex()
41 {
42  static std::mutex mutex;
43  return mutex;
44 }
45 
46 void printNoClock_ErrorMessage()
47 {
48  yCError(TIME, "Warning an issue has been found, please update the code.");
49  yCError(TIME, " Clock is not initialized.");
50  yCError(TIME, " This means YARP framework has not been properly initialized.");
51  yCError(TIME, " The clock can be initialized with one of the following methods:");
52  yCError(TIME, " - Create yarp::os::Network object or call yarp::os::Network::init()");
53  yCError(TIME, " - Call useSystemClock()");
54  yCError(TIME, " otherwise use yarp::os::SystemClock::nowSystem() and yarp::os::SystemClock::delaySystem() instead of Time::now() and Time::delay()");
55 }
56 
57 Clock* getClock()
58 {
59  if (pclock == nullptr) {
60  /*
61  * Assuming this should never happen, if we do get here, what shall be done??
62  *
63  * 1: create system clock
64  * If we get here, probably there is some sort of race condition while changing the clock,
65  * so creating a system clock may not be what we want to do, and this may interfere with the
66  * clock really wanted by the user, i.e. this system clock may be destroyed again to
67  * instantiate the good one, leaving space for another possible race condition.
68  *
69  * 2: use the system clock only for this call
70  *
71  * 3: exit now and ask user to correctly initialize the framework
72  * This is better because it shows initialization problems right from the start and help user
73  * to fix the code, which may otherwise lead to undefined behaviour.
74  *
75  * So now initialize a system clock and exit.
76  */
77  printNoClock_ErrorMessage();
78  std::exit(-1);
79  }
80  return pclock;
81 }
82 } // namespace
83 
84 
86 {
87  if (pclock != nullptr) {
88  delete pclock;
89  pclock = nullptr;
90  }
91  yarp_clock_type = YARP_CLOCK_UNINITIALIZED;
92 }
93 
95 {
96 #if defined(_WIN32)
97  // only does something on Microsoft Windows
98  TIMECAPS tm;
99  timeGetDevCaps(&tm, sizeof(TIMECAPS));
100  timeBeginPeriod(tm.wPeriodMin);
101 #endif
102 }
103 
105 {
106 #if defined(_WIN32)
107  // only does something on Microsoft Windows
108  TIMECAPS tm;
109  timeGetDevCaps(&tm, sizeof(TIMECAPS));
110  timeEndPeriod(tm.wPeriodMin);
111 #endif
112 }
113 
114 void Time::delay(double seconds)
115 {
116  if (isSystemClock()) {
117  return SystemClock::delaySystem(seconds);
118  }
119 
120  Clock* clk = getClock();
121  clk->delay(seconds);
122 }
123 
124 double Time::now()
125 {
126  if (isSystemClock()) {
127  return SystemClock::nowSystem();
128  }
129 
130  Clock* clk = getClock();
131  return clk->now();
132 }
133 
134 #ifndef YARP_NO_DEPRECATED // Since YARP 3.0.0
136 {
138 }
139 #endif // YARP_NO_DEPRECATED
140 
142 {
143  return yarp::os::Thread::yield();
144 }
145 
146 
148 {
149  if (!isSystemClock()) {
150  getTimeMutex().lock();
151 
152  Clock* old_pclock = pclock;
153  bool old_clock_owned = clock_owned;
154 
155  pclock = new SystemClock();
156  yCAssert(TIME, pclock);
157  yarp_clock_type = YARP_CLOCK_SYSTEM;
158  clock_owned = true;
159 
160  if (old_clock_owned && (old_pclock != nullptr)) {
161  delete old_pclock;
162  }
163 
164  getTimeMutex().unlock();
165  }
166 }
167 
168 /* Creation of network clock may fail for different causes:
169  * - cannot open port
170  * - cannot connect to nameserver
171  *
172  * They may be handled in different ways, for example for the firsts two cases, it simply fails and
173  * continue with system clock. Failure should be reported to the user and 'pclock' pointer will be temporary
174  * set to NULL (which is an INVALID value).
175  * isSystemClock() will still return true because it is the clock currently active.
176  *
177  * In case the source clock is not yet publishing time data, we wait here for the first valid clock, this way
178  * the application will not start until the clock is correctly configured.
179  * In this situation
180  * - isSystemClock() will be false
181  * - isNetworkClock() will be true
182  * - isValid() will be false until the first clock message is received, then it'll be true
183  *
184  * As soon as the clock starts being published, the networkClock has to acknowledge it and 'attach' to it. Clock will
185  * then be valid.
186  */
187 void Time::useNetworkClock(const std::string& clock, const std::string& localPortName)
188 {
189  // re-create the clock also in case we already use a network clock, because
190  // the input clock port may be different or the clock producer may be changed (different
191  // clock source publishing on the same port/topic), so we may need to reconnect.
192  getTimeMutex().lock();
193 
194  Clock* old_pclock = pclock; // store current clock pointer to delete it afterward
195  bool old_clock_owned = clock_owned;
196  auto* _networkClock = new NetworkClock();
197  if (_networkClock == nullptr) {
198  yCFatal(TIME, "failed creating NetworkClock client");
199  return;
200  }
201  if (_networkClock->open(clock, localPortName)) {
202  network_clock_ok = true; // see if it is really needed
203  // updating clock pointer with the new one already initialized.
204 
205  pclock = _networkClock;
206  clock_owned = true;
207  yarp_clock_type = YARP_CLOCK_NETWORK;
208  } else {
209  yCFatal(TIME, "failed creating NetworkClock client, cannot open input port");
210  return;
211  }
212 
213  if (old_clock_owned && (old_pclock != nullptr)) {
214  delete old_pclock;
215  }
216 
217  getTimeMutex().unlock();
218 
219  int i = -1;
220  while ((pclock != nullptr) && !pclock->isValid()) {
221  i++;
222  if ((i % 50) == 0) {
223  yCInfo(TIME, "Waiting for clock server to start broadcasting data ...");
224  i = 0;
225  }
227  }
228 }
229 
231 {
232  if (clock == nullptr) {
233  yCFatal(TIME, "failed configuring CustomClock client");
234  return;
235  }
236 
237  if (!clock->isValid()) {
238  yCFatal(TIME, "Error: CustomClock is not valid");
239  return;
240  }
241 
242  getTimeMutex().lock();
243 
244  // store current clock pointer to delete it afterward
245  Clock* old_pclock = pclock;
246  bool old_clock_owned = clock_owned;
247 
248  pclock = clock;
249  yarp_clock_type = YARP_CLOCK_CUSTOM;
250  clock_owned = false;
251 
252  // delete old clock
253  if (old_clock_owned && (old_pclock != nullptr)) {
254  delete old_pclock;
255  }
256 
257  getTimeMutex().unlock();
258 }
259 
261 {
262  return (yarp_clock_type != YARP_CLOCK_UNINITIALIZED);
263 }
264 
266 {
267  return (yarp_clock_type == YARP_CLOCK_SYSTEM);
268 }
269 
271 {
272  return (yarp_clock_type == YARP_CLOCK_NETWORK);
273 }
274 
276 {
277  return (yarp_clock_type == YARP_CLOCK_CUSTOM);
278 }
279 
281 {
282  return yarp_clock_type;
283 }
284 
286 {
287  std::string clockTypeString;
288  if (type == -1) {
289  type = yarp_clock_type;
290  }
291 
292  switch (type) {
293  case YARP_CLOCK_SYSTEM:
294  clockTypeString = "System clock";
295  break;
296 
297  case YARP_CLOCK_NETWORK:
298  clockTypeString = "Network clock";
299  break;
300 
301  case YARP_CLOCK_CUSTOM:
302  clockTypeString = "Custom clock";
303  break;
304 
306  clockTypeString = "Clock has not been initialized yet: This should never happen. Is the object yarp::os::Network been initialized?";
307  break;
308 
309  default:
310  clockTypeString = "Unknown clock: This should never happen. Is the object yarp::os::Network been initialized?";
311  break;
312  }
313  return clockTypeString;
314 }
315 
316 
318 {
319  // The clock must never be NULL here
320  return getClock()->isValid();
321 }
yarp::os::YARP_CLOCK_UNINITIALIZED
@ YARP_CLOCK_UNINITIALIZED
Definition: Time.h:30
SystemClock.h
Network.h
yarp::os::impl::Time::endTurboBoost
void endTurboBoost()
Definition: Time.cpp:104
yarp::os::Thread::yield
static void yield()
Reschedule the execution of current thread, allowing other threads to run.
Definition: Thread.cpp:153
yarp::os::impl::Time::removeClock
void removeClock()
Definition: Time.cpp:85
yarp::os::Time::isValid
bool isValid()
Check if time is valid (non-zero).
Definition: Time.cpp:317
yarp::os::Clock
Definition: Clock.h:17
yarp::os::Clock::delay
virtual void delay(double seconds)=0
Wait for a certain number of seconds.
yarp::os::Time::isCustomClock
bool isCustomClock()
Check if YARP is using a user-defined custom time.
Definition: Time.cpp:275
yarp::os::impl::Time::startTurboBoost
void startTurboBoost()
For OS where it makes sense sets the scheduler to be called more often.
Definition: Time.cpp:94
LogComponent.h
yarp::os::Time::now
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:124
yarp::os::SystemClock::nowSystem
static double nowSystem()
Definition: SystemClock.cpp:37
yarp::os::Time::useSystemClock
void useSystemClock()
Configure YARP to use system time (this is the default).
Definition: Time.cpp:147
yarp::os::YARP_CLOCK_SYSTEM
@ YARP_CLOCK_SYSTEM
Definition: Time.h:32
yarp::os::SystemClock
Definition: SystemClock.h:17
yarp::os::YARP_CLOCK_NETWORK
@ YARP_CLOCK_NETWORK
Definition: Time.h:33
yarp::os::SystemClock::delaySystem
static void delaySystem(double seconds)
Definition: SystemClock.cpp:32
yarp::os::Time::getClockType
yarpClockType getClockType()
Definition: Time.cpp:280
yarp::os::NetworkClock
Definition: NetworkClock.h:21
yarp::os::Time::yield
void yield()
The calling thread releases its remaining quantum upon calling this function.
Definition: Time.cpp:141
yarp::os::Time::turboBoost
void turboBoost()
For OS where it makes sense sets the scheduler to be called more often.
Definition: Time.cpp:135
Thread.h
NetworkClock.h
TimeImpl.h
yarp::os::Clock::now
virtual double now()=0
Return the current time in seconds, relative to an arbitrary starting point.
yarp::os::Time::useNetworkClock
void useNetworkClock(const std::string &clock, const std::string &localPortName="")
Configure YARP to read time from a specified topic.
Definition: Time.cpp:187
yCAssert
#define yCAssert(component, x)
Definition: LogComponent.h:136
yCError
#define yCError(component,...)
Definition: LogComponent.h:127
yCInfo
#define yCInfo(component,...)
Definition: LogComponent.h:115
yarp::os
Definition: AbstractCarrier.h:17
yarp::os::Time::isSystemClock
bool isSystemClock()
Check if YARP is providing system time.
Definition: Time.cpp:265
yarp::os::Time::clockTypeToString
std::string clockTypeToString(yarpClockType type)
Converts clock type enum into string.
Definition: Time.cpp:285
yarp::os::Time::isClockInitialized
bool isClockInitialized()
Check if YARP clock is initialized.
Definition: Time.cpp:260
Time.h
yarp::os::YARP_CLOCK_CUSTOM
@ YARP_CLOCK_CUSTOM
Definition: Time.h:34
yarp::os::Time::useCustomClock
void useCustomClock(Clock *clock)
Configure YARP clients to use a custom clock source provided by the user.
Definition: Time.cpp:230
PlatformTime.h
YARP_OS_LOG_COMPONENT
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:36
yarp::os::Clock::isValid
virtual bool isValid() const =0
Check if time is valid (non-zero).
yarp::os::Time::delay
void delay(double seconds)
Wait for a certain number of seconds.
Definition: Time.cpp:114
yarp::os::Time::isNetworkClock
bool isNetworkClock()
Check if YARP is providing network time.
Definition: Time.cpp:270
yarp::os::yarpClockType
yarpClockType
Definition: Time.h:28
yCFatal
#define yCFatal(component,...)
Definition: LogComponent.h:133