#include "gtest/gtest.h"
#include <chrono>
#include <thread>
#include <future>
#include <array>
#include <numeric>
#include <regex>
#include "sync.h"
#include "common.h"
using namespace std;
using namespace srt::sync;
TEST(SyncDuration, BasicChecks)
{
const steady_clock::duration d = steady_clock::duration();
EXPECT_EQ(d.count(), 0);
EXPECT_TRUE(d == d); EXPECT_FALSE(d != d); EXPECT_EQ(d, steady_clock::duration::zero());
EXPECT_EQ(d, microseconds_from(0));
EXPECT_EQ(d, milliseconds_from(0));
EXPECT_EQ(d, seconds_from(0));
EXPECT_EQ(count_milliseconds(d), 0);
EXPECT_EQ(count_microseconds(d), 0);
EXPECT_EQ(count_seconds(d), 0);
const steady_clock::duration a = d + milliseconds_from(120);
EXPECT_EQ(a, milliseconds_from(120));
EXPECT_EQ(count_milliseconds(a), 120);
EXPECT_EQ(count_microseconds(a), 120000);
EXPECT_EQ(count_seconds(a), 0);
}
TEST(SyncDuration, DurationFrom)
{
const int64_t val = int64_t(numeric_limits<uint32_t>::max()) + 1;
const steady_clock::duration us_from = microseconds_from(val);
EXPECT_EQ(count_microseconds(us_from), val);
const steady_clock::duration ms_from = milliseconds_from(val);
EXPECT_EQ(count_milliseconds(ms_from), val);
const steady_clock::duration s_from = seconds_from(val);
EXPECT_EQ(count_seconds(s_from), val);
}
TEST(SyncDuration, RelOperators)
{
const steady_clock::duration a = steady_clock::duration();
EXPECT_EQ(a.count(), 0);
EXPECT_TRUE(a == a); EXPECT_FALSE(a != a); EXPECT_FALSE(a > a); EXPECT_FALSE(a < a); EXPECT_TRUE(a <= a); EXPECT_TRUE(a >= a);
const steady_clock::duration b = a + milliseconds_from(120);
EXPECT_FALSE(b == a); EXPECT_TRUE(b != a); EXPECT_TRUE(b > a); EXPECT_FALSE(a > b); EXPECT_FALSE(b < a); EXPECT_TRUE(a < b); EXPECT_FALSE(b <= a); EXPECT_TRUE(a <= b); EXPECT_TRUE(b >= a); EXPECT_FALSE(a >= b);
const steady_clock::duration c = steady_clock::duration(numeric_limits<int64_t>::max());
EXPECT_EQ(c.count(), numeric_limits<int64_t>::max());
const steady_clock::duration d = steady_clock::duration(numeric_limits<int64_t>::min());
EXPECT_EQ(d.count(), numeric_limits<int64_t>::min());
}
TEST(SyncDuration, OperatorMinus)
{
const steady_clock::duration a = seconds_from(5);
const steady_clock::duration b = milliseconds_from(3500);
EXPECT_EQ(count_milliseconds(a - b), 1500);
EXPECT_EQ(count_milliseconds(b - a), -1500);
EXPECT_EQ((a - a).count(), 0);
}
TEST(SyncDuration, OperatorMinusEq)
{
const steady_clock::duration a = seconds_from(5);
const steady_clock::duration b = milliseconds_from(3500);
steady_clock::duration c = a;
EXPECT_EQ(c, a);
c -= b;
EXPECT_EQ(count_milliseconds(c), 1500);
c = b;
EXPECT_EQ(c, b);
c -= a;
EXPECT_EQ(count_milliseconds(c), -1500);
}
TEST(SyncDuration, OperatorPlus)
{
const steady_clock::duration a = seconds_from(5);
const steady_clock::duration b = milliseconds_from(3500);
EXPECT_EQ(count_milliseconds(a + b), 8500);
EXPECT_EQ(count_milliseconds(b + a), 8500);
}
TEST(SyncDuration, OperatorPlusEq)
{
const steady_clock::duration a = seconds_from(5);
const steady_clock::duration b = milliseconds_from(3500);
steady_clock::duration c = a;
EXPECT_EQ(c, a);
c += b;
EXPECT_EQ(count_milliseconds(c), 8500);
c = b;
EXPECT_EQ(c, b);
c += a;
EXPECT_EQ(count_milliseconds(c), 8500);
}
TEST(SyncDuration, OperatorMultInt)
{
const steady_clock::duration a = milliseconds_from(3500);
EXPECT_EQ(count_milliseconds(a), 3500);
EXPECT_EQ(count_milliseconds(a * 2), 7000);
}
TEST(SyncDuration, OperatorMultIntEq)
{
steady_clock::duration a = milliseconds_from(3500);
EXPECT_EQ(count_milliseconds(a), 3500);
a *= 2;
EXPECT_EQ(count_milliseconds(a), 7000);
}
TEST(SyncTimePoint, DefaultConstructorZero)
{
steady_clock::time_point a;
EXPECT_TRUE(is_zero(a));
}
TEST(SyncTimePoint, RelOperators)
{
const steady_clock::time_point a(steady_clock::time_point::max());
const steady_clock::time_point b(steady_clock::time_point::min());
EXPECT_TRUE(a == a);
EXPECT_FALSE(a == b);
EXPECT_TRUE(a != b);
EXPECT_TRUE(a != b);
EXPECT_TRUE(a >= a);
EXPECT_FALSE(b >= a);
EXPECT_TRUE(a > b);
EXPECT_FALSE(a > a);
EXPECT_TRUE(a <= a);
EXPECT_TRUE(b <= a);
EXPECT_FALSE(a <= b);
EXPECT_FALSE(a < a);
EXPECT_TRUE(b < a);
EXPECT_FALSE(a < b);
}
#ifndef ENABLE_STDCXX_SYNC
TEST(SyncTimePoint, OperatorMinus)
{
const int64_t delta = 1024;
const steady_clock::time_point a(numeric_limits<uint64_t>::max());
const steady_clock::time_point b(numeric_limits<uint64_t>::max() - delta);
EXPECT_EQ((a - b).count(), delta);
EXPECT_EQ((b - a).count(), -delta);
}
TEST(SyncTimePoint, OperatorEq)
{
const int64_t delta = 1024;
const steady_clock::time_point a(numeric_limits<uint64_t>::max() - delta);
const steady_clock::time_point b = a;
EXPECT_EQ(a, b);
}
TEST(SyncTimePoint, OperatorMinusPlusDuration)
{
const int64_t delta = 1024;
const steady_clock::time_point a(numeric_limits<uint64_t>::max());
const steady_clock::time_point b(numeric_limits<uint64_t>::max() - delta);
EXPECT_EQ((a + steady_clock::duration(-delta)), b);
EXPECT_EQ((b + steady_clock::duration(+delta)), a);
EXPECT_EQ((a - steady_clock::duration(+delta)), b);
EXPECT_EQ((b - steady_clock::duration(-delta)), a);
}
TEST(SyncTimePoint, OperatorPlusEqDuration)
{
const int64_t delta = 1024;
const steady_clock::time_point a(numeric_limits<uint64_t>::max());
const steady_clock::time_point b(numeric_limits<uint64_t>::max() - delta);
steady_clock::time_point r = a;
EXPECT_EQ(r, a);
r += steady_clock::duration(-delta);
EXPECT_EQ(r, b);
r = b;
EXPECT_EQ(r, b);
r += steady_clock::duration(+delta);
EXPECT_EQ(r, a);
r = a;
EXPECT_EQ(r, a);
r -= steady_clock::duration(+delta);
EXPECT_EQ((a - steady_clock::duration(+delta)), b);
EXPECT_EQ((b - steady_clock::duration(-delta)), a);
}
TEST(SyncTimePoint, OperatorMinusEqDuration)
{
const int64_t delta = 1024;
const steady_clock::time_point a(numeric_limits<uint64_t>::max());
const steady_clock::time_point b(numeric_limits<uint64_t>::max() - delta);
steady_clock::time_point r = a;
EXPECT_EQ(r, a);
r -= steady_clock::duration(+delta);
EXPECT_EQ(r, b);
r = b;
EXPECT_EQ(r, b);
r -= steady_clock::duration(-delta);
EXPECT_EQ(r, a);
}
#endif
TEST(SyncEvent, WaitFor)
{
Mutex mutex;
Condition cond;
cond.init();
for (int timeout_us : {50, 100, 500, 1000, 101000, 1001000})
{
const steady_clock::duration timeout = microseconds_from(timeout_us);
UniqueLock lock(mutex);
const steady_clock::time_point start = steady_clock::now();
const bool on_timeout = !cond.wait_for(lock, timeout);
const steady_clock::time_point stop = steady_clock::now();
const steady_clock::duration waittime = stop - start;
const int64_t waittime_us = count_microseconds(waittime);
#if defined(ENABLE_STDCXX_SYNC) || !defined(_WIN32)
if (on_timeout) {
EXPECT_GE(waittime_us, timeout_us);
}
#endif
if (on_timeout) {
EXPECT_LE(waittime_us, 10 * 1001000); }
string spurious = on_timeout ? "" : " (SPURIOUS)";
if (timeout_us < 1000)
{
cerr << "SyncEvent::wait_for(" << timeout_us << "us) took "
<< waittime_us << "us" << spurious << endl;
}
else
{
cerr << "SyncEvent::wait_for(" << count_milliseconds(timeout) << " ms) took "
<< (waittime_us / 1000.0) << " ms" << spurious << endl;
}
}
cond.destroy();
}
TEST(SyncEvent, WaitForNotifyOne)
{
Mutex mutex;
Condition cond;
cond.init();
const steady_clock::duration timeout = seconds_from(5);
auto wait_async = [](Condition* cond, Mutex* mutex, const steady_clock::duration& timeout) {
UniqueLock lock(*mutex);
return cond->wait_for(lock, timeout);
};
auto wait_async_res = async(launch::async, wait_async, &cond, &mutex, timeout);
EXPECT_EQ(wait_async_res.wait_for(chrono::milliseconds(100)), future_status::timeout);
cond.notify_one();
ASSERT_EQ(wait_async_res.wait_for(chrono::milliseconds(100)), future_status::ready);
const bool wait_for_res = wait_async_res.get();
EXPECT_TRUE(wait_for_res) << "Woken up by a notification";
cond.destroy();
}
TEST(SyncEvent, WaitNotifyOne)
{
Mutex mutex;
Condition cond;
cond.init();
auto wait_async = [](Condition* cond, Mutex* mutex) {
UniqueLock lock(*mutex);
return cond->wait(lock);
};
auto wait_async_res = async(launch::async, wait_async, &cond, &mutex);
EXPECT_EQ(wait_async_res.wait_for(chrono::milliseconds(100)), future_status::timeout);
cond.notify_one();
ASSERT_EQ(wait_async_res.wait_for(chrono::milliseconds(100)), future_status::ready);
wait_async_res.get();
cond.destroy();
}
TEST(SyncEvent, WaitForTwoNotifyOne)
{
Mutex mutex;
Condition cond;
vector<int> notified_clients;
cond.init();
const steady_clock::duration timeout = seconds_from(3);
const int VAL_SIGNAL = 42;
const int VAL_NO_SIGNAL = 0;
volatile bool resource_ready = true;
auto wait_async = [&](Condition* cond, Mutex* mutex, const steady_clock::duration& timeout, int id) {
UniqueLock lock(*mutex);
if (cond->wait_for(lock, timeout) && resource_ready)
{
notified_clients.push_back(id);
resource_ready = false;
return VAL_SIGNAL;
}
return VAL_NO_SIGNAL;
};
using future_t = decltype(async(launch::async, wait_async, &cond, &mutex, timeout, 0));
future_t future_result[2] = {
async(launch::async, wait_async, &cond, &mutex, timeout, 0),
async(launch::async, wait_async, &cond, &mutex, timeout, 1)
};
for (auto& wr: future_result)
{
ASSERT_EQ(wr.wait_for(chrono::milliseconds(100)), future_status::timeout);
}
{
ScopedLock lk(mutex);
cond.notify_one();
}
using wait_t = decltype(future_t().wait_for(chrono::microseconds(0)));
wait_t wait_state[2] = {
move(future_result[0].wait_for(chrono::microseconds(100))),
move(future_result[1].wait_for(chrono::microseconds(100)))
};
cerr << "SyncEvent::WaitForTwoNotifyOne: NOTIFICATION came from " << notified_clients.size()
<< " clients:";
for (auto& nof: notified_clients)
cerr << " " << nof;
cerr << endl;
ASSERT_EQ(notified_clients.size(), 1);
const int ready = notified_clients[0];
const int not_ready = (ready + 1) % 2;
int future_val[2];
ASSERT_TRUE(future_result[ready].valid());
future_val[ready] = future_result[ready].get();
if (future_result[not_ready].valid())
{
future_val[not_ready] = future_result[not_ready].get();
}
else
{
future_val[not_ready] = VAL_NO_SIGNAL-1; }
string disp_future[16];
disp_future[int(future_status::timeout)] = "timeout";
disp_future[int(future_status::ready)] = "ready";
cerr << "SyncEvent::WaitForTwoNotifyOne: READY THREAD: " << ready
<< " STATUS " << disp_future[int(wait_state[ready])]
<< " RESULT " << future_val[ready] << endl;
cerr << "SyncEvent::WaitForTwoNotifyOne: TMOUT THREAD: " << not_ready
<< " STATUS " << disp_future[int(wait_state[not_ready])]
<< " RESULT " << future_val[not_ready] << endl;
EXPECT_EQ(wait_state[ready], future_status::ready);
EXPECT_EQ(wait_state[not_ready], future_status::timeout);
EXPECT_EQ(future_val[ready], VAL_SIGNAL);
EXPECT_LE(future_val[not_ready], VAL_NO_SIGNAL);
cond.destroy();
}
TEST(SyncEvent, WaitForTwoNotifyAll)
{
Mutex mutex;
Condition cond;
cond.init();
const steady_clock::duration timeout = seconds_from(3);
auto wait_async = [](Condition* cond, Mutex* mutex, const steady_clock::duration& timeout) {
UniqueLock lock(*mutex);
return cond->wait_for(lock, timeout);
};
auto wait_async1_res = async(launch::async, wait_async, &cond, &mutex, timeout);
auto wait_async2_res = async(launch::async, wait_async, &cond, &mutex, timeout);
EXPECT_EQ(wait_async1_res.wait_for(chrono::milliseconds(100)), future_status::timeout);
EXPECT_EQ(wait_async2_res.wait_for(chrono::milliseconds(100)), future_status::timeout);
cond.notify_all();
const future_status status1 = wait_async1_res.wait_for(chrono::milliseconds(100));
const future_status status2 = wait_async2_res.wait_for(chrono::milliseconds(100));
EXPECT_EQ(status1, future_status::ready);
EXPECT_EQ(status2, future_status::ready);
EXPECT_TRUE(wait_async1_res.get());
EXPECT_TRUE(wait_async2_res.get());
cond.destroy();
}
TEST(SyncEvent, WaitForNotifyAll)
{
Mutex mutex;
Condition cond;
cond.init();
const steady_clock::duration timeout = seconds_from(5);
auto wait_async = [](Condition* cond, Mutex* mutex, const steady_clock::duration& timeout) {
UniqueLock lock(*mutex);
return cond->wait_for(lock, timeout);
};
auto wait_async_res = async(launch::async, wait_async, &cond, &mutex, timeout);
EXPECT_EQ(wait_async_res.wait_for(chrono::milliseconds(500)), future_status::timeout);
cond.notify_all();
ASSERT_EQ(wait_async_res.wait_for(chrono::milliseconds(500)), future_status::ready);
const bool wait_for_res = wait_async_res.get();
EXPECT_TRUE(wait_for_res) << "Woken up by condition";
cond.destroy();
}
#if !defined(__GNUC__) || defined(__clang__) || (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))
TEST(Sync, FormatTime)
{
auto parse_time = [](const string& timestr) -> long long {
const regex rex("([[:digit:]]+D )?([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6}) \\[STD\\]");
std::smatch sm;
EXPECT_TRUE(regex_match(timestr, sm, rex));
EXPECT_LE(sm.size(), 6);
if (sm.size() != 6 && sm.size() != 5)
return 0;
const long long d = sm[1].matched ? std::stoi(sm[1]) : 0;
const long long h = std::stoi(sm[2]);
const long long m = std::stoi(sm[3]);
const long long s = std::stoi(sm[4]);
const long long u = std::stoi(sm[5]);
return u + s * 1000000 + m * 60000000 + h * 60 * 60 * 1000000 + d * 24 * 60 * 60 * 1000000;
};
auto print_timediff = [&parse_time](const string& desc, const string& time, const string& time_base) {
const long long diff = parse_time(time) - parse_time(time_base);
cerr << desc << time << " (" << diff << " us)" << endl;
};
const auto a = steady_clock::now();
const string time1 = FormatTime(a);
const string time2 = FormatTime(a);
const string time3 = FormatTime(a + milliseconds_from(500));
const string time4 = FormatTime(a + seconds_from(1));
const string time5 = FormatTime(a + seconds_from(5));
const string time6 = FormatTime(a + milliseconds_from(-4350));
cerr << "Current time formated: " << time1 << endl;
const long long diff_2_1 = parse_time(time2) - parse_time(time1);
cerr << "Same time formated again: " << time2 << " (" << diff_2_1 << " us)" << endl;
print_timediff("Same time formated again: ", time2, time1);
print_timediff("Time +500 ms formated: ", time3, time1);
print_timediff("Time +1 sec formated: ", time4, time1);
print_timediff("Time +5 sec formated: ", time5, time1);
print_timediff("Time -4350 ms formated: ", time6, time1);
EXPECT_TRUE(time1 == time2);
}
TEST(Sync, FormatTimeSys)
{
auto parse_time = [](const string& timestr) -> long long {
const regex rex("([[:digit:]]{2}):([[:digit:]]{2}):([[:digit:]]{2}).([[:digit:]]{6}) \\[SYS\\]");
std::smatch sm;
EXPECT_TRUE(regex_match(timestr, sm, rex));
EXPECT_EQ(sm.size(), 5);
if (sm.size() != 5)
return 0;
const long long h = std::stoi(sm[1]);
const long long m = std::stoi(sm[2]);
const long long s = std::stoi(sm[3]);
const long long u = std::stoi(sm[4]);
return u + s * 1000000 + m * 60000000 + h * 60 * 60 * 1000000;
};
auto print_timediff = [&parse_time](const string& desc, const string& time, const string& time_base) {
const long long diff = parse_time(time) - parse_time(time_base);
cerr << desc << time << " (" << diff << " us)" << endl;
};
const steady_clock::time_point a = steady_clock::now();
const string time1 = FormatTimeSys(a);
const string time2 = FormatTimeSys(a);
const string time3 = FormatTimeSys(a + milliseconds_from(500));
const string time4 = FormatTimeSys(a + seconds_from(1));
const string time5 = FormatTimeSys(a + seconds_from(5));
const string time6 = FormatTimeSys(a + milliseconds_from(-4350));
cerr << "Current time formated: " << time1 << endl;
const long long diff_2_1 = parse_time(time2) - parse_time(time1);
cerr << "Same time formated again: " << time2 << " (" << diff_2_1 << " us)" << endl;
print_timediff("Same time formated again: ", time2, time1);
print_timediff("Time +500 ms formated: ", time3, time1);
print_timediff("Time +1 sec formated: ", time4, time1);
print_timediff("Time +5 sec formated: ", time5, time1);
print_timediff("Time -4350 ms formated: ", time6, time1);
EXPECT_TRUE(time1 == time2);
}
#endif