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_