YARP
Yet Another Robot Platform
Time.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2019 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/LogStream.h>
13 #include <yarp/os/Network.h>
14 #include <yarp/os/NetworkClock.h>
15 #include <yarp/os/SystemClock.h>
16 #include <yarp/os/Thread.h>
17 #include <yarp/os/impl/Logger.h>
19 #include <yarp/os/impl/TimeImpl.h>
20 
21 #include <mutex>
22 
23 #if defined(_WIN32)
24 // for WIN32 MM functions
25 # include <mmsystem.h>
26 #endif
27 
28 using namespace yarp::os;
30 
31 namespace {
32 
33 bool clock_owned = false;
34 bool network_clock_ok = false;
35 Clock* pclock = nullptr;
36 yarpClockType yarp_clock_type = YARP_CLOCK_UNINITIALIZED;
37 
38 std::mutex& getTimeMutex()
39 {
40  static std::mutex mutex;
41  return mutex;
42 }
43 
44 void printNoClock_ErrorMessage()
45 {
46  YARP_ERROR(Logger::get(), "\n Warning an issue has been found, please update the code.\n \
47  Clock is not initialized: This means YARP framework has not been properly initialized. \n \
48  The clock can be initialized with one of the following methods:\n \
49  - Create yarp::os::Network object or call yarp::os::Network::init()\n \
50  - Call useSystemClock()\n \
51  otherwise use yarp::os::SystemClock::nowSystem() and yarp::os::SystemClock::delaySystem() instead of Time::now() and Time::delay()\n");
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  yAssert(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  YARP_FAIL(Logger::get(), "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  YARP_FAIL(Logger::get(), "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  YARP_INFO(Logger::get(), "Waiting for clock server to start broadcasting data ...");
221  i = 0;
222  }
224  }
225 }
226 
228 {
229  if (clock == nullptr) {
230  YARP_FAIL(Logger::get(), "failed configuring CustomClock client");
231  return;
232  }
233 
234  if (!clock->isValid()) {
235  YARP_FAIL(Logger::get(), "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_SYSTEM);
260 }
261 
263 {
264  return (yarp_clock_type == YARP_CLOCK_NETWORK);
265 }
266 
268 {
269  return (yarp_clock_type == YARP_CLOCK_CUSTOM);
270 }
271 
273 {
274  return yarp_clock_type;
275 }
276 
278 {
279  std::string clockTypeString;
280  if (type == -1) {
281  type = yarp_clock_type;
282  }
283 
284  switch (type) {
285  case YARP_CLOCK_SYSTEM:
286  clockTypeString = "System clock";
287  break;
288 
289  case YARP_CLOCK_NETWORK:
290  clockTypeString = "Network clock";
291  break;
292 
293  case YARP_CLOCK_CUSTOM:
294  clockTypeString = "Custom clock";
295  break;
296 
298  clockTypeString = "Clock has not been initialized yet: This should never happen. Is the object yarp::os::Network been initialized?";
299  break;
300 
301  default:
302  clockTypeString = "Unknown clock: This should never happen. Is the object yarp::os::Network been initialized?";
303  break;
304  }
305  return clockTypeString;
306 }
307 
308 
310 {
311  // The clock must never be NULL here
312  return getClock()->isValid();
313 }
void startTurboBoost()
For OS where it makes sense sets the scheduler to be called more often.
Definition: Time.cpp:91
bool isNetworkClock()
Check if YARP is providing network time.
Definition: Time.cpp:262
#define YARP_INFO(log, x)
Definition: Logger.h:89
static void yield()
Reschedule the execution of current thread, allowing other threads to run.
Definition: Thread.cpp:154
virtual bool isValid() const =0
Check if time is valid (non-zero).
static double nowSystem()
Definition: SystemClock.cpp:37
void useSystemClock()
Configure YARP to use system time (this is the default).
Definition: Time.cpp:144
bool isCustomClock()
Check if YARP is using a user-defined custom time.
Definition: Time.cpp:267
This is a wrapper for message logging.
Definition: Logger.h:34
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
virtual double now()=0
Return the current time in seconds, relative to an arbitrary starting point.
virtual void delay(double seconds)=0
Wait for a certain number of seconds.
void endTurboBoost()
Definition: Time.cpp:101
#define YARP_FAIL(log, x)
Definition: Logger.h:95
void useNetworkClock(const std::string &clock, const std::string &localPortName="")
Configure YARP to read time from a specified topic.
Definition: Time.cpp:184
#define YARP_ERROR(log, x)
Definition: Logger.h:87
void turboBoost()
For OS where it makes sense sets the scheduler to be called more often.
Definition: Time.cpp:132
#define yAssert(x)
Definition: Log.h:112
yarpClockType
Definition: Time.h:28
yarpClockType getClockType()
Definition: Time.cpp:272
static void delaySystem(double seconds)
Definition: SystemClock.cpp:32
std::string clockTypeToString(yarpClockType type)
Converts clock type enum into string.
Definition: Time.cpp:277
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
An interface to the operating system, including Port based communication.
bool isSystemClock()
Check if YARP is providing system time.
Definition: Time.cpp:257
void removeClock()
Definition: Time.cpp:82
bool isValid()
Check if time is valid (non-zero).
Definition: Time.cpp:309