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