Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,299 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(ConditionVariable, WaitForAfterNofityOnSameThread) {
for (int n = 0; n < 10; ++n) {
Mutex mutex;
ConditionVariable cv;
MutexGuard lock_guard(&mutex);
cv.NotifyOne();
EXPECT_FALSE(cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
cv.NotifyAll();
EXPECT_FALSE(cv.WaitFor(&mutex, TimeDelta::FromMicroseconds(n)));
}
}
namespace {
class ThreadWithMutexAndConditionVariable final : public Thread {
public:
ThreadWithMutexAndConditionVariable()
: Thread(Options("ThreadWithMutexAndConditionVariable")),
running_(false),
finished_(false) {}
void Run() override {
MutexGuard lock_guard(&mutex_);
running_ = true;
cv_.NotifyOne();
while (running_) {
cv_.Wait(&mutex_);
}
finished_ = true;
cv_.NotifyAll();
}
bool running_;
bool finished_;
ConditionVariable cv_;
Mutex mutex_;
};
} // namespace
TEST(ConditionVariable, MultipleThreadsWithSeparateConditionVariables) {
static const int kThreadCount = 128;
ThreadWithMutexAndConditionVariable threads[kThreadCount];
for (int n = 0; n < kThreadCount; ++n) {
MutexGuard lock_guard(&threads[n].mutex_);
EXPECT_FALSE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
CHECK(threads[n].Start());
// Wait for nth thread to start.
while (!threads[n].running_) {
threads[n].cv_.Wait(&threads[n].mutex_);
}
}
for (int n = kThreadCount - 1; n >= 0; --n) {
MutexGuard lock_guard(&threads[n].mutex_);
EXPECT_TRUE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
}
for (int n = 0; n < kThreadCount; ++n) {
MutexGuard lock_guard(&threads[n].mutex_);
EXPECT_TRUE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
// Tell the nth thread to quit.
threads[n].running_ = false;
threads[n].cv_.NotifyOne();
}
for (int n = kThreadCount - 1; n >= 0; --n) {
// Wait for nth thread to quit.
MutexGuard lock_guard(&threads[n].mutex_);
while (!threads[n].finished_) {
threads[n].cv_.Wait(&threads[n].mutex_);
}
EXPECT_FALSE(threads[n].running_);
EXPECT_TRUE(threads[n].finished_);
}
for (int n = 0; n < kThreadCount; ++n) {
threads[n].Join();
MutexGuard lock_guard(&threads[n].mutex_);
EXPECT_FALSE(threads[n].running_);
EXPECT_TRUE(threads[n].finished_);
}
}
namespace {
class ThreadWithSharedMutexAndConditionVariable final : public Thread {
public:
ThreadWithSharedMutexAndConditionVariable()
: Thread(Options("ThreadWithSharedMutexAndConditionVariable")),
running_(false),
finished_(false),
cv_(nullptr),
mutex_(nullptr) {}
void Run() override {
MutexGuard lock_guard(mutex_);
running_ = true;
cv_->NotifyAll();
while (running_) {
cv_->Wait(mutex_);
}
finished_ = true;
cv_->NotifyAll();
}
bool running_;
bool finished_;
ConditionVariable* cv_;
Mutex* mutex_;
};
} // namespace
TEST(ConditionVariable, MultipleThreadsWithSharedSeparateConditionVariables) {
static const int kThreadCount = 128;
ThreadWithSharedMutexAndConditionVariable threads[kThreadCount];
ConditionVariable cv;
Mutex mutex;
for (int n = 0; n < kThreadCount; ++n) {
threads[n].mutex_ = &mutex;
threads[n].cv_ = &cv;
}
// Start all threads.
{
MutexGuard lock_guard(&mutex);
for (int n = 0; n < kThreadCount; ++n) {
EXPECT_FALSE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
CHECK(threads[n].Start());
}
}
// Wait for all threads to start.
{
MutexGuard lock_guard(&mutex);
for (int n = kThreadCount - 1; n >= 0; --n) {
while (!threads[n].running_) {
cv.Wait(&mutex);
}
}
}
// Make sure that all threads are running.
{
MutexGuard lock_guard(&mutex);
for (int n = 0; n < kThreadCount; ++n) {
EXPECT_TRUE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
}
}
// Tell all threads to quit.
{
MutexGuard lock_guard(&mutex);
for (int n = kThreadCount - 1; n >= 0; --n) {
EXPECT_TRUE(threads[n].running_);
EXPECT_FALSE(threads[n].finished_);
// Tell the nth thread to quit.
threads[n].running_ = false;
}
cv.NotifyAll();
}
// Wait for all threads to quit.
{
MutexGuard lock_guard(&mutex);
for (int n = 0; n < kThreadCount; ++n) {
while (!threads[n].finished_) {
cv.Wait(&mutex);
}
}
}
// Make sure all threads are finished.
{
MutexGuard lock_guard(&mutex);
for (int n = kThreadCount - 1; n >= 0; --n) {
EXPECT_FALSE(threads[n].running_);
EXPECT_TRUE(threads[n].finished_);
}
}
// Join all threads.
for (int n = 0; n < kThreadCount; ++n) {
threads[n].Join();
}
}
namespace {
class LoopIncrementThread final : public Thread {
public:
LoopIncrementThread(int rem, int* counter, int limit, int thread_count,
ConditionVariable* cv, Mutex* mutex)
: Thread(Options("LoopIncrementThread")),
rem_(rem),
counter_(counter),
limit_(limit),
thread_count_(thread_count),
cv_(cv),
mutex_(mutex) {
EXPECT_LT(rem, thread_count);
EXPECT_EQ(0, limit % thread_count);
}
void Run() override {
int last_count = -1;
while (true) {
MutexGuard lock_guard(mutex_);
int count = *counter_;
while (count % thread_count_ != rem_ && count < limit_) {
cv_->Wait(mutex_);
count = *counter_;
}
if (count >= limit_) break;
EXPECT_EQ(*counter_, count);
if (last_count != -1) {
EXPECT_EQ(last_count + (thread_count_ - 1), count);
}
count++;
*counter_ = count;
last_count = count;
cv_->NotifyAll();
}
}
private:
const int rem_;
int* counter_;
const int limit_;
const int thread_count_;
ConditionVariable* cv_;
Mutex* mutex_;
};
} // namespace
TEST(ConditionVariable, LoopIncrement) {
static const int kMaxThreadCount = 16;
Mutex mutex;
ConditionVariable cv;
for (int thread_count = 1; thread_count < kMaxThreadCount; ++thread_count) {
int limit = thread_count * 10;
int counter = 0;
// Setup the threads.
Thread** threads = new Thread* [thread_count];
for (int n = 0; n < thread_count; ++n) {
threads[n] = new LoopIncrementThread(n, &counter, limit, thread_count,
&cv, &mutex);
}
// Start all threads.
for (int n = thread_count - 1; n >= 0; --n) {
CHECK(threads[n]->Start());
}
// Join and cleanup all threads.
for (int n = 0; n < thread_count; ++n) {
threads[n]->Join();
delete threads[n];
}
delete[] threads;
EXPECT_EQ(limit, counter);
}
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,100 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/platform/mutex.h"
#include <chrono> // NOLINT(build/c++11)
#include <queue>
#include <thread> // NOLINT(build/c++11)
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/platform.h"
#include "src/base/utils/random-number-generator.h"
#include "test/unittests/fuzztest.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(Mutex, LockGuardMutex) {
Mutex mutex;
{ MutexGuard lock_guard(&mutex); }
{ MutexGuard lock_guard(&mutex); }
}
TEST(Mutex, LockGuardRecursiveMutex) {
RecursiveMutex recursive_mutex;
{ LockGuard<RecursiveMutex> lock_guard(&recursive_mutex); }
{
LockGuard<RecursiveMutex> lock_guard1(&recursive_mutex);
LockGuard<RecursiveMutex> lock_guard2(&recursive_mutex);
}
}
TEST(Mutex, LockGuardLazyMutex) {
LazyMutex lazy_mutex = LAZY_MUTEX_INITIALIZER;
{ MutexGuard lock_guard(lazy_mutex.Pointer()); }
{ MutexGuard lock_guard(lazy_mutex.Pointer()); }
}
TEST(Mutex, LockGuardLazyRecursiveMutex) {
LazyRecursiveMutex lazy_recursive_mutex = LAZY_RECURSIVE_MUTEX_INITIALIZER;
{ LockGuard<RecursiveMutex> lock_guard(lazy_recursive_mutex.Pointer()); }
{
LockGuard<RecursiveMutex> lock_guard1(lazy_recursive_mutex.Pointer());
LockGuard<RecursiveMutex> lock_guard2(lazy_recursive_mutex.Pointer());
}
}
TEST(Mutex, MultipleMutexes) {
Mutex mutex1;
Mutex mutex2;
Mutex mutex3;
// Order 1
mutex1.Lock();
mutex2.Lock();
mutex3.Lock();
mutex1.Unlock();
mutex2.Unlock();
mutex3.Unlock();
// Order 2
mutex1.Lock();
mutex2.Lock();
mutex3.Lock();
mutex3.Unlock();
mutex2.Unlock();
mutex1.Unlock();
}
TEST(Mutex, MultipleRecursiveMutexes) {
RecursiveMutex recursive_mutex1;
RecursiveMutex recursive_mutex2;
// Order 1
recursive_mutex1.Lock();
recursive_mutex2.Lock();
EXPECT_TRUE(recursive_mutex1.TryLock());
EXPECT_TRUE(recursive_mutex2.TryLock());
recursive_mutex1.Unlock();
recursive_mutex1.Unlock();
recursive_mutex2.Unlock();
recursive_mutex2.Unlock();
// Order 2
recursive_mutex1.Lock();
EXPECT_TRUE(recursive_mutex1.TryLock());
recursive_mutex2.Lock();
EXPECT_TRUE(recursive_mutex2.TryLock());
recursive_mutex2.Unlock();
recursive_mutex1.Unlock();
recursive_mutex2.Unlock();
recursive_mutex1.Unlock();
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,318 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/platform/platform.h"
#include <cstdio>
#include <cstring>
#include "include/v8-function.h"
#include "src/base/build_config.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#ifdef V8_TARGET_OS_LINUX
#include <sys/sysmacros.h>
#include "src/base/platform/platform-linux.h"
#endif
#ifdef V8_OS_WIN
#include <windows.h>
#endif
namespace v8 {
namespace base {
#ifdef V8_TARGET_OS_WIN
// Alignment is constrained on Windows.
constexpr size_t kMaxPageSize = 4096;
#elif V8_HOST_ARCH_PPC64
#if defined(_AIX)
// gcc might complain about overalignment (bug):
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=89357
constexpr size_t kMaxPageSize = 4096;
#else
// Native PPC linux has large (64KB) physical pages.
constexpr size_t kMaxPageSize = 65536;
#endif
#else
constexpr size_t kMaxPageSize = 16384;
#endif
alignas(kMaxPageSize) const char kArray[kMaxPageSize] =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod "
"tempor incididunt ut labore et dolore magna aliqua.";
TEST(OS, GetCurrentProcessId) {
#ifdef V8_OS_POSIX
EXPECT_EQ(static_cast<int>(getpid()), OS::GetCurrentProcessId());
#endif
#ifdef V8_OS_WIN
EXPECT_EQ(static_cast<int>(::GetCurrentProcessId()),
OS::GetCurrentProcessId());
#endif
}
TEST(OS, RemapPages) {
if constexpr (OS::IsRemapPageSupported()) {
const size_t size = base::OS::AllocatePageSize();
ASSERT_TRUE(size <= kMaxPageSize);
const void* data = static_cast<const void*>(kArray);
// Target mapping.
void* remapped_data =
OS::Allocate(nullptr, size, base::OS::AllocatePageSize(),
OS::MemoryPermission::kReadWrite);
ASSERT_TRUE(remapped_data);
EXPECT_TRUE(OS::RemapPages(data, size, remapped_data,
OS::MemoryPermission::kReadExecute));
EXPECT_EQ(0, memcmp(remapped_data, data, size));
OS::Free(remapped_data, size);
}
}
#ifdef V8_TARGET_OS_LINUX
TEST(OS, ParseProcMaps) {
// Truncated
std::string line = "00000000-12345678 r--p";
EXPECT_FALSE(MemoryRegion::FromMapsLine(line.c_str()));
// Constants below are for 64 bit architectures.
#ifdef V8_TARGET_ARCH_64_BIT
// File-backed.
line =
"7f861d1e3000-7f861d33b000 r-xp 00026000 fe:01 12583839 "
" /lib/x86_64-linux-gnu/libc-2.33.so";
auto region = MemoryRegion::FromMapsLine(line.c_str());
EXPECT_TRUE(region);
EXPECT_EQ(region->start, 0x7f861d1e3000u);
EXPECT_EQ(region->end, 0x7f861d33b000u);
EXPECT_EQ(std::string(region->permissions), std::string("r-xp"));
EXPECT_EQ(region->offset, 0x00026000u);
EXPECT_EQ(region->dev, makedev(0xfe, 0x01));
EXPECT_EQ(region->inode, 12583839u);
EXPECT_EQ(region->pathname,
std::string("/lib/x86_64-linux-gnu/libc-2.33.so"));
// Large device numbers. (The major device number 0x103 is from a real
// system, the minor device number 0x104 is synthetic.)
line =
"556bea200000-556beaa1c000 r--p 00000000 103:104 22 "
" /usr/local/bin/node";
region = MemoryRegion::FromMapsLine(line.c_str());
EXPECT_EQ(region->start, 0x556bea200000u);
EXPECT_EQ(region->end, 0x556beaa1c000u);
EXPECT_EQ(std::string(region->permissions), std::string("r--p"));
EXPECT_EQ(region->offset, 0x00000000);
EXPECT_EQ(region->dev, makedev(0x103, 0x104));
EXPECT_EQ(region->inode, 22u);
EXPECT_EQ(region->pathname, std::string("/usr/local/bin/node"));
// Anonymous, but named.
line =
"5611cc7eb000-5611cc80c000 rw-p 00000000 00:00 0 "
" [heap]";
region = MemoryRegion::FromMapsLine(line.c_str());
EXPECT_TRUE(region);
EXPECT_EQ(region->start, 0x5611cc7eb000u);
EXPECT_EQ(region->end, 0x5611cc80c000u);
EXPECT_EQ(std::string(region->permissions), std::string("rw-p"));
EXPECT_EQ(region->offset, 0u);
EXPECT_EQ(region->dev, makedev(0x0, 0x0));
EXPECT_EQ(region->inode, 0u);
EXPECT_EQ(region->pathname, std::string("[heap]"));
// Anonymous, not named.
line = "5611cc7eb000-5611cc80c000 rw-p 00000000 00:00 0";
region = MemoryRegion::FromMapsLine(line.c_str());
EXPECT_TRUE(region);
EXPECT_EQ(region->start, 0x5611cc7eb000u);
EXPECT_EQ(region->end, 0x5611cc80c000u);
EXPECT_EQ(std::string(region->permissions), std::string("rw-p"));
EXPECT_EQ(region->offset, 0u);
EXPECT_EQ(region->dev, makedev(0x0, 0x0));
EXPECT_EQ(region->inode, 0u);
EXPECT_EQ(region->pathname, std::string(""));
#endif // V8_TARGET_ARCH_64_BIT
}
TEST(OS, GetSharedLibraryAddresses) {
FILE* fp = tmpfile();
ASSERT_TRUE(fp);
const char* contents =
R"EOF(12340000-12345000 r-xp 00026000 fe:01 12583839 /lib/x86_64-linux-gnu/libc-2.33.so
12365000-12376000 rw-p 00000000 00:00 0 [heap]
12430000-12435000 r-xp 00062000 fe:01 12583839 /path/to/SomeApplication.apk
)EOF";
size_t length = strlen(contents);
ASSERT_EQ(fwrite(contents, 1, length, fp), length);
rewind(fp);
auto shared_library_addresses = GetSharedLibraryAddresses(fp);
EXPECT_EQ(shared_library_addresses.size(), 2u);
EXPECT_EQ(shared_library_addresses[0].library_path,
"/lib/x86_64-linux-gnu/libc-2.33.so");
EXPECT_EQ(shared_library_addresses[0].start, 0x12340000u - 0x26000);
EXPECT_EQ(shared_library_addresses[1].library_path,
"/path/to/SomeApplication.apk");
#if defined(V8_OS_ANDROID)
EXPECT_EQ(shared_library_addresses[1].start, 0x12430000u);
#else
EXPECT_EQ(shared_library_addresses[1].start, 0x12430000u - 0x62000);
#endif
}
#endif // V8_TARGET_OS_LINUX
namespace {
class ThreadLocalStorageTest : public Thread, public ::testing::Test {
public:
ThreadLocalStorageTest() : Thread(Options("ThreadLocalStorageTest")) {
for (size_t i = 0; i < arraysize(keys_); ++i) {
keys_[i] = Thread::CreateThreadLocalKey();
}
}
~ThreadLocalStorageTest() override {
for (size_t i = 0; i < arraysize(keys_); ++i) {
Thread::DeleteThreadLocalKey(keys_[i]);
}
}
void Run() final {
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK(!Thread::HasThreadLocal(keys_[i]));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
Thread::SetThreadLocal(keys_[i], GetValue(i));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK(Thread::HasThreadLocal(keys_[i]));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK_EQ(GetValue(i), Thread::GetThreadLocal(keys_[i]));
CHECK_EQ(GetValue(i), Thread::GetExistingThreadLocal(keys_[i]));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
Thread::SetThreadLocal(keys_[i], GetValue(arraysize(keys_) - i - 1));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK(Thread::HasThreadLocal(keys_[i]));
}
for (size_t i = 0; i < arraysize(keys_); i++) {
CHECK_EQ(GetValue(arraysize(keys_) - i - 1),
Thread::GetThreadLocal(keys_[i]));
CHECK_EQ(GetValue(arraysize(keys_) - i - 1),
Thread::GetExistingThreadLocal(keys_[i]));
}
}
private:
static void* GetValue(size_t x) { return reinterpret_cast<void*>(x + 1); }
// Older versions of Android have fewer TLS slots (nominally 64, but the
// system uses "about 5 of them" itself).
Thread::LocalStorageKey keys_[32];
};
} // namespace
TEST_F(ThreadLocalStorageTest, DoTest) {
Run();
CHECK(Start());
Join();
}
TEST(StackTest, GetStackStart) { EXPECT_NE(nullptr, Stack::GetStackStart()); }
TEST(StackTest, GetCurrentStackPosition) {
EXPECT_NE(nullptr, Stack::GetCurrentStackPosition());
}
#if !defined(V8_OS_FUCHSIA)
TEST(StackTest, StackVariableInBounds) {
void* dummy;
ASSERT_GT(static_cast<void*>(Stack::GetStackStart()),
Stack::GetCurrentStackPosition());
EXPECT_GT(static_cast<void*>(Stack::GetStackStart()),
Stack::GetRealStackAddressForSlot(&dummy));
EXPECT_LT(static_cast<void*>(Stack::GetCurrentStackPosition()),
Stack::GetRealStackAddressForSlot(&dummy));
}
#endif // !V8_OS_FUCHSIA
using SetDataReadOnlyTest = ::testing::Test;
TEST_F(SetDataReadOnlyTest, SetDataReadOnly) {
static struct alignas(kMaxPageSize) TestData {
int x;
int y;
} test_data;
static_assert(alignof(TestData) == kMaxPageSize);
static_assert(sizeof(TestData) == kMaxPageSize);
test_data.x = 25;
test_data.y = 41;
OS::SetDataReadOnly(&test_data, sizeof(test_data));
CHECK_EQ(25, test_data.x);
CHECK_EQ(41, test_data.y);
ASSERT_DEATH_IF_SUPPORTED(test_data.x = 1, "");
ASSERT_DEATH_IF_SUPPORTED(test_data.y = 0, "");
}
} // namespace base
namespace {
#ifdef V8_CC_GNU
static uintptr_t sp_addr = 0;
void GetStackPointerCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
GET_STACK_POINTER_TO(sp_addr);
CHECK(i::ValidateCallbackInfo(info));
info.GetReturnValue().Set(v8::Integer::NewFromUnsigned(
info.GetIsolate(), static_cast<uint32_t>(sp_addr)));
}
using PlatformTest = v8::TestWithIsolate;
TEST_F(PlatformTest, StackAlignment) {
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate());
global_template->Set(
isolate(), "get_stack_pointer",
FunctionTemplate::New(isolate(), GetStackPointerCallback));
Local<Context> context = Context::New(isolate(), nullptr, global_template);
Context::Scope context_scope(context);
TryRunJS(
"function foo() {"
" return get_stack_pointer();"
"}");
Local<Object> global_object = context->Global();
Local<Function> foo = v8::Local<v8::Function>::Cast(
global_object->Get(isolate()->GetCurrentContext(), NewString("foo"))
.ToLocalChecked());
Local<v8::Value> result =
foo->Call(isolate()->GetCurrentContext(), global_object, 0, nullptr)
.ToLocalChecked();
CHECK_EQ(0u, result->Uint32Value(isolate()->GetCurrentContext()).FromJust() %
base::OS::ActivationFrameAlignment());
}
#endif // V8_CC_GNU
} // namespace
} // namespace v8

View File

@ -0,0 +1,142 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstring>
#include "src/base/platform/platform.h"
#include "src/base/platform/semaphore.h"
#include "src/base/platform/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
namespace {
static const char kAlphabet[] = "XKOAD";
static const size_t kAlphabetSize = sizeof(kAlphabet) - 1;
static const size_t kBufferSize = 987; // GCD(buffer size, alphabet size) = 1
static const size_t kDataSize = kBufferSize * kAlphabetSize * 10;
class ProducerThread final : public Thread {
public:
ProducerThread(char* buffer, Semaphore* free_space, Semaphore* used_space)
: Thread(Options("ProducerThread")),
buffer_(buffer),
free_space_(free_space),
used_space_(used_space) {}
void Run() override {
for (size_t n = 0; n < kDataSize; ++n) {
free_space_->Wait();
buffer_[n % kBufferSize] = kAlphabet[n % kAlphabetSize];
used_space_->Signal();
}
}
private:
char* buffer_;
Semaphore* const free_space_;
Semaphore* const used_space_;
};
class ConsumerThread final : public Thread {
public:
ConsumerThread(const char* buffer, Semaphore* free_space,
Semaphore* used_space)
: Thread(Options("ConsumerThread")),
buffer_(buffer),
free_space_(free_space),
used_space_(used_space) {}
void Run() override {
for (size_t n = 0; n < kDataSize; ++n) {
used_space_->Wait();
EXPECT_EQ(kAlphabet[n % kAlphabetSize], buffer_[n % kBufferSize]);
free_space_->Signal();
}
}
private:
const char* buffer_;
Semaphore* const free_space_;
Semaphore* const used_space_;
};
class WaitAndSignalThread final : public Thread {
public:
explicit WaitAndSignalThread(Semaphore* semaphore)
: Thread(Options("WaitAndSignalThread")), semaphore_(semaphore) {}
void Run() override {
for (int n = 0; n < 100; ++n) {
semaphore_->Wait();
ASSERT_FALSE(semaphore_->WaitFor(TimeDelta::FromMicroseconds(1)));
semaphore_->Signal();
}
}
private:
Semaphore* const semaphore_;
};
} // namespace
TEST(Semaphore, ProducerConsumer) {
char buffer[kBufferSize];
std::memset(buffer, 0, sizeof(buffer));
Semaphore free_space(kBufferSize);
Semaphore used_space(0);
ProducerThread producer_thread(buffer, &free_space, &used_space);
ConsumerThread consumer_thread(buffer, &free_space, &used_space);
CHECK(producer_thread.Start());
CHECK(consumer_thread.Start());
producer_thread.Join();
consumer_thread.Join();
}
TEST(Semaphore, WaitAndSignal) {
Semaphore semaphore(0);
WaitAndSignalThread t1(&semaphore);
WaitAndSignalThread t2(&semaphore);
CHECK(t1.Start());
CHECK(t2.Start());
// Make something available.
semaphore.Signal();
t1.Join();
t2.Join();
semaphore.Wait();
EXPECT_FALSE(semaphore.WaitFor(TimeDelta::FromMicroseconds(1)));
}
TEST(Semaphore, WaitFor) {
Semaphore semaphore(0);
// Semaphore not signalled - timeout.
ASSERT_FALSE(semaphore.WaitFor(TimeDelta::FromMicroseconds(0)));
ASSERT_FALSE(semaphore.WaitFor(TimeDelta::FromMicroseconds(100)));
ASSERT_FALSE(semaphore.WaitFor(TimeDelta::FromMicroseconds(1000)));
// Semaphore signalled - no timeout.
semaphore.Signal();
ASSERT_TRUE(semaphore.WaitFor(TimeDelta::FromMicroseconds(0)));
semaphore.Signal();
ASSERT_TRUE(semaphore.WaitFor(TimeDelta::FromMicroseconds(100)));
semaphore.Signal();
ASSERT_TRUE(semaphore.WaitFor(TimeDelta::FromMicroseconds(1000)));
}
} // namespace base
} // namespace v8

View File

@ -0,0 +1,581 @@
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/platform/time.h"
#if V8_OS_DARWIN
#include <mach/mach_time.h>
#endif
#if V8_OS_POSIX
#include <sys/time.h>
#endif
#if V8_OS_WIN
#include <windows.h>
#endif
#include <vector>
#include "src/base/platform/elapsed-timer.h"
#include "src/base/platform/platform.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace base {
TEST(TimeDelta, ZeroMinMax) {
constexpr TimeDelta kZero;
static_assert(kZero.IsZero(), "");
constexpr TimeDelta kMax = TimeDelta::Max();
static_assert(kMax.IsMax(), "");
static_assert(kMax == TimeDelta::Max(), "");
EXPECT_GT(kMax, TimeDelta::FromDays(100 * 365));
static_assert(kMax > kZero, "");
constexpr TimeDelta kMin = TimeDelta::Min();
static_assert(kMin.IsMin(), "");
static_assert(kMin == TimeDelta::Min(), "");
EXPECT_LT(kMin, TimeDelta::FromDays(-100 * 365));
static_assert(kMin < kZero, "");
}
TEST(TimeDelta, MaxConversions) {
// static_assert also confirms constexpr works as intended.
constexpr TimeDelta kMax = TimeDelta::Max();
EXPECT_EQ(kMax.InDays(), std::numeric_limits<int>::max());
EXPECT_EQ(kMax.InHours(), std::numeric_limits<int>::max());
EXPECT_EQ(kMax.InMinutes(), std::numeric_limits<int>::max());
EXPECT_EQ(kMax.InSecondsF(), std::numeric_limits<double>::infinity());
EXPECT_EQ(kMax.InSeconds(), std::numeric_limits<int64_t>::max());
EXPECT_EQ(kMax.InMillisecondsF(), std::numeric_limits<double>::infinity());
EXPECT_EQ(kMax.InMilliseconds(), std::numeric_limits<int64_t>::max());
EXPECT_EQ(kMax.InMillisecondsRoundedUp(),
std::numeric_limits<int64_t>::max());
// TODO(v8-team): Import overflow support from Chromium's base.
// EXPECT_TRUE(TimeDelta::FromDays(std::numeric_limits<int>::max()).IsMax());
// EXPECT_TRUE(
// TimeDelta::FromHours(std::numeric_limits<int>::max()).IsMax());
// EXPECT_TRUE(
// TimeDelta::FromMinutes(std::numeric_limits<int>::max()).IsMax());
// constexpr int64_t max_int = std::numeric_limits<int64_t>::max();
// constexpr int64_t min_int = std::numeric_limits<int64_t>::min();
// EXPECT_TRUE(
// TimeDelta::FromSeconds(max_int / Time::kMicrosecondsPerSecond + 1)
// .IsMax());
// EXPECT_TRUE(TimeDelta::FromMilliseconds(
// max_int / Time::kMillisecondsPerSecond + 1)
// .IsMax());
// EXPECT_TRUE(TimeDelta::FromMicroseconds(max_int).IsMax());
// EXPECT_TRUE(
// TimeDelta::FromSeconds(min_int / Time::kMicrosecondsPerSecond - 1)
// .IsMin());
// EXPECT_TRUE(TimeDelta::FromMilliseconds(
// min_int / Time::kMillisecondsPerSecond - 1)
// .IsMin());
// EXPECT_TRUE(TimeDelta::FromMicroseconds(min_int).IsMin());
// EXPECT_TRUE(
// TimeDelta::FromMicroseconds(std::numeric_limits<int64_t>::min())
// .IsMin());
}
TEST(TimeDelta, NumericOperators) {
constexpr int i = 2;
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) * i));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) / i));
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) *= i));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) /= i));
constexpr int64_t i64 = 2;
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) * i64));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) / i64));
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) *= i64));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) /= i64));
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) * 2));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) / 2));
EXPECT_EQ(TimeDelta::FromMilliseconds(2000),
(TimeDelta::FromMilliseconds(1000) *= 2));
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
(TimeDelta::FromMilliseconds(1000) /= 2));
}
// TODO(v8-team): Import support for overflow from Chromium's base.
TEST(TimeDelta, DISABLED_Overflows) {
// Some sanity checks. static_assert's used were possible to verify constexpr
// evaluation at the same time.
static_assert(TimeDelta::Max().IsMax(), "");
static_assert(-TimeDelta::Max() < TimeDelta(), "");
static_assert(-TimeDelta::Max() > TimeDelta::Min(), "");
static_assert(TimeDelta() > -TimeDelta::Max(), "");
TimeDelta large_delta = TimeDelta::Max() - TimeDelta::FromMilliseconds(1);
TimeDelta large_negative = -large_delta;
EXPECT_GT(TimeDelta(), large_negative);
EXPECT_FALSE(large_delta.IsMax());
EXPECT_FALSE((-large_negative).IsMin());
const TimeDelta kOneSecond = TimeDelta::FromSeconds(1);
// Test +, -, * and / operators.
EXPECT_TRUE((large_delta + kOneSecond).IsMax());
EXPECT_TRUE((large_negative + (-kOneSecond)).IsMin());
EXPECT_TRUE((large_negative - kOneSecond).IsMin());
EXPECT_TRUE((large_delta - (-kOneSecond)).IsMax());
EXPECT_TRUE((large_delta * 2).IsMax());
EXPECT_TRUE((large_delta * -2).IsMin());
// Test +=, -=, *= and /= operators.
TimeDelta delta = large_delta;
delta += kOneSecond;
EXPECT_TRUE(delta.IsMax());
delta = large_negative;
delta += -kOneSecond;
EXPECT_TRUE((delta).IsMin());
delta = large_negative;
delta -= kOneSecond;
EXPECT_TRUE((delta).IsMin());
delta = large_delta;
delta -= -kOneSecond;
EXPECT_TRUE(delta.IsMax());
delta = large_delta;
delta *= 2;
EXPECT_TRUE(delta.IsMax());
// Test operations with Time and TimeTicks.
EXPECT_TRUE((large_delta + Time::Now()).IsMax());
EXPECT_TRUE((large_delta + TimeTicks::Now()).IsMax());
EXPECT_TRUE((Time::Now() + large_delta).IsMax());
EXPECT_TRUE((TimeTicks::Now() + large_delta).IsMax());
Time time_now = Time::Now();
EXPECT_EQ(kOneSecond, (time_now + kOneSecond) - time_now);
EXPECT_EQ(-kOneSecond, (time_now - kOneSecond) - time_now);
TimeTicks ticks_now = TimeTicks::Now();
EXPECT_EQ(-kOneSecond, (ticks_now - kOneSecond) - ticks_now);
EXPECT_EQ(kOneSecond, (ticks_now + kOneSecond) - ticks_now);
}
TEST(TimeDelta, FromAndIn) {
EXPECT_EQ(TimeDelta::FromDays(2), TimeDelta::FromHours(48));
EXPECT_EQ(TimeDelta::FromHours(3), TimeDelta::FromMinutes(180));
EXPECT_EQ(TimeDelta::FromMinutes(2), TimeDelta::FromSeconds(120));
EXPECT_EQ(TimeDelta::FromSeconds(2), TimeDelta::FromMilliseconds(2000));
EXPECT_EQ(TimeDelta::FromMilliseconds(2), TimeDelta::FromMicroseconds(2000));
EXPECT_EQ(static_cast<int>(13), TimeDelta::FromDays(13).InDays());
EXPECT_EQ(static_cast<int>(13), TimeDelta::FromHours(13).InHours());
EXPECT_EQ(static_cast<int>(13), TimeDelta::FromMinutes(13).InMinutes());
EXPECT_EQ(static_cast<int64_t>(13), TimeDelta::FromSeconds(13).InSeconds());
EXPECT_DOUBLE_EQ(13.0, TimeDelta::FromSeconds(13).InSecondsF());
EXPECT_EQ(static_cast<int64_t>(13),
TimeDelta::FromMilliseconds(13).InMilliseconds());
EXPECT_DOUBLE_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF());
EXPECT_EQ(static_cast<int64_t>(13),
TimeDelta::FromMicroseconds(13).InMicroseconds());
}
#if V8_OS_DARWIN
TEST(TimeDelta, MachTimespec) {
TimeDelta null = TimeDelta();
EXPECT_EQ(null, TimeDelta::FromMachTimespec(null.ToMachTimespec()));
TimeDelta delta1 = TimeDelta::FromMilliseconds(42);
EXPECT_EQ(delta1, TimeDelta::FromMachTimespec(delta1.ToMachTimespec()));
TimeDelta delta2 = TimeDelta::FromDays(42);
EXPECT_EQ(delta2, TimeDelta::FromMachTimespec(delta2.ToMachTimespec()));
}
#endif
TEST(Time, Max) {
Time max = Time::Max();
EXPECT_TRUE(max.IsMax());
EXPECT_EQ(max, Time::Max());
EXPECT_GT(max, Time::Now());
EXPECT_GT(max, Time());
}
TEST(Time, MaxConversions) {
Time t = Time::Max();
EXPECT_EQ(std::numeric_limits<int64_t>::max(), t.ToInternalValue());
// TODO(v8-team): Time::FromJsTime() overflows with infinity. Import support
// from Chromium's base.
// t = Time::FromJsTime(std::numeric_limits<double>::infinity());
// EXPECT_TRUE(t.IsMax());
// EXPECT_EQ(std::numeric_limits<double>::infinity(), t.ToJsTime());
#if defined(OS_POSIX)
struct timeval tval;
tval.tv_sec = std::numeric_limits<time_t>::max();
tval.tv_usec = static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1;
t = Time::FromTimeVal(tval);
EXPECT_TRUE(t.IsMax());
tval = t.ToTimeVal();
EXPECT_EQ(std::numeric_limits<time_t>::max(), tval.tv_sec);
EXPECT_EQ(static_cast<suseconds_t>(Time::kMicrosecondsPerSecond) - 1,
tval.tv_usec);
#endif
#if defined(OS_WIN)
FILETIME ftime;
ftime.dwHighDateTime = std::numeric_limits<DWORD>::max();
ftime.dwLowDateTime = std::numeric_limits<DWORD>::max();
t = Time::FromFileTime(ftime);
EXPECT_TRUE(t.IsMax());
ftime = t.ToFileTime();
EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwHighDateTime);
EXPECT_EQ(std::numeric_limits<DWORD>::max(), ftime.dwLowDateTime);
#endif
}
TEST(Time, JsTime) {
Time t = Time::FromJsTime(700000.3);
EXPECT_DOUBLE_EQ(700000.3, t.ToJsTime());
}
#if V8_OS_POSIX
TEST(Time, Timespec) {
Time null;
EXPECT_TRUE(null.IsNull());
EXPECT_EQ(null, Time::FromTimespec(null.ToTimespec()));
Time now = Time::Now();
EXPECT_EQ(now, Time::FromTimespec(now.ToTimespec()));
Time now_sys = Time::NowFromSystemTime();
EXPECT_EQ(now_sys, Time::FromTimespec(now_sys.ToTimespec()));
Time unix_epoch = Time::UnixEpoch();
EXPECT_EQ(unix_epoch, Time::FromTimespec(unix_epoch.ToTimespec()));
Time max = Time::Max();
EXPECT_TRUE(max.IsMax());
EXPECT_EQ(max, Time::FromTimespec(max.ToTimespec()));
}
TEST(Time, Timeval) {
Time null;
EXPECT_TRUE(null.IsNull());
EXPECT_EQ(null, Time::FromTimeval(null.ToTimeval()));
Time now = Time::Now();
EXPECT_EQ(now, Time::FromTimeval(now.ToTimeval()));
Time now_sys = Time::NowFromSystemTime();
EXPECT_EQ(now_sys, Time::FromTimeval(now_sys.ToTimeval()));
Time unix_epoch = Time::UnixEpoch();
EXPECT_EQ(unix_epoch, Time::FromTimeval(unix_epoch.ToTimeval()));
Time max = Time::Max();
EXPECT_TRUE(max.IsMax());
EXPECT_EQ(max, Time::FromTimeval(max.ToTimeval()));
}
#endif
#if V8_OS_WIN
TEST(Time, Filetime) {
Time null;
EXPECT_TRUE(null.IsNull());
EXPECT_EQ(null, Time::FromFiletime(null.ToFiletime()));
Time now = Time::Now();
EXPECT_EQ(now, Time::FromFiletime(now.ToFiletime()));
Time now_sys = Time::NowFromSystemTime();
EXPECT_EQ(now_sys, Time::FromFiletime(now_sys.ToFiletime()));
Time unix_epoch = Time::UnixEpoch();
EXPECT_EQ(unix_epoch, Time::FromFiletime(unix_epoch.ToFiletime()));
Time max = Time::Max();
EXPECT_TRUE(max.IsMax());
EXPECT_EQ(max, Time::FromFiletime(max.ToFiletime()));
}
#endif
namespace {
template <typename T>
static void ResolutionTest(T (*Now)(), TimeDelta target_granularity) {
// We're trying to measure that intervals increment in a VERY small amount
// of time -- according to the specified target granularity. Unfortunately,
// if we happen to have a context switch in the middle of our test, the
// context switch could easily exceed our limit. So, we iterate on this
// several times. As long as we're able to detect the fine-granularity
// timers at least once, then the test has succeeded.
static const TimeDelta kExpirationTimeout = TimeDelta::FromSeconds(1);
ElapsedTimer timer;
timer.Start();
TimeDelta delta;
do {
T start = Now();
T now = start;
// Loop until we can detect that the clock has changed. Non-HighRes timers
// will increment in chunks, i.e. 15ms. By spinning until we see a clock
// change, we detect the minimum time between measurements.
do {
now = Now();
delta = now - start;
} while (now <= start);
EXPECT_NE(static_cast<int64_t>(0), delta.InMicroseconds());
} while (delta > target_granularity && !timer.HasExpired(kExpirationTimeout));
EXPECT_LE(delta, target_granularity);
}
} // namespace
TEST(Time, NowResolution) {
// We assume that Time::Now() has at least 16ms resolution.
static const TimeDelta kTargetGranularity = TimeDelta::FromMilliseconds(16);
ResolutionTest<Time>(&Time::Now, kTargetGranularity);
}
TEST(TimeTicks, NowResolution) {
// TimeTicks::Now() is documented as having "no worse than one microsecond"
// resolution. Unless !TimeTicks::IsHighResolution() in which case the clock
// could be as coarse as ~15.6ms.
const TimeDelta kTargetGranularity = TimeTicks::IsHighResolution()
? TimeDelta::FromMicroseconds(1)
: TimeDelta::FromMilliseconds(16);
ResolutionTest<TimeTicks>(&TimeTicks::Now, kTargetGranularity);
}
TEST(TimeTicks, IsMonotonic) {
TimeTicks previous_ticks;
ElapsedTimer timer;
timer.Start();
while (!timer.HasExpired(TimeDelta::FromMilliseconds(100))) {
TimeTicks ticks = TimeTicks::Now();
EXPECT_GE(ticks, previous_ticks);
EXPECT_GE((ticks - previous_ticks).InMicroseconds(), 0);
previous_ticks = ticks;
}
}
namespace {
void Sleep(TimeDelta wait_time) {
ElapsedTimer waiter;
waiter.Start();
while (!waiter.HasExpired(wait_time)) {
OS::Sleep(TimeDelta::FromMilliseconds(1));
}
}
} // namespace
TEST(ElapsedTimer, StartStop) {
TimeDelta wait_time = TimeDelta::FromMilliseconds(100);
TimeDelta noise = TimeDelta::FromMilliseconds(100);
ElapsedTimer timer;
DCHECK(!timer.IsStarted());
timer.Start();
DCHECK(timer.IsStarted());
Sleep(wait_time);
TimeDelta delta = timer.Elapsed();
DCHECK(timer.IsStarted());
EXPECT_GE(delta, wait_time);
EXPECT_LT(delta, wait_time + noise);
DCHECK(!timer.IsPaused());
timer.Pause();
DCHECK(timer.IsPaused());
Sleep(wait_time);
timer.Resume();
DCHECK(timer.IsStarted());
delta = timer.Elapsed();
DCHECK(!timer.IsPaused());
timer.Pause();
DCHECK(timer.IsPaused());
EXPECT_GE(delta, wait_time);
EXPECT_LT(delta, wait_time + noise);
Sleep(wait_time);
timer.Resume();
DCHECK(!timer.IsPaused());
DCHECK(timer.IsStarted());
delta = timer.Elapsed();
EXPECT_GE(delta, wait_time);
EXPECT_LT(delta, wait_time + noise);
timer.Stop();
DCHECK(!timer.IsStarted());
}
TEST(ElapsedTimer, StartStopArgs) {
TimeDelta wait_time = TimeDelta::FromMilliseconds(100);
ElapsedTimer timer1;
ElapsedTimer timer2;
DCHECK(!timer1.IsStarted());
DCHECK(!timer2.IsStarted());
TimeTicks now = TimeTicks::Now();
timer1.Start(now);
timer2.Start(now);
DCHECK(timer1.IsStarted());
DCHECK(timer2.IsStarted());
Sleep(wait_time);
now = TimeTicks::Now();
TimeDelta delta1 = timer1.Elapsed(now);
Sleep(wait_time);
TimeDelta delta2 = timer2.Elapsed(now);
DCHECK(timer1.IsStarted());
DCHECK(timer2.IsStarted());
EXPECT_GE(delta1, delta2);
Sleep(wait_time);
EXPECT_NE(delta1, timer2.Elapsed());
TimeTicks now2 = TimeTicks::Now();
EXPECT_NE(timer1.Elapsed(now), timer1.Elapsed(now2));
EXPECT_NE(delta1, timer1.Elapsed(now2));
EXPECT_NE(delta2, timer2.Elapsed(now2));
EXPECT_GE(timer1.Elapsed(now2), timer2.Elapsed(now2));
now = TimeTicks::Now();
timer1.Pause(now);
timer2.Pause(now);
DCHECK(timer1.IsPaused());
DCHECK(timer2.IsPaused());
Sleep(wait_time);
now = TimeTicks::Now();
timer1.Resume(now);
DCHECK(!timer1.IsPaused());
DCHECK(timer2.IsPaused());
Sleep(wait_time);
timer2.Resume(now);
DCHECK(!timer1.IsPaused());
DCHECK(!timer2.IsPaused());
DCHECK(timer1.IsStarted());
DCHECK(timer2.IsStarted());
delta1 = timer1.Elapsed(now);
Sleep(wait_time);
delta2 = timer2.Elapsed(now);
EXPECT_GE(delta1, delta2);
timer1.Stop();
timer2.Stop();
DCHECK(!timer1.IsStarted());
DCHECK(!timer2.IsStarted());
}
#if V8_OS_ANDROID
#define MAYBE_ThreadNow DISABLED_ThreadNow
#else
#define MAYBE_ThreadNow ThreadNow
#endif
TEST(ThreadTicks, MAYBE_ThreadNow) {
if (ThreadTicks::IsSupported()) {
ThreadTicks::WaitUntilInitialized();
TimeTicks end, begin = TimeTicks::Now();
ThreadTicks end_thread, begin_thread = ThreadTicks::Now();
TimeDelta delta;
// Make sure that ThreadNow value is non-zero.
EXPECT_GT(begin_thread, ThreadTicks());
int iterations_count = 0;
#if V8_OS_WIN && V8_HOST_ARCH_ARM64
// The implementation of ThreadTicks::Now() is quite imprecise on arm64
// Windows, so the following test often fails with the default 10ms. By
// increasing to 100ms, we can make the test reliable.
const int limit_ms = 100;
#else
const int limit_ms = 10;
#endif
const int limit_us = limit_ms * 1000;
// Some systems have low resolution thread timers, this code makes sure
// that thread time has progressed by at least one tick.
// Limit waiting to 10ms to prevent infinite loops.
while (ThreadTicks::Now() == begin_thread &&
((TimeTicks::Now() - begin).InMicroseconds() < limit_us)) {
}
EXPECT_GT(ThreadTicks::Now(), begin_thread);
do {
// Sleep for 10 milliseconds to get the thread de-scheduled.
OS::Sleep(base::TimeDelta::FromMilliseconds(limit_ms));
end_thread = ThreadTicks::Now();
end = TimeTicks::Now();
delta = end - begin;
EXPECT_LE(++iterations_count, 2); // fail after 2 attempts.
} while (delta.InMicroseconds() <
limit_us); // Make sure that the OS did sleep for at least 10 ms.
TimeDelta delta_thread = end_thread - begin_thread;
// Make sure that some thread time have elapsed.
EXPECT_GT(delta_thread.InMicroseconds(), 0);
// But the thread time is at least 9ms less than clock time.
TimeDelta difference = delta - delta_thread;
EXPECT_GE(difference.InMicroseconds(), limit_us * 9 / 10);
}
}
#if V8_OS_WIN
TEST(TimeTicks, TimerPerformance) {
// Verify that various timer mechanisms can always complete quickly.
// Note: This is a somewhat arbitrary test.
const int kLoops = 10000;
using TestFunc = TimeTicks (*)();
struct TestCase {
TestFunc func;
const char *description;
};
// Cheating a bit here: assumes sizeof(TimeTicks) == sizeof(Time)
// in order to create a single test case list.
static_assert(sizeof(TimeTicks) == sizeof(Time),
"TimeTicks and Time must be the same size");
std::vector<TestCase> cases;
cases.push_back({reinterpret_cast<TestFunc>(&Time::Now), "Time::Now"});
cases.push_back({&TimeTicks::Now, "TimeTicks::Now"});
if (ThreadTicks::IsSupported()) {
ThreadTicks::WaitUntilInitialized();
cases.push_back(
{reinterpret_cast<TestFunc>(&ThreadTicks::Now), "ThreadTicks::Now"});
}
for (const auto& test_case : cases) {
TimeTicks start = TimeTicks::Now();
for (int index = 0; index < kLoops; index++)
test_case.func();
TimeTicks stop = TimeTicks::Now();
// Turning off the check for acceptable delays. Without this check,
// the test really doesn't do much other than measure. But the
// measurements are still useful for testing timers on various platforms.
// The reason to remove the check is because the tests run on many
// buildbots, some of which are VMs. These machines can run horribly
// slow, and there is really no value for checking against a max timer.
// const int kMaxTime = 35; // Maximum acceptable milliseconds for test.
// EXPECT_LT((stop - start).InMilliseconds(), kMaxTime);
printf("%s: %1.2fus per call\n", test_case.description,
(stop - start).InMillisecondsF() * 1000 / kLoops);
}
}
#endif // V8_OS_WIN
} // namespace base
} // namespace v8