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>
11#include <yarp/os/SystemClock.h>
12#include <yarp/os/Thread.h>
16
17#include <mutex>
18
19#if defined(_WIN32)
20// for WIN32 MM functions
21# include <mmsystem.h>
22#endif
23
24using namespace yarp::os;
25
26namespace {
27YARP_OS_LOG_COMPONENT(TIME, "yarp.os.Time")
28} // namespace
29
30namespace {
31
32bool clock_owned = false;
33bool network_clock_ok = false;
34Clock* pclock = nullptr;
36
37std::mutex& getTimeMutex()
38{
39 static std::mutex mutex;
40 return mutex;
41}
42
43void 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
54Clock* 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
111void 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
121double 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{
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 */
184void 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 }
223 SystemClock::delaySystem(0.1);
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) {
291 clockTypeString = "System clock";
292 break;
293
295 clockTypeString = "Network clock";
296 break;
297
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 void yield()
Reschedule the execution of current thread, allowing other threads to run.
Definition: Thread.cpp:150
#define yCInfo(component,...)
Definition: LogComponent.h:171
#define yCError(component,...)
Definition: LogComponent.h:213
#define yCAssert(component, x)
Definition: LogComponent.h:240
#define yCFatal(component,...)
Definition: LogComponent.h:234
#define YARP_OS_LOG_COMPONENT(name, name_string)
Definition: LogComponent.h:29
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:25
@ YARP_CLOCK_UNINITIALIZED
Definition: Time.h:26
@ YARP_CLOCK_CUSTOM
Definition: Time.h:30
@ YARP_CLOCK_SYSTEM
Definition: Time.h:28
@ YARP_CLOCK_NETWORK
Definition: Time.h:29