Skip to content
Open

Usleep #1597

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion common/http/OlaHTTPServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ int OlaHTTPServer::DisplayDebug(const HTTPRequest*,
m_clock.CurrentTime(&now);
ola::TimeInterval diff = now - m_start_time;
ostringstream str;
str << diff.InMilliSeconds();
str << diff.InMilliseconds();
m_export_map->GetStringVar(K_UPTIME_VAR)->Set(str.str());

vector<BaseVariable*> variables = m_export_map->AllVariables();
Expand Down
2 changes: 1 addition & 1 deletion common/io/EPoller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ bool EPoller::Poll(TimeoutManager *timeout_manager,
(*m_loop_iterations)++;
}

int ms_to_sleep = sleep_interval.InMilliSeconds();
int ms_to_sleep = sleep_interval.InMilliseconds();
int ready = epoll_wait(m_epoll_fd, reinterpret_cast<epoll_event*>(&events),
MAX_EVENTS, ms_to_sleep ? ms_to_sleep : 1);

Expand Down
2 changes: 1 addition & 1 deletion common/io/KQueuePoller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ bool KQueuePoller::Poll(TimeoutManager *timeout_manager,

struct timespec sleep_time;
sleep_time.tv_sec = sleep_interval.Seconds();
sleep_time.tv_nsec = sleep_interval.MicroSeconds() * 1000;
sleep_time.tv_nsec = sleep_interval.Microseconds() * ONE_THOUSAND;

int ready = kevent(
m_kqueue_fd, reinterpret_cast<struct kevent*>(m_change_set),
Expand Down
2 changes: 1 addition & 1 deletion common/io/WindowsPoller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ bool WindowsPoller::Poll(TimeoutManager *timeout_manager,
(*m_loop_iterations)++;
}

int ms_to_sleep = sleep_interval.InMilliSeconds();
int ms_to_sleep = sleep_interval.InMilliseconds();

// Prepare events
vector<HANDLE> events;
Expand Down
2 changes: 1 addition & 1 deletion common/math/Random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void InitRandom() {
TimeStamp now;
clock.CurrentTime(&now);

uint64_t seed = (static_cast<uint64_t>(now.MicroSeconds()) << 32) +
uint64_t seed = (static_cast<uint64_t>(now.Microseconds()) << 32) +
static_cast<uint64_t>(getpid());
#ifdef HAVE_RANDOM
generator_.seed(seed);
Expand Down
2 changes: 1 addition & 1 deletion common/thread/Mutex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ void ConditionVariable::Wait(Mutex *mutex) {
bool ConditionVariable::TimedWait(Mutex *mutex, const TimeStamp &wake_up_time) {
struct timespec ts = {
wake_up_time.Seconds(),
wake_up_time.MicroSeconds() * ONE_THOUSAND
wake_up_time.Microseconds() * ONE_THOUSAND
};
int i = pthread_cond_timedwait(&m_condition, &mutex->m_mutex, &ts);
return i == 0;
Expand Down
107 changes: 96 additions & 11 deletions common/utils/Clock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include <sstream>
#include <string>

#include "ola/Logging.h"

namespace ola {

using std::string;
Expand Down Expand Up @@ -124,13 +126,13 @@ void BaseTimeVal::AsTimeval(struct timeval *tv) const {
*tv = m_tv;
}

int64_t BaseTimeVal::InMilliSeconds() const {
return (m_tv.tv_sec * static_cast<int64_t>(ONE_THOUSAND) +
m_tv.tv_usec / ONE_THOUSAND);
int64_t BaseTimeVal::InMilliseconds() const {
return (m_tv.tv_sec * static_cast<int64_t>(MSECS_IN_SECOND) +
m_tv.tv_usec / MSECS_IN_SECOND);
}

int64_t BaseTimeVal::AsInt() const {
return (m_tv.tv_sec * static_cast<int64_t>(USEC_IN_SECONDS) + m_tv.tv_usec);
return (m_tv.tv_sec * static_cast<int64_t>(USECS_IN_SECOND) + m_tv.tv_usec);
}

string BaseTimeVal::ToString() const {
Expand All @@ -144,9 +146,9 @@ void BaseTimeVal::TimerAdd(const struct timeval &tv1, const struct timeval &tv2,
struct timeval *result) const {
result->tv_sec = tv1.tv_sec + tv2.tv_sec;
result->tv_usec = tv1.tv_usec + tv2.tv_usec;
if (result->tv_usec >= USEC_IN_SECONDS) {
if (result->tv_usec >= USECS_IN_SECOND) {
result->tv_sec++;
result->tv_usec -= USEC_IN_SECONDS;
result->tv_usec -= USECS_IN_SECOND;
}
}

Expand All @@ -156,22 +158,22 @@ void BaseTimeVal::TimerSub(const struct timeval &tv1, const struct timeval &tv2,
result->tv_usec = tv1.tv_usec - tv2.tv_usec;
if (result->tv_usec < 0) {
result->tv_sec--;
result->tv_usec += USEC_IN_SECONDS;
result->tv_usec += USECS_IN_SECOND;
}
}

void BaseTimeVal::Set(int64_t interval_useconds) {
#ifdef HAVE_TIME_T
m_tv.tv_sec = static_cast<time_t>(
interval_useconds / USEC_IN_SECONDS);
interval_useconds / USECS_IN_SECOND);
#else
m_tv.tv_sec = interval_useconds / USEC_IN_SECONDS;
m_tv.tv_sec = interval_useconds / USECS_IN_SECOND;
#endif // HAVE_TIME_T

#ifdef HAVE_SUSECONDS_T
m_tv.tv_usec = static_cast<suseconds_t>(interval_useconds % USEC_IN_SECONDS);
m_tv.tv_usec = static_cast<suseconds_t>(interval_useconds % USECS_IN_SECOND);
#else
m_tv.tv_usec = interval_useconds % USEC_IN_SECONDS;
m_tv.tv_usec = interval_useconds % USECS_IN_SECOND;
#endif // HAVE_SUSECONDS_T
}

Expand Down Expand Up @@ -272,4 +274,87 @@ void MockClock::CurrentTime(TimeStamp *timestamp) const {
*timestamp = tv;
*timestamp += m_offset;
}

Sleep::Sleep(std::string caller) :
m_caller(caller) {
}

/**
* @brief Set wanted granularity for usleep and check it.
* @note does not check at the nanosecond level,
* since internal structures use usecs.
*
* @param wanted wanted/needed granularity in usecs
* @param maxDeviation max deviation in usecs tolerated by calling thread.
*
* @attention the granularity of sleep is highly fluctuating depending on the
* load of the system, a prior GOOD state is no guarantee for future proper
* timing.
*/
bool Sleep::CheckTimeGranularity(uint64_t wanted, uint64_t max_deviation) {
TimeStamp ts1, ts2;
Clock clock;

m_wanted_granularity = wanted;
m_max_granularity_deviation = max_deviation;

timespec t;
t.tv_sec = wanted / USECS_IN_SECOND;
t.tv_nsec = (wanted % USECS_IN_SECOND) * ONE_THOUSAND;

clock.CurrentTime(&ts1);
this->Usleep(1);
clock.CurrentTime(&ts2);
TimeInterval interval = ts2 - ts1;
m_clock_overhead = interval.InMicroseconds() - 1;

clock.CurrentTime(&ts1);
this->Usleep(t);
clock.CurrentTime(&ts2);

interval = ts2 - ts1;
m_granularity = (interval.InMicroseconds() >
(wanted + max_deviation + m_clock_overhead)) ? BAD : GOOD;

OLA_INFO << "Granularity for OlaSleep in " << m_caller << " is "
<< ((m_granularity == GOOD) ? "GOOD" : "BAD")
<< " Requested: " << wanted << " Got: " << interval.InMicroseconds()
<< " Overhead: " << m_clock_overhead;
if (m_granularity == GOOD) {
return true;
}
return false;
}

void Sleep::Usleep(TimeInterval requested) {
timespec req;
req.tv_sec = requested.Seconds();
req.tv_nsec = requested.Microseconds() * ONE_THOUSAND;

this->Usleep(req);
}

void Sleep::Usleep(uint32_t requested) {
timespec req;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be using the existing maths in TimeInterval rather than rewriting that code here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow, we have a TimeIntervalToTimeSpec function somewhere?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(It does make sense to add such a function but I did not see any such thing listed in the header)

req.tv_sec = requested / USECS_IN_SECOND;
req.tv_nsec = (requested % USECS_IN_SECOND) * ONE_THOUSAND;

this->Usleep(req);
}

void Sleep::Usleep(timespec requested) {
timespec rem;

if (nanosleep(&requested, &rem) < 0) {
if (errno == EINTR) {
while (rem.tv_nsec > 0 || rem.tv_sec > 0) {
requested.tv_nsec = rem.tv_nsec;
requested.tv_sec = rem.tv_sec;
nanosleep(&requested, &rem);
}
} else {
OLA_WARN << "nanosleep failed with state: " << errno;
}
}
}
} // namespace ola
10 changes: 5 additions & 5 deletions common/utils/ClockTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ void ClockTest::testTimeStamp() {
OLA_ASSERT_EQ(static_cast<int64_t>(1500000),
one_point_five_seconds.AsInt());
OLA_ASSERT_EQ(static_cast<int64_t>(1500),
one_point_five_seconds.InMilliSeconds());
one_point_five_seconds.InMilliseconds());
}


Expand Down Expand Up @@ -131,16 +131,16 @@ void ClockTest::testTimeInterval() {
void ClockTest::testTimeIntervalMutliplication() {
TimeInterval half_second(500000); // 0.5s
TimeInterval zero_seconds = half_second * 0;
OLA_ASSERT_EQ((int64_t) 0, zero_seconds.InMilliSeconds());
OLA_ASSERT_EQ((int64_t) 0, zero_seconds.InMilliseconds());

TimeInterval another_half_second = half_second * 1;
OLA_ASSERT_EQ((int64_t) 500, another_half_second.InMilliSeconds());
OLA_ASSERT_EQ((int64_t) 500, another_half_second.InMilliseconds());

TimeInterval two_seconds = half_second * 4;
OLA_ASSERT_EQ((int64_t) 2000, two_seconds.InMilliSeconds());
OLA_ASSERT_EQ((int64_t) 2000, two_seconds.InMilliseconds());

TimeInterval twenty_seconds = half_second * 40;
OLA_ASSERT_EQ((int64_t) 20000, twenty_seconds.InMilliSeconds());
OLA_ASSERT_EQ((int64_t) 20000, twenty_seconds.InMilliseconds());
}


Expand Down
4 changes: 2 additions & 2 deletions common/utils/TokenBucket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ bool TokenBucket::GetToken(const TimeStamp &now) {
*/
unsigned int TokenBucket::Count(const TimeStamp &now) {
int64_t delta = (now - m_last).AsInt();
uint64_t tokens = delta * m_rate / USEC_IN_SECONDS;
uint64_t tokens = delta * m_rate / USECS_IN_SECOND;

m_count = std::min(static_cast<uint64_t>(m_max), m_count + tokens);
if (tokens)
m_last += ola::TimeInterval(tokens * USEC_IN_SECONDS / m_rate);
m_last += ola::TimeInterval(tokens * USECS_IN_SECOND / m_rate);
return m_count;
}
} // namespace ola
2 changes: 1 addition & 1 deletion examples/ShowSaver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ bool ShowSaver::NewFrame(const ola::TimeStamp &arrival_time,
// this is not the first frame so write the delay in ms
const ola::TimeInterval delta = arrival_time - m_last_frame;

m_show_file << delta.InMilliSeconds() << endl;
m_show_file << delta.InMilliseconds() << endl;
}
m_last_frame = arrival_time;
m_show_file << universe << " " << data.ToString() << endl;
Expand Down
4 changes: 2 additions & 2 deletions examples/ola-latency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ void Tracker::Start() {
// if you want.
cout << "--------------" << endl;
cout << "Sent " << m_count << " RPCs" << endl;
cout << "Max was " << m_max.MicroSeconds() << " microseconds" << endl;
cout << "Max was " << m_max.Microseconds() << " microseconds" << endl;
cout << "Mean " << m_sum / m_count << " microseconds" << endl;
}

Expand Down Expand Up @@ -130,7 +130,7 @@ void Tracker::LogTime() {
if (delta > m_max) {
m_max = delta;
}
m_sum += delta.MicroSeconds();
m_sum += delta.Microseconds();

OLA_INFO << "RPC took " << delta;
if (FLAGS_count == ++m_count) {
Expand Down
49 changes: 43 additions & 6 deletions include/ola/Clock.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,10 @@

namespace ola {

static const int USEC_IN_SECONDS = 1000000;
static const int ONE_THOUSAND = 1000;
static const int MSECS_IN_SECOND = ONE_THOUSAND;
static const int USECS_IN_SECOND = MSECS_IN_SECOND * ONE_THOUSAND;
static const uint64_t NSECS_IN_SECOND = USECS_IN_SECOND * ONE_THOUSAND;

/**
* Don't use this class directly. It's an implementation detail of TimeInterval
Expand Down Expand Up @@ -90,20 +92,26 @@ class BaseTimeVal {
* @brief Returns the microseconds portion of the BaseTimeVal
* @return The microseconds portion of the BaseTimeVal
*/
int32_t MicroSeconds() const { return static_cast<int32_t>(m_tv.tv_usec); }
int32_t Microseconds() const { return static_cast<int32_t>(m_tv.tv_usec); }

/**
* @brief Returns the entire BaseTimeVal as milliseconds
* @return The entire BaseTimeVal in milliseconds
*/
int64_t InMilliSeconds() const;
int64_t InMilliseconds() const;

/**
* @brief Returns the entire BaseTimeVal as microseconds
* @return The entire BaseTimeVal in microseconds
*/
int64_t AsInt() const;

/**
* @brief InMicroseconds wrapper for AsInt() for name consistency.
* @return The entire BaseTimeVal in microseconds
*/
int64_t InMicroseconds() const { return this->AsInt(); }

std::string ToString() const;

private:
Expand Down Expand Up @@ -157,9 +165,10 @@ class TimeInterval {
void AsTimeval(struct timeval *tv) const { m_interval.AsTimeval(tv); }

time_t Seconds() const { return m_interval.Seconds(); }
int32_t MicroSeconds() const { return m_interval.MicroSeconds(); }
int32_t Microseconds() const { return m_interval.Microseconds(); }

int64_t InMilliSeconds() const { return m_interval.InMilliSeconds(); }
int64_t InMilliseconds() const { return m_interval.InMilliseconds(); }
int64_t InMicroseconds() const { return m_interval.InMicroseconds(); }
int64_t AsInt() const { return m_interval.AsInt(); }

std::string ToString() const { return m_interval.ToString(); }
Expand Down Expand Up @@ -211,7 +220,7 @@ class TimeStamp {
bool IsSet() const { return m_tv.IsSet(); }

time_t Seconds() const { return m_tv.Seconds(); }
int32_t MicroSeconds() const { return m_tv.MicroSeconds(); }
int32_t Microseconds() const { return m_tv.Microseconds(); }

std::string ToString() const { return m_tv.ToString(); }

Expand Down Expand Up @@ -257,5 +266,33 @@ class MockClock: public Clock {
private:
TimeInterval m_offset;
};

enum TimerGranularity { UNKNOWN, GOOD, BAD };

/**
* @brief The Sleep class implements usleep with some granulatiry detection.
*/
class Sleep {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should probably be called GranularSleep or something should we ever have to do something like wrap/override usleep or similar.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole point of this class is to do usleep without the various test systems screaming at us that usleep shouldn't be used.

public:
explicit Sleep(std::string caller);

void SetCaller(std::string caller) { m_caller = caller; }

void Usleep(TimeInterval requested);
void Usleep(uint32_t requested);
void Usleep(timespec requested);

TimerGranularity GetGranularity() { return m_granularity; }
bool CheckTimeGranularity(uint64_t wanted, uint64_t max_deviation);
private:
std::string m_caller;
uint64_t m_wanted_granularity;
uint64_t m_max_granularity_deviation;
uint64_t m_clock_overhead;

static const uint32_t BAD_GRANULARITY_LIMIT = 10;

TimerGranularity m_granularity = UNKNOWN;
};
} // namespace ola
#endif // INCLUDE_OLA_CLOCK_H_
Loading