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_