Remove base/synchronization/, move src/ to util/ Moving src/ to util/ (and removing src/ from include path) was necessary so that Semaphore could live in "util/semaphore.h" rather than having a different name (because <semaphore.h> is where sem_t is on Linux). Unfortunately this required touching ~everything because build_config.h needed to be changed to util/build_config.h. Change-Id: I4c913542c1f1504dca1c82e6ab6d99ef263f5cac Reviewed-on: https://gn-review.googlesource.com/1680 Commit-Queue: Scott Graham <scottmg@chromium.org> Reviewed-by: Brett Wilson <brettw@chromium.org>
diff --git a/util/auto_reset_event.h b/util/auto_reset_event.h new file mode 100644 index 0000000..5e040a8 --- /dev/null +++ b/util/auto_reset_event.h
@@ -0,0 +1,53 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can +// be found in the LICENSE file. + +#ifndef UTIL_AUTO_RESET_EVENT_H_ +#define UTIL_AUTO_RESET_EVENT_H_ + +#include <atomic> + +#include "base/logging.h" +#include "util/semaphore.h" + +// From http://preshing.com/20150316/semaphores-are-surprisingly-versatile/, +// but using V8's Semaphore. +class AutoResetEvent { + private: + // status_ == 1: Event object is signaled. + // status_ == 0: Event object is reset and no threads are waiting. + // status_ == -N: Event object is reset and N threads are waiting. + std::atomic<int> status_; + Semaphore semaphore_; + + public: + AutoResetEvent() : status_(0), semaphore_(0) {} + + void Signal() { + int old_status = status_.load(std::memory_order_relaxed); + // Increment status_ atomically via CAS loop. + for (;;) { + DCHECK_LE(old_status, 1); + int new_status = old_status < 1 ? old_status + 1 : 1; + if (status_.compare_exchange_weak(old_status, new_status, + std::memory_order_release, + std::memory_order_relaxed)) { + break; + } + // The compare-exchange failed, likely because another thread changed + // status_. old_status has been updated. Retry the CAS loop. + } + if (old_status < 0) + semaphore_.Signal(); // Release one waiting thread. + } + + void Wait() { + int old_status = status_.fetch_sub(1, std::memory_order_acquire); + DCHECK_LE(old_status, 1); + if (old_status < 1) { + semaphore_.Wait(); + } + } +}; + +#endif // UTIL_AUTO_RESET_EVENT_H_
diff --git a/util/build_config.h b/util/build_config.h new file mode 100644 index 0000000..addd7cf --- /dev/null +++ b/util/build_config.h
@@ -0,0 +1,196 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file adds defines about the platform we're currently building on. +// Operating System: +// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) / +// OS_NACL (NACL_SFI or NACL_NONSFI) / OS_NACL_SFI / OS_NACL_NONSFI +// OS_CHROMEOS is set by the build system +// Compiler: +// COMPILER_MSVC / COMPILER_GCC +// Processor: +// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64) +// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS + +#ifndef BUILD_BUILD_CONFIG_H_ +#define BUILD_BUILD_CONFIG_H_ + +// A set of macros to use for platform detection. +#if defined(__native_client__) +// __native_client__ must be first, so that other OS_ defines are not set. +#define OS_NACL 1 +// OS_NACL comes in two sandboxing technology flavors, SFI or Non-SFI. +// PNaCl toolchain defines __native_client_nonsfi__ macro in Non-SFI build +// mode, while it does not in SFI build mode. +#if defined(__native_client_nonsfi__) +#define OS_NACL_NONSFI +#else +#define OS_NACL_SFI +#endif +#elif defined(ANDROID) +#define OS_ANDROID 1 +#elif defined(__APPLE__) +// only include TargetConditions after testing ANDROID as some android builds +// on mac don't have this header available and it's not needed unless the target +// is really mac/ios. +#include <TargetConditionals.h> +#define OS_MACOSX 1 +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#define OS_IOS 1 +#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#elif defined(__linux__) +#define OS_LINUX 1 +// include a system header to pull in features.h for glibc/uclibc macros. +#include <unistd.h> +#if defined(__GLIBC__) && !defined(__UCLIBC__) +// we really are using glibc, not uClibc pretending to be glibc +#define LIBC_GLIBC 1 +#endif +#elif defined(_WIN32) +#define OS_WIN 1 +#elif defined(__Fuchsia__) +#define OS_FUCHSIA 1 +#elif defined(__FreeBSD__) +#define OS_FREEBSD 1 +#elif defined(__NetBSD__) +#define OS_NETBSD 1 +#elif defined(__OpenBSD__) +#define OS_OPENBSD 1 +#elif defined(__sun) +#define OS_SOLARIS 1 +#elif defined(__QNXNTO__) +#define OS_QNX 1 +#elif defined(_AIX) +#define OS_AIX 1 +#elif defined(__asmjs__) +#define OS_ASMJS +#else +#error Please add support for your platform in build_config.h +#endif +// NOTE: Adding a new port? Please follow +// https://chromium.googlesource.com/chromium/src/+/master/docs/new_port_policy.md + +// For access to standard BSD features, use OS_BSD instead of a +// more specific macro. +#if defined(OS_FREEBSD) || defined(OS_NETBSD) || defined(OS_OPENBSD) +#define OS_BSD 1 +#endif + +// For access to standard POSIXish features, use OS_POSIX instead of a +// more specific macro. +#if defined(OS_AIX) || defined(OS_ANDROID) || defined(OS_ASMJS) || \ + defined(OS_FREEBSD) || defined(OS_LINUX) || defined(OS_MACOSX) || \ + defined(OS_NACL) || defined(OS_NETBSD) || defined(OS_OPENBSD) || \ + defined(OS_QNX) || defined(OS_SOLARIS) +#define OS_POSIX 1 +#endif + +// Use tcmalloc +#if (defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)) && \ + !defined(NO_TCMALLOC) +#define USE_TCMALLOC 1 +#endif + +// Compiler detection. +#if defined(__GNUC__) +#define COMPILER_GCC 1 +#elif defined(_MSC_VER) +#define COMPILER_MSVC 1 +#else +#error Please add support for your compiler in build_config.h +#endif + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86_64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(_M_IX86) || defined(__i386__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__s390x__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390X 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__s390__) +#define ARCH_CPU_S390_FAMILY 1 +#define ARCH_CPU_S390 1 +#define ARCH_CPU_31_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif (defined(__PPC64__) || defined(__PPC__)) && defined(__BIG_ENDIAN__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#elif defined(__PPC64__) +#define ARCH_CPU_PPC64_FAMILY 1 +#define ARCH_CPU_PPC64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARMEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__aarch64__) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARM64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__pnacl__) || defined(__asmjs__) +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__MIPSEL__) +#if defined(__LP64__) +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS64EL 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#else +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPSEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#endif +#elif defined(__MIPSEB__) +#if defined(__LP64__) +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#else +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPS 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_BIG_ENDIAN 1 +#endif +#else +#error Please add support for your architecture in build_config.h +#endif + +// Type detection for wchar_t. +#if defined(OS_WIN) +#define WCHAR_T_IS_UTF16 +#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \ + (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) +#define WCHAR_T_IS_UTF32 +#elif defined(OS_POSIX) && defined(COMPILER_GCC) && defined(__WCHAR_MAX__) && \ + (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff) +// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to +// compile in this mode (in particular, Chrome doesn't). This is intended for +// other projects using base who manage their own dependencies and make sure +// short wchar works for them. +#define WCHAR_T_IS_UTF16 +#else +#error Please add support for your compiler in build_config.h +#endif + +#endif // BUILD_BUILD_CONFIG_H_
diff --git a/util/exe_path.cc b/util/exe_path.cc new file mode 100644 index 0000000..f2adab5 --- /dev/null +++ b/util/exe_path.cc
@@ -0,0 +1,61 @@ +// Copyright 2018 The Chromium 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 "util/exe_path.h" + +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "util/build_config.h" + +#if defined(OS_MACOSX) +#include <mach-o/dyld.h> +#elif defined(OS_WIN) +#include <windows.h> +#endif + +#if defined(OS_MACOSX) + +base::FilePath GetExePath() { + // Executable path can have relative references ("..") depending on + // how the app was launched. + uint32_t executable_length = 0; + _NSGetExecutablePath(NULL, &executable_length); + DCHECK_GT(executable_length, 1u); + std::string executable_path; + int rv = _NSGetExecutablePath( + base::WriteInto(&executable_path, executable_length), &executable_length); + DCHECK_EQ(rv, 0); + + // _NSGetExecutablePath may return paths containing ./ or ../ which makes + // FilePath::DirName() work incorrectly, convert it to absolute path so that + // paths such as DIR_SOURCE_ROOT can work, since we expect absolute paths to + // be returned here. + return base::MakeAbsoluteFilePath(base::FilePath(executable_path)); +} + +#elif defined(OS_WIN) + +base::FilePath GetExePath() { + wchar_t system_buffer[MAX_PATH]; + system_buffer[0] = 0; + if (GetModuleFileName(NULL, system_buffer, MAX_PATH) == 0) { + return base::FilePath(); + } + return base::FilePath(system_buffer); +} + +#else + +base::FilePath GetExePath() { + base::FilePath result; + const char kProcSelfExe[] = "/proc/self/exe"; + if (!ReadSymbolicLink(base::FilePath(kProcSelfExe), &result)) { + NOTREACHED() << "Unable to resolve " << kProcSelfExe << "."; + return base::FilePath(); + } + return result; +} + +#endif
diff --git a/util/exe_path.h b/util/exe_path.h new file mode 100644 index 0000000..0e1b8cb --- /dev/null +++ b/util/exe_path.h
@@ -0,0 +1,12 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UTIL_EXE_PATH_H_ +#define UTIL_EXE_PATH_H_ + +#include "base/files/file_path.h" + +base::FilePath GetExePath(); + +#endif // UTIL_EXE_PATH_H_
diff --git a/util/msg_loop.cc b/util/msg_loop.cc new file mode 100644 index 0000000..dbb92cf --- /dev/null +++ b/util/msg_loop.cc
@@ -0,0 +1,76 @@ +// Copyright 2018 The Chromium 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 "util/msg_loop.h" + +#include "base/logging.h" + +namespace { + +thread_local MsgLoop* g_current; +} + +MsgLoop::MsgLoop() { + DCHECK(g_current == nullptr); + g_current = this; +} + +MsgLoop::~MsgLoop() { + DCHECK(g_current == this); + g_current = nullptr; +} + +void MsgLoop::Run() { + while (!should_quit_) { + Task task; + { + std::unique_lock<std::mutex> queue_lock(queue_mutex_); + notifier_.wait(queue_lock, [this]() { + return (!task_queue_.empty()) || should_quit_; + }); + + if (should_quit_) + return; + + task = std::move(task_queue_.front()); + task_queue_.pop(); + } + + std::move(task).Run(); + } +} + +void MsgLoop::PostQuit() { + PostTask( + base::BindOnce([](MsgLoop* self) { self->should_quit_ = true; }, this)); +} + +void MsgLoop::PostTask(Task work) { + { + std::unique_lock<std::mutex> queue_lock(queue_mutex_); + task_queue_.emplace(std::move(work)); + } + + notifier_.notify_one(); +} + +void MsgLoop::RunUntilIdleForTesting() { + for (bool done = false; !done;) { + Task task; + { + std::unique_lock<std::mutex> queue_lock(queue_mutex_); + task = std::move(task_queue_.front()); + task_queue_.pop(); + + if (task_queue_.empty()) + done = true; + } + + std::move(task).Run(); + } +} + +MsgLoop* MsgLoop::Current() { + return g_current; +}
diff --git a/util/msg_loop.h b/util/msg_loop.h new file mode 100644 index 0000000..267d2a9 --- /dev/null +++ b/util/msg_loop.h
@@ -0,0 +1,47 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UTIL_RUN_LOOP_H_ +#define UTIL_RUN_LOOP_H_ + +#include "base/macros.h" +#include "util/task.h" + +#include <condition_variable> +#include <mutex> +#include <queue> + +class MsgLoop { + public: + MsgLoop(); + ~MsgLoop(); + + // Blocks until PostQuit() is called, processing work items posted via + void Run(); + + // Schedules Run() to exit, but will not happen until other outstanding tasks + // complete. Can be called from any thread. + void PostQuit(); + + // Posts a work item to this queue. All items will be run on the thread from + // which Run() was called. Can be called from any thread. + void PostTask(Task task); + + // Run()s until the queue is empty. Should only be used (carefully) in tests. + void RunUntilIdleForTesting(); + + // Gets the MsgLoop for the thread from which it's called, or nullptr if + // there's no MsgLoop for the current thread. + static MsgLoop* Current(); + + private: + std::mutex queue_mutex_; + std::queue<Task> task_queue_; + std::condition_variable notifier_; + bool should_quit_ = false; + + DISALLOW_COPY_AND_ASSIGN(MsgLoop); +}; + +#endif // UTIL_RUN_LOOP_H_
diff --git a/util/semaphore.cc b/util/semaphore.cc new file mode 100644 index 0000000..a1fa779 --- /dev/null +++ b/util/semaphore.cc
@@ -0,0 +1,95 @@ +// Copyright 2013 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. +// +// Based on +// https://cs.chromium.org/chromium/src/v8/src/base/platform/semaphore.cc + +#include "util/semaphore.h" + +#include "base/logging.h" + +#if defined(OS_MACOSX) + +Semaphore::Semaphore(int count) { + kern_return_t result = semaphore_create(mach_task_self(), &native_handle_, + SYNC_POLICY_FIFO, count); + DCHECK_EQ(KERN_SUCCESS, result); +} + +Semaphore::~Semaphore() { + kern_return_t result = semaphore_destroy(mach_task_self(), native_handle_); + DCHECK_EQ(KERN_SUCCESS, result); +} + +void Semaphore::Signal() { + kern_return_t result = semaphore_signal(native_handle_); + DCHECK_EQ(KERN_SUCCESS, result); +} + +void Semaphore::Wait() { + while (true) { + kern_return_t result = semaphore_wait(native_handle_); + if (result == KERN_SUCCESS) + return; // Semaphore was signalled. + DCHECK_EQ(KERN_ABORTED, result); + } +} + +#elif defined(OS_LINUX) + +Semaphore::Semaphore(int count) { + DCHECK_GE(count, 0); + int result = sem_init(&native_handle_, 0, count); + DCHECK_EQ(0, result); +} + +Semaphore::~Semaphore() { + int result = sem_destroy(&native_handle_); + DCHECK_EQ(0, result); +} + +void Semaphore::Signal() { + int result = sem_post(&native_handle_); + // This check may fail with <libc-2.21, which we use on the try bots, if the + // semaphore is destroyed while sem_post is still executed. A work around is + // to extend the lifetime of the semaphore. + CHECK_EQ(0, result); +} + +void Semaphore::Wait() { + while (true) { + int result = sem_wait(&native_handle_); + if (result == 0) + return; // Semaphore was signalled. + // Signal caused spurious wakeup. + DCHECK_EQ(-1, result); + DCHECK_EQ(EINTR, errno); + } +} + +#elif defined(OS_WIN) + +Semaphore::Semaphore(int count) { + DCHECK_GE(count, 0); + native_handle_ = ::CreateSemaphoreA(nullptr, count, 0x7FFFFFFF, nullptr); + DCHECK(native_handle_); +} + +Semaphore::~Semaphore() { + BOOL result = CloseHandle(native_handle_); + DCHECK(result); +} + +void Semaphore::Signal() { + LONG dummy; + BOOL result = ReleaseSemaphore(native_handle_, 1, &dummy); + DCHECK(result); +} + +void Semaphore::Wait() { + DWORD result = WaitForSingleObject(native_handle_, INFINITE); + DCHECK(result == WAIT_OBJECT_0); +} + +#endif
diff --git a/util/semaphore.h b/util/semaphore.h new file mode 100644 index 0000000..84d8b60 --- /dev/null +++ b/util/semaphore.h
@@ -0,0 +1,53 @@ +// Copyright 2013 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. +// +// Based on +// https://cs.chromium.org/chromium/src/v8/src/base/platform/semaphore.h + +#ifndef UTIL_SEMAPHORE_H_ +#define UTIL_SEMAPHORE_H_ + +#include "base/macros.h" +#include "util/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#elif defined(OS_MACOSX) +#include <mach/mach.h> +#elif defined(OS_LINUX) +#include <semaphore.h> +#else +#error Port. +#endif + +class Semaphore { + public: + explicit Semaphore(int count); + ~Semaphore(); + + // Increments the semaphore counter. + void Signal(); + + // Decrements the semaphore counter if it is positive, or blocks until it + // becomes positive and then decrements the counter. + void Wait(); + +#if defined(OS_MACOSX) + typedef semaphore_t NativeHandle; +#elif defined(OS_LINUX) + typedef sem_t NativeHandle; +#elif defined(OS_WIN) + typedef HANDLE NativeHandle; +#endif + + NativeHandle& native_handle() { return native_handle_; } + const NativeHandle& native_handle() const { return native_handle_; } + + private: + NativeHandle native_handle_; + + DISALLOW_COPY_AND_ASSIGN(Semaphore); +}; + +#endif // UTIL_SEMAPHORE_H_
diff --git a/util/sys_info.cc b/util/sys_info.cc new file mode 100644 index 0000000..a1ce3e9 --- /dev/null +++ b/util/sys_info.cc
@@ -0,0 +1,81 @@ +// Copyright 2018 The Chromium 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 "util/sys_info.h" + +#include "base/logging.h" +#include "util/build_config.h" + +#if defined(OS_POSIX) +#include <sys/utsname.h> +#include <unistd.h> +#endif + +#if defined(OS_WIN) +#include <windows.h> +#endif + +std::string OperatingSystemArchitecture() { +#if defined(OS_POSIX) + struct utsname info; + if (uname(&info) < 0) { + NOTREACHED(); + return std::string(); + } + std::string arch(info.machine); + if (arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686") { + arch = "x86"; + } else if (arch == "amd64") { + arch = "x86_64"; + } else if (std::string(info.sysname) == "AIX") { + arch = "ppc64"; + } + return arch; +#elif defined(OS_WIN) + SYSTEM_INFO system_info = {}; + ::GetNativeSystemInfo(&system_info); + switch (system_info.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_INTEL: + return "x86"; + case PROCESSOR_ARCHITECTURE_AMD64: + return "x86_64"; + case PROCESSOR_ARCHITECTURE_IA64: + return "ia64"; + } + return std::string(); +#else +#error +#endif +} + +int NumberOfProcessors() { +#if defined(OS_POSIX) + // sysconf returns the number of "logical" (not "physical") processors on both + // Mac and Linux. So we get the number of max available "logical" processors. + // + // Note that the number of "currently online" processors may be fewer than the + // returned value of NumberOfProcessors(). On some platforms, the kernel may + // make some processors offline intermittently, to save power when system + // loading is low. + // + // One common use case that needs to know the processor count is to create + // optimal number of threads for optimization. It should make plan according + // to the number of "max available" processors instead of "currently online" + // ones. The kernel should be smart enough to make all processors online when + // it has sufficient number of threads waiting to run. + long res = sysconf(_SC_NPROCESSORS_CONF); + if (res == -1) { + NOTREACHED(); + return 1; + } + + return static_cast<int>(res); +#elif defined(OS_WIN) + SYSTEM_INFO system_info = {}; + ::GetNativeSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; +#else +#error +#endif +}
diff --git a/util/sys_info.h b/util/sys_info.h new file mode 100644 index 0000000..68133e1 --- /dev/null +++ b/util/sys_info.h
@@ -0,0 +1,13 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UTIL_SYS_INFO_H_ +#define UTIL_SYS_INFO_H_ + +#include <string> + +std::string OperatingSystemArchitecture(); +int NumberOfProcessors(); + +#endif // UTIL_SYS_INFO_H_
diff --git a/util/task.h b/util/task.h new file mode 100644 index 0000000..278ff13 --- /dev/null +++ b/util/task.h
@@ -0,0 +1,13 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UTIL_TASK_H_ +#define UTIL_TASK_H_ + +#include "base/bind.h" +#include "base/callback.h" + +using Task = base::OnceClosure; + +#endif // UTIL_TASK_H_
diff --git a/util/test/gn_test.cc b/util/test/gn_test.cc new file mode 100644 index 0000000..6b2e26d --- /dev/null +++ b/util/test/gn_test.cc
@@ -0,0 +1,162 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "base/command_line.h" +#include "util/build_config.h" +#include "util/test/test.h" + +#if defined(OS_WIN) +#include <windows.h> +#else +#include <unistd.h> +#endif + +namespace testing { +Test* g_current_test; +} // namespace testing + +struct RegisteredTest { + testing::Test* (*factory)(); + const char* name; + bool should_run; +}; + +// This can't be a vector because tests call RegisterTest from static +// initializers and the order static initializers run it isn't specified. So +// the vector constructor isn't guaranteed to run before all of the +// RegisterTest() calls. +static RegisteredTest tests[10000]; +static int ntests; + +void RegisterTest(testing::Test* (*factory)(), const char* name) { + tests[ntests].factory = factory; + tests[ntests++].name = name; +} + +namespace { + +bool PatternMatchesString(const char* pattern, const char* str) { + switch (*pattern) { + case '\0': + case '-': + return *str == '\0'; + case '*': + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: + return *pattern == *str && PatternMatchesString(pattern + 1, str + 1); + } +} + +bool TestMatchesFilter(const char* test, const char* filter) { + // Split --gtest_filter at '-' into positive and negative filters. + const char* const dash = strchr(filter, '-'); + const char* pos = + dash == filter ? "*" : filter; // Treat '-test1' as '*-test1' + const char* neg = dash ? dash + 1 : ""; + return PatternMatchesString(pos, test) && !PatternMatchesString(neg, test); +} + +#if defined(OS_WIN) +struct ScopedEnableVTEscapeProcessing { + ScopedEnableVTEscapeProcessing() { + console_ = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + if (GetConsoleScreenBufferInfo(console_, &csbi) && + GetConsoleMode(console_, &original_mode_)) { + SetConsoleMode(console_, original_mode_ | + ENABLE_VIRTUAL_TERMINAL_PROCESSING | + DISABLE_NEWLINE_AUTO_RETURN); + } else { + console_ = INVALID_HANDLE_VALUE; + } + } + + ~ScopedEnableVTEscapeProcessing() { + if (is_valid()) + SetConsoleMode(console_, original_mode_); + } + + bool is_valid() const { return console_ != INVALID_HANDLE_VALUE; } + + HANDLE console_; + DWORD original_mode_; +}; +#endif + +} // namespace + +int main(int argc, char** argv) { + base::CommandLine::Init(argc, argv); + +#if defined(OS_WIN) + ScopedEnableVTEscapeProcessing enable_vt_processing; +#endif + setvbuf(stdout, NULL, _IOLBF, BUFSIZ); + + int tests_started = 0; + + const char* test_filter = "*"; + for (int i = 1; i < argc; ++i) { + const char kTestFilterPrefix[] = "--gtest_filter="; + if (strncmp(argv[i], kTestFilterPrefix, strlen(kTestFilterPrefix)) == 0) { + test_filter = &argv[i][strlen(kTestFilterPrefix)]; + } + } + + int num_active_tests = 0; + for (int i = 0; i < ntests; i++) { + tests[i].should_run = TestMatchesFilter(tests[i].name, test_filter); + if (tests[i].should_run) { + ++num_active_tests; + } + } + + const char* prefix = ""; + const char* suffix = "\n"; +#if defined(OS_WIN) + if (enable_vt_processing.is_valid()) +#else + if (isatty(1)) +#endif + { + prefix = "\r"; + suffix = "\x1B[K"; + } + bool passed = true; + for (int i = 0; i < ntests; i++) { + if (!tests[i].should_run) + continue; + + ++tests_started; + testing::Test* test = tests[i].factory(); + printf("%s[%d/%d] %s%s", prefix, tests_started, num_active_tests, + tests[i].name, suffix); + test->SetUp(); + test->Run(); + test->TearDown(); + if (test->Failed()) + passed = false; + delete test; + } + + printf("\n%s\n", passed ? "PASSED" : "FAILED"); + fflush(stdout); + return passed ? EXIT_SUCCESS : EXIT_FAILURE; +}
diff --git a/util/test/test.h b/util/test/test.h new file mode 100644 index 0000000..b5539d7 --- /dev/null +++ b/util/test/test.h
@@ -0,0 +1,195 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UTIL_TEST_TEST_H_ +#define UTIL_TEST_TEST_H_ + +#include <string.h> + +#include <sstream> +#include <string> + +// This is a minimal googletest-like testing framework. It's originally derived +// from Ninja's src/test.h. You might prefer that one if you have different +// tradeoffs (in particular, if you don't need to stream message to assertion +// failures, Ninja's is a bit simpler.) +namespace testing { + +class Test { + public: + Test() : failed_(false) {} + virtual ~Test() {} + virtual void SetUp() {} + virtual void TearDown() {} + virtual void Run() = 0; + + bool Failed() const { return failed_; } + + private: + friend class TestResult; + + bool failed_; + int assertion_failures_; +}; + +extern testing::Test* g_current_test; + +class TestResult { + public: + TestResult(bool condition, const char* error) + : condition_(condition), error_(error) { + if (!condition) + g_current_test->failed_ = true; + } + + operator bool() const { return condition_; } + const char* error() const { return error_; } + + private: + bool condition_; + const char* error_; +}; + +class Message { + public: + Message() {} + ~Message() { printf("%s\n\n", ss_.str().c_str()); } + + template <typename T> + inline Message& operator<<(const T& val) { + ss_ << val; + return *this; + } + + private: + std::stringstream ss_; +}; + +class AssertHelper { + public: + AssertHelper(const char* file, int line, const TestResult& test_result) + : file_(file), line_(line), error_(test_result.error()) {} + + void operator=(const Message& message) const { + printf("\n*** FAILURE %s:%d: %s\n", file_, line_, error_); + } + + private: + const char* file_; + int line_; + const char* error_; +}; + +} // namespace testing + +void RegisterTest(testing::Test* (*)(), const char*); + +#define TEST_F_(x, y, name) \ + struct y : public x { \ + static testing::Test* Create() { return testing::g_current_test = new y; } \ + virtual void Run(); \ + }; \ + struct Register##y { \ + Register##y() { RegisterTest(y::Create, name); } \ + }; \ + Register##y g_register_##y; \ + void y::Run() + +#define TEST_F(x, y) TEST_F_(x, x##y, #x "." #y) +#define TEST(x, y) TEST_F_(testing::Test, x##y, #x "." #y) + +#define FRIEND_TEST(x, y) friend class x##y + +// Some compilers emit a warning if nested "if" statements are followed by an +// "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (something) +// ASSERT_TRUE(condition) << "Some message"; +#define TEST_AMBIGUOUS_ELSE_BLOCKER_ \ + switch (0) \ + case 0: \ + default: + +#define TEST_ASSERT_(expression, on_failure) \ + TEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::TestResult test_result = (expression)) \ + ; \ + else \ + on_failure(test_result) + +#define TEST_NONFATAL_FAILURE_(message) \ + ::testing::AssertHelper(__FILE__, __LINE__, message) = ::testing::Message() + +#define TEST_FATAL_FAILURE_(message) \ + return ::testing::AssertHelper(__FILE__, __LINE__, message) = \ + ::testing::Message() + +#define EXPECT_EQ(a, b) \ + TEST_ASSERT_(::testing::TestResult(a == b, #a " == " #b), \ + TEST_NONFATAL_FAILURE_) + +#define EXPECT_NE(a, b) \ + TEST_ASSERT_(::testing::TestResult(a != b, #a " != " #b), \ + TEST_NONFATAL_FAILURE_) + +#define EXPECT_LT(a, b) \ + TEST_ASSERT_(::testing::TestResult(a < b, #a " < " #b), \ + TEST_NONFATAL_FAILURE_) + +#define EXPECT_GT(a, b) \ + TEST_ASSERT_(::testing::TestResult(a > b, #a " > " #b), \ + TEST_NONFATAL_FAILURE_) + +#define EXPECT_LE(a, b) \ + TEST_ASSERT_(::testing::TestResult(a <= b, #a " <= " #b), \ + TEST_NONFATAL_FAILURE_) + +#define EXPECT_GE(a, b) \ + TEST_ASSERT_(::testing::TestResult(a >= b, #a " >= " #b), \ + TEST_NONFATAL_FAILURE_) + +#define EXPECT_TRUE(a) \ + TEST_ASSERT_(::testing::TestResult(static_cast<bool>(a), #a), \ + TEST_NONFATAL_FAILURE_) + +#define EXPECT_FALSE(a) \ + TEST_ASSERT_(::testing::TestResult(!static_cast<bool>(a), #a), \ + TEST_NONFATAL_FAILURE_) + +#define EXPECT_STREQ(a, b) \ + TEST_ASSERT_(::testing::TestResult(strcmp(a, b) == 0, #a " str== " #b), \ + TEST_NONFATAL_FAILURE_) + +#define ASSERT_EQ(a, b) \ + TEST_ASSERT_(::testing::TestResult(a == b, #a " == " #b), TEST_FATAL_FAILURE_) + +#define ASSERT_NE(a, b) \ + TEST_ASSERT_(::testing::TestResult(a != b, #a " != " #b), TEST_FATAL_FAILURE_) + +#define ASSERT_LT(a, b) \ + TEST_ASSERT_(::testing::TestResult(a < b, #a " < " #b), TEST_FATAL_FAILURE_) + +#define ASSERT_GT(a, b) \ + TEST_ASSERT_(::testing::TestResult(a > b, #a " > " #b), TEST_FATAL_FAILURE_) + +#define ASSERT_LE(a, b) \ + TEST_ASSERT_(::testing::TestResult(a <= b, #a " <= " #b), TEST_FATAL_FAILURE_) + +#define ASSERT_GE(a, b) \ + TEST_ASSERT_(::testing::TestResult(a >= b, #a " >= " #b), TEST_FATAL_FAILURE_) + +#define ASSERT_TRUE(a) \ + TEST_ASSERT_(::testing::TestResult(static_cast<bool>(a), #a), \ + TEST_FATAL_FAILURE_) + +#define ASSERT_FALSE(a) \ + TEST_ASSERT_(::testing::TestResult(!static_cast<bool>(a), #a), \ + TEST_FATAL_FAILURE_) + +#define ASSERT_STREQ(a, b) \ + TEST_ASSERT_(::testing::TestResult(strcmp(a, b) == 0, #a " str== " #b), \ + TEST_FATAL_FAILURE_) + +#endif // UTIL_TEST_TEST_H_
diff --git a/util/worker_pool.cc b/util/worker_pool.cc new file mode 100644 index 0000000..92fadd4 --- /dev/null +++ b/util/worker_pool.cc
@@ -0,0 +1,97 @@ +// Copyright 2018 The Chromium 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 "util/worker_pool.h" + +#include "base/command_line.h" +#include "base/strings/string_number_conversions.h" +#include "tools/gn/switches.h" +#include "util/sys_info.h" + +namespace { + +int GetThreadCount() { + std::string thread_count = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kThreads); + + // See if an override was specified on the command line. + int result; + if (!thread_count.empty() && base::StringToInt(thread_count, &result) && + result >= 1) { + return result; + } + + // Base the default number of worker threads on number of cores in the + // system. When building large projects, the speed can be limited by how fast + // the main thread can dispatch work and connect the dependency graph. If + // there are too many worker threads, the main thread can be starved and it + // will run slower overall. + // + // One less worker thread than the number of physical CPUs seems to be a + // good value, both theoretically and experimentally. But always use at + // least some workers to prevent us from being too sensitive to I/O latency + // on low-end systems. + // + // The minimum thread count is based on measuring the optimal threads for the + // Chrome build on a several-year-old 4-core MacBook. + // Almost all CPUs now are hyperthreaded. + int num_cores = NumberOfProcessors() / 2; + return std::max(num_cores - 1, 8); +} + +} // namespace + +WorkerPool::WorkerPool() : WorkerPool(GetThreadCount()) {} + +WorkerPool::WorkerPool(size_t thread_count) : should_stop_processing_(false) { + threads_.reserve(thread_count); + for (size_t i = 0; i < thread_count; ++i) + threads_.emplace_back([this]() { Worker(); }); +} + +WorkerPool::~WorkerPool() { + { + std::unique_lock<std::mutex> queue_lock(queue_mutex_); + should_stop_processing_ = true; + } + + pool_notifier_.notify_all(); + + for (auto& task_thread : threads_) { + task_thread.join(); + } +} + +void WorkerPool::PostTask(Task work) { + { + std::unique_lock<std::mutex> queue_lock(queue_mutex_); + CHECK(!should_stop_processing_); + task_queue_.emplace(std::move(work)); + } + + pool_notifier_.notify_one(); +} + +void WorkerPool::Worker() { + for (;;) { + Task task; + + { + std::unique_lock<std::mutex> queue_lock(queue_mutex_); + + pool_notifier_.wait(queue_lock, [this]() { + return (!task_queue_.empty()) || should_stop_processing_; + }); + + if (should_stop_processing_ && task_queue_.empty()) + return; + + task = std::move(task_queue_.front()); + task_queue_.pop(); + } + + std::move(task).Run(); + } +}
diff --git a/util/worker_pool.h b/util/worker_pool.h new file mode 100644 index 0000000..d061644 --- /dev/null +++ b/util/worker_pool.h
@@ -0,0 +1,37 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UTIL_WORKER_POOL_H_ +#define UTIL_WORKER_POOL_H_ + +#include <condition_variable> +#include <mutex> +#include <queue> +#include <thread> + +#include "base/logging.h" +#include "base/macros.h" +#include "util/task.h" + +class WorkerPool { + public: + WorkerPool(); + WorkerPool(size_t thread_count); + ~WorkerPool(); + + void PostTask(Task work); + + private: + void Worker(); + + std::vector<std::thread> threads_; + std::queue<base::OnceClosure> task_queue_; + std::mutex queue_mutex_; + std::condition_variable_any pool_notifier_; + bool should_stop_processing_; + + DISALLOW_COPY_AND_ASSIGN(WorkerPool); +}; + +#endif // UTIL_WORKER_POOL_H_