diff options
Diffstat (limited to 'ipc/chromium/src/base')
150 files changed, 28035 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/at_exit.cc b/ipc/chromium/src/base/at_exit.cc new file mode 100644 index 0000000000..9481f36e2f --- /dev/null +++ b/ipc/chromium/src/base/at_exit.cc @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/at_exit.h" +#include "base/logging.h" + +namespace base { + +// Keep a stack of registered AtExitManagers. We always operate on the most +// recent, and we should never have more than one outside of testing, when we +// use the shadow version of the constructor. We don't protect this for +// thread-safe access, since it will only be modified in testing. +static AtExitManager* g_top_manager = NULL; + +AtExitManager::AtExitManager() : next_manager_(NULL) { + DCHECK(!g_top_manager); + g_top_manager = this; +} + +AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) { + DCHECK(shadow || !g_top_manager); + g_top_manager = this; +} + +AtExitManager::~AtExitManager() { + if (!g_top_manager) { + NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager"; + return; + } + DCHECK(g_top_manager == this); + + ProcessCallbacksNow(); + g_top_manager = next_manager_; +} + +// static +void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { + if (!g_top_manager) { + NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; + return; + } + + DCHECK(func); + + AutoLock lock(g_top_manager->lock_); + g_top_manager->stack_.push(CallbackAndParam(func, param)); +} + +// static +void AtExitManager::ProcessCallbacksNow() { + if (!g_top_manager) { + NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager"; + return; + } + + AutoLock lock(g_top_manager->lock_); + + while (!g_top_manager->stack_.empty()) { + CallbackAndParam callback_and_param = g_top_manager->stack_.top(); + g_top_manager->stack_.pop(); + + callback_and_param.func_(callback_and_param.param_); + } +} + +// static +bool AtExitManager::AlreadyRegistered() { + return !!g_top_manager; +} + +} // namespace base diff --git a/ipc/chromium/src/base/at_exit.h b/ipc/chromium/src/base/at_exit.h new file mode 100644 index 0000000000..5644dfa52e --- /dev/null +++ b/ipc/chromium/src/base/at_exit.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_AT_EXIT_H_ +#define BASE_AT_EXIT_H_ + +#include <stack> + +#include "base/basictypes.h" +#include "base/lock.h" + +namespace base { + +// This class provides a facility similar to the CRT atexit(), except that +// we control when the callbacks are executed. Under Windows for a DLL they +// happen at a really bad time and under the loader lock. This facility is +// mostly used by base::Singleton. +// +// The usage is simple. Early in the main() or WinMain() scope create an +// AtExitManager object on the stack: +// int main(...) { +// base::AtExitManager exit_manager; +// +// } +// When the exit_manager object goes out of scope, all the registered +// callbacks and singleton destructors will be called. + +class AtExitManager { + protected: + // This constructor will allow this instance of AtExitManager to be created + // even if one already exists. This should only be used for testing! + // AtExitManagers are kept on a global stack, and it will be removed during + // destruction. This allows you to shadow another AtExitManager. + explicit AtExitManager(bool shadow); + + public: + typedef void (*AtExitCallbackType)(void*); + + AtExitManager(); + + // The dtor calls all the registered callbacks. Do not try to register more + // callbacks after this point. + ~AtExitManager(); + + // Registers the specified function to be called at exit. The prototype of + // the callback function is void func(). + static void RegisterCallback(AtExitCallbackType func, void* param); + + // Calls the functions registered with RegisterCallback in LIFO order. It + // is possible to register new callbacks after calling this function. + static void ProcessCallbacksNow(); + + static bool AlreadyRegistered(); + + private: + struct CallbackAndParam { + CallbackAndParam(AtExitCallbackType func, void* param) + : func_(func), param_(param) { } + AtExitCallbackType func_; + void* param_; + }; + + Lock lock_; + std::stack<CallbackAndParam> stack_; + AtExitManager* next_manager_; // Stack of managers to allow shadowing. + + DISALLOW_COPY_AND_ASSIGN(AtExitManager); +}; + +} // namespace base + +#endif // BASE_AT_EXIT_H_ diff --git a/ipc/chromium/src/base/atomic_sequence_num.h b/ipc/chromium/src/base/atomic_sequence_num.h new file mode 100644 index 0000000000..cd0ad95603 --- /dev/null +++ b/ipc/chromium/src/base/atomic_sequence_num.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// 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. + +#ifndef BASE_ATOMIC_SEQUENCE_NUM_H_ +#define BASE_ATOMIC_SEQUENCE_NUM_H_ + +#include "base/atomicops.h" +#include "base/basictypes.h" + +namespace base { + +class AtomicSequenceNumber; + +// Static (POD) AtomicSequenceNumber that MUST be used in global scope (or +// non-function scope) ONLY. This implementation does not generate any static +// initializer. Note that it does not implement any constructor which means +// that its fields are not initialized except when it is stored in the global +// data section (.data in ELF). If you want to allocate an atomic sequence +// number on the stack (or heap), please use the AtomicSequenceNumber class +// declared below. +class StaticAtomicSequenceNumber { + public: + inline int GetNext() { + return static_cast<int>( + base::subtle::NoBarrier_AtomicIncrement(&seq_, 1) - 1); + } + + private: + friend class AtomicSequenceNumber; + + inline void Reset() { + base::subtle::Release_Store(&seq_, 0); + } + + base::subtle::Atomic32 seq_; +}; + +// AtomicSequenceNumber that can be stored and used safely (i.e. its fields are +// always initialized as opposed to StaticAtomicSequenceNumber declared above). +// Please use StaticAtomicSequenceNumber if you want to declare an atomic +// sequence number in the global scope. +class AtomicSequenceNumber { + public: + AtomicSequenceNumber() { + seq_.Reset(); + } + + inline int GetNext() { + return seq_.GetNext(); + } + + private: + StaticAtomicSequenceNumber seq_; + DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber); +}; + +} // namespace base + +#endif // BASE_ATOMIC_SEQUENCE_NUM_H_ diff --git a/ipc/chromium/src/base/atomicops.h b/ipc/chromium/src/base/atomicops.h new file mode 100644 index 0000000000..dcb6917100 --- /dev/null +++ b/ipc/chromium/src/base/atomicops.h @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// For atomic operations on reference counts, see atomic_refcount.h. +// For atomic operations on sequence numbers, see atomic_sequence_num.h. + +// The routines exported by this module are subtle. If you use them, even if +// you get the code right, it will depend on careful reasoning about atomicity +// and memory ordering; it will be less readable, and harder to maintain. If +// you plan to use these routines, you should have a good reason, such as solid +// evidence that performance would otherwise suffer, or there being no +// alternative. You should assume only properties explicitly guaranteed by the +// specifications in this file. You are almost certainly _not_ writing code +// just for the x86; if you assume x86 semantics, x86 hardware bugs and +// implementations on other archtectures will cause your code to break. If you +// do not know what you are doing, avoid these routines, and use a Mutex. +// +// It is incorrect to make direct assignments to/from an atomic variable. +// You should use one of the Load or Store routines. The NoBarrier +// versions are provided when no barriers are needed: +// NoBarrier_Store() +// NoBarrier_Load() +// Although there are currently no compiler enforcement, you are encouraged +// to use these. +// + +#ifndef BASE_ATOMICOPS_H_ +#define BASE_ATOMICOPS_H_ + +#include "base/basictypes.h" +#include "base/port.h" + +namespace base { +namespace subtle { + +// Bug 1308991. We need this for /Wp64, to mark it safe for AtomicWord casting. +#ifndef OS_WIN +#define __w64 +#endif +typedef __w64 int32_t Atomic32; +#ifdef ARCH_CPU_64_BITS +typedef int64_t Atomic64; +#endif + +// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or +// Atomic64 routines below, depending on your architecture. +#ifdef OS_OPENBSD +#ifdef ARCH_CPU_64_BITS +typedef Atomic64 AtomicWord; +#else +typedef Atomic32 AtomicWord; +#endif // ARCH_CPU_64_BITS +#else +typedef intptr_t AtomicWord; +#endif // OS_OPENBSD + +// Atomically execute: +// result = *ptr; +// if (*ptr == old_value) +// *ptr = new_value; +// return result; +// +// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value". +// Always return the old value of "*ptr" +// +// This routine implies no memory barriers. +Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); + +// Atomically store new_value into *ptr, returning the previous value held in +// *ptr. This routine implies no memory barriers. +Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value); + +// Atomically increment *ptr by "increment". Returns the new value of +// *ptr with the increment applied. This routine implies no memory barriers. +Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment); + +Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment); + +// These following lower-level operations are typically useful only to people +// implementing higher-level synchronization operations like spinlocks, +// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or +// a store with appropriate memory-ordering instructions. "Acquire" operations +// ensure that no later memory access can be reordered ahead of the operation. +// "Release" operations ensure that no previous memory access can be reordered +// after the operation. "Barrier" operations have both "Acquire" and "Release" +// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory +// access. +Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); +Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); + +void MemoryBarrier(); +void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value); +void Acquire_Store(volatile Atomic32* ptr, Atomic32 value); +void Release_Store(volatile Atomic32* ptr, Atomic32 value); + +Atomic32 NoBarrier_Load(volatile const Atomic32* ptr); +Atomic32 Acquire_Load(volatile const Atomic32* ptr); +Atomic32 Release_Load(volatile const Atomic32* ptr); + +// 64-bit atomic operations (only available on 64-bit processors). +#ifdef ARCH_CPU_64_BITS +Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value); +Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); +Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); + +Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value); +void Acquire_Store(volatile Atomic64* ptr, Atomic64 value); +void Release_Store(volatile Atomic64* ptr, Atomic64 value); +Atomic64 NoBarrier_Load(volatile const Atomic64* ptr); +Atomic64 Acquire_Load(volatile const Atomic64* ptr); +Atomic64 Release_Load(volatile const Atomic64* ptr); +#endif // CPU_ARCH_64_BITS + +} // namespace base::subtle +} // namespace base + +// Include our platform specific implementation. +#if defined(OS_WIN) && defined(ARCH_CPU_X86_FAMILY) +#include "base/atomicops_internals_x86_msvc.h" +#elif defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY) +#include "base/atomicops_internals_x86_macosx.h" +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) +#include "base/atomicops_internals_x86_gcc.h" +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL) +#include "base/atomicops_internals_arm_gcc.h" +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM64) +#include "base/atomicops_internals_arm64_gcc.h" +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS) +#include "base/atomicops_internals_mips_gcc.h" +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_PPC_FAMILY) +#include "base/atomicops_internals_ppc_gcc.h" +#else +#include "base/atomicops_internals_mutex.h" +#endif + +#endif // BASE_ATOMICOPS_H_ diff --git a/ipc/chromium/src/base/atomicops_internals_arm64_gcc.h b/ipc/chromium/src/base/atomicops_internals_arm64_gcc.h new file mode 100644 index 0000000000..c5d1a92902 --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_arm64_gcc.h @@ -0,0 +1,362 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright 2014 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 is an internal atomic implementation, use base/atomicops.h instead. + +// TODO(rmcilroy): Investigate whether we can use __sync__ intrinsics instead of +// the hand coded assembly without introducing perf regressions. +// TODO(rmcilroy): Investigate whether we can use acquire / release versions of +// exclusive load / store assembly instructions and do away with +// the barriers. + +#ifndef BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_ +#define BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_ + +#if defined(OS_QNX) +#include <sys/cpuinline.h> +#endif + +namespace base { +namespace subtle { + +inline void MemoryBarrier() { + __asm__ __volatile__ ( // NOLINT + "dmb ish \n\t" // Data memory barrier. + ::: "memory" + ); // NOLINT +} + + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %w[prev], %[ptr] \n\t" // Load the previous value. + "cmp %w[prev], %w[old_value] \n\t" + "bne 1f \n\t" + "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value. + "cbnz %w[temp], 0b \n\t" // Retry if it did not work. + "1: \n\t" + "clrex \n\t" // In case we didn't swap. + : [prev]"=&r" (prev), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [old_value]"r" (old_value), + [new_value]"r" (new_value) + : "memory", "cc" + ); // NOLINT + + return prev; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + Atomic32 result; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %w[result], %[ptr] \n\t" // Load the previous value. + "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value. + "cbnz %w[temp], 0b \n\t" // Retry if it did not work. + : [result]"=&r" (result), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [new_value]"r" (new_value) + : "memory" + ); // NOLINT + + return result; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + Atomic32 result; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %w[result], %[ptr] \n\t" // Load the previous value. + "add %w[result], %w[result], %w[increment]\n\t" + "stxr %w[temp], %w[result], %[ptr] \n\t" // Try to store the result. + "cbnz %w[temp], 0b \n\t" // Retry on failure. + : [result]"=&r" (result), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [increment]"r" (increment) + : "memory" + ); // NOLINT + + return result; +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + MemoryBarrier(); + Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment); + MemoryBarrier(); + + return result; +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %w[prev], %[ptr] \n\t" // Load the previous value. + "cmp %w[prev], %w[old_value] \n\t" + "bne 1f \n\t" + "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value. + "cbnz %w[temp], 0b \n\t" // Retry if it did not work. + "dmb ish \n\t" // Data memory barrier. + "1: \n\t" + // If the compare failed the 'dmb' is unnecessary, but we still need a + // 'clrex'. + "clrex \n\t" + : [prev]"=&r" (prev), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [old_value]"r" (old_value), + [new_value]"r" (new_value) + : "memory", "cc" + ); // NOLINT + + return prev; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev; + int32_t temp; + + MemoryBarrier(); + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %w[prev], %[ptr] \n\t" // Load the previous value. + "cmp %w[prev], %w[old_value] \n\t" + "bne 1f \n\t" + "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value. + "cbnz %w[temp], 0b \n\t" // Retry if it did not work. + "1: \n\t" + // If the compare failed the we still need a 'clrex'. + "clrex \n\t" + : [prev]"=&r" (prev), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [old_value]"r" (old_value), + [new_value]"r" (new_value) + : "memory", "cc" + ); // NOLINT + + return prev; +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +// 64-bit versions of the operations. +// See the 32-bit versions for comments. + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %[prev], %[ptr] \n\t" + "cmp %[prev], %[old_value] \n\t" + "bne 1f \n\t" + "stxr %w[temp], %[new_value], %[ptr] \n\t" + "cbnz %w[temp], 0b \n\t" + "1: \n\t" + "clrex \n\t" + : [prev]"=&r" (prev), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [old_value]"r" (old_value), + [new_value]"r" (new_value) + : "memory", "cc" + ); // NOLINT + + return prev; +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + Atomic64 result; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %[result], %[ptr] \n\t" + "stxr %w[temp], %[new_value], %[ptr] \n\t" + "cbnz %w[temp], 0b \n\t" + : [result]"=&r" (result), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [new_value]"r" (new_value) + : "memory" + ); // NOLINT + + return result; +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + Atomic64 result; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %[result], %[ptr] \n\t" + "add %[result], %[result], %[increment] \n\t" + "stxr %w[temp], %[result], %[ptr] \n\t" + "cbnz %w[temp], 0b \n\t" + : [result]"=&r" (result), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [increment]"r" (increment) + : "memory" + ); // NOLINT + + return result; +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + MemoryBarrier(); + Atomic64 result = NoBarrier_AtomicIncrement(ptr, increment); + MemoryBarrier(); + + return result; +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev; + int32_t temp; + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %[prev], %[ptr] \n\t" + "cmp %[prev], %[old_value] \n\t" + "bne 1f \n\t" + "stxr %w[temp], %[new_value], %[ptr] \n\t" + "cbnz %w[temp], 0b \n\t" + "dmb ish \n\t" + "1: \n\t" + "clrex \n\t" + : [prev]"=&r" (prev), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [old_value]"r" (old_value), + [new_value]"r" (new_value) + : "memory", "cc" + ); // NOLINT + + return prev; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev; + int32_t temp; + + MemoryBarrier(); + + __asm__ __volatile__ ( // NOLINT + "0: \n\t" + "ldxr %[prev], %[ptr] \n\t" + "cmp %[prev], %[old_value] \n\t" + "bne 1f \n\t" + "stxr %w[temp], %[new_value], %[ptr] \n\t" + "cbnz %w[temp], 0b \n\t" + "1: \n\t" + "clrex \n\t" + : [prev]"=&r" (prev), + [temp]"=&r" (temp), + [ptr]"+Q" (*ptr) + : [old_value]"r" (old_value), + [new_value]"r" (new_value) + : "memory", "cc" + ); // NOLINT + + return prev; +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_ diff --git a/ipc/chromium/src/base/atomicops_internals_arm_gcc.h b/ipc/chromium/src/base/atomicops_internals_arm_gcc.h new file mode 100644 index 0000000000..628eb3d460 --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_arm_gcc.h @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 is an internal atomic implementation, use base/atomicops.h instead. +// +// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears. + +#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ +#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ + +namespace base { +namespace subtle { + +// 0xffff0fc0 is the hard coded address of a function provided by +// the kernel which implements an atomic compare-exchange. On older +// ARM architecture revisions (pre-v6) this may be implemented using +// a syscall. This address is stable, and in active use (hard coded) +// by at least glibc-2.7 and the Android C library. +typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value, + Atomic32 new_value, + volatile Atomic32* ptr); +LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg __attribute__((weak)) = + (LinuxKernelCmpxchgFunc) 0xffff0fc0; + +typedef void (*LinuxKernelMemoryBarrierFunc)(void); +LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) = + (LinuxKernelMemoryBarrierFunc) 0xffff0fa0; + + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev_value = *ptr; + do { + if (!pLinuxKernelCmpxchg(old_value, new_value, + const_cast<Atomic32*>(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + Atomic32 old_value; + do { + old_value = *ptr; + } while (pLinuxKernelCmpxchg(old_value, new_value, + const_cast<Atomic32*>(ptr))); + return old_value; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + for (;;) { + // Atomic exchange the old value with an incremented one. + Atomic32 old_value = *ptr; + Atomic32 new_value = old_value + increment; + if (pLinuxKernelCmpxchg(old_value, new_value, + const_cast<Atomic32*>(ptr)) == 0) { + // The exchange took place as expected. + return new_value; + } + // Otherwise, *ptr changed mid-loop and we need to retry. + } + +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void MemoryBarrier() { + pLinuxKernelMemoryBarrier(); +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ diff --git a/ipc/chromium/src/base/atomicops_internals_mips_gcc.h b/ipc/chromium/src/base/atomicops_internals_mips_gcc.h new file mode 100644 index 0000000000..aec4561b2d --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_mips_gcc.h @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is an internal atomic implementation, use base/atomicops.h instead. +// +// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears. + +#ifndef GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_MIPS_GCC_H_ +#define GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_MIPS_GCC_H_ + +namespace base { +namespace subtle { + +// Atomically execute: +// result = *ptr; +// if (*ptr == old_value) +// *ptr = new_value; +// return result; +// +// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value". +// Always return the old value of "*ptr" +// +// This routine implies no memory barriers. +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev, tmp; + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "ll %0, %5\n" // prev = *ptr + "bne %0, %3, 2f\n" // if (prev != old_value) goto 2 + "move %2, %4\n" // tmp = new_value + "sc %2, %1\n" // *ptr = tmp (with atomic check) + "beqz %2, 1b\n" // start again on atomic error + "nop\n" // delay slot nop + "2:\n" + ".set pop\n" + : "=&r" (prev), "=m" (*ptr), "=&r" (tmp) + : "r" (old_value), "r" (new_value), "m" (*ptr) + : "memory"); + return prev; +} + +// Atomically store new_value into *ptr, returning the previous value held in +// *ptr. This routine implies no memory barriers. +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + Atomic32 temp, old; + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "ll %1, %4\n" // old = *ptr + "move %0, %3\n" // temp = new_value + "sc %0, %2\n" // *ptr = temp (with atomic check) + "beqz %0, 1b\n" // start again on atomic error + "nop\n" // delay slot nop + ".set pop\n" + : "=&r" (temp), "=&r" (old), "=m" (*ptr) + : "r" (new_value), "m" (*ptr) + : "memory"); + + return old; +} + +// Atomically increment *ptr by "increment". Returns the new value of +// *ptr with the increment applied. This routine implies no memory barriers. +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + Atomic32 temp, temp2; + + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "ll %0, %4\n" // temp = *ptr + "addu %1, %0, %3\n" // temp2 = temp + increment + "sc %1, %2\n" // *ptr = temp2 (with atomic check) + "beqz %1, 1b\n" // start again on atomic error + "addu %1, %0, %3\n" // temp2 = temp + increment + ".set pop\n" + : "=&r" (temp), "=&r" (temp2), "=m" (*ptr) + : "Ir" (increment), "m" (*ptr) + : "memory"); + // temp2 now holds the final value. + return temp2; +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + MemoryBarrier(); + Atomic32 res = NoBarrier_AtomicIncrement(ptr, increment); + MemoryBarrier(); + return res; +} + +// "Acquire" operations +// ensure that no later memory access can be reordered ahead of the operation. +// "Release" operations ensure that no previous memory access can be reordered +// after the operation. "Barrier" operations have both "Acquire" and "Release" +// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory +// access. +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + MemoryBarrier(); + return res; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + MemoryBarrier(); + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void MemoryBarrier() { + __asm__ __volatile__("sync" : : : "memory"); +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +#if defined(__LP64__) +// 64-bit versions of the atomic ops. + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev, tmp; + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "lld %0, %5\n" // prev = *ptr + "bne %0, %3, 2f\n" // if (prev != old_value) goto 2 + "move %2, %4\n" // tmp = new_value + "scd %2, %1\n" // *ptr = tmp (with atomic check) + "beqz %2, 1b\n" // start again on atomic error + "nop\n" // delay slot nop + "2:\n" + ".set pop\n" + : "=&r" (prev), "=m" (*ptr), "=&r" (tmp) + : "r" (old_value), "r" (new_value), "m" (*ptr) + : "memory"); + return prev; +} + +// Atomically store new_value into *ptr, returning the previous value held in +// *ptr. This routine implies no memory barriers. +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + Atomic64 temp, old; + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "lld %1, %4\n" // old = *ptr + "move %0, %3\n" // temp = new_value + "scd %0, %2\n" // *ptr = temp (with atomic check) + "beqz %0, 1b\n" // start again on atomic error + "nop\n" // delay slot nop + ".set pop\n" + : "=&r" (temp), "=&r" (old), "=m" (*ptr) + : "r" (new_value), "m" (*ptr) + : "memory"); + + return old; +} + +// Atomically increment *ptr by "increment". Returns the new value of +// *ptr with the increment applied. This routine implies no memory barriers. +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + Atomic64 temp, temp2; + + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "lld %0, %4\n" // temp = *ptr + "daddu %1, %0, %3\n" // temp2 = temp + increment + "scd %1, %2\n" // *ptr = temp2 (with atomic check) + "beqz %1, 1b\n" // start again on atomic error + "daddu %1, %0, %3\n" // temp2 = temp + increment + ".set pop\n" + : "=&r" (temp), "=&r" (temp2), "=m" (*ptr) + : "Ir" (increment), "m" (*ptr) + : "memory"); + // temp2 now holds the final value. + return temp2; +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + MemoryBarrier(); + Atomic64 res = NoBarrier_AtomicIncrement(ptr, increment); + MemoryBarrier(); + return res; +} + +// "Acquire" operations +// ensure that no later memory access can be reordered ahead of the operation. +// "Release" operations ensure that no previous memory access can be reordered +// after the operation. "Barrier" operations have both "Acquire" and "Release" +// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory +// access. +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + MemoryBarrier(); + return res; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + MemoryBarrier(); + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} +#endif + +} // namespace base::subtle +} // namespace base + +#endif // GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_MIPS_GCC_H_ diff --git a/ipc/chromium/src/base/atomicops_internals_mutex.cc b/ipc/chromium/src/base/atomicops_internals_mutex.cc new file mode 100644 index 0000000000..b129c86e82 --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_mutex.cc @@ -0,0 +1,15 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "base/atomicops.h" + +namespace base { +namespace subtle { + +Lock gAtomicsMutex; + +} // namespace subtle +} // namespace base diff --git a/ipc/chromium/src/base/atomicops_internals_mutex.h b/ipc/chromium/src/base/atomicops_internals_mutex.h new file mode 100644 index 0000000000..f8fe4c6acb --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_mutex.h @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This file is an internal atomic implementation, use +// base/atomicops.h instead. +// +// This is a very slow fallback implementation of atomic operations +// that uses a mutex instead of atomic instructions. +// +// (NB: a small "optimization" here would be using a spinlock instead +// of a blocking mutex, but it's probably not worth the time.) + +#ifndef base_atomicops_internals_mutex_h +#define base_atomicops_internals_mutex_h + +#include "base/lock.h" + +namespace base { +namespace subtle { + +extern Lock gAtomicsMutex; + +template<typename T> +T Locked_CAS(volatile T* ptr, T old_value, T new_value) { + AutoLock _(gAtomicsMutex); + + T current_value = *ptr; + if (current_value == old_value) + *ptr = new_value; + + return current_value; +} + +template<typename T> +T Locked_AtomicExchange(volatile T* ptr, T new_value) { + AutoLock _(gAtomicsMutex); + + T current_value = *ptr; + *ptr = new_value; + return current_value; +} + +template<typename T> +T Locked_AtomicIncrement(volatile T* ptr, T increment) { + AutoLock _(gAtomicsMutex); + return *ptr += increment; +} + +template<typename T> +void Locked_Store(volatile T* ptr, T value) { + AutoLock _(gAtomicsMutex); + *ptr = value; +} + +template<typename T> +T Locked_Load(volatile const T* ptr) { + AutoLock _(gAtomicsMutex); + return *ptr; +} + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return Locked_CAS(ptr, old_value, new_value); +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + return Locked_AtomicExchange(ptr, new_value); +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return Locked_AtomicIncrement(ptr, increment); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return Locked_AtomicIncrement(ptr, increment); +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return Locked_CAS(ptr, old_value, new_value); +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return Locked_CAS(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + return Locked_Store(ptr, value); +} + +inline void MemoryBarrier() { + AutoLock _(gAtomicsMutex); + // lock/unlock work as a barrier here +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + return Locked_Store(ptr, value); +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + return Locked_Store(ptr, value); +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return Locked_Load(ptr); +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + return NoBarrier_Load(ptr); +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + return Locked_Load(ptr); +} + +#ifdef ARCH_CPU_64_BITS + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + return Locked_CAS(ptr, old_value, new_value); +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + return Locked_AtomicExchange(ptr, new_value); +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return Locked_AtomicIncrement(ptr, increment); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return Locked_AtomicIncrement(ptr, increment); +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + return Locked_Store(ptr, value); +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + return Locked_CAS(ptr, old_value, new_value); +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + return Locked_Store(ptr, value); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + return Locked_Store(ptr, value); +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return Locked_Load(ptr); +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + return Locked_Load(ptr); +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + return Locked_Load(ptr); +} + +#endif // ARCH_CPU_64_BITS + +#ifdef OS_MACOSX +// From atomicops_internals_x86_macosx.h: +// +// MacOS uses long for intptr_t, AtomicWord and Atomic32 are always +// different on the Mac, even when they are the same size. We need +// to explicitly cast from AtomicWord to Atomic32/64 to implement +// the AtomicWord interface. + +inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return Locked_CAS(ptr, old_value, new_value); +} + +inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr, + AtomicWord new_value) { + return Locked_AtomicExchange(ptr, new_value); +} + +inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return Locked_AtomicIncrement(ptr, increment); +} + +inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return Locked_AtomicIncrement(ptr, increment); +} + +inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return Locked_CAS(ptr, old_value, new_value); +} + +inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return Locked_CAS(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) { + return Locked_Store(ptr, value); +} + +inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) { + return Locked_Store(ptr, value); +} + +inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) { + return Locked_Store(ptr, value); +} + +inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) { + return Locked_Load(ptr); +} + +inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) { + return Locked_Load(ptr); +} + +inline AtomicWord Release_Load(volatile const AtomicWord* ptr) { + return Locked_Load(ptr); +} + +#endif // OS_MACOSX + +} // namespace subtle +} // namespace base + +#endif // base_atomicops_internals_mutex_h diff --git a/ipc/chromium/src/base/atomicops_internals_ppc_gcc.h b/ipc/chromium/src/base/atomicops_internals_ppc_gcc.h new file mode 100644 index 0000000000..90c01e4a32 --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_ppc_gcc.h @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright 2012 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. +// This file is an internal atomic implementation, use atomicops.h instead. +// +#ifndef BASE_ATOMICOPS_INTERNALS_PPC_H_ +#define BASE_ATOMICOPS_INTERNALS_PPC_H_ +namespace base { +namespace subtle { +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return (__sync_val_compare_and_swap(ptr, old_value, new_value)); +} +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + Atomic32 old_value; + do { + old_value = *ptr; + } while (__sync_bool_compare_and_swap(ptr, old_value, new_value) == false); + return old_value; +} +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + for (;;) { + Atomic32 old_value = *ptr; + Atomic32 new_value = old_value + increment; + if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) { + return new_value; + // The exchange took place as expected. + } + // Otherwise, *ptr changed mid-loop and we need to retry. + } +} +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} +inline void MemoryBarrier() { + __asm__ __volatile__("sync" : : : "memory"); } +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + MemoryBarrier(); + *ptr = value; +} +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return *ptr; } +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; + MemoryBarrier(); + return value; +} +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} +#ifdef ARCH_CPU_PPC64 +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + return (__sync_val_compare_and_swap(ptr, old_value, new_value)); +} +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + Atomic64 old_value; + do { + old_value = *ptr; + } while (__sync_bool_compare_and_swap(ptr, old_value, new_value) == false); + return old_value; +} +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + for (;;) { + Atomic64 old_value = *ptr; + Atomic64 new_value = old_value + increment; + if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) { + return new_value; + // The exchange took place as expected. + } + // Otherwise, *ptr changed mid-loop and we need to retry. + } +} +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, Atomic64 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, Atomic64 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + MemoryBarrier(); + *ptr = value; +} +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { return *ptr; } +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value = *ptr; + MemoryBarrier(); + return value; +} +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} +#endif +} // namespace base::subtle +} // namespace base +#endif // BASE_ATOMICOPS_INTERNALS_PPC_GCC_H_ diff --git a/ipc/chromium/src/base/atomicops_internals_x86_gcc.cc b/ipc/chromium/src/base/atomicops_internals_x86_gcc.cc new file mode 100644 index 0000000000..c5b11b31c5 --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_x86_gcc.cc @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 module gets enough CPU information to optimize the +// atomicops module on x86. + +#include <string.h> + +#include "base/atomicops.h" +#include "base/basictypes.h" + +// This file only makes sense with atomicops_internals_x86_gcc.h -- it +// depends on structs that are defined in that file. If atomicops.h +// doesn't sub-include that file, then we aren't needed, and shouldn't +// try to do anything. +#ifdef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ + +// Inline cpuid instruction. In PIC compilations, %ebx contains the address +// of the global offset table. To avoid breaking such executables, this code +// must preserve that register's value across cpuid instructions. +#if defined(__i386__) +#define cpuid(a, b, c, d, inp) \ + asm ("mov %%ebx, %%edi\n" \ + "cpuid\n" \ + "xchg %%edi, %%ebx\n" \ + : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) +#elif defined (__x86_64__) +#define cpuid(a, b, c, d, inp) \ + asm ("mov %%rbx, %%rdi\n" \ + "cpuid\n" \ + "xchg %%rdi, %%rbx\n" \ + : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) +#endif + +#if defined(cpuid) // initialize the struct only on x86 + +// Set the flags so that code will run correctly and conservatively, so even +// if we haven't been initialized yet, we're probably single threaded, and our +// default values should hopefully be pretty safe. +struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = { + false, // bug can't exist before process spawns multiple threads + false, // no SSE2 +}; + +// Initialize the AtomicOps_Internalx86CPUFeatures struct. +static void AtomicOps_Internalx86CPUFeaturesInit() { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + + // Get vendor string (issue CPUID with eax = 0) + cpuid(eax, ebx, ecx, edx, 0); + char vendor[13]; + memcpy(vendor, &ebx, 4); + memcpy(vendor + 4, &edx, 4); + memcpy(vendor + 8, &ecx, 4); + vendor[12] = 0; + + // get feature flags in ecx/edx, and family/model in eax + cpuid(eax, ebx, ecx, edx, 1); + + int family = (eax >> 8) & 0xf; // family and model fields + int model = (eax >> 4) & 0xf; + if (family == 0xf) { // use extended family and model fields + family += (eax >> 20) & 0xff; + model += ((eax >> 16) & 0xf) << 4; + } + + // Opteron Rev E has a bug in which on very rare occasions a locked + // instruction doesn't act as a read-acquire barrier if followed by a + // non-locked read-modify-write instruction. Rev F has this bug in + // pre-release versions, but not in versions released to customers, + // so we test only for Rev E, which is family 15, model 32..63 inclusive. + if (strcmp(vendor, "AuthenticAMD") == 0 && // AMD + family == 15 && + 32 <= model && model <= 63) { + AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = true; + } else { + AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = false; + } + + // edx bit 26 is SSE2 which we use to tell use whether we can use mfence + AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1); +} + +namespace { + +class AtomicOpsx86Initializer { + public: + AtomicOpsx86Initializer() { + AtomicOps_Internalx86CPUFeaturesInit(); + } +}; + +// A global to get use initialized on startup via static initialization :/ +AtomicOpsx86Initializer g_initer; + +} // namespace + +#endif // if x86 + +#endif // ifdef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ diff --git a/ipc/chromium/src/base/atomicops_internals_x86_gcc.h b/ipc/chromium/src/base/atomicops_internals_x86_gcc.h new file mode 100644 index 0000000000..8088e7f6a4 --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_x86_gcc.h @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 is an internal atomic implementation, use base/atomicops.h instead. + +#ifndef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ +#define BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ + +// This struct is not part of the public API of this module; clients may not +// use it. +// Features of this x86. Values may not be correct before main() is run, +// but are set conservatively. +struct AtomicOps_x86CPUFeatureStruct { + bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence + // after acquire compare-and-swap. + bool has_sse2; // Processor has SSE2. +}; +extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures; + +#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory") + +namespace base { +namespace subtle { + +// 32-bit low-level operations on any platform. + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev; + __asm__ __volatile__("lock; cmpxchgl %1,%2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + __asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg. + : "=r" (new_value) + : "m" (*ptr), "0" (new_value) + : "memory"); + return new_value; // Now it's the previous value. +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + Atomic32 temp = increment; + __asm__ __volatile__("lock; xaddl %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + // temp now holds the old value of *ptr + return temp + increment; +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + Atomic32 temp = increment; + __asm__ __volatile__("lock; xaddl %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + // temp now holds the old value of *ptr + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return temp + increment; +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return x; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +#if defined(__x86_64__) + +// 64-bit implementations of memory barrier can be simpler, because it +// "mfence" is guaranteed to exist. +inline void MemoryBarrier() { + __asm__ __volatile__("mfence" : : : "memory"); +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +#else + +inline void MemoryBarrier() { + if (AtomicOps_Internalx86CPUFeatures.has_sse2) { + __asm__ __volatile__("mfence" : : : "memory"); + } else { // mfence is faster but not present on PIII + Atomic32 x = 0; + NoBarrier_AtomicExchange(&x, 0); // acts as a barrier on PIII + } +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + if (AtomicOps_Internalx86CPUFeatures.has_sse2) { + *ptr = value; + __asm__ __volatile__("mfence" : : : "memory"); + } else { + NoBarrier_AtomicExchange(ptr, value); + // acts as a barrier on PIII + } +} +#endif + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + ATOMICOPS_COMPILER_BARRIER(); + *ptr = value; // An x86 store acts as a release barrier. + // See comments in Atomic64 version of Release_Store(), below. +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; // An x86 load acts as a acquire barrier. + // See comments in Atomic64 version of Release_Store(), below. + ATOMICOPS_COMPILER_BARRIER(); + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +#if defined(__x86_64__) + +// 64-bit low-level operations on 64-bit platform. + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev; + __asm__ __volatile__("lock; cmpxchgq %1,%2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev; +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + __asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg. + : "=r" (new_value) + : "m" (*ptr), "0" (new_value) + : "memory"); + return new_value; // Now it's the previous value. +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + Atomic64 temp = increment; + __asm__ __volatile__("lock; xaddq %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + // temp now contains the previous value of *ptr + return temp + increment; +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + Atomic64 temp = increment; + __asm__ __volatile__("lock; xaddq %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + // temp now contains the previous value of *ptr + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return temp + increment; +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + /* XXX/cjones: no idea if this is necessary... */ + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return x; +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + ATOMICOPS_COMPILER_BARRIER(); + + *ptr = value; // An x86 store acts as a release barrier + // for current AMD/Intel chips as of Jan 2008. + // See also Acquire_Load(), below. + + // When new chips come out, check: + // IA-32 Intel Architecture Software Developer's Manual, Volume 3: + // System Programming Guide, Chatper 7: Multiple-processor management, + // Section 7.2, Memory Ordering. + // Last seen at: + // http://developer.intel.com/design/pentium4/manuals/index_new.htm + // + // x86 stores/loads fail to act as barriers for a few instructions (clflush + // maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are + // not generated by the compiler, and are rare. Users of these instructions + // need to know about cache behaviour in any case since all of these involve + // either flushing cache lines or non-temporal cache hints. +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value = *ptr; // An x86 load acts as a acquire barrier, + // for current AMD/Intel chips as of Jan 2008. + // See also Release_Store(), above. + ATOMICOPS_COMPILER_BARRIER(); + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} +#endif // defined(__x86_64__) + +} // namespace base::subtle +} // namespace base + +#undef ATOMICOPS_COMPILER_BARRIER + +#endif // BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ diff --git a/ipc/chromium/src/base/atomicops_internals_x86_macosx.h b/ipc/chromium/src/base/atomicops_internals_x86_macosx.h new file mode 100644 index 0000000000..c8f3a67af8 --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_x86_macosx.h @@ -0,0 +1,281 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 is an internal atomic implementation, use base/atomicops.h instead. + +#ifndef BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_ +#define BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_ + +#include <libkern/OSAtomic.h> + +namespace base { +namespace subtle { + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev_value; + do { + if (OSAtomicCompareAndSwap32(old_value, new_value, + const_cast<Atomic32*>(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr, + Atomic32 new_value) { + Atomic32 old_value; + do { + old_value = *ptr; + } while (!OSAtomicCompareAndSwap32(old_value, new_value, + const_cast<Atomic32*>(ptr))); + return old_value; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr, + Atomic32 increment) { + return OSAtomicAdd32(increment, const_cast<Atomic32*>(ptr)); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr, + Atomic32 increment) { + return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr)); +} + +inline void MemoryBarrier() { + OSMemoryBarrier(); +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev_value; + do { + if (OSAtomicCompareAndSwap32Barrier(old_value, new_value, + const_cast<Atomic32*>(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr, + Atomic32 old_value, + Atomic32 new_value) { + return Acquire_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) { + Atomic32 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32 *ptr) { + MemoryBarrier(); + return *ptr; +} + +#ifdef __LP64__ + +// 64-bit implementation on 64-bit platform + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev_value; + do { + if (OSAtomicCompareAndSwap64(old_value, new_value, + const_cast<Atomic64*>(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, + Atomic64 new_value) { + Atomic64 old_value; + do { + old_value = *ptr; + } while (!OSAtomicCompareAndSwap64(old_value, new_value, + const_cast<Atomic64*>(ptr))); + return old_value; +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr, + Atomic64 increment) { + return OSAtomicAdd64(increment, const_cast<Atomic64*>(ptr)); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr, + Atomic64 increment) { + return OSAtomicAdd64Barrier(increment, const_cast<Atomic64*>(ptr)); +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev_value; + do { + if (OSAtomicCompareAndSwap64Barrier(old_value, new_value, + const_cast<Atomic64*>(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr, + Atomic64 old_value, + Atomic64 new_value) { + // The lib kern interface does not distinguish between + // Acquire and Release memory barriers; they are equivalent. + return Acquire_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) { + Atomic64 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64 *ptr) { + MemoryBarrier(); + return *ptr; +} + +#endif // defined(__LP64__) + +// MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different +// on the Mac, even when they are the same size. We need to explicitly cast +// from AtomicWord to Atomic32/64 to implement the AtomicWord interface. +#ifdef __LP64__ +#define AtomicWordCastType Atomic64 +#else +#define AtomicWordCastType Atomic32 +#endif + +inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return NoBarrier_CompareAndSwap( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), + old_value, new_value); +} + +inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr, + AtomicWord new_value) { + return NoBarrier_AtomicExchange( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), new_value); +} + +inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return NoBarrier_AtomicIncrement( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), increment); +} + +inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return Barrier_AtomicIncrement( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), increment); +} + +inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return base::subtle::Acquire_CompareAndSwap( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), + old_value, new_value); +} + +inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return base::subtle::Release_CompareAndSwap( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), + old_value, new_value); +} + +inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) { + NoBarrier_Store( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), value); +} + +inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) { + return base::subtle::Acquire_Store( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), value); +} + +inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) { + return base::subtle::Release_Store( + reinterpret_cast<volatile AtomicWordCastType*>(ptr), value); +} + +inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) { + return NoBarrier_Load( + reinterpret_cast<volatile const AtomicWordCastType*>(ptr)); +} + +inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) { + return base::subtle::Acquire_Load( + reinterpret_cast<volatile const AtomicWordCastType*>(ptr)); +} + +inline AtomicWord Release_Load(volatile const AtomicWord* ptr) { + return base::subtle::Release_Load( + reinterpret_cast<volatile const AtomicWordCastType*>(ptr)); +} + +#undef AtomicWordCastType + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_ diff --git a/ipc/chromium/src/base/atomicops_internals_x86_msvc.h b/ipc/chromium/src/base/atomicops_internals_x86_msvc.h new file mode 100644 index 0000000000..524046935e --- /dev/null +++ b/ipc/chromium/src/base/atomicops_internals_x86_msvc.h @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 is an internal atomic implementation, use base/atomicops.h instead. + +#ifndef BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ +#define BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ + +#include <windows.h> + +namespace base { +namespace subtle { + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + LONG result = InterlockedCompareExchange( + reinterpret_cast<volatile LONG*>(ptr), + static_cast<LONG>(new_value), + static_cast<LONG>(old_value)); + return static_cast<Atomic32>(result); +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + LONG result = InterlockedExchange( + reinterpret_cast<volatile LONG*>(ptr), + static_cast<LONG>(new_value)); + return static_cast<Atomic32>(result); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return InterlockedExchangeAdd( + reinterpret_cast<volatile LONG*>(ptr), + static_cast<LONG>(increment)) + increment; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} + +inline void MemoryBarrier() { + // We use MemoryBarrier from WinNT.h + ::MemoryBarrier(); +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + NoBarrier_AtomicExchange(ptr, value); + // acts as a barrier in this implementation +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; // works w/o barrier for current Intel chips as of June 2005 + // See comments in Atomic64 version of Release_Store() below. +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +#if defined(_WIN64) + +// 64-bit low-level operations on 64-bit platform. + +COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic); + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + PVOID result = InterlockedCompareExchangePointer( + reinterpret_cast<volatile PVOID*>(ptr), + reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value)); + return reinterpret_cast<Atomic64>(result); +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + PVOID result = InterlockedExchangePointer( + reinterpret_cast<volatile PVOID*>(ptr), + reinterpret_cast<PVOID>(new_value)); + return reinterpret_cast<Atomic64>(result); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return InterlockedExchangeAdd64( + reinterpret_cast<volatile LONGLONG*>(ptr), + static_cast<LONGLONG>(increment)) + increment; +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + NoBarrier_AtomicExchange(ptr, value); + // acts as a barrier in this implementation +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; // works w/o barrier for current Intel chips as of June 2005 + + // When new chips come out, check: + // IA-32 Intel Architecture Software Developer's Manual, Volume 3: + // System Programming Guide, Chatper 7: Multiple-processor management, + // Section 7.2, Memory Ordering. + // Last seen at: + // http://developer.intel.com/design/pentium4/manuals/index_new.htm +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value = *ptr; + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} + +#endif // defined(_WIN64) + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ diff --git a/ipc/chromium/src/base/basictypes.h b/ipc/chromium/src/base/basictypes.h new file mode 100644 index 0000000000..6afc7e2381 --- /dev/null +++ b/ipc/chromium/src/base/basictypes.h @@ -0,0 +1,272 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_BASICTYPES_H_ +#define BASE_BASICTYPES_H_ + +#include <limits.h> // So we can set the bounds of our types +#include <stddef.h> // For size_t +#include <string.h> // for memcpy + +#include "base/port.h" // Types that only need exist on certain systems + +#include "mozilla/Assertions.h" +#include "mozilla/IntegerPrintfMacros.h" + +// A type to represent a Unicode code-point value. As of Unicode 4.0, +// such values require up to 21 bits. +// (For type-checking on pointers, make this explicitly signed, +// and it should always be the signed version of whatever int32_t is.) +typedef signed int char32; + +const uint8_t kuint8max = (( uint8_t) 0xFF); +const uint16_t kuint16max = ((uint16_t) 0xFFFF); +const uint32_t kuint32max = ((uint32_t) 0xFFFFFFFF); +const uint64_t kuint64max = ((uint64_t) GG_LONGLONG(0xFFFFFFFFFFFFFFFF)); +const int8_t kint8min = (( int8_t) 0x80); +const int8_t kint8max = (( int8_t) 0x7F); +const int16_t kint16min = (( int16_t) 0x8000); +const int16_t kint16max = (( int16_t) 0x7FFF); +const int32_t kint32min = (( int32_t) 0x80000000); +const int32_t kint32max = (( int32_t) 0x7FFFFFFF); +const int64_t kint64min = (( int64_t) GG_LONGLONG(0x8000000000000000)); +const int64_t kint64max = (( int64_t) GG_LONGLONG(0x7FFFFFFFFFFFFFFF)); + +// Platform- and hardware-dependent printf specifiers +# if defined(OS_POSIX) +# define PRId64L "I64d" +# define PRIu64L "I64u" +# define PRIx64L "I64x" +# elif defined(OS_WIN) +# define PRId64L L"I64d" +# define PRIu64L L"I64u" +# define PRIx64L L"I64x" +# endif + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#undef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// An older, deprecated, politically incorrect name for the above. +#undef DISALLOW_EVIL_CONSTRUCTORS +#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#undef DISALLOW_IMPLICIT_CONSTRUCTORS +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +// +// One caveat is that arraysize() doesn't accept any array of an +// anonymous type or a type defined inside a function. In these rare +// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is +// due to a limitation in C++'s template system. The limitation might +// eventually be removed, but it hasn't happened yet. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +template <typename T, size_t N> +char (&ArraySizeHelper(T (&array)[N]))[N]; + +// That gcc wants both of these prototypes seems mysterious. VC, for +// its part, can't decide which to use (another mystery). Matching of +// template overloads: the final frontier. +#ifndef _MSC_VER +template <typename T, size_t N> +char (&ArraySizeHelper(const T (&array)[N]))[N]; +#endif + +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + +// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize, +// but can be used on anonymous types or types defined inside +// functions. It's less safe than arraysize as it accepts some +// (although not all) pointers. Therefore, you should use arraysize +// whenever possible. +// +// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type +// size_t. +// +// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error +// +// "warning: division by zero in ..." +// +// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer. +// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays. +// +// The following comments are on the implementation details, and can +// be ignored by the users. +// +// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in +// the array) and sizeof(*(arr)) (the # of bytes in one array +// element). If the former is divisible by the latter, perhaps arr is +// indeed an array, in which case the division result is the # of +// elements in the array. Otherwise, arr cannot possibly be an array, +// and we generate a compiler error to prevent the code from +// compiling. +// +// Since the size of bool is implementation-defined, we need to cast +// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final +// result has type size_t. +// +// This macro is not perfect as it wrongfully accepts certain +// pointers, namely where the pointer size is divisible by the pointee +// size. Since all our code has to go through a 32-bit compiler, +// where a pointer is 4 bytes, this means all pointers to a type whose +// size is 3 or greater than 4 will be (righteously) rejected. + +#define ARRAYSIZE_UNSAFE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) + + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertable to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast<ToType>(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template<typename To, typename From> +inline To implicit_cast(From const &f) { + return f; +} + +// The COMPILE_ASSERT macro (below) creates an otherwise-unused typedef. This +// triggers compiler warnings with gcc 4.8 and higher, so mark the typedef +// as permissibly-unused to disable the warnings. +# if defined(__GNUC__) +# define COMPILE_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +# else +# define COMPILE_ASSERT_UNUSED_ATTRIBUTE /* nothing */ +# endif + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +// Avoid multiple definitions for webrtc +#if !defined(COMPILE_ASSERT) +template <bool> +struct CompileAssert { +}; + +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] \ + COMPILE_ASSERT_UNUSED_ATTRIBUTE +#endif + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert<bool(expr)> +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + + +// MetatagId refers to metatag-id that we assign to +// each metatag <name, value> pair.. +typedef uint32_t MetatagId; + +// Argument type used in interfaces that can optionally take ownership +// of a passed in argument. If TAKE_OWNERSHIP is passed, the called +// object takes ownership of the argument. Otherwise it does not. +enum Ownership { + DO_NOT_TAKE_OWNERSHIP, + TAKE_OWNERSHIP +}; + +// The following enum should be used only as a constructor argument to indicate +// that the variable has static storage class, and that the constructor should +// do nothing to its state. It indicates to the reader that it is legal to +// declare a static instance of the class, provided the constructor is given +// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a +// static variable that has a constructor or a destructor because invocation +// order is undefined. However, IF the type can be initialized by filling with +// zeroes (which the loader does for static variables), AND the destructor also +// does nothing to the storage, AND there are no virtual methods, then a +// constructor declared as +// explicit MyClass(base::LinkerInitialized x) {} +// and invoked as +// static MyClass my_variable_name(base::LINKER_INITIALIZED); +namespace base { +enum LinkerInitialized { LINKER_INITIALIZED }; +} // base + + +#include "nscore.h" // pick up mozalloc operator new() etc. + + +#endif // BASE_BASICTYPES_H_ diff --git a/ipc/chromium/src/base/chrome_application_mac.h b/ipc/chromium/src/base/chrome_application_mac.h new file mode 100644 index 0000000000..2f9719438c --- /dev/null +++ b/ipc/chromium/src/base/chrome_application_mac.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 BASE_CHROME_APPLICATION_MAC_H_ +#define BASE_CHROME_APPLICATION_MAC_H_ + +#import <AppKit/AppKit.h> + +#include "base/basictypes.h" +#include "base/scoped_nsobject.h" + +// Event hooks must implement this protocol. +@protocol CrApplicationEventHookProtocol +- (void)hookForEvent:(NSEvent*)theEvent; +@end + + +@interface CrApplication : NSApplication { + @private + BOOL handlingSendEvent_; + // Array of objects implementing the CrApplicationEventHookProtocol + scoped_nsobject<NSMutableArray> eventHooks_; +} +@property(readonly, + getter=isHandlingSendEvent, + nonatomic) BOOL handlingSendEvent; + +// Add or remove an event hook to be called for every sendEvent: +// that the application receives. These handlers are called before +// the normal [NSApplication sendEvent:] call is made. + +// This is not a good alternative to a nested event loop. It should +// be used only when normal event logic and notification breaks down +// (e.g. when clicking outside a canBecomeKey:NO window to "switch +// context" out of it). +- (void)addEventHook:(id<CrApplicationEventHookProtocol>)hook; +- (void)removeEventHook:(id<CrApplicationEventHookProtocol>)hook; + ++ (NSApplication*)sharedApplication; +@end + +namespace chrome_application_mac { + +// Controls the state of |handlingSendEvent_| in the event loop so that it is +// reset properly. +class ScopedSendingEvent { + public: + ScopedSendingEvent(); + ~ScopedSendingEvent(); + + private: + CrApplication* app_; + BOOL handling_; + DISALLOW_COPY_AND_ASSIGN(ScopedSendingEvent); +}; + +} // chrome_application_mac + +#endif // BASE_CHROME_APPLICATION_MAC_H_ diff --git a/ipc/chromium/src/base/chrome_application_mac.mm b/ipc/chromium/src/base/chrome_application_mac.mm new file mode 100644 index 0000000000..b9341c6cea --- /dev/null +++ b/ipc/chromium/src/base/chrome_application_mac.mm @@ -0,0 +1,68 @@ +// Copyright (c) 2009 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. + +#import "chrome_application_mac.h" + +#include "base/logging.h" + +@interface CrApplication () +@property(readwrite, + getter=isHandlingSendEvent, + nonatomic) BOOL handlingSendEvent; +@end + +@implementation CrApplication +@synthesize handlingSendEvent = handlingSendEvent_; + +// Initialize NSApplication using the custom subclass. Check whether NSApp +// was already initialized using another class, because that would break +// some things. ++ (NSApplication*)sharedApplication { + NSApplication* app = [super sharedApplication]; + if (![NSApp isKindOfClass:self]) { + CHROMIUM_LOG(ERROR) << "NSApp should be of type " << [[self className] UTF8String] + << ", not " << [[NSApp className] UTF8String]; + DCHECK(false) << "NSApp is of wrong type"; + } + return app; +} + +- (id)init { + if ((self = [super init])) { + eventHooks_.reset([[NSMutableArray alloc] init]); + } + return self; +} + +- (void)sendEvent:(NSEvent*)event { + chrome_application_mac::ScopedSendingEvent sendingEventScoper; + for (id<CrApplicationEventHookProtocol> handler in eventHooks_.get()) { + [handler hookForEvent:event]; + } + [super sendEvent:event]; +} + +- (void)addEventHook:(id<CrApplicationEventHookProtocol>)handler { + [eventHooks_ addObject:handler]; +} + +- (void)removeEventHook:(id<CrApplicationEventHookProtocol>)handler { + [eventHooks_ removeObject:handler]; +} + +@end + +namespace chrome_application_mac { + +ScopedSendingEvent::ScopedSendingEvent() + : app_(static_cast<CrApplication*>([CrApplication sharedApplication])), + handling_([app_ isHandlingSendEvent]) { + [app_ setHandlingSendEvent:YES]; +} + +ScopedSendingEvent::~ScopedSendingEvent() { + [app_ setHandlingSendEvent:handling_]; +} + +} // namespace chrome_application_mac diff --git a/ipc/chromium/src/base/command_line.cc b/ipc/chromium/src/base/command_line.cc new file mode 100644 index 0000000000..85fd953e19 --- /dev/null +++ b/ipc/chromium/src/base/command_line.cc @@ -0,0 +1,394 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/command_line.h" + +#if defined(OS_WIN) +#include <windows.h> +#include <shellapi.h> +#endif + +#include <algorithm> + +#include "base/logging.h" +#include "base/singleton.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" + +CommandLine* CommandLine::current_process_commandline_ = NULL; + +// Since we use a lazy match, make sure that longer versions (like L"--") +// are listed before shorter versions (like L"-") of similar prefixes. +#if defined(OS_WIN) +const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; +const wchar_t kSwitchTerminator[] = L"--"; +const wchar_t kSwitchValueSeparator[] = L"="; +#elif defined(OS_POSIX) +// Unixes don't use slash as a switch. +const char* const kSwitchPrefixes[] = {"--", "-"}; +const char kSwitchTerminator[] = "--"; +const char kSwitchValueSeparator[] = "="; +#endif + +#if defined(OS_WIN) +// Lowercase a string. This is used to lowercase switch names. +// Is this what we really want? It seems crazy to me. I've left it in +// for backwards compatibility on Windows. +static void Lowercase(std::wstring* parameter) { + transform(parameter->begin(), parameter->end(), parameter->begin(), + tolower); +} +#endif + +#if defined(OS_WIN) +void CommandLine::ParseFromString(const std::wstring& command_line) { + TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); + + if (command_line_string_.empty()) + return; + + int num_args = 0; + wchar_t** args = NULL; + + args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); + + // Populate program_ with the trimmed version of the first arg. + TrimWhitespace(args[0], TRIM_ALL, &program_); + + bool parse_switches = true; + for (int i = 1; i < num_args; ++i) { + std::wstring arg; + TrimWhitespace(args[i], TRIM_ALL, &arg); + + if (!parse_switches) { + loose_values_.push_back(arg); + continue; + } + + if (arg == kSwitchTerminator) { + parse_switches = false; + continue; + } + + std::string switch_string; + std::wstring switch_value; + if (IsSwitch(arg, &switch_string, &switch_value)) { + switches_[switch_string] = switch_value; + } else { + loose_values_.push_back(arg); + } + } + + if (args) + LocalFree(args); +} +CommandLine::CommandLine(const std::wstring& program) { + if (!program.empty()) { + program_ = program; + command_line_string_ = L'"' + program + L'"'; + } +} +#elif defined(OS_POSIX) +CommandLine::CommandLine(int argc, const char* const* argv) { + for (int i = 0; i < argc; ++i) + argv_.push_back(argv[i]); + InitFromArgv(); +} +CommandLine::CommandLine(const std::vector<std::string>& argv) { + argv_ = argv; + InitFromArgv(); +} + +void CommandLine::InitFromArgv() { + bool parse_switches = true; + for (size_t i = 1; i < argv_.size(); ++i) { + const std::string& arg = argv_[i]; + + if (!parse_switches) { + loose_values_.push_back(arg); + continue; + } + + if (arg == kSwitchTerminator) { + parse_switches = false; + continue; + } + + std::string switch_string; + std::string switch_value; + if (IsSwitch(arg, &switch_string, &switch_value)) { + switches_[switch_string] = switch_value; + } else { + loose_values_.push_back(arg); + } + } +} + +CommandLine::CommandLine(const std::wstring& program) { + argv_.push_back(WideToASCII(program)); +} +#endif + +// static +bool CommandLine::IsSwitch(const StringType& parameter_string, + std::string* switch_string, + StringType* switch_value) { + switch_string->clear(); + switch_value->clear(); + + for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { + StringType prefix(kSwitchPrefixes[i]); + if (parameter_string.find(prefix) != 0) + continue; + + const size_t switch_start = prefix.length(); + const size_t equals_position = parameter_string.find( + kSwitchValueSeparator, switch_start); + StringType switch_native; + if (equals_position == StringType::npos) { + switch_native = parameter_string.substr(switch_start); + } else { + switch_native = parameter_string.substr( + switch_start, equals_position - switch_start); + *switch_value = parameter_string.substr(equals_position + 1); + } +#if defined(OS_WIN) + Lowercase(&switch_native); + *switch_string = WideToASCII(switch_native); +#else + *switch_string = switch_native; +#endif + + return true; + } + + return false; +} + +// static +void CommandLine::Init(int argc, const char* const* argv) { + DCHECK(current_process_commandline_ == NULL); +#if defined(OS_WIN) + current_process_commandline_ = new CommandLine; + current_process_commandline_->ParseFromString(::GetCommandLineW()); +#elif defined(OS_POSIX) + current_process_commandline_ = new CommandLine(argc, argv); +#endif +} + +void CommandLine::Terminate() { + DCHECK(current_process_commandline_ != NULL); + delete current_process_commandline_; + current_process_commandline_ = NULL; +} + +bool CommandLine::HasSwitch(const std::wstring& switch_string) const { + std::wstring lowercased_switch(switch_string); +#if defined(OS_WIN) + Lowercase(&lowercased_switch); +#endif + return switches_.find(WideToASCII(lowercased_switch)) != switches_.end(); +} + +std::wstring CommandLine::GetSwitchValue( + const std::wstring& switch_string) const { + std::wstring lowercased_switch(switch_string); +#if defined(OS_WIN) + Lowercase(&lowercased_switch); +#endif + + std::map<std::string, StringType>::const_iterator result = + switches_.find(WideToASCII(lowercased_switch)); + + if (result == switches_.end()) { + return L""; + } else { +#if defined(OS_WIN) + return result->second; +#else + return ASCIIToWide(result->second); +#endif + } +} + +#if defined(OS_WIN) +std::vector<std::wstring> CommandLine::GetLooseValues() const { + return loose_values_; +} +std::wstring CommandLine::program() const { + return program_; +} +#else +std::vector<std::wstring> CommandLine::GetLooseValues() const { + std::vector<std::wstring> values; + for (size_t i = 0; i < loose_values_.size(); ++i) + values.push_back(ASCIIToWide(loose_values_[i])); + return values; +} +std::wstring CommandLine::program() const { + DCHECK(argv_.size() > 0); + return ASCIIToWide(argv_[0]); +} +#endif + + +// static +std::wstring CommandLine::PrefixedSwitchString( + const std::wstring& switch_string) { + return StringPrintf(L"%ls%ls", + kSwitchPrefixes[0], + switch_string.c_str()); +} + +// static +std::wstring CommandLine::PrefixedSwitchStringWithValue( + const std::wstring& switch_string, const std::wstring& value_string) { + if (value_string.empty()) { + return PrefixedSwitchString(switch_string); + } + + return StringPrintf(L"%ls%ls%ls%ls", + kSwitchPrefixes[0], + switch_string.c_str(), + kSwitchValueSeparator, + value_string.c_str()); +} + +#if defined(OS_WIN) +void CommandLine::AppendSwitch(const std::wstring& switch_string) { + std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string); + command_line_string_.append(L" "); + command_line_string_.append(prefixed_switch_string); + switches_[WideToASCII(switch_string)] = L""; +} + +// Quote a string if necessary, such that CommandLineToArgvW() will +// always process it as a single argument. +static std::wstring WindowsStyleQuote(const std::wstring& arg) { + // We follow the quoting rules of CommandLineToArgvW. + // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + if (arg.find_first_of(L" \\\"\t") == std::wstring::npos) { + // No quoting necessary. + return arg; + } + + std::wstring out; + out.push_back(L'"'); + for (size_t i = 0; i < arg.size(); ++i) { + if (arg[i] == '\\') { + // Find the extent of this run of backslashes. + size_t start = i, end = start + 1; + for (; end < arg.size() && arg[end] == '\\'; ++end) + /* empty */; + size_t backslash_count = end - start; + + // Backslashes are escapes only if the run is followed by a double quote. + // Since we also will end the string with a double quote, we escape for + // either a double quote or the end of the string. + if (end == arg.size() || arg[end] == '"') { + // To quote, we need to output 2x as many backslashes. + backslash_count *= 2; + } + for (size_t j = 0; j < backslash_count; ++j) + out.push_back('\\'); + + // Advance i to one before the end to balance i++ in loop. + i = end - 1; + } else if (arg[i] == '"') { + out.push_back('\\'); + out.push_back('"'); + } else { + out.push_back(arg[i]); + } + } + out.push_back('"'); + + return out; +} + +void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string) { + std::wstring quoted_value_string = WindowsStyleQuote(value_string); + std::wstring combined_switch_string = + PrefixedSwitchStringWithValue(switch_string, quoted_value_string); + + command_line_string_.append(L" "); + command_line_string_.append(combined_switch_string); + + switches_[WideToASCII(switch_string)] = value_string; +} + +void CommandLine::AppendLooseValue(const std::wstring& value) { + command_line_string_.append(L" "); + command_line_string_.append(WindowsStyleQuote(value)); +} + +void CommandLine::AppendArguments(const CommandLine& other, + bool include_program) { + // Verify include_program is used correctly. + // Logic could be shorter but this is clearer. + DCHECK(include_program ? !other.program().empty() : other.program().empty()); + command_line_string_ += L" " + other.command_line_string_; + std::map<std::string, StringType>::const_iterator i; + for (i = other.switches_.begin(); i != other.switches_.end(); ++i) + switches_[i->first] = i->second; +} + +void CommandLine::PrependWrapper(const std::wstring& wrapper) { + // The wrapper may have embedded arguments (like "gdb --args"). In this case, + // we don't pretend to do anything fancy, we just split on spaces. + std::vector<std::wstring> wrapper_and_args; + SplitString(wrapper, ' ', &wrapper_and_args); + program_ = wrapper_and_args[0]; + command_line_string_ = wrapper + L" " + command_line_string_; +} + +#elif defined(OS_POSIX) +void CommandLine::AppendSwitch(const std::wstring& switch_string) { + std::string ascii_switch = WideToASCII(switch_string); + argv_.push_back(kSwitchPrefixes[0] + ascii_switch); + switches_[ascii_switch] = ""; +} + +void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string) { + std::string ascii_switch = WideToASCII(switch_string); + std::string ascii_value = WideToASCII(value_string); + + argv_.push_back(kSwitchPrefixes[0] + ascii_switch + + kSwitchValueSeparator + ascii_value); + switches_[ascii_switch] = ascii_value; +} + +void CommandLine::AppendLooseValue(const std::wstring& value) { + argv_.push_back(WideToASCII(value)); +} + +void CommandLine::AppendArguments(const CommandLine& other, + bool include_program) { + // Verify include_program is used correctly. + // Logic could be shorter but this is clearer. + DCHECK(include_program ? !other.program().empty() : other.program().empty()); + + size_t first_arg = include_program ? 0 : 1; + for (size_t i = first_arg; i < other.argv_.size(); ++i) + argv_.push_back(other.argv_[i]); + std::map<std::string, StringType>::const_iterator i; + for (i = other.switches_.begin(); i != other.switches_.end(); ++i) + switches_[i->first] = i->second; +} + +void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) { + // The wrapper may have embedded arguments (like "gdb --args"). In this case, + // we don't pretend to do anything fancy, we just split on spaces. + const std::string wrapper = WideToASCII(wrapper_wide); + std::vector<std::string> wrapper_and_args; + SplitString(wrapper, ' ', &wrapper_and_args); + argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); +} + +#endif diff --git a/ipc/chromium/src/base/command_line.h b/ipc/chromium/src/base/command_line.h new file mode 100644 index 0000000000..3ce94ed1bc --- /dev/null +++ b/ipc/chromium/src/base/command_line.h @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 class works with command lines: building and parsing. +// Switches can optionally have a value attached using an equals sign, +// as in "-switch=value". Arguments that aren't prefixed with a +// switch prefix are considered "loose parameters". Switch names are +// case-insensitive. An argument of "--" will terminate switch +// parsing, causing everything after to be considered as loose +// parameters. + +// There is a singleton read-only CommandLine that represents the command +// line that the current process was started with. It must be initialized +// in main() (or whatever the platform's equivalent function is). + +#ifndef BASE_COMMAND_LINE_H_ +#define BASE_COMMAND_LINE_H_ + +#include "build/build_config.h" + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" + +class InProcessBrowserTest; + +class CommandLine { + public: +#if defined(OS_WIN) + // Creates a parsed version of the given command-line string. + // The program name is assumed to be the first item in the string. + void ParseFromString(const std::wstring& command_line); +#elif defined(OS_POSIX) + // Initialize from an argv vector (or directly from main()'s argv). + CommandLine(int argc, const char* const* argv); + explicit CommandLine(const std::vector<std::string>& argv); +#endif + + // Construct a new, empty command line. + // |program| is the name of the program to run (aka argv[0]). + // TODO(port): should be a FilePath. + explicit CommandLine(const std::wstring& program); + + // Initialize the current process CommandLine singleton. On Windows, + // ignores its arguments (we instead parse GetCommandLineW() + // directly) because we don't trust the CRT's parsing of the command + // line, but it still must be called to set up the command line. + static void Init(int argc, const char* const* argv); + + // Destroys the current process CommandLine singleton. This is necessary if + // you want to reset the base library to its initial state (for example in an + // outer library that needs to be able to terminate, and be re-initialized). + // If Init is called only once, e.g. in main(), calling Terminate() is not + // necessary. + static void Terminate(); + + // Get the singleton CommandLine representing the current process's + // command line. + static const CommandLine* ForCurrentProcess() { + DCHECK(current_process_commandline_); + return current_process_commandline_; + } + + static bool IsInitialized() { + return !!current_process_commandline_; + } + + // Returns true if this command line contains the given switch. + // (Switch names are case-insensitive.) + bool HasSwitch(const std::wstring& switch_string) const; + + // Returns the value associated with the given switch. If the + // switch has no value or isn't present, this method returns + // the empty string. + std::wstring GetSwitchValue(const std::wstring& switch_string) const; + + // Get the remaining arguments to the command. + // WARNING: this is incorrect on POSIX; we must do string conversions. + std::vector<std::wstring> GetLooseValues() const; + +#if defined(OS_WIN) + // Returns the original command line string. + const std::wstring& command_line_string() const { + return command_line_string_; + } +#elif defined(OS_POSIX) + // Returns the original command line string as a vector of strings. + const std::vector<std::string>& argv() const { + return argv_; + } +#endif + + // Returns the program part of the command line string (the first item). + std::wstring program() const; + + // Return a copy of the string prefixed with a switch prefix. + // Used internally. + static std::wstring PrefixedSwitchString(const std::wstring& switch_string); + + // Return a copy of the string prefixed with a switch prefix, + // and appended with the given value. Used internally. + static std::wstring PrefixedSwitchStringWithValue( + const std::wstring& switch_string, + const std::wstring& value_string); + + // Appends the given switch string (preceded by a space and a switch + // prefix) to the given string. + void AppendSwitch(const std::wstring& switch_string); + + // Appends the given switch string (preceded by a space and a switch + // prefix) to the given string, with the given value attached. + void AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string); + + // Append a loose value to the command line. + void AppendLooseValue(const std::wstring& value); + +#if defined(OS_WIN) + void AppendLooseValue(const wchar_t* value) { + AppendLooseValue(std::wstring(value)); + } +#endif + + // Append the arguments from another command line to this one. + // If |include_program| is true, include |other|'s program as well. + void AppendArguments(const CommandLine& other, + bool include_program); + + // On POSIX systems it's common to run processes via a wrapper (like + // "valgrind" or "gdb --args"). + void PrependWrapper(const std::wstring& wrapper); + + private: + friend class InProcessBrowserTest; + + CommandLine() {} + + // Used by InProcessBrowserTest. + static CommandLine* ForCurrentProcessMutable() { + DCHECK(current_process_commandline_); + return current_process_commandline_; + } + + // The singleton CommandLine instance representing the current process's + // command line. + static CommandLine* current_process_commandline_; + + // We store a platform-native version of the command line, used when building + // up a new command line to be executed. This ifdef delimits that code. + +#if defined(OS_WIN) + // The quoted, space-separated command-line string. + std::wstring command_line_string_; + + // The name of the program. + std::wstring program_; + + // The type of native command line arguments. + typedef std::wstring StringType; + +#elif defined(OS_POSIX) + // The argv array, with the program name in argv_[0]. + std::vector<std::string> argv_; + + // The type of native command line arguments. + typedef std::string StringType; + + // Shared by the two POSIX constructor forms. Initalize from argv_. + void InitFromArgv(); +#endif + + // Returns true and fills in |switch_string| and |switch_value| + // if |parameter_string| represents a switch. + static bool IsSwitch(const StringType& parameter_string, + std::string* switch_string, + StringType* switch_value); + + // Parsed-out values. + std::map<std::string, StringType> switches_; + + // Non-switch command-line arguments. + std::vector<StringType> loose_values_; + + // We allow copy constructors, because a common pattern is to grab a + // copy of the current process's command line and then add some + // flags to it. E.g.: + // CommandLine cl(*CommandLine::ForCurrentProcess()); + // cl.AppendSwitch(...); +}; + +#endif // BASE_COMMAND_LINE_H_ diff --git a/ipc/chromium/src/base/compiler_specific.h b/ipc/chromium/src/base/compiler_specific.h new file mode 100644 index 0000000000..509dfa4b46 --- /dev/null +++ b/ipc/chromium/src/base/compiler_specific.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_COMPILER_SPECIFIC_H_ +#define BASE_COMPILER_SPECIFIC_H_ + +#include "build/build_config.h" + +#if defined(COMPILER_MSVC) + +// Macros for suppressing and disabling warnings on MSVC. +// +// Warning numbers are enumerated at: +// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx +// +// The warning pragma: +// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx +// +// Using __pragma instead of #pragma inside macros: +// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx + +// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and +// for the next line of the source file. +#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress:n)) + +// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled. +// The warning remains disabled until popped by MSVC_POP_WARNING. +#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ + __pragma(warning(disable:n)) + +// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level +// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all +// warnings. +#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n)) + +// Pop effects of innermost MSVC_PUSH_* macro. +#define MSVC_POP_WARNING() __pragma(warning(pop)) + +#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off)) +#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on)) + +// Allows |this| to be passed as an argument in constructor initializer lists. +// This uses push/pop instead of the seemingly simpler suppress feature to avoid +// having the warning be disabled for more than just |code|. +// +// Example usage: +// Foo::Foo() : x(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(y(this)), z(3) {} +// +// Compiler warning C4355: 'this': used in base member initializer list: +// http://msdn.microsoft.com/en-us/library/3c594ae3(VS.80).aspx +#define ALLOW_THIS_IN_INITIALIZER_LIST(code) MSVC_PUSH_DISABLE_WARNING(4355) \ + code \ + MSVC_POP_WARNING() + +#else // Not MSVC + +#define MSVC_SUPPRESS_WARNING(n) +#define MSVC_PUSH_DISABLE_WARNING(n) +#define MSVC_PUSH_WARNING_LEVEL(n) +#define MSVC_POP_WARNING() +#define MSVC_DISABLE_OPTIMIZE() +#define MSVC_ENABLE_OPTIMIZE() +#define ALLOW_THIS_IN_INITIALIZER_LIST(code) code + +#endif // COMPILER_MSVC + + +#if defined(COMPILER_GCC) +#define ALLOW_UNUSED __attribute__((unused)) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else // Not GCC +#define ALLOW_UNUSED +#define WARN_UNUSED_RESULT +#endif + +#endif // BASE_COMPILER_SPECIFIC_H_ diff --git a/ipc/chromium/src/base/condition_variable.h b/ipc/chromium/src/base/condition_variable.h new file mode 100644 index 0000000000..4c95dc0bc2 --- /dev/null +++ b/ipc/chromium/src/base/condition_variable.h @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// ConditionVariable wraps pthreads condition variable synchronization or, on +// Windows, simulates it. This functionality is very helpful for having +// several threads wait for an event, as is common with a thread pool managed +// by a master. The meaning of such an event in the (worker) thread pool +// scenario is that additional tasks are now available for processing. It is +// used in Chrome in the DNS prefetching system to notify worker threads that +// a queue now has items (tasks) which need to be tended to. A related use +// would have a pool manager waiting on a ConditionVariable, waiting for a +// thread in the pool to announce (signal) that there is now more room in a +// (bounded size) communications queue for the manager to deposit tasks, or, +// as a second example, that the queue of tasks is completely empty and all +// workers are waiting. +// +// USAGE NOTE 1: spurious signal events are possible with this and +// most implementations of condition variables. As a result, be +// *sure* to retest your condition before proceeding. The following +// is a good example of doing this correctly: +// +// while (!work_to_be_done()) Wait(...); +// +// In contrast do NOT do the following: +// +// if (!work_to_be_done()) Wait(...); // Don't do this. +// +// Especially avoid the above if you are relying on some other thread only +// issuing a signal up *if* there is work-to-do. There can/will +// be spurious signals. Recheck state on waiting thread before +// assuming the signal was intentional. Caveat caller ;-). +// +// USAGE NOTE 2: Broadcast() frees up all waiting threads at once, +// which leads to contention for the locks they all held when they +// called Wait(). This results in POOR performance. A much better +// approach to getting a lot of threads out of Wait() is to have each +// thread (upon exiting Wait()) call Signal() to free up another +// Wait'ing thread. Look at condition_variable_unittest.cc for +// both examples. +// +// Broadcast() can be used nicely during teardown, as it gets the job +// done, and leaves no sleeping threads... and performance is less +// critical at that point. +// +// The semantics of Broadcast() are carefully crafted so that *all* +// threads that were waiting when the request was made will indeed +// get signaled. Some implementations mess up, and don't signal them +// all, while others allow the wait to be effectively turned off (for +// a while while waiting threads come around). This implementation +// appears correct, as it will not "lose" any signals, and will guarantee +// that all threads get signaled by Broadcast(). +// +// This implementation offers support for "performance" in its selection of +// which thread to revive. Performance, in direct contrast with "fairness," +// assures that the thread that most recently began to Wait() is selected by +// Signal to revive. Fairness would (if publicly supported) assure that the +// thread that has Wait()ed the longest is selected. The default policy +// may improve performance, as the selected thread may have a greater chance of +// having some of its stack data in various CPU caches. +// +// For a discussion of the many very subtle implementation details, see the FAQ +// at the end of condition_variable_win.cc. + +#ifndef BASE_CONDITION_VARIABLE_H_ +#define BASE_CONDITION_VARIABLE_H_ + +#include "base/lock.h" + +namespace base { + class TimeDelta; +} + +class ConditionVariable { + public: + // Construct a cv for use with ONLY one user lock. + explicit ConditionVariable(Lock* user_lock); + + ~ConditionVariable(); + + // Wait() releases the caller's critical section atomically as it starts to + // sleep, and the reacquires it when it is signaled. + void Wait(); + void TimedWait(const base::TimeDelta& max_time); + + // Broadcast() revives all waiting threads. + void Broadcast(); + // Signal() revives one waiting thread. + void Signal(); + + private: + +#if defined(OS_WIN) + + // Define Event class that is used to form circularly linked lists. + // The list container is an element with NULL as its handle_ value. + // The actual list elements have a non-zero handle_ value. + // All calls to methods MUST be done under protection of a lock so that links + // can be validated. Without the lock, some links might asynchronously + // change, and the assertions would fail (as would list change operations). + class Event { + public: + // Default constructor with no arguments creates a list container. + Event(); + ~Event(); + + // InitListElement transitions an instance from a container, to an element. + void InitListElement(); + + // Methods for use on lists. + bool IsEmpty() const; + void PushBack(Event* other); + Event* PopFront(); + Event* PopBack(); + + // Methods for use on list elements. + // Accessor method. + HANDLE handle() const; + // Pull an element from a list (if it's in one). + Event* Extract(); + + // Method for use on a list element or on a list. + bool IsSingleton() const; + + private: + // Provide pre/post conditions to validate correct manipulations. + bool ValidateAsDistinct(Event* other) const; + bool ValidateAsItem() const; + bool ValidateAsList() const; + bool ValidateLinks() const; + + HANDLE handle_; + Event* next_; + Event* prev_; + DISALLOW_COPY_AND_ASSIGN(Event); + }; + + // Note that RUNNING is an unlikely number to have in RAM by accident. + // This helps with defensive destructor coding in the face of user error. + enum RunState { SHUTDOWN = 0, RUNNING = 64213 }; + + // Internal implementation methods supporting Wait(). + Event* GetEventForWaiting(); + void RecycleEvent(Event* used_event); + + RunState run_state_; + + // Private critical section for access to member data. + Lock internal_lock_; + + // Lock that is acquired before calling Wait(). + Lock& user_lock_; + + // Events that threads are blocked on. + Event waiting_list_; + + // Free list for old events. + Event recycling_list_; + int recycling_list_size_; + + // The number of allocated, but not yet deleted events. + int allocation_counter_; + +#elif defined(OS_POSIX) + + pthread_cond_t condition_; + pthread_mutex_t* user_mutex_; + +#endif + + DISALLOW_COPY_AND_ASSIGN(ConditionVariable); +}; + +#endif // BASE_CONDITION_VARIABLE_H_ diff --git a/ipc/chromium/src/base/condition_variable_posix.cc b/ipc/chromium/src/base/condition_variable_posix.cc new file mode 100644 index 0000000000..42cedad15a --- /dev/null +++ b/ipc/chromium/src/base/condition_variable_posix.cc @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/condition_variable.h" + +#include <errno.h> +#include <sys/time.h> + +#include "base/lock.h" +#include "base/lock_impl.h" +#include "base/logging.h" +#include "base/time.h" + +using base::Time; +using base::TimeDelta; + +ConditionVariable::ConditionVariable(Lock* user_lock) + : user_mutex_(user_lock->lock_impl()->os_lock()) { + int rv = 0; +#if !defined(OS_MACOSX) && \ + !(defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)) + pthread_condattr_t attrs; + rv = pthread_condattr_init(&attrs); + DCHECK_EQ(0, rv); + pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC); + rv = pthread_cond_init(&condition_, &attrs); + pthread_condattr_destroy(&attrs); +#else + rv = pthread_cond_init(&condition_, NULL); +#endif + DCHECK_EQ(0, rv); +} + +ConditionVariable::~ConditionVariable() { + int rv = pthread_cond_destroy(&condition_); + DCHECK(rv == 0); +} + +void ConditionVariable::Wait() { + int rv = pthread_cond_wait(&condition_, user_mutex_); + DCHECK(rv == 0); +} + +void ConditionVariable::TimedWait(const TimeDelta& max_time) { + int64_t usecs = max_time.InMicroseconds(); + + struct timespec relative_time; + relative_time.tv_sec = usecs / Time::kMicrosecondsPerSecond; + relative_time.tv_nsec = + (usecs % Time::kMicrosecondsPerSecond) * Time::kNanosecondsPerMicrosecond; + +#if defined(OS_MACOSX) + int rv = pthread_cond_timedwait_relative_np( + &condition_, user_mutex_, &relative_time); +#else + // The timeout argument to pthread_cond_timedwait is in absolute time. + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + struct timespec absolute_time; + absolute_time.tv_sec = now.tv_sec; + absolute_time.tv_nsec = now.tv_nsec; + absolute_time.tv_sec += relative_time.tv_sec; + absolute_time.tv_nsec += relative_time.tv_nsec; + absolute_time.tv_sec += absolute_time.tv_nsec / Time::kNanosecondsPerSecond; + absolute_time.tv_nsec %= Time::kNanosecondsPerSecond; + DCHECK_GE(absolute_time.tv_sec, now.tv_sec); // Overflow paranoia + +#if defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) + int rv = pthread_cond_timedwait_monotonic_np( + &condition_, user_mutex_, &absolute_time); +#else + int rv = pthread_cond_timedwait(&condition_, user_mutex_, &absolute_time); +#endif // OS_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC +#endif // OS_MACOSX + + DCHECK(rv == 0 || rv == ETIMEDOUT); +} + +void ConditionVariable::Broadcast() { + int rv = pthread_cond_broadcast(&condition_); + DCHECK(rv == 0); +} + +void ConditionVariable::Signal() { + int rv = pthread_cond_signal(&condition_); + DCHECK(rv == 0); +} diff --git a/ipc/chromium/src/base/condition_variable_win.cc b/ipc/chromium/src/base/condition_variable_win.cc new file mode 100644 index 0000000000..b0a3df5fa0 --- /dev/null +++ b/ipc/chromium/src/base/condition_variable_win.cc @@ -0,0 +1,448 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/condition_variable.h" + +#include <stack> + +#include "base/lock.h" +#include "base/logging.h" +#include "base/time.h" + +using base::TimeDelta; + +ConditionVariable::ConditionVariable(Lock* user_lock) + : user_lock_(*user_lock), + run_state_(RUNNING), + allocation_counter_(0), + recycling_list_size_(0) { + DCHECK(user_lock); +} + +ConditionVariable::~ConditionVariable() { + AutoLock auto_lock(internal_lock_); + run_state_ = SHUTDOWN; // Prevent any more waiting. + + DCHECK_EQ(recycling_list_size_, allocation_counter_); + if (recycling_list_size_ != allocation_counter_) { // Rare shutdown problem. + // There are threads of execution still in this->TimedWait() and yet the + // caller has instigated the destruction of this instance :-/. + // A common reason for such "overly hasty" destruction is that the caller + // was not willing to wait for all the threads to terminate. Such hasty + // actions are a violation of our usage contract, but we'll give the + // waiting thread(s) one last chance to exit gracefully (prior to our + // destruction). + // Note: waiting_list_ *might* be empty, but recycling is still pending. + AutoUnlock auto_unlock(internal_lock_); + Broadcast(); // Make sure all waiting threads have been signaled. + Sleep(10); // Give threads a chance to grab internal_lock_. + // All contained threads should be blocked on user_lock_ by now :-). + } // Reacquire internal_lock_. + + DCHECK_EQ(recycling_list_size_, allocation_counter_); +} + +void ConditionVariable::Wait() { + // Default to "wait forever" timing, which means have to get a Signal() + // or Broadcast() to come out of this wait state. + TimedWait(TimeDelta::FromMilliseconds(INFINITE)); +} + +void ConditionVariable::TimedWait(const TimeDelta& max_time) { + Event* waiting_event; + HANDLE handle; + { + AutoLock auto_lock(internal_lock_); + if (RUNNING != run_state_) return; // Destruction in progress. + waiting_event = GetEventForWaiting(); + handle = waiting_event->handle(); + DCHECK(handle); + } // Release internal_lock. + + { + AutoUnlock unlock(user_lock_); // Release caller's lock + WaitForSingleObject(handle, static_cast<DWORD>(max_time.InMilliseconds())); + // Minimize spurious signal creation window by recycling asap. + AutoLock auto_lock(internal_lock_); + RecycleEvent(waiting_event); + // Release internal_lock_ + } // Reacquire callers lock to depth at entry. +} + +// Broadcast() is guaranteed to signal all threads that were waiting (i.e., had +// a cv_event internally allocated for them) before Broadcast() was called. +void ConditionVariable::Broadcast() { + std::stack<HANDLE> handles; // See FAQ-question-10. + { + AutoLock auto_lock(internal_lock_); + if (waiting_list_.IsEmpty()) + return; + while (!waiting_list_.IsEmpty()) + // This is not a leak from waiting_list_. See FAQ-question 12. + handles.push(waiting_list_.PopBack()->handle()); + } // Release internal_lock_. + while (!handles.empty()) { + SetEvent(handles.top()); + handles.pop(); + } +} + +// Signal() will select one of the waiting threads, and signal it (signal its +// cv_event). For better performance we signal the thread that went to sleep +// most recently (LIFO). If we want fairness, then we wake the thread that has +// been sleeping the longest (FIFO). +void ConditionVariable::Signal() { + HANDLE handle; + { + AutoLock auto_lock(internal_lock_); + if (waiting_list_.IsEmpty()) + return; // No one to signal. + // Only performance option should be used. + // This is not a leak from waiting_list. See FAQ-question 12. + handle = waiting_list_.PopBack()->handle(); // LIFO. + } // Release internal_lock_. + SetEvent(handle); +} + +// GetEventForWaiting() provides a unique cv_event for any caller that needs to +// wait. This means that (worst case) we may over time create as many cv_event +// objects as there are threads simultaneously using this instance's Wait() +// functionality. +ConditionVariable::Event* ConditionVariable::GetEventForWaiting() { + // We hold internal_lock, courtesy of Wait(). + Event* cv_event; + if (0 == recycling_list_size_) { + DCHECK(recycling_list_.IsEmpty()); + cv_event = new Event(); + cv_event->InitListElement(); + allocation_counter_++; + // CHECK_NE is not defined in our codebase, so we have to use CHECK + CHECK(cv_event->handle()); + } else { + cv_event = recycling_list_.PopFront(); + recycling_list_size_--; + } + waiting_list_.PushBack(cv_event); + return cv_event; +} + +// RecycleEvent() takes a cv_event that was previously used for Wait()ing, and +// recycles it for use in future Wait() calls for this or other threads. +// Note that there is a tiny chance that the cv_event is still signaled when we +// obtain it, and that can cause spurious signals (if/when we re-use the +// cv_event), but such is quite rare (see FAQ-question-5). +void ConditionVariable::RecycleEvent(Event* used_event) { + // We hold internal_lock, courtesy of Wait(). + // If the cv_event timed out, then it is necessary to remove it from + // waiting_list_. If it was selected by Broadcast() or Signal(), then it is + // already gone. + used_event->Extract(); // Possibly redundant + recycling_list_.PushBack(used_event); + recycling_list_size_++; +} +//------------------------------------------------------------------------------ +// The next section provides the implementation for the private Event class. +//------------------------------------------------------------------------------ + +// Event provides a doubly-linked-list of events for use exclusively by the +// ConditionVariable class. + +// This custom container was crafted because no simple combination of STL +// classes appeared to support the functionality required. The specific +// unusual requirement for a linked-list-class is support for the Extract() +// method, which can remove an element from a list, potentially for insertion +// into a second list. Most critically, the Extract() method is idempotent, +// turning the indicated element into an extracted singleton whether it was +// contained in a list or not. This functionality allows one (or more) of +// threads to do the extraction. The iterator that identifies this extractable +// element (in this case, a pointer to the list element) can be used after +// arbitrary manipulation of the (possibly) enclosing list container. In +// general, STL containers do not provide iterators that can be used across +// modifications (insertions/extractions) of the enclosing containers, and +// certainly don't provide iterators that can be used if the identified +// element is *deleted* (removed) from the container. + +// It is possible to use multiple redundant containers, such as an STL list, +// and an STL map, to achieve similar container semantics. This container has +// only O(1) methods, while the corresponding (multiple) STL container approach +// would have more complex O(log(N)) methods (yeah... N isn't that large). +// Multiple containers also makes correctness more difficult to assert, as +// data is redundantly stored and maintained, which is generally evil. + +ConditionVariable::Event::Event() : handle_(0) { + next_ = prev_ = this; // Self referencing circular. +} + +ConditionVariable::Event::~Event() { + if (0 == handle_) { + // This is the list holder + while (!IsEmpty()) { + Event* cv_event = PopFront(); + DCHECK(cv_event->ValidateAsItem()); + delete cv_event; + } + } + DCHECK(IsSingleton()); + if (0 != handle_) { + int ret_val = CloseHandle(handle_); + DCHECK(ret_val); + } +} + +// Change a container instance permanently into an element of a list. +void ConditionVariable::Event::InitListElement() { + DCHECK(!handle_); + handle_ = CreateEvent(NULL, false, false, NULL); + CHECK(handle_); +} + +// Methods for use on lists. +bool ConditionVariable::Event::IsEmpty() const { + DCHECK(ValidateAsList()); + return IsSingleton(); +} + +void ConditionVariable::Event::PushBack(Event* other) { + DCHECK(ValidateAsList()); + DCHECK(other->ValidateAsItem()); + DCHECK(other->IsSingleton()); + // Prepare other for insertion. + other->prev_ = prev_; + other->next_ = this; + // Cut into list. + prev_->next_ = other; + prev_ = other; + DCHECK(ValidateAsDistinct(other)); +} + +ConditionVariable::Event* ConditionVariable::Event::PopFront() { + DCHECK(ValidateAsList()); + DCHECK(!IsSingleton()); + return next_->Extract(); +} + +ConditionVariable::Event* ConditionVariable::Event::PopBack() { + DCHECK(ValidateAsList()); + DCHECK(!IsSingleton()); + return prev_->Extract(); +} + +// Methods for use on list elements. +// Accessor method. +HANDLE ConditionVariable::Event::handle() const { + DCHECK(ValidateAsItem()); + return handle_; +} + +// Pull an element from a list (if it's in one). +ConditionVariable::Event* ConditionVariable::Event::Extract() { + DCHECK(ValidateAsItem()); + if (!IsSingleton()) { + // Stitch neighbors together. + next_->prev_ = prev_; + prev_->next_ = next_; + // Make extractee into a singleton. + prev_ = next_ = this; + } + DCHECK(IsSingleton()); + return this; +} + +// Method for use on a list element or on a list. +bool ConditionVariable::Event::IsSingleton() const { + DCHECK(ValidateLinks()); + return next_ == this; +} + +// Provide pre/post conditions to validate correct manipulations. +bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const { + return ValidateLinks() && other->ValidateLinks() && (this != other); +} + +bool ConditionVariable::Event::ValidateAsItem() const { + return (0 != handle_) && ValidateLinks(); +} + +bool ConditionVariable::Event::ValidateAsList() const { + return (0 == handle_) && ValidateLinks(); +} + +bool ConditionVariable::Event::ValidateLinks() const { + // Make sure both of our neighbors have links that point back to us. + // We don't do the O(n) check and traverse the whole loop, and instead only + // do a local check to (and returning from) our immediate neighbors. + return (next_->prev_ == this) && (prev_->next_ == this); +} + + +/* +FAQ On subtle implementation details: + +1) What makes this problem subtle? Please take a look at "Strategies +for Implementing POSIX Condition Variables on Win32" by Douglas +C. Schmidt and Irfan Pyarali. +http://www.cs.wustl.edu/~schmidt/win32-cv-1.html It includes +discussions of numerous flawed strategies for implementing this +functionality. I'm not convinced that even the final proposed +implementation has semantics that are as nice as this implementation +(especially with regard to Broadcast() and the impact on threads that +try to Wait() after a Broadcast() has been called, but before all the +original waiting threads have been signaled). + +2) Why can't you use a single wait_event for all threads that call +Wait()? See FAQ-question-1, or consider the following: If a single +event were used, then numerous threads calling Wait() could release +their cs locks, and be preempted just before calling +WaitForSingleObject(). If a call to Broadcast() was then presented on +a second thread, it would be impossible to actually signal all +waiting(?) threads. Some number of SetEvent() calls *could* be made, +but there could be no guarantee that those led to to more than one +signaled thread (SetEvent()'s may be discarded after the first!), and +there could be no guarantee that the SetEvent() calls didn't just +awaken "other" threads that hadn't even started waiting yet (oops). +Without any limit on the number of requisite SetEvent() calls, the +system would be forced to do many such calls, allowing many new waits +to receive spurious signals. + +3) How does this implementation cause spurious signal events? The +cause in this implementation involves a race between a signal via +time-out and a signal via Signal() or Broadcast(). The series of +actions leading to this are: + +a) Timer fires, and a waiting thread exits the line of code: + + WaitForSingleObject(waiting_event, max_time.InMilliseconds()); + +b) That thread (in (a)) is randomly pre-empted after the above line, +leaving the waiting_event reset (unsignaled) and still in the +waiting_list_. + +c) A call to Signal() (or Broadcast()) on a second thread proceeds, and +selects the waiting cv_event (identified in step (b)) as the event to revive +via a call to SetEvent(). + +d) The Signal() method (step c) calls SetEvent() on waiting_event (step b). + +e) The waiting cv_event (step b) is now signaled, but no thread is +waiting on it. + +f) When that waiting_event (step b) is reused, it will immediately +be signaled (spuriously). + + +4) Why do you recycle events, and cause spurious signals? First off, +the spurious events are very rare. They can only (I think) appear +when the race described in FAQ-question-3 takes place. This should be +very rare. Most(?) uses will involve only timer expiration, or only +Signal/Broadcast() actions. When both are used, it will be rare that +the race will appear, and it would require MANY Wait() and signaling +activities. If this implementation did not recycle events, then it +would have to create and destroy events for every call to Wait(). +That allocation/deallocation and associated construction/destruction +would be costly (per wait), and would only be a rare benefit (when the +race was "lost" and a spurious signal took place). That would be bad +(IMO) optimization trade-off. Finally, such spurious events are +allowed by the specification of condition variables (such as +implemented in Vista), and hence it is better if any user accommodates +such spurious events (see usage note in condition_variable.h). + +5) Why don't you reset events when you are about to recycle them, or +about to reuse them, so that the spurious signals don't take place? +The thread described in FAQ-question-3 step c may be pre-empted for an +arbitrary length of time before proceeding to step d. As a result, +the wait_event may actually be re-used *before* step (e) is reached. +As a result, calling reset would not help significantly. + +6) How is it that the callers lock is released atomically with the +entry into a wait state? We commit to the wait activity when we +allocate the wait_event for use in a given call to Wait(). This +allocation takes place before the caller's lock is released (and +actually before our internal_lock_ is released). That allocation is +the defining moment when "the wait state has been entered," as that +thread *can* now be signaled by a call to Broadcast() or Signal(). +Hence we actually "commit to wait" before releasing the lock, making +the pair effectively atomic. + +8) Why do you need to lock your data structures during waiting, as the +caller is already in possession of a lock? We need to Acquire() and +Release() our internal lock during Signal() and Broadcast(). If we tried +to use a callers lock for this purpose, we might conflict with their +external use of the lock. For example, the caller may use to consistently +hold a lock on one thread while calling Signal() on another, and that would +block Signal(). + +9) Couldn't a more efficient implementation be provided if you +preclude using more than one external lock in conjunction with a +single ConditionVariable instance? Yes, at least it could be viewed +as a simpler API (since you don't have to reiterate the lock argument +in each Wait() call). One of the constructors now takes a specific +lock as an argument, and a there are corresponding Wait() calls that +don't specify a lock now. It turns that the resulting implmentation +can't be made more efficient, as the internal lock needs to be used by +Signal() and Broadcast(), to access internal data structures. As a +result, I was not able to utilize the user supplied lock (which is +being used by the user elsewhere presumably) to protect the private +member access. + +9) Since you have a second lock, how can be be sure that there is no +possible deadlock scenario? Our internal_lock_ is always the last +lock acquired, and the first one released, and hence a deadlock (due +to critical section problems) is impossible as a consequence of our +lock. + +10) When doing a Broadcast(), why did you copy all the events into +an STL queue, rather than making a linked-loop, and iterating over it? +The iterating during Broadcast() is done so outside the protection +of the internal lock. As a result, other threads, such as the thread +wherein a related event is waiting, could asynchronously manipulate +the links around a cv_event. As a result, the link structure cannot +be used outside a lock. Broadcast() could iterate over waiting +events by cycling in-and-out of the protection of the internal_lock, +but that appears more expensive than copying the list into an STL +stack. + +11) Why did the lock.h file need to be modified so much for this +change? Central to a Condition Variable is the atomic release of a +lock during a Wait(). This places Wait() functionality exactly +mid-way between the two classes, Lock and Condition Variable. Given +that there can be nested Acquire()'s of locks, and Wait() had to +Release() completely a held lock, it was necessary to augment the Lock +class with a recursion counter. Even more subtle is the fact that the +recursion counter (in a Lock) must be protected, as many threads can +access it asynchronously. As a positive fallout of this, there are +now some DCHECKS to be sure no one Release()s a Lock more than they +Acquire()ed it, and there is ifdef'ed functionality that can detect +nested locks (legal under windows, but not under Posix). + +12) Why is it that the cv_events removed from list in Broadcast() and Signal() +are not leaked? How are they recovered?? The cv_events that appear to leak are +taken from the waiting_list_. For each element in that list, there is currently +a thread in or around the WaitForSingleObject() call of Wait(), and those +threads have references to these otherwise leaked events. They are passed as +arguments to be recycled just aftre returning from WaitForSingleObject(). + +13) Why did you use a custom container class (the linked list), when STL has +perfectly good containers, such as an STL list? The STL list, as with any +container, does not guarantee the utility of an iterator across manipulation +(such as insertions and deletions) of the underlying container. The custom +double-linked-list container provided that assurance. I don't believe any +combination of STL containers provided the services that were needed at the same +O(1) efficiency as the custom linked list. The unusual requirement +for the container class is that a reference to an item within a container (an +iterator) needed to be maintained across an arbitrary manipulation of the +container. This requirement exposes itself in the Wait() method, where a +waiting_event must be selected prior to the WaitForSingleObject(), and then it +must be used as part of recycling to remove the related instance from the +waiting_list. A hash table (STL map) could be used, but I was embarrased to +use a complex and relatively low efficiency container when a doubly linked list +provided O(1) performance in all required operations. Since other operations +to provide performance-and/or-fairness required queue (FIFO) and list (LIFO) +containers, I would also have needed to use an STL list/queue as well as an STL +map. In the end I decided it would be "fun" to just do it right, and I +put so many assertions (DCHECKs) into the container class that it is trivial to +code review and validate its correctness. + +*/ diff --git a/ipc/chromium/src/base/cpu.cc b/ipc/chromium/src/base/cpu.cc new file mode 100644 index 0000000000..919b69bd06 --- /dev/null +++ b/ipc/chromium/src/base/cpu.cc @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/cpu.h" +#include <intrin.h> +#include <string> + +namespace base { + +CPU::CPU() + : type_(0), + family_(0), + model_(0), + stepping_(0), + ext_model_(0), + ext_family_(0), + cpu_vendor_("unknown") { + Initialize(); +} + +void CPU::Initialize() { + int cpu_info[4] = {-1}; + char cpu_string[0x20]; + + // __cpuid with an InfoType argument of 0 returns the number of + // valid Ids in CPUInfo[0] and the CPU identification string in + // the other three array elements. The CPU identification string is + // not in linear order. The code below arranges the information + // in a human readable form. + // + // More info can be found here: + // http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + __cpuid(cpu_info, 0); + int num_ids = cpu_info[0]; + memset(cpu_string, 0, sizeof(cpu_string)); + *(reinterpret_cast<int*>(cpu_string)) = cpu_info[1]; + *(reinterpret_cast<int*>(cpu_string+4)) = cpu_info[3]; + *(reinterpret_cast<int*>(cpu_string+8)) = cpu_info[2]; + + // Interpret CPU feature information. + if (num_ids > 0) { + __cpuid(cpu_info, 1); + stepping_ = cpu_info[0] & 0xf; + model_ = (cpu_info[0] >> 4) & 0xf; + family_ = (cpu_info[0] >> 8) & 0xf; + type_ = (cpu_info[0] >> 12) & 0x3; + ext_model_ = (cpu_info[0] >> 16) & 0xf; + ext_family_ = (cpu_info[0] >> 20) & 0xff; + cpu_vendor_ = cpu_string; + } +} + +} // namespace base diff --git a/ipc/chromium/src/base/cpu.h b/ipc/chromium/src/base/cpu.h new file mode 100644 index 0000000000..8260fdb463 --- /dev/null +++ b/ipc/chromium/src/base/cpu.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_CPU_H_ +#define BASE_CPU_H_ + +#include <string> + +namespace base { + +// Query information about the processor. +class CPU { + public: + // Constructor + CPU(); + + // Accessors for CPU information. + const std::string& vendor_name() const { return cpu_vendor_; } + int stepping() const { return stepping_; } + int model() const { return model_; } + int family() const { return family_; } + int type() const { return type_; } + int extended_model() const { return ext_model_; } + int extended_family() const { return ext_family_; } + + private: + // Query the processor for CPUID information. + void Initialize(); + + int type_; // process type + int family_; // family of the processor + int model_; // model of processor + int stepping_; // processor revision number + int ext_model_; + int ext_family_; + std::string cpu_vendor_; +}; + +} // namespace base + +#endif // BASE_CPU_H_ diff --git a/ipc/chromium/src/base/dir_reader_bsd.h b/ipc/chromium/src/base/dir_reader_bsd.h new file mode 100644 index 0000000000..35ba6bd153 --- /dev/null +++ b/ipc/chromium/src/base/dir_reader_bsd.h @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 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. + +// derived from dir_reader_linux.h + +#ifndef BASE_DIR_READER_BSD_H_ +#define BASE_DIR_READER_BSD_H_ +#pragma once + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/eintr_wrapper.h" + +// See the comments in dir_reader_posix.h about this. + +namespace base { + +class DirReaderBSD { + public: + explicit DirReaderBSD(const char* directory_path) +#ifdef O_DIRECTORY + : fd_(open(directory_path, O_RDONLY | O_DIRECTORY)), +#else + : fd_(open(directory_path, O_RDONLY)), +#endif + offset_(0), + size_(0) { + memset(buf_, 0, sizeof(buf_)); + } + + ~DirReaderBSD() { + if (fd_ >= 0) { + if (HANDLE_EINTR(close(fd_))) + DLOG(ERROR) << "Failed to close directory handle"; + } + } + + bool IsValid() const { + return fd_ >= 0; + } + + // Move to the next entry returning false if the iteration is complete. + bool Next() { + if (size_) { + struct dirent* dirent = reinterpret_cast<struct dirent*>(&buf_[offset_]); +#ifdef OS_DRAGONFLY + offset_ += _DIRENT_DIRSIZ(dirent); +#else + offset_ += dirent->d_reclen; +#endif + } + + if (offset_ != size_) + return true; + + const int r = getdents(fd_, buf_, sizeof(buf_)); + if (r == 0) + return false; + if (r == -1) { + DLOG(ERROR) << "getdents returned an error: " << errno; + return false; + } + size_ = r; + offset_ = 0; + return true; + } + + const char* name() const { + if (!size_) + return NULL; + + const struct dirent* dirent = + reinterpret_cast<const struct dirent*>(&buf_[offset_]); + return dirent->d_name; + } + + int fd() const { + return fd_; + } + + static bool IsFallback() { + return false; + } + + private: + const int fd_; + char buf_[512]; + size_t offset_, size_; + + DISALLOW_COPY_AND_ASSIGN(DirReaderBSD); +}; + +} // namespace base + +#endif // BASE_DIR_READER_BSD_H_ diff --git a/ipc/chromium/src/base/dir_reader_fallback.h b/ipc/chromium/src/base/dir_reader_fallback.h new file mode 100644 index 0000000000..788ba32eff --- /dev/null +++ b/ipc/chromium/src/base/dir_reader_fallback.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 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 BASE_DIR_READER_FALLBACK_H_ +#define BASE_DIR_READER_FALLBACK_H_ +#pragma once + +namespace base { + +class DirReaderFallback { + public: + // Open a directory. If |IsValid| is true, then |Next| can be called to start + // the iteration at the beginning of the directory. + explicit DirReaderFallback(const char* directory_path) { } + // After construction, IsValid returns true iff the directory was + // successfully opened. + bool IsValid() const { return false; } + // Move to the next entry returning false if the iteration is complete. + bool Next() { return false; } + // Return the name of the current directory entry. + const char* name() { return 0;} + // Return the file descriptor which is being used. + int fd() const { return -1; } + // Returns true if this is a no-op fallback class (for testing). + static bool IsFallback() { return true; } +}; + +} // namespace base + +#endif // BASE_DIR_READER_FALLBACK_H_ diff --git a/ipc/chromium/src/base/dir_reader_linux.h b/ipc/chromium/src/base/dir_reader_linux.h new file mode 100644 index 0000000000..530fe2e015 --- /dev/null +++ b/ipc/chromium/src/base/dir_reader_linux.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 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 BASE_DIR_READER_LINUX_H_ +#define BASE_DIR_READER_LINUX_H_ +#pragma once + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/eintr_wrapper.h" + +// See the comments in dir_reader_posix.h about this. + +namespace base { + +struct linux_dirent { + uint64_t d_ino; + int64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[0]; +}; + +class DirReaderLinux { + public: + explicit DirReaderLinux(const char* directory_path) + : fd_(open(directory_path, O_RDONLY | O_DIRECTORY)), + offset_(0), + size_(0) { + memset(buf_, 0, sizeof(buf_)); + } + + ~DirReaderLinux() { + if (fd_ >= 0) { + if (HANDLE_EINTR(close(fd_))) + DLOG(ERROR) << "Failed to close directory handle"; + } + } + + bool IsValid() const { + return fd_ >= 0; + } + + // Move to the next entry returning false if the iteration is complete. + bool Next() { + if (size_) { + linux_dirent* dirent = reinterpret_cast<linux_dirent*>(&buf_[offset_]); + offset_ += dirent->d_reclen; + } + + if (offset_ != size_) + return true; + + const int r = syscall(__NR_getdents64, fd_, buf_, sizeof(buf_)); + if (r == 0) + return false; + if (r == -1) { + DLOG(ERROR) << "getdents64 returned an error: " << errno; + return false; + } + size_ = r; + offset_ = 0; + return true; + } + + const char* name() const { + if (!size_) + return NULL; + + const linux_dirent* dirent = + reinterpret_cast<const linux_dirent*>(&buf_[offset_]); + return dirent->d_name; + } + + int fd() const { + return fd_; + } + + static bool IsFallback() { + return false; + } + + private: + const int fd_; + unsigned char buf_[512]; + size_t offset_, size_; + + DISALLOW_COPY_AND_ASSIGN(DirReaderLinux); +}; + +} // namespace base + +#endif // BASE_DIR_READER_LINUX_H_ diff --git a/ipc/chromium/src/base/dir_reader_posix.h b/ipc/chromium/src/base/dir_reader_posix.h new file mode 100644 index 0000000000..4be04bd531 --- /dev/null +++ b/ipc/chromium/src/base/dir_reader_posix.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 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 BASE_DIR_READER_POSIX_H_ +#define BASE_DIR_READER_POSIX_H_ +#pragma once + +#include "build/build_config.h" + +// This header provides a class, DirReaderPosix, which allows one to open and +// read from directories without allocating memory. For the interface, see +// the generic fallback in dir_reader_fallback.h. + +// Mac note: OS X has getdirentries, but it only works if we restrict Chrome to +// 32-bit inodes. There is a getdirentries64 syscall in 10.6, but it's not +// wrapped and the direct syscall interface is unstable. Using an unstable API +// seems worse than falling back to enumerating all file descriptors so we will +// probably never implement this on the Mac. + +#if defined(OS_LINUX) +#include "base/dir_reader_linux.h" +#elif defined(OS_BSD) && !defined(__GLIBC__) +#include "base/dir_reader_bsd.h" +#else +#include "base/dir_reader_fallback.h" +#endif + +namespace base { + +#if defined(OS_LINUX) +typedef DirReaderLinux DirReaderPosix; +#elif defined(OS_BSD) && !defined(__GLIBC__) +typedef DirReaderBSD DirReaderPosix; +#else +typedef DirReaderFallback DirReaderPosix; +#endif + +} // namespace base + +#endif // BASE_DIR_READER_POSIX_H_ diff --git a/ipc/chromium/src/base/eintr_wrapper.h b/ipc/chromium/src/base/eintr_wrapper.h new file mode 100644 index 0000000000..ea4546d9d7 --- /dev/null +++ b/ipc/chromium/src/base/eintr_wrapper.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 provides a wrapper around system calls which may be interrupted by a +// signal and return EINTR. See man 7 signal. +// +// On Windows, this wrapper macro does nothing. + +#ifndef BASE_EINTR_WRAPPER_H_ +#define BASE_EINTR_WRAPPER_H_ + +#include "build/build_config.h" + +#if defined(OS_POSIX) + +#include <errno.h> + +#define HANDLE_EINTR(x) ({ \ + typeof(x) __eintr_result__; \ + do { \ + __eintr_result__ = x; \ + } while (__eintr_result__ == -1 && errno == EINTR); \ + __eintr_result__;\ +}) + +#else + +#define HANDLE_EINTR(x) x + +#endif // OS_POSIX + +#endif // !BASE_EINTR_WRAPPER_H_ diff --git a/ipc/chromium/src/base/file_descriptor_posix.h b/ipc/chromium/src/base/file_descriptor_posix.h new file mode 100644 index 0000000000..adbf6234f3 --- /dev/null +++ b/ipc/chromium/src/base/file_descriptor_posix.h @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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 BASE_FILE_DESCRIPTOR_POSIX_H_ +#define BASE_FILE_DESCRIPTOR_POSIX_H_ + +namespace base { + +// ----------------------------------------------------------------------------- +// We introduct a special structure for file descriptors in order that we are +// able to use template specialisation to special-case their handling. +// +// WARNING: (Chromium only) There are subtleties to consider if serialising +// these objects over IPC. See comments in chrome/common/ipc_message_utils.h +// above the template specialisation for this structure. +// ----------------------------------------------------------------------------- +struct FileDescriptor { + FileDescriptor() + : fd(-1), + auto_close(false) { } + + FileDescriptor(int ifd, bool iauto_close) + : fd(ifd), + auto_close(iauto_close) { } + + int fd; + // If true, this file descriptor should be closed after it has been used. For + // example an IPC system might interpret this flag as indicating that the + // file descriptor it has been given should be closed after use. + bool auto_close; +}; + +} // namespace base + +#endif // BASE_FILE_DESCRIPTOR_POSIX_H_ diff --git a/ipc/chromium/src/base/file_descriptor_shuffle.cc b/ipc/chromium/src/base/file_descriptor_shuffle.cc new file mode 100644 index 0000000000..13f0c1e705 --- /dev/null +++ b/ipc/chromium/src/base/file_descriptor_shuffle.cc @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 "base/file_descriptor_shuffle.h" + +#include <errno.h> +#include <unistd.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" + +namespace base { + +bool PerformInjectiveMultimapDestructive( + InjectiveMultimap* m, InjectionDelegate* delegate) { + static const size_t kMaxExtraFDs = 16; + int extra_fds[kMaxExtraFDs]; + unsigned next_extra_fd = 0; + + // DANGER: this function may not allocate. + + for (InjectiveMultimap::iterator i = m->begin(); i != m->end(); ++i) { + int temp_fd = -1; + + // We DCHECK the injectiveness of the mapping. + for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) { + DCHECK(i->dest != j->dest) << "Both fd " << i->source + << " and " << j->source << " map to " << i->dest; + } + + const bool is_identity = i->source == i->dest; + + for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) { + if (!is_identity && i->dest == j->source) { + if (temp_fd == -1) { + if (!delegate->Duplicate(&temp_fd, i->dest)) + return false; + if (next_extra_fd < kMaxExtraFDs) { + extra_fds[next_extra_fd++] = temp_fd; + } else { + DLOG(ERROR) << "PerformInjectiveMultimapDestructive overflowed " + << "extra_fds. Leaking file descriptors!"; + } + } + + j->source = temp_fd; + j->close = false; + } + + if (i->close && i->source == j->dest) + i->close = false; + + if (i->close && i->source == j->source) { + i->close = false; + j->close = true; + } + } + + if (!is_identity) { + if (!delegate->Move(i->source, i->dest)) + return false; + } + + if (!is_identity && i->close) + delegate->Close(i->source); + } + + for (unsigned i = 0; i < next_extra_fd; i++) + delegate->Close(extra_fds[i]); + + return true; +} + +bool PerformInjectiveMultimap(const InjectiveMultimap& m_in, + InjectionDelegate* delegate) { + InjectiveMultimap m(m_in); + return PerformInjectiveMultimapDestructive(&m, delegate); +} + +bool FileDescriptorTableInjection::Duplicate(int* result, int fd) { + *result = HANDLE_EINTR(dup(fd)); + return *result >= 0; +} + +bool FileDescriptorTableInjection::Move(int src, int dest) { + return HANDLE_EINTR(dup2(src, dest)) != -1; +} + +void FileDescriptorTableInjection::Close(int fd) { + HANDLE_EINTR(close(fd)); +} + +} // namespace base diff --git a/ipc/chromium/src/base/file_descriptor_shuffle.h b/ipc/chromium/src/base/file_descriptor_shuffle.h new file mode 100644 index 0000000000..a0a8a325c0 --- /dev/null +++ b/ipc/chromium/src/base/file_descriptor_shuffle.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 BASE_FILE_DESCRIPTOR_SHUFFLE_H_ +#define BASE_FILE_DESCRIPTOR_SHUFFLE_H_ + +#include "mozilla/Attributes.h" + +// This code exists to perform the shuffling of file descriptors which is +// commonly needed when forking subprocesses. The naive approve is very simple, +// just call dup2 to setup the desired descriptors, but wrong. It's tough to +// handle the edge cases (like mapping 0 -> 1, 1 -> 0) correctly. +// +// In order to unittest this code, it's broken into the abstract action (an +// injective multimap) and the concrete code for dealing with file descriptors. +// Users should use the code like this: +// base::InjectiveMultimap file_descriptor_map; +// file_descriptor_map.push_back(base::InjectionArc(devnull, 0, true)); +// file_descriptor_map.push_back(base::InjectionArc(devnull, 2, true)); +// file_descriptor_map.push_back(base::InjectionArc(pipe[1], 1, true)); +// base::ShuffleFileDescriptors(file_descriptor_map); +// +// and trust the the Right Thing will get done. + +#include <vector> + +namespace base { + +// A Delegate which performs the actions required to perform an injective +// multimapping in place. +class InjectionDelegate { + public: + // Duplicate |fd|, an element of the domain, and write a fresh element of the + // domain into |result|. Returns true iff successful. + virtual bool Duplicate(int* result, int fd) = 0; + // Destructively move |src| to |dest|, overwriting |dest|. Returns true iff + // successful. + virtual bool Move(int src, int dest) = 0; + // Delete an element of the domain. + virtual void Close(int fd) = 0; +}; + +// An implementation of the InjectionDelegate interface using the file +// descriptor table of the current process as the domain. +class FileDescriptorTableInjection : public InjectionDelegate { + virtual bool Duplicate(int* result, int fd) override; + virtual bool Move(int src, int dest) override; + virtual void Close(int fd) override; +}; + +// A single arc of the directed graph which describes an injective multimapping. +struct InjectionArc { + InjectionArc(int in_source, int in_dest, bool in_close) + : source(in_source), + dest(in_dest), + close(in_close) { + } + + int source; + int dest; + bool close; // if true, delete the source element after performing the + // mapping. +}; + +typedef std::vector<InjectionArc> InjectiveMultimap; + +bool PerformInjectiveMultimap(const InjectiveMultimap& map, + InjectionDelegate* delegate); +bool PerformInjectiveMultimapDestructive(InjectiveMultimap* map, + InjectionDelegate* delegate); + +// This function will not call malloc but will mutate |map| +static inline bool ShuffleFileDescriptors(InjectiveMultimap *map) { + FileDescriptorTableInjection delegate; + return PerformInjectiveMultimapDestructive(map, &delegate); +} + +} // namespace base + +#endif // !BASE_FILE_DESCRIPTOR_SHUFFLE_H_ diff --git a/ipc/chromium/src/base/file_path.cc b/ipc/chromium/src/base/file_path.cc new file mode 100644 index 0000000000..f901b583d9 --- /dev/null +++ b/ipc/chromium/src/base/file_path.cc @@ -0,0 +1,332 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 <fstream> + +#include "base/file_path.h" +#include "base/logging.h" + +// These includes are just for the *Hack functions, and should be removed +// when those functions are removed. +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" + +#if defined(FILE_PATH_USES_WIN_SEPARATORS) +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); +#else // FILE_PATH_USES_WIN_SEPARATORS +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); +#endif // FILE_PATH_USES_WIN_SEPARATORS + +const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); +const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); + +const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); + + +namespace { + +// If this FilePath contains a drive letter specification, returns the +// position of the last character of the drive letter specification, +// otherwise returns npos. This can only be true on Windows, when a pathname +// begins with a letter followed by a colon. On other platforms, this always +// returns npos. +FilePath::StringType::size_type FindDriveLetter( + const FilePath::StringType& path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + // This is dependent on an ASCII-based character set, but that's a + // reasonable assumption. iswalpha can be too inclusive here. + if (path.length() >= 2 && path[1] == L':' && + ((path[0] >= L'A' && path[0] <= L'Z') || + (path[0] >= L'a' && path[0] <= L'z'))) { + return 1; + } +#endif // FILE_PATH_USES_DRIVE_LETTERS + return FilePath::StringType::npos; +} + +bool IsPathAbsolute(const FilePath::StringType& path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + FilePath::StringType::size_type letter = FindDriveLetter(path); + if (letter != FilePath::StringType::npos) { + // Look for a separator right after the drive specification. + return path.length() > letter + 1 && + FilePath::IsSeparator(path[letter + 1]); + } + // Look for a pair of leading separators. + return path.length() > 1 && + FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]); +#else // FILE_PATH_USES_DRIVE_LETTERS + // Look for a separator in the first position. + return path.length() > 0 && FilePath::IsSeparator(path[0]); +#endif // FILE_PATH_USES_DRIVE_LETTERS +} + +} // namespace + +bool FilePath::IsSeparator(CharType character) { + for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) { + if (character == kSeparators[i]) { + return true; + } + } + + return false; +} + +// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't +// guaranteed to not modify their input strings, and in fact are implemented +// differently in this regard on different platforms. Don't use them, but +// adhere to their behavior. +FilePath FilePath::DirName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, always needs to remain in the output. If there + // is no drive letter, as will always be the case on platforms which do not + // support drive letters, letter will be npos, or -1, so the comparisons and + // resizes below using letter will still be valid. + StringType::size_type letter = FindDriveLetter(new_path.path_); + + StringType::size_type last_separator = + new_path.path_.find_last_of(kSeparators, StringType::npos, + arraysize(kSeparators) - 1); + if (last_separator == StringType::npos) { + // path_ is in the current directory. + new_path.path_.resize(letter + 1); + } else if (last_separator == letter + 1) { + // path_ is in the root directory. + new_path.path_.resize(letter + 2); + } else if (last_separator == letter + 2 && + IsSeparator(new_path.path_[letter + 1])) { + // path_ is in "//" (possibly with a drive letter); leave the double + // separator intact indicating alternate root. + new_path.path_.resize(letter + 3); + } else if (last_separator != 0) { + // path_ is somewhere else, trim the basename. + new_path.path_.resize(last_separator); + } + + new_path.StripTrailingSeparatorsInternal(); + if (!new_path.path_.length()) + new_path.path_ = kCurrentDirectory; + + return new_path; +} + +FilePath FilePath::BaseName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, is always stripped. + StringType::size_type letter = FindDriveLetter(new_path.path_); + if (letter != StringType::npos) { + new_path.path_.erase(0, letter + 1); + } + + // Keep everything after the final separator, but if the pathname is only + // one character and it's a separator, leave it alone. + StringType::size_type last_separator = + new_path.path_.find_last_of(kSeparators, StringType::npos, + arraysize(kSeparators) - 1); + if (last_separator != StringType::npos && + last_separator < new_path.path_.length() - 1) { + new_path.path_.erase(0, last_separator + 1); + } + + return new_path; +} + +FilePath::StringType FilePath::Extension() const { + // BaseName() calls StripTrailingSeparators, so cases like /foo.baz/// work. + StringType base = BaseName().value(); + + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) + return StringType(); + + const StringType::size_type last_dot = base.rfind(kExtensionSeparator); + if (last_dot == StringType::npos) + return StringType(); + return StringType(base, last_dot); +} + +FilePath FilePath::RemoveExtension() const { + StringType ext = Extension(); + // It's important to check Extension() since that verifies that the + // kExtensionSeparator actually appeared in the last path component. + if (ext.empty()) + return FilePath(path_); + // Since Extension() verified that the extension is in fact in the last path + // component, this substr will effectively strip trailing separators. + const StringType::size_type last_dot = path_.rfind(kExtensionSeparator); + return FilePath(path_.substr(0, last_dot)); +} + +FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const { + if (suffix.empty()) + return FilePath(path_); + + if (path_.empty()) + return FilePath(); + + StringType base = BaseName().value(); + if (base.empty()) + return FilePath(); + if (*(base.end() - 1) == kExtensionSeparator) { + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) { + return FilePath(); + } + } + + StringType ext = Extension(); + StringType ret = RemoveExtension().value(); + ret.append(suffix); + ret.append(ext); + return FilePath(ret); +} + +FilePath FilePath::ReplaceExtension(const StringType& extension) const { + if (path_.empty()) + return FilePath(); + + StringType base = BaseName().value(); + if (base.empty()) + return FilePath(); + if (*(base.end() - 1) == kExtensionSeparator) { + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) { + return FilePath(); + } + } + + FilePath no_ext = RemoveExtension(); + // If the new extension is "" or ".", then just remove the current extension. + if (extension.empty() || extension == StringType(1, kExtensionSeparator)) + return no_ext; + + StringType str = no_ext.value(); + if (extension[0] != kExtensionSeparator) + str.append(1, kExtensionSeparator); + str.append(extension); + return FilePath(str); +} + +FilePath FilePath::Append(const StringType& component) const { + DCHECK(!IsPathAbsolute(component)); + if (path_.compare(kCurrentDirectory) == 0) { + // Append normally doesn't do any normalization, but as a special case, + // when appending to kCurrentDirectory, just return a new path for the + // component argument. Appending component to kCurrentDirectory would + // serve no purpose other than needlessly lengthening the path, and + // it's likely in practice to wind up with FilePath objects containing + // only kCurrentDirectory when calling DirName on a single relative path + // component. + return FilePath(component); + } + + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // Don't append a separator if the path is empty (indicating the current + // directory) or if the path component is empty (indicating nothing to + // append). + if (component.length() > 0 && new_path.path_.length() > 0) { + + // Don't append a separator if the path still ends with a trailing + // separator after stripping (indicating the root directory). + if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { + + // Don't append a separator if the path is just a drive letter. + if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { + new_path.path_.append(1, kSeparators[0]); + } + } + } + + new_path.path_.append(component); + return new_path; +} + +FilePath FilePath::Append(const FilePath& component) const { + return Append(component.value()); +} + +FilePath FilePath::AppendASCII(const std::string& component) const { + DCHECK(IsStringASCII(component)); +#if defined(OS_WIN) + return Append(ASCIIToWide(component)); +#elif defined(OS_POSIX) + return Append(component); +#endif +} + +bool FilePath::IsAbsolute() const { + return IsPathAbsolute(path_); +} + +#if defined(OS_POSIX) +// See file_path.h for a discussion of the encoding of paths on POSIX +// platforms. These *Hack() functions are not quite correct, but they're +// only temporary while we fix the remainder of the code. +// Remember to remove the #includes at the top when you remove these. + +// static +FilePath FilePath::FromWStringHack(const std::wstring& wstring) { + return FilePath(base::SysWideToNativeMB(wstring)); +} +std::wstring FilePath::ToWStringHack() const { + return base::SysNativeMBToWide(path_); +} +#elif defined(OS_WIN) +// static +FilePath FilePath::FromWStringHack(const std::wstring& wstring) { + return FilePath(wstring); +} +std::wstring FilePath::ToWStringHack() const { + return path_; +} +#endif + +void FilePath::OpenInputStream(std::ifstream& stream) const { + stream.open( +#ifndef __MINGW32__ + path_.c_str(), +#else + base::SysWideToNativeMB(path_).c_str(), +#endif + std::ios::in | std::ios::binary); +} + +FilePath FilePath::StripTrailingSeparators() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + return new_path; +} + +void FilePath::StripTrailingSeparatorsInternal() { + // If there is no drive letter, start will be 1, which will prevent stripping + // the leading separator if there is only one separator. If there is a drive + // letter, start will be set appropriately to prevent stripping the first + // separator following the drive letter, if a separator immediately follows + // the drive letter. + StringType::size_type start = FindDriveLetter(path_) + 2; + + StringType::size_type last_stripped = StringType::npos; + for (StringType::size_type pos = path_.length(); + pos > start && IsSeparator(path_[pos - 1]); + --pos) { + // If the string only has two separators and they're at the beginning, + // don't strip them, unless the string began with more than two separators. + if (pos != start + 1 || last_stripped == start + 2 || + !IsSeparator(path_[start - 1])) { + path_.resize(pos - 1); + last_stripped = pos; + } + } +} diff --git a/ipc/chromium/src/base/file_path.h b/ipc/chromium/src/base/file_path.h new file mode 100644 index 0000000000..b2e9f9a00d --- /dev/null +++ b/ipc/chromium/src/base/file_path.h @@ -0,0 +1,277 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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. + +// FilePath is a container for pathnames stored in a platform's native string +// type, providing containers for manipulation in according with the +// platform's conventions for pathnames. It supports the following path +// types: +// +// POSIX Windows +// --------------- ---------------------------------- +// Fundamental type char[] wchar_t[] +// Encoding unspecified* UTF-16 +// Separator / \, tolerant of / +// Drive letters no case-insensitive A-Z followed by : +// Alternate root // (surprise!) \\, for UNC paths +// +// * The encoding need not be specified on POSIX systems, although some +// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. +// Linux does not specify an encoding, but in practice, the locale's +// character set may be used. +// +// FilePath objects are intended to be used anywhere paths are. An +// application may pass FilePath objects around internally, masking the +// underlying differences between systems, only differing in implementation +// where interfacing directly with the system. For example, a single +// OpenFile(const FilePath &) function may be made available, allowing all +// callers to operate without regard to the underlying implementation. On +// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might +// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This +// allows each platform to pass pathnames around without requiring conversions +// between encodings, which has an impact on performance, but more imporantly, +// has an impact on correctness on platforms that do not have well-defined +// encodings for pathnames. +// +// Several methods are available to perform common operations on a FilePath +// object, such as determining the parent directory (DirName), isolating the +// final path component (BaseName), and appending a relative pathname string +// to an existing FilePath object (Append). These methods are highly +// recommended over attempting to split and concatenate strings directly. +// These methods are based purely on string manipulation and knowledge of +// platform-specific pathname conventions, and do not consult the filesystem +// at all, making them safe to use without fear of blocking on I/O operations. +// These methods do not function as mutators but instead return distinct +// instances of FilePath objects, and are therefore safe to use on const +// objects. The objects themselves are safe to share between threads. +// +// To aid in initialization of FilePath objects from string literals, a +// FILE_PATH_LITERAL macro is provided, which accounts for the difference +// between char[]-based pathnames on POSIX systems and wchar_t[]-based +// pathnames on Windows. +// +// Because a FilePath object should not be instantiated at the global scope, +// instead, use a FilePath::CharType[] and initialize it with +// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the +// character array. Example: +// +// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); +// | +// | void Function() { +// | FilePath log_file_path(kLogFileName); +// | [...] +// | } + +#ifndef BASE_FILE_PATH_H_ +#define BASE_FILE_PATH_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/hash_tables.h" + +// Windows-style drive letter support and pathname separator characters can be +// enabled and disabled independently, to aid testing. These #defines are +// here so that the same setting can be used in both the implementation and +// in the unit test. +#if defined(OS_WIN) +#define FILE_PATH_USES_DRIVE_LETTERS +#define FILE_PATH_USES_WIN_SEPARATORS +#endif // OS_WIN + +// An abstraction to isolate users from the differences between native +// pathnames on different platforms. +class FilePath { + public: +#if defined(OS_POSIX) + // On most platforms, native pathnames are char arrays, and the encoding + // may or may not be specified. On Mac OS X, native pathnames are encoded + // in UTF-8. + typedef std::string StringType; +#elif defined(OS_WIN) + // On Windows, for Unicode-aware applications, native pathnames are wchar_t + // arrays encoded in UTF-16. + typedef std::wstring StringType; +#endif // OS_WIN + + typedef StringType::value_type CharType; + + // Null-terminated array of separators used to separate components in + // hierarchical paths. Each character in this array is a valid separator, + // but kSeparators[0] is treated as the canonical separator and will be used + // when composing pathnames. + static const CharType kSeparators[]; + + // A special path component meaning "this directory." + static const CharType kCurrentDirectory[]; + + // A special path component meaning "the parent directory." + static const CharType kParentDirectory[]; + + // The character used to identify a file extension. + static const CharType kExtensionSeparator; + + FilePath() {} + FilePath(const FilePath& that) : path_(that.path_) {} + explicit FilePath(const StringType& path) : path_(path) {} + +#if defined(OS_WIN) + explicit FilePath(const wchar_t* path) : path_(path) {} +#endif + + FilePath& operator=(const FilePath& that) { + path_ = that.path_; + return *this; + } + + bool operator==(const FilePath& that) const { + return path_ == that.path_; + } + + bool operator!=(const FilePath& that) const { + return path_ != that.path_; + } + + // Required for some STL containers and operations + bool operator<(const FilePath& that) const { + return path_ < that.path_; + } + + const StringType& value() const { return path_; } + + bool empty() const { return path_.empty(); } + + // Returns true if |character| is in kSeparators. + static bool IsSeparator(CharType character); + + // Returns a FilePath corresponding to the directory containing the path + // named by this object, stripping away the file component. If this object + // only contains one component, returns a FilePath identifying + // kCurrentDirectory. If this object already refers to the root directory, + // returns a FilePath identifying the root directory. + FilePath DirName() const; + + // Returns a FilePath corresponding to the last path component of this + // object, either a file or a directory. If this object already refers to + // the root directory, returns a FilePath identifying the root directory; + // this is the only situation in which BaseName will return an absolute path. + FilePath BaseName() const; + + // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if + // the file has no extension. If non-empty, Extension() will always start + // with precisely one ".". The following code should always work regardless + // of the value of path. + // new_path = path.RemoveExtension().value().append(path.Extension()); + // ASSERT(new_path == path.value()); + // NOTE: this is different from the original file_util implementation which + // returned the extension without a leading "." ("jpg" instead of ".jpg") + StringType Extension() const; + + // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" + // NOTE: this is slightly different from the similar file_util implementation + // which returned simply 'jojo'. + FilePath RemoveExtension() const; + + // Inserts |suffix| after the file name portion of |path| but before the + // extension. Returns "" if BaseName() == "." or "..". + // Examples: + // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg" + // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg" + // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)" + // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)" + FilePath InsertBeforeExtension(const StringType& suffix) const; + + // Replaces the extension of |file_name| with |extension|. If |file_name| + // does not have an extension, them |extension| is added. If |extension| is + // empty, then the extension is removed from |file_name|. + // Returns "" if BaseName() == "." or "..". + FilePath ReplaceExtension(const StringType& extension) const; + + // Returns a FilePath by appending a separator and the supplied path + // component to this object's path. Append takes care to avoid adding + // excessive separators if this object's path already ends with a separator. + // If this object's path is kCurrentDirectory, a new FilePath corresponding + // only to |component| is returned. |component| must be a relative path; + // it is an error to pass an absolute path. + FilePath Append(const StringType& component) const WARN_UNUSED_RESULT; + FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT; + + // Although Windows StringType is std::wstring, since the encoding it uses for + // paths is well defined, it can handle ASCII path components as well. + // Mac uses UTF8, and since ASCII is a subset of that, it works there as well. + // On Linux, although it can use any 8-bit encoding for paths, we assume that + // ASCII is a valid subset, regardless of the encoding, since many operating + // system paths will always be ASCII. + FilePath AppendASCII(const std::string& component) const WARN_UNUSED_RESULT; + + // Returns true if this FilePath contains an absolute path. On Windows, an + // absolute path begins with either a drive letter specification followed by + // a separator character, or with two separator characters. On POSIX + // platforms, an absolute path begins with a separator character. + bool IsAbsolute() const; + + // Returns a copy of this FilePath that does not end with a trailing + // separator. + FilePath StripTrailingSeparators() const; + + // Calls open on given ifstream instance + void OpenInputStream(std::ifstream &stream) const; + + // Older Chromium code assumes that paths are always wstrings. + // This function converts a wstring to a FilePath, and is useful to smooth + // porting that old code to the FilePath API. + // It has "Hack" in its name so people feel bad about using it. + // TODO(port): remove these functions. + static FilePath FromWStringHack(const std::wstring& wstring); + + // Older Chromium code assumes that paths are always wstrings. + // This function produces a wstring from a FilePath, and is useful to smooth + // porting that old code to the FilePath API. + // It has "Hack" in its name so people feel bad about using it. + // TODO(port): remove these functions. + std::wstring ToWStringHack() const; + + private: + // Remove trailing separators from this object. If the path is absolute, it + // will never be stripped any more than to refer to the absolute root + // directory, so "////" will become "/", not "". A leading pair of + // separators is never stripped, to support alternate roots. This is used to + // support UNC paths on Windows. + void StripTrailingSeparatorsInternal(); + + StringType path_; +}; + +// Macros for string literal initialization of FilePath::CharType[]. +#if defined(OS_POSIX) +#define FILE_PATH_LITERAL(x) x +#elif defined(OS_WIN) +#define FILE_PATH_LITERAL(x) L ## x +#endif // OS_WIN + +// Implement hash function so that we can use FilePaths in hashsets and maps. +#if defined(COMPILER_GCC) && !defined(ANDROID) +namespace __gnu_cxx { + +template<> +struct hash<FilePath> { + size_t operator()(const FilePath& f) const { + return hash<FilePath::StringType>()(f.value()); + } +}; + +} // namespace __gnu_cxx +#elif defined(COMPILER_MSVC) +namespace stdext { + +inline size_t hash_value(const FilePath& f) { + return hash_value(f.value()); +} + +} // namespace stdext +#endif // COMPILER + +#endif // BASE_FILE_PATH_H_ diff --git a/ipc/chromium/src/base/file_util.cc b/ipc/chromium/src/base/file_util.cc new file mode 100644 index 0000000000..6f7d92f3ab --- /dev/null +++ b/ipc/chromium/src/base/file_util.cc @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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 "base/file_util.h" + +#if defined(OS_WIN) +#include <io.h> +#endif +#include <stdio.h> +#if defined(ANDROID) || defined(OS_POSIX) +#include <unistd.h> +#endif + +#include <fstream> + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/string_util.h" + +#include "base/string_piece.h" +#include "base/sys_string_conversions.h" + +namespace { + +const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.'); + +} // namespace + +namespace file_util { + +bool EndsWithSeparator(const FilePath& path) { + FilePath::StringType value = path.value(); + if (value.empty()) + return false; + + return FilePath::IsSeparator(value[value.size() - 1]); +} + +void TrimTrailingSeparator(std::wstring* dir) { + while (dir->length() > 1 && EndsWithSeparator(dir)) + dir->resize(dir->length() - 1); +} + +FilePath::StringType GetFileExtensionFromPath(const FilePath& path) { + FilePath::StringType file_name = path.BaseName().value(); + const FilePath::StringType::size_type last_dot = + file_name.rfind(kExtensionSeparator); + return FilePath::StringType(last_dot == FilePath::StringType::npos ? + FILE_PATH_LITERAL("") : + file_name, last_dot+1); +} + +void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) { + FilePath::StringType& value = + const_cast<FilePath::StringType&>(path->value()); + + const FilePath::StringType::size_type last_dot = + value.rfind(kExtensionSeparator); + const FilePath::StringType::size_type last_separator = + value.find_last_of(FilePath::StringType(FilePath::kSeparators)); + + if (last_dot == FilePath::StringType::npos || + (last_separator != std::wstring::npos && last_dot < last_separator)) { + // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo". + // We should just append the suffix to the entire path. + value.append(suffix); + return; + } + + value.insert(last_dot, suffix); +} + +void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) { + FilePath::StringType clean_extension; + // If the new extension is "" or ".", then we will just remove the current + // extension. + if (!extension.empty() && + extension != FilePath::StringType(&kExtensionSeparator, 1)) { + if (extension[0] != kExtensionSeparator) + clean_extension.append(&kExtensionSeparator, 1); + clean_extension.append(extension); + } + + FilePath::StringType& value = + const_cast<FilePath::StringType&>(path->value()); + const FilePath::StringType::size_type last_dot = + value.rfind(kExtensionSeparator); + const FilePath::StringType::size_type last_separator = + value.find_last_of(FilePath::StringType(FilePath::kSeparators)); + + // Erase the current extension, if any. + if ((last_dot > last_separator || + last_separator == FilePath::StringType::npos) && + last_dot != FilePath::StringType::npos) + value.erase(last_dot); + + value.append(clean_extension); +} + +FILE* CreateAndOpenTemporaryFile(FilePath* path) { + FilePath directory; + if (!GetTempDir(&directory)) + return NULL; + + return CreateAndOpenTemporaryFileInDir(directory, path); +} + +bool GetFileSize(const FilePath& file_path, int64_t* file_size) { + FileInfo info; + if (!GetFileInfo(file_path, &info)) + return false; + *file_size = info.size; + return true; +} + +bool CloseFile(FILE* file) { + if (file == NULL) + return true; + return fclose(file) == 0; +} + +// Deprecated functions ---------------------------------------------------- + +bool AbsolutePath(std::wstring* path_str) { + FilePath path(FilePath::FromWStringHack(*path_str)); + if (!AbsolutePath(&path)) + return false; + *path_str = path.ToWStringHack(); + return true; +} +void AppendToPath(std::wstring* path, const std::wstring& new_ending) { + if (!path) { + NOTREACHED(); + return; // Don't crash in this function in release builds. + } + + if (!EndsWithSeparator(path)) + path->push_back(FilePath::kSeparators[0]); + path->append(new_ending); +} +bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) { + return CopyFile(FilePath::FromWStringHack(from_path), + FilePath::FromWStringHack(to_path)); +} +bool CreateDirectory(const std::wstring& full_path) { + return CreateDirectory(FilePath::FromWStringHack(full_path)); +} +bool CreateNewTempDirectory(const std::wstring& prefix, + std::wstring* new_temp_path) { +#if defined(OS_WIN) + FilePath::StringType dir_prefix(prefix); +#elif defined(OS_POSIX) + FilePath::StringType dir_prefix = WideToUTF8(prefix); +#endif + FilePath temp_path; + if (!CreateNewTempDirectory(dir_prefix, &temp_path)) + return false; + *new_temp_path = temp_path.ToWStringHack(); + return true; +} +bool CreateTemporaryFileName(std::wstring* temp_file) { + FilePath temp_file_path; + if (!CreateTemporaryFileName(&temp_file_path)) + return false; + *temp_file = temp_file_path.ToWStringHack(); + return true; +} +bool Delete(const std::wstring& path) { + return Delete(FilePath::FromWStringHack(path)); +} +bool DirectoryExists(const std::wstring& path) { + return DirectoryExists(FilePath::FromWStringHack(path)); +} +bool EndsWithSeparator(std::wstring* path) { + return EndsWithSeparator(FilePath::FromWStringHack(*path)); +} +bool EndsWithSeparator(const std::wstring& path) { + return EndsWithSeparator(FilePath::FromWStringHack(path)); +} +bool GetCurrentDirectory(std::wstring* path_str) { + FilePath path; + if (!GetCurrentDirectory(&path)) + return false; + *path_str = path.ToWStringHack(); + return true; +} +std::wstring GetFileExtensionFromPath(const std::wstring& path) { + FilePath::StringType extension = + GetFileExtensionFromPath(FilePath::FromWStringHack(path)); +#if defined(OS_WIN) + return extension; +#elif defined(OS_POSIX) + return UTF8ToWide(extension); +#endif +} +bool GetFileInfo(const std::wstring& file_path, FileInfo* results) { + return GetFileInfo(FilePath::FromWStringHack(file_path), results); +} +std::wstring GetFilenameFromPath(const std::wstring& path) { + if (path.empty() || EndsWithSeparator(path)) + return std::wstring(); + + return FilePath::FromWStringHack(path).BaseName().ToWStringHack(); +} +bool GetFileSize(const std::wstring& file_path, int64_t* file_size) { + return GetFileSize(FilePath::FromWStringHack(file_path), file_size); +} +bool GetTempDir(std::wstring* path_str) { + FilePath path; + if (!GetTempDir(&path)) + return false; + *path_str = path.ToWStringHack(); + return true; +} +FILE* OpenFile(const std::wstring& filename, const char* mode) { + return OpenFile(FilePath::FromWStringHack(filename), mode); +} +bool PathExists(const std::wstring& path) { + return PathExists(FilePath::FromWStringHack(path)); +} +bool PathIsWritable(const std::wstring& path) { + return PathIsWritable(FilePath::FromWStringHack(path)); +} +int ReadFile(const std::wstring& filename, char* data, int size) { + return ReadFile(FilePath::FromWStringHack(filename), data, size); +} +bool SetCurrentDirectory(const std::wstring& directory) { + return SetCurrentDirectory(FilePath::FromWStringHack(directory)); +} +void UpOneDirectory(std::wstring* dir) { + FilePath path = FilePath::FromWStringHack(*dir); + FilePath directory = path.DirName(); + // If there is no separator, we will get back kCurrentDirectory. + // In this case don't change |dir|. + if (directory.value() != FilePath::kCurrentDirectory) + *dir = directory.ToWStringHack(); +} +int WriteFile(const std::wstring& filename, const char* data, int size) { + return WriteFile(FilePath::FromWStringHack(filename), data, size); +} +} // namespace diff --git a/ipc/chromium/src/base/file_util.h b/ipc/chromium/src/base/file_util.h new file mode 100644 index 0000000000..c6bcc1895c --- /dev/null +++ b/ipc/chromium/src/base/file_util.h @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 contains utility functions for dealing with the local +// filesystem. + +#ifndef BASE_FILE_UTIL_H_ +#define BASE_FILE_UTIL_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#elif defined(ANDROID) +#include <sys/stat.h> +#elif defined(OS_POSIX) +#include <sys/types.h> +#include <sys/stat.h> +#endif + +#include <stdio.h> + +#include <stack> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/file_path.h" + +namespace file_util { + +//----------------------------------------------------------------------------- +// Functions that operate purely on a path string w/o touching the filesystem: + +// Returns true if the given path ends with a path separator character. +bool EndsWithSeparator(const FilePath& path); +// These two versions are both deprecated. TODO(estade): remove them. +bool EndsWithSeparator(std::wstring* path); +bool EndsWithSeparator(const std::wstring& path); + +// Modifies a string by trimming all trailing separators from the end. +// Deprecated. FilePath does this automatically, and if it's constructed from a +// path with a trailing separator, StripTrailingSeparators() may be used. +void TrimTrailingSeparator(std::wstring* dir); + +// Strips the topmost directory from the end of 'dir'. Assumes 'dir' does not +// refer to a file. +// If 'dir' is a root directory, return without change. +// Deprecated. Use FilePath::DirName instead. +void UpOneDirectory(std::wstring* dir); + +// Returns the filename portion of 'path', without any leading \'s or /'s. +// Deprecated. Use FilePath::BaseName instead. +std::wstring GetFilenameFromPath(const std::wstring& path); + +// Deprecated compatibility function. Use FilePath::Extension. +FilePath::StringType GetFileExtensionFromPath(const FilePath& path); +// Deprecated temporary compatibility function. +std::wstring GetFileExtensionFromPath(const std::wstring& path); + +// Appends new_ending to path, adding a separator between the two if necessary. +void AppendToPath(std::wstring* path, const std::wstring& new_ending); + +// Convert provided relative path into an absolute path. Returns false on +// error. On POSIX, this function fails if the path does not exist. +bool AbsolutePath(FilePath* path); +// Deprecated temporary compatibility function. +bool AbsolutePath(std::wstring* path); + +// Deprecated compatibility function. Use FilePath::InsertBeforeExtension. +void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix); + +// Deprecated compatibility function. Use FilePath::ReplaceExtension. +void ReplaceExtension(FilePath* file_name, + const FilePath::StringType& extension); + +#if defined(OS_WIN) +// Deprecated temporary compatibility functions. +void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix); +void ReplaceExtension(std::wstring* file_name, const std::wstring& extension); +#endif + +//----------------------------------------------------------------------------- +// Functions that involve filesystem access or modification: + +// Deletes the given path, whether it's a file or a directory. +// If it's a directory, it's perfectly happy to delete all of the +// directory's contents. +// Returns true if successful, false otherwise. +bool Delete(const FilePath& path); +// Deprecated temporary compatibility function. +bool Delete(const std::wstring& path); + +// Copies a single file. Use CopyDirectory to copy directories. +bool CopyFile(const FilePath& from_path, const FilePath& to_path); +// Deprecated temporary compatibility function. +bool CopyFile(const std::wstring& from_path, const std::wstring& to_path); + +// Returns true if the given path exists on the local filesystem, +// false otherwise. +bool PathExists(const FilePath& path); +// Deprecated temporary compatibility function. +bool PathExists(const std::wstring& path); + +// Returns true if the given path is writable by the user, false otherwise. +bool PathIsWritable(const FilePath& path); +// Deprecated temporary compatibility function. +bool PathIsWritable(const std::wstring& path); + +// Returns true if the given path exists and is a directory, false otherwise. +bool DirectoryExists(const FilePath& path); +// Deprecated temporary compatibility function. +bool DirectoryExists(const std::wstring& path); + +#if defined(OS_POSIX) +// Read exactly |bytes| bytes from file descriptor |fd|, storing the result +// in |buffer|. This function is protected against EINTR and partial reads. +// Returns true iff |bytes| bytes have been successfuly read from |fd|. +bool ReadFromFD(int fd, char* buffer, size_t bytes); +#endif // defined(OS_POSIX) + +// Get the temporary directory provided by the system. +bool GetTempDir(FilePath* path); +// Deprecated temporary compatibility function. +bool GetTempDir(std::wstring* path); +// Get a temporary directory for shared memory files. +// Only useful on POSIX; redirects to GetTempDir() on Windows. +bool GetShmemTempDir(FilePath* path); + +// Creates a temporary file. The full path is placed in |path|, and the +// function returns true if was successful in creating the file. The file will +// be empty and all handles closed after this function returns. +// TODO(erikkay): rename this function and track down all of the callers. +// (Clarification of erik's comment: the intent is to rename the BlahFileName() +// calls into BlahFile(), since they create temp files (not temp filenames).) +bool CreateTemporaryFileName(FilePath* path); +// Deprecated temporary compatibility function. +bool CreateTemporaryFileName(std::wstring* temp_file); + +// Create and open a temporary file. File is opened for read/write. +// The full path is placed in |path|, and the function returns true if +// was successful in creating and opening the file. +FILE* CreateAndOpenTemporaryFile(FilePath* path); +// Like above but for shmem files. Only useful for POSIX. +FILE* CreateAndOpenTemporaryShmemFile(FilePath* path); + +// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|. +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path); + +// Same as CreateTemporaryFileName but the file is created in |dir|. +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file); + +// Create a new directory under TempPath. If prefix is provided, the new +// directory name is in the format of prefixyyyy. +// NOTE: prefix is ignored in the POSIX implementation. +// TODO(erikkay): is this OK? +// If success, return true and output the full path of the directory created. +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path); +// Deprecated temporary compatibility function. +bool CreateNewTempDirectory(const std::wstring& prefix, + std::wstring* new_temp_path); + +// Creates a directory, as well as creating any parent directories, if they +// don't exist. Returns 'true' on successful creation, or if the directory +// already exists. +bool CreateDirectory(const FilePath& full_path); +// Deprecated temporary compatibility function. +bool CreateDirectory(const std::wstring& full_path); + +// Returns the file size. Returns true on success. +bool GetFileSize(const FilePath& file_path, int64_t* file_size); +// Deprecated temporary compatibility function. +bool GetFileSize(const std::wstring& file_path, int64_t* file_size); + +// Used to hold information about a given file path. See GetFileInfo below. +struct FileInfo { + // The size of the file in bytes. Undefined when is_directory is true. + int64_t size; + + // True if the file corresponds to a directory. + bool is_directory; + + // Add additional fields here as needed. +}; + +// Returns information about the given file path. +bool GetFileInfo(const FilePath& file_path, FileInfo* info); +// Deprecated temporary compatibility function. +bool GetFileInfo(const std::wstring& file_path, FileInfo* info); + +// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. +FILE* OpenFile(const FilePath& filename, const char* mode); +// Deprecated temporary compatibility functions. +FILE* OpenFile(const std::string& filename, const char* mode); +FILE* OpenFile(const std::wstring& filename, const char* mode); + +// Closes file opened by OpenFile. Returns true on success. +bool CloseFile(FILE* file); + +// Reads the given number of bytes from the file into the buffer. Returns +// the number of read bytes, or -1 on error. +int ReadFile(const FilePath& filename, char* data, int size); +// Deprecated temporary compatibility function. +int ReadFile(const std::wstring& filename, char* data, int size); + +// Writes the given buffer into the file, overwriting any data that was +// previously there. Returns the number of bytes written, or -1 on error. +int WriteFile(const FilePath& filename, const char* data, int size); +// Deprecated temporary compatibility function. +int WriteFile(const std::wstring& filename, const char* data, int size); + +// Gets the current working directory for the process. +bool GetCurrentDirectory(FilePath* path); +// Deprecated temporary compatibility function. +bool GetCurrentDirectory(std::wstring* path); + +// Sets the current working directory for the process. +bool SetCurrentDirectory(const FilePath& path); +// Deprecated temporary compatibility function. +bool SetCurrentDirectory(const std::wstring& current_directory); + +} // namespace file_util + +#endif // BASE_FILE_UTIL_H_ diff --git a/ipc/chromium/src/base/file_util_mac.mm b/ipc/chromium/src/base/file_util_mac.mm new file mode 100644 index 0000000000..f801e7d7f9 --- /dev/null +++ b/ipc/chromium/src/base/file_util_mac.mm @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2008 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 "base/file_util.h" + +#import <Cocoa/Cocoa.h> +#include <copyfile.h> + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "base/scoped_nsautorelease_pool.h" + +namespace file_util { + +bool GetTempDir(FilePath* path) { + base::ScopedNSAutoreleasePool autorelease_pool; + NSString* tmp = NSTemporaryDirectory(); + if (tmp == nil) + return false; + *path = FilePath([tmp fileSystemRepresentation]); + return true; +} + +bool GetShmemTempDir(FilePath* path) { + return GetTempDir(path); +} + +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + return (copyfile(from_path.value().c_str(), + to_path.value().c_str(), NULL, COPYFILE_ALL) == 0); +} + +} // namespace diff --git a/ipc/chromium/src/base/file_util_posix.cc b/ipc/chromium/src/base/file_util_posix.cc new file mode 100644 index 0000000000..d648974049 --- /dev/null +++ b/ipc/chromium/src/base/file_util_posix.cc @@ -0,0 +1,337 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/file_util.h" + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <libgen.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/mman.h> +#define _DARWIN_USE_64_BIT_INODE // Use 64-bit inode data structures +#include <sys/stat.h> +#include <sys/types.h> +#include <time.h> +#include <unistd.h> + +#include <fstream> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "base/time.h" + +namespace file_util { + +#if defined(GOOGLE_CHROME_BUILD) +static const char* kTempFileName = "com.google.chrome.XXXXXX"; +#else +static const char* kTempFileName = "org.chromium.XXXXXX"; +#endif + +bool AbsolutePath(FilePath* path) { + char full_path[PATH_MAX]; + if (realpath(path->value().c_str(), full_path) == NULL) + return false; + *path = FilePath(full_path); + return true; +} + +// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" +// which works both with and without the recursive flag. I'm not sure we need +// that functionality. If not, remove from file_util_win.cc, otherwise add it +// here. +bool Delete(const FilePath& path) { + const char* path_str = path.value().c_str(); + struct stat file_info; + int test = stat(path_str, &file_info); + if (test != 0) { + // The Windows version defines this condition as success. + bool ret = (errno == ENOENT || errno == ENOTDIR); + return ret; + } + if (!S_ISDIR(file_info.st_mode)) + return (unlink(path_str) == 0); + + return (rmdir(path_str) == 0); +} + +bool PathExists(const FilePath& path) { + struct stat file_info; + return (stat(path.value().c_str(), &file_info) == 0); +} + +bool PathIsWritable(const FilePath& path) { + FilePath test_path(path); + struct stat file_info; + if (stat(test_path.value().c_str(), &file_info) != 0) { + // If the path doesn't exist, test the parent dir. + test_path = test_path.DirName(); + // If the parent dir doesn't exist, then return false (the path is not + // directly writable). + if (stat(test_path.value().c_str(), &file_info) != 0) + return false; + } + if (S_IWOTH & file_info.st_mode) + return true; + if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode)) + return true; + if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode)) + return true; + return false; +} + +bool DirectoryExists(const FilePath& path) { + struct stat file_info; + if (stat(path.value().c_str(), &file_info) == 0) + return S_ISDIR(file_info.st_mode); + return false; +} + +bool ReadFromFD(int fd, char* buffer, size_t bytes) { + size_t total_read = 0; + while (total_read < bytes) { + ssize_t bytes_read = + HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); + if (bytes_read <= 0) + break; + total_read += bytes_read; + } + return total_read == bytes; +} + +// Creates and opens a temporary file in |directory|, returning the +// file descriptor. |path| is set to the temporary file path. +// Note TODO(erikkay) comment in header for BlahFileName() calls; the +// intent is to rename these files BlahFile() (since they create +// files, not filenames). This function does NOT unlink() the file. +int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { + *path = directory.Append(kTempFileName); + const std::string& tmpdir_string = path->value(); + // this should be OK since mkstemp just replaces characters in place + char* buffer = const_cast<char*>(tmpdir_string.c_str()); + + return mkstemp(buffer); +} + +bool CreateTemporaryFileName(FilePath* path) { + FilePath directory; + if (!GetTempDir(&directory)) + return false; + int fd = CreateAndOpenFdForTemporaryFile(directory, path); + if (fd < 0) + return false; + close(fd); + return true; +} + +FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { + FilePath directory; + if (!GetShmemTempDir(&directory)) + return NULL; + + return CreateAndOpenTemporaryFileInDir(directory, path); +} + +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { + int fd = CreateAndOpenFdForTemporaryFile(dir, path); + if (fd < 0) + return NULL; + + return fdopen(fd, "a+"); +} + +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file) { + // Not implemented yet. + NOTREACHED(); + return false; +} + +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path) { + FilePath tmpdir; + if (!GetTempDir(&tmpdir)) + return false; + tmpdir = tmpdir.Append(kTempFileName); + std::string tmpdir_string = tmpdir.value(); +#ifdef ANDROID + char* dtemp = NULL; +#else + // this should be OK since mkdtemp just replaces characters in place + char* buffer = const_cast<char*>(tmpdir_string.c_str()); + char* dtemp = mkdtemp(buffer); +#endif + if (!dtemp) + return false; + *new_temp_path = FilePath(dtemp); + return true; +} + +bool CreateDirectory(const FilePath& full_path) { + std::vector<FilePath> subpaths; + + // Collect a list of all parent directories. + FilePath last_path = full_path; + subpaths.push_back(full_path); + for (FilePath path = full_path.DirName(); + path.value() != last_path.value(); path = path.DirName()) { + subpaths.push_back(path); + last_path = path; + } + + // Iterate through the parents and create the missing ones. + for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin(); + i != subpaths.rend(); ++i) { + if (!DirectoryExists(*i)) { + if (mkdir(i->value().c_str(), 0777) != 0) + return false; + } + } + return true; +} + +bool GetFileInfo(const FilePath& file_path, FileInfo* results) { + struct stat file_info; + if (stat(file_path.value().c_str(), &file_info) != 0) + return false; + results->is_directory = S_ISDIR(file_info.st_mode); + results->size = file_info.st_size; + return true; +} + +FILE* OpenFile(const std::string& filename, const char* mode) { + return OpenFile(FilePath(filename), mode); +} + +FILE* OpenFile(const FilePath& filename, const char* mode) { + return fopen(filename.value().c_str(), mode); +} + +int ReadFile(const FilePath& filename, char* data, int size) { + int fd = open(filename.value().c_str(), O_RDONLY); + if (fd < 0) + return -1; + + int ret_value = HANDLE_EINTR(read(fd, data, size)); + HANDLE_EINTR(close(fd)); + return ret_value; +} + +int WriteFile(const FilePath& filename, const char* data, int size) { + int fd = creat(filename.value().c_str(), 0666); + if (fd < 0) + return -1; + + // Allow for partial writes + ssize_t bytes_written_total = 0; + do { + ssize_t bytes_written_partial = + HANDLE_EINTR(write(fd, data + bytes_written_total, + size - bytes_written_total)); + if (bytes_written_partial < 0) { + HANDLE_EINTR(close(fd)); + return -1; + } + bytes_written_total += bytes_written_partial; + } while (bytes_written_total < size); + + HANDLE_EINTR(close(fd)); + return bytes_written_total; +} + +// Gets the current working directory for the process. +bool GetCurrentDirectory(FilePath* dir) { + char system_buffer[PATH_MAX] = ""; + if (!getcwd(system_buffer, sizeof(system_buffer))) { + NOTREACHED(); + return false; + } + *dir = FilePath(system_buffer); + return true; +} + +// Sets the current working directory for the process. +bool SetCurrentDirectory(const FilePath& path) { + int ret = chdir(path.value().c_str()); + return !ret; +} + +#if !defined(OS_MACOSX) +bool GetTempDir(FilePath* path) { + const char* tmp = getenv("TMPDIR"); + if (tmp) + *path = FilePath(tmp); + else + *path = FilePath("/tmp"); + return true; +} + +bool GetShmemTempDir(FilePath* path) { +#if defined(OS_LINUX) && !defined(ANDROID) + *path = FilePath("/dev/shm"); + return true; +#else + return GetTempDir(path); +#endif +} + +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + int infile = open(from_path.value().c_str(), O_RDONLY); + if (infile < 0) + return false; + + int outfile = creat(to_path.value().c_str(), 0666); + if (outfile < 0) { + close(infile); + return false; + } + + const size_t kBufferSize = 32768; + std::vector<char> buffer(kBufferSize); + bool result = true; + + while (result) { + ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size())); + if (bytes_read < 0) { + result = false; + break; + } + if (bytes_read == 0) + break; + // Allow for partial writes + ssize_t bytes_written_per_read = 0; + do { + ssize_t bytes_written_partial = HANDLE_EINTR(write( + outfile, + &buffer[bytes_written_per_read], + bytes_read - bytes_written_per_read)); + if (bytes_written_partial < 0) { + result = false; + break; + } + bytes_written_per_read += bytes_written_partial; + } while (bytes_written_per_read < bytes_read); + } + + if (HANDLE_EINTR(close(infile)) < 0) + result = false; + if (HANDLE_EINTR(close(outfile)) < 0) + result = false; + + return result; +} +#endif // !defined(OS_MACOSX) + +} // namespace file_util diff --git a/ipc/chromium/src/base/file_util_win.cc b/ipc/chromium/src/base/file_util_win.cc new file mode 100644 index 0000000000..fd3fdbeeb5 --- /dev/null +++ b/ipc/chromium/src/base/file_util_win.cc @@ -0,0 +1,356 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/file_util.h" + +#include <windows.h> +#include <shellapi.h> +#include <shlobj.h> +#include <time.h> +#include <string> + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/win_util.h" + +namespace file_util { + +bool AbsolutePath(FilePath* path) { + wchar_t file_path_buf[MAX_PATH]; + if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH)) + return false; + *path = FilePath(file_path_buf); + return true; +} + +bool Delete(const FilePath& path) { + if (path.value().length() >= MAX_PATH) + return false; + + // Use DeleteFile; it should be faster. DeleteFile + // fails if passed a directory though, which is why we fall through on + // failure to the SHFileOperation. + if (DeleteFile(path.value().c_str()) != 0) + return true; + + // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, + // so we have to use wcscpy because wcscpy_s writes non-NULLs + // into the rest of the buffer. + wchar_t double_terminated_path[MAX_PATH + 1] = {0}; +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation + wcscpy(double_terminated_path, path.value().c_str()); + + SHFILEOPSTRUCT file_operation = {0}; + file_operation.wFunc = FO_DELETE; + file_operation.pFrom = double_terminated_path; + file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION; + file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; + int err = SHFileOperation(&file_operation); + // Some versions of Windows return ERROR_FILE_NOT_FOUND when + // deleting an empty directory. + return (err == 0 || err == ERROR_FILE_NOT_FOUND); +} + +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + // NOTE: I suspect we could support longer paths, but that would involve + // analyzing all our usage of files. + if (from_path.value().length() >= MAX_PATH || + to_path.value().length() >= MAX_PATH) { + return false; + } + return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(), + false) != 0); +} + +bool ShellCopy(const FilePath& from_path, const FilePath& to_path, + bool recursive) { + // NOTE: I suspect we could support longer paths, but that would involve + // analyzing all our usage of files. + if (from_path.value().length() >= MAX_PATH || + to_path.value().length() >= MAX_PATH) { + return false; + } + + // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, + // so we have to use wcscpy because wcscpy_s writes non-NULLs + // into the rest of the buffer. + wchar_t double_terminated_path_from[MAX_PATH + 1] = {0}; + wchar_t double_terminated_path_to[MAX_PATH + 1] = {0}; +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation + wcscpy(double_terminated_path_from, from_path.value().c_str()); +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation + wcscpy(double_terminated_path_to, to_path.value().c_str()); + + SHFILEOPSTRUCT file_operation = {0}; + file_operation.wFunc = FO_COPY; + file_operation.pFrom = double_terminated_path_from; + file_operation.pTo = double_terminated_path_to; + file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | + FOF_NOCONFIRMMKDIR; + if (!recursive) + file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; + + return (SHFileOperation(&file_operation) == 0); +} + +bool PathExists(const FilePath& path) { + return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); +} + +bool PathIsWritable(const FilePath& path) { + HANDLE dir = + CreateFile(path.value().c_str(), FILE_ADD_FILE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + if (dir == INVALID_HANDLE_VALUE) + return false; + + CloseHandle(dir); + return true; +} + +bool DirectoryExists(const FilePath& path) { + DWORD fileattr = GetFileAttributes(path.value().c_str()); + if (fileattr != INVALID_FILE_ATTRIBUTES) + return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; + return false; +} + +bool GetTempDir(FilePath* path) { + wchar_t temp_path[MAX_PATH + 1]; + DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); + if (path_len >= MAX_PATH || path_len <= 0) + return false; + // TODO(evanm): the old behavior of this function was to always strip the + // trailing slash. We duplicate this here, but it shouldn't be necessary + // when everyone is using the appropriate FilePath APIs. + std::wstring path_str(temp_path); + TrimTrailingSeparator(&path_str); + *path = FilePath(path_str); + return true; +} + +bool GetShmemTempDir(FilePath* path) { + return GetTempDir(path); +} + +bool CreateTemporaryFileName(FilePath* path) { + std::wstring temp_path, temp_file; + + if (!GetTempDir(&temp_path)) + return false; + + if (CreateTemporaryFileNameInDir(temp_path, &temp_file)) { + *path = FilePath(temp_file); + return true; + } + + return false; +} + +FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { + return CreateAndOpenTemporaryFile(path); +} + +// On POSIX we have semantics to create and open a temporary file +// atomically. +// TODO(jrg): is there equivalent call to use on Windows instead of +// going 2-step? +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { + std::wstring wstring_path; + if (!CreateTemporaryFileNameInDir(dir.value(), &wstring_path)) { + return NULL; + } + *path = FilePath(wstring_path); + // Open file in binary mode, to avoid problems with fwrite. On Windows + // it replaces \n's with \r\n's, which may surprise you. + // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx + return OpenFile(*path, "wb+"); +} + +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file) { + wchar_t temp_name[MAX_PATH + 1]; + + if (!GetTempFileName(dir.c_str(), L"", 0, temp_name)) + return false; // fail! + + DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH); + if (path_len > MAX_PATH + 1 || path_len == 0) + return false; // fail! + + temp_file->assign(temp_name, path_len); + return true; +} + +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path) { + FilePath system_temp_dir; + if (!GetTempDir(&system_temp_dir)) + return false; + + FilePath path_to_create; + srand(static_cast<uint32_t>(time(NULL))); + + int count = 0; + while (count < 50) { + // Try create a new temporary directory with random generated name. If + // the one exists, keep trying another path name until we reach some limit. + path_to_create = system_temp_dir; + std::wstring new_dir_name; + new_dir_name.assign(prefix); + new_dir_name.append(IntToWString(rand() % kint16max)); + path_to_create = path_to_create.Append(new_dir_name); + + if (::CreateDirectory(path_to_create.value().c_str(), NULL)) + break; + count++; + } + + if (count == 50) { + return false; + } + + *new_temp_path = path_to_create; + return true; +} + +bool CreateDirectory(const FilePath& full_path) { + if (DirectoryExists(full_path)) + return true; + int err = SHCreateDirectoryEx(NULL, full_path.value().c_str(), NULL); + return err == ERROR_SUCCESS; +} + +bool GetFileInfo(const FilePath& file_path, FileInfo* results) { + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesEx(file_path.ToWStringHack().c_str(), + GetFileExInfoStandard, &attr)) { + return false; + } + + ULARGE_INTEGER size; + size.HighPart = attr.nFileSizeHigh; + size.LowPart = attr.nFileSizeLow; + results->size = size.QuadPart; + + results->is_directory = + (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + return true; +} + +FILE* OpenFile(const FilePath& filename, const char* mode) { + std::wstring w_mode = ASCIIToWide(std::string(mode)); + FILE* file; + if (_wfopen_s(&file, filename.value().c_str(), w_mode.c_str()) != 0) { + return NULL; + } + return file; +} + +FILE* OpenFile(const std::string& filename, const char* mode) { + FILE* file; + if (fopen_s(&file, filename.c_str(), mode) != 0) { + return NULL; + } + return file; +} + +int ReadFile(const FilePath& filename, char* data, int size) { + ScopedHandle file(CreateFile(filename.value().c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL)); + if (file == INVALID_HANDLE_VALUE) + return -1; + + int ret_value; + DWORD read; + if (::ReadFile(file, data, size, &read, NULL) && read == size) { + ret_value = static_cast<int>(read); + } else { + ret_value = -1; + } + + return ret_value; +} + +int WriteFile(const FilePath& filename, const char* data, int size) { + ScopedHandle file(CreateFile(filename.value().c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + 0, + NULL)); + if (file == INVALID_HANDLE_VALUE) { + CHROMIUM_LOG(WARNING) << "CreateFile failed for path " << filename.value() << + " error code=" << GetLastError() << + " error text=" << win_util::FormatLastWin32Error(); + return -1; + } + + DWORD written; + BOOL result = ::WriteFile(file, data, size, &written, NULL); + if (result && written == size) + return static_cast<int>(written); + + if (!result) { + // WriteFile failed. + CHROMIUM_LOG(WARNING) << "writing file " << filename.value() << + " failed, error code=" << GetLastError() << + " description=" << win_util::FormatLastWin32Error(); + } else { + // Didn't write all the bytes. + CHROMIUM_LOG(WARNING) << "wrote" << written << " bytes to " << + filename.value() << " expected " << size; + } + return -1; +} + +// Gets the current working directory for the process. +bool GetCurrentDirectory(FilePath* dir) { + wchar_t system_buffer[MAX_PATH]; + system_buffer[0] = 0; + DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer); + if (len == 0 || len > MAX_PATH) + return false; + // TODO(evanm): the old behavior of this function was to always strip the + // trailing slash. We duplicate this here, but it shouldn't be necessary + // when everyone is using the appropriate FilePath APIs. + std::wstring dir_str(system_buffer); + file_util::TrimTrailingSeparator(&dir_str); + *dir = FilePath(dir_str); + return true; +} + +// Sets the current working directory for the process. +bool SetCurrentDirectory(const FilePath& directory) { + BOOL ret = ::SetCurrentDirectory(directory.value().c_str()); + return ret != 0; +} + +// Deprecated functions ---------------------------------------------------- + +void InsertBeforeExtension(std::wstring* path_str, + const std::wstring& suffix) { + FilePath path(*path_str); + InsertBeforeExtension(&path, suffix); + path_str->assign(path.value()); +} +void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) { + FilePath path(*file_name); + ReplaceExtension(&path, extension); + file_name->assign(path.value()); +} +} // namespace file_util diff --git a/ipc/chromium/src/base/hash_tables.h b/ipc/chromium/src/base/hash_tables.h new file mode 100644 index 0000000000..956cadb3d4 --- /dev/null +++ b/ipc/chromium/src/base/hash_tables.h @@ -0,0 +1,136 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. +// + +// +// Deal with the differences between Microsoft and GNU implemenations +// of hash_map. Allows all platforms to use |base::hash_map| and +// |base::hash_set|. +// eg: +// base::hash_map<int> my_map; +// base::hash_set<int> my_set; +// + +#ifndef BASE_HASH_TABLES_H_ +#define BASE_HASH_TABLES_H_ + +#include "build/build_config.h" + +#include "base/string16.h" + +#if defined(COMPILER_MSVC) || (defined(ANDROID) && defined(_STLP_STD_NAME)) +#ifdef COMPILER_MSVC +#pragma push_macro("_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS") +#define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS +#endif + +// Suppress -Wshadow warnings from stlport headers. +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wshadow" +# if MOZ_GCC_VERSION_AT_LEAST(4, 9, 0) +# pragma GCC diagnostic ignored "-Wshadow-local" +# endif +#endif + +#include <hash_map> +#include <hash_set> + +#ifdef __GNUC__ +# if MOZ_GCC_VERSION_AT_LEAST(4, 9, 0) +# pragma GCC diagnostic pop // -Wshadow-local +# endif +# pragma GCC diagnostic pop // -Wshadow +#endif + +#ifdef COMPILER_MSVC +#pragma pop_macro("_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS") +#endif +namespace base { +#ifdef ANDROID +using _STLP_STD_NAME::hash_map; +using _STLP_STD_NAME::hash_set; +#else +using stdext::hash_map; +using stdext::hash_set; +#endif +} +#elif defined(COMPILER_GCC) +// This is a hack to disable the gcc 4.4 warning about hash_map and hash_set +// being deprecated. We can get rid of this when we upgrade to VS2008 and we +// can use <tr1/unordered_map> and <tr1/unordered_set>. +#ifdef __DEPRECATED +#define CHROME_OLD__DEPRECATED __DEPRECATED +#undef __DEPRECATED +#endif + +#include <ext/hash_map> +#include <ext/hash_set> +#include <string> + +#ifdef CHROME_OLD__DEPRECATED +#define __DEPRECATED CHROME_OLD__DEPRECATED +#undef CHROME_OLD__DEPRECATED +#endif + +namespace base { +using __gnu_cxx::hash_map; +using __gnu_cxx::hash_set; +} // namespace base + +namespace __gnu_cxx { + +// The GNU C++ library provides identiy hash functions for many integral types, +// but not for |long long|. This hash function will truncate if |size_t| is +// narrower than |long long|. This is probably good enough for what we will +// use it for. + +#define DEFINE_TRIVIAL_HASH(integral_type) \ + template<> \ + struct hash<integral_type> { \ + std::size_t operator()(integral_type value) const { \ + return static_cast<std::size_t>(value); \ + } \ + } + +DEFINE_TRIVIAL_HASH(long long); +DEFINE_TRIVIAL_HASH(unsigned long long); + +#undef DEFINE_TRIVIAL_HASH + +// Implement string hash functions so that strings of various flavors can +// be used as keys in STL maps and sets. The hash algorithm comes from the +// GNU C++ library, in <tr1/functional>. It is duplicated here because GCC +// versions prior to 4.3.2 are unable to compile <tr1/functional> when RTTI +// is disabled, as it is in our build. + +#define DEFINE_STRING_HASH(string_type) \ + template<> \ + struct hash<string_type> { \ + std::size_t operator()(const string_type& s) const { \ + std::size_t result = 0; \ + for (string_type::const_iterator i = s.begin(); i != s.end(); ++i) \ + result = (result * 131) + *i; \ + return result; \ + } \ + } + +DEFINE_STRING_HASH(std::string); +DEFINE_STRING_HASH(std::wstring); + +#if defined(WCHAR_T_IS_UTF32) +// If string16 and std::wstring are not the same type, provide a +// specialization for string16. +DEFINE_STRING_HASH(string16); +#endif // WCHAR_T_IS_UTF32 + +#undef DEFINE_STRING_HASH + +} // namespace __gnu_cxx + +#endif // COMPILER + +#endif // BASE_HASH_TABLES_H_ diff --git a/ipc/chromium/src/base/histogram.cc b/ipc/chromium/src/base/histogram.cc new file mode 100644 index 0000000000..49561bf608 --- /dev/null +++ b/ipc/chromium/src/base/histogram.cc @@ -0,0 +1,1176 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 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. + +// Histogram is an object that aggregates statistics, and can summarize them in +// various forms, including ASCII graphical, HTML, and numerically (as a +// vector of numbers corresponding to each of the aggregating buckets). +// See header file for details and examples. + +#include "base/histogram.h" + +#include <math.h> + +#include <algorithm> +#include <string> + +#include "base/logging.h" +#include "base/pickle.h" +#include "base/string_util.h" +#include "base/logging.h" + +namespace base { + +#define DVLOG(x) CHROMIUM_LOG(ERROR) +#define CHECK_GT DCHECK_GT +#define CHECK_LT DCHECK_LT +typedef ::Lock Lock; +typedef ::AutoLock AutoLock; + +// Static table of checksums for all possible 8 bit bytes. +const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL, +0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, +0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, +0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, +0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, +0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, +0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, +0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, +0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, +0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, +0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, +0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, +0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL, +0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, +0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, +0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, +0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, +0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, +0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, +0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, +0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, +0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, +0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L, +0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, +0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, +0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, +0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, +0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, +0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, +0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, +0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, +0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, +0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, +0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L, +0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, +0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, +0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, +0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, +0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, +0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, +0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, +0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, +0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, +0x2d02ef8dL, +}; + +typedef Histogram::Count Count; + +// static +const size_t Histogram::kBucketCount_MAX = 16384u; + +Histogram* Histogram::FactoryGet(const std::string& name, + Sample minimum, + Sample maximum, + size_t bucket_count, + Flags flags) { + Histogram* histogram(NULL); + + // Defensive code. + if (minimum < 1) + minimum = 1; + if (maximum > kSampleType_MAX - 1) + maximum = kSampleType_MAX - 1; + + if (!StatisticsRecorder::FindHistogram(name, &histogram)) { + // Extra variable is not needed... but this keeps this section basically + // identical to other derived classes in this file (and compiler will + // optimize away the extra variable. + Histogram* tentative_histogram = + new Histogram(name, minimum, maximum, bucket_count); + tentative_histogram->InitializeBucketRange(); + tentative_histogram->SetFlags(flags); + histogram = + StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); + } + + DCHECK_EQ(HISTOGRAM, histogram->histogram_type()); + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + return histogram; +} + +Histogram* Histogram::FactoryTimeGet(const std::string& name, + TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count, + Flags flags) { + return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), + bucket_count, flags); +} + +void Histogram::Add(int value) { + if (value > kSampleType_MAX - 1) + value = kSampleType_MAX - 1; + if (value < 0) + value = 0; + size_t index = BucketIndex(value); + DCHECK_GE(value, ranges(index)); + DCHECK_LT(value, ranges(index + 1)); + Accumulate(value, 1, index); +} + +void Histogram::Subtract(int value) { + if (value > kSampleType_MAX - 1) + value = kSampleType_MAX - 1; + if (value < 0) + value = 0; + size_t index = BucketIndex(value); + DCHECK_GE(value, ranges(index)); + DCHECK_LT(value, ranges(index + 1)); + Accumulate(value, -1, index); +} + +void Histogram::AddBoolean(bool value) { + DCHECK(false); +} + +void Histogram::AddSampleSet(const SampleSet& sample) { + sample_.Add(sample); +} + +void Histogram::Clear() { + SampleSet ss; + ss.Resize(*this); + sample_ = ss; +} + +void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) { + DCHECK(false); +} + +// The following methods provide a graphical histogram display. +void Histogram::WriteHTMLGraph(std::string* output) const { + // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc. + output->append("<PRE>"); + WriteAscii(true, "<br>", output); + output->append("</PRE>"); +} + +void Histogram::WriteAscii(bool graph_it, const std::string& newline, + std::string* output) const { + // Get local (stack) copies of all effectively volatile class data so that we + // are consistent across our output activities. + SampleSet snapshot; + SnapshotSample(&snapshot); + + Count sample_count = snapshot.TotalCount(); + + WriteAsciiHeader(snapshot, sample_count, output); + output->append(newline); + + // Prepare to normalize graphical rendering of bucket contents. + double max_size = 0; + if (graph_it) + max_size = GetPeakBucketSize(snapshot); + + // Calculate space needed to print bucket range numbers. Leave room to print + // nearly the largest bucket range without sliding over the histogram. + size_t largest_non_empty_bucket = bucket_count() - 1; + while (0 == snapshot.counts(largest_non_empty_bucket)) { + if (0 == largest_non_empty_bucket) + break; // All buckets are empty. + --largest_non_empty_bucket; + } + + // Calculate largest print width needed for any of our bucket range displays. + size_t print_width = 1; + for (size_t i = 0; i < bucket_count(); ++i) { + if (snapshot.counts(i)) { + size_t width = GetAsciiBucketRange(i).size() + 1; + if (width > print_width) + print_width = width; + } + } + + int64_t remaining = sample_count; + int64_t past = 0; + // Output the actual histogram graph. + for (size_t i = 0; i < bucket_count(); ++i) { + Count current = snapshot.counts(i); + if (!current && !PrintEmptyBucket(i)) + continue; + remaining -= current; + std::string range = GetAsciiBucketRange(i); + output->append(range); + for (size_t j = 0; range.size() + j < print_width + 1; ++j) + output->push_back(' '); + if (0 == current && + i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) { + while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) + ++i; + output->append("... "); + output->append(newline); + continue; // No reason to plot emptiness. + } + double current_size = GetBucketSize(current, i); + if (graph_it) + WriteAsciiBucketGraph(current_size, max_size, output); + WriteAsciiBucketContext(past, current, remaining, i, output); + output->append(newline); + past += current; + } + DCHECK_EQ(sample_count, past); +} + +//------------------------------------------------------------------------------ +// Methods for the validating a sample and a related histogram. +//------------------------------------------------------------------------------ + +Histogram::Inconsistencies +Histogram::FindCorruption(const SampleSet& snapshot) const +{ + int inconsistencies = NO_INCONSISTENCIES; + Sample previous_range = -1; // Bottom range is always 0. + int64_t count = 0; + for (size_t index = 0; index < bucket_count(); ++index) { + count += snapshot.counts(index); + int new_range = ranges(index); + if (previous_range >= new_range) + inconsistencies |= BUCKET_ORDER_ERROR; + previous_range = new_range; + } + + if (!HasValidRangeChecksum()) + inconsistencies |= RANGE_CHECKSUM_ERROR; + + int64_t delta64 = snapshot.redundant_count() - count; + if (delta64 != 0) { + int delta = static_cast<int>(delta64); + if (delta != delta64) + delta = INT_MAX; // Flag all giant errors as INT_MAX. + // Since snapshots of histograms are taken asynchronously relative to + // sampling (and snapped from different threads), it is pretty likely that + // we'll catch a redundant count that doesn't match the sample count. We + // allow for a certain amount of slop before flagging this as an + // inconsistency. Even with an inconsistency, we'll snapshot it again (for + // UMA in about a half hour, so we'll eventually get the data, if it was + // not the result of a corruption. If histograms show that 1 is "too tight" + // then we may try to use 2 or 3 for this slop value. + const int kCommonRaceBasedCountMismatch = 1; + if (delta > 0) { + UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta); + if (delta > kCommonRaceBasedCountMismatch) + inconsistencies |= COUNT_HIGH_ERROR; + } else { + DCHECK_GT(0, delta); + UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta); + if (-delta > kCommonRaceBasedCountMismatch) + inconsistencies |= COUNT_LOW_ERROR; + } + } + return static_cast<Inconsistencies>(inconsistencies); +} + +Histogram::ClassType Histogram::histogram_type() const { + return HISTOGRAM; +} + +Histogram::Sample Histogram::ranges(size_t i) const { + return ranges_[i]; +} + +size_t Histogram::bucket_count() const { + return bucket_count_; +} + +void Histogram::SnapshotSample(SampleSet* sample) const { + *sample = sample_; +} + +bool Histogram::HasConstructorArguments(Sample minimum, + Sample maximum, + size_t bucket_count) { + return ((minimum == declared_min_) && (maximum == declared_max_) && + (bucket_count == bucket_count_)); +} + +bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count) { + return ((minimum.InMilliseconds() == declared_min_) && + (maximum.InMilliseconds() == declared_max_) && + (bucket_count == bucket_count_)); +} + +bool Histogram::HasValidRangeChecksum() const { + return CalculateRangeChecksum() == range_checksum_; +} + +size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) +{ + size_t n = 0; + n += aMallocSizeOf(this); + // We're not allowed to do deep dives into STL data structures. This + // is as close as we can get to measuring this array. + n += aMallocSizeOf(&ranges_[0]); + n += sample_.SizeOfExcludingThis(aMallocSizeOf); + return n; +} + +size_t +Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) +{ + // We're not allowed to do deep dives into STL data structures. This + // is as close as we can get to measuring this array. + return aMallocSizeOf(&counts_[0]); +} + +Histogram::Histogram(const std::string& name, Sample minimum, + Sample maximum, size_t bucket_count) + : sample_(), + histogram_name_(name), + declared_min_(minimum), + declared_max_(maximum), + bucket_count_(bucket_count), + flags_(kNoFlags), + ranges_(bucket_count + 1, 0), + range_checksum_(0), + recording_enabled_(true) { + Initialize(); +} + +Histogram::Histogram(const std::string& name, TimeDelta minimum, + TimeDelta maximum, size_t bucket_count) + : sample_(), + histogram_name_(name), + declared_min_(static_cast<int> (minimum.InMilliseconds())), + declared_max_(static_cast<int> (maximum.InMilliseconds())), + bucket_count_(bucket_count), + flags_(kNoFlags), + ranges_(bucket_count + 1, 0), + range_checksum_(0), + recording_enabled_(true) { + Initialize(); +} + +Histogram::~Histogram() { + if (StatisticsRecorder::dump_on_exit()) { + std::string output; + WriteAscii(true, "\n", &output); + CHROMIUM_LOG(INFO) << output; + } + + // Just to make sure most derived class did this properly... + DCHECK(ValidateBucketRanges()); +} + +// Calculate what range of values are held in each bucket. +// We have to be careful that we don't pick a ratio between starting points in +// consecutive buckets that is sooo small, that the integer bounds are the same +// (effectively making one bucket get no values). We need to avoid: +// ranges_[i] == ranges_[i + 1] +// To avoid that, we just do a fine-grained bucket width as far as we need to +// until we get a ratio that moves us along at least 2 units at a time. From +// that bucket onward we do use the exponential growth of buckets. +void Histogram::InitializeBucketRange() { + double log_max = log(static_cast<double>(declared_max())); + double log_ratio; + double log_next; + size_t bucket_index = 1; + Sample current = declared_min(); + SetBucketRange(bucket_index, current); + while (bucket_count() > ++bucket_index) { + double log_current; + log_current = log(static_cast<double>(current)); + // Calculate the count'th root of the range. + log_ratio = (log_max - log_current) / (bucket_count() - bucket_index); + // See where the next bucket would start. + log_next = log_current + log_ratio; + int next; + next = static_cast<int>(floor(exp(log_next) + 0.5)); + if (next > current) + current = next; + else + ++current; // Just do a narrow bucket, and keep trying. + SetBucketRange(bucket_index, current); + } + ResetRangeChecksum(); + + DCHECK_EQ(bucket_count(), bucket_index); +} + +bool Histogram::PrintEmptyBucket(size_t index) const { + return true; +} + +size_t Histogram::BucketIndex(Sample value) const { + // Use simple binary search. This is very general, but there are better + // approaches if we knew that the buckets were linearly distributed. + DCHECK_LE(ranges(0), value); + DCHECK_GT(ranges(bucket_count()), value); + size_t under = 0; + size_t over = bucket_count(); + size_t mid; + + do { + DCHECK_GE(over, under); + mid = under + (over - under)/2; + if (mid == under) + break; + if (ranges(mid) <= value) + under = mid; + else + over = mid; + } while (true); + + DCHECK_LE(ranges(mid), value); + CHECK_GT(ranges(mid+1), value); + return mid; +} + +// Use the actual bucket widths (like a linear histogram) until the widths get +// over some transition value, and then use that transition width. Exponentials +// get so big so fast (and we don't expect to see a lot of entries in the large +// buckets), so we need this to make it possible to see what is going on and +// not have 0-graphical-height buckets. +double Histogram::GetBucketSize(Count current, size_t i) const { + DCHECK_GT(ranges(i + 1), ranges(i)); + static const double kTransitionWidth = 5; + double denominator = ranges(i + 1) - ranges(i); + if (denominator > kTransitionWidth) + denominator = kTransitionWidth; // Stop trying to normalize. + return current/denominator; +} + +void Histogram::ResetRangeChecksum() { + range_checksum_ = CalculateRangeChecksum(); +} + +const std::string Histogram::GetAsciiBucketRange(size_t i) const { + std::string result; + if (kHexRangePrintingFlag & flags_) + StringAppendF(&result, "%#x", ranges(i)); + else + StringAppendF(&result, "%d", ranges(i)); + return result; +} + +// Update histogram data with new sample. +void Histogram::Accumulate(Sample value, Count count, size_t index) { + sample_.Accumulate(value, count, index); +} + +void Histogram::SetBucketRange(size_t i, Sample value) { + DCHECK_GT(bucket_count_, i); + ranges_[i] = value; +} + +bool Histogram::ValidateBucketRanges() const { + // Standard assertions that all bucket ranges should satisfy. + DCHECK_EQ(bucket_count_ + 1, ranges_.size()); + DCHECK_EQ(0, ranges_[0]); + DCHECK_EQ(declared_min(), ranges_[1]); + DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]); + DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]); + return true; +} + +uint32_t Histogram::CalculateRangeChecksum() const { + DCHECK_EQ(ranges_.size(), bucket_count() + 1); + uint32_t checksum = static_cast<uint32_t>(ranges_.size()); // Seed checksum. + for (size_t index = 0; index < bucket_count(); ++index) + checksum = Crc32(checksum, ranges(index)); + return checksum; +} + +void Histogram::Initialize() { + sample_.Resize(*this); + if (declared_min_ < 1) + declared_min_ = 1; + if (declared_max_ > kSampleType_MAX - 1) + declared_max_ = kSampleType_MAX - 1; + DCHECK_LE(declared_min_, declared_max_); + DCHECK_GT(bucket_count_, 1u); + CHECK_LT(bucket_count_, kBucketCount_MAX); + size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; + DCHECK_LE(bucket_count_, maximal_bucket_count); + DCHECK_EQ(0, ranges_[0]); + ranges_[bucket_count_] = kSampleType_MAX; +} + +// We generate the CRC-32 using the low order bits to select whether to XOR in +// the reversed polynomial 0xedb88320L. This is nice and simple, and allows us +// to keep the quotient in a uint32_t. Since we're not concerned about the nature +// of corruptions (i.e., we don't care about bit sequencing, since we are +// handling memory changes, which are more grotesque) so we don't bother to +// get the CRC correct for big-endian vs little-ending calculations. All we +// need is a nice hash, that tends to depend on all the bits of the sample, with +// very little chance of changes in one place impacting changes in another +// place. +uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) { + const bool kUseRealCrc = true; // TODO(jar): Switch to false and watch stats. + if (kUseRealCrc) { + union { + Histogram::Sample range; + unsigned char bytes[sizeof(Histogram::Sample)]; + } converter; + converter.range = range; + for (size_t i = 0; i < sizeof(converter); ++i) + sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8); + } else { + // Use hash techniques provided in ReallyFastHash, except we don't care + // about "avalanching" (which would worsten the hash, and add collisions), + // and we don't care about edge cases since we have an even number of bytes. + union { + Histogram::Sample range; + uint16_t ints[sizeof(Histogram::Sample) / 2]; + } converter; + DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter)); + converter.range = range; + sum += converter.ints[0]; + sum = (sum << 16) ^ sum ^ (static_cast<uint32_t>(converter.ints[1]) << 11); + sum += sum >> 11; + } + return sum; +} + +//------------------------------------------------------------------------------ +// Private methods + +double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { + double max = 0; + for (size_t i = 0; i < bucket_count() ; ++i) { + double current_size + = GetBucketSize(snapshot.counts(i), i); + if (current_size > max) + max = current_size; + } + return max; +} + +void Histogram::WriteAsciiHeader(const SampleSet& snapshot, + Count sample_count, + std::string* output) const { + StringAppendF(output, + "Histogram: %s recorded %d samples", + histogram_name().c_str(), + sample_count); + int64_t snapshot_sum = snapshot.sum(); + if (0 == sample_count) { + DCHECK_EQ(snapshot_sum, 0); + } else { + double average = static_cast<float>(snapshot_sum) / sample_count; + + StringAppendF(output, ", average = %.1f", average); + } + if (flags_ & ~kHexRangePrintingFlag) + StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag); +} + +void Histogram::WriteAsciiBucketContext(const int64_t past, + const Count current, + const int64_t remaining, + const size_t i, + std::string* output) const { + double scaled_sum = (past + current + remaining) / 100.0; + WriteAsciiBucketValue(current, scaled_sum, output); + if (0 < i) { + double percentage = past / scaled_sum; + StringAppendF(output, " {%3.1f%%}", percentage); + } +} + +void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, + std::string* output) const { + StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); +} + +void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, + std::string* output) const { + const int k_line_length = 72; // Maximal horizontal width of graph. + int x_count = static_cast<int>(k_line_length * (current_size / max_size) + + 0.5); + int x_remainder = k_line_length - x_count; + + while (0 < x_count--) + output->append("-"); + output->append("O"); + while (0 < x_remainder--) + output->append(" "); +} + +//------------------------------------------------------------------------------ +// Methods for the Histogram::SampleSet class +//------------------------------------------------------------------------------ + +Histogram::SampleSet::SampleSet() + : counts_(), + sum_(0), + redundant_count_(0) { +} + +Histogram::SampleSet::~SampleSet() { +} + +void Histogram::SampleSet::Resize(const Histogram& histogram) { + counts_.resize(histogram.bucket_count(), 0); +} + +void Histogram::SampleSet::Accumulate(Sample value, Count count, + size_t index) { + DCHECK(count == 1 || count == -1); + counts_[index] += count; + redundant_count_ += count; + sum_ += static_cast<int64_t>(count) * value; + DCHECK_GE(counts_[index], 0); + DCHECK_GE(sum_, 0); + DCHECK_GE(redundant_count_, 0); +} + +Count Histogram::SampleSet::TotalCount() const { + Count total = 0; + for (Counts::const_iterator it = counts_.begin(); + it != counts_.end(); + ++it) { + total += *it; + } + return total; +} + +void Histogram::SampleSet::Add(const SampleSet& other) { + DCHECK_EQ(counts_.size(), other.counts_.size()); + sum_ += other.sum_; + redundant_count_ += other.redundant_count_; + for (size_t index = 0; index < counts_.size(); ++index) + counts_[index] += other.counts_[index]; +} + +//------------------------------------------------------------------------------ +// LinearHistogram: This histogram uses a traditional set of evenly spaced +// buckets. +//------------------------------------------------------------------------------ + +LinearHistogram::~LinearHistogram() { +} + +Histogram* LinearHistogram::FactoryGet(const std::string& name, + Sample minimum, + Sample maximum, + size_t bucket_count, + Flags flags) { + Histogram* histogram(NULL); + + if (minimum < 1) + minimum = 1; + if (maximum > kSampleType_MAX - 1) + maximum = kSampleType_MAX - 1; + + if (!StatisticsRecorder::FindHistogram(name, &histogram)) { + LinearHistogram* tentative_histogram = + new LinearHistogram(name, minimum, maximum, bucket_count); + tentative_histogram->InitializeBucketRange(); + tentative_histogram->SetFlags(flags); + histogram = + StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); + } + + DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type()); + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + return histogram; +} + +Histogram* LinearHistogram::FactoryTimeGet(const std::string& name, + TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count, + Flags flags) { + return FactoryGet(name, minimum.InMilliseconds(), maximum.InMilliseconds(), + bucket_count, flags); +} + +Histogram::ClassType LinearHistogram::histogram_type() const { + return LINEAR_HISTOGRAM; +} + +void LinearHistogram::Accumulate(Sample value, Count count, size_t index) { + sample_.Accumulate(value, count, index); +} + +void LinearHistogram::SetRangeDescriptions( + const DescriptionPair descriptions[]) { + for (int i =0; descriptions[i].description; ++i) { + bucket_description_[descriptions[i].sample] = descriptions[i].description; + } +} + +LinearHistogram::LinearHistogram(const std::string& name, + Sample minimum, + Sample maximum, + size_t bucket_count) + : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { +} + +LinearHistogram::LinearHistogram(const std::string& name, + TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count) + : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? + minimum : TimeDelta::FromMilliseconds(1), + maximum, bucket_count) { +} + +void LinearHistogram::InitializeBucketRange() { + DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. + double min = declared_min(); + double max = declared_max(); + size_t i; + for (i = 1; i < bucket_count(); ++i) { + double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / + (bucket_count() - 2); + SetBucketRange(i, static_cast<int> (linear_range + 0.5)); + } + ResetRangeChecksum(); +} + +double LinearHistogram::GetBucketSize(Count current, size_t i) const { + DCHECK_GT(ranges(i + 1), ranges(i)); + // Adjacent buckets with different widths would have "surprisingly" many (few) + // samples in a histogram if we didn't normalize this way. + double denominator = ranges(i + 1) - ranges(i); + return current/denominator; +} + +const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { + int range = ranges(i); + BucketDescriptionMap::const_iterator it = bucket_description_.find(range); + if (it == bucket_description_.end()) + return Histogram::GetAsciiBucketRange(i); + return it->second; +} + +bool LinearHistogram::PrintEmptyBucket(size_t index) const { + return bucket_description_.find(ranges(index)) == bucket_description_.end(); +} + + +//------------------------------------------------------------------------------ +// This section provides implementation for BooleanHistogram. +//------------------------------------------------------------------------------ + +Histogram* BooleanHistogram::FactoryGet(const std::string& name, Flags flags) { + Histogram* histogram(NULL); + + if (!StatisticsRecorder::FindHistogram(name, &histogram)) { + BooleanHistogram* tentative_histogram = new BooleanHistogram(name); + tentative_histogram->InitializeBucketRange(); + tentative_histogram->SetFlags(flags); + histogram = + StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); + } + + DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type()); + return histogram; +} + +Histogram::ClassType BooleanHistogram::histogram_type() const { + return BOOLEAN_HISTOGRAM; +} + +void BooleanHistogram::AddBoolean(bool value) { + Add(value ? 1 : 0); +} + +BooleanHistogram::BooleanHistogram(const std::string& name) + : LinearHistogram(name, 1, 2, 3) { +} + +void +BooleanHistogram::Accumulate(Sample value, Count count, size_t index) +{ + // Callers will have computed index based on the non-booleanified value. + // So we need to adjust the index manually. + LinearHistogram::Accumulate(!!value, count, value ? 1 : 0); +} + +//------------------------------------------------------------------------------ +// FlagHistogram: +//------------------------------------------------------------------------------ + +Histogram * +FlagHistogram::FactoryGet(const std::string &name, Flags flags) +{ + Histogram *h(nullptr); + + if (!StatisticsRecorder::FindHistogram(name, &h)) { + FlagHistogram *fh = new FlagHistogram(name); + fh->InitializeBucketRange(); + fh->SetFlags(flags); + size_t zero_index = fh->BucketIndex(0); + fh->LinearHistogram::Accumulate(0, 1, zero_index); + h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh); + } + + return h; +} + +FlagHistogram::FlagHistogram(const std::string &name) + : BooleanHistogram(name), mSwitched(false) { +} + +Histogram::ClassType +FlagHistogram::histogram_type() const +{ + return FLAG_HISTOGRAM; +} + +void +FlagHistogram::Accumulate(Sample value, Count count, size_t index) +{ + if (mSwitched) { + return; + } + + mSwitched = true; + DCHECK_EQ(value, 1); + LinearHistogram::Accumulate(value, 1, index); + size_t zero_index = BucketIndex(0); + LinearHistogram::Accumulate(0, -1, zero_index); +} + +void +FlagHistogram::AddSampleSet(const SampleSet& sample) { + DCHECK_EQ(bucket_count(), sample.size()); + // We can't be sure the SampleSet provided came from another FlagHistogram, + // so we take the following steps: + // - If our flag has already been set do nothing. + // - Set our flag if the following hold: + // - The sum of the counts in the provided SampleSet is 1. + // - The bucket index for that single value is the same as the index where we + // would place our set flag. + // - Otherwise, take no action. + + if (mSwitched) { + return; + } + + if (sample.sum() != 1) { + return; + } + + size_t one_index = BucketIndex(1); + if (sample.counts(one_index) == 1) { + Accumulate(1, 1, one_index); + } +} + +void +FlagHistogram::Clear() { + Histogram::Clear(); + + mSwitched = false; + size_t zero_index = BucketIndex(0); + LinearHistogram::Accumulate(0, 1, zero_index); +} + +//------------------------------------------------------------------------------ +// CountHistogram: +//------------------------------------------------------------------------------ + +Histogram * +CountHistogram::FactoryGet(const std::string &name, Flags flags) +{ + Histogram *h(nullptr); + + if (!StatisticsRecorder::FindHistogram(name, &h)) { + CountHistogram *fh = new CountHistogram(name); + fh->InitializeBucketRange(); + fh->SetFlags(flags); + h = StatisticsRecorder::RegisterOrDeleteDuplicate(fh); + } + + return h; +} + +CountHistogram::CountHistogram(const std::string &name) + : LinearHistogram(name, 1, 2, 3) { +} + +Histogram::ClassType +CountHistogram::histogram_type() const +{ + return COUNT_HISTOGRAM; +} + +void +CountHistogram::Accumulate(Sample value, Count count, size_t index) +{ + size_t zero_index = BucketIndex(0); + LinearHistogram::Accumulate(value, 1, zero_index); +} + +void +CountHistogram::AddSampleSet(const SampleSet& sample) { + DCHECK_EQ(bucket_count(), sample.size()); + // We can't be sure the SampleSet provided came from another CountHistogram, + // so we at least check that the unused buckets are empty. + + const size_t indices[] = { BucketIndex(0), BucketIndex(1), BucketIndex(2) }; + + if (sample.counts(indices[1]) != 0 || sample.counts(indices[2]) != 0) { + return; + } + + if (sample.counts(indices[0]) != 0) { + Accumulate(1, sample.counts(indices[0]), indices[0]); + } +} + + +//------------------------------------------------------------------------------ +// CustomHistogram: +//------------------------------------------------------------------------------ + +Histogram* CustomHistogram::FactoryGet(const std::string& name, + const std::vector<Sample>& custom_ranges, + Flags flags) { + Histogram* histogram(NULL); + + // Remove the duplicates in the custom ranges array. + std::vector<int> ranges = custom_ranges; + ranges.push_back(0); // Ensure we have a zero value. + std::sort(ranges.begin(), ranges.end()); + ranges.erase(std::unique(ranges.begin(), ranges.end()), ranges.end()); + if (ranges.size() <= 1) { + DCHECK(false); + // Note that we pushed a 0 in above, so for defensive code.... + ranges.push_back(1); // Put in some data so we can index to [1]. + } + + DCHECK_LT(ranges.back(), kSampleType_MAX); + + if (!StatisticsRecorder::FindHistogram(name, &histogram)) { + CustomHistogram* tentative_histogram = new CustomHistogram(name, ranges); + tentative_histogram->InitializedCustomBucketRange(ranges); + tentative_histogram->SetFlags(flags); + histogram = + StatisticsRecorder::RegisterOrDeleteDuplicate(tentative_histogram); + } + + DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM); + DCHECK(histogram->HasConstructorArguments(ranges[1], ranges.back(), + ranges.size())); + return histogram; +} + +Histogram::ClassType CustomHistogram::histogram_type() const { + return CUSTOM_HISTOGRAM; +} + +CustomHistogram::CustomHistogram(const std::string& name, + const std::vector<Sample>& custom_ranges) + : Histogram(name, custom_ranges[1], custom_ranges.back(), + custom_ranges.size()) { + DCHECK_GT(custom_ranges.size(), 1u); + DCHECK_EQ(custom_ranges[0], 0); +} + +void CustomHistogram::InitializedCustomBucketRange( + const std::vector<Sample>& custom_ranges) { + DCHECK_GT(custom_ranges.size(), 1u); + DCHECK_EQ(custom_ranges[0], 0); + DCHECK_LE(custom_ranges.size(), bucket_count()); + for (size_t index = 0; index < custom_ranges.size(); ++index) + SetBucketRange(index, custom_ranges[index]); + ResetRangeChecksum(); +} + +double CustomHistogram::GetBucketSize(Count current, size_t i) const { + return 1; +} + +//------------------------------------------------------------------------------ +// The next section handles global (central) support for all histograms, as well +// as startup/teardown of this service. +//------------------------------------------------------------------------------ + +// This singleton instance should be started during the single threaded portion +// of main(), and hence it is not thread safe. It initializes globals to +// provide support for all future calls. +StatisticsRecorder::StatisticsRecorder() { + DCHECK(!histograms_); + if (lock_ == NULL) { + // This will leak on purpose. It's the only way to make sure we won't race + // against the static uninitialization of the module while one of our + // static methods relying on the lock get called at an inappropriate time + // during the termination phase. Since it's a static data member, we will + // leak one per process, which would be similar to the instance allocated + // during static initialization and released only on process termination. + lock_ = new base::Lock; + } + base::AutoLock auto_lock(*lock_); + histograms_ = new HistogramMap; +} + +StatisticsRecorder::~StatisticsRecorder() { + DCHECK(histograms_ && lock_); + + if (dump_on_exit_) { + std::string output; + WriteGraph("", &output); + CHROMIUM_LOG(INFO) << output; + } + // Clean up. + HistogramMap* histograms = NULL; + { + base::AutoLock auto_lock(*lock_); + histograms = histograms_; + histograms_ = NULL; + for (HistogramMap::iterator it = histograms->begin(); + histograms->end() != it; + ++it) { + // No other clients permanently hold Histogram references, so we + // have the only one and it is safe to delete it. + delete it->second; + } + } + delete histograms; + // We don't delete lock_ on purpose to avoid having to properly protect + // against it going away after we checked for NULL in the static methods. +} + +// static +bool StatisticsRecorder::IsActive() { + if (lock_ == NULL) + return false; + base::AutoLock auto_lock(*lock_); + return NULL != histograms_; +} + +Histogram* StatisticsRecorder::RegisterOrDeleteDuplicate(Histogram* histogram) { + DCHECK(histogram->HasValidRangeChecksum()); + if (lock_ == NULL) + return histogram; + base::AutoLock auto_lock(*lock_); + if (!histograms_) + return histogram; + const std::string name = histogram->histogram_name(); + HistogramMap::iterator it = histograms_->find(name); + // Avoid overwriting a previous registration. + if (histograms_->end() == it) { + (*histograms_)[name] = histogram; + } else { + delete histogram; // We already have one by this name. + histogram = it->second; + } + return histogram; +} + +// static +void StatisticsRecorder::WriteHTMLGraph(const std::string& query, + std::string* output) { + if (!IsActive()) + return; + output->append("<html><head><title>About Histograms"); + if (!query.empty()) + output->append(" - " + query); + output->append("</title>" + // We'd like the following no-cache... but it doesn't work. + // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">" + "</head><body>"); + + Histograms snapshot; + GetSnapshot(query, &snapshot); + for (Histograms::iterator it = snapshot.begin(); + it != snapshot.end(); + ++it) { + (*it)->WriteHTMLGraph(output); + output->append("<br><hr><br>"); + } + output->append("</body></html>"); +} + +// static +void StatisticsRecorder::WriteGraph(const std::string& query, + std::string* output) { + if (!IsActive()) + return; + if (query.length()) + StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); + else + output->append("Collections of all histograms\n"); + + Histograms snapshot; + GetSnapshot(query, &snapshot); + for (Histograms::iterator it = snapshot.begin(); + it != snapshot.end(); + ++it) { + (*it)->WriteAscii(true, "\n", output); + output->append("\n"); + } +} + +// static +void StatisticsRecorder::GetHistograms(Histograms* output) { + if (lock_ == NULL) + return; + base::AutoLock auto_lock(*lock_); + if (!histograms_) + return; + for (HistogramMap::iterator it = histograms_->begin(); + histograms_->end() != it; + ++it) { + DCHECK_EQ(it->first, it->second->histogram_name()); + output->push_back(it->second); + } +} + +bool StatisticsRecorder::FindHistogram(const std::string& name, + Histogram** histogram) { + if (lock_ == NULL) + return false; + base::AutoLock auto_lock(*lock_); + if (!histograms_) + return false; + HistogramMap::iterator it = histograms_->find(name); + if (histograms_->end() == it) + return false; + *histogram = it->second; + return true; +} + +// private static +void StatisticsRecorder::GetSnapshot(const std::string& query, + Histograms* snapshot) { + if (lock_ == NULL) + return; + base::AutoLock auto_lock(*lock_); + if (!histograms_) + return; + for (HistogramMap::iterator it = histograms_->begin(); + histograms_->end() != it; + ++it) { + if (it->first.find(query) != std::string::npos) + snapshot->push_back(it->second); + } +} + +// static +StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; +// static +base::Lock* StatisticsRecorder::lock_ = NULL; +// static +bool StatisticsRecorder::dump_on_exit_ = false; + +} // namespace base diff --git a/ipc/chromium/src/base/histogram.h b/ipc/chromium/src/base/histogram.h new file mode 100644 index 0000000000..20868e9dc1 --- /dev/null +++ b/ipc/chromium/src/base/histogram.h @@ -0,0 +1,780 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 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. + +// Histogram is an object that aggregates statistics, and can summarize them in +// various forms, including ASCII graphical, HTML, and numerically (as a +// vector of numbers corresponding to each of the aggregating buckets). + +// It supports calls to accumulate either time intervals (which are processed +// as integral number of milliseconds), or arbitrary integral units. + +// The default layout of buckets is exponential. For example, buckets might +// contain (sequentially) the count of values in the following intervals: +// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity) +// That bucket allocation would actually result from construction of a histogram +// for values between 1 and 64, with 8 buckets, such as: +// Histogram count(L"some name", 1, 64, 8); +// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity) +// are not counted by the constructor in the user supplied "bucket_count" +// argument. +// The above example has an exponential ratio of 2 (doubling the bucket width +// in each consecutive bucket. The Histogram class automatically calculates +// the smallest ratio that it can use to construct the number of buckets +// selected in the constructor. An another example, if you had 50 buckets, +// and millisecond time values from 1 to 10000, then the ratio between +// consecutive bucket widths will be approximately somewhere around the 50th +// root of 10000. This approach provides very fine grain (narrow) buckets +// at the low end of the histogram scale, but allows the histogram to cover a +// gigantic range with the addition of very few buckets. + +// Histograms use a pattern involving a function static variable, that is a +// pointer to a histogram. This static is explicitly initialized on any thread +// that detects a uninitialized (NULL) pointer. The potentially racy +// initialization is not a problem as it is always set to point to the same +// value (i.e., the FactoryGet always returns the same value). FactoryGet +// is also completely thread safe, which results in a completely thread safe, +// and relatively fast, set of counters. To avoid races at shutdown, the static +// pointer is NOT deleted, and we leak the histograms at process termination. + +#ifndef BASE_METRICS_HISTOGRAM_H_ +#define BASE_METRICS_HISTOGRAM_H_ +#pragma once + +#include "mozilla/Atomics.h" +#include "mozilla/MemoryReporting.h" + +#include <map> +#include <string> +#include <vector> + +#include "base/time.h" +#include "base/lock.h" + +namespace base { + +//------------------------------------------------------------------------------ +// Provide easy general purpose histogram in a macro, just like stats counters. +// The first four macros use 50 buckets. + +#define HISTOGRAM_TIMES(name, sample) HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50) + +#define HISTOGRAM_COUNTS(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000000, 50) + +#define HISTOGRAM_COUNTS_100(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 100, 50) + +#define HISTOGRAM_COUNTS_10000(name, sample) HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 10000, 50) + +#define HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::Histogram::FactoryGet(name, min, max, bucket_count, \ + base::Histogram::kNoFlags); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->Add(sample); \ + } while (0) + +#define HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ + HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) + +// For folks that need real specific times, use this to select a precise range +// of times you want plotted, and the number of buckets you want used. +#define HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ + base::Histogram::kNoFlags); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->AddTime(sample); \ + } while (0) + +// DO NOT USE THIS. It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES. +#define HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ + base::Histogram::kNoFlags); \ + DCHECK_EQ(name, counter->histogram_name()); \ + if ((sample) < (max)) counter->AddTime(sample); \ + } while (0) + +// Support histograming of an enumerated value. The samples should always be +// less than boundary_value. + +#define HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ + boundary_value + 1, base::Histogram::kNoFlags); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->Add(sample); \ + } while (0) + +#define HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::CustomHistogram::FactoryGet(name, custom_ranges, \ + base::Histogram::kNoFlags); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->Add(sample); \ + } while (0) + + +//------------------------------------------------------------------------------ +// Define Debug vs non-debug flavors of macros. +#ifndef NDEBUG + +#define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample) +#define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample) +#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) HISTOGRAM_PERCENTAGE(\ + name, under_one_hundred) +#define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ + HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) +#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \ + HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) +#define DHISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ + HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) +#define DHISTOGRAM_ENUMERATION(name, sample, boundary_value) \ + HISTOGRAM_ENUMERATION(name, sample, boundary_value) +#define DHISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ + HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) + +#else // NDEBUG + +#define DHISTOGRAM_TIMES(name, sample) do {} while (0) +#define DHISTOGRAM_COUNTS(name, sample) do {} while (0) +#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) do {} while (0) +#define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ + do {} while (0) +#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \ + do {} while (0) +#define DHISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ + do {} while (0) +#define DHISTOGRAM_ENUMERATION(name, sample, boundary_value) do {} while (0) +#define DHISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ + do {} while (0) + +#endif // NDEBUG + +//------------------------------------------------------------------------------ +// The following macros provide typical usage scenarios for callers that wish +// to record histogram data, and have the data submitted/uploaded via UMA. +// Not all systems support such UMA, but if they do, the following macros +// should work with the service. + +#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50) + +#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(10), \ + base::TimeDelta::FromMinutes(3), 50) + +// Use this macro when times can routinely be much longer than 10 seconds. +#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \ + name, sample, base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromHours(1), 50) + +#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ + base::Histogram::kUmaTargetedHistogramFlag); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->AddTime(sample); \ + } while (0) + +// DO NOT USE THIS. It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES. +#define UMA_HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \ + base::Histogram::kUmaTargetedHistogramFlag); \ + DCHECK_EQ(name, counter->histogram_name()); \ + if ((sample) < (max)) counter->AddTime(sample); \ + } while (0) + +#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000000, 50) + +#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 100, 50) + +#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 10000, 50) + +#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::Histogram::FactoryGet(name, min, max, bucket_count, \ + base::Histogram::kUmaTargetedHistogramFlag); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->Add(sample); \ + } while (0) + +#define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1000, 500000, 50) + +#define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, sample, 1, 1000, 50) + +#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ + UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101) + +#define UMA_HISTOGRAM_BOOLEAN(name, sample) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::BooleanHistogram::FactoryGet(name, \ + base::Histogram::kUmaTargetedHistogramFlag); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->AddBoolean(sample); \ + } while (0) + +#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::LinearHistogram::FactoryGet(name, 1, boundary_value, \ + boundary_value + 1, base::Histogram::kUmaTargetedHistogramFlag); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->Add(sample); \ + } while (0) + +#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) do { \ + static base::Histogram* counter(NULL); \ + if (!counter) \ + counter = base::CustomHistogram::FactoryGet(name, custom_ranges, \ + base::Histogram::kUmaTargetedHistogramFlag); \ + DCHECK_EQ(name, counter->histogram_name()); \ + counter->Add(sample); \ + } while (0) + +//------------------------------------------------------------------------------ + +class BooleanHistogram; +class CustomHistogram; +class Histogram; +class LinearHistogram; + +class Histogram { + public: + typedef int Sample; // Used for samples (and ranges of samples). + typedef int Count; // Used to count samples in a bucket. + static const Sample kSampleType_MAX = INT_MAX; + // Initialize maximum number of buckets in histograms as 16,384. + static const size_t kBucketCount_MAX; + + typedef std::vector<Count> Counts; + typedef std::vector<Sample> Ranges; + + // These enums are used to facilitate deserialization of renderer histograms + // into the browser. + enum ClassType { + HISTOGRAM, + LINEAR_HISTOGRAM, + BOOLEAN_HISTOGRAM, + FLAG_HISTOGRAM, + COUNT_HISTOGRAM, + CUSTOM_HISTOGRAM, + NOT_VALID_IN_RENDERER + }; + + enum BucketLayout { + EXPONENTIAL, + LINEAR, + CUSTOM + }; + + enum Flags { + kNoFlags = 0, + kUmaTargetedHistogramFlag = 0x1, // Histogram should be UMA uploaded. + + kHexRangePrintingFlag = 0x8000 // Fancy bucket-naming supported. + }; + + enum Inconsistencies { + NO_INCONSISTENCIES = 0x0, + RANGE_CHECKSUM_ERROR = 0x1, + BUCKET_ORDER_ERROR = 0x2, + COUNT_HIGH_ERROR = 0x4, + COUNT_LOW_ERROR = 0x8, + + NEVER_EXCEEDED_VALUE = 0x10 + }; + + struct DescriptionPair { + Sample sample; + const char* description; // Null means end of a list of pairs. + }; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); + + //---------------------------------------------------------------------------- + // Statistic values, developed over the life of the histogram. + + class SampleSet { + public: + explicit SampleSet(); + ~SampleSet(); + + // None of the methods in this class are thread-safe. Callers + // must deal with locking themselves. + + // Adjust size of counts_ for use with given histogram. + void Resize(const Histogram& histogram); + + // Accessor for histogram to make routine additions. + void Accumulate(Sample value, Count count, size_t index); + + // Arithmetic manipulation of corresponding elements of the set. + void Add(const SampleSet& other); + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf); + + Count counts(size_t i) const { + return counts_[i]; + } + Count TotalCount() const; + int64_t sum() const { + return sum_; + } + int64_t redundant_count() const { + return redundant_count_; + } + size_t size() const { + return counts_.size(); + } + + protected: + // Actual histogram data is stored in buckets, showing the count of values + // that fit into each bucket. + Counts counts_; + + // Save simple stats locally. Note that this MIGHT get done in base class + // without shared memory at some point. + int64_t sum_; // sum of samples. + + // To help identify memory corruption, we reduntantly save the number of + // samples we've accumulated into all of our buckets. We can compare this + // count to the sum of the counts in all buckets, and detect problems. Note + // that due to races in histogram accumulation (if a histogram is indeed + // updated on several threads simultaneously), the tallies might mismatch, + // and also the snapshotting code may asynchronously get a mismatch (though + // generally either race based mismatch cause is VERY rare). + int64_t redundant_count_; + }; + + //---------------------------------------------------------------------------- + // minimum should start from 1. 0 is invalid as a minimum. 0 is an implicit + // default underflow bucket. + static Histogram* FactoryGet(const std::string& name, + Sample minimum, + Sample maximum, + size_t bucket_count, + Flags flags); + static Histogram* FactoryTimeGet(const std::string& name, + base::TimeDelta minimum, + base::TimeDelta maximum, + size_t bucket_count, + Flags flags); + + void Add(int value); + void Subtract(int value); + + // TODO: Currently recording_enabled_ is not used by any Histogram class, but + // rather examined only by the telemetry code (via IsRecordingEnabled). + // Move handling to Histogram's Add() etc after simplifying Histogram. + void SetRecordingEnabled(bool aEnabled) { recording_enabled_ = aEnabled; }; + bool IsRecordingEnabled() const { return recording_enabled_; }; + + // This method is an interface, used only by BooleanHistogram. + virtual void AddBoolean(bool value); + + // Accept a TimeDelta to increment. + void AddTime(TimeDelta time) { + Add(static_cast<int>(time.InMilliseconds())); + } + + virtual void AddSampleSet(const SampleSet& sample); + + virtual void Clear(); + + // This method is an interface, used only by LinearHistogram. + virtual void SetRangeDescriptions(const DescriptionPair descriptions[]); + + // The following methods provide graphical histogram displays. + void WriteHTMLGraph(std::string* output) const; + void WriteAscii(bool graph_it, const std::string& newline, + std::string* output) const; + + // Support generic flagging of Histograms. + // 0x1 Currently used to mark this histogram to be recorded by UMA.. + // 0x8000 means print ranges in hex. + void SetFlags(Flags flags) { flags_ = static_cast<Flags> (flags_ | flags); } + void ClearFlags(Flags flags) { flags_ = static_cast<Flags>(flags_ & ~flags); } + int flags() const { return flags_; } + + // Check to see if bucket ranges, counts and tallies in the snapshot are + // consistent with the bucket ranges and checksums in our histogram. This can + // produce a false-alarm if a race occurred in the reading of the data during + // a SnapShot process, but should otherwise be false at all times (unless we + // have memory over-writes, or DRAM failures). + virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const; + + //---------------------------------------------------------------------------- + // Accessors for factory constuction, serialization and testing. + //---------------------------------------------------------------------------- + virtual ClassType histogram_type() const; + const std::string& histogram_name() const { return histogram_name_; } + Sample declared_min() const { return declared_min_; } + Sample declared_max() const { return declared_max_; } + virtual Sample ranges(size_t i) const; + uint32_t range_checksum() const { return range_checksum_; } + virtual size_t bucket_count() const; + + // Do a safe atomic snapshot of sample data. The caller is assumed to + // have exclusive access to the destination, |*sample|, and no locking + // of it is done here. + virtual void SnapshotSample(SampleSet* sample) const; + + virtual bool HasConstructorArguments(Sample minimum, Sample maximum, + size_t bucket_count); + + virtual bool HasConstructorTimeDeltaArguments(TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count); + // Return true iff the range_checksum_ matches current ranges_ vector. + bool HasValidRangeChecksum() const; + + protected: + Histogram(const std::string& name, Sample minimum, + Sample maximum, size_t bucket_count); + Histogram(const std::string& name, TimeDelta minimum, + TimeDelta maximum, size_t bucket_count); + + virtual ~Histogram(); + + // Initialize ranges_ mapping. + void InitializeBucketRange(); + + // Method to override to skip the display of the i'th bucket if it's empty. + virtual bool PrintEmptyBucket(size_t index) const; + + //---------------------------------------------------------------------------- + // Methods to override to create histogram with different bucket widths. + //---------------------------------------------------------------------------- + // Find bucket to increment for sample value. + virtual size_t BucketIndex(Sample value) const; + // Get normalized size, relative to the ranges_[i]. + virtual double GetBucketSize(Count current, size_t i) const; + + // Recalculate range_checksum_. + void ResetRangeChecksum(); + + // Return a string description of what goes in a given bucket. + // Most commonly this is the numeric value, but in derived classes it may + // be a name (or string description) given to the bucket. + virtual const std::string GetAsciiBucketRange(size_t it) const; + + //---------------------------------------------------------------------------- + // Methods to override to create thread safe histogram. + //---------------------------------------------------------------------------- + // Update all our internal data, including histogram + virtual void Accumulate(Sample value, Count count, size_t index); + + //---------------------------------------------------------------------------- + // Accessors for derived classes. + //---------------------------------------------------------------------------- + void SetBucketRange(size_t i, Sample value); + + // Validate that ranges_ was created sensibly (top and bottom range + // values relate properly to the declared_min_ and declared_max_).. + bool ValidateBucketRanges() const; + + virtual uint32_t CalculateRangeChecksum() const; + + // Finally, provide the state that changes with the addition of each new + // sample. + SampleSet sample_; + + private: + friend class StatisticsRecorder; // To allow it to delete duplicates. + + // Post constructor initialization. + void Initialize(); + + // Checksum function for accumulating range values into a checksum. + static uint32_t Crc32(uint32_t sum, Sample range); + + //---------------------------------------------------------------------------- + // Helpers for emitting Ascii graphic. Each method appends data to output. + + // Find out how large the (graphically) the largest bucket will appear to be. + double GetPeakBucketSize(const SampleSet& snapshot) const; + + // Write a common header message describing this histogram. + void WriteAsciiHeader(const SampleSet& snapshot, + Count sample_count, std::string* output) const; + + // Write information about previous, current, and next buckets. + // Information such as cumulative percentage, etc. + void WriteAsciiBucketContext(const int64_t past, const Count current, + const int64_t remaining, const size_t i, + std::string* output) const; + + // Write textual description of the bucket contents (relative to histogram). + // Output is the count in the buckets, as well as the percentage. + void WriteAsciiBucketValue(Count current, double scaled_sum, + std::string* output) const; + + // Produce actual graph (set of blank vs non blank char's) for a bucket. + void WriteAsciiBucketGraph(double current_size, double max_size, + std::string* output) const; + + //---------------------------------------------------------------------------- + // Table for generating Crc32 values. + static const uint32_t kCrcTable[256]; + //---------------------------------------------------------------------------- + // Invariant values set at/near construction time + + // ASCII version of original name given to the constructor. All identically + // named instances will be coalesced cross-project. + const std::string histogram_name_; + Sample declared_min_; // Less than this goes into counts_[0] + Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1]. + size_t bucket_count_; // Dimension of counts_[]. + + // Flag the histogram for recording by UMA via metric_services.h. + Flags flags_; + + // For each index, show the least value that can be stored in the + // corresponding bucket. We also append one extra element in this array, + // containing kSampleType_MAX, to make calculations easy. + // The dimension of ranges_ is bucket_count + 1. + Ranges ranges_; + + // For redundancy, we store a checksum of all the sample ranges when ranges + // are generated. If ever there is ever a difference, then the histogram must + // have been corrupted. + uint32_t range_checksum_; + + // When false, new samples are completely ignored. + mozilla::Atomic<bool, mozilla::Relaxed> recording_enabled_; + + DISALLOW_COPY_AND_ASSIGN(Histogram); +}; + +//------------------------------------------------------------------------------ + +// LinearHistogram is a more traditional histogram, with evenly spaced +// buckets. +class LinearHistogram : public Histogram { + public: + virtual ~LinearHistogram(); + + /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit + default underflow bucket. */ + static Histogram* FactoryGet(const std::string& name, + Sample minimum, + Sample maximum, + size_t bucket_count, + Flags flags); + static Histogram* FactoryTimeGet(const std::string& name, + TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count, + Flags flags); + + // Overridden from Histogram: + virtual ClassType histogram_type() const; + + virtual void Accumulate(Sample value, Count count, size_t index); + + // Store a list of number/text values for use in rendering the histogram. + // The last element in the array has a null in its "description" slot. + virtual void SetRangeDescriptions(const DescriptionPair descriptions[]); + + protected: + LinearHistogram(const std::string& name, Sample minimum, + Sample maximum, size_t bucket_count); + + LinearHistogram(const std::string& name, TimeDelta minimum, + TimeDelta maximum, size_t bucket_count); + + // Initialize ranges_ mapping. + void InitializeBucketRange(); + virtual double GetBucketSize(Count current, size_t i) const; + + // If we have a description for a bucket, then return that. Otherwise + // let parent class provide a (numeric) description. + virtual const std::string GetAsciiBucketRange(size_t i) const; + + // Skip printing of name for numeric range if we have a name (and if this is + // an empty bucket). + virtual bool PrintEmptyBucket(size_t index) const; + + private: + // For some ranges, we store a printable description of a bucket range. + // If there is no desciption, then GetAsciiBucketRange() uses parent class + // to provide a description. + typedef std::map<Sample, std::string> BucketDescriptionMap; + BucketDescriptionMap bucket_description_; + + DISALLOW_COPY_AND_ASSIGN(LinearHistogram); +}; + +//------------------------------------------------------------------------------ + +// BooleanHistogram is a histogram for booleans. +class BooleanHistogram : public LinearHistogram { + public: + static Histogram* FactoryGet(const std::string& name, Flags flags); + + virtual ClassType histogram_type() const; + + virtual void AddBoolean(bool value); + + virtual void Accumulate(Sample value, Count count, size_t index); + + protected: + explicit BooleanHistogram(const std::string& name); + + DISALLOW_COPY_AND_ASSIGN(BooleanHistogram); +}; + +//------------------------------------------------------------------------------ + +// FlagHistogram is like boolean histogram, but only allows a single off/on value. +class FlagHistogram : public BooleanHistogram +{ +public: + static Histogram *FactoryGet(const std::string &name, Flags flags); + + virtual ClassType histogram_type() const; + + virtual void Accumulate(Sample value, Count count, size_t index); + + virtual void AddSampleSet(const SampleSet& sample); + + virtual void Clear(); + +private: + explicit FlagHistogram(const std::string &name); + bool mSwitched; + + DISALLOW_COPY_AND_ASSIGN(FlagHistogram); +}; + +// CountHistogram only allows a single monotic counter value. +class CountHistogram : public LinearHistogram +{ +public: + static Histogram *FactoryGet(const std::string &name, Flags flags); + + virtual ClassType histogram_type() const; + + virtual void Accumulate(Sample value, Count count, size_t index); + + virtual void AddSampleSet(const SampleSet& sample); + +private: + explicit CountHistogram(const std::string &name); + + DISALLOW_COPY_AND_ASSIGN(CountHistogram); +}; + +//------------------------------------------------------------------------------ + +// CustomHistogram is a histogram for a set of custom integers. +class CustomHistogram : public Histogram { + public: + + static Histogram* FactoryGet(const std::string& name, + const std::vector<Sample>& custom_ranges, + Flags flags); + + // Overridden from Histogram: + virtual ClassType histogram_type() const; + + protected: + CustomHistogram(const std::string& name, + const std::vector<Sample>& custom_ranges); + + // Initialize ranges_ mapping. + void InitializedCustomBucketRange(const std::vector<Sample>& custom_ranges); + virtual double GetBucketSize(Count current, size_t i) const; + + DISALLOW_COPY_AND_ASSIGN(CustomHistogram); +}; + +//------------------------------------------------------------------------------ +// StatisticsRecorder handles all histograms in the system. It provides a +// general place for histograms to register, and supports a global API for +// accessing (i.e., dumping, or graphing) the data in all the histograms. + +class StatisticsRecorder { + public: + typedef std::vector<Histogram*> Histograms; + + StatisticsRecorder(); + + ~StatisticsRecorder(); + + // Find out if histograms can now be registered into our list. + static bool IsActive(); + + // Register, or add a new histogram to the collection of statistics. If an + // identically named histogram is already registered, then the argument + // |histogram| will deleted. The returned value is always the registered + // histogram (either the argument, or the pre-existing registered histogram). + static Histogram* RegisterOrDeleteDuplicate(Histogram* histogram); + + // Methods for printing histograms. Only histograms which have query as + // a substring are written to output (an empty string will process all + // registered histograms). + static void WriteHTMLGraph(const std::string& query, std::string* output); + static void WriteGraph(const std::string& query, std::string* output); + + // Method for extracting histograms which were marked for use by UMA. + static void GetHistograms(Histograms* output); + + // Find a histogram by name. It matches the exact name. This method is thread + // safe. If a matching histogram is not found, then the |histogram| is + // not changed. + static bool FindHistogram(const std::string& query, Histogram** histogram); + + static bool dump_on_exit() { return dump_on_exit_; } + + static void set_dump_on_exit(bool enable) { dump_on_exit_ = enable; } + + // GetSnapshot copies some of the pointers to registered histograms into the + // caller supplied vector (Histograms). Only histograms with names matching + // query are returned. The query must be a substring of histogram name for its + // pointer to be copied. + static void GetSnapshot(const std::string& query, Histograms* snapshot); + + + private: + // We keep all registered histograms in a map, from name to histogram. + typedef std::map<std::string, Histogram*> HistogramMap; + + static HistogramMap* histograms_; + + // lock protects access to the above map. + static Lock* lock_; + + // Dump all known histograms to log. + static bool dump_on_exit_; + + DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder); +}; + +} // namespace base + +#endif // BASE_METRICS_HISTOGRAM_H_ diff --git a/ipc/chromium/src/base/id_map.h b/ipc/chromium/src/base/id_map.h new file mode 100644 index 0000000000..bdd17effd4 --- /dev/null +++ b/ipc/chromium/src/base/id_map.h @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_ID_MAP_H__ +#define BASE_ID_MAP_H__ + +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/logging.h" + +// This object maintains a list of IDs that can be quickly converted to +// pointers to objects. It is implemented as a hash table, optimized for +// relatively small data sets (in the common case, there will be exactly one +// item in the list). +// +// Items can be inserted into the container with arbitrary ID, but the caller +// must ensure they are unique. Inserting IDs and relying on automatically +// generated ones is not allowed because they can collide. +template<class T> +class IDMap { + private: + typedef base::hash_map<int32_t, T*> HashTable; + typedef typename HashTable::iterator iterator; + + public: + // support const iterators over the items + // Note, use iterator->first to get the ID, iterator->second to get the T* + typedef typename HashTable::const_iterator const_iterator; + + IDMap() : next_id_(1) { + } + IDMap(const IDMap& other) : next_id_(other.next_id_), + data_(other.data_) { + } + + const_iterator begin() const { + return data_.begin(); + } + const_iterator end() const { + return data_.end(); + } + + // Adds a view with an automatically generated unique ID. See AddWithID. + int32_t Add(T* data) { + int32_t this_id = next_id_; + DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item"; + data_[this_id] = data; + next_id_++; + return this_id; + } + + // Adds a new data member with the specified ID. The ID must not be in + // the list. The caller either must generate all unique IDs itself and use + // this function, or allow this object to generate IDs and call Add. These + // two methods may not be mixed, or duplicate IDs may be generated + void AddWithID(T* data, int32_t id) { + DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item"; + data_[id] = data; + } + + void Remove(int32_t id) { + iterator i = data_.find(id); + if (i == data_.end()) { + NOTREACHED() << "Attempting to remove an item not in the list"; + return; + } + data_.erase(i); + } + + bool IsEmpty() const { + return data_.empty(); + } + + void Clear() { + data_.clear(); + } + + bool HasData(const T* data) const { + // XXX would like to use <algorithm> here ... + for (const_iterator it = begin(); it != end(); ++it) + if (data == it->second) + return true; + return false; + } + + T* Lookup(int32_t id) const { + const_iterator i = data_.find(id); + if (i == data_.end()) + return NULL; + return i->second; + } + + size_t size() const { + return data_.size(); + } + + protected: + // The next ID that we will return from Add() + int32_t next_id_; + + HashTable data_; +}; + +#endif // BASE_ID_MAP_H__ diff --git a/ipc/chromium/src/base/linked_ptr.h b/ipc/chromium/src/base/linked_ptr.h new file mode 100644 index 0000000000..1484bb4a37 --- /dev/null +++ b/ipc/chromium/src/base/linked_ptr.h @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is released, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Thread Safety: +// A linked_ptr is NOT thread safe. Copying a linked_ptr object is +// effectively a read-write operation. +// +// Alternative: to linked_ptr is shared_ptr, which +// - is also two pointers in size (8 bytes for 32 bit addresses) +// - is thread safe for copying and deletion +// - supports weak_ptrs + +#ifndef BASE_LINKED_PTR_H_ +#define BASE_LINKED_PTR_H_ + +#include "base/logging.h" // for CHECK macros + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) { + next_ = ptr->next_; + ptr->next_ = this; + } + + // Leave whatever circle we're part of. Returns true iff we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() { + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template <typename T> +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { DCHECK_NE(&ptr, this); copy(&ptr); } + + // Assignment releases the old value and acquires the new. + template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { depart(); capture(ptr); } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + // Release ownership of the pointed object and returns it. + // Sole ownership by this linked_ptr object is required. + T* release() { + bool last = link_.depart(); + CHECK(last); + T* v = value_; + value_ = NULL; + return v; + } + + bool operator==(const T* p) const { return value_ == p; } + bool operator!=(const T* p) const { return value_ != p; } + template <typename U> + bool operator==(linked_ptr<U> const& ptr) const { + return value_ == ptr.get(); + } + template <typename U> + bool operator!=(linked_ptr<U> const& ptr) const { + return value_ != ptr.get(); + } + + private: + template <typename U> + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template <typename U> void copy(linked_ptr<U> const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template<typename T> inline +bool operator==(T* ptr, const linked_ptr<T>& x) { + return ptr == x.get(); +} + +template<typename T> inline +bool operator!=(T* ptr, const linked_ptr<T>& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr<T> +// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation +// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg)) +template <typename T> +linked_ptr<T> make_linked_ptr(T* ptr) { + return linked_ptr<T>(ptr); +} + +#endif // BASE_LINKED_PTR_H_ diff --git a/ipc/chromium/src/base/lock.cc b/ipc/chromium/src/base/lock.cc new file mode 100644 index 0000000000..47d3a4de1e --- /dev/null +++ b/ipc/chromium/src/base/lock.cc @@ -0,0 +1,9 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// Lock class. + +// Depricated file. See lock_impl_*.cc for platform specific versions. diff --git a/ipc/chromium/src/base/lock.h b/ipc/chromium/src/base/lock.h new file mode 100644 index 0000000000..3f8b96a8f2 --- /dev/null +++ b/ipc/chromium/src/base/lock.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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 BASE_LOCK_H_ +#define BASE_LOCK_H_ + +#include "base/lock_impl.h" + +// A convenient wrapper for an OS specific critical section. + +class Lock { + public: + Lock() : lock_() {} + ~Lock() {} + void Acquire() { lock_.Lock(); } + void Release() { lock_.Unlock(); } + // If the lock is not held, take it and return true. If the lock is already + // held by another thread, immediately return false. + bool Try() { return lock_.Try(); } + + // In debug builds this method checks that the lock has been acquired by the + // calling thread. If the lock has not been acquired, then the method + // will DCHECK(). In non-debug builds, the LockImpl's implementation of + // AssertAcquired() is an empty inline method. + void AssertAcquired() const { return lock_.AssertAcquired(); } + + // Return the underlying lock implementation. + // TODO(awalker): refactor lock and condition variables so that this is + // unnecessary. + LockImpl* lock_impl() { return &lock_; } + + private: + LockImpl lock_; // Platform specific underlying lock implementation. + + DISALLOW_COPY_AND_ASSIGN(Lock); +}; + +// A helper class that acquires the given Lock while the AutoLock is in scope. +class AutoLock { + public: + explicit AutoLock(Lock& lock) : lock_(lock) { + lock_.Acquire(); + } + + ~AutoLock() { + lock_.AssertAcquired(); + lock_.Release(); + } + + private: + Lock& lock_; + DISALLOW_COPY_AND_ASSIGN(AutoLock); +}; + +// AutoUnlock is a helper that will Release() the |lock| argument in the +// constructor, and re-Acquire() it in the destructor. +class AutoUnlock { + public: + explicit AutoUnlock(Lock& lock) : lock_(lock) { + // We require our caller to have the lock. + lock_.AssertAcquired(); + lock_.Release(); + } + + ~AutoUnlock() { + lock_.Acquire(); + } + + private: + Lock& lock_; + DISALLOW_COPY_AND_ASSIGN(AutoUnlock); +}; + +#endif // BASE_LOCK_H_ diff --git a/ipc/chromium/src/base/lock_impl.h b/ipc/chromium/src/base/lock_impl.h new file mode 100644 index 0000000000..aea01bd883 --- /dev/null +++ b/ipc/chromium/src/base/lock_impl.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_LOCK_IMPL_H_ +#define BASE_LOCK_IMPL_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#elif defined(OS_POSIX) +#include <pthread.h> +#endif + +#include "base/basictypes.h" +#include "base/platform_thread.h" + +// This class implements the underlying platform-specific spin-lock mechanism +// used for the Lock class. Most users should not use LockImpl directly, but +// should instead use Lock. +class LockImpl { + public: +#if defined(OS_WIN) + typedef CRITICAL_SECTION OSLockType; +#elif defined(OS_POSIX) + typedef pthread_mutex_t OSLockType; +#endif + + LockImpl(); + ~LockImpl(); + + // If the lock is not held, take it and return true. If the lock is already + // held by something else, immediately return false. + bool Try(); + + // Take the lock, blocking until it is available if necessary. + void Lock(); + + // Release the lock. This must only be called by the lock's holder: after + // a successful call to Try, or a call to Lock. + void Unlock(); + + // Debug-only method that will DCHECK() if the lock is not acquired by the + // current thread. In non-debug builds, no check is performed. + // Because linux and mac condition variables modify the underlyning lock + // through the os_lock() method, runtime assertions can not be done on those + // builds. +#if defined(NDEBUG) || !defined(OS_WIN) + void AssertAcquired() const {} +#else + void AssertAcquired() const; +#endif + + // Return the native underlying lock. Not supported for Windows builds. + // TODO(awalker): refactor lock and condition variables so that this is + // unnecessary. +#if !defined(OS_WIN) + OSLockType* os_lock() { return &os_lock_; } +#endif + + private: + OSLockType os_lock_; + +#if !defined(NDEBUG) && defined(OS_WIN) + // All private data is implicitly protected by lock_. + // Be VERY careful to only access members under that lock. + PlatformThreadId owning_thread_id_; + int32_t recursion_count_shadow_; + bool recursion_used_; // Allow debugging to continued after a DCHECK(). +#endif // NDEBUG + + DISALLOW_COPY_AND_ASSIGN(LockImpl); +}; + + +#endif // BASE_LOCK_IMPL_H_ diff --git a/ipc/chromium/src/base/lock_impl_posix.cc b/ipc/chromium/src/base/lock_impl_posix.cc new file mode 100644 index 0000000000..50fec95db2 --- /dev/null +++ b/ipc/chromium/src/base/lock_impl_posix.cc @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/lock_impl.h" + +#include <errno.h> + +#include "base/logging.h" + +LockImpl::LockImpl() { +#ifndef NDEBUG + // In debug, setup attributes for lock error checking. + pthread_mutexattr_t mta; + int rv = pthread_mutexattr_init(&mta); + DCHECK_EQ(rv, 0); + rv = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK); + DCHECK_EQ(rv, 0); + rv = pthread_mutex_init(&os_lock_, &mta); + DCHECK_EQ(rv, 0); + rv = pthread_mutexattr_destroy(&mta); + DCHECK_EQ(rv, 0); +#else + // In release, go with the default lock attributes. + pthread_mutex_init(&os_lock_, NULL); +#endif +} + +LockImpl::~LockImpl() { + int rv = pthread_mutex_destroy(&os_lock_); + DCHECK_EQ(rv, 0); +} + +bool LockImpl::Try() { + int rv = pthread_mutex_trylock(&os_lock_); + DCHECK(rv == 0 || rv == EBUSY); + return rv == 0; +} + +void LockImpl::Lock() { + int rv = pthread_mutex_lock(&os_lock_); + DCHECK_EQ(rv, 0); +} + +void LockImpl::Unlock() { + int rv = pthread_mutex_unlock(&os_lock_); + DCHECK_EQ(rv, 0); +} diff --git a/ipc/chromium/src/base/lock_impl_win.cc b/ipc/chromium/src/base/lock_impl_win.cc new file mode 100644 index 0000000000..2a50fb234f --- /dev/null +++ b/ipc/chromium/src/base/lock_impl_win.cc @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/lock_impl.h" +#include "base/logging.h" + +// NOTE: Although windows critical sections support recursive locks, we do not +// allow this, and we will commonly fire a DCHECK() if a thread attempts to +// acquire the lock a second time (while already holding it). + +LockImpl::LockImpl() { +#ifndef NDEBUG + recursion_count_shadow_ = 0; + recursion_used_ = false; + owning_thread_id_ = 0; +#endif // NDEBUG + // The second parameter is the spin count, for short-held locks it avoid the + // contending thread from going to sleep which helps performance greatly. + ::InitializeCriticalSectionAndSpinCount(&os_lock_, 2000); +} + +LockImpl::~LockImpl() { + ::DeleteCriticalSection(&os_lock_); +} + +bool LockImpl::Try() { + if (::TryEnterCriticalSection(&os_lock_) != FALSE) { +#ifndef NDEBUG + // ONLY access data after locking. + owning_thread_id_ = PlatformThread::CurrentId(); + DCHECK_NE(owning_thread_id_, 0); + recursion_count_shadow_++; + if (2 == recursion_count_shadow_ && !recursion_used_) { + recursion_used_ = true; + DCHECK(false); // Catch accidental redundant lock acquisition. + } +#endif + return true; + } + return false; +} + +void LockImpl::Lock() { + ::EnterCriticalSection(&os_lock_); +#ifndef NDEBUG + // ONLY access data after locking. + owning_thread_id_ = PlatformThread::CurrentId(); + DCHECK_NE(owning_thread_id_, 0); + recursion_count_shadow_++; + if (2 == recursion_count_shadow_ && !recursion_used_) { + recursion_used_ = true; + DCHECK(false); // Catch accidental redundant lock acquisition. + } +#endif // NDEBUG +} + +void LockImpl::Unlock() { +#ifndef NDEBUG + --recursion_count_shadow_; // ONLY access while lock is still held. + DCHECK(0 <= recursion_count_shadow_); + owning_thread_id_ = 0; +#endif // NDEBUG + ::LeaveCriticalSection(&os_lock_); +} + +// In non-debug builds, this method is declared as an empty inline method. +#ifndef NDEBUG +void LockImpl::AssertAcquired() const { + DCHECK(recursion_count_shadow_ > 0); + DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId()); +} +#endif diff --git a/ipc/chromium/src/base/logging.cc b/ipc/chromium/src/base/logging.cc new file mode 100644 index 0000000000..d793990de4 --- /dev/null +++ b/ipc/chromium/src/base/logging.cc @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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 "base/logging.h" +#include "prmem.h" +#include "prprf.h" +#include "base/string_util.h" +#include "nsXPCOM.h" + +namespace mozilla { + +Logger::~Logger() +{ + LogLevel prlevel = LogLevel::Debug; + int xpcomlevel = -1; + + switch (mSeverity) { + case LOG_INFO: + prlevel = LogLevel::Debug; + xpcomlevel = -1; + break; + + case LOG_WARNING: + prlevel = LogLevel::Warning; + xpcomlevel = NS_DEBUG_WARNING; + break; + + case LOG_ERROR: + prlevel = LogLevel::Error; + xpcomlevel = NS_DEBUG_WARNING; + break; + + case LOG_ERROR_REPORT: + prlevel = LogLevel::Error; + xpcomlevel = NS_DEBUG_ASSERTION; + break; + + case LOG_FATAL: + prlevel = LogLevel::Error; + xpcomlevel = NS_DEBUG_ABORT; + break; + } + + MOZ_LOG(gChromiumPRLog, prlevel, ("%s:%i: %s", mFile, mLine, mMsg ? mMsg : "<no message>")); + if (xpcomlevel != -1) + NS_DebugBreak(xpcomlevel, mMsg, NULL, mFile, mLine); + + PR_Free(mMsg); +} + +void +Logger::printf(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + mMsg = PR_vsprintf_append(mMsg, fmt, args); + va_end(args); +} + +LazyLogModule Logger::gChromiumPRLog("chromium"); +} // namespace mozilla + +mozilla::Logger& +operator<<(mozilla::Logger& log, const char* s) +{ + log.printf("%s", s); + return log; +} + +mozilla::Logger& +operator<<(mozilla::Logger& log, const std::string& s) +{ + log.printf("%s", s.c_str()); + return log; +} + +mozilla::Logger& +operator<<(mozilla::Logger& log, int i) +{ + log.printf("%i", i); + return log; +} + +mozilla::Logger& +operator<<(mozilla::Logger& log, const std::wstring& s) +{ + log.printf("%s", WideToASCII(s).c_str()); + return log; +} + +mozilla::Logger& +operator<<(mozilla::Logger& log, void* p) +{ + log.printf("%p", p); + return log; +} diff --git a/ipc/chromium/src/base/logging.h b/ipc/chromium/src/base/logging.h new file mode 100644 index 0000000000..37523d307e --- /dev/null +++ b/ipc/chromium/src/base/logging.h @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_LOGGING_H_ +#define BASE_LOGGING_H_ + +#include <string> +#include <cstring> + +#include "base/basictypes.h" +#include "mozilla/Logging.h" + +#ifdef NO_CHROMIUM_LOGGING +#include <sstream> +#endif + +// Replace the Chromium logging code with NSPR-based logging code and +// some C++ wrappers to emulate std::ostream + +namespace mozilla { + +enum LogSeverity { + LOG_INFO, + LOG_WARNING, + LOG_ERROR, + LOG_ERROR_REPORT, + LOG_FATAL, + LOG_0 = LOG_ERROR +}; + +class Logger +{ +public: + Logger(LogSeverity severity, const char* file, int line) + : mSeverity(severity) + , mFile(file) + , mLine(line) + , mMsg(NULL) + { } + + ~Logger(); + + // not private so that the operator<< overloads can get to it + void printf(const char* fmt, ...); + +private: + static mozilla::LazyLogModule gChromiumPRLog; +// static PRLogModuleInfo* GetLog(); + + LogSeverity mSeverity; + const char* mFile; + int mLine; + char* mMsg; + + DISALLOW_EVIL_CONSTRUCTORS(Logger); +}; + +class LogWrapper +{ +public: + LogWrapper(LogSeverity severity, const char* file, int line) : + log(severity, file, line) { } + + operator Logger&() const { return log; } + +private: + mutable Logger log; + + DISALLOW_EVIL_CONSTRUCTORS(LogWrapper); +}; + +struct EmptyLog +{ +}; + +} // namespace mozilla + +mozilla::Logger& operator<<(mozilla::Logger& log, const char* s); +mozilla::Logger& operator<<(mozilla::Logger& log, const std::string& s); +mozilla::Logger& operator<<(mozilla::Logger& log, int i); +mozilla::Logger& operator<<(mozilla::Logger& log, const std::wstring& s); +mozilla::Logger& operator<<(mozilla::Logger& log, void* p); + +template<class T> +const mozilla::EmptyLog& operator <<(const mozilla::EmptyLog& log, const T&) +{ + return log; +} + +#ifdef NO_CHROMIUM_LOGGING +#define CHROMIUM_LOG(info) std::stringstream() +#define LOG_IF(info, condition) if (!(condition)) std::stringstream() +#else +#define CHROMIUM_LOG(info) mozilla::LogWrapper(mozilla::LOG_ ## info, __FILE__, __LINE__) +#define LOG_IF(info, condition) \ + if (!(condition)) mozilla::LogWrapper(mozilla::LOG_ ## info, __FILE__, __LINE__) +#endif + + +#ifdef DEBUG +#define DLOG(info) CHROMIUM_LOG(info) +#define DLOG_IF(info) LOG_IF(info) +#define DCHECK(condition) CHECK(condition) +#else +#define DLOG(info) mozilla::EmptyLog() +#define DLOG_IF(info, condition) mozilla::EmptyLog() +#define DCHECK(condition) while (false && (condition)) mozilla::EmptyLog() +#endif + +#undef LOG_ASSERT +#define LOG_ASSERT(cond) CHECK(0) +#define DLOG_ASSERT(cond) DCHECK(0) + +#define NOTREACHED() CHROMIUM_LOG(ERROR) +#define NOTIMPLEMENTED() CHROMIUM_LOG(ERROR) + +#undef CHECK +#define CHECK(condition) LOG_IF(WARNING, condition) + +#define DCHECK_EQ(v1, v2) DCHECK((v1) == (v2)) +#define DCHECK_NE(v1, v2) DCHECK((v1) != (v2)) +#define DCHECK_LE(v1, v2) DCHECK((v1) <= (v2)) +#define DCHECK_LT(v1, v2) DCHECK((v1) < (v2)) +#define DCHECK_GE(v1, v2) DCHECK((v1) >= (v2)) +#define DCHECK_GT(v1, v2) DCHECK((v1) > (v2)) + +#endif // BASE_LOGGING_H_ diff --git a/ipc/chromium/src/base/mac_util.h b/ipc/chromium/src/base/mac_util.h new file mode 100644 index 0000000000..6af2bd7032 --- /dev/null +++ b/ipc/chromium/src/base/mac_util.h @@ -0,0 +1,17 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 BASE_MAC_UTIL_H_ +#define BASE_MAC_UTIL_H_ + +namespace mac_util { + +// Returns true if the application is running from a bundle +bool AmIBundled(); + +} // namespace mac_util + +#endif // BASE_MAC_UTIL_H_ diff --git a/ipc/chromium/src/base/mac_util.mm b/ipc/chromium/src/base/mac_util.mm new file mode 100644 index 0000000000..2124583678 --- /dev/null +++ b/ipc/chromium/src/base/mac_util.mm @@ -0,0 +1,34 @@ +// Copyright (c) 2008 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 "base/mac_util.h" + +#include <Carbon/Carbon.h> +#import <Cocoa/Cocoa.h> + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/scoped_cftyperef.h" +#include "base/sys_string_conversions.h" + +namespace mac_util { + +// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled +bool AmIBundled() { + ProcessSerialNumber psn = {0, kCurrentProcess}; + + FSRef fsref; + if (GetProcessBundleLocation(&psn, &fsref) != noErr) + return false; + + FSCatalogInfo info; + if (FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info, + NULL, NULL, NULL) != noErr) { + return false; + } + + return info.nodeFlags & kFSNodeIsDirectoryMask; +} + +} // namespace mac_util diff --git a/ipc/chromium/src/base/message_loop.cc b/ipc/chromium/src/base/message_loop.cc new file mode 100644 index 0000000000..3c794b2763 --- /dev/null +++ b/ipc/chromium/src/base/message_loop.cc @@ -0,0 +1,579 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 "base/message_loop.h" + +#include <algorithm> + +#include "mozilla/Atomics.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/message_pump_default.h" +#include "base/string_util.h" +#include "base/thread_local.h" + +#if defined(OS_MACOSX) +#include "base/message_pump_mac.h" +#endif +#if defined(OS_POSIX) +#include "base/message_pump_libevent.h" +#endif +#if defined(OS_LINUX) || defined(OS_BSD) +#if defined(MOZ_WIDGET_GTK) +#include "base/message_pump_glib.h" +#endif +#endif +#ifdef ANDROID +#include "base/message_pump_android.h" +#endif +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#include "TracedTaskCommon.h" +#endif + +#include "MessagePump.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + +using mozilla::Move; +using mozilla::Runnable; + +static base::ThreadLocalPointer<MessageLoop>& get_tls_ptr() { + static base::ThreadLocalPointer<MessageLoop> tls_ptr; + return tls_ptr; +} + +//------------------------------------------------------------------------------ + +// Logical events for Histogram profiling. Run with -message-loop-histogrammer +// to get an accounting of messages and actions taken on each thread. +static const int kTaskRunEvent = 0x1; +static const int kTimerEvent = 0x2; + +// Provide range of message IDs for use in histogramming and debug display. +static const int kLeastNonZeroMessageId = 1; +static const int kMaxMessageId = 1099; +static const int kNumberOfDistinctMessagesDisplayed = 1100; + +//------------------------------------------------------------------------------ + +#if defined(OS_WIN) + +// Upon a SEH exception in this thread, it restores the original unhandled +// exception filter. +static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) { + ::SetUnhandledExceptionFilter(old_filter); + return EXCEPTION_CONTINUE_SEARCH; +} + +// Retrieves a pointer to the current unhandled exception filter. There +// is no standalone getter method. +static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() { + LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL; + top_filter = ::SetUnhandledExceptionFilter(0); + ::SetUnhandledExceptionFilter(top_filter); + return top_filter; +} + +#endif // defined(OS_WIN) + +//------------------------------------------------------------------------------ + +// static +MessageLoop* MessageLoop::current() { + return get_tls_ptr().Get(); +} + +static mozilla::Atomic<int32_t> message_loop_id_seq(0); + +MessageLoop::MessageLoop(Type type, nsIThread* aThread) + : type_(type), + id_(++message_loop_id_seq), + nestable_tasks_allowed_(true), + exception_restoration_(false), + state_(NULL), + run_depth_base_(1), +#ifdef OS_WIN + os_modal_loop_(false), +#endif // OS_WIN + transient_hang_timeout_(0), + permanent_hang_timeout_(0), + next_sequence_num_(0) { + DCHECK(!current()) << "should only have one message loop per thread"; + get_tls_ptr().Set(this); + + switch (type_) { + case TYPE_MOZILLA_PARENT: + MOZ_RELEASE_ASSERT(!aThread); + pump_ = new mozilla::ipc::MessagePump(aThread); + return; + case TYPE_MOZILLA_CHILD: + MOZ_RELEASE_ASSERT(!aThread); + pump_ = new mozilla::ipc::MessagePumpForChildProcess(); + // There is a MessageLoop Run call from XRE_InitChildProcess + // and another one from MessagePumpForChildProcess. The one + // from MessagePumpForChildProcess becomes the base, so we need + // to set run_depth_base_ to 2 or we'll never be able to process + // Idle tasks. + run_depth_base_ = 2; + return; + case TYPE_MOZILLA_NONMAINTHREAD: + pump_ = new mozilla::ipc::MessagePumpForNonMainThreads(aThread); + return; +#if defined(OS_WIN) + case TYPE_MOZILLA_NONMAINUITHREAD: + pump_ = new mozilla::ipc::MessagePumpForNonMainUIThreads(aThread); + return; +#endif + default: + // Create one of Chromium's standard MessageLoop types below. + break; + } + +#if defined(OS_WIN) + // TODO(rvargas): Get rid of the OS guards. + if (type_ == TYPE_DEFAULT) { + pump_ = new base::MessagePumpDefault(); + } else if (type_ == TYPE_IO) { + pump_ = new base::MessagePumpForIO(); + } else { + DCHECK(type_ == TYPE_UI); + pump_ = new base::MessagePumpForUI(); + } +#elif defined(OS_POSIX) + if (type_ == TYPE_UI) { +#if defined(OS_MACOSX) + pump_ = base::MessagePumpMac::Create(); +#elif defined(OS_LINUX) || defined(OS_BSD) + pump_ = new base::MessagePumpForUI(); +#endif // OS_LINUX + } else if (type_ == TYPE_IO) { + pump_ = new base::MessagePumpLibevent(); + } else { + pump_ = new base::MessagePumpDefault(); + } +#endif // OS_POSIX +} + +MessageLoop::~MessageLoop() { + DCHECK(this == current()); + + // Let interested parties have one last shot at accessing this. + FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_, + WillDestroyCurrentMessageLoop()); + + DCHECK(!state_); + + // Clean up any unprocessed tasks, but take care: deleting a task could + // result in the addition of more tasks (e.g., via DeleteSoon). We set a + // limit on the number of times we will allow a deleted task to generate more + // tasks. Normally, we should only pass through this loop once or twice. If + // we end up hitting the loop limit, then it is probably due to one task that + // is being stubborn. Inspect the queues to see who is left. + bool did_work; + for (int i = 0; i < 100; ++i) { + DeletePendingTasks(); + ReloadWorkQueue(); + // If we end up with empty queues, then break out of the loop. + did_work = DeletePendingTasks(); + if (!did_work) + break; + } + DCHECK(!did_work); + + // OK, now make it so that no one can find us. + get_tls_ptr().Set(NULL); +} + +void MessageLoop::AddDestructionObserver(DestructionObserver *obs) { + DCHECK(this == current()); + destruction_observers_.AddObserver(obs); +} + +void MessageLoop::RemoveDestructionObserver(DestructionObserver *obs) { + DCHECK(this == current()); + destruction_observers_.RemoveObserver(obs); +} + +void MessageLoop::Run() { + AutoRunState save_state(this); + RunHandler(); +} + +// Runs the loop in two different SEH modes: +// enable_SEH_restoration_ = false : any unhandled exception goes to the last +// one that calls SetUnhandledExceptionFilter(). +// enable_SEH_restoration_ = true : any unhandled exception goes to the filter +// that was existed before the loop was run. +void MessageLoop::RunHandler() { +#if defined(OS_WIN) + if (exception_restoration_) { + LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter(); + MOZ_SEH_TRY { + RunInternal(); + } MOZ_SEH_EXCEPT(SEHFilter(current_filter)) { + } + return; + } +#endif + + RunInternal(); +} + +//------------------------------------------------------------------------------ + +void MessageLoop::RunInternal() { + DCHECK(this == current()); + pump_->Run(this); +} + +//------------------------------------------------------------------------------ +// Wrapper functions for use in above message loop framework. + +bool MessageLoop::ProcessNextDelayedNonNestableTask() { + if (state_->run_depth > run_depth_base_) + return false; + + if (deferred_non_nestable_work_queue_.empty()) + return false; + + RefPtr<Runnable> task = deferred_non_nestable_work_queue_.front().task.forget(); + deferred_non_nestable_work_queue_.pop(); + + RunTask(task.forget()); + return true; +} + +//------------------------------------------------------------------------------ + +void MessageLoop::Quit() { + DCHECK(current() == this); + if (state_) { + state_->quit_received = true; + } else { + NOTREACHED() << "Must be inside Run to call Quit"; + } +} + +void MessageLoop::PostTask(already_AddRefed<Runnable> task) { + PostTask_Helper(Move(task), 0); +} + +void MessageLoop::PostDelayedTask(already_AddRefed<Runnable> task, int delay_ms) { + PostTask_Helper(Move(task), delay_ms); +} + +void MessageLoop::PostIdleTask(already_AddRefed<Runnable> task) { + DCHECK(current() == this); + MOZ_ASSERT(NS_IsMainThread()); + + PendingTask pending_task(Move(task), false); + deferred_non_nestable_work_queue_.push(Move(pending_task)); +} + +// Possibly called on a background thread! +void MessageLoop::PostTask_Helper(already_AddRefed<Runnable> task, int delay_ms) { + if (nsIEventTarget* target = pump_->GetXPCOMThread()) { + nsresult rv; + if (delay_ms) { + rv = target->DelayedDispatch(Move(task), delay_ms); + } else { + rv = target->Dispatch(Move(task), 0); + } + MOZ_ALWAYS_SUCCEEDS(rv); + return; + } + + PendingTask pending_task(Move(task), true); + + if (delay_ms > 0) { + pending_task.delayed_run_time = + TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms); + } else { + DCHECK(delay_ms == 0) << "delay should not be negative"; + } + + // Warning: Don't try to short-circuit, and handle this thread's tasks more + // directly, as it could starve handling of foreign threads. Put every task + // into this queue. + + RefPtr<base::MessagePump> pump; + { + AutoLock locked(incoming_queue_lock_); + incoming_queue_.push(Move(pending_task)); + pump = pump_; + } + // Since the incoming_queue_ may contain a task that destroys this message + // loop, we cannot exit incoming_queue_lock_ until we are done with |this|. + // We use a stack-based reference to the message pump so that we can call + // ScheduleWork outside of incoming_queue_lock_. + + pump->ScheduleWork(); +} + +void MessageLoop::SetNestableTasksAllowed(bool allowed) { + if (nestable_tasks_allowed_ != allowed) { + nestable_tasks_allowed_ = allowed; + if (!nestable_tasks_allowed_) + return; + // Start the native pump if we are not already pumping. + pump_->ScheduleWorkForNestedLoop(); + } +} + +void MessageLoop::ScheduleWork() { + // Start the native pump if we are not already pumping. + pump_->ScheduleWork(); +} + +bool MessageLoop::NestableTasksAllowed() const { + return nestable_tasks_allowed_; +} + +//------------------------------------------------------------------------------ + +void MessageLoop::RunTask(already_AddRefed<Runnable> aTask) { + DCHECK(nestable_tasks_allowed_); + // Execute the task and assume the worst: It is probably not reentrant. + nestable_tasks_allowed_ = false; + + RefPtr<Runnable> task = aTask; + task->Run(); + task = nullptr; + + nestable_tasks_allowed_ = true; +} + +bool MessageLoop::DeferOrRunPendingTask(PendingTask&& pending_task) { + if (pending_task.nestable || state_->run_depth <= run_depth_base_) { + RunTask(pending_task.task.forget()); + // Show that we ran a task (Note: a new one might arrive as a + // consequence!). + return true; + } + + // We couldn't run the task now because we're in a nested message loop + // and the task isn't nestable. + deferred_non_nestable_work_queue_.push(Move(pending_task)); + return false; +} + +void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) { + // Move to the delayed work queue. Initialize the sequence number + // before inserting into the delayed_work_queue_. The sequence number + // is used to faciliate FIFO sorting when two tasks have the same + // delayed_run_time value. + PendingTask new_pending_task(pending_task); + new_pending_task.sequence_num = next_sequence_num_++; + delayed_work_queue_.push(Move(new_pending_task)); +} + +void MessageLoop::ReloadWorkQueue() { + // We can improve performance of our loading tasks from incoming_queue_ to + // work_queue_ by waiting until the last minute (work_queue_ is empty) to + // load. That reduces the number of locks-per-task significantly when our + // queues get large. + if (!work_queue_.empty()) + return; // Wait till we *really* need to lock and load. + + // Acquire all we can from the inter-thread queue with one lock acquisition. + { + AutoLock lock(incoming_queue_lock_); + if (incoming_queue_.empty()) + return; + std::swap(incoming_queue_, work_queue_); + DCHECK(incoming_queue_.empty()); + } +} + +bool MessageLoop::DeletePendingTasks() { + MOZ_ASSERT(work_queue_.empty()); + bool did_work = !deferred_non_nestable_work_queue_.empty(); + while (!deferred_non_nestable_work_queue_.empty()) { + deferred_non_nestable_work_queue_.pop(); + } + did_work |= !delayed_work_queue_.empty(); + while (!delayed_work_queue_.empty()) { + delayed_work_queue_.pop(); + } + return did_work; +} + +bool MessageLoop::DoWork() { + if (!nestable_tasks_allowed_) { + // Task can't be executed right now. + return false; + } + + for (;;) { + ReloadWorkQueue(); + if (work_queue_.empty()) + break; + + // Execute oldest task. + do { + PendingTask pending_task = Move(work_queue_.front()); + work_queue_.pop(); + if (!pending_task.delayed_run_time.is_null()) { + // NB: Don't move, because we use this later! + AddToDelayedWorkQueue(pending_task); + // If we changed the topmost task, then it is time to re-schedule. + if (delayed_work_queue_.top().task == pending_task.task) + pump_->ScheduleDelayedWork(pending_task.delayed_run_time); + } else { + if (DeferOrRunPendingTask(Move(pending_task))) + return true; + } + } while (!work_queue_.empty()); + } + + // Nothing happened. + return false; +} + +bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) { + if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) { + *next_delayed_work_time = TimeTicks(); + return false; + } + + if (delayed_work_queue_.top().delayed_run_time > TimeTicks::Now()) { + *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; + return false; + } + + PendingTask pending_task = delayed_work_queue_.top(); + delayed_work_queue_.pop(); + + if (!delayed_work_queue_.empty()) + *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; + + return DeferOrRunPendingTask(Move(pending_task)); +} + +bool MessageLoop::DoIdleWork() { + if (ProcessNextDelayedNonNestableTask()) + return true; + + if (state_->quit_received) + pump_->Quit(); + + return false; +} + +//------------------------------------------------------------------------------ +// MessageLoop::AutoRunState + +MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) { + // Make the loop reference us. + previous_state_ = loop_->state_; + if (previous_state_) { + run_depth = previous_state_->run_depth + 1; + } else { + run_depth = 1; + } + loop_->state_ = this; + + // Initialize the other fields: + quit_received = false; +#if defined(OS_WIN) + dispatcher = NULL; +#endif +} + +MessageLoop::AutoRunState::~AutoRunState() { + loop_->state_ = previous_state_; +} + +//------------------------------------------------------------------------------ +// MessageLoop::PendingTask + +bool MessageLoop::PendingTask::operator<(const PendingTask& other) const { + // Since the top of a priority queue is defined as the "greatest" element, we + // need to invert the comparison here. We want the smaller time to be at the + // top of the heap. + + if (delayed_run_time < other.delayed_run_time) + return false; + + if (delayed_run_time > other.delayed_run_time) + return true; + + // If the times happen to match, then we use the sequence number to decide. + // Compare the difference to support integer roll-over. + return (sequence_num - other.sequence_num) > 0; +} + +//------------------------------------------------------------------------------ +// MessageLoopForUI + +#if defined(OS_WIN) + +void MessageLoopForUI::Run(Dispatcher* dispatcher) { + AutoRunState save_state(this); + state_->dispatcher = dispatcher; + RunHandler(); +} + +void MessageLoopForUI::AddObserver(Observer* observer) { + pump_win()->AddObserver(observer); +} + +void MessageLoopForUI::RemoveObserver(Observer* observer) { + pump_win()->RemoveObserver(observer); +} + +void MessageLoopForUI::WillProcessMessage(const MSG& message) { + pump_win()->WillProcessMessage(message); +} +void MessageLoopForUI::DidProcessMessage(const MSG& message) { + pump_win()->DidProcessMessage(message); +} +void MessageLoopForUI::PumpOutPendingPaintMessages() { + pump_ui()->PumpOutPendingPaintMessages(); +} + +#endif // defined(OS_WIN) + +//------------------------------------------------------------------------------ +// MessageLoopForIO + +#if defined(OS_WIN) + +void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) { + pump_io()->RegisterIOHandler(file, handler); +} + +bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { + return pump_io()->WaitForIOCompletion(timeout, filter); +} + +#elif defined(OS_POSIX) + +bool MessageLoopForIO::WatchFileDescriptor(int fd, + bool persistent, + Mode mode, + FileDescriptorWatcher *controller, + Watcher *delegate) { + return pump_libevent()->WatchFileDescriptor( + fd, + persistent, + static_cast<base::MessagePumpLibevent::Mode>(mode), + controller, + delegate); +} + +bool +MessageLoopForIO::CatchSignal(int sig, + SignalEvent* sigevent, + SignalWatcher* delegate) +{ + return pump_libevent()->CatchSignal(sig, sigevent, delegate); +} + +#endif diff --git a/ipc/chromium/src/base/message_loop.h b/ipc/chromium/src/base/message_loop.h new file mode 100644 index 0000000000..c07d7e0798 --- /dev/null +++ b/ipc/chromium/src/base/message_loop.h @@ -0,0 +1,558 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_MESSAGE_LOOP_H_ +#define BASE_MESSAGE_LOOP_H_ + +#include <deque> +#include <queue> +#include <string> +#include <vector> +#include <map> + +#include "base/lock.h" +#include "base/message_pump.h" +#include "base/observer_list.h" + +#if defined(OS_WIN) +// We need this to declare base::MessagePumpWin::Dispatcher, which we should +// really just eliminate. +#include "base/message_pump_win.h" +#elif defined(OS_POSIX) +#include "base/message_pump_libevent.h" +#endif + +#include "nsAutoPtr.h" +#include "nsThreadUtils.h" + +class nsIThread; + +namespace mozilla { +namespace ipc { + +class DoWorkRunnable; + +} /* namespace ipc */ +} /* namespace mozilla */ + +// A MessageLoop is used to process events for a particular thread. There is +// at most one MessageLoop instance per thread. +// +// Events include at a minimum Task instances submitted to PostTask or those +// managed by TimerManager. Depending on the type of message pump used by the +// MessageLoop other events such as UI messages may be processed. On Windows +// APC calls (as time permits) and signals sent to a registered set of HANDLEs +// may also be processed. +// +// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called +// on the thread where the MessageLoop's Run method executes. +// +// NOTE: MessageLoop has task reentrancy protection. This means that if a +// task is being processed, a second task cannot start until the first task is +// finished. Reentrancy can happen when processing a task, and an inner +// message pump is created. That inner pump then processes native messages +// which could implicitly start an inner task. Inner message pumps are created +// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions +// (DoDragDrop), printer functions (StartDoc) and *many* others. +// +// Sample workaround when inner task processing is needed: +// bool old_state = MessageLoop::current()->NestableTasksAllowed(); +// MessageLoop::current()->SetNestableTasksAllowed(true); +// HRESULT hr = DoDragDrop(...); // Implicitly runs a modal message loop here. +// MessageLoop::current()->SetNestableTasksAllowed(old_state); +// // Process hr (the result returned by DoDragDrop(). +// +// Please be SURE your task is reentrant (nestable) and all global variables +// are stable and accessible before calling SetNestableTasksAllowed(true). +// +class MessageLoop : public base::MessagePump::Delegate { + + friend class mozilla::ipc::DoWorkRunnable; + +public: + // A DestructionObserver is notified when the current MessageLoop is being + // destroyed. These obsevers are notified prior to MessageLoop::current() + // being changed to return NULL. This gives interested parties the chance to + // do final cleanup that depends on the MessageLoop. + // + // NOTE: Any tasks posted to the MessageLoop during this notification will + // not be run. Instead, they will be deleted. + // + class DestructionObserver { + public: + virtual ~DestructionObserver() {} + virtual void WillDestroyCurrentMessageLoop() = 0; + }; + + // Add a DestructionObserver, which will start receiving notifications + // immediately. + void AddDestructionObserver(DestructionObserver* destruction_observer); + + // Remove a DestructionObserver. It is safe to call this method while a + // DestructionObserver is receiving a notification callback. + void RemoveDestructionObserver(DestructionObserver* destruction_observer); + + // The "PostTask" family of methods call the task's Run method asynchronously + // from within a message loop at some point in the future. + // + // With the PostTask variant, tasks are invoked in FIFO order, inter-mixed + // with normal UI or IO event processing. With the PostDelayedTask variant, + // tasks are called after at least approximately 'delay_ms' have elapsed. + // + // The NonNestable variants work similarly except that they promise never to + // dispatch the task from a nested invocation of MessageLoop::Run. Instead, + // such tasks get deferred until the top-most MessageLoop::Run is executing. + // + // The MessageLoop takes ownership of the Task, and deletes it after it has + // been Run(). + // + // NOTE: These methods may be called on any thread. The Task will be invoked + // on the thread that executes MessageLoop::Run(). + + void PostTask(already_AddRefed<mozilla::Runnable> task); + + void PostDelayedTask(already_AddRefed<mozilla::Runnable> task, int delay_ms); + + // PostIdleTask is not thread safe and should be called on this thread + void PostIdleTask(already_AddRefed<mozilla::Runnable> task); + + // Run the message loop. + void Run(); + + // Signals the Run method to return after it is done processing all pending + // messages. This method may only be called on the same thread that called + // Run, and Run must still be on the call stack. + // + // Use QuitTask if you need to Quit another thread's MessageLoop, but note + // that doing so is fairly dangerous if the target thread makes nested calls + // to MessageLoop::Run. The problem being that you won't know which nested + // run loop you are quiting, so be careful! + // + void Quit(); + + // Invokes Quit on the current MessageLoop when run. Useful to schedule an + // arbitrary MessageLoop to Quit. + class QuitTask : public mozilla::Runnable { + public: + NS_IMETHOD Run() override { + MessageLoop::current()->Quit(); + return NS_OK; + } + }; + + // A MessageLoop has a particular type, which indicates the set of + // asynchronous events it may process in addition to tasks and timers. + // + // TYPE_DEFAULT + // This type of ML only supports tasks and timers. + // + // TYPE_UI + // This type of ML also supports native UI events (e.g., Windows messages). + // See also MessageLoopForUI. + // + // TYPE_IO + // This type of ML also supports asynchronous IO. See also + // MessageLoopForIO. + // + // TYPE_MOZILLA_CHILD + // This type of ML is used in Mozilla child processes which initialize + // XPCOM and use the gecko event loop. + // + // TYPE_MOZILLA_PARENT + // This type of ML is used in Mozilla parent processes which initialize + // XPCOM and use the gecko event loop. + // + // TYPE_MOZILLA_NONMAINTHREAD + // This type of ML is used in Mozilla parent processes which initialize + // XPCOM and use the nsThread event loop. + // + // TYPE_MOZILLA_NONMAINUITHREAD + // This type of ML is used in Mozilla processes which initialize XPCOM + // and use TYPE_UI loop logic. + // + enum Type { + TYPE_DEFAULT, + TYPE_UI, + TYPE_IO, + TYPE_MOZILLA_CHILD, + TYPE_MOZILLA_PARENT, + TYPE_MOZILLA_NONMAINTHREAD, + TYPE_MOZILLA_NONMAINUITHREAD + }; + + // Normally, it is not necessary to instantiate a MessageLoop. Instead, it + // is typical to make use of the current thread's MessageLoop instance. + explicit MessageLoop(Type type = TYPE_DEFAULT, nsIThread* aThread = nullptr); + ~MessageLoop(); + + // Returns the type passed to the constructor. + Type type() const { return type_; } + + // Unique, non-repeating ID for this message loop. + int32_t id() const { return id_; } + + // Optional call to connect the thread name with this loop. + void set_thread_name(const std::string& aThreadName) { + DCHECK(thread_name_.empty()) << "Should not rename this thread!"; + thread_name_ = aThreadName; + } + const std::string& thread_name() const { return thread_name_; } + + // Returns the MessageLoop object for the current thread, or null if none. + static MessageLoop* current(); + + // Enables or disables the recursive task processing. This happens in the case + // of recursive message loops. Some unwanted message loop may occurs when + // using common controls or printer functions. By default, recursive task + // processing is disabled. + // + // The specific case where tasks get queued is: + // - The thread is running a message loop. + // - It receives a task #1 and execute it. + // - The task #1 implicitly start a message loop, like a MessageBox in the + // unit test. This can also be StartDoc or GetSaveFileName. + // - The thread receives a task #2 before or while in this second message + // loop. + // - With NestableTasksAllowed set to true, the task #2 will run right away. + // Otherwise, it will get executed right after task #1 completes at "thread + // message loop level". + void SetNestableTasksAllowed(bool allowed); + void ScheduleWork(); + bool NestableTasksAllowed() const; + + // Enables or disables the restoration during an exception of the unhandled + // exception filter that was active when Run() was called. This can happen + // if some third party code call SetUnhandledExceptionFilter() and never + // restores the previous filter. + void set_exception_restoration(bool restore) { + exception_restoration_ = restore; + } + +#if defined(OS_WIN) + void set_os_modal_loop(bool os_modal_loop) { + os_modal_loop_ = os_modal_loop; + } + + bool & os_modal_loop() { + return os_modal_loop_; + } +#endif // OS_WIN + + // Set the timeouts for background hang monitoring. + // A value of 0 indicates there is no timeout. + void set_hang_timeouts(uint32_t transient_timeout_ms, + uint32_t permanent_timeout_ms) { + transient_hang_timeout_ = transient_timeout_ms; + permanent_hang_timeout_ = permanent_timeout_ms; + } + uint32_t transient_hang_timeout() const { + return transient_hang_timeout_; + } + uint32_t permanent_hang_timeout() const { + return permanent_hang_timeout_; + } + + //---------------------------------------------------------------------------- + protected: + struct RunState { + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // Used to record that Quit() was called, or that we should quit the pump + // once it becomes idle. + bool quit_received; + +#if defined(OS_WIN) + base::MessagePumpWin::Dispatcher* dispatcher; +#endif + }; + + class AutoRunState : RunState { + public: + explicit AutoRunState(MessageLoop* loop); + ~AutoRunState(); + private: + MessageLoop* loop_; + RunState* previous_state_; + }; + + // This structure is copied around by value. + struct PendingTask { + RefPtr<mozilla::Runnable> task; // The task to run. + base::TimeTicks delayed_run_time; // The time when the task should be run. + int sequence_num; // Secondary sort key for run time. + bool nestable; // OK to dispatch from a nested loop. + + PendingTask(already_AddRefed<mozilla::Runnable> aTask, bool aNestable) + : task(aTask), sequence_num(0), nestable(aNestable) { + } + + PendingTask(PendingTask&& aOther) + : task(aOther.task.forget()), + delayed_run_time(aOther.delayed_run_time), + sequence_num(aOther.sequence_num), + nestable(aOther.nestable) { + } + + // std::priority_queue<T>::top is dumb, so we have to have this. + PendingTask(const PendingTask& aOther) + : task(aOther.task), + delayed_run_time(aOther.delayed_run_time), + sequence_num(aOther.sequence_num), + nestable(aOther.nestable) { + } + PendingTask& operator=(const PendingTask& aOther) + { + task = aOther.task; + delayed_run_time = aOther.delayed_run_time; + sequence_num = aOther.sequence_num; + nestable = aOther.nestable; + return *this; + } + + // Used to support sorting. + bool operator<(const PendingTask& other) const; + }; + + typedef std::queue<PendingTask> TaskQueue; + typedef std::priority_queue<PendingTask> DelayedTaskQueue; + +#if defined(OS_WIN) + base::MessagePumpWin* pump_win() { + return static_cast<base::MessagePumpWin*>(pump_.get()); + } +#elif defined(OS_POSIX) + base::MessagePumpLibevent* pump_libevent() { + return static_cast<base::MessagePumpLibevent*>(pump_.get()); + } +#endif + + // A function to encapsulate all the exception handling capability in the + // stacks around the running of a main message loop. It will run the message + // loop in a SEH try block or not depending on the set_SEH_restoration() + // flag. + void RunHandler(); + + // A surrounding stack frame around the running of the message loop that + // supports all saving and restoring of state, as is needed for any/all (ugly) + // recursive calls. + void RunInternal(); + + // Called to process any delayed non-nestable tasks. + bool ProcessNextDelayedNonNestableTask(); + + //---------------------------------------------------------------------------- + // Run a work_queue_ task or new_task, and delete it (if it was processed by + // PostTask). If there are queued tasks, the oldest one is executed and + // new_task is queued. new_task is optional and can be NULL. In this NULL + // case, the method will run one pending task (if any exist). Returns true if + // it executes a task. Queued tasks accumulate only when there is a + // non-nestable task currently processing, in which case the new_task is + // appended to the list work_queue_. Such re-entrancy generally happens when + // an unrequested message pump (typical of a native dialog) is executing in + // the context of a task. + bool QueueOrRunTask(already_AddRefed<mozilla::Runnable> new_task); + + // Runs the specified task and deletes it. + void RunTask(already_AddRefed<mozilla::Runnable> task); + + // Calls RunTask or queues the pending_task on the deferred task list if it + // cannot be run right now. Returns true if the task was run. + bool DeferOrRunPendingTask(PendingTask&& pending_task); + + // Adds the pending task to delayed_work_queue_. + void AddToDelayedWorkQueue(const PendingTask& pending_task); + + // Load tasks from the incoming_queue_ into work_queue_ if the latter is + // empty. The former requires a lock to access, while the latter is directly + // accessible on this thread. + void ReloadWorkQueue(); + + // Delete tasks that haven't run yet without running them. Used in the + // destructor to make sure all the task's destructors get called. Returns + // true if some work was done. + bool DeletePendingTasks(); + + // Post a task to our incomming queue. + void PostTask_Helper(already_AddRefed<mozilla::Runnable> task, int delay_ms); + + // base::MessagePump::Delegate methods: + virtual bool DoWork() override; + virtual bool DoDelayedWork(base::TimeTicks* next_delayed_work_time) override; + virtual bool DoIdleWork() override; + + Type type_; + int32_t id_; + + // A list of tasks that need to be processed by this instance. Note that + // this queue is only accessed (push/pop) by our current thread. + TaskQueue work_queue_; + + // Contains delayed tasks, sorted by their 'delayed_run_time' property. + DelayedTaskQueue delayed_work_queue_; + + // A queue of non-nestable tasks that we had to defer because when it came + // time to execute them we were in a nested message loop. They will execute + // once we're out of nested message loops. + TaskQueue deferred_non_nestable_work_queue_; + + RefPtr<base::MessagePump> pump_; + + base::ObserverList<DestructionObserver> destruction_observers_; + + // A recursion block that prevents accidentally running additonal tasks when + // insider a (accidentally induced?) nested message pump. + bool nestable_tasks_allowed_; + + bool exception_restoration_; + + std::string thread_name_; + + // A null terminated list which creates an incoming_queue of tasks that are + // aquired under a mutex for processing on this instance's thread. These tasks + // have not yet been sorted out into items for our work_queue_ vs items that + // will be handled by the TimerManager. + TaskQueue incoming_queue_; + // Protect access to incoming_queue_. + Lock incoming_queue_lock_; + + RunState* state_; + int run_depth_base_; + +#if defined(OS_WIN) + // Should be set to true before calling Windows APIs like TrackPopupMenu, etc + // which enter a modal message loop. + bool os_modal_loop_; +#endif + + // Timeout values for hang monitoring + uint32_t transient_hang_timeout_; + uint32_t permanent_hang_timeout_; + + // The next sequence number to use for delayed tasks. + int next_sequence_num_; + + DISALLOW_COPY_AND_ASSIGN(MessageLoop); +}; + +//----------------------------------------------------------------------------- +// MessageLoopForUI extends MessageLoop with methods that are particular to a +// MessageLoop instantiated with TYPE_UI. +// +// This class is typically used like so: +// MessageLoopForUI::current()->...call some method... +// +class MessageLoopForUI : public MessageLoop { + public: + explicit MessageLoopForUI(Type aType=TYPE_UI) : MessageLoop(aType) { + } + + // Returns the MessageLoopForUI of the current thread. + static MessageLoopForUI* current() { + MessageLoop* loop = MessageLoop::current(); + if (!loop) + return NULL; + Type type = loop->type(); + DCHECK(type == MessageLoop::TYPE_UI || + type == MessageLoop::TYPE_MOZILLA_PARENT || + type == MessageLoop::TYPE_MOZILLA_CHILD); + return static_cast<MessageLoopForUI*>(loop); + } + +#if defined(OS_WIN) + typedef base::MessagePumpWin::Dispatcher Dispatcher; + typedef base::MessagePumpWin::Observer Observer; + + // Please see MessagePumpWin for definitions of these methods. + void Run(Dispatcher* dispatcher); + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + void WillProcessMessage(const MSG& message); + void DidProcessMessage(const MSG& message); + void PumpOutPendingPaintMessages(); + + protected: + // TODO(rvargas): Make this platform independent. + base::MessagePumpForUI* pump_ui() { + return static_cast<base::MessagePumpForUI*>(pump_.get()); + } +#endif // defined(OS_WIN) +}; + +// Do not add any member variables to MessageLoopForUI! This is important b/c +// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra +// data that you need should be stored on the MessageLoop's pump_ instance. +COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI), + MessageLoopForUI_should_not_have_extra_member_variables); + +//----------------------------------------------------------------------------- +// MessageLoopForIO extends MessageLoop with methods that are particular to a +// MessageLoop instantiated with TYPE_IO. +// +// This class is typically used like so: +// MessageLoopForIO::current()->...call some method... +// +class MessageLoopForIO : public MessageLoop { + public: + MessageLoopForIO() : MessageLoop(TYPE_IO) { + } + + // Returns the MessageLoopForIO of the current thread. + static MessageLoopForIO* current() { + MessageLoop* loop = MessageLoop::current(); + DCHECK_EQ(MessageLoop::TYPE_IO, loop->type()); + return static_cast<MessageLoopForIO*>(loop); + } + +#if defined(OS_WIN) + typedef base::MessagePumpForIO::IOHandler IOHandler; + typedef base::MessagePumpForIO::IOContext IOContext; + + // Please see MessagePumpWin for definitions of these methods. + void RegisterIOHandler(HANDLE file_handle, IOHandler* handler); + bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); + + protected: + // TODO(rvargas): Make this platform independent. + base::MessagePumpForIO* pump_io() { + return static_cast<base::MessagePumpForIO*>(pump_.get()); + } + +#elif defined(OS_POSIX) + typedef base::MessagePumpLibevent::Watcher Watcher; + typedef base::MessagePumpLibevent::FileDescriptorWatcher + FileDescriptorWatcher; + typedef base::LineWatcher LineWatcher; + + enum Mode { + WATCH_READ = base::MessagePumpLibevent::WATCH_READ, + WATCH_WRITE = base::MessagePumpLibevent::WATCH_WRITE, + WATCH_READ_WRITE = base::MessagePumpLibevent::WATCH_READ_WRITE + }; + + // Please see MessagePumpLibevent for definition. + bool WatchFileDescriptor(int fd, + bool persistent, + Mode mode, + FileDescriptorWatcher *controller, + Watcher *delegate); + + typedef base::MessagePumpLibevent::SignalEvent SignalEvent; + typedef base::MessagePumpLibevent::SignalWatcher SignalWatcher; + bool CatchSignal(int sig, + SignalEvent* sigevent, + SignalWatcher* delegate); + +#endif // defined(OS_POSIX) +}; + +// Do not add any member variables to MessageLoopForIO! This is important b/c +// MessageLoopForIO is often allocated via MessageLoop(TYPE_IO). Any extra +// data that you need should be stored on the MessageLoop's pump_ instance. +COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForIO), + MessageLoopForIO_should_not_have_extra_member_variables); + +#endif // BASE_MESSAGE_LOOP_H_ diff --git a/ipc/chromium/src/base/message_pump.h b/ipc/chromium/src/base/message_pump.h new file mode 100644 index 0000000000..08bdbe30a5 --- /dev/null +++ b/ipc/chromium/src/base/message_pump.h @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_MESSAGE_PUMP_H_ +#define BASE_MESSAGE_PUMP_H_ + +#include "nsISupportsImpl.h" + +class nsIEventTarget; + +namespace base { + +class TimeTicks; + +class MessagePump { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessagePump) + + // Please see the comments above the Run method for an illustration of how + // these delegate methods are used. + class Delegate { + public: + virtual ~Delegate() {} + + // Called from within Run in response to ScheduleWork or when the message + // pump would otherwise call DoDelayedWork. Returns true to indicate that + // work was done. DoDelayedWork will not be called if DoWork returns true. + virtual bool DoWork() = 0; + + // Called from within Run in response to ScheduleDelayedWork or when the + // message pump would otherwise sleep waiting for more work. Returns true + // to indicate that delayed work was done. DoIdleWork will not be called + // if DoDelayedWork returns true. Upon return |next_delayed_work_time| + // indicates the time when DoDelayedWork should be called again. If + // |next_delayed_work_time| is null (per Time::is_null), then the queue of + // future delayed work (timer events) is currently empty, and no additional + // calls to this function need to be scheduled. + virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0; + + // Called from within Run just before the message pump goes to sleep. + // Returns true to indicate that idle work was done. + virtual bool DoIdleWork() = 0; + }; + + // The Run method is called to enter the message pump's run loop. + // + // Within the method, the message pump is responsible for processing native + // messages as well as for giving cycles to the delegate periodically. The + // message pump should take care to mix delegate callbacks with native + // message processing so neither type of event starves the other of cycles. + // + // The anatomy of a typical run loop: + // + // for (;;) { + // bool did_work = DoInternalWork(); + // if (should_quit_) + // break; + // + // did_work |= delegate_->DoWork(); + // if (should_quit_) + // break; + // + // did_work |= delegate_->DoDelayedWork(); + // if (should_quit_) + // break; + // + // if (did_work) + // continue; + // + // did_work = delegate_->DoIdleWork(); + // if (should_quit_) + // break; + // + // if (did_work) + // continue; + // + // WaitForWork(); + // } + // + // Here, DoInternalWork is some private method of the message pump that is + // responsible for dispatching the next UI message or notifying the next IO + // completion (for example). WaitForWork is a private method that simply + // blocks until there is more work of any type to do. + // + // Notice that the run loop cycles between calling DoInternalWork, DoWork, + // and DoDelayedWork methods. This helps ensure that neither work queue + // starves the other. This is important for message pumps that are used to + // drive animations, for example. + // + // Notice also that after each callout to foreign code, the run loop checks + // to see if it should quit. The Quit method is responsible for setting this + // flag. No further work is done once the quit flag is set. + // + // NOTE: Care must be taken to handle Run being called again from within any + // of the callouts to foreign code. Native message pumps may also need to + // deal with other native message pumps being run outside their control + // (e.g., the MessageBox API on Windows pumps UI messages!). To be specific, + // the callouts (DoWork and DoDelayedWork) MUST still be provided even in + // nested sub-loops that are "seemingly" outside the control of this message + // pump. DoWork in particular must never be starved for time slices unless + // it returns false (meaning it has run out of things to do). + // + virtual void Run(Delegate* delegate) = 0; + + // Quit immediately from the most recently entered run loop. This method may + // only be used on the thread that called Run. + virtual void Quit() = 0; + + // Schedule a DoWork callback to happen reasonably soon. Does nothing if a + // DoWork callback is already scheduled. This method may be called from any + // thread. Once this call is made, DoWork should not be "starved" at least + // until it returns a value of false. + virtual void ScheduleWork() = 0; + + // This method may only called from the thread that called Run. + // + // Ensure that DoWork will be called if a nested loop is entered. + // If a MessagePump can already guarantee that DoWork will be called + // "reasonably soon", this method can be a no-op to avoid expensive + // atomic tests and/or syscalls required for ScheduleWork(). + virtual void ScheduleWorkForNestedLoop() { ScheduleWork(); }; + + // Schedule a DoDelayedWork callback to happen at the specified time, + // cancelling any pending DoDelayedWork callback. This method may only be + // used on the thread that called Run. + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0; + + // If returned, just use the nsThread. + virtual nsIEventTarget* GetXPCOMThread() + { + return nullptr; + } + +protected: + virtual ~MessagePump() {}; +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_H_ diff --git a/ipc/chromium/src/base/message_pump_android.cc b/ipc/chromium/src/base/message_pump_android.cc new file mode 100644 index 0000000000..2d8f310d15 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_android.cc @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 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 "base/message_pump_android.h" + +#include <fcntl.h> +#include <math.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace mozilla { +bool ProcessNextEvent(); +void NotifyEvent(); +} + +namespace base { + +MessagePumpForUI::MessagePumpForUI() + : state_(NULL) + , pump(*this) +{ +} + +MessagePumpForUI::~MessagePumpForUI() { +} + +MessagePumpAndroid::MessagePumpAndroid(MessagePumpForUI &aPump) + : pump(aPump) +{ +} + +MessagePumpAndroid::~MessagePumpAndroid() +{ +} + +void MessagePumpForUI::Run(Delegate* delegate) { + RunState state; + state.delegate = delegate; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunAllPending() works correctly. + state.more_work_is_plausible = true; + + RunState* previous_state = state_; + state_ = &state; + + // We run our own loop instead of using g_main_loop_quit in one of the + // callbacks. This is so we only quit our own loops, and we don't quit + // nested loops run by others. TODO(deanm): Is this what we want? + + while (!state_->should_quit) { + mozilla::ProcessNextEvent(); + if (work_scheduled) { + work_scheduled = false; + HandleDispatch(); + } + } + + state_ = previous_state; +} + +void MessagePumpForUI::HandleDispatch() { + // We should only ever have a single message on the wakeup pipe, since we + // are only signaled when the queue went from empty to non-empty. The qApp + // poll will tell us whether there was data, so this read shouldn't block. + if (state_->should_quit) + return; + + state_->more_work_is_plausible = false; + + if (state_->delegate->DoWork()) + state_->more_work_is_plausible = true; + + if (state_->should_quit) + return; + + if (state_->delegate->DoDelayedWork(&delayed_work_time_)) + state_->more_work_is_plausible = true; + if (state_->should_quit) + return; + + // Don't do idle work if we think there are more important things + // that we could be doing. + if (state_->more_work_is_plausible) + return; + + if (state_->delegate->DoIdleWork()) + state_->more_work_is_plausible = true; + if (state_->should_quit) + return; +} + +void MessagePumpForUI::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpForUI::ScheduleWork() { + // This can be called on any thread, so we don't want to touch any state + // variables as we would then need locks all over. This ensures that if + // we are sleeping in a poll that we will wake up. + work_scheduled = true; + mozilla::NotifyEvent(); +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // We need to wake up the loop in case the poll timeout needs to be + // adjusted. This will cause us to try to do work, but that's ok. + delayed_work_time_ = delayed_work_time; + ScheduleWork(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_android.h b/ipc/chromium/src/base/message_pump_android.h new file mode 100644 index 0000000000..ac9801843f --- /dev/null +++ b/ipc/chromium/src/base/message_pump_android.h @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 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 BASE_MESSAGE_PUMP_ANDROID_H_ +#define BASE_MESSAGE_PUMP_ANDROID_H_ + +#include "base/message_pump.h" +#include "base/time.h" + +namespace base { + +class MessagePumpForUI; + +class MessagePumpAndroid { + + public: + MessagePumpAndroid(MessagePumpForUI &pump); + ~MessagePumpAndroid(); + + private: + base::MessagePumpForUI &pump; +}; + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// Android +class MessagePumpForUI : public MessagePump { + + public: + MessagePumpForUI(); + ~MessagePumpForUI(); + + virtual void Run(Delegate* delegate); + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. + // HandleDispatch is called after the poll has completed. + void HandleDispatch(); + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState { + Delegate* delegate; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // Used internally for controlling whether we want a message pump + // iteration to be blocking or not. + bool more_work_is_plausible; + }; + + RunState* state_; + + // This is the time when we need to do delayed work. + TimeTicks delayed_work_time_; + + bool work_scheduled; + + // MessagePump implementation for Android based on the GLib implement. + MessagePumpAndroid pump; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_ANDROID_H_ diff --git a/ipc/chromium/src/base/message_pump_default.cc b/ipc/chromium/src/base/message_pump_default.cc new file mode 100644 index 0000000000..ef37b6296b --- /dev/null +++ b/ipc/chromium/src/base/message_pump_default.cc @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/message_pump_default.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_nsautorelease_pool.h" +#include "GeckoProfiler.h" + +#include "mozilla/BackgroundHangMonitor.h" + +namespace base { + +MessagePumpDefault::MessagePumpDefault() + : keep_running_(true), + event_(false, false) { +} + +void MessagePumpDefault::Run(Delegate* delegate) { + DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + + const MessageLoop* const loop = MessageLoop::current(); + mozilla::BackgroundHangMonitor hangMonitor( + loop->thread_name().c_str(), + loop->transient_hang_timeout(), + loop->permanent_hang_timeout()); + + for (;;) { + ScopedNSAutoreleasePool autorelease_pool; + + hangMonitor.NotifyActivity(); + bool did_work = delegate->DoWork(); + if (!keep_running_) + break; + + hangMonitor.NotifyActivity(); + did_work |= delegate->DoDelayedWork(&delayed_work_time_); + if (!keep_running_) + break; + + if (did_work) + continue; + + hangMonitor.NotifyActivity(); + did_work = delegate->DoIdleWork(); + if (!keep_running_) + break; + + if (did_work) + continue; + + if (delayed_work_time_.is_null()) { + hangMonitor.NotifyWait(); + PROFILER_LABEL("MessagePump", "Wait", + js::ProfileEntry::Category::OTHER); + { + GeckoProfilerSleepRAII profiler_sleep; + event_.Wait(); + } + } else { + TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); + if (delay > TimeDelta()) { + hangMonitor.NotifyWait(); + PROFILER_LABEL("MessagePump", "Wait", + js::ProfileEntry::Category::OTHER); + { + GeckoProfilerSleepRAII profiler_sleep; + event_.TimedWait(delay); + } + } else { + // It looks like delayed_work_time_ indicates a time in the past, so we + // need to call DoDelayedWork now. + delayed_work_time_ = TimeTicks(); + } + } + // Since event_ is auto-reset, we don't need to do anything special here + // other than service each delegate method. + } + + keep_running_ = true; +} + +void MessagePumpDefault::Quit() { + keep_running_ = false; +} + +void MessagePumpDefault::ScheduleWork() { + // Since this can be called on any thread, we need to ensure that our Run + // loop wakes up. + event_.Signal(); +} + +void MessagePumpDefault::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_default.h b/ipc/chromium/src/base/message_pump_default.h new file mode 100644 index 0000000000..98c204e623 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_default.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_MESSAGE_PUMP_DEFAULT_H_ +#define BASE_MESSAGE_PUMP_DEFAULT_H_ + +#include "base/message_pump.h" +#include "base/time.h" +#include "base/waitable_event.h" + +namespace base { + +class MessagePumpDefault : public MessagePump { + public: + MessagePumpDefault(); + ~MessagePumpDefault() {} + + // MessagePump methods: + virtual void Run(Delegate* delegate); + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + protected: + // This flag is set to false when Run should return. + bool keep_running_; + + // Used to sleep until there is more work to do. + WaitableEvent event_; + + // The time at which we should call DoDelayedWork. + TimeTicks delayed_work_time_; + + private: + DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_DEFAULT_H_ diff --git a/ipc/chromium/src/base/message_pump_glib.cc b/ipc/chromium/src/base/message_pump_glib.cc new file mode 100644 index 0000000000..36ebfdd69f --- /dev/null +++ b/ipc/chromium/src/base/message_pump_glib.cc @@ -0,0 +1,341 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/message_pump_glib.h" + +#include <fcntl.h> +#include <math.h> + +#include <gtk/gtk.h> +#include <glib.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace { + +// Return a timeout suitable for the glib loop, -1 to block forever, +// 0 to return right away, or a timeout in milliseconds from now. +int GetTimeIntervalMilliseconds(const base::TimeTicks& from) { + if (from.is_null()) + return -1; + + // Be careful here. TimeDelta has a precision of microseconds, but we want a + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or + // 6? It should be 6 to avoid executing delayed work too early. + int delay = static_cast<int>( + ceil((from - base::TimeTicks::Now()).InMillisecondsF())); + + // If this value is negative, then we need to run delayed work soon. + return delay < 0 ? 0 : delay; +} + +// A brief refresher on GLib: +// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize. +// On each iteration of the GLib pump, it calls each source's Prepare function. +// This function should return TRUE if it wants GLib to call its Dispatch, and +// FALSE otherwise. It can also set a timeout in this case for the next time +// Prepare should be called again (it may be called sooner). +// After the Prepare calls, GLib does a poll to check for events from the +// system. File descriptors can be attached to the sources. The poll may block +// if none of the Prepare calls returned TRUE. It will block indefinitely, or +// by the minimum time returned by a source in Prepare. +// After the poll, GLib calls Check for each source that returned FALSE +// from Prepare. The return value of Check has the same meaning as for Prepare, +// making Check a second chance to tell GLib we are ready for Dispatch. +// Finally, GLib calls Dispatch for each source that is ready. If Dispatch +// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive +// (i.e., you can call Run from them), but Prepare and Check cannot. +// Finalize is called when the source is destroyed. +// NOTE: It is common for subsytems to want to process pending events while +// doing intensive work, for example the flash plugin. They usually use the +// following pattern (recommended by the GTK docs): +// while (gtk_events_pending()) { +// gtk_main_iteration(); +// } +// +// gtk_events_pending just calls g_main_context_pending, which does the +// following: +// - Call prepare on all the sources. +// - Do the poll with a timeout of 0 (not blocking). +// - Call check on all the sources. +// - *Does not* call dispatch on the sources. +// - Return true if any of prepare() or check() returned true. +// +// gtk_main_iteration just calls g_main_context_iteration, which does the whole +// thing, respecting the timeout for the poll (and block, although it is +// expected not to if gtk_events_pending returned true), and call dispatch. +// +// Thus it is important to only return true from prepare or check if we +// actually have events or work to do. We also need to make sure we keep +// internal state consistent so that if prepare/check return true when called +// from gtk_events_pending, they will still return true when called right +// after, from gtk_main_iteration. +// +// For the GLib pump we try to follow the Windows UI pump model: +// - Whenever we receive a wakeup event or the timer for delayed work expires, +// we run DoWork and/or DoDelayedWork. That part will also run in the other +// event pumps. +// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main +// loop, around event handling. + +struct WorkSource : public GSource { + base::MessagePumpForUI* pump; +}; + +gboolean WorkSourcePrepare(GSource* source, + gint* timeout_ms) { + *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare(); + // We always return FALSE, so that our timeout is honored. If we were + // to return TRUE, the timeout would be considered to be 0 and the poll + // would never block. Once the poll is finished, Check will be called. + return FALSE; +} + +gboolean WorkSourceCheck(GSource* source) { + // Only return TRUE if Dispatch should be called. + return static_cast<WorkSource*>(source)->pump->HandleCheck(); +} + +gboolean WorkSourceDispatch(GSource* source, + GSourceFunc unused_func, + gpointer unused_data) { + + static_cast<WorkSource*>(source)->pump->HandleDispatch(); + // Always return TRUE so our source stays registered. + return TRUE; +} + +// I wish these could be const, but g_source_new wants non-const. +GSourceFuncs WorkSourceFuncs = { + WorkSourcePrepare, + WorkSourceCheck, + WorkSourceDispatch, + NULL +}; + +} // namespace + + +namespace base { + +MessagePumpForUI::MessagePumpForUI() + : state_(NULL), + context_(g_main_context_default()), + wakeup_gpollfd_(new GPollFD), + pipe_full_(false) { + // Create our wakeup pipe, which is used to flag when work was scheduled. + int fds[2]; + CHECK(pipe(fds) == 0); + wakeup_pipe_read_ = fds[0]; + wakeup_pipe_write_ = fds[1]; + wakeup_gpollfd_->fd = wakeup_pipe_read_; + wakeup_gpollfd_->events = G_IO_IN; + + work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); + static_cast<WorkSource*>(work_source_)->pump = this; + g_source_add_poll(work_source_, wakeup_gpollfd_.get()); + // Use a low priority so that we let other events in the queue go first. + g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); + // This is needed to allow Run calls inside Dispatch. + g_source_set_can_recurse(work_source_, TRUE); + g_source_attach(work_source_, context_); + gdk_event_handler_set(&EventDispatcher, this, NULL); +} + +MessagePumpForUI::~MessagePumpForUI() { + gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event), + this, NULL); + g_source_destroy(work_source_); + g_source_unref(work_source_); + close(wakeup_pipe_read_); + close(wakeup_pipe_write_); +} + +void MessagePumpForUI::RunWithDispatcher(Delegate* delegate, + Dispatcher* dispatcher) { +#ifndef NDEBUG + // Make sure we only run this on one thread. GTK only has one message pump + // so we can only have one UI loop per process. + static PlatformThreadId thread_id = PlatformThread::CurrentId(); + DCHECK(thread_id == PlatformThread::CurrentId()) << + "Running MessagePumpForUI on two different threads; " + "this is unsupported by GLib!"; +#endif + + RunState state; + state.delegate = delegate; + state.dispatcher = dispatcher; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + state.has_work = false; + + RunState* previous_state = state_; + state_ = &state; + + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunAllPending() works correctly. + bool more_work_is_plausible = true; + + // We run our own loop instead of using g_main_loop_quit in one of the + // callbacks. This is so we only quit our own loops, and we don't quit + // nested loops run by others. TODO(deanm): Is this what we want? + for (;;) { + // Don't block if we think we have more work to do. + bool block = !more_work_is_plausible; + + // g_main_context_iteration returns true if events have been dispatched. + more_work_is_plausible = g_main_context_iteration(context_, block); + if (state_->should_quit) + break; + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) + break; + } + + state_ = previous_state; +} + +// Return the timeout we want passed to poll. +int MessagePumpForUI::HandlePrepare() { + // We know we have work, but we haven't called HandleDispatch yet. Don't let + // the pump block so that we can do some processing. + if (state_ && // state_ may be null during tests. + state_->has_work) + return 0; + + // We don't think we have work to do, but make sure not to block + // longer than the next time we need to run delayed work. + return GetTimeIntervalMilliseconds(delayed_work_time_); +} + +bool MessagePumpForUI::HandleCheck() { + if (!state_) // state_ may be null during tests. + return false; + + // We should only ever have a single message on the wakeup pipe since we only + // write to the pipe when pipe_full_ is false. The glib poll will tell us + // whether there was data, so this read shouldn't block. + if (wakeup_gpollfd_->revents & G_IO_IN) { + pipe_full_ = false; + + char msg; + if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') { + NOTREACHED() << "Error reading from the wakeup pipe."; + } + // Since we ate the message, we need to record that we have more work, + // because HandleCheck() may be called without HandleDispatch being called + // afterwards. + state_->has_work = true; + } + + if (state_->has_work) + return true; + + if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) { + // The timer has expired. That condition will stay true until we process + // that delayed work, so we don't need to record this differently. + return true; + } + + return false; +} + +void MessagePumpForUI::HandleDispatch() { + state_->has_work = false; + if (state_->delegate->DoWork()) { + // NOTE: on Windows at this point we would call ScheduleWork (see + // MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here, + // instead of posting a message on the wakeup pipe, we can avoid the + // syscalls and just signal that we have more work. + state_->has_work = true; + } + + if (state_->should_quit) + return; + + state_->delegate->DoDelayedWork(&delayed_work_time_); +} + +void MessagePumpForUI::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void MessagePumpForUI::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void MessagePumpForUI::WillProcessEvent(GdkEvent* event) { + FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event)); +} + +void MessagePumpForUI::DidProcessEvent(GdkEvent* event) { + FOR_EACH_OBSERVER(Observer, observers_, DidProcessEvent(event)); +} + +void MessagePumpForUI::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpForUI::ScheduleWork() { + bool was_full = pipe_full_.exchange(true); + if (was_full) { + return; + } + + // This can be called on any thread, so we don't want to touch any state + // variables as we would then need locks all over. This ensures that if + // we are sleeping in a poll that we will wake up. + char msg = '!'; + if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) { + NOTREACHED() << "Could not write to the UI message loop wakeup pipe!"; + } +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // We need to wake up the loop in case the poll timeout needs to be + // adjusted. This will cause us to try to do work, but that's ok. + delayed_work_time_ = delayed_work_time; + ScheduleWork(); +} + +// static +void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) { + MessagePumpForUI* message_pump = reinterpret_cast<MessagePumpForUI*>(data); + + message_pump->WillProcessEvent(event); + if (message_pump->state_ && // state_ may be null during tests. + message_pump->state_->dispatcher) { + if (!message_pump->state_->dispatcher->Dispatch(event)) + message_pump->state_->should_quit = true; + } else { + gtk_main_do_event(event); + } + message_pump->DidProcessEvent(event); +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_glib.h b/ipc/chromium/src/base/message_pump_glib.h new file mode 100644 index 0000000000..1d0e057096 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_glib.h @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 BASE_MESSAGE_PUMP_GLIB_H_ +#define BASE_MESSAGE_PUMP_GLIB_H_ + +#include "base/message_pump.h" +#include "base/observer_list.h" +#include "base/time.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Atomics.h" + +typedef union _GdkEvent GdkEvent; +typedef struct _GMainContext GMainContext; +typedef struct _GPollFD GPollFD; +typedef struct _GSource GSource; + +namespace base { + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// OS_LINUX platforms using GLib. +class MessagePumpForUI : public MessagePump { + public: + // Observer is notified prior to a GdkEvent event being dispatched. As + // Observers are notified of every change, they have to be FAST! + class Observer { + public: + virtual ~Observer() {} + + // This method is called before processing a message. + virtual void WillProcessEvent(GdkEvent* event) = 0; + + // This method is called after processing a message. + virtual void DidProcessEvent(GdkEvent* event) = 0; + }; + + // Dispatcher is used during a nested invocation of Run to dispatch events. + // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not + // dispatch events (or invoke gtk_main_do_event), rather every event is + // passed to Dispatcher's Dispatch method for dispatch. It is up to the + // Dispatcher to dispatch, or not, the event. + // + // The nested loop is exited by either posting a quit, or returning false + // from Dispatch. + class Dispatcher { + public: + virtual ~Dispatcher() {} + // Dispatches the event. If true is returned processing continues as + // normal. If false is returned, the nested loop exits immediately. + virtual bool Dispatch(GdkEvent* event) = 0; + }; + + MessagePumpForUI(); + virtual ~MessagePumpForUI(); + + // Like MessagePump::Run, but GdkEvent objects are routed through dispatcher. + virtual void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher); + + virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); } + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. HandlePrepare + // is called during the prepare step of glib, and returns a timeout that + // will be passed to the poll. HandleCheck is called after the poll + // has completed, and returns whether or not HandleDispatch should be called. + // HandleDispatch is called if HandleCheck returned true. + int HandlePrepare(); + bool HandleCheck(); + void HandleDispatch(); + + // Adds an Observer, which will start receiving notifications immediately. + void AddObserver(Observer* observer); + + // Removes an Observer. It is safe to call this method while an Observer is + // receiving a notification callback. + void RemoveObserver(Observer* observer); + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState { + Delegate* delegate; + Dispatcher* dispatcher; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // This keeps the state of whether the pump got signaled that there was new + // work to be done. Since we eat the message on the wake up pipe as soon as + // we get it, we keep that state here to stay consistent. + bool has_work; + }; + + // Invoked from EventDispatcher. Notifies all observers we're about to + // process an event. + void WillProcessEvent(GdkEvent* event); + + // Invoked from EventDispatcher. Notifies all observers we processed an + // event. + void DidProcessEvent(GdkEvent* event); + + // Callback prior to gdk dispatching an event. + static void EventDispatcher(GdkEvent* event, void* data); + + RunState* state_; + + // This is a GLib structure that we can add event sources to. We use the + // default GLib context, which is the one to which all GTK events are + // dispatched. + GMainContext* context_; + + // This is the time when we need to do delayed work. + TimeTicks delayed_work_time_; + + // The work source. It is shared by all calls to Run and destroyed when + // the message pump is destroyed. + GSource* work_source_; + + // We use a wakeup pipe to make sure we'll get out of the glib polling phase + // when another thread has scheduled us to do some work. There is a glib + // mechanism g_main_context_wakeup, but this won't guarantee that our event's + // Dispatch() will be called. + int wakeup_pipe_read_; + int wakeup_pipe_write_; + // Use an autoptr to avoid needing the definition of GPollFD in the header. + mozilla::UniquePtr<GPollFD> wakeup_gpollfd_; + + mozilla::Atomic<bool> pipe_full_; + + // List of observers. + ObserverList<Observer> observers_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_GLIB_H_ diff --git a/ipc/chromium/src/base/message_pump_libevent.cc b/ipc/chromium/src/base/message_pump_libevent.cc new file mode 100644 index 0000000000..3cca238c1e --- /dev/null +++ b/ipc/chromium/src/base/message_pump_libevent.cc @@ -0,0 +1,458 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/message_pump_libevent.h" + +#include <errno.h> +#include <fcntl.h> +#if defined(ANDROID) || defined(OS_POSIX) +#include <unistd.h> +#endif + +#include "eintr_wrapper.h" +#include "base/logging.h" +#include "base/scoped_nsautorelease_pool.h" +#include "base/time.h" +#include "nsDependentSubstring.h" +#include "event.h" +#include "mozilla/UniquePtr.h" + +// This macro checks that the _EVENT_SIZEOF_* constants defined in +// ipc/chromiume/src/third_party/<platform>/event2/event-config.h are correct. +#if defined(_EVENT_SIZEOF_SHORT) +#define CHECK_EVENT_SIZEOF(TYPE, type) \ + static_assert(_EVENT_SIZEOF_##TYPE == sizeof(type), \ + "bad _EVENT_SIZEOF_"#TYPE); +#elif defined(EVENT__SIZEOF_SHORT) +#define CHECK_EVENT_SIZEOF(TYPE, type) \ + static_assert(EVENT__SIZEOF_##TYPE == sizeof(type), \ + "bad EVENT__SIZEOF_"#TYPE); +#else +#error Cannot find libevent type sizes +#endif + +CHECK_EVENT_SIZEOF(LONG, long); +CHECK_EVENT_SIZEOF(LONG_LONG, long long); +CHECK_EVENT_SIZEOF(PTHREAD_T, pthread_t); +CHECK_EVENT_SIZEOF(SHORT, short); +CHECK_EVENT_SIZEOF(SIZE_T, size_t); +CHECK_EVENT_SIZEOF(VOID_P, void*); + +// Lifecycle of struct event +// Libevent uses two main data structures: +// struct event_base (of which there is one per message pump), and +// struct event (of which there is roughly one per socket). +// The socket's struct event is created in +// MessagePumpLibevent::WatchFileDescriptor(), +// is owned by the FileDescriptorWatcher, and is destroyed in +// StopWatchingFileDescriptor(). +// It is moved into and out of lists in struct event_base by +// the libevent functions event_add() and event_del(). +// +// TODO(dkegel): +// At the moment bad things happen if a FileDescriptorWatcher +// is active after its MessagePumpLibevent has been destroyed. +// See MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop +// Not clear yet whether that situation occurs in practice, +// but if it does, we need to fix it. + +namespace base { + +// Return 0 on success +// Too small a function to bother putting in a library? +static int SetNonBlocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher() + : is_persistent_(false), + event_(NULL) { +} + +MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() { + if (event_) { + StopWatchingFileDescriptor(); + } +} + +void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e, + bool is_persistent) { + DCHECK(e); + DCHECK(event_ == NULL); + + is_persistent_ = is_persistent; + event_ = e; +} + +event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() { + struct event *e = event_; + event_ = NULL; + return e; +} + +bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() { + event* e = ReleaseEvent(); + if (e == NULL) + return true; + + // event_del() is a no-op if the event isn't active. + int rv = event_del(e); + delete e; + return (rv == 0); +} + +// Called if a byte is received on the wakeup pipe. +void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) { + base::MessagePumpLibevent* that = + static_cast<base::MessagePumpLibevent*>(context); + DCHECK(that->wakeup_pipe_out_ == socket); + + // Remove and discard the wakeup byte. + char buf; + int nread = HANDLE_EINTR(read(socket, &buf, 1)); + DCHECK_EQ(nread, 1); + // Tell libevent to break out of inner loop. + event_base_loopbreak(that->event_base_); +} + +MessagePumpLibevent::MessagePumpLibevent() + : keep_running_(true), + in_run_(false), + event_base_(event_base_new()), + wakeup_pipe_in_(-1), + wakeup_pipe_out_(-1) { + if (!Init()) + NOTREACHED(); +} + +bool MessagePumpLibevent::Init() { + int fds[2]; + if (pipe(fds)) { + DLOG(ERROR) << "pipe() failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[0])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[1])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno; + return false; + } + wakeup_pipe_out_ = fds[0]; + wakeup_pipe_in_ = fds[1]; + + wakeup_event_ = new event; + event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST, + OnWakeup, this); + event_base_set(event_base_, wakeup_event_); + + if (event_add(wakeup_event_, 0)) + return false; + return true; +} + +MessagePumpLibevent::~MessagePumpLibevent() { + DCHECK(wakeup_event_); + DCHECK(event_base_); + event_del(wakeup_event_); + delete wakeup_event_; + if (wakeup_pipe_in_ >= 0) + close(wakeup_pipe_in_); + if (wakeup_pipe_out_ >= 0) + close(wakeup_pipe_out_); + event_base_free(event_base_); +} + +bool MessagePumpLibevent::WatchFileDescriptor(int fd, + bool persistent, + Mode mode, + FileDescriptorWatcher *controller, + Watcher *delegate) { + DCHECK(fd > 0); + DCHECK(controller); + DCHECK(delegate); + DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); + + int event_mask = persistent ? EV_PERSIST : 0; + if ((mode & WATCH_READ) != 0) { + event_mask |= EV_READ; + } + if ((mode & WATCH_WRITE) != 0) { + event_mask |= EV_WRITE; + } + + // |should_delete_event| is true if we're modifying an event that's currently + // active in |controller|. + // If we're modifying an existing event and there's an error then we need to + // tell libevent to clean it up via event_delete() before returning. + bool should_delete_event = true; + mozilla::UniquePtr<event> evt(controller->ReleaseEvent()); + if (evt.get() == NULL) { + should_delete_event = false; + // Ownership is transferred to the controller. + evt = mozilla::MakeUnique<event>(); + } else { + // It's illegal to use this function to listen on 2 separate fds with the + // same |controller|. + if (EVENT_FD(evt.get()) != fd) { + NOTREACHED() << "FDs don't match" << EVENT_FD(evt.get()) << "!=" << fd; + return false; + } + + // Make sure we don't pick up any funky internal libevent masks. + int old_interest_mask = evt.get()->ev_events & + (EV_READ | EV_WRITE | EV_PERSIST); + + // Combine old/new event masks. + event_mask |= old_interest_mask; + + // Must disarm the event before we can reuse it. + event_del(evt.get()); + } + + // Set current interest mask and message pump for this event. + event_set(evt.get(), fd, event_mask, OnLibeventNotification, + delegate); + + // Tell libevent which message pump this socket will belong to when we add it. + if (event_base_set(event_base_, evt.get()) != 0) { + if (should_delete_event) { + event_del(evt.get()); + } + return false; + } + + // Add this socket to the list of monitored sockets. + if (event_add(evt.get(), NULL) != 0) { + if (should_delete_event) { + event_del(evt.get()); + } + return false; + } + + // Transfer ownership of evt to controller. + controller->Init(evt.release(), persistent); + return true; +} + + +void MessagePumpLibevent::OnLibeventNotification(int fd, short flags, + void* context) { + Watcher* watcher = static_cast<Watcher*>(context); + + if (flags & EV_WRITE) { + watcher->OnFileCanWriteWithoutBlocking(fd); + } + if (flags & EV_READ) { + watcher->OnFileCanReadWithoutBlocking(fd); + } +} + + +MessagePumpLibevent::SignalEvent::SignalEvent() : + event_(NULL) +{ +} + +MessagePumpLibevent::SignalEvent::~SignalEvent() +{ + if (event_) { + StopCatching(); + } +} + +void +MessagePumpLibevent::SignalEvent::Init(event *e) +{ + DCHECK(e); + DCHECK(event_ == NULL); + event_ = e; +} + +bool +MessagePumpLibevent::SignalEvent::StopCatching() +{ + // XXX/cjones: this code could be shared with + // FileDescriptorWatcher. ironic that libevent is "more" + // object-oriented than this C++ + event* e = ReleaseEvent(); + if (e == NULL) + return true; + + // event_del() is a no-op if the event isn't active. + int rv = event_del(e); + delete e; + return (rv == 0); +} + +event * +MessagePumpLibevent::SignalEvent::ReleaseEvent() +{ + event *e = event_; + event_ = NULL; + return e; +} + +bool +MessagePumpLibevent::CatchSignal(int sig, + SignalEvent* sigevent, + SignalWatcher* delegate) +{ + DCHECK(sig > 0); + DCHECK(sigevent); + DCHECK(delegate); + // TODO if we want to support re-using SignalEvents, this code needs + // to jump through the same hoops as WatchFileDescriptor(). Not + // needed at present + DCHECK(NULL == sigevent->event_); + + mozilla::UniquePtr<event> evt = mozilla::MakeUnique<event>(); + signal_set(evt.get(), sig, OnLibeventSignalNotification, delegate); + + if (event_base_set(event_base_, evt.get())) + return false; + + if (signal_add(evt.get(), NULL)) + return false; + + // Transfer ownership of evt to controller. + sigevent->Init(evt.release()); + return true; +} + +void +MessagePumpLibevent::OnLibeventSignalNotification(int sig, short flags, + void* context) +{ + DCHECK(sig > 0); + DCHECK(EV_SIGNAL == flags); + DCHECK(context); + reinterpret_cast<SignalWatcher*>(context)->OnSignal(sig); +} + + +// Reentrant! +void MessagePumpLibevent::Run(Delegate* delegate) { + DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + + bool old_in_run = in_run_; + in_run_ = true; + + for (;;) { + ScopedNSAutoreleasePool autorelease_pool; + + bool did_work = delegate->DoWork(); + if (!keep_running_) + break; + + did_work |= delegate->DoDelayedWork(&delayed_work_time_); + if (!keep_running_) + break; + + if (did_work) + continue; + + did_work = delegate->DoIdleWork(); + if (!keep_running_) + break; + + if (did_work) + continue; + + // EVLOOP_ONCE tells libevent to only block once, + // but to service all pending events when it wakes up. + if (delayed_work_time_.is_null()) { + event_base_loop(event_base_, EVLOOP_ONCE); + } else { + TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); + if (delay > TimeDelta()) { + struct timeval poll_tv; + poll_tv.tv_sec = delay.InSeconds(); + poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond; + event_base_loopexit(event_base_, &poll_tv); + event_base_loop(event_base_, EVLOOP_ONCE); + } else { + // It looks like delayed_work_time_ indicates a time in the past, so we + // need to call DoDelayedWork now. + delayed_work_time_ = TimeTicks(); + } + } + } + + keep_running_ = true; + in_run_ = old_in_run; +} + +void MessagePumpLibevent::Quit() { + DCHECK(in_run_); + // Tell both libevent and Run that they should break out of their loops. + keep_running_ = false; + ScheduleWork(); +} + +void MessagePumpLibevent::ScheduleWork() { + // Tell libevent (in a threadsafe way) that it should break out of its loop. + char buf = 0; + int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1)); + DCHECK(nwrite == 1 || errno == EAGAIN) + << "[nwrite:" << nwrite << "] [errno:" << errno << "]"; +} + +void MessagePumpLibevent::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +void LineWatcher::OnFileCanReadWithoutBlocking(int aFd) +{ + ssize_t length = 0; + + while (true) { + length = read(aFd, mReceiveBuffer.get(), mBufferSize - mReceivedIndex); + DCHECK(length <= ssize_t(mBufferSize - mReceivedIndex)); + if (length <= 0) { + if (length < 0) { + if (errno == EINTR) { + continue; // retry system call when interrupted + } + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; // no data available: return and re-poll + } + DLOG(ERROR) << "Can't read from fd, error " << errno; + } else { + DLOG(ERROR) << "End of file"; + } + // At this point, assume that we can't actually access + // the socket anymore, and indicate an error. + OnError(); + mReceivedIndex = 0; + return; + } + + while (length-- > 0) { + DCHECK(mReceivedIndex < mBufferSize); + if (mReceiveBuffer[mReceivedIndex] == mTerminator) { + nsDependentCSubstring message(mReceiveBuffer.get(), mReceivedIndex); + OnLineRead(aFd, message); + if (length > 0) { + DCHECK(mReceivedIndex < (mBufferSize - 1)); + memmove(&mReceiveBuffer[0], &mReceiveBuffer[mReceivedIndex + 1], length); + } + mReceivedIndex = 0; + } else { + mReceivedIndex++; + } + } + } +} +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_libevent.h b/ipc/chromium/src/base/message_pump_libevent.h new file mode 100644 index 0000000000..224f70932c --- /dev/null +++ b/ipc/chromium/src/base/message_pump_libevent.h @@ -0,0 +1,220 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_MESSAGE_PUMP_LIBEVENT_H_ +#define BASE_MESSAGE_PUMP_LIBEVENT_H_ + +#include "base/message_pump.h" +#include "base/time.h" +#include "mozilla/UniquePtr.h" + +// Declare structs we need from libevent.h rather than including it +struct event_base; +struct event; + +class nsDependentCSubstring; + +namespace base { + +// Class to monitor sockets and issue callbacks when sockets are ready for I/O +// TODO(dkegel): add support for background file IO somehow +class MessagePumpLibevent : public MessagePump { + public: + + // Object returned by WatchFileDescriptor to manage further watching. + class FileDescriptorWatcher { + public: + FileDescriptorWatcher(); + ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor. + + // NOTE: These methods aren't called StartWatching()/StopWatching() to + // avoid confusion with the win32 ObjectWatcher class. + + // Stop watching the FD, always safe to call. No-op if there's nothing + // to do. + bool StopWatchingFileDescriptor(); + + private: + // Called by MessagePumpLibevent, ownership of |e| is transferred to this + // object. + void Init(event* e, bool is_persistent); + + // Used by MessagePumpLibevent to take ownership of event_. + event *ReleaseEvent(); + friend class MessagePumpLibevent; + + private: + bool is_persistent_; // false if this event is one-shot. + event* event_; + DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher); + }; + + // Used with WatchFileDescptor to asynchronously monitor the I/O readiness of + // a File Descriptor. + class Watcher { + public: + virtual ~Watcher() {} + // Called from MessageLoop::Run when an FD can be read from/written to + // without blocking + virtual void OnFileCanReadWithoutBlocking(int fd) = 0; + virtual void OnFileCanWriteWithoutBlocking(int fd) = 0; + }; + + MessagePumpLibevent(); + + enum Mode { + WATCH_READ = 1 << 0, + WATCH_WRITE = 1 << 1, + WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE + }; + + // Have the current thread's message loop watch for a a situation in which + // reading/writing to the FD can be performed without Blocking. + // Callers must provide a preallocated FileDescriptorWatcher object which + // can later be used to manage the Lifetime of this event. + // If a FileDescriptorWatcher is passed in which is already attached to + // an event, then the effect is cumulative i.e. after the call |controller| + // will watch both the previous event and the new one. + // If an error occurs while calling this method in a cumulative fashion, the + // event previously attached to |controller| is aborted. + // Returns true on success. + // TODO(dkegel): switch to edge-triggered readiness notification + bool WatchFileDescriptor(int fd, + bool persistent, + Mode mode, + FileDescriptorWatcher *controller, + Watcher *delegate); + + + // This is analagous to FileDescriptorWatcher above, which really is + // just a wrapper around libevent's |struct event|. This class acts + // as a sort of "scoped event watcher" in that it guarantees that + // when this class is out of scope, the signal-event it wraps is + // removed from libevent's guts. + // + // XXX/cjones: this isn't my favorite API, but preserving it in + // order to match code above + class SignalEvent { + friend class MessagePumpLibevent; + + public: + SignalEvent(); + ~SignalEvent(); // implicitly calls StopCatching() + + // Have libevent forget this event. + bool StopCatching(); + + private: + void Init(event* e); + event* ReleaseEvent(); + + event* event_; + + DISALLOW_COPY_AND_ASSIGN(SignalEvent); + }; + + class SignalWatcher { + public: + virtual ~SignalWatcher() {} + // Called from MessageLoop::Run when |sig| has been delivered to + // this process + virtual void OnSignal(int sig) = 0; + }; + + // Have the current thread's message loop catch the signal |sig|. + // Multiple watchers can catch the same signal; they're all notified + // upon its delivery. Callers must provide a preallocated + // SignalEvent object which can be used to manage the lifetime of + // this event. Returns true on success. + bool CatchSignal(int sig, + SignalEvent* sigevent, + SignalWatcher* delegate); + + + // MessagePump methods: + virtual void Run(Delegate* delegate) override; + virtual void Quit() override; + virtual void ScheduleWork() override; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; + + protected: + + virtual ~MessagePumpLibevent(); + + private: + + // Risky part of constructor. Returns true on success. + bool Init(); + + // This flag is set to false when Run should return. + bool keep_running_; + + // This flag is set when inside Run. + bool in_run_; + + // The time at which we should call DoDelayedWork. + TimeTicks delayed_work_time_; + + // Libevent dispatcher. Watches all sockets registered with it, and sends + // readiness callbacks when a socket is ready for I/O. + event_base* event_base_; + + // Called by libevent to tell us a registered FD can be read/written to. + static void OnLibeventNotification(int fd, short flags, + void* context); + + // Called by libevent upon receiving a signal + static void OnLibeventSignalNotification(int sig, short flags, + void* context); + + // Unix pipe used to implement ScheduleWork() + // ... callback; called by libevent inside Run() when pipe is ready to read + static void OnWakeup(int socket, short flags, void* context); + // ... write end; ScheduleWork() writes a single byte to it + int wakeup_pipe_in_; + // ... read end; OnWakeup reads it and then breaks Run() out of its sleep + int wakeup_pipe_out_; + // ... libevent wrapper for read end + event* wakeup_event_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent); +}; + +/** + * LineWatcher overrides OnFileCanReadWithoutBlocking. It separates the read + * data by mTerminator and passes each line to OnLineRead. + */ +class LineWatcher : public MessagePumpLibevent::Watcher +{ +public: + LineWatcher(char aTerminator, int aBufferSize) : mReceivedIndex(0), + mBufferSize(aBufferSize), + mTerminator(aTerminator) + { + mReceiveBuffer = mozilla::MakeUnique<char[]>(mBufferSize); + } + + ~LineWatcher() {} + +protected: + /** + * OnError will be called when |read| returns error. Derived class should + * implement this function to handle error cases when needed. + */ + virtual void OnError() {} + virtual void OnLineRead(int aFd, nsDependentCSubstring& aMessage) = 0; + virtual void OnFileCanWriteWithoutBlocking(int /* aFd */) override {} +private: + virtual void OnFileCanReadWithoutBlocking(int aFd) final override; + + mozilla::UniquePtr<char[]> mReceiveBuffer; + int mReceivedIndex; + int mBufferSize; + char mTerminator; +}; +} // namespace base + +#endif // BASE_MESSAGE_PUMP_LIBEVENT_H_ diff --git a/ipc/chromium/src/base/message_pump_mac.h b/ipc/chromium/src/base/message_pump_mac.h new file mode 100644 index 0000000000..7c8b3625f0 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_mac.h @@ -0,0 +1,279 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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. + +// The basis for all native run loops on the Mac is the CFRunLoop. It can be +// used directly, it can be used as the driving force behind the similar +// Foundation NSRunLoop, and it can be used to implement higher-level event +// loops such as the NSApplication event loop. +// +// This file introduces a basic CFRunLoop-based implementation of the +// MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all +// of the machinery necessary to dispatch events to a delegate, but does not +// implement the specific run loop. Concrete subclasses must provide their +// own DoRun and Quit implementations. +// +// A concrete subclass that just runs a CFRunLoop loop is provided in +// MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop +// is provided. +// +// For the application's event loop, an implementation based on AppKit's +// NSApplication event system is provided in MessagePumpNSApplication. +// +// Typically, MessagePumpNSApplication only makes sense on a Cocoa +// application's main thread. If a CFRunLoop-based message pump is needed on +// any other thread, one of the other concrete subclasses is preferrable. +// MessagePumpMac::Create is defined, which returns a new NSApplication-based +// or NSRunLoop-based MessagePump subclass depending on which thread it is +// called on. + +#ifndef BASE_MESSAGE_PUMP_MAC_H_ +#define BASE_MESSAGE_PUMP_MAC_H_ + +#include "base/message_pump.h" + +#include "base/basictypes.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> + +#if defined(__OBJC__) +@class NSAutoreleasePool; +#else // defined(__OBJC__) +class NSAutoreleasePool; +#endif // defined(__OBJC__) + +namespace base { + +class TimeTicks; + +class MessagePumpCFRunLoopBase : public MessagePump { + // Needs access to CreateAutoreleasePool. + friend class MessagePumpScopedAutoreleasePool; + public: + MessagePumpCFRunLoopBase(); + virtual ~MessagePumpCFRunLoopBase(); + + // Subclasses should implement the work they need to do in MessagePump::Run + // in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly. + // This arrangement is used because MessagePumpCFRunLoopBase needs to set + // up and tear down things before and after the "meat" of DoRun. + virtual void Run(Delegate* delegate); + virtual void DoRun(Delegate* delegate) = 0; + + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + protected: + // Accessors for private data members to be used by subclasses. + CFRunLoopRef run_loop() const { return run_loop_; } + int nesting_level() const { return nesting_level_; } + int run_nesting_level() const { return run_nesting_level_; } + + // Return an autorelease pool to wrap around any work being performed. + // In some cases, CreateAutoreleasePool may return nil intentionally to + // preventing an autorelease pool from being created, allowing any + // objects autoreleased by work to fall into the current autorelease pool. + virtual NSAutoreleasePool* CreateAutoreleasePool(); + + private: + // Timer callback scheduled by ScheduleDelayedWork. This does not do any + // work, but it signals delayed_work_source_ so that delayed work can be + // performed within the appropriate priority constraints. + static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info); + + // Perform highest-priority work. This is associated with work_source_ + // signalled by ScheduleWork. The static method calls the instance method; + // the instance method returns true if work was done. + static void RunWorkSource(void* info); + bool RunWork(); + + // Perform delayed-priority work. This is associated with + // delayed_work_source_ signalled by RunDelayedWorkTimer, and is responsible + // for calling ScheduleDelayedWork again if appropriate. The static method + // calls the instance method; the instance method returns true if more + // delayed work is available. + static void RunDelayedWorkSource(void* info); + bool RunDelayedWork(); + + // Perform idle-priority work. This is normally called by PreWaitObserver, + // but is also associated with idle_work_source_. When this function + // actually does perform idle work, it will resignal that source. The + // static method calls the instance method; the instance method returns + // true if idle work was done. + static void RunIdleWorkSource(void* info); + bool RunIdleWork(); + + // Perform work that may have been deferred because it was not runnable + // within a nested run loop. This is associated with + // nesting_deferred_work_source_ and is signalled by + // MaybeScheduleNestingDeferredWork when returning from a nested loop, + // so that an outer loop will be able to perform the necessary tasks if it + // permits nestable tasks. + static void RunNestingDeferredWorkSource(void* info); + bool RunNestingDeferredWork(); + + // Schedules possible nesting-deferred work to be processed before the run + // loop goes to sleep, exits, or begins processing sources at the top of its + // loop. If this function detects that a nested loop had run since the + // previous attempt to schedule nesting-deferred work, it will schedule a + // call to RunNestingDeferredWorkSource. + void MaybeScheduleNestingDeferredWork(); + + // Observer callback responsible for performing idle-priority work, before + // the run loop goes to sleep. Associated with idle_work_observer_. + static void PreWaitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Observer callback called before the run loop processes any sources. + // Associated with pre_source_observer_. + static void PreSourceObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Observer callback called when the run loop starts and stops, at the + // beginning and end of calls to CFRunLoopRun. This is used to maintain + // nesting_level_. Associated with enter_exit_observer_. + static void EnterExitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Called by EnterExitObserver after performing maintenance on nesting_level_. + // This allows subclasses an opportunity to perform additional processing on + // the basis of run loops starting and stopping. + virtual void EnterExitRunLoop(CFRunLoopActivity activity); + + // IOKit power state change notification callback, called when the system + // enters and leaves the sleep state. + static void PowerStateNotification(void* info, io_service_t service, + uint32_t message_type, + void* message_argument); + + // The thread's run loop. + CFRunLoopRef run_loop_; + + // The timer, sources, and observers are described above alongside their + // callbacks. + CFRunLoopTimerRef delayed_work_timer_; + CFRunLoopSourceRef work_source_; + CFRunLoopSourceRef delayed_work_source_; + CFRunLoopSourceRef idle_work_source_; + CFRunLoopSourceRef nesting_deferred_work_source_; + CFRunLoopObserverRef pre_wait_observer_; + CFRunLoopObserverRef pre_source_observer_; + CFRunLoopObserverRef enter_exit_observer_; + + // Objects used for power state notification. See PowerStateNotification. + io_connect_t root_power_domain_; + IONotificationPortRef power_notification_port_; + io_object_t power_notification_object_; + + // (weak) Delegate passed as an argument to the innermost Run call. + Delegate* delegate_; + + // The time that delayed_work_timer_ is scheduled to fire. This is tracked + // independently of CFRunLoopTimerGetNextFireDate(delayed_work_timer_) + // to be able to reset the timer properly after waking from system sleep. + // See PowerStateNotification. + CFAbsoluteTime delayed_work_fire_time_; + + // The recursion depth of the currently-executing CFRunLoopRun loop on the + // run loop's thread. 0 if no run loops are running inside of whatever scope + // the object was created in. + int nesting_level_; + + // The recursion depth (calculated in the same way as nesting_level_) of the + // innermost executing CFRunLoopRun loop started by a call to Run. + int run_nesting_level_; + + // The deepest (numerically highest) recursion depth encountered since the + // most recent attempt to run nesting-deferred work. + int deepest_nesting_level_; + + // "Delegateless" work flags are set when work is ready to be performed but + // must wait until a delegate is available to process it. This can happen + // when a MessagePumpCFRunLoopBase is instantiated and work arrives without + // any call to Run on the stack. The Run method will check for delegateless + // work on entry and redispatch it as needed once a delegate is available. + bool delegateless_work_; + bool delegateless_delayed_work_; + bool delegateless_idle_work_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); +}; + +class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { + public: + MessagePumpCFRunLoop(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + private: + virtual void EnterExitRunLoop(CFRunLoopActivity activity); + + // True if Quit is called to stop the innermost MessagePump + // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_) + // is running inside the MessagePump's innermost Run call. + bool quit_pending_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop); +}; + +class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { + public: + MessagePumpNSRunLoop(); + virtual ~MessagePumpNSRunLoop(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + private: + // A source that doesn't do anything but provide something signalable + // attached to the run loop. This source will be signalled when Quit + // is called, to cause the loop to wake up so that it can stop. + CFRunLoopSourceRef quit_source_; + + // False after Quit is called. + bool keep_running_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop); +}; + +class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { + public: + MessagePumpNSApplication(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + protected: + // Returns nil if NSApp is currently in the middle of calling -sendEvent. + virtual NSAutoreleasePool* CreateAutoreleasePool(); + + private: + // False after Quit is called. + bool keep_running_; + + // True if DoRun is managing its own run loop as opposed to letting + // -[NSApplication run] handle it. The outermost run loop in the application + // is managed by -[NSApplication run], inner run loops are handled by a loop + // in DoRun. + bool running_own_loop_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication); +}; + +class MessagePumpMac { + public: + // Returns a new instance of MessagePumpNSApplication if called on the main + // thread. Otherwise, returns a new instance of MessagePumpNSRunLoop. + static MessagePump* Create(); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_MAC_H_ diff --git a/ipc/chromium/src/base/message_pump_mac.mm b/ipc/chromium/src/base/message_pump_mac.mm new file mode 100644 index 0000000000..7032849456 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_mac.mm @@ -0,0 +1,752 @@ +// Copyright (c) 2008 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 "base/message_pump_mac.h" + +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> +#include <IOKit/IOMessage.h> +#include <IOKit/pwr_mgt/IOPMLib.h> + +#include <limits> + +#import "base/chrome_application_mac.h" +#include "base/logging.h" +#include "base/time.h" + +namespace { + +void NoOp(void* info) { +} + +const CFTimeInterval kCFTimeIntervalMax = + std::numeric_limits<CFTimeInterval>::max(); + +} // namespace + +namespace base { + +// A scoper for autorelease pools created from message pump run loops. +// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare +// case where an autorelease pool needs to be passed in. +class MessagePumpScopedAutoreleasePool { + public: + explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) : + pool_(pump->CreateAutoreleasePool()) { + } + ~MessagePumpScopedAutoreleasePool() { + [pool_ drain]; + } + + private: + NSAutoreleasePool* pool_; + DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); +}; + +// Must be called on the run loop thread. +MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() + : delegate_(NULL), + delayed_work_fire_time_(kCFTimeIntervalMax), + nesting_level_(0), + run_nesting_level_(0), + deepest_nesting_level_(0), + delegateless_work_(false), + delegateless_delayed_work_(false), + delegateless_idle_work_(false) { + run_loop_ = CFRunLoopGetCurrent(); + CFRetain(run_loop_); + + // Set a repeating timer with a preposterous firing time and interval. The + // timer will effectively never fire as-is. The firing time will be adjusted + // as needed when ScheduleDelayedWork is called. + CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); + timer_context.info = this; + delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator + kCFTimeIntervalMax, // fire time + kCFTimeIntervalMax, // interval + 0, // flags + 0, // priority + RunDelayedWorkTimer, + &timer_context); + CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); + source_context.info = this; + source_context.perform = RunWorkSource; + work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 1, // priority + &source_context); + CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunDelayedWorkSource; + delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 2, // priority + &source_context); + CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunIdleWorkSource; + idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 3, // priority + &source_context); + CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunNestingDeferredWorkSource; + nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, + kCFRunLoopCommonModes); + + CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); + observer_context.info = this; + pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopBeforeWaiting, + true, // repeat + 0, // priority + PreWaitObserver, + &observer_context); + CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); + + pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopBeforeSources, + true, // repeat + 0, // priority + PreSourceObserver, + &observer_context); + CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); + + enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopEntry | + kCFRunLoopExit, + true, // repeat + 0, // priority + EnterExitObserver, + &observer_context); + CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); + + root_power_domain_ = IORegisterForSystemPower(this, + &power_notification_port_, + PowerStateNotification, + &power_notification_object_); + if (root_power_domain_ != MACH_PORT_NULL) { + CFRunLoopAddSource( + run_loop_, + IONotificationPortGetRunLoopSource(power_notification_port_), + kCFRunLoopCommonModes); + } +} + +// Ideally called on the run loop thread. If other run loops were running +// lower on the run loop thread's stack when this object was created, the +// same number of run loops must be running when this object is destroyed. +MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { + if (root_power_domain_ != MACH_PORT_NULL) { + CFRunLoopRemoveSource( + run_loop_, + IONotificationPortGetRunLoopSource(power_notification_port_), + kCFRunLoopCommonModes); + IODeregisterForSystemPower(&power_notification_object_); + IOServiceClose(root_power_domain_); + IONotificationPortDestroy(power_notification_port_); + } + + CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, + kCFRunLoopCommonModes); + CFRelease(enter_exit_observer_); + + CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, + kCFRunLoopCommonModes); + CFRelease(pre_source_observer_); + + CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, + kCFRunLoopCommonModes); + CFRelease(pre_wait_observer_); + + CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, + kCFRunLoopCommonModes); + CFRelease(nesting_deferred_work_source_); + + CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + CFRelease(idle_work_source_); + + CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); + CFRelease(delayed_work_source_); + + CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); + CFRelease(work_source_); + + CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + CFRelease(delayed_work_timer_); + + CFRelease(run_loop_); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { + // nesting_level_ will be incremented in EnterExitRunLoop, so set + // run_nesting_level_ accordingly. + int last_run_nesting_level = run_nesting_level_; + run_nesting_level_ = nesting_level_ + 1; + + Delegate* last_delegate = delegate_; + delegate_ = delegate; + + if (delegate) { + // If any work showed up but could not be dispatched for want of a + // delegate, set it up for dispatch again now that a delegate is + // available. + if (delegateless_work_) { + CFRunLoopSourceSignal(work_source_); + delegateless_work_ = false; + } + if (delegateless_delayed_work_) { + CFRunLoopSourceSignal(delayed_work_source_); + delegateless_delayed_work_ = false; + } + if (delegateless_idle_work_) { + CFRunLoopSourceSignal(idle_work_source_); + delegateless_idle_work_ = false; + } + } + + DoRun(delegate); + + // Restore the previous state of the object. + delegate_ = last_delegate; + run_nesting_level_ = last_run_nesting_level; +} + +// May be called on any thread. +void MessagePumpCFRunLoopBase::ScheduleWork() { + CFRunLoopSourceSignal(work_source_); + CFRunLoopWakeUp(run_loop_); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoopBase::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + TimeDelta delta = delayed_work_time - TimeTicks::Now(); + delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); + CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // The timer won't fire again until it's reset. + self->delayed_work_fire_time_ = kCFTimeIntervalMax; + + // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. + // In order to establish the proper priority where delegate_->DoDelayedWork + // can only be called if delegate_->DoWork returns false, the timer used + // to schedule delayed work must signal a CFRunLoopSource set at a lower + // priority than the one used for delegate_->DoWork. + CFRunLoopSourceSignal(self->delayed_work_source_); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunWorkSource. +bool MessagePumpCFRunLoopBase::RunWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. Arrange to come back + // here when a delegate is available. + delegateless_work_ = true; + return false; + } + + // The NSApplication-based run loop only drains the autorelease pool at each + // UI event (NSEvent). The autorelease pool is not drained for each + // CFRunLoopSource target that's run. Use a local pool for any autoreleased + // objects if the app is not currently handling a UI event to ensure they're + // released promptly even in the absence of UI events. + MessagePumpScopedAutoreleasePool autorelease_pool(this); + + // Call DoWork once, and if something was done, arrange to come back here + // again as long as the loop is still running. + bool did_work = delegate_->DoWork(); + if (did_work) { + CFRunLoopSourceSignal(work_source_); + } + + return did_work; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunDelayedWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. +bool MessagePumpCFRunLoopBase::RunDelayedWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. Arrange to come back + // here when a delegate is available. + delegateless_delayed_work_ = true; + return false; + } + + // The NSApplication-based run loop only drains the autorelease pool at each + // UI event (NSEvent). The autorelease pool is not drained for each + // CFRunLoopSource target that's run. Use a local pool for any autoreleased + // objects if the app is not currently handling a UI event to ensure they're + // released promptly even in the absence of UI events. + MessagePumpScopedAutoreleasePool autorelease_pool(this); + + TimeTicks next_time; + delegate_->DoDelayedWork(&next_time); + + bool more_work = !next_time.is_null(); + if (more_work) { + TimeDelta delay = next_time - TimeTicks::Now(); + if (delay > TimeDelta()) { + // There's more delayed work to be done in the future. + ScheduleDelayedWork(next_time); + } else { + // There's more delayed work to be done, and its time is in the past. + // Arrange to come back here directly as long as the loop is still + // running. + CFRunLoopSourceSignal(delayed_work_source_); + } + } + + return more_work; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunIdleWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. +bool MessagePumpCFRunLoopBase::RunIdleWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. Arrange to come back + // here when a delegate is available. + delegateless_idle_work_ = true; + return false; + } + + // The NSApplication-based run loop only drains the autorelease pool at each + // UI event (NSEvent). The autorelease pool is not drained for each + // CFRunLoopSource target that's run. Use a local pool for any autoreleased + // objects if the app is not currently handling a UI event to ensure they're + // released promptly even in the absence of UI events. + MessagePumpScopedAutoreleasePool autorelease_pool(this); + + // Call DoIdleWork once, and if something was done, arrange to come back here + // again as long as the loop is still running. + bool did_work = delegate_->DoIdleWork(); + if (did_work) { + CFRunLoopSourceSignal(idle_work_source_); + } + + return did_work; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunNestingDeferredWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. +bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. There's no sense in + // attempting to do any work or signalling the work sources because + // without a delegate, work is not possible. + return false; + } + + // Immediately try work in priority order. + if (!RunWork()) { + if (!RunDelayedWork()) { + if (!RunIdleWork()) { + return false; + } + } else { + // There was no work, and delayed work was done. Arrange for the loop + // to try non-nestable idle work on a subsequent pass. + CFRunLoopSourceSignal(idle_work_source_); + } + } else { + // Work was done. Arrange for the loop to try non-nestable delayed and + // idle work on a subsequent pass. + CFRunLoopSourceSignal(delayed_work_source_); + CFRunLoopSourceSignal(idle_work_source_); + } + + return true; +} + +// Called before the run loop goes to sleep or exits, or processes sources. +void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { + // deepest_nesting_level_ is set as run loops are entered. If the deepest + // level encountered is deeper than the current level, a nested loop + // (relative to the current level) ran since the last time nesting-deferred + // work was scheduled. When that situation is encountered, schedule + // nesting-deferred work in case any work was deferred because nested work + // was disallowed. + if (deepest_nesting_level_ > nesting_level_) { + deepest_nesting_level_ = nesting_level_; + CFRunLoopSourceSignal(nesting_deferred_work_source_); + } +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // Attempt to do some idle work before going to sleep. + self->RunIdleWork(); + + // The run loop is about to go to sleep. If any of the work done since it + // started or woke up resulted in a nested run loop running, + // nesting-deferred work may have accumulated. Schedule it for processing + // if appropriate. + self->MaybeScheduleNestingDeferredWork(); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // The run loop has reached the top of the loop and is about to begin + // processing sources. If the last iteration of the loop at this nesting + // level did not sleep or exit, nesting-deferred work may have accumulated + // if a nested loop ran. Schedule nesting-deferred work for processing if + // appropriate. + self->MaybeScheduleNestingDeferredWork(); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + switch (activity) { + case kCFRunLoopEntry: + ++self->nesting_level_; + if (self->nesting_level_ > self->deepest_nesting_level_) { + self->deepest_nesting_level_ = self->nesting_level_; + } + break; + + case kCFRunLoopExit: + // Not all run loops go to sleep. If a run loop is stopped before it + // goes to sleep due to a CFRunLoopStop call, or if the timeout passed + // to CFRunLoopRunInMode expires, the run loop may proceed directly from + // handling sources to exiting without any sleep. This most commonly + // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it + // to make a single pass through the loop and exit without sleep. Some + // native loops use CFRunLoop in this way. Because PreWaitObserver will + // not be called in these case, MaybeScheduleNestingDeferredWork needs + // to be called here, as the run loop exits. + // + // MaybeScheduleNestingDeferredWork consults self->nesting_level_ + // to determine whether to schedule nesting-deferred work. It expects + // the nesting level to be set to the depth of the loop that is going + // to sleep or exiting. It must be called before decrementing the + // value so that the value still corresponds to the level of the exiting + // loop. + self->MaybeScheduleNestingDeferredWork(); + --self->nesting_level_; + break; + + default: + break; + } + + self->EnterExitRunLoop(activity); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PowerStateNotification(void* info, + io_service_t service, + uint32_t message_type, + void* message_argument) { + // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which + // measures the number of seconds since 2001-01-01 00:00:00.0 Z. It is + // implemented in terms of kernel ticks, as in mach_absolute_time. While an + // offset and scale factor can be applied to convert between the two time + // bases at any time after boot, the kernel clock stops while the system is + // asleep, altering the offset. (The offset will also change when the + // real-time clock is adjusted.) CFRunLoopTimers are not readjusted to take + // this into account when the system wakes up, so any timers that were + // pending while the system was asleep will be delayed by the sleep + // duration. + // + // The MessagePump interface assumes that scheduled delayed work will be + // performed at the time ScheduleDelayedWork was asked to perform it. The + // delay caused by the CFRunLoopTimer not firing at the appropriate time + // results in a stall of queued delayed work when the system wakes up. + // With this limitation, scheduled work would not be performed until + // (system wake time + scheduled work time - system sleep time), while it + // would be expected to be performed at (scheduled work time). + // + // To work around this problem, when the system wakes up from sleep, if a + // delayed work timer is pending, it is rescheduled to fire at the original + // time that it was scheduled to fire. + // + // This mechanism is not resilient if the real-time clock does not maintain + // stable time while the system is sleeping, but it matches the behavior of + // the various other MessagePump implementations, and MessageLoop seems to + // be limited in the same way. + // + // References + // - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com, + // http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html + // - Apple Technical Q&A QA1340, "Registering and unregistering for sleep + // and wake notifications," + // http://developer.apple.com/mac/library/qa/qa2004/qa1340.html + // - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c, + // http://www.opensource.apple.com/ + + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + switch (message_type) { + case kIOMessageSystemWillPowerOn: + if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) { + CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_, + self->delayed_work_fire_time_); + } + break; + + case kIOMessageSystemWillSleep: + case kIOMessageCanSystemSleep: + // The system will wait for 30 seconds before entering sleep if neither + // IOAllowPowerChange nor IOCancelPowerChange are called. That would be + // pretty antisocial. + IOAllowPowerChange(self->root_power_domain_, + reinterpret_cast<long>(message_argument)); + break; + + default: + break; + } +} + +// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default +// implementation is a no-op. +void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) { +} + +// Base version returns a standard NSAutoreleasePool. +NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { + return [[NSAutoreleasePool alloc] init]; +} + +MessagePumpCFRunLoop::MessagePumpCFRunLoop() + : quit_pending_(false) { +} + +// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were +// running lower on the run loop thread's stack when this object was created, +// the same number of CFRunLoopRun loops must be running for the outermost call +// to Run. Run/DoRun are reentrant after that point. +void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { + // This is completely identical to calling CFRunLoopRun(), except autorelease + // pool management is introduced. + int result; + do { + MessagePumpScopedAutoreleasePool autorelease_pool(this); + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, + kCFTimeIntervalMax, + false); + } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoop::Quit() { + // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. + if (nesting_level() == run_nesting_level()) { + // This object is running the innermost loop, just stop it. + CFRunLoopStop(run_loop()); + } else { + // There's another loop running inside the loop managed by this object. + // In other words, someone else called CFRunLoopRunInMode on the same + // thread, deeper on the stack than the deepest Run call. Don't preempt + // other run loops, just mark this object to quit the innermost Run as + // soon as the other inner loops not managed by Run are done. + quit_pending_ = true; + } +} + +// Called by MessagePumpCFRunLoopBase::EnterExitObserver. +void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { + if (activity == kCFRunLoopExit && + nesting_level() == run_nesting_level() && + quit_pending_) { + // Quit was called while loops other than those managed by this object + // were running further inside a run loop managed by this object. Now + // that all unmanaged inner run loops are gone, stop the loop running + // just inside Run. + CFRunLoopStop(run_loop()); + quit_pending_ = false; + } +} + +MessagePumpNSRunLoop::MessagePumpNSRunLoop() + : keep_running_(true) { + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); + source_context.perform = NoOp; + quit_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); +} + +MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { + CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); + CFRelease(quit_source_); +} + +void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { + while (keep_running_) { + // NSRunLoop manages autorelease pools itself. + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]; + } + + keep_running_ = true; +} + +void MessagePumpNSRunLoop::Quit() { + keep_running_ = false; + CFRunLoopSourceSignal(quit_source_); + CFRunLoopWakeUp(run_loop()); +} + +MessagePumpNSApplication::MessagePumpNSApplication() + : keep_running_(true), + running_own_loop_(false) { +} + +void MessagePumpNSApplication::DoRun(Delegate* delegate) { + bool last_running_own_loop_ = running_own_loop_; + + // TODO(dmaclach): Get rid of this gratuitous sharedApplication. + // Tests should be setting up their applications on their own. + [CrApplication sharedApplication]; + + if (![NSApp isRunning]) { + running_own_loop_ = false; + // NSApplication manages autorelease pools itself when run this way. + [NSApp run]; + } else { + running_own_loop_ = true; + NSDate* distant_future = [NSDate distantFuture]; + while (keep_running_) { + MessagePumpScopedAutoreleasePool autorelease_pool(this); + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:distant_future + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) { + [NSApp sendEvent:event]; + } + } + keep_running_ = true; + } + + running_own_loop_ = last_running_own_loop_; +} + +void MessagePumpNSApplication::Quit() { + if (!running_own_loop_) { + [NSApp stop:nil]; + } else { + keep_running_ = false; + } + + // Send a fake event to wake the loop up. + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:NULL + subtype:0 + data1:0 + data2:0] + atStart:NO]; +} + +// Prevents an autorelease pool from being created if the app is in the midst of +// handling a UI event because various parts of AppKit depend on objects that +// are created while handling a UI event to be autoreleased in the event loop. +// An example of this is NSWindowController. When a window with a window +// controller is closed it goes through a stack like this: +// (Several stack frames elided for clarity) +// +// #0 [NSWindowController autorelease] +// #1 DoAClose +// #2 MessagePumpCFRunLoopBase::DoWork() +// #3 [NSRunLoop run] +// #4 [NSButton performClick:] +// #5 [NSWindow sendEvent:] +// #6 [NSApp sendEvent:] +// #7 [NSApp run] +// +// -performClick: spins a nested run loop. If the pool created in DoWork was a +// standard NSAutoreleasePool, it would release the objects that were +// autoreleased into it once DoWork released it. This would cause the window +// controller, which autoreleased itself in frame #0, to release itself, and +// possibly free itself. Unfortunately this window controller controls the +// window in frame #5. When the stack is unwound to frame #5, the window would +// no longer exists and crashes may occur. Apple gets around this by never +// releasing the pool it creates in frame #4, and letting frame #7 clean it up +// when it cleans up the pool that wraps frame #7. When an autorelease pool is +// released it releases all other pools that were created after it on the +// autorelease pool stack. +// +// CrApplication is responsible for setting handlingSendEvent to true just +// before it sends the event throught the event handling mechanism, and +// returning it to its previous value once the event has been sent. +NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() { + NSAutoreleasePool* pool = nil; + DCHECK([NSApp isKindOfClass:[CrApplication class]]); + if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) { + pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool(); + } + return pool; +} + +// static +MessagePump* MessagePumpMac::Create() { + if ([NSThread isMainThread]) { + return new MessagePumpNSApplication; + } + + return new MessagePumpNSRunLoop; +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_qt.cc b/ipc/chromium/src/base/message_pump_qt.cc new file mode 100644 index 0000000000..0fcd685b9d --- /dev/null +++ b/ipc/chromium/src/base/message_pump_qt.cc @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 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 <qabstracteventdispatcher.h> +#include <qevent.h> +#include <QCoreApplication> +#include <QThread> +#include <qtimer.h> + +#include "base/message_pump_qt.h" + +#include <fcntl.h> +#include <limits> +#include <math.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace { +// Cached QEvent user type, registered for our event system +static int sPokeEvent; +} // namespace + +namespace base { + +MessagePumpForUI::MessagePumpForUI() + : state_(NULL), + qt_pump(*this) +{ +} + +MessagePumpForUI::~MessagePumpForUI() { +} + +MessagePumpQt::MessagePumpQt(MessagePumpForUI &aPump) + : pump(aPump), mTimer(new QTimer(this)) +{ + // Register our custom event type, to use in qApp event loop + sPokeEvent = QEvent::registerEventType(); + connect(mTimer, SIGNAL(timeout()), this, SLOT(dispatchDelayed())); + mTimer->setSingleShot(true); +} + +MessagePumpQt::~MessagePumpQt() +{ + mTimer->stop(); + delete mTimer; +} + +bool +MessagePumpQt::event(QEvent *e) +{ + if (e->type() == sPokeEvent) { + pump.HandleDispatch(); + return true; + } + return false; +} + +void +MessagePumpQt::scheduleDelayedIfNeeded(const TimeTicks& delayed_work_time) +{ + if (delayed_work_time.is_null()) { + return; + } + + if (mTimer->isActive()) { + mTimer->stop(); + } + + TimeDelta later = delayed_work_time - TimeTicks::Now(); + // later.InMilliseconds() returns an int64_t, QTimer only accepts int's for start(), + // std::min only works on exact same types. + int laterMsecs = later.InMilliseconds() > std::numeric_limits<int>::max() ? + std::numeric_limits<int>::max() : later.InMilliseconds(); + mTimer->start(laterMsecs > 0 ? laterMsecs : 0); +} + +void +MessagePumpQt::dispatchDelayed() +{ + pump.HandleDispatch(); +} + +void MessagePumpForUI::Run(Delegate* delegate) { + RunState state; + state.delegate = delegate; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunAllPending() works correctly. + bool more_work_is_plausible = true; + + RunState* previous_state = state_; + state_ = &state; + + for(;;) { + QEventLoop::ProcessEventsFlags block = QEventLoop::AllEvents; + if (!more_work_is_plausible) { + block |= QEventLoop::WaitForMoreEvents; + } + + QAbstractEventDispatcher* dispatcher = + QAbstractEventDispatcher::instance(QThread::currentThread()); + // An assertion seems too much here, as during startup, + // the dispatcher might not be ready yet. + if (!dispatcher) { + return; + } + + // processEvent's returns true if an event has been processed. + more_work_is_plausible = dispatcher->processEvents(block); + + if (state_->should_quit) { + break; + } + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) { + break; + } + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) { + break; + } + + qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); + + if (more_work_is_plausible) { + continue; + } + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) { + break; + } + } + + state_ = previous_state; +} + +void MessagePumpForUI::HandleDispatch() { + if (state_->should_quit) { + return; + } + + if (state_->delegate->DoWork()) { + // there might be more, see more_work_is_plausible + // variable above, that's why we ScheduleWork() to keep going. + ScheduleWork(); + } + + if (state_->should_quit) { + return; + } + + state_->delegate->DoDelayedWork(&delayed_work_time_); + qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); +} + +void MessagePumpForUI::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpForUI::ScheduleWork() { + QCoreApplication::postEvent(&qt_pump, + new QEvent((QEvent::Type) sPokeEvent)); +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // On GLib implementation, a work source is defined which explicitly checks the + // time that has passed. Here, on Qt we can use a QTimer that enqueues our + // event signal in an event queue. + delayed_work_time_ = delayed_work_time; + qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_qt.h b/ipc/chromium/src/base/message_pump_qt.h new file mode 100644 index 0000000000..625b10095a --- /dev/null +++ b/ipc/chromium/src/base/message_pump_qt.h @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 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 BASE_MESSAGE_PUMP_QT_H_ +#define BASE_MESSAGE_PUMP_QT_H_ + +#include <qobject.h> + +#include "base/message_pump.h" +#include "base/time.h" + +class QTimer; + +namespace base { + +class MessagePumpForUI; + +class MessagePumpQt : public QObject { + Q_OBJECT + + public: + MessagePumpQt(MessagePumpForUI &pump); + ~MessagePumpQt(); + + virtual bool event (QEvent *e); + void scheduleDelayedIfNeeded(const TimeTicks& delayed_work_time); + + public Q_SLOTS: + void dispatchDelayed(); + + private: + base::MessagePumpForUI &pump; + QTimer* mTimer; +}; + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// OS_LINUX platforms using QApplication event loop +class MessagePumpForUI : public MessagePump { + + public: + MessagePumpForUI(); + ~MessagePumpForUI(); + + virtual void Run(Delegate* delegate); + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. + // HandleDispatch is called after the poll has completed. + void HandleDispatch(); + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState { + Delegate* delegate; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + }; + + RunState* state_; + + // This is the time when we need to do delayed work. + TimeTicks delayed_work_time_; + + // MessagePump implementation for Qt based on the GLib implement. + // On Qt we use a QObject base class and the + // default qApp in order to process events through QEventLoop. + MessagePumpQt qt_pump; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_QT_H_ diff --git a/ipc/chromium/src/base/message_pump_win.cc b/ipc/chromium/src/base/message_pump_win.cc new file mode 100644 index 0000000000..407a470650 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_win.cc @@ -0,0 +1,547 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 "base/message_pump_win.h" + +#include <math.h> + +#include "base/message_loop.h" +#include "base/histogram.h" +#include "base/win_util.h" +#include "WinUtils.h" + +using base::Time; + +namespace base { + +static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow"; + +// Message sent to get an additional time slice for pumping (processing) another +// task (a series of such messages creates a continuous task pump). +static const int kMsgHaveWork = WM_USER + 1; + +//----------------------------------------------------------------------------- +// MessagePumpWin public: + +void MessagePumpWin::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void MessagePumpWin::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void MessagePumpWin::WillProcessMessage(const MSG& msg) { + FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg)); +} + +void MessagePumpWin::DidProcessMessage(const MSG& msg) { + FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg)); +} + +void MessagePumpWin::RunWithDispatcher( + Delegate* delegate, Dispatcher* dispatcher) { + RunState s; + s.delegate = delegate; + s.dispatcher = dispatcher; + s.should_quit = false; + s.run_depth = state_ ? state_->run_depth + 1 : 1; + + RunState* previous_state = state_; + state_ = &s; + + DoRunLoop(); + + state_ = previous_state; +} + +void MessagePumpWin::Quit() { + DCHECK(state_); + state_->should_quit = true; +} + +//----------------------------------------------------------------------------- +// MessagePumpWin protected: + +int MessagePumpWin::GetCurrentDelay() const { + if (delayed_work_time_.is_null()) + return -1; + + // Be careful here. TimeDelta has a precision of microseconds, but we want a + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or + // 6? It should be 6 to avoid executing delayed work too early. + double timeout = + ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF()); + + // If this value is negative, then we need to run delayed work soon. + int delay = static_cast<int>(timeout); + if (delay < 0) + delay = 0; + + return delay; +} + +//----------------------------------------------------------------------------- +// MessagePumpForUI public: + +MessagePumpForUI::MessagePumpForUI() { + InitMessageWnd(); +} + +MessagePumpForUI::~MessagePumpForUI() { + DestroyWindow(message_hwnd_); + UnregisterClass(kWndClass, GetModuleHandle(NULL)); +} + +void MessagePumpForUI::ScheduleWork() { + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0); + + // In order to wake up any cross-process COM calls which may currently be + // pending on the main thread, we also have to post a UI message. + PostMessage(message_hwnd_, WM_NULL, 0, 0); +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // + // We would *like* to provide high resolution timers. Windows timers using + // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup + // mechanism because the application can enter modal windows loops where it + // is not running our MessageLoop; the only way to have our timers fire in + // these cases is to post messages there. + // + // To provide sub-10ms timers, we process timers directly from our run loop. + // For the common case, timers will be processed there as the run loop does + // its normal work. However, we *also* set the system timer so that WM_TIMER + // events fire. This mops up the case of timers not being able to work in + // modal message loops. It is possible for the SetTimer to pop and have no + // pending timers, because they could have already been processed by the + // run loop itself. + // + // We use a single SetTimer corresponding to the timer that will expire + // soonest. As new timers are created and destroyed, we update SetTimer. + // Getting a spurrious SetTimer event firing is benign, as we'll just be + // processing an empty timer queue. + // + delayed_work_time_ = delayed_work_time; + + int delay_msec = GetCurrentDelay(); + DCHECK(delay_msec >= 0); + if (delay_msec < USER_TIMER_MINIMUM) + delay_msec = USER_TIMER_MINIMUM; + + // Create a WM_TIMER event that will wake us up to check for any pending + // timers (in case we are running within a nested, external sub-pump). + SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL); +} + +void MessagePumpForUI::PumpOutPendingPaintMessages() { + // If we are being called outside of the context of Run, then don't try to do + // any work. + if (!state_) + return; + + // Create a mini-message-pump to force immediate processing of only Windows + // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking + // to get the job done. Actual common max is 4 peeks, but we'll be a little + // safe here. + const int kMaxPeekCount = 20; + int peek_count; + for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { + MSG msg; + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) + break; + ProcessMessageHelper(msg); + if (state_->should_quit) // Handle WM_QUIT. + break; + } + // Histogram what was really being used, to help to adjust kMaxPeekCount. + DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count); +} + +//----------------------------------------------------------------------------- +// MessagePumpForUI private: + +// static +LRESULT CALLBACK MessagePumpForUI::WndProcThunk( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + switch (message) { + case kMsgHaveWork: + reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); + break; + case WM_TIMER: + reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); + break; + } + return DefWindowProc(hwnd, message, wparam, lparam); +} + +void MessagePumpForUI::DoRunLoop() { + // IF this was just a simple PeekMessage() loop (servicing all possible work + // queues), then Windows would try to achieve the following order according + // to MSDN documentation about PeekMessage with no filter): + // * Sent messages + // * Posted messages + // * Sent messages (again) + // * WM_PAINT messages + // * WM_TIMER messages + // + // Summary: none of the above classes is starved, and sent messages has twice + // the chance of being processed (i.e., reduced service time). + + for (;;) { + // If we do any work, we may create more messages etc., and more work may + // possibly be waiting in another task group. When we (for example) + // ProcessNextWindowsMessage(), there is a good chance there are still more + // messages waiting. On the other hand, when any of these methods return + // having done no work, then it is pretty unlikely that calling them again + // quickly will find any work to do. Finally, if they all say they had no + // work, then it is a good time to consider sleeping (waiting) for more + // work. + + bool more_work_is_plausible = ProcessNextWindowsMessage(); + if (state_->should_quit) + break; + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + // If we did not process any delayed work, then we can assume that our + // existing WM_TIMER if any will fire when delayed work should run. We + // don't want to disturb that timer if it is already in flight. However, + // if we did do all remaining delayed work, then lets kill the WM_TIMER. + if (more_work_is_plausible && delayed_work_time_.is_null()) + KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + WaitForWork(); // Wait (sleep) until we have work to do again. + } +} + +void MessagePumpForUI::InitMessageWnd() { + HINSTANCE hinst = GetModuleHandle(NULL); + + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = WndProcThunk; + wc.hInstance = hinst; + wc.lpszClassName = kWndClass; + RegisterClassEx(&wc); + + message_hwnd_ = + CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0); + DCHECK(message_hwnd_); +} + +void MessagePumpForUI::WaitForWork() { + // Wait until a message is available, up to the time needed by the timer + // manager to fire the next set of timers. + int delay = GetCurrentDelay(); + if (delay < 0) // Negative value means no timers waiting. + delay = INFINITE; + + mozilla::widget::WinUtils::WaitForMessage(delay); +} + +void MessagePumpForUI::HandleWorkMessage() { + // If we are being called outside of the context of Run, then don't try to do + // any work. This could correspond to a MessageBox call or something of that + // sort. + if (!state_) { + // Since we handled a kMsgHaveWork message, we must still update this flag. + InterlockedExchange(&have_work_, 0); + return; + } + + // Let whatever would have run had we not been putting messages in the queue + // run now. This is an attempt to make our dummy message not starve other + // messages that may be in the Windows message queue. + ProcessPumpReplacementMessage(); + + // Now give the delegate a chance to do some work. He'll let us know if he + // needs to do more work. + if (state_->delegate->DoWork()) + ScheduleWork(); +} + +void MessagePumpForUI::HandleTimerMessage() { + KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); + + // If we are being called outside of the context of Run, then don't do + // anything. This could correspond to a MessageBox call or something of + // that sort. + if (!state_) + return; + + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (!delayed_work_time_.is_null()) { + // A bit gratuitous to set delayed_work_time_ again, but oh well. + ScheduleDelayedWork(delayed_work_time_); + } +} + +bool MessagePumpForUI::ProcessNextWindowsMessage() { + // If there are sent messages in the queue then PeekMessage internally + // dispatches the message and returns false. We return true in this + // case to ensure that the message loop peeks again instead of calling + // MsgWaitForMultipleObjectsEx again. + bool sent_messages_in_queue = false; + DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); + if (HIWORD(queue_status) & QS_SENDMESSAGE) + sent_messages_in_queue = true; + + MSG msg; + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + return ProcessMessageHelper(msg); + + return sent_messages_in_queue; +} + +bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { + if (WM_QUIT == msg.message) { + // Repost the QUIT message so that it will be retrieved by the primary + // GetMessage() loop. + state_->should_quit = true; + PostQuitMessage(static_cast<int>(msg.wParam)); + return false; + } + + // While running our main message pump, we discard kMsgHaveWork messages. + if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) + return ProcessPumpReplacementMessage(); + + WillProcessMessage(msg); + + if (state_->dispatcher) { + if (!state_->dispatcher->Dispatch(msg)) + state_->should_quit = true; + } else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + DidProcessMessage(msg); + return true; +} + +bool MessagePumpForUI::ProcessPumpReplacementMessage() { + // When we encounter a kMsgHaveWork message, this method is called to peek + // and process a replacement message, such as a WM_PAINT or WM_TIMER. The + // goal is to make the kMsgHaveWork as non-intrusive as possible, even though + // a continuous stream of such messages are posted. This method carefully + // peeks a message while there is no chance for a kMsgHaveWork to be pending, + // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to + // possibly be posted), and finally dispatches that peeked replacement. Note + // that the re-post of kMsgHaveWork may be asynchronous to this thread!! + + MSG msg; + bool have_message = false; + if (MessageLoop::current()->os_modal_loop()) { + // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. + have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || + PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); + } else { + have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); + + if (have_message && msg.message == WM_NULL) + have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); + } + + DCHECK(!have_message || kMsgHaveWork != msg.message || + msg.hwnd != message_hwnd_); + + // Since we discarded a kMsgHaveWork message, we must update the flag. + int old_have_work = InterlockedExchange(&have_work_, 0); + DCHECK(old_have_work); + + // We don't need a special time slice if we didn't have_message to process. + if (!have_message) + return false; + + // Guarantee we'll get another time slice in the case where we go into native + // windows code. This ScheduleWork() may hurt performance a tiny bit when + // tasks appear very infrequently, but when the event queue is busy, the + // kMsgHaveWork events get (percentage wise) rarer and rarer. + ScheduleWork(); + return ProcessMessageHelper(msg); +} + +//----------------------------------------------------------------------------- +// MessagePumpForIO public: + +MessagePumpForIO::MessagePumpForIO() { + port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1)); + DCHECK(port_.IsValid()); +} + +void MessagePumpForIO::ScheduleWork() { + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + BOOL ret = PostQueuedCompletionStatus(port_, 0, + reinterpret_cast<ULONG_PTR>(this), + reinterpret_cast<OVERLAPPED*>(this)); + DCHECK(ret); +} + +void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // We know that we can't be blocked right now since this method can only be + // called on the same thread as Run, so we only need to update our record of + // how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, + IOHandler* handler) { + ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler); + HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1); + DCHECK(port == port_.Get()); +} + +//----------------------------------------------------------------------------- +// MessagePumpForIO private: + +void MessagePumpForIO::DoRunLoop() { + for (;;) { + // If we do any work, we may create more messages etc., and more work may + // possibly be waiting in another task group. When we (for example) + // WaitForIOCompletion(), there is a good chance there are still more + // messages waiting. On the other hand, when any of these methods return + // having done no work, then it is pretty unlikely that calling them + // again quickly will find any work to do. Finally, if they all say they + // had no work, then it is a good time to consider sleeping (waiting) for + // more work. + + bool more_work_is_plausible = state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= WaitForIOCompletion(0, NULL); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + WaitForWork(); // Wait (sleep) until we have work to do again. + } +} + +// Wait until IO completes, up to the time needed by the timer manager to fire +// the next set of timers. +void MessagePumpForIO::WaitForWork() { + // We do not support nested IO message loops. This is to avoid messy + // recursion problems. + DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!"; + + int timeout = GetCurrentDelay(); + if (timeout < 0) // Negative value means no timers waiting. + timeout = INFINITE; + + WaitForIOCompletion(timeout, NULL); +} + +bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { + IOItem item; + if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) { + // We have to ask the system for another IO completion. + if (!GetIOItem(timeout, &item)) + return false; + + if (ProcessInternalIOItem(item)) + return true; + } + + if (item.context->handler) { + if (filter && item.handler != filter) { + // Save this item for later + completed_io_.push_back(item); + } else { + DCHECK(item.context->handler == item.handler); + item.handler->OnIOCompleted(item.context, item.bytes_transfered, + item.error); + } + } else { + // The handler must be gone by now, just cleanup the mess. + delete item.context; + } + return true; +} + +// Asks the OS for another IO completion result. +bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { + memset(item, 0, sizeof(*item)); + ULONG_PTR key = 0; + OVERLAPPED* overlapped = NULL; + if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key, + &overlapped, timeout)) { + if (!overlapped) + return false; // Nothing in the queue. + item->error = GetLastError(); + item->bytes_transfered = 0; + } + + item->handler = reinterpret_cast<IOHandler*>(key); + item->context = reinterpret_cast<IOContext*>(overlapped); + return true; +} + +bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { + if (this == reinterpret_cast<MessagePumpForIO*>(item.context) && + this == reinterpret_cast<MessagePumpForIO*>(item.handler)) { + // This is our internal completion. + DCHECK(!item.bytes_transfered); + InterlockedExchange(&have_work_, 0); + return true; + } + return false; +} + +// Returns a completion item that was previously received. +bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { + DCHECK(!completed_io_.empty()); + for (std::list<IOItem>::iterator it = completed_io_.begin(); + it != completed_io_.end(); ++it) { + if (!filter || it->handler == filter) { + *item = *it; + completed_io_.erase(it); + return true; + } + } + return false; +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_win.h b/ipc/chromium/src/base/message_pump_win.h new file mode 100644 index 0000000000..ddb6e0d459 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_win.h @@ -0,0 +1,348 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_MESSAGE_PUMP_WIN_H_ +#define BASE_MESSAGE_PUMP_WIN_H_ + +#include <windows.h> + +#include <list> + +#include "base/lock.h" +#include "base/message_pump.h" +#include "base/observer_list.h" +#include "base/scoped_handle.h" +#include "base/time.h" + +namespace base { + +// MessagePumpWin serves as the base for specialized versions of the MessagePump +// for Windows. It provides basic functionality like handling of observers and +// controlling the lifetime of the message pump. +class MessagePumpWin : public MessagePump { + public: + // An Observer is an object that receives global notifications from the + // MessageLoop. + // + // NOTE: An Observer implementation should be extremely fast! + // + class Observer { + public: + virtual ~Observer() {} + + // This method is called before processing a message. + // The message may be undefined in which case msg.message is 0 + virtual void WillProcessMessage(const MSG& msg) = 0; + + // This method is called when control returns from processing a UI message. + // The message may be undefined in which case msg.message is 0 + virtual void DidProcessMessage(const MSG& msg) = 0; + }; + + // Dispatcher is used during a nested invocation of Run to dispatch events. + // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not + // dispatch events (or invoke TranslateMessage), rather every message is + // passed to Dispatcher's Dispatch method for dispatch. It is up to the + // Dispatcher to dispatch, or not, the event. + // + // The nested loop is exited by either posting a quit, or returning false + // from Dispatch. + class Dispatcher { + public: + virtual ~Dispatcher() {} + // Dispatches the event. If true is returned processing continues as + // normal. If false is returned, the nested loop exits immediately. + virtual bool Dispatch(const MSG& msg) = 0; + }; + + MessagePumpWin() : have_work_(0), state_(NULL) {} + virtual ~MessagePumpWin() {} + + // Add an Observer, which will start receiving notifications immediately. + void AddObserver(Observer* observer); + + // Remove an Observer. It is safe to call this method while an Observer is + // receiving a notification callback. + void RemoveObserver(Observer* observer); + + // Give a chance to code processing additional messages to notify the + // message loop observers that another message has been processed. + void WillProcessMessage(const MSG& msg); + void DidProcessMessage(const MSG& msg); + + // Like MessagePump::Run, but MSG objects are routed through dispatcher. + void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher); + + // MessagePump methods: + virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); } + virtual void Quit(); + + protected: + struct RunState { + Delegate* delegate; + Dispatcher* dispatcher; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + }; + + virtual void DoRunLoop() = 0; + int GetCurrentDelay() const; + + ObserverList<Observer> observers_; + + // The time at which delayed work should run. + TimeTicks delayed_work_time_; + + // A boolean value used to indicate if there is a kMsgDoWork message pending + // in the Windows Message queue. There is at most one such message, and it + // can drive execution of tasks when a native message pump is running. + LONG have_work_; + + // State for the current invocation of Run. + RunState* state_; +}; + +//----------------------------------------------------------------------------- +// MessagePumpForUI extends MessagePumpWin with methods that are particular to a +// MessageLoop instantiated with TYPE_UI. +// +// MessagePumpForUI implements a "traditional" Windows message pump. It contains +// a nearly infinite loop that peeks out messages, and then dispatches them. +// Intermixed with those peeks are callouts to DoWork for pending tasks, and +// DoDelayedWork for pending timers. When there are no events to be serviced, +// this pump goes into a wait state. In most cases, this message pump handles +// all processing. +// +// However, when a task, or windows event, invokes on the stack a native dialog +// box or such, that window typically provides a bare bones (native?) message +// pump. That bare-bones message pump generally supports little more than a +// peek of the Windows message queue, followed by a dispatch of the peeked +// message. MessageLoop extends that bare-bones message pump to also service +// Tasks, at the cost of some complexity. +// +// The basic structure of the extension (refered to as a sub-pump) is that a +// special message, kMsgHaveWork, is repeatedly injected into the Windows +// Message queue. Each time the kMsgHaveWork message is peeked, checks are +// made for an extended set of events, including the availability of Tasks to +// run. +// +// After running a task, the special message kMsgHaveWork is again posted to +// the Windows Message queue, ensuring a future time slice for processing a +// future event. To prevent flooding the Windows Message queue, care is taken +// to be sure that at most one kMsgHaveWork message is EVER pending in the +// Window's Message queue. +// +// There are a few additional complexities in this system where, when there are +// no Tasks to run, this otherwise infinite stream of messages which drives the +// sub-pump is halted. The pump is automatically re-started when Tasks are +// queued. +// +// A second complexity is that the presence of this stream of posted tasks may +// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER. +// Such paint and timer events always give priority to a posted message, such as +// kMsgHaveWork messages. As a result, care is taken to do some peeking in +// between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork +// is peeked, and before a replacement kMsgHaveWork is posted). +// +// NOTE: Although it may seem odd that messages are used to start and stop this +// flow (as opposed to signaling objects, etc.), it should be understood that +// the native message pump will *only* respond to messages. As a result, it is +// an excellent choice. It is also helpful that the starter messages that are +// placed in the queue when new task arrive also awakens DoRunLoop. +// +class MessagePumpForUI : public MessagePumpWin { + public: + MessagePumpForUI(); + virtual ~MessagePumpForUI(); + + // MessagePump methods: + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Applications can call this to encourage us to process all pending WM_PAINT + // messages. This method will process all paint messages the Windows Message + // queue can provide, up to some fixed number (to avoid any infinite loops). + void PumpOutPendingPaintMessages(); + +protected: + virtual void DoRunLoop(); + + bool ProcessNextWindowsMessage(); + void InitMessageWnd(); + void WaitForWork(); + void HandleWorkMessage(); + void HandleTimerMessage(); + bool ProcessMessageHelper(const MSG& msg); + bool ProcessPumpReplacementMessage(); + + // A hidden message-only window. + HWND message_hwnd_; + + private: + static LRESULT CALLBACK WndProcThunk( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); +}; + +//----------------------------------------------------------------------------- +// MessagePumpForIO extends MessagePumpWin with methods that are particular to a +// MessageLoop instantiated with TYPE_IO. This version of MessagePump does not +// deal with Windows mesagges, and instead has a Run loop based on Completion +// Ports so it is better suited for IO operations. +// +class MessagePumpForIO : public MessagePumpWin { + public: + struct IOContext; + + // Clients interested in receiving OS notifications when asynchronous IO + // operations complete should implement this interface and register themselves + // with the message pump. + // + // Typical use #1: + // // Use only when there are no user's buffers involved on the actual IO, + // // so that all the cleanup can be done by the message pump. + // class MyFile : public IOHandler { + // MyFile() { + // ... + // context_ = new IOContext; + // context_->handler = this; + // message_pump->RegisterIOHandler(file_, this); + // } + // ~MyFile() { + // if (pending_) { + // // By setting the handler to NULL, we're asking for this context + // // to be deleted when received, without calling back to us. + // context_->handler = NULL; + // } else { + // delete context_; + // } + // } + // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + // DWORD error) { + // pending_ = false; + // } + // void DoSomeIo() { + // ... + // // The only buffer required for this operation is the overlapped + // // structure. + // ConnectNamedPipe(file_, &context_->overlapped); + // pending_ = true; + // } + // bool pending_; + // IOContext* context_; + // HANDLE file_; + // }; + // + // Typical use #2: + // class MyFile : public IOHandler { + // MyFile() { + // ... + // message_pump->RegisterIOHandler(file_, this); + // } + // // Plus some code to make sure that this destructor is not called + // // while there are pending IO operations. + // ~MyFile() { + // } + // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + // DWORD error) { + // ... + // delete context; + // } + // void DoSomeIo() { + // ... + // IOContext* context = new IOContext; + // // This is not used for anything. It just prevents the context from + // // being considered "abandoned". + // context->handler = this; + // ReadFile(file_, buffer, num_bytes, &read, &context->overlapped); + // } + // HANDLE file_; + // }; + // + // Typical use #3: + // Same as the previous example, except that in order to deal with the + // requirement stated for the destructor, the class calls WaitForIOCompletion + // from the destructor to block until all IO finishes. + // ~MyFile() { + // while(pending_) + // message_pump->WaitForIOCompletion(INFINITE, this); + // } + // + class IOHandler { + public: + virtual ~IOHandler() {} + // This will be called once the pending IO operation associated with + // |context| completes. |error| is the Win32 error code of the IO operation + // (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero + // on error. + virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + DWORD error) = 0; + }; + + // The extended context that should be used as the base structure on every + // overlapped IO operation. |handler| must be set to the registered IOHandler + // for the given file when the operation is started, and it can be set to NULL + // before the operation completes to indicate that the handler should not be + // called anymore, and instead, the IOContext should be deleted when the OS + // notifies the completion of this operation. Please remember that any buffers + // involved with an IO operation should be around until the callback is + // received, so this technique can only be used for IO that do not involve + // additional buffers (other than the overlapped structure itself). + struct IOContext { + OVERLAPPED overlapped; + IOHandler* handler; + }; + + MessagePumpForIO(); + virtual ~MessagePumpForIO() {} + + // MessagePump methods: + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Register the handler to be used when asynchronous IO for the given file + // completes. The registration persists as long as |file_handle| is valid, so + // |handler| must be valid as long as there is pending IO for the given file. + void RegisterIOHandler(HANDLE file_handle, IOHandler* handler); + + // Waits for the next IO completion that should be processed by |filter|, for + // up to |timeout| milliseconds. Return true if any IO operation completed, + // regardless of the involved handler, and false if the timeout expired. If + // the completion port received any message and the involved IO handler + // matches |filter|, the callback is called before returning from this code; + // if the handler is not the one that we are looking for, the callback will + // be postponed for another time, so reentrancy problems can be avoided. + // External use of this method should be reserved for the rare case when the + // caller is willing to allow pausing regular task dispatching on this thread. + bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); + + private: + struct IOItem { + IOHandler* handler; + IOContext* context; + DWORD bytes_transfered; + DWORD error; + }; + + virtual void DoRunLoop(); + void WaitForWork(); + bool MatchCompletedIOItem(IOHandler* filter, IOItem* item); + bool GetIOItem(DWORD timeout, IOItem* item); + bool ProcessInternalIOItem(const IOItem& item); + + // The completion port associated with this thread. + ScopedHandle port_; + // This list will be empty almost always. It stores IO completions that have + // not been delivered yet because somebody was doing cleanup. + std::list<IOItem> completed_io_; +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_WIN_H_ diff --git a/ipc/chromium/src/base/object_watcher.cc b/ipc/chromium/src/base/object_watcher.cc new file mode 100644 index 0000000000..ced911fa20 --- /dev/null +++ b/ipc/chromium/src/base/object_watcher.cc @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/object_watcher.h" + +#include "base/logging.h" + +namespace base { + +//----------------------------------------------------------------------------- + +class ObjectWatcher::Watch : public mozilla::Runnable { +public: + ObjectWatcher* watcher; // The associated ObjectWatcher instance + HANDLE object; // The object being watched + HANDLE wait_object; // Returned by RegisterWaitForSingleObject + MessageLoop* origin_loop; // Used to get back to the origin thread + Delegate* delegate; // Delegate to notify when signaled + bool did_signal; // DoneWaiting was called + + NS_IMETHOD Run() override { + // The watcher may have already been torn down, in which case we need to + // just get out of dodge. + if (!watcher) + return NS_OK; + + DCHECK(did_signal); + watcher->StopWatching(); + + delegate->OnObjectSignaled(object); + + return NS_OK; + } +}; + +//----------------------------------------------------------------------------- + +ObjectWatcher::ObjectWatcher() : watch_(nullptr) { +} + +ObjectWatcher::~ObjectWatcher() { + StopWatching(); +} + +bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) { + if (watch_) { + NOTREACHED() << "Already watching an object"; + return false; + } + + RefPtr<Watch> watch = new Watch; + watch->watcher = this; + watch->object = object; + watch->origin_loop = MessageLoop::current(); + watch->delegate = delegate; + watch->did_signal = false; + + // Since our job is to just notice when an object is signaled and report the + // result back to this thread, we can just run on a Windows wait thread. + DWORD wait_flags = WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE; + + if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting, + watch.get(), INFINITE, wait_flags)) { + NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError(); + return false; + } + + watch_ = watch.forget(); + + // We need to know if the current message loop is going away so we can + // prevent the wait thread from trying to access a dead message loop. + MessageLoop::current()->AddDestructionObserver(this); + return true; +} + +bool ObjectWatcher::StopWatching() { + if (!watch_) + return false; + + // Make sure ObjectWatcher is used in a single-threaded fashion. + DCHECK(watch_->origin_loop == MessageLoop::current()); + + // If DoneWaiting is in progress, we wait for it to finish. We know whether + // DoneWaiting happened or not by inspecting the did_signal flag. + if (!UnregisterWaitEx(watch_->wait_object, INVALID_HANDLE_VALUE)) { + NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError(); + return false; + } + + // Make sure that we see any mutation to did_signal. This should be a no-op + // since we expect that UnregisterWaitEx resulted in a memory barrier, but + // just to be sure, we're going to be explicit. + MemoryBarrier(); + + // If the watch has been posted, then we need to make sure it knows not to do + // anything once it is run. + watch_->watcher = NULL; + + watch_ = nullptr; + + MessageLoop::current()->RemoveDestructionObserver(this); + return true; +} + +HANDLE ObjectWatcher::GetWatchedObject() { + if (!watch_) + return NULL; + + return watch_->object; +} + +// static +void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) { + DCHECK(!timed_out); + + Watch* watch = static_cast<Watch*>(param); + RefPtr<Watch> addrefedWatch = watch; + + // Record that we ran this function. + watch->did_signal = true; + + // We rely on the locking in PostTask() to ensure that a memory barrier is + // provided, which in turn ensures our change to did_signal can be observed + // on the target thread. + watch->origin_loop->PostTask(addrefedWatch.forget()); +} + +void ObjectWatcher::WillDestroyCurrentMessageLoop() { + // Need to shutdown the watch so that we don't try to access the MessageLoop + // after this point. + StopWatching(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/object_watcher.h b/ipc/chromium/src/base/object_watcher.h new file mode 100644 index 0000000000..f3ddf7ef87 --- /dev/null +++ b/ipc/chromium/src/base/object_watcher.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_OBJECT_WATCHER_H_ +#define BASE_OBJECT_WATCHER_H_ + +#include <windows.h> +#ifdef GetClassName +#undef GetClassName +#endif + +#include "base/message_loop.h" + +namespace base { + +// A class that provides a means to asynchronously wait for a Windows object to +// become signaled. It is an abstraction around RegisterWaitForSingleObject +// that provides a notification callback, OnObjectSignaled, that runs back on +// the origin thread (i.e., the thread that called StartWatching). +// +// This class acts like a smart pointer such that when it goes out-of-scope, +// UnregisterWaitEx is automatically called, and any in-flight notification is +// suppressed. +// +// Typical usage: +// +// class MyClass : public base::ObjectWatcher::Delegate { +// public: +// void DoStuffWhenSignaled(HANDLE object) { +// watcher_.StartWatching(object, this); +// } +// virtual void OnObjectSignaled(HANDLE object) { +// // OK, time to do stuff! +// } +// private: +// base::ObjectWatcher watcher_; +// }; +// +// In the above example, MyClass wants to "do stuff" when object becomes +// signaled. ObjectWatcher makes this task easy. When MyClass goes out of +// scope, the watcher_ will be destroyed, and there is no need to worry about +// OnObjectSignaled being called on a deleted MyClass pointer. Easy! +// +class ObjectWatcher : public MessageLoop::DestructionObserver { + public: + class Delegate { + public: + virtual ~Delegate() {} + // Called from the MessageLoop when a signaled object is detected. To + // continue watching the object, AddWatch must be called again. + virtual void OnObjectSignaled(HANDLE object) = 0; + }; + + ObjectWatcher(); + ~ObjectWatcher(); + + // When the object is signaled, the given delegate is notified on the thread + // where StartWatching is called. The ObjectWatcher is not responsible for + // deleting the delegate. + // + // Returns true if the watch was started. Otherwise, false is returned. + // + bool StartWatching(HANDLE object, Delegate* delegate); + + // Stops watching. Does nothing if the watch has already completed. If the + // watch is still active, then it is canceled, and the associated delegate is + // not notified. + // + // Returns true if the watch was canceled. Otherwise, false is returned. + // + bool StopWatching(); + + // Returns the handle of the object being watched, or NULL if the object + // watcher is stopped. + HANDLE GetWatchedObject(); + + private: + // Called on a background thread when done waiting. + static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out); + + // MessageLoop::DestructionObserver implementation: + virtual void WillDestroyCurrentMessageLoop(); + + // Internal state. + class Watch; + RefPtr<Watch> watch_; + + DISALLOW_COPY_AND_ASSIGN(ObjectWatcher); +}; + +} // namespace base + +#endif // BASE_OBJECT_WATCHER_H_ diff --git a/ipc/chromium/src/base/observer_list.h b/ipc/chromium/src/base/observer_list.h new file mode 100644 index 0000000000..4dce7ae59b --- /dev/null +++ b/ipc/chromium/src/base/observer_list.h @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_OBSERVER_LIST_H__ +#define BASE_OBSERVER_LIST_H__ + +#include <algorithm> +#include <limits> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" + +#if defined(ANDROID) && defined(_STLP_STD_NAME) +using _STLP_STD_NAME::find; +#endif + +namespace base { + +/////////////////////////////////////////////////////////////////////////////// +// +// OVERVIEW: +// +// A container for a list of observers. Unlike a normal STL vector or list, +// this container can be modified during iteration without invalidating the +// iterator. So, it safely handles the case of an observer removing itself +// or other observers from the list while observers are being notified. +// +// TYPICAL USAGE: +// +// class MyWidget { +// public: +// ... +// +// class Observer { +// public: +// virtual void OnFoo(MyWidget* w) = 0; +// virtual void OnBar(MyWidget* w, int x, int y) = 0; +// }; +// +// void AddObserver(Observer* obs) { +// observer_list_.AddObserver(obs); +// } +// +// void RemoveObserver(Observer* obs) { +// observer_list_.RemoveObserver(obs); +// } +// +// void NotifyFoo() { +// FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this)); +// } +// +// void NotifyBar(int x, int y) { +// FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y)); +// } +// +// private: +// ObserverList<Observer> observer_list_; +// }; +// +// +/////////////////////////////////////////////////////////////////////////////// + +template <class ObserverType, bool check_empty = false> +class ObserverList { + public: + // Enumeration of which observers are notified. + enum NotificationType { + // Specifies that any observers added during notification are notified. + // This is the default type if non type is provided to the constructor. + NOTIFY_ALL, + + // Specifies that observers added while sending out notification are not + // notified. + NOTIFY_EXISTING_ONLY + }; + + ObserverList() : notify_depth_(0), type_(NOTIFY_ALL) {} + explicit ObserverList(NotificationType type) : notify_depth_(0), type_(type) {} + ~ObserverList() { + // When check_empty is true, assert that the list is empty on destruction. + if (check_empty) { + Compact(); + DCHECK_EQ(observers_.size(), 0U); + } + } + + // Add an observer to the list. + void AddObserver(ObserverType* obs) { + DCHECK(find(observers_.begin(), observers_.end(), obs) == observers_.end()) + << "Observers can only be added once!"; + observers_.push_back(obs); + } + + // Remove an observer from the list. + void RemoveObserver(ObserverType* obs) { + typename ListType::iterator it = + std::find(observers_.begin(), observers_.end(), obs); + if (it != observers_.end()) { + if (notify_depth_) { + *it = 0; + } else { + observers_.erase(it); + } + } + } + + size_t size() const { + return observers_.size(); + } + + ObserverType* GetElementAt(int index) const { + return observers_[index]; + } + + // An iterator class that can be used to access the list of observers. See + // also the FOREACH_OBSERVER macro defined below. + class Iterator { + public: + explicit Iterator(const ObserverList<ObserverType>& list) + : list_(list), + index_(0), + max_index_(list.type_ == NOTIFY_ALL ? + std::numeric_limits<size_t>::max() : + list.observers_.size()) { + ++list_.notify_depth_; + } + + ~Iterator() { + if (--list_.notify_depth_ == 0) + list_.Compact(); + } + + ObserverType* GetNext() { + ListType& observers = list_.observers_; + // Advance if the current element is null + size_t max_index = std::min(max_index_, observers.size()); + while (index_ < max_index && !observers[index_]) + ++index_; + return index_ < max_index ? observers[index_++] : NULL; + } + + private: + const ObserverList<ObserverType>& list_; + size_t index_; + size_t max_index_; + }; + + private: + typedef std::vector<ObserverType*> ListType; + + void Compact() const { + typename ListType::iterator it = observers_.begin(); + while (it != observers_.end()) { + if (*it) { + ++it; + } else { + it = observers_.erase(it); + } + } + } + + // These are marked mutable to facilitate having NotifyAll be const. + mutable ListType observers_; + mutable int notify_depth_; + NotificationType type_; + + friend class ObserverList::Iterator; + + DISALLOW_EVIL_CONSTRUCTORS(ObserverList); +}; + +} // namespace base + +#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \ + do { \ + base::ObserverList<ObserverType>::Iterator it(observer_list); \ + ObserverType* obs; \ + while ((obs = it.GetNext()) != NULL) \ + obs->func; \ + } while (0) + +#endif // BASE_OBSERVER_LIST_H__ diff --git a/ipc/chromium/src/base/pickle.cc b/ipc/chromium/src/base/pickle.cc new file mode 100644 index 0000000000..4422ea125d --- /dev/null +++ b/ipc/chromium/src/base/pickle.cc @@ -0,0 +1,588 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/pickle.h" + +#include "mozilla/Alignment.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/TypeTraits.h" + +#include <stdlib.h> + +#include <limits> +#include <string> +#include <algorithm> + +#include "nsDebug.h" + +//------------------------------------------------------------------------------ + +static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t), + "Insufficient alignment"); + +static const uint32_t kHeaderSegmentCapacity = 64; + +static const uint32_t kDefaultSegmentCapacity = 4096; + +static const char kBytePaddingMarker = char(0xbf); + +namespace { + +// We want to copy data to our payload as efficiently as possible. +// memcpy fits the bill for copying, but not all compilers or +// architectures support inlining memcpy from void*, which has unknown +// static alignment. However, we know that all the members of our +// payload will be aligned on memberAlignmentType boundaries. We +// therefore use that knowledge to construct a copier that will copy +// efficiently (via standard C++ assignment mechanisms) if the datatype +// needs that alignment or less, and memcpy otherwise. (The compiler +// may still inline memcpy, of course.) + +template<typename T, size_t size, bool hasSufficientAlignment> +struct Copier +{ + static void Copy(T* dest, const char* iter) { + memcpy(dest, iter, sizeof(T)); + } +}; + +// Copying 64-bit quantities happens often enough and can easily be made +// worthwhile on 32-bit platforms, so handle it specially. Only do it +// if 64-bit types aren't sufficiently aligned; the alignment +// requirements for them vary between 32-bit platforms. +#ifndef HAVE_64BIT_BUILD +template<typename T> +struct Copier<T, sizeof(uint64_t), false> +{ + static void Copy(T* dest, const char* iter) { +#if MOZ_LITTLE_ENDIAN + static const int loIndex = 0, hiIndex = 1; +#else + static const int loIndex = 1, hiIndex = 0; +#endif + static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*), + "Pointers have different alignments"); + const uint32_t* src = reinterpret_cast<const uint32_t*>(iter); + uint32_t* uint32dest = reinterpret_cast<uint32_t*>(dest); + uint32dest[loIndex] = src[loIndex]; + uint32dest[hiIndex] = src[hiIndex]; + } +}; +#endif + +template<typename T, size_t size> +struct Copier<T, size, true> +{ + static void Copy(T* dest, const char* iter) { + *dest = *reinterpret_cast<const T*>(iter); + } +}; + +} // anonymous namespace + +PickleIterator::PickleIterator(const Pickle& pickle) + : iter_(pickle.buffers_.Iter()) { + iter_.Advance(pickle.buffers_, pickle.header_size_); +} + +template<typename T> +void +PickleIterator::CopyInto(T* dest) { + static_assert(mozilla::IsPod<T>::value, "Copied type must be a POD type"); + Copier<T, sizeof(T), (MOZ_ALIGNOF(T) <= sizeof(Pickle::memberAlignmentType))>::Copy(dest, iter_.Data()); +} + +bool Pickle::IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const { + // Make sure we don't get into trouble where AlignInt(len) == 0. + MOZ_RELEASE_ASSERT(len < 64); + + return iter.iter_.HasRoomFor(AlignInt(len)); +} + +void Pickle::UpdateIter(PickleIterator* iter, uint32_t bytes) const { + // Make sure we don't get into trouble where AlignInt(bytes) == 0. + MOZ_RELEASE_ASSERT(bytes < 64); + + iter->iter_.Advance(buffers_, AlignInt(bytes)); +} + +// Payload is sizeof(Pickle::memberAlignmentType) aligned. + +Pickle::Pickle(uint32_t header_size) + : buffers_(AlignInt(header_size), kHeaderSegmentCapacity, kDefaultSegmentCapacity), + header_(nullptr), + header_size_(AlignInt(header_size)) { + DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header)); + DCHECK(header_size_ <= kHeaderSegmentCapacity); + header_ = reinterpret_cast<Header*>(buffers_.Start()); + header_->payload_size = 0; +} + +Pickle::Pickle(uint32_t header_size, const char* data, uint32_t length) + : buffers_(length, AlignCapacity(length), kDefaultSegmentCapacity), + header_(nullptr), + header_size_(AlignInt(header_size)) { + DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header)); + DCHECK(header_size <= kHeaderSegmentCapacity); + MOZ_RELEASE_ASSERT(header_size <= length); + + header_ = reinterpret_cast<Header*>(buffers_.Start()); + memcpy(header_, data, length); +} + +Pickle::Pickle(Pickle&& other) + : buffers_(mozilla::Move(other.buffers_)), + header_(other.header_), + header_size_(other.header_size_) { + other.header_ = nullptr; +} + +Pickle::~Pickle() { +} + +Pickle& Pickle::operator=(Pickle&& other) { + BufferList tmp = mozilla::Move(other.buffers_); + other.buffers_ = mozilla::Move(buffers_); + buffers_ = mozilla::Move(tmp); + + //std::swap(buffers_, other.buffers_); + std::swap(header_, other.header_); + std::swap(header_size_, other.header_size_); + return *this; +} + +bool Pickle::ReadBool(PickleIterator* iter, bool* result) const { + DCHECK(iter); + + int tmp; + if (!ReadInt(iter, &tmp)) + return false; + DCHECK(0 == tmp || 1 == tmp); + *result = tmp ? true : false; + return true; +} + +bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadInt(PickleIterator* iter, int* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +// Always written as a 64-bit value since the size for this type can +// differ between architectures. +bool Pickle::ReadLong(PickleIterator* iter, long* result) const { + DCHECK(iter); + + int64_t big_result = 0; + if (IteratorHasRoomFor(*iter, sizeof(big_result))) { + iter->CopyInto(&big_result); + UpdateIter(iter, sizeof(big_result)); + } else { + if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) { + return false; + } + } + DCHECK(big_result <= LONG_MAX && big_result >= LONG_MIN); + *result = static_cast<long>(big_result); + + return true; +} + +// Always written as a 64-bit value since the size for this type can +// differ between architectures. +bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const { + DCHECK(iter); + + uint64_t big_result = 0; + if (IteratorHasRoomFor(*iter, sizeof(big_result))) { + iter->CopyInto(&big_result); + UpdateIter(iter, sizeof(big_result)); + } else { + if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) { + return false; + } + } + DCHECK(big_result <= ULONG_MAX); + *result = static_cast<unsigned long>(big_result); + + return true; +} + +bool Pickle::ReadLength(PickleIterator* iter, int* result) const { + if (!ReadInt(iter, result)) + return false; + return ((*result) >= 0); +} + +// Always written as a 64-bit value since the size for this type can +// differ between architectures. +bool Pickle::ReadSize(PickleIterator* iter, size_t* result) const { + DCHECK(iter); + + uint64_t big_result = 0; + if (IteratorHasRoomFor(*iter, sizeof(big_result))) { + iter->CopyInto(&big_result); + UpdateIter(iter, sizeof(big_result)); + } else { + if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) { + return false; + } + } + DCHECK(big_result <= std::numeric_limits<size_t>::max()); + *result = static_cast<size_t>(big_result); + + return true; +} + +bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadDouble(PickleIterator* iter, double* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +// Always written as a 64-bit value since the size for this type can +// differ between architectures. +bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const { + DCHECK(iter); + + int64_t big_result = 0; + if (IteratorHasRoomFor(*iter, sizeof(big_result))) { + iter->CopyInto(&big_result); + UpdateIter(iter, sizeof(big_result)); + } else { + if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) { + return false; + } + } + + DCHECK(big_result <= std::numeric_limits<intptr_t>::max() && big_result >= std::numeric_limits<intptr_t>::min()); + *result = static_cast<intptr_t>(big_result); + + return true; +} + +bool Pickle::ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadString(PickleIterator* iter, std::string* result) const { + DCHECK(iter); + + int len; + if (!ReadLength(iter, &len)) + return false; + + auto chars = mozilla::MakeUnique<char[]>(len); + if (!ReadBytesInto(iter, chars.get(), len)) { + return false; + } + result->assign(chars.get(), len); + + return true; +} + +bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const { + DCHECK(iter); + + int len; + if (!ReadLength(iter, &len)) + return false; + // Avoid integer multiplication overflow. + if (len > INT_MAX / static_cast<int>(sizeof(wchar_t))) + return false; + + auto chars = mozilla::MakeUnique<wchar_t[]>(len); + if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) { + return false; + } + result->assign(chars.get(), len); + + return true; +} + +bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers, + uint32_t alignment) const +{ + DCHECK(iter); + DCHECK(buffers); + DCHECK(alignment == 4 || alignment == 8); + DCHECK(intptr_t(header_) % alignment == 0); + + if (AlignInt(length) < length) { + return false; + } + + uint32_t padding_len = intptr_t(iter->iter_.Data()) % alignment; + if (!iter->iter_.AdvanceAcrossSegments(buffers_, padding_len)) { + return false; + } + + bool success; + *buffers = const_cast<BufferList*>(&buffers_)->Extract(iter->iter_, length, &success); + if (!success) { + return false; + } + + return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length); +} + +bool Pickle::ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const { + if (AlignInt(length) < length) { + return false; + } + + if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) { + return false; + } + + return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length); +} + +#ifdef MOZ_PICKLE_SENTINEL_CHECKING +bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const { + uint32_t found; + if (!ReadUInt32(iter, &found)) { + return false; + } + return found == sentinel; +} + +bool Pickle::WriteSentinel(uint32_t sentinel) { + return WriteUInt32(sentinel); +} +#endif + +void Pickle::EndRead(PickleIterator& iter) const { + DCHECK(iter.iter_.Done()); +} + +void Pickle::BeginWrite(uint32_t length, uint32_t alignment) { + DCHECK(alignment % 4 == 0) << "Must be at least 32-bit aligned!"; + + // write at an alignment-aligned offset from the beginning of the header + uint32_t offset = AlignInt(header_->payload_size); + uint32_t padding = (header_size_ + offset) % alignment; + uint32_t new_size = offset + padding + AlignInt(length); + MOZ_RELEASE_ASSERT(new_size >= header_->payload_size); + + DCHECK(intptr_t(header_) % alignment == 0); + +#ifdef ARCH_CPU_64_BITS + DCHECK_LE(length, std::numeric_limits<uint32_t>::max()); +#endif + + if (padding) { + MOZ_RELEASE_ASSERT(padding <= 8); + static const char padding_data[8] = { + kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, + kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, + }; + buffers_.WriteBytes(padding_data, padding); + } + + DCHECK((header_size_ + header_->payload_size + padding) % alignment == 0); + + header_->payload_size = new_size; +} + +void Pickle::EndWrite(uint32_t length) { + // Zero-pad to keep tools like purify from complaining about uninitialized + // memory. + uint32_t padding = AlignInt(length) - length; + if (padding) { + MOZ_RELEASE_ASSERT(padding <= 4); + static const char padding_data[4] = { + kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, + }; + buffers_.WriteBytes(padding_data, padding); + } +} + +bool Pickle::WriteBytes(const void* data, uint32_t data_len, uint32_t alignment) { + DCHECK(alignment == 4 || alignment == 8); + DCHECK(intptr_t(header_) % alignment == 0); + + BeginWrite(data_len, alignment); + + buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len); + + EndWrite(data_len); + return true; +} + +bool Pickle::WriteString(const std::string& value) { +#ifdef MOZ_FAULTY + std::string v(value); + Singleton<mozilla::ipc::Faulty>::get()->FuzzString(v); + if (!WriteInt(static_cast<int>(v.size()))) + return false; + + return WriteBytes(v.data(), static_cast<int>(v.size())); +#else + if (!WriteInt(static_cast<int>(value.size()))) + return false; + + return WriteBytes(value.data(), static_cast<int>(value.size())); +#endif +} + +bool Pickle::WriteWString(const std::wstring& value) { +#ifdef MOZ_FAULTY + std::wstring v(value); + Singleton<mozilla::ipc::Faulty>::get()->FuzzWString(v); + if (!WriteInt(static_cast<int>(v.size()))) + return false; + + return WriteBytes(v.data(), + static_cast<int>(v.size() * sizeof(wchar_t))); +#else + if (!WriteInt(static_cast<int>(value.size()))) + return false; + + return WriteBytes(value.data(), + static_cast<int>(value.size() * sizeof(wchar_t))); +#endif +} + +bool Pickle::WriteData(const char* data, uint32_t length) { +#ifdef MOZ_FAULTY + std::string v(data, length); + Singleton<mozilla::ipc::Faulty>::get()->FuzzData(v, v.size()); + return WriteInt(v.size()) && WriteBytes(v.data(), v.size()); +#else + return WriteInt(length) && WriteBytes(data, length); +#endif +} + +void Pickle::InputBytes(const char* data, uint32_t length) { + buffers_.WriteBytes(data, length); +} + +int32_t* Pickle::GetInt32PtrForTest(uint32_t offset) { + size_t pos = buffers_.Size() - offset; + BufferList::IterImpl iter(buffers_); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(buffers_, pos)); + return reinterpret_cast<int32_t*>(iter.Data()); +} + +// static +uint32_t Pickle::MessageSize(uint32_t header_size, + const char* start, + const char* end) { + DCHECK(header_size == AlignInt(header_size)); + DCHECK(header_size <= static_cast<memberAlignmentType>(kHeaderSegmentCapacity)); + + if (end < start) + return 0; + size_t length = static_cast<size_t>(end - start); + if (length < sizeof(Header)) + return 0; + + const Header* hdr = reinterpret_cast<const Header*>(start); + if (length < header_size) + return 0; + + mozilla::CheckedInt<uint32_t> sum(header_size); + sum += hdr->payload_size; + + if (!sum.isValid()) + return 0; + + return sum.value(); +} diff --git a/ipc/chromium/src/base/pickle.h b/ipc/chromium/src/base/pickle.h new file mode 100644 index 0000000000..d031f61245 --- /dev/null +++ b/ipc/chromium/src/base/pickle.h @@ -0,0 +1,326 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_PICKLE_H__ +#define BASE_PICKLE_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/string16.h" + +#include "mozilla/Attributes.h" +#include "mozilla/BufferList.h" +#include "mozilla/mozalloc.h" + +#ifdef MOZ_FAULTY +#include "base/singleton.h" +#include "mozilla/ipc/Faulty.h" +#endif + +#if !defined(RELEASE_OR_BETA) || defined(DEBUG) +#define MOZ_PICKLE_SENTINEL_CHECKING +#endif + +class Pickle; + +class PickleIterator { +public: + explicit PickleIterator(const Pickle& pickle); + +private: + friend class Pickle; + + mozilla::BufferList<InfallibleAllocPolicy>::IterImpl iter_; + + template<typename T> + void CopyInto(T* dest); +}; + +// This class provides facilities for basic binary value packing and unpacking. +// +// The Pickle class supports appending primitive values (ints, strings, etc.) +// to a pickle instance. The Pickle instance grows its internal memory buffer +// dynamically to hold the sequence of primitive values. The internal memory +// buffer is exposed as the "data" of the Pickle. This "data" can be passed +// to a Pickle object to initialize it for reading. +// +// When reading from a Pickle object, it is important for the consumer to know +// what value types to read and in what order to read them as the Pickle does +// not keep track of the type of data written to it. +// +// The Pickle's data has a header which contains the size of the Pickle's +// payload. It can optionally support additional space in the header. That +// space is controlled by the header_size parameter passed to the Pickle +// constructor. +// +class Pickle { + public: + ~Pickle(); + + Pickle() = delete; + + // Initialize a Pickle object with the specified header size in bytes, which + // must be greater-than-or-equal-to sizeof(Pickle::Header). The header size + // will be rounded up to ensure that the header size is 32bit-aligned. + explicit Pickle(uint32_t header_size); + + Pickle(uint32_t header_size, const char* data, uint32_t length); + + Pickle(const Pickle& other) = delete; + + Pickle(Pickle&& other); + + // Performs a deep copy. + Pickle& operator=(const Pickle& other) = delete; + + Pickle& operator=(Pickle&& other); + + // Returns the size of the Pickle's data. + uint32_t size() const { return header_size_ + header_->payload_size; } + + typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList; + + const BufferList& Buffers() const { return buffers_; } + + uint32_t CurrentSize() const { return buffers_.Size(); } + + // Methods for reading the payload of the Pickle. To read from the start of + // the Pickle, initialize *iter to NULL. If successful, these methods return + // true. Otherwise, false is returned to indicate that the result could not + // be extracted. + MOZ_MUST_USE bool ReadBool(PickleIterator* iter, bool* result) const; + MOZ_MUST_USE bool ReadInt16(PickleIterator* iter, int16_t* result) const; + MOZ_MUST_USE bool ReadUInt16(PickleIterator* iter, uint16_t* result) const; + MOZ_MUST_USE bool ReadShort(PickleIterator* iter, short* result) const; + MOZ_MUST_USE bool ReadInt(PickleIterator* iter, int* result) const; + MOZ_MUST_USE bool ReadLong(PickleIterator* iter, long* result) const; + MOZ_MUST_USE bool ReadULong(PickleIterator* iter, unsigned long* result) const; + MOZ_MUST_USE bool ReadSize(PickleIterator* iter, size_t* result) const; + MOZ_MUST_USE bool ReadInt32(PickleIterator* iter, int32_t* result) const; + MOZ_MUST_USE bool ReadUInt32(PickleIterator* iter, uint32_t* result) const; + MOZ_MUST_USE bool ReadInt64(PickleIterator* iter, int64_t* result) const; + MOZ_MUST_USE bool ReadUInt64(PickleIterator* iter, uint64_t* result) const; + MOZ_MUST_USE bool ReadDouble(PickleIterator* iter, double* result) const; + MOZ_MUST_USE bool ReadIntPtr(PickleIterator* iter, intptr_t* result) const; + MOZ_MUST_USE bool ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const; + MOZ_MUST_USE bool ReadString(PickleIterator* iter, std::string* result) const; + MOZ_MUST_USE bool ReadWString(PickleIterator* iter, std::wstring* result) const; + MOZ_MUST_USE bool ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const; + MOZ_MUST_USE bool ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers, + uint32_t alignment = sizeof(memberAlignmentType)) const; + + // Safer version of ReadInt() checks for the result not being negative. + // Use it for reading the object sizes. + MOZ_MUST_USE bool ReadLength(PickleIterator* iter, int* result) const; + + MOZ_MUST_USE bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const +#ifdef MOZ_PICKLE_SENTINEL_CHECKING + ; +#else + { + return true; + } +#endif + + void EndRead(PickleIterator& iter) const; + + // Methods for adding to the payload of the Pickle. These values are + // appended to the end of the Pickle's payload. When reading values from a + // Pickle, it is important to read them in the order in which they were added + // to the Pickle. + bool WriteBool(bool value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzBool(&value); +#endif + return WriteInt(value ? 1 : 0); + } + bool WriteInt16(int16_t value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzInt16(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt16(uint16_t value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzUInt16(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteInt(int value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzInt(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteLong(long value) { + // Always written as a 64-bit value since the size for this type can + // differ between architectures. +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzLong(&value); +#endif + return WriteInt64(int64_t(value)); + } + bool WriteULong(unsigned long value) { + // Always written as a 64-bit value since the size for this type can + // differ between architectures. +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzULong(&value); +#endif + return WriteUInt64(uint64_t(value)); + } + bool WriteSize(size_t value) { + // Always written as a 64-bit value since the size for this type can + // differ between architectures. +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzSize(&value); +#endif + return WriteUInt64(uint64_t(value)); + } + bool WriteInt32(int32_t value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzInt(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt32(uint32_t value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzUInt32(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteInt64(int64_t value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzInt64(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt64(uint64_t value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzUInt64(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteDouble(double value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzDouble(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteIntPtr(intptr_t value) { + // Always written as a 64-bit value since the size for this type can + // differ between architectures. + return WriteInt64(int64_t(value)); + } + bool WriteUnsignedChar(unsigned char value) { +#ifdef MOZ_FAULTY + Singleton<mozilla::ipc::Faulty>::get()->FuzzUChar(&value); +#endif + return WriteBytes(&value, sizeof(value)); + } + bool WriteString(const std::string& value); + bool WriteWString(const std::wstring& value); + bool WriteData(const char* data, uint32_t length); + bool WriteBytes(const void* data, uint32_t data_len, + uint32_t alignment = sizeof(memberAlignmentType)); + + bool WriteSentinel(uint32_t sentinel) +#ifdef MOZ_PICKLE_SENTINEL_CHECKING + ; +#else + { + return true; + } +#endif + + int32_t* GetInt32PtrForTest(uint32_t offset); + + void InputBytes(const char* data, uint32_t length); + + // Payload follows after allocation of Header (header size is customizable). + struct Header { + uint32_t payload_size; // Specifies the size of the payload. + }; + + // Returns the header, cast to a user-specified type T. The type T must be a + // subclass of Header and its size must correspond to the header_size passed + // to the Pickle constructor. + template <class T> + T* headerT() { + DCHECK(sizeof(T) == header_size_); + return static_cast<T*>(header_); + } + template <class T> + const T* headerT() const { + DCHECK(sizeof(T) == header_size_); + return static_cast<const T*>(header_); + } + + typedef uint32_t memberAlignmentType; + + protected: + uint32_t payload_size() const { return header_->payload_size; } + + // Resizes the buffer for use when writing the specified amount of data. The + // location that the data should be written at is returned, or NULL if there + // was an error. Call EndWrite with the returned offset and the given length + // to pad out for the next write. + void BeginWrite(uint32_t length, uint32_t alignment); + + // Completes the write operation by padding the data with NULL bytes until it + // is padded. Should be paired with BeginWrite, but it does not necessarily + // have to be called after the data is written. + void EndWrite(uint32_t length); + + // Round 'bytes' up to the next multiple of 'alignment'. 'alignment' must be + // a power of 2. + template<uint32_t alignment> struct ConstantAligner { + static uint32_t align(int bytes) { + static_assert((alignment & (alignment - 1)) == 0, + "alignment must be a power of two"); + return (bytes + (alignment - 1)) & ~static_cast<uint32_t>(alignment - 1); + } + }; + + static uint32_t AlignInt(int bytes) { + return ConstantAligner<sizeof(memberAlignmentType)>::align(bytes); + } + + static uint32_t AlignCapacity(int bytes) { + return ConstantAligner<kSegmentAlignment>::align(bytes); + } + + // Returns true if the given iterator could point to data with the given + // length. If there is no room for the given data before the end of the + // payload, returns false. + bool IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const; + + // Moves the iterator by the given number of bytes, making sure it is aligned. + // Pointer (iterator) is NOT aligned, but the change in the pointer + // is guaranteed to be a multiple of sizeof(memberAlignmentType). + void UpdateIter(PickleIterator* iter, uint32_t bytes) const; + + // Figure out how big the message starting at range_start is. Returns 0 if + // there's no enough data to determine (i.e., if [range_start, range_end) does + // not contain enough of the message header to know the size). + static uint32_t MessageSize(uint32_t header_size, + const char* range_start, + const char* range_end); + + // Segments capacities are aligned to 8 bytes to ensure that all reads/writes + // at 8-byte aligned offsets will be on 8-byte aligned pointers. + static const uint32_t kSegmentAlignment = 8; + + private: + friend class PickleIterator; + + BufferList buffers_; + Header* header_; + uint32_t header_size_; +}; + +#endif // BASE_PICKLE_H__ diff --git a/ipc/chromium/src/base/platform_file.h b/ipc/chromium/src/base/platform_file.h new file mode 100644 index 0000000000..326a7a0e17 --- /dev/null +++ b/ipc/chromium/src/base/platform_file.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_PLATFORM_FILE_H_ +#define BASE_PLATFORM_FILE_H_ + +#include "build/build_config.h" +#if defined(OS_WIN) +#include <windows.h> +#endif + +#include <string> + +namespace base { + +#if defined(OS_WIN) +typedef HANDLE PlatformFile; +const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; +#elif defined(OS_POSIX) +typedef int PlatformFile; +const PlatformFile kInvalidPlatformFileValue = -1; +#endif + +enum PlatformFileFlags { + PLATFORM_FILE_OPEN = 1, + PLATFORM_FILE_CREATE = 2, + PLATFORM_FILE_OPEN_ALWAYS = 4, // May create a new file. + PLATFORM_FILE_CREATE_ALWAYS = 8, // May overwrite an old file. + PLATFORM_FILE_READ = 16, + PLATFORM_FILE_WRITE = 32, + PLATFORM_FILE_EXCLUSIVE_READ = 64, // EXCLUSIVE is opposite of Windows SHARE + PLATFORM_FILE_EXCLUSIVE_WRITE = 128, + PLATFORM_FILE_ASYNC = 256 +}; + +// Creates or open the given file. If PLATFORM_FILE_OPEN_ALWAYS is used, and +// |created| is provided, |created| will be set to true if the file was created +// or to false in case the file was just opened. +PlatformFile CreatePlatformFile(const std::wstring& name, + int flags, + bool* created); + +} // namespace base + +#endif // BASE_PLATFORM_FILE_H_ diff --git a/ipc/chromium/src/base/platform_file_posix.cc b/ipc/chromium/src/base/platform_file_posix.cc new file mode 100644 index 0000000000..4289afbc68 --- /dev/null +++ b/ipc/chromium/src/base/platform_file_posix.cc @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/platform_file.h" + +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#ifdef ANDROID +#include <linux/stat.h> +#endif + +#include "base/logging.h" +#include "base/string_util.h" + +namespace base { + +// TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here? +PlatformFile CreatePlatformFile(const std::wstring& name, + int flags, + bool* created) { + int open_flags = 0; + if (flags & PLATFORM_FILE_CREATE) + open_flags = O_CREAT | O_EXCL; + + if (flags & PLATFORM_FILE_CREATE_ALWAYS) { + DCHECK(!open_flags); + open_flags = O_CREAT | O_TRUNC; + } + + if (!open_flags && !(flags & PLATFORM_FILE_OPEN) && + !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { + NOTREACHED(); + errno = ENOTSUP; + return kInvalidPlatformFileValue; + } + + if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) { + open_flags |= O_RDWR; + } else if (flags & PLATFORM_FILE_WRITE) { + open_flags |= O_WRONLY; + } else if (!(flags & PLATFORM_FILE_READ)) { + NOTREACHED(); + } + + DCHECK(O_RDONLY == 0); + + int descriptor = open(WideToUTF8(name).c_str(), open_flags, + S_IRUSR | S_IWUSR); + + if (flags & PLATFORM_FILE_OPEN_ALWAYS) { + if (descriptor > 0) { + if (created) + *created = false; + } else { + open_flags |= O_CREAT; + descriptor = open(WideToUTF8(name).c_str(), open_flags, + S_IRUSR | S_IWUSR); + if (created && descriptor > 0) + *created = true; + } + } + + return descriptor; +} + +} // namespace base diff --git a/ipc/chromium/src/base/platform_file_win.cc b/ipc/chromium/src/base/platform_file_win.cc new file mode 100644 index 0000000000..a624acd65d --- /dev/null +++ b/ipc/chromium/src/base/platform_file_win.cc @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/platform_file.h" + +#include "base/logging.h" + +namespace base { + +PlatformFile CreatePlatformFile(const std::wstring& name, + int flags, + bool* created) { + DWORD disposition = 0; + + if (flags & PLATFORM_FILE_OPEN) + disposition = OPEN_EXISTING; + + if (flags & PLATFORM_FILE_CREATE) { + DCHECK(!disposition); + disposition = CREATE_NEW; + } + + if (flags & PLATFORM_FILE_OPEN_ALWAYS) { + DCHECK(!disposition); + disposition = OPEN_ALWAYS; + } + + if (flags & PLATFORM_FILE_CREATE_ALWAYS) { + DCHECK(!disposition); + disposition = CREATE_ALWAYS; + } + + if (!disposition) { + NOTREACHED(); + return NULL; + } + + DWORD access = (flags & PLATFORM_FILE_READ) ? GENERIC_READ : 0; + if (flags & PLATFORM_FILE_WRITE) + access |= GENERIC_WRITE; + + DWORD sharing = (flags & PLATFORM_FILE_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; + if (!(flags & PLATFORM_FILE_EXCLUSIVE_WRITE)) + sharing |= FILE_SHARE_WRITE; + + DWORD create_flags = 0; + if (flags & PLATFORM_FILE_ASYNC) + create_flags |= FILE_FLAG_OVERLAPPED; + + HANDLE file = CreateFile(name.c_str(), access, sharing, NULL, disposition, + create_flags, NULL); + + if ((flags & PLATFORM_FILE_OPEN_ALWAYS) && created && + INVALID_HANDLE_VALUE != file) { + *created = (ERROR_ALREADY_EXISTS != GetLastError()); + } + + return file; +} + +} // namespace disk_cache diff --git a/ipc/chromium/src/base/platform_thread.h b/ipc/chromium/src/base/platform_thread.h new file mode 100644 index 0000000000..727a13a84c --- /dev/null +++ b/ipc/chromium/src/base/platform_thread.h @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// WARNING: You should *NOT* be using this class directly. PlatformThread is +// the low-level platform-specific abstraction to the OS's threading interface. +// You should instead be using a message-loop driven Thread, see thread.h. + +#ifndef BASE_PLATFORM_THREAD_H_ +#define BASE_PLATFORM_THREAD_H_ + +#include "base/basictypes.h" + +// PlatformThreadHandle should not be assumed to be a numeric type, since the +// standard intends to allow pthread_t to be a structure. This means you +// should not initialize it to a value, like 0. If it's a member variable, the +// constructor can safely "value initialize" using () in the initializer list. +#if defined(OS_WIN) +#include <windows.h> +typedef DWORD PlatformThreadId; +typedef void* PlatformThreadHandle; // HANDLE +#elif defined(OS_POSIX) +#include <pthread.h> +typedef pthread_t PlatformThreadHandle; +#if defined(OS_LINUX) || defined(OS_OPENBSD) || defined(__GLIBC__) +#include <unistd.h> +typedef pid_t PlatformThreadId; +#elif defined(OS_BSD) +#include <sys/types.h> +typedef lwpid_t PlatformThreadId; +#elif defined(OS_MACOSX) +#include <mach/mach.h> +typedef mach_port_t PlatformThreadId; +#endif +#endif + +// A namespace for low-level thread functions. +class PlatformThread { + public: + // Gets the current thread id, which may be useful for logging purposes. + static PlatformThreadId CurrentId(); + + // Yield the current thread so another thread can be scheduled. + static void YieldCurrentThread(); + + // Sleeps for the specified duration (units are milliseconds). + static void Sleep(int duration_ms); + + // Sets the thread name visible to a debugger. This has no effect otherwise. + static void SetName(const char* name); + + // Implement this interface to run code on a background thread. Your + // ThreadMain method will be called on the newly created thread. + class Delegate { + public: + virtual ~Delegate() {} + virtual void ThreadMain() = 0; + }; + + // Creates a new thread. The |stack_size| parameter can be 0 to indicate + // that the default stack size should be used. Upon success, + // |*thread_handle| will be assigned a handle to the newly created thread, + // and |delegate|'s ThreadMain method will be executed on the newly created + // thread. + // NOTE: When you are done with the thread handle, you must call Join to + // release system resources associated with the thread. You must ensure that + // the Delegate object outlives the thread. + static bool Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle); + + // CreateNonJoinable() does the same thing as Create() except the thread + // cannot be Join()'d. Therefore, it also does not output a + // PlatformThreadHandle. + static bool CreateNonJoinable(size_t stack_size, Delegate* delegate); + + // Joins with a thread created via the Create function. This function blocks + // the caller until the designated thread exits. This will invalidate + // |thread_handle|. + static void Join(PlatformThreadHandle thread_handle); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); +}; + +#endif // BASE_PLATFORM_THREAD_H_ diff --git a/ipc/chromium/src/base/platform_thread_mac.mm b/ipc/chromium/src/base/platform_thread_mac.mm new file mode 100644 index 0000000000..ef558a7968 --- /dev/null +++ b/ipc/chromium/src/base/platform_thread_mac.mm @@ -0,0 +1,77 @@ +// Copyright (c) 2006-2008 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 "base/platform_thread.h" + +#import <Foundation/Foundation.h> +#include <dlfcn.h> + +#include "base/logging.h" +#include "base/scoped_nsautorelease_pool.h" + +// A simple class that demonstrates our impressive ability to do nothing. +@interface NoOp : NSObject + +// Does the deed. Or does it? ++ (void)noOp; + +@end + +@implementation NoOp + ++ (void)noOp { +} + +@end + +namespace base { + +// If Cocoa is to be used on more than one thread, it must know that the +// application is multithreaded. Since it's possible to enter Cocoa code +// from threads created by pthread_thread_create, Cocoa won't necessarily +// be aware that the application is multithreaded. Spawning an NSThread is +// enough to get Cocoa to set up for multithreaded operation, so this is done +// if necessary before pthread_thread_create spawns any threads. +// +// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html +void InitThreading() { + // this is called early in startup, before the event loop, so provide + // an autorelease pool to prevent leaks here + ScopedNSAutoreleasePool pool; + + static BOOL multithreaded = [NSThread isMultiThreaded]; + if (!multithreaded) { + [NSThread detachNewThreadSelector:@selector(noOp) + toTarget:[NoOp class] + withObject:nil]; + multithreaded = YES; + + DCHECK([NSThread isMultiThreaded]); + } +} + +} // namespace base + +// static +void PlatformThread::SetName(const char* name) { + // pthread_setname_np is only available in 10.6 or later, so test + // for it at runtime. + int (*dynamic_pthread_setname_np)(const char*); + *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = + dlsym(RTLD_DEFAULT, "pthread_setname_np"); + if (!dynamic_pthread_setname_np) + return; + + // Mac OS X does not expose the length limit of the name, so + // hardcode it. + const int kMaxNameLength = 63; + std::string shortened_name = std::string(name).substr(0, kMaxNameLength); + // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does. + // See http://crbug.com/47058 + + // The name parameter is copied thus it's safe to release it after calling. + // Checked against the bionic implementation in bionic/libc/bionic/pthread.c + dynamic_pthread_setname_np(shortened_name.c_str()); +} + diff --git a/ipc/chromium/src/base/platform_thread_posix.cc b/ipc/chromium/src/base/platform_thread_posix.cc new file mode 100644 index 0000000000..6c978336ee --- /dev/null +++ b/ipc/chromium/src/base/platform_thread_posix.cc @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/platform_thread.h" + +#include <errno.h> +#include <sched.h> + +#if defined(OS_MACOSX) +#include <mach/mach.h> +#elif defined(OS_NETBSD) +#include <lwp.h> +#elif defined(OS_LINUX) +#include <sys/syscall.h> +#include <sys/prctl.h> +#endif + +#if !defined(OS_MACOSX) +#include <unistd.h> +#endif + +#if defined(OS_BSD) && !defined(OS_NETBSD) && !defined(__GLIBC__) +#include <pthread_np.h> +#endif + +#if defined(OS_MACOSX) +namespace base { +void InitThreading(); +} // namespace +#endif + +static void* ThreadFunc(void* closure) { + PlatformThread::Delegate* delegate = + static_cast<PlatformThread::Delegate*>(closure); + delegate->ThreadMain(); + return NULL; +} + +// static +PlatformThreadId PlatformThread::CurrentId() { + // Pthreads doesn't have the concept of a thread ID, so we have to reach down + // into the kernel. +#if defined(OS_MACOSX) + mach_port_t port = mach_thread_self(); + mach_port_deallocate(mach_task_self(), port); + return port; +#elif defined(OS_LINUX) +#ifdef MOZ_WIDGET_GONK + return (intptr_t) (pthread_self()); +#else + return syscall(__NR_gettid); +#endif +#elif defined(OS_OPENBSD) || defined(__GLIBC__) + return (intptr_t) (pthread_self()); +#elif defined(OS_NETBSD) + return _lwp_self(); +#elif defined(OS_DRAGONFLY) + return lwp_gettid(); +#elif defined(OS_FREEBSD) + return pthread_getthreadid_np(); +#endif +} + +// static +void PlatformThread::YieldCurrentThread() { + sched_yield(); +} + +// static +void PlatformThread::Sleep(int duration_ms) { + struct timespec sleep_time, remaining; + + // Contains the portion of duration_ms >= 1 sec. + sleep_time.tv_sec = duration_ms / 1000; + duration_ms -= sleep_time.tv_sec * 1000; + + // Contains the portion of duration_ms < 1 sec. + sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds. + + while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) + sleep_time = remaining; +} + +#ifndef OS_MACOSX +// Mac is implemented in platform_thread_mac.mm. + +// static +void PlatformThread::SetName(const char* name) { + // On linux we can get the thread names to show up in the debugger by setting + // the process name for the LWP. We don't want to do this for the main + // thread because that would rename the process, causing tools like killall + // to stop working. + if (PlatformThread::CurrentId() == getpid()) + return; + + // http://0pointer.de/blog/projects/name-your-threads.html + // Set the name for the LWP (which gets truncated to 15 characters). + // Note that glibc also has a 'pthread_setname_np' api, but it may not be + // available everywhere and it's only benefit over using prctl directly is + // that it can set the name of threads other than the current thread. +#if defined(OS_LINUX) + prctl(PR_SET_NAME, reinterpret_cast<uintptr_t>(name), 0, 0, 0); +#elif defined(OS_NETBSD) + pthread_setname_np(pthread_self(), "%s", (void *)name); +#elif defined(OS_BSD) && !defined(__GLIBC__) + pthread_set_name_np(pthread_self(), name); +#else +#endif +} +#endif // !OS_MACOSX + +namespace { + +bool CreateThread(size_t stack_size, bool joinable, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* thread_handle) { +#if defined(OS_MACOSX) + base::InitThreading(); +#endif // OS_MACOSX + + bool success = false; + pthread_attr_t attributes; + pthread_attr_init(&attributes); + + // Pthreads are joinable by default, so only specify the detached attribute if + // the thread should be non-joinable. + if (!joinable) { + pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); + } + + if (stack_size > 0) + pthread_attr_setstacksize(&attributes, stack_size); + + success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate); + + pthread_attr_destroy(&attributes); + return success; +} + +} // anonymous namespace + +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + return CreateThread(stack_size, true /* joinable thread */, + delegate, thread_handle); +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + PlatformThreadHandle unused; + + bool result = CreateThread(stack_size, false /* non-joinable thread */, + delegate, &unused); + return result; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + pthread_join(thread_handle, NULL); +} diff --git a/ipc/chromium/src/base/platform_thread_win.cc b/ipc/chromium/src/base/platform_thread_win.cc new file mode 100644 index 0000000000..2e76142306 --- /dev/null +++ b/ipc/chromium/src/base/platform_thread_win.cc @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/platform_thread.h" + +#include "base/logging.h" +#include "base/win_util.h" + +namespace { + +// The information on how to set the thread name comes from +// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx +const DWORD kVCThreadNameException = 0x406D1388; + +typedef struct tagTHREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; + +DWORD __stdcall ThreadFunc(void* closure) { + PlatformThread::Delegate* delegate = + static_cast<PlatformThread::Delegate*>(closure); + delegate->ThreadMain(); + return 0; +} + +} // namespace + +// static +PlatformThreadId PlatformThread::CurrentId() { + return GetCurrentThreadId(); +} + +// static +void PlatformThread::YieldCurrentThread() { + ::Sleep(0); +} + +// static +void PlatformThread::Sleep(int duration_ms) { + ::Sleep(duration_ms); +} + +// static +void PlatformThread::SetName(const char* name) { +#ifdef HAVE_SEH_EXCEPTIONS + // The debugger needs to be around to catch the name in the exception. If + // there isn't a debugger, we are just needlessly throwing an exception. + if (!::IsDebuggerPresent()) + return; + + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = CurrentId(); + info.dwFlags = 0; + + MOZ_SEH_TRY { + RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD), + reinterpret_cast<DWORD_PTR*>(&info)); + } MOZ_SEH_EXCEPT(EXCEPTION_CONTINUE_EXECUTION) { + } +#endif +} + +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + unsigned int flags = 0; + if (stack_size > 0) { + flags = STACK_SIZE_PARAM_IS_A_RESERVATION; + } else { + stack_size = 0; + } + + // Using CreateThread here vs _beginthreadex makes thread creation a bit + // faster and doesn't require the loader lock to be available. Our code will + // have to work running on CreateThread() threads anyway, since we run code + // on the Windows thread pool, etc. For some background on the difference: + // http://www.microsoft.com/msj/1099/win32/win321099.aspx + *thread_handle = CreateThread( + NULL, stack_size, ThreadFunc, delegate, flags, NULL); + return *thread_handle != NULL; +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + PlatformThreadHandle thread_handle; + bool result = Create(stack_size, delegate, &thread_handle); + CloseHandle(thread_handle); + return result; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + DCHECK(thread_handle); + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + DWORD result = WaitForSingleObject(thread_handle, INFINITE); + DCHECK_EQ(WAIT_OBJECT_0, result); + + CloseHandle(thread_handle); +} diff --git a/ipc/chromium/src/base/port.h b/ipc/chromium/src/base/port.h new file mode 100644 index 0000000000..d206d2530e --- /dev/null +++ b/ipc/chromium/src/base/port.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_PORT_H_ +#define BASE_PORT_H_ + +#include <stdarg.h> +#include "build/build_config.h" + +#ifdef COMPILER_MSVC +#define GG_LONGLONG(x) x##I64 +#define GG_ULONGLONG(x) x##UI64 +#else +#define GG_LONGLONG(x) x##LL +#define GG_ULONGLONG(x) x##ULL +#endif + +// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including <stdint.h> +// to get the INTn_C and UINTn_C macros for integer constants. It's difficult +// to guarantee any specific ordering of header includes, so it's difficult to +// guarantee that the INTn_C macros can be defined by including <stdint.h> at +// any specific point. Provide GG_INTn_C macros instead. + +#define GG_INT8_C(x) (x) +#define GG_INT16_C(x) (x) +#define GG_INT32_C(x) (x) +#define GG_INT64_C(x) GG_LONGLONG(x) + +#define GG_UINT8_C(x) (x ## U) +#define GG_UINT16_C(x) (x ## U) +#define GG_UINT32_C(x) (x ## U) +#define GG_UINT64_C(x) GG_ULONGLONG(x) + +namespace base { + +// It's possible for functions that use a va_list, such as StringPrintf, to +// invalidate the data in it upon use. The fix is to make a copy of the +// structure before using it and use that copy instead. va_copy is provided +// for this purpose. MSVC does not provide va_copy, so define an +// implementation here. It is not guaranteed that assignment is a copy, so the +// StringUtil.VariableArgsFunc unit test tests this capability. + +// The C standard says that va_copy is a "macro", not a function. Trying to +// use va_list as ref args to a function, as above, breaks some machines. +# if defined(COMPILER_GCC) +# define base_va_copy(_a, _b) ::va_copy(_a, _b) +# elif defined(COMPILER_MSVC) +# define base_va_copy(_a, _b) (_a = _b) +# else +# error No va_copy for your compiler +# endif + +} // namespace base + +// Define an OS-neutral wrapper for shared library entry points +#if defined(OS_WIN) +#define API_CALL __stdcall +#elif defined(OS_LINUX) || defined(OS_MACOSX) +#define API_CALL +#endif + +#endif // BASE_PORT_H_ diff --git a/ipc/chromium/src/base/process.h b/ipc/chromium/src/base/process.h new file mode 100644 index 0000000000..b562771e62 --- /dev/null +++ b/ipc/chromium/src/base/process.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_PROCESS_H_ +#define BASE_PROCESS_H_ + +#include "base/basictypes.h" + +#include <sys/types.h> +#ifdef OS_WIN +#include <windows.h> +#endif + +namespace base { + +// ProcessHandle is a platform specific type which represents the underlying OS +// handle to a process. +// ProcessId is a number which identifies the process in the OS. +#if defined(OS_WIN) +typedef HANDLE ProcessHandle; +typedef DWORD ProcessId; +#elif defined(OS_POSIX) +// On POSIX, our ProcessHandle will just be the PID. +typedef pid_t ProcessHandle; +typedef pid_t ProcessId; +#endif + +class Process { + public: + Process() : process_(0), last_working_set_size_(0) {} + explicit Process(ProcessHandle aHandle) : + process_(aHandle), last_working_set_size_(0) {} + + // A handle to the current process. + static Process Current(); + + // Get/Set the handle for this process. The handle will be 0 if the process + // is no longer running. + ProcessHandle handle() const { return process_; } + void set_handle(ProcessHandle aHandle) { process_ = aHandle; } + + // Get the PID for this process. + ProcessId pid() const; + + // Is the this process the current process. + bool is_current() const; + + // Close the process handle. This will not terminate the process. + void Close(); + + // Terminates the process with extreme prejudice. The given result code will + // be the exit code of the process. If the process has already exited, this + // will do nothing. + void Terminate(int result_code); + + // A process is backgrounded when it's priority is lower than normal. + // Return true if this process is backgrounded, false otherwise. + bool IsProcessBackgrounded() const; + + // Set a prcess as backgrounded. If value is true, the priority + // of the process will be lowered. If value is false, the priority + // of the process will be made "normal" - equivalent to default + // process priority. + // Returns true if the priority was changed, false otherwise. + bool SetProcessBackgrounded(bool value); + + // Releases as much of the working set back to the OS as possible. + // Returns true if successful, false otherwise. + bool EmptyWorkingSet(); + + private: + ProcessHandle process_; + size_t last_working_set_size_; +}; + +} // namespace base + +#endif // BASE_PROCESS_H_ diff --git a/ipc/chromium/src/base/process_posix.cc b/ipc/chromium/src/base/process_posix.cc new file mode 100644 index 0000000000..d8ea644c6b --- /dev/null +++ b/ipc/chromium/src/base/process_posix.cc @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/process.h" +#include "base/process_util.h" + +namespace base { + +void Process::Close() { + process_ = 0; + // if the process wasn't termiated (so we waited) or the state + // wasn't already collected w/ a wait from process_utils, we're gonna + // end up w/ a zombie when it does finally exit. +} + +void Process::Terminate(int result_code) { + // result_code isn't supportable. + if (!process_) + return; + // We don't wait here. It's the responsibility of other code to reap the + // child. + KillProcess(process_, result_code, false); +} + +bool Process::IsProcessBackgrounded() const { + // http://code.google.com/p/chromium/issues/detail?id=8083 + return false; +} + +bool Process::SetProcessBackgrounded(bool value) { + // http://code.google.com/p/chromium/issues/detail?id=8083 + // Just say we did it to keep renderer happy at the moment. Need to finish + // cleaning this up w/in higher layers since windows is probably the only + // one that can raise priorities w/o privileges. + return true; +} + +bool Process::EmptyWorkingSet() { + // http://code.google.com/p/chromium/issues/detail?id=8083 + return false; +} + +ProcessId Process::pid() const { + if (process_ == 0) + return 0; + + return GetProcId(process_); +} + +bool Process::is_current() const { + return process_ == GetCurrentProcessHandle(); +} + +// static +Process Process::Current() { + return Process(GetCurrentProcessHandle()); +} + +} // namspace base diff --git a/ipc/chromium/src/base/process_util.h b/ipc/chromium/src/base/process_util.h new file mode 100644 index 0000000000..4df1f29fba --- /dev/null +++ b/ipc/chromium/src/base/process_util.h @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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/namespace contains utility functions for enumerating, ending and +// computing statistics of processes. + +#ifndef BASE_PROCESS_UTIL_H_ +#define BASE_PROCESS_UTIL_H_ + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include <windows.h> +#include <tlhelp32.h> +#include <io.h> +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#elif defined(OS_LINUX) || defined(__GLIBC__) +#include <dirent.h> +#include <limits.h> +#include <sys/types.h> +#elif defined(OS_MACOSX) +#include <mach/mach.h> +#endif + +#include <map> +#include <string> +#include <vector> +#include <stdio.h> +#include <stdlib.h> +#ifndef OS_WIN +#include <unistd.h> +#endif + +#include "base/command_line.h" +#include "base/process.h" + +#if defined(OS_WIN) +typedef PROCESSENTRY32 ProcessEntry; +typedef IO_COUNTERS IoCounters; +#elif defined(OS_POSIX) +// TODO(port): we should not rely on a Win32 structure. +struct ProcessEntry { + int pid; + int ppid; + char szExeFile[NAME_MAX + 1]; +}; + +struct IoCounters { + unsigned long long ReadOperationCount; + unsigned long long WriteOperationCount; + unsigned long long OtherOperationCount; + unsigned long long ReadTransferCount; + unsigned long long WriteTransferCount; + unsigned long long OtherTransferCount; +}; + +#include "base/file_descriptor_shuffle.h" +#endif + +#if defined(OS_MACOSX) +struct kinfo_proc; +#endif + +namespace base { + +// These can be used in a 32-bit bitmask. +enum ProcessArchitecture { + PROCESS_ARCH_I386 = 0x1, + PROCESS_ARCH_X86_64 = 0x2, + PROCESS_ARCH_PPC = 0x4, + PROCESS_ARCH_ARM = 0x8, + PROCESS_ARCH_MIPS = 0x10, + PROCESS_ARCH_ARM64 = 0x20 +}; + +inline ProcessArchitecture GetCurrentProcessArchitecture() +{ + base::ProcessArchitecture currentArchitecture; +#if defined(ARCH_CPU_X86) + currentArchitecture = base::PROCESS_ARCH_I386; +#elif defined(ARCH_CPU_X86_64) + currentArchitecture = base::PROCESS_ARCH_X86_64; +#elif defined(ARCH_CPU_PPC) + currentArchitecture = base::PROCESS_ARCH_PPC; +#elif defined(ARCH_CPU_ARMEL) + currentArchitecture = base::PROCESS_ARCH_ARM; +#elif defined(ARCH_CPU_MIPS) + currentArchitecture = base::PROCESS_ARCH_MIPS; +#elif defined(ARCH_CPU_ARM64) + currentArchitecture = base::PROCESS_ARCH_ARM64; +#endif + return currentArchitecture; +} + +// A minimalistic but hopefully cross-platform set of exit codes. +// Do not change the enumeration values or you will break third-party +// installers. +enum { + PROCESS_END_NORMAL_TERMINATON = 0, + PROCESS_END_KILLED_BY_USER = 1, + PROCESS_END_PROCESS_WAS_HUNG = 2 +}; + +// Returns the id of the current process. +ProcessId GetCurrentProcId(); + +// Returns the ProcessHandle of the current process. +ProcessHandle GetCurrentProcessHandle(); + +// Converts a PID to a process handle. This handle must be closed by +// CloseProcessHandle when you are done with it. Returns true on success. +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle); + +// Converts a PID to a process handle. On Windows the handle is opened +// with more access rights and must only be used by trusted code. +// You have to close returned handle using CloseProcessHandle. Returns true +// on success. +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle); + +// Closes the process handle opened by OpenProcessHandle. +void CloseProcessHandle(ProcessHandle process); + +// Returns the unique ID for the specified process. This is functionally the +// same as Windows' GetProcessId(), but works on versions of Windows before +// Win XP SP1 as well. +ProcessId GetProcId(ProcessHandle process); + +#if defined(OS_POSIX) +// Sets all file descriptors to close on exec except for stdin, stdout +// and stderr. +// TODO(agl): remove this function +// WARNING: do not use. It's inherently race-prone in the face of +// multi-threading. +void SetAllFDsToCloseOnExec(); +// Close all file descriptors, expect those which are a destination in the +// given multimap. Only call this function in a child process where you know +// that there aren't any other threads. +void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map); +#endif + +enum ChildPrivileges { + PRIVILEGES_DEFAULT, + PRIVILEGES_UNPRIVILEGED, + PRIVILEGES_INHERIT, + PRIVILEGES_LAST +}; + +#if defined(OS_WIN) +// Runs the given application name with the given command line. Normally, the +// first command line argument should be the path to the process, and don't +// forget to quote it. +// +// If wait is true, it will block and wait for the other process to finish, +// otherwise, it will just continue asynchronously. +// +// Example (including literal quotes) +// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\" +// +// If process_handle is non-NULL, the process handle of the launched app will be +// stored there on a successful launch. +// NOTE: In this case, the caller is responsible for closing the handle so +// that it doesn't leak! +bool LaunchApp(const std::wstring& cmdline, + bool wait, bool start_hidden, ProcessHandle* process_handle); +#elif defined(OS_POSIX) +// Runs the application specified in argv[0] with the command line argv. +// Before launching all FDs open in the parent process will be marked as +// close-on-exec. |fds_to_remap| defines a mapping of src fd->dest fd to +// propagate FDs into the child process. +// +// As above, if wait is true, execute synchronously. The pid will be stored +// in process_handle if that pointer is non-null. +// +// Note that the first argument in argv must point to the filename, +// and must be fully specified. +typedef std::vector<std::pair<int, int> > file_handle_mapping_vector; +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle); + +typedef std::map<std::string, std::string> environment_map; +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + ChildPrivileges privs, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch=GetCurrentProcessArchitecture()); +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch=GetCurrentProcessArchitecture()); +#endif + +// Adjust the privileges of this process to match |privs|. Only +// returns if privileges were successfully adjusted. +void SetCurrentProcessPrivileges(ChildPrivileges privs); + +// Executes the application specified by cl. This function delegates to one +// of the above two platform-specific functions. +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, ProcessHandle* process_handle); + +// Used to filter processes by process ID. +class ProcessFilter { + public: + // Returns true to indicate set-inclusion and false otherwise. This method + // should not have side-effects and should be idempotent. + virtual bool Includes(ProcessId pid, ProcessId parent_pid) const = 0; + virtual ~ProcessFilter() { } +}; + +// Attempts to kill the process identified by the given process +// entry structure, giving it the specified exit code. If |wait| is true, wait +// for the process to be actually terminated before returning. +// Returns true if this is successful, false otherwise. +bool KillProcess(ProcessHandle process, int exit_code, bool wait); + +// Get the termination status (exit code) of the process and return true if the +// status indicates the process crashed. |child_exited| is set to true iff the +// child process has terminated. (|child_exited| may be NULL.) +// +// On Windows, it is an error to call this if the process hasn't terminated +// yet. On POSIX, |child_exited| is set correctly since we detect terminate in +// a different manner on POSIX. +bool DidProcessCrash(bool* child_exited, ProcessHandle handle); + +// Provides performance metrics for a specified process (CPU usage, memory and +// IO counters). To use it, invoke CreateProcessMetrics() to get an instance +// for a specific process, then access the information with the different get +// methods. +class ProcessMetrics { + public: + // Creates a ProcessMetrics for the specified process. + // The caller owns the returned object. + static ProcessMetrics* CreateProcessMetrics(ProcessHandle process); + + ~ProcessMetrics(); + + // Returns the CPU usage in percent since the last time this method was + // called. The first time this method is called it returns 0 and will return + // the actual CPU info on subsequent calls. + // Note that on multi-processor machines, the CPU usage value is for all + // CPUs. So if you have 2 CPUs and your process is using all the cycles + // of 1 CPU and not the other CPU, this method returns 50. + int GetCPUUsage(); + + private: + explicit ProcessMetrics(ProcessHandle process); + + ProcessHandle process_; + + int processor_count_; + + // Used to store the previous times so we can compute the CPU usage. + int64_t last_time_; + int64_t last_system_time_; + + DISALLOW_EVIL_CONSTRUCTORS(ProcessMetrics); +}; + +} // namespace base + +namespace mozilla { + +class EnvironmentLog +{ +public: + explicit EnvironmentLog(const char* varname) { + const char *e = getenv(varname); + if (e && *e) { + fname_ = e; + } + } + + ~EnvironmentLog() {} + + void print(const char* format, ...) { + if (!fname_.size()) + return; + + FILE* f; + if (fname_.compare("-") == 0) { + f = fdopen(dup(STDOUT_FILENO), "a"); + } else { + f = fopen(fname_.c_str(), "a"); + } + + if (!f) + return; + + va_list a; + va_start(a, format); + vfprintf(f, format, a); + va_end(a); + fclose(f); + } + +private: + std::string fname_; + + DISALLOW_EVIL_CONSTRUCTORS(EnvironmentLog); +}; + +} // namespace mozilla + +#if defined(OS_WIN) +// Undo the windows.h damage +#undef GetMessage +#undef CreateEvent +#undef GetClassName +#undef GetBinaryType +#undef RemoveDirectory +#endif + +#endif // BASE_PROCESS_UTIL_H_ diff --git a/ipc/chromium/src/base/process_util_bsd.cc b/ipc/chromium/src/base/process_util_bsd.cc new file mode 100644 index 0000000000..57cf7fef97 --- /dev/null +++ b/ipc/chromium/src/base/process_util_bsd.cc @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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. + +// derived from process_util_mac.cc + +#include "base/process_util.h" + +#include <fcntl.h> +#include <spawn.h> +#include <sys/wait.h> + +#include <string> + +#include "nspr.h" +#include "base/eintr_wrapper.h" + +namespace { + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +void FreeEnvVarsArray(char* array[], int length) +{ + for (int i = 0; i < length; i++) { + free(array[i]); + } + delete[] array; +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle) { + return LaunchApp(argv, fds_to_remap, environment_map(), + wait, process_handle); +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch) { + return LaunchApp(argv, fds_to_remap, env_vars_to_set, + PRIVILEGES_INHERIT, + wait, process_handle); +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + ChildPrivileges privs, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch) { + bool retval = true; + + char* argv_copy[argv.size() + 1]; + for (size_t i = 0; i < argv.size(); i++) { + argv_copy[i] = const_cast<char*>(argv[i].c_str()); + } + argv_copy[argv.size()] = NULL; + + // Make sure we don't leak any FDs to the child process by marking all FDs + // as close-on-exec. + SetAllFDsToCloseOnExec(); + + // Copy environment to a new char array and add the variables + // in env_vars_to_set. + // Existing variables are overwritten by env_vars_to_set. + int pos = 0; + environment_map combined_env_vars = env_vars_to_set; + char **environ = PR_DuplicateEnvironment(); + while(environ[pos] != NULL) { + std::string varString = environ[pos]; + std::string varName = varString.substr(0, varString.find_first_of('=')); + std::string varValue = varString.substr(varString.find_first_of('=') + 1); + if (combined_env_vars.find(varName) == combined_env_vars.end()) { + combined_env_vars[varName] = varValue; + } + PR_Free(environ[pos++]); + } + PR_Free(environ); + int varsLen = combined_env_vars.size() + 1; + + char** vars = new char*[varsLen]; + int i = 0; + for (environment_map::const_iterator it = combined_env_vars.begin(); + it != combined_env_vars.end(); ++it) { + std::string entry(it->first); + entry += "="; + entry += it->second; + vars[i] = strdup(entry.c_str()); + i++; + } + vars[i] = NULL; + + posix_spawn_file_actions_t file_actions; + if (posix_spawn_file_actions_init(&file_actions) != 0) { + FreeEnvVarsArray(vars, varsLen); + return false; + } + + // Turn fds_to_remap array into a set of dup2 calls. + for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin(); + it != fds_to_remap.end(); + ++it) { + int src_fd = it->first; + int dest_fd = it->second; + + if (src_fd == dest_fd) { + int flags = fcntl(src_fd, F_GETFD); + if (flags != -1) { + fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC); + } + } else { + if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) { + posix_spawn_file_actions_destroy(&file_actions); + FreeEnvVarsArray(vars, varsLen); + return false; + } + } + } + + pid_t pid = 0; + int spawn_succeeded = (posix_spawnp(&pid, + argv_copy[0], + &file_actions, + NULL, + argv_copy, + vars) == 0); + + FreeEnvVarsArray(vars, varsLen); + + posix_spawn_file_actions_destroy(&file_actions); + + bool process_handle_valid = pid > 0; + if (!spawn_succeeded || !process_handle_valid) { + retval = false; + } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); + if (wait) + HANDLE_EINTR(waitpid(pid, 0, 0)); + + if (process_handle) + *process_handle = pid; + } + + return retval; +} + +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, ProcessHandle* process_handle) { + // TODO(playmobil): Do we need to respect the start_hidden flag? + file_handle_mapping_vector no_files; + return LaunchApp(cl.argv(), no_files, wait, process_handle); +} + +void SetCurrentProcessPrivileges(ChildPrivileges privs) { + +} + +} // namespace base diff --git a/ipc/chromium/src/base/process_util_linux.cc b/ipc/chromium/src/base/process_util_linux.cc new file mode 100644 index 0000000000..204017db50 --- /dev/null +++ b/ipc/chromium/src/base/process_util_linux.cc @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/process_util.h" + +#include <ctype.h> +#include <fcntl.h> +#include <memory> +#include <unistd.h> +#include <string> +#include <sys/types.h> +#include <sys/wait.h> + +#include "base/eintr_wrapper.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "nsLiteralString.h" +#include "mozilla/UniquePtr.h" + +#include "prenv.h" +#include "prmem.h" + +#ifdef MOZ_WIDGET_GONK +/* + * AID_APP is the first application UID used by Android. We're using + * it as our unprivilegied UID. This ensure the UID used is not + * shared with any other processes than our own childs. + */ +# include <private/android_filesystem_config.h> +# define CHILD_UNPRIVILEGED_UID AID_APP +# define CHILD_UNPRIVILEGED_GID AID_APP +#else +/* + * On platforms that are not gonk based, we fall back to an arbitrary + * UID. This is generally the UID for user `nobody', albeit it is not + * always the case. + */ +# define CHILD_UNPRIVILEGED_UID 65534 +# define CHILD_UNPRIVILEGED_GID 65534 +#endif + +namespace { + +enum ParsingState { + KEY_NAME, + KEY_VALUE +}; + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +class EnvironmentEnvp +{ +public: + EnvironmentEnvp() + : mEnvp(PR_DuplicateEnvironment()) {} + + explicit EnvironmentEnvp(const environment_map &em) + { + mEnvp = (char **)PR_Malloc(sizeof(char *) * (em.size() + 1)); + if (!mEnvp) { + return; + } + char **e = mEnvp; + for (environment_map::const_iterator it = em.begin(); + it != em.end(); ++it, ++e) { + std::string str = it->first; + str += "="; + str += it->second; + size_t len = str.length() + 1; + *e = static_cast<char*>(PR_Malloc(len)); + memcpy(*e, str.c_str(), len); + } + *e = NULL; + } + + ~EnvironmentEnvp() + { + if (!mEnvp) { + return; + } + for (char **e = mEnvp; *e; ++e) { + PR_Free(*e); + } + PR_Free(mEnvp); + } + + char * const *AsEnvp() { return mEnvp; } + + void ToMap(environment_map &em) + { + if (!mEnvp) { + return; + } + em.clear(); + for (char **e = mEnvp; *e; ++e) { + const char *eq; + if ((eq = strchr(*e, '=')) != NULL) { + std::string varname(*e, eq - *e); + em[varname.c_str()] = &eq[1]; + } + } + } + +private: + char **mEnvp; +}; + +class Environment : public environment_map +{ +public: + Environment() + { + EnvironmentEnvp envp; + envp.ToMap(*this); + } + + char * const *AsEnvp() { + mEnvp.reset(new EnvironmentEnvp(*this)); + return mEnvp->AsEnvp(); + } + + void Merge(const environment_map &em) + { + for (const_iterator it = em.begin(); it != em.end(); ++it) { + (*this)[it->first] = it->second; + } + } +private: + std::auto_ptr<EnvironmentEnvp> mEnvp; +}; + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle) { + return LaunchApp(argv, fds_to_remap, environment_map(), + wait, process_handle); +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch) { + return LaunchApp(argv, fds_to_remap, env_vars_to_set, + PRIVILEGES_INHERIT, + wait, process_handle); +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + ChildPrivileges privs, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch) { + mozilla::UniquePtr<char*[]> argv_cstr(new char*[argv.size() + 1]); + // Illegal to allocate memory after fork and before execvp + InjectiveMultimap fd_shuffle1, fd_shuffle2; + fd_shuffle1.reserve(fds_to_remap.size()); + fd_shuffle2.reserve(fds_to_remap.size()); + + Environment env; + env.Merge(env_vars_to_set); + char * const *envp = env.AsEnvp(); + if (!envp) { + DLOG(ERROR) << "FAILED to duplicate environment for: " << argv_cstr[0]; + return false; + } + + pid_t pid = fork(); + if (pid < 0) + return false; + + if (pid == 0) { + for (file_handle_mapping_vector::const_iterator + it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) { + fd_shuffle1.push_back(InjectionArc(it->first, it->second, false)); + fd_shuffle2.push_back(InjectionArc(it->first, it->second, false)); + } + + if (!ShuffleFileDescriptors(&fd_shuffle1)) + _exit(127); + + CloseSuperfluousFds(fd_shuffle2); + + for (size_t i = 0; i < argv.size(); i++) + argv_cstr[i] = const_cast<char*>(argv[i].c_str()); + argv_cstr[argv.size()] = NULL; + + SetCurrentProcessPrivileges(privs); + + execve(argv_cstr[0], argv_cstr.get(), envp); + // if we get here, we're in serious trouble and should complain loudly + // NOTE: This is async signal unsafe; it could deadlock instead. (But + // only on debug builds; otherwise it's a signal-safe no-op.) + DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0]; + _exit(127); + } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); + if (wait) + HANDLE_EINTR(waitpid(pid, 0, 0)); + + if (process_handle) + *process_handle = pid; + } + + return true; +} + +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, + ProcessHandle* process_handle) { + file_handle_mapping_vector no_files; + return LaunchApp(cl.argv(), no_files, wait, process_handle); +} + +void SetCurrentProcessPrivileges(ChildPrivileges privs) { + if (privs == PRIVILEGES_INHERIT) { + return; + } + + gid_t gid = CHILD_UNPRIVILEGED_GID; + uid_t uid = CHILD_UNPRIVILEGED_UID; +#ifdef MOZ_WIDGET_GONK + { + static bool checked_pix_max, pix_max_ok; + if (!checked_pix_max) { + checked_pix_max = true; + int fd = open("/proc/sys/kernel/pid_max", O_CLOEXEC | O_RDONLY); + if (fd < 0) { + DLOG(ERROR) << "Failed to open pid_max"; + _exit(127); + } + char buf[PATH_MAX]; + ssize_t len = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (len < 0) { + DLOG(ERROR) << "Failed to read pid_max"; + _exit(127); + } + buf[len] = '\0'; + int pid_max = atoi(buf); + pix_max_ok = + (pid_max + CHILD_UNPRIVILEGED_UID > CHILD_UNPRIVILEGED_UID); + } + if (!pix_max_ok) { + DLOG(ERROR) << "Can't safely get unique uid/gid"; + _exit(127); + } + gid += getpid(); + uid += getpid(); + } +#endif + if (setgid(gid) != 0) { + DLOG(ERROR) << "FAILED TO setgid() CHILD PROCESS"; + _exit(127); + } + if (setuid(uid) != 0) { + DLOG(ERROR) << "FAILED TO setuid() CHILD PROCESS"; + _exit(127); + } + if (chdir("/") != 0) + gProcessLog.print("==> could not chdir()\n"); +} + +} // namespace base diff --git a/ipc/chromium/src/base/process_util_mac.mm b/ipc/chromium/src/base/process_util_mac.mm new file mode 100644 index 0000000000..7d06411b25 --- /dev/null +++ b/ipc/chromium/src/base/process_util_mac.mm @@ -0,0 +1,202 @@ +// Copyright (c) 2008 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 "base/process_util.h" + +#import <Cocoa/Cocoa.h> +#include <crt_externs.h> +#include <spawn.h> +#include <sys/wait.h> + +#include <string> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/string_util.h" +#include "base/time.h" + +namespace { + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +void FreeEnvVarsArray(char* array[], int length) +{ + for (int i = 0; i < length; i++) { + free(array[i]); + } + delete[] array; +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle) { + return LaunchApp(argv, fds_to_remap, environment_map(), + wait, process_handle); +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch) { + return LaunchApp(argv, fds_to_remap, env_vars_to_set, + PRIVILEGES_INHERIT, + wait, process_handle); +} + +bool LaunchApp(const std::vector<std::string>& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + ChildPrivileges privs, + bool wait, ProcessHandle* process_handle, + ProcessArchitecture arch) { + bool retval = true; + + char* argv_copy[argv.size() + 1]; + for (size_t i = 0; i < argv.size(); i++) { + argv_copy[i] = const_cast<char*>(argv[i].c_str()); + } + argv_copy[argv.size()] = NULL; + + // Make sure we don't leak any FDs to the child process by marking all FDs + // as close-on-exec. + SetAllFDsToCloseOnExec(); + + // Copy _NSGetEnviron() to a new char array and add the variables + // in env_vars_to_set. + // Existing variables are overwritten by env_vars_to_set. + int pos = 0; + environment_map combined_env_vars = env_vars_to_set; + while((*_NSGetEnviron())[pos] != NULL) { + std::string varString = (*_NSGetEnviron())[pos]; + std::string varName = varString.substr(0, varString.find_first_of('=')); + std::string varValue = varString.substr(varString.find_first_of('=') + 1); + if (combined_env_vars.find(varName) == combined_env_vars.end()) { + combined_env_vars[varName] = varValue; + } + pos++; + } + int varsLen = combined_env_vars.size() + 1; + + char** vars = new char*[varsLen]; + int i = 0; + for (environment_map::const_iterator it = combined_env_vars.begin(); + it != combined_env_vars.end(); ++it) { + std::string entry(it->first); + entry += "="; + entry += it->second; + vars[i] = strdup(entry.c_str()); + i++; + } + vars[i] = NULL; + + posix_spawn_file_actions_t file_actions; + if (posix_spawn_file_actions_init(&file_actions) != 0) { + FreeEnvVarsArray(vars, varsLen); + return false; + } + + // Turn fds_to_remap array into a set of dup2 calls. + for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin(); + it != fds_to_remap.end(); + ++it) { + int src_fd = it->first; + int dest_fd = it->second; + + if (src_fd == dest_fd) { + int flags = fcntl(src_fd, F_GETFD); + if (flags != -1) { + fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC); + } + } else { + if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) { + posix_spawn_file_actions_destroy(&file_actions); + FreeEnvVarsArray(vars, varsLen); + return false; + } + } + } + + // Set up the CPU preference array. + cpu_type_t cpu_types[1]; + switch (arch) { + case PROCESS_ARCH_I386: + cpu_types[0] = CPU_TYPE_X86; + break; + case PROCESS_ARCH_X86_64: + cpu_types[0] = CPU_TYPE_X86_64; + break; + case PROCESS_ARCH_PPC: + cpu_types[0] = CPU_TYPE_POWERPC; + break; + default: + cpu_types[0] = CPU_TYPE_ANY; + break; + } + + // Initialize spawn attributes. + posix_spawnattr_t spawnattr; + if (posix_spawnattr_init(&spawnattr) != 0) { + FreeEnvVarsArray(vars, varsLen); + return false; + } + + // Set spawn attributes. + size_t attr_count = 1; + size_t attr_ocount = 0; + if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, cpu_types, &attr_ocount) != 0 || + attr_ocount != attr_count) { + FreeEnvVarsArray(vars, varsLen); + posix_spawnattr_destroy(&spawnattr); + return false; + } + + int pid = 0; + int spawn_succeeded = (posix_spawnp(&pid, + argv_copy[0], + &file_actions, + &spawnattr, + argv_copy, + vars) == 0); + + FreeEnvVarsArray(vars, varsLen); + + posix_spawn_file_actions_destroy(&file_actions); + + posix_spawnattr_destroy(&spawnattr); + + bool process_handle_valid = pid > 0; + if (!spawn_succeeded || !process_handle_valid) { + retval = false; + } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); + if (wait) + HANDLE_EINTR(waitpid(pid, 0, 0)); + + if (process_handle) + *process_handle = pid; + } + + return retval; +} + +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, ProcessHandle* process_handle) { + // TODO(playmobil): Do we need to respect the start_hidden flag? + file_handle_mapping_vector no_files; + return LaunchApp(cl.argv(), no_files, wait, process_handle); +} + +void SetCurrentProcessPrivileges(ChildPrivileges privs) { + +} + +} // namespace base diff --git a/ipc/chromium/src/base/process_util_posix.cc b/ipc/chromium/src/base/process_util_posix.cc new file mode 100644 index 0000000000..ade3ebe5eb --- /dev/null +++ b/ipc/chromium/src/base/process_util_posix.cc @@ -0,0 +1,348 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <limits> +#include <set> + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/process_util.h" +#include "base/sys_info.h" +#include "base/time.h" +#include "base/waitable_event.h" +#include "base/dir_reader_posix.h" + +#include "mozilla/UniquePtr.h" + +const int kMicrosecondsPerSecond = 1000000; + +namespace base { + +ProcessId GetCurrentProcId() { + return getpid(); +} + +ProcessHandle GetCurrentProcessHandle() { + return GetCurrentProcId(); +} + +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { + // On Posix platforms, process handles are the same as PIDs, so we + // don't need to do anything. + *handle = pid; + return true; +} + +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { + // On POSIX permissions are checked for each operation on process, + // not when opening a "handle". + return OpenProcessHandle(pid, handle); +} + +void CloseProcessHandle(ProcessHandle process) { + // See OpenProcessHandle, nothing to do. + return; +} + +ProcessId GetProcId(ProcessHandle process) { + return process; +} + +// Attempts to kill the process identified by the given process +// entry structure. Ignores specified exit_code; posix can't force that. +// Returns true if this is successful, false otherwise. +bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) { + bool result = kill(process_id, SIGTERM) == 0; + + if (result && wait) { + int tries = 60; + bool exited = false; + // The process may not end immediately due to pending I/O + while (tries-- > 0) { + int pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG)); + if (pid == process_id) { + exited = true; + break; + } + + sleep(1); + } + + if (!exited) { + result = kill(process_id, SIGKILL) == 0; + } + } + + if (!result) + DLOG(ERROR) << "Unable to terminate process."; + + return result; +} + +#ifdef ANDROID +typedef unsigned long int rlim_t; +#endif + +// A class to handle auto-closing of DIR*'s. +class ScopedDIRClose { + public: + inline void operator()(DIR* x) const { + if (x) { + closedir(x); + } + } +}; +typedef mozilla::UniquePtr<DIR, ScopedDIRClose> ScopedDIR; + + +void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { + // DANGER: no calls to malloc are allowed from now on: + // http://crbug.com/36678 +#if defined(ANDROID) + static const rlim_t kSystemDefaultMaxFds = 1024; + static const char kFDDir[] = "/proc/self/fd"; +#elif defined(OS_LINUX) + static const rlim_t kSystemDefaultMaxFds = 8192; + static const char kFDDir[] = "/proc/self/fd"; +#elif defined(OS_MACOSX) + static const rlim_t kSystemDefaultMaxFds = 256; + static const char kFDDir[] = "/dev/fd"; +#elif defined(OS_BSD) + // the getrlimit below should never fail, so whatever .. + static const rlim_t kSystemDefaultMaxFds = 1024; + // at least /dev/fd will exist + static const char kFDDir[] = "/dev/fd"; +#endif + + // Get the maximum number of FDs possible. + struct rlimit nofile; + rlim_t max_fds; + if (getrlimit(RLIMIT_NOFILE, &nofile)) { + // getrlimit failed. Take a best guess. + max_fds = kSystemDefaultMaxFds; + DLOG(ERROR) << "getrlimit(RLIMIT_NOFILE) failed: " << errno; + } else { + max_fds = nofile.rlim_cur; + } + + if (max_fds > INT_MAX) + max_fds = INT_MAX; + + DirReaderPosix fd_dir(kFDDir); + + if (!fd_dir.IsValid()) { + // Fallback case: Try every possible fd. + for (rlim_t i = 0; i < max_fds; ++i) { + const int fd = static_cast<int>(i); + if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) + continue; + InjectiveMultimap::const_iterator j; + for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) { + if (fd == j->dest) + break; + } + if (j != saved_mapping.end()) + continue; + + // Since we're just trying to close anything we can find, + // ignore any error return values of close(). + HANDLE_EINTR(close(fd)); + } + return; + } + + const int dir_fd = fd_dir.fd(); + + for ( ; fd_dir.Next(); ) { + // Skip . and .. entries. + if (fd_dir.name()[0] == '.') + continue; + + char *endptr; + errno = 0; + const long int fd = strtol(fd_dir.name(), &endptr, 10); + if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno) + continue; + if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) + continue; + InjectiveMultimap::const_iterator i; + for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) { + if (fd == i->dest) + break; + } + if (i != saved_mapping.end()) + continue; + if (fd == dir_fd) + continue; + + // When running under Valgrind, Valgrind opens several FDs for its + // own use and will complain if we try to close them. All of + // these FDs are >= |max_fds|, so we can check against that here + // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758 + if (fd < static_cast<int>(max_fds)) { + int ret = HANDLE_EINTR(close(fd)); + if (ret != 0) { + DLOG(ERROR) << "Problem closing fd"; + } + } + } +} + +// Sets all file descriptors to close on exec except for stdin, stdout +// and stderr. +// TODO(agl): Remove this function. It's fundamentally broken for multithreaded +// apps. +void SetAllFDsToCloseOnExec() { +#if defined(OS_LINUX) + const char fd_dir[] = "/proc/self/fd"; +#elif defined(OS_MACOSX) || defined(OS_BSD) + const char fd_dir[] = "/dev/fd"; +#endif + ScopedDIR dir_closer(opendir(fd_dir)); + DIR *dir = dir_closer.get(); + if (NULL == dir) { + DLOG(ERROR) << "Unable to open " << fd_dir; + return; + } + + struct dirent *ent; + while ((ent = readdir(dir))) { + // Skip . and .. entries. + if (ent->d_name[0] == '.') + continue; + int i = atoi(ent->d_name); + // We don't close stdin, stdout or stderr. + if (i <= STDERR_FILENO) + continue; + + int flags = fcntl(i, F_GETFD); + if ((flags == -1) || (fcntl(i, F_SETFD, flags | FD_CLOEXEC) == -1)) { + DLOG(ERROR) << "fcntl failure."; + } + } +} + +ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), + last_time_(0), + last_system_time_(0) { + processor_count_ = base::SysInfo::NumberOfProcessors(); +} + +// static +ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { + return new ProcessMetrics(process); +} + +ProcessMetrics::~ProcessMetrics() { } + +bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { + int status; + const int result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); + if (result == -1) { + // This shouldn't happen, but sometimes it does. The error is + // probably ECHILD and the reason is probably that a pid was + // waited on again after a previous wait reclaimed its zombie. + // (It could also occur if the process isn't a direct child, but + // don't do that.) This is bad, because it risks interfering with + // an unrelated child process if the pid is reused. + // + // So, lacking reliable information, we indicate that the process + // is dead, in the hope that the caller will give up and stop + // calling us. See also bug 943174 and bug 933680. + CHROMIUM_LOG(ERROR) << "waitpid failed pid:" << handle << " errno:" << errno; + if (child_exited) + *child_exited = true; + return false; + } else if (result == 0) { + // the child hasn't exited yet. + if (child_exited) + *child_exited = false; + return false; + } + + if (child_exited) + *child_exited = true; + + if (WIFSIGNALED(status)) { + switch(WTERMSIG(status)) { + case SIGSYS: + case SIGSEGV: + case SIGILL: + case SIGABRT: + case SIGFPE: + return true; + default: + return false; + } + } + + if (WIFEXITED(status)) + return WEXITSTATUS(status) != 0; + + return false; +} + +namespace { + +int64_t TimeValToMicroseconds(const struct timeval& tv) { + return tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec; +} + +} + +int ProcessMetrics::GetCPUUsage() { + struct timeval now; + struct rusage usage; + + int retval = gettimeofday(&now, NULL); + if (retval) + return 0; + retval = getrusage(RUSAGE_SELF, &usage); + if (retval) + return 0; + + int64_t system_time = (TimeValToMicroseconds(usage.ru_stime) + + TimeValToMicroseconds(usage.ru_utime)) / + processor_count_; + int64_t time = TimeValToMicroseconds(now); + + if ((last_system_time_ == 0) || (last_time_ == 0)) { + // First call, just set the last values. + last_system_time_ = system_time; + last_time_ = time; + return 0; + } + + int64_t system_time_delta = system_time - last_system_time_; + int64_t time_delta = time - last_time_; + DCHECK(time_delta != 0); + if (time_delta == 0) + return 0; + + // We add time_delta / 2 so the result is rounded. + int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) / + time_delta); + + last_system_time_ = system_time; + last_time_ = time; + + return cpu; +} + +} // namespace base diff --git a/ipc/chromium/src/base/process_util_win.cc b/ipc/chromium/src/base/process_util_win.cc new file mode 100644 index 0000000000..12fbc23a5c --- /dev/null +++ b/ipc/chromium/src/base/process_util_win.cc @@ -0,0 +1,489 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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. + +// We need extended process and thread attribute support +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 + +#include "base/process_util.h" + +#include <windows.h> +#include <winternl.h> +#include <psapi.h> + +#include "base/histogram.h" +#include "base/logging.h" +#include "base/win_util.h" + +#include <algorithm> +#include "prenv.h" + +#include "mozilla/WindowsVersion.h" + +namespace { + +// System pagesize. This value remains constant on x86/64 architectures. +const int PAGESIZE_KB = 4; + +// HeapSetInformation function pointer. +typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); + +typedef BOOL (WINAPI * InitializeProcThreadAttributeListFn)( + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, + DWORD dwAttributeCount, + DWORD dwFlags, + PSIZE_T lpSize +); + +typedef BOOL (WINAPI * DeleteProcThreadAttributeListFn)( + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList +); + +typedef BOOL (WINAPI * UpdateProcThreadAttributeFn)( + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, + DWORD dwFlags, + DWORD_PTR Attribute, + PVOID lpValue, + SIZE_T cbSize, + PVOID lpPreviousValue, + PSIZE_T lpReturnSize +); + +static InitializeProcThreadAttributeListFn InitializeProcThreadAttributeListPtr; +static DeleteProcThreadAttributeListFn DeleteProcThreadAttributeListPtr; +static UpdateProcThreadAttributeFn UpdateProcThreadAttributePtr; + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +ProcessId GetCurrentProcId() { + return ::GetCurrentProcessId(); +} + +ProcessHandle GetCurrentProcessHandle() { + return ::GetCurrentProcess(); +} + +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { + // TODO(phajdan.jr): Take even more permissions out of this list. + ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | + PROCESS_TERMINATE | + PROCESS_QUERY_INFORMATION | + SYNCHRONIZE, + FALSE, pid); + + if (result == NULL) { + return false; + } + + *handle = result; + return true; +} + +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { + ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | + PROCESS_TERMINATE | + PROCESS_QUERY_INFORMATION | + PROCESS_VM_READ | + SYNCHRONIZE, + FALSE, pid); + + if (result == NULL) { + return false; + } + + *handle = result; + return true; +} + +void CloseProcessHandle(ProcessHandle process) { + // closing a handle twice on Windows can be catastrophic - after the first + // close the handle value may be reused, so the second close will kill that + // other new handle. + BOOL ok = CloseHandle(process); + DCHECK(ok); +} + +// Helper for GetProcId() +bool GetProcIdViaGetProcessId(ProcessHandle process, DWORD* id) { + // Dynamically get a pointer to GetProcessId(). + typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE); + static GetProcessIdFunction GetProcessIdPtr = NULL; + static bool initialize_get_process_id = true; + if (initialize_get_process_id) { + initialize_get_process_id = false; + HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll"); + if (!kernel32_handle) { + NOTREACHED(); + return false; + } + GetProcessIdPtr = reinterpret_cast<GetProcessIdFunction>(GetProcAddress( + kernel32_handle, "GetProcessId")); + } + if (!GetProcessIdPtr) + return false; + // Ask for the process ID. + *id = (*GetProcessIdPtr)(process); + return true; +} + +// Helper for GetProcId() +bool GetProcIdViaNtQueryInformationProcess(ProcessHandle process, DWORD* id) { + // Dynamically get a pointer to NtQueryInformationProcess(). + typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)( + HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); + static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL; + static bool initialize_query_information_process = true; + if (initialize_query_information_process) { + initialize_query_information_process = false; + // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though + // the Windows docs seem to imply that you should LoadLibrary() it. + HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll"); + if (!ntdll_handle) { + NOTREACHED(); + return false; + } + NtQueryInformationProcessPtr = + reinterpret_cast<NtQueryInformationProcessFunction>(GetProcAddress( + ntdll_handle, "NtQueryInformationProcess")); + } + if (!NtQueryInformationProcessPtr) + return false; + // Ask for the process ID. + PROCESS_BASIC_INFORMATION info; + ULONG bytes_returned; + NTSTATUS status = (*NtQueryInformationProcessPtr)(process, + ProcessBasicInformation, + &info, sizeof info, + &bytes_returned); + if (!SUCCEEDED(status) || (bytes_returned != (sizeof info))) + return false; + + *id = static_cast<DWORD>(info.UniqueProcessId); + return true; +} + +ProcessId GetProcId(ProcessHandle process) { + // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights. + HANDLE current_process = GetCurrentProcess(); + HANDLE process_with_query_rights; + if (DuplicateHandle(current_process, process, current_process, + &process_with_query_rights, PROCESS_QUERY_INFORMATION, + false, 0)) { + // Try to use GetProcessId(), if it exists. Fall back on + // NtQueryInformationProcess() otherwise (< Win XP SP1). + DWORD id; + bool success = + GetProcIdViaGetProcessId(process_with_query_rights, &id) || + GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id); + CloseHandle(process_with_query_rights); + if (success) + return id; + } + + // We're screwed. + NOTREACHED(); + return 0; +} + +// from sandbox_policy_base.cc in a later version of the chromium ipc code... +bool IsInheritableHandle(HANDLE handle) { + if (!handle) + return false; + if (handle == INVALID_HANDLE_VALUE) + return false; + // File handles (FILE_TYPE_DISK) and pipe handles are known to be + // inheritable. Console handles (FILE_TYPE_CHAR) are not + // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST. + DWORD handle_type = GetFileType(handle); + return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE; +} + +void LoadThreadAttributeFunctions() { + HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); + InitializeProcThreadAttributeListPtr = + reinterpret_cast<InitializeProcThreadAttributeListFn> + (GetProcAddress(kernel32, "InitializeProcThreadAttributeList")); + DeleteProcThreadAttributeListPtr = + reinterpret_cast<DeleteProcThreadAttributeListFn> + (GetProcAddress(kernel32, "DeleteProcThreadAttributeList")); + UpdateProcThreadAttributePtr = + reinterpret_cast<UpdateProcThreadAttributeFn> + (GetProcAddress(kernel32, "UpdateProcThreadAttribute")); +} + +// Creates and returns a "thread attribute list" to pass to the child process. +// On return, is a pointer to a THREAD_ATTRIBUTE_LIST or NULL if either the +// functions we need aren't available (eg, XP or earlier) or the functions we +// need failed. +// The result of this function must be passed to FreeThreadAttributeList. +// Note that the pointer to the HANDLE array ends up embedded in the result of +// this function and must stay alive until FreeThreadAttributeList is called, +// hence it is passed in so the owner is the caller of this function. +LPPROC_THREAD_ATTRIBUTE_LIST CreateThreadAttributeList(HANDLE *handlesToInherit, + int handleCount) { + if (!InitializeProcThreadAttributeListPtr || + !DeleteProcThreadAttributeListPtr || + !UpdateProcThreadAttributePtr) + LoadThreadAttributeFunctions(); + // shouldn't happen as we are only called for Vista+, but better safe than sorry... + if (!InitializeProcThreadAttributeListPtr || + !DeleteProcThreadAttributeListPtr || + !UpdateProcThreadAttributePtr) + return NULL; + + SIZE_T threadAttrSize; + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; + + if (!(*InitializeProcThreadAttributeListPtr)(NULL, 1, 0, &threadAttrSize) && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto fail; + lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST> + (malloc(threadAttrSize)); + if (!lpAttributeList || + !(*InitializeProcThreadAttributeListPtr)(lpAttributeList, 1, 0, &threadAttrSize)) + goto fail; + + if (!(*UpdateProcThreadAttributePtr)(lpAttributeList, + 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + handlesToInherit, + sizeof(handlesToInherit[0]) * handleCount, + NULL, NULL)) { + (*DeleteProcThreadAttributeListPtr)(lpAttributeList); + goto fail; + } + return lpAttributeList; + +fail: + if (lpAttributeList) + free(lpAttributeList); + return NULL; +} + +// Frees the data returned by CreateThreadAttributeList. +void FreeThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList) { + // must be impossible to get a NULL DeleteProcThreadAttributeListPtr, as + // we already checked it existed when creating the data we are now freeing. + (*DeleteProcThreadAttributeListPtr)(lpAttributeList); + free(lpAttributeList); +} + +bool LaunchApp(const std::wstring& cmdline, + bool wait, bool start_hidden, ProcessHandle* process_handle) { + + // We want to inherit the std handles so dump() statements and assertion + // messages in the child process can be seen - but we *do not* want to + // blindly have all handles inherited. Vista and later has a technique + // where only specified handles are inherited - so we use this technique if + // we can. If that technique isn't available (or it fails), we just don't + // inherit anything. This can cause us a problem for Windows XP testing, + // because we sometimes need the handles to get inherited for test logging to + // work. So we also inherit when a specific environment variable is set. + DWORD dwCreationFlags = 0; + BOOL bInheritHandles = FALSE; + // We use a STARTUPINFOEX, but if we can't do the thread attribute thing, we + // just pass the size of a STARTUPINFO. + STARTUPINFOEX startup_info_ex; + ZeroMemory(&startup_info_ex, sizeof(startup_info_ex)); + STARTUPINFO &startup_info = startup_info_ex.StartupInfo; + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESHOWWINDOW; + startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW; + + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL; + // Don't even bother trying pre-Vista... + if (mozilla::IsVistaOrLater()) { + // setup our handle array first - if we end up with no handles that can + // be inherited we can avoid trying to do the ThreadAttributeList dance... + HANDLE handlesToInherit[2]; + int handleCount = 0; + HANDLE stdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE stdErr = ::GetStdHandle(STD_ERROR_HANDLE); + + if (IsInheritableHandle(stdOut)) + handlesToInherit[handleCount++] = stdOut; + if (stdErr != stdOut && IsInheritableHandle(stdErr)) + handlesToInherit[handleCount++] = stdErr; + + if (handleCount) { + lpAttributeList = CreateThreadAttributeList(handlesToInherit, handleCount); + if (lpAttributeList) { + // it's safe to inherit handles, so arrange for that... + startup_info.cb = sizeof(startup_info_ex); + startup_info.dwFlags |= STARTF_USESTDHANDLES; + startup_info.hStdOutput = stdOut; + startup_info.hStdError = stdErr; + startup_info.hStdInput = INVALID_HANDLE_VALUE; + startup_info_ex.lpAttributeList = lpAttributeList; + dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT; + bInheritHandles = TRUE; + } + } + } else if (PR_GetEnv("MOZ_WIN_INHERIT_STD_HANDLES_PRE_VISTA")) { + // Even if we can't limit what gets inherited, we sometimes want to inherit + // stdout/err for testing purposes. + startup_info.dwFlags |= STARTF_USESTDHANDLES; + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + startup_info.hStdInput = INVALID_HANDLE_VALUE; + bInheritHandles = TRUE; + } + + PROCESS_INFORMATION process_info; + BOOL createdOK = CreateProcess(NULL, + const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, + bInheritHandles, dwCreationFlags, NULL, NULL, + &startup_info, &process_info); + if (lpAttributeList) + FreeThreadAttributeList(lpAttributeList); + if (!createdOK) + return false; + + gProcessLog.print("==> process %d launched child process %d (%S)\n", + GetCurrentProcId(), + process_info.dwProcessId, + cmdline.c_str()); + + // Handles must be closed or they will leak + CloseHandle(process_info.hThread); + + if (wait) + WaitForSingleObject(process_info.hProcess, INFINITE); + + // If the caller wants the process handle, we won't close it. + if (process_handle) { + *process_handle = process_info.hProcess; + } else { + CloseHandle(process_info.hProcess); + } + return true; +} + +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, ProcessHandle* process_handle) { + return LaunchApp(cl.command_line_string(), wait, + start_hidden, process_handle); +} + +bool KillProcess(ProcessHandle process, int exit_code, bool wait) { + bool result = (TerminateProcess(process, exit_code) != FALSE); + if (result && wait) { + // The process may not end immediately due to pending I/O + if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) + DLOG(ERROR) << "Error waiting for process exit: " << GetLastError(); + } else if (!result) { + DLOG(ERROR) << "Unable to terminate process: " << GetLastError(); + } + return result; +} + +bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { + DWORD exitcode = 0; + + if (child_exited) + *child_exited = true; // On Windows it an error to call this function if + // the child hasn't already exited. + if (!::GetExitCodeProcess(handle, &exitcode)) { + NOTREACHED(); + return false; + } + if (exitcode == STILL_ACTIVE) { + // The process is likely not dead or it used 0x103 as exit code. + NOTREACHED(); + return false; + } + + // Warning, this is not generic code; it heavily depends on the way + // the rest of the code kills a process. + + if (exitcode == PROCESS_END_NORMAL_TERMINATON || + exitcode == PROCESS_END_KILLED_BY_USER || + exitcode == PROCESS_END_PROCESS_WAS_HUNG || + exitcode == 0xC0000354 || // STATUS_DEBUGGER_INACTIVE. + exitcode == 0xC000013A || // Control-C/end session. + exitcode == 0x40010004) { // Debugger terminated process/end session. + return false; + } + + return true; +} + +void SetCurrentProcessPrivileges(ChildPrivileges privs) { + +} + +/////////////////////////////////////////////////////////////////////////////// +// ProcesMetrics + +ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), + last_time_(0), + last_system_time_(0) { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + processor_count_ = system_info.dwNumberOfProcessors; +} + +// static +ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { + return new ProcessMetrics(process); +} + +ProcessMetrics::~ProcessMetrics() { } + +static uint64_t FileTimeToUTC(const FILETIME& ftime) { + LARGE_INTEGER li; + li.LowPart = ftime.dwLowDateTime; + li.HighPart = ftime.dwHighDateTime; + return li.QuadPart; +} + +int ProcessMetrics::GetCPUUsage() { + FILETIME now; + FILETIME creation_time; + FILETIME exit_time; + FILETIME kernel_time; + FILETIME user_time; + + GetSystemTimeAsFileTime(&now); + + if (!GetProcessTimes(process_, &creation_time, &exit_time, + &kernel_time, &user_time)) { + // We don't assert here because in some cases (such as in the Task Manager) + // we may call this function on a process that has just exited but we have + // not yet received the notification. + return 0; + } + int64_t system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) / + processor_count_; + int64_t time = FileTimeToUTC(now); + + if ((last_system_time_ == 0) || (last_time_ == 0)) { + // First call, just set the last values. + last_system_time_ = system_time; + last_time_ = time; + return 0; + } + + int64_t system_time_delta = system_time - last_system_time_; + int64_t time_delta = time - last_time_; + DCHECK(time_delta != 0); + if (time_delta == 0) + return 0; + + // We add time_delta / 2 so the result is rounded. + int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) / + time_delta); + + last_system_time_ = system_time; + last_time_ = time; + + return cpu; +} + +} // namespace base diff --git a/ipc/chromium/src/base/process_win.cc b/ipc/chromium/src/base/process_win.cc new file mode 100644 index 0000000000..4d0f137c4e --- /dev/null +++ b/ipc/chromium/src/base/process_win.cc @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/process.h" +#include "base/logging.h" +#include "base/process_util.h" + +namespace base { + +void Process::Close() { + if (!process_) + return; + CloseProcessHandle(process_); + process_ = NULL; +} + +void Process::Terminate(int result_code) { + if (!process_) + return; + ::TerminateProcess(process_, result_code); +} + +bool Process::IsProcessBackgrounded() const { + DCHECK(process_); + DWORD priority = GetPriorityClass(process_); + if (priority == 0) + return false; // Failure case. + return priority == BELOW_NORMAL_PRIORITY_CLASS; +} + +bool Process::SetProcessBackgrounded(bool value) { + DCHECK(process_); + DWORD priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS; + return (SetPriorityClass(process_, priority) != 0); +} + +bool Process::EmptyWorkingSet() { + if (!process_) + return false; + + BOOL rv = SetProcessWorkingSetSize(process_, -1, -1); + return rv == TRUE; +} + +ProcessId Process::pid() const { + if (process_ == 0) + return 0; + + return GetProcId(process_); +} + +bool Process::is_current() const { + return process_ == GetCurrentProcess(); +} + +// static +Process Process::Current() { + return Process(GetCurrentProcess()); +} + +} // namespace base diff --git a/ipc/chromium/src/base/rand_util.cc b/ipc/chromium/src/base/rand_util.cc new file mode 100644 index 0000000000..9129786168 --- /dev/null +++ b/ipc/chromium/src/base/rand_util.cc @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/rand_util.h" + +#include <math.h> + +#include <limits> + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace base { + +int RandInt(int min, int max) { + DCHECK(min <= max); + + uint64_t range = static_cast<int64_t>(max) - min + 1; + uint64_t number = base::RandUint64(); + int result = min + static_cast<int>(number % range); + DCHECK(result >= min && result <= max); + return result; +} + +double RandDouble() { + // We try to get maximum precision by masking out as many bits as will fit + // in the target type's mantissa, and raising it to an appropriate power to + // produce output in the range [0, 1). For IEEE 754 doubles, the mantissa + // is expected to accommodate 53 bits. + + COMPILE_ASSERT(std::numeric_limits<double>::radix == 2, otherwise_use_scalbn); + static const int kBits = std::numeric_limits<double>::digits; + uint64_t random_bits = base::RandUint64() & ((GG_UINT64_C(1) << kBits) - 1); + double result = ldexp(static_cast<double>(random_bits), -1 * kBits); + DCHECK(result >= 0.0 && result < 1.0); + return result; +} + +} // namespace base diff --git a/ipc/chromium/src/base/rand_util.h b/ipc/chromium/src/base/rand_util.h new file mode 100644 index 0000000000..fd014441f8 --- /dev/null +++ b/ipc/chromium/src/base/rand_util.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 BASE_RAND_UTIL_H_ +#define BASE_RAND_UTIL_H_ + +#include "base/basictypes.h" + +namespace base { + +// Returns a random number in range [0, kuint64max]. Thread-safe. +uint64_t RandUint64(); + +// Returns a random number between min and max (inclusive). Thread-safe. +int RandInt(int min, int max); + +// Returns a random double in range [0, 1). Thread-safe. +double RandDouble(); + +// Fills |output_length| bytes of |output| with random data. +// +// WARNING: +// Do not use for security-sensitive purposes. +// See crypto/ for cryptographically secure random number generation APIs. +void RandBytes(void* output, size_t output_length); + +} // namespace base + +#endif // BASE_RAND_UTIL_H_ diff --git a/ipc/chromium/src/base/rand_util_posix.cc b/ipc/chromium/src/base/rand_util_posix.cc new file mode 100644 index 0000000000..9a6b0f0ef6 --- /dev/null +++ b/ipc/chromium/src/base/rand_util_posix.cc @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/rand_util.h" + +#include <fcntl.h> +#include <unistd.h> + +#include "base/file_util.h" +#include "base/logging.h" + +namespace base { + +uint64_t RandUint64() { + uint64_t number; + + int urandom_fd = open("/dev/urandom", O_RDONLY); + CHECK(urandom_fd >= 0); + bool success = file_util::ReadFromFD(urandom_fd, + reinterpret_cast<char*>(&number), + sizeof(number)); + CHECK(success); + close(urandom_fd); + + return number; +} + +} // namespace base diff --git a/ipc/chromium/src/base/rand_util_win.cc b/ipc/chromium/src/base/rand_util_win.cc new file mode 100644 index 0000000000..ee189f28ee --- /dev/null +++ b/ipc/chromium/src/base/rand_util_win.cc @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// 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. + +#include "base/rand_util.h" +#include <windows.h> +#include <stddef.h> +#include <stdint.h> + +// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the +// "Community Additions" comment on MSDN here: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +#define SystemFunction036 NTAPI SystemFunction036 +#include <ntsecapi.h> +#undef SystemFunction036 + +#include <algorithm> +#include <limits> + +#include "base/logging.h" + +namespace base { + +// NOTE: This function must be cryptographically secure. http://crbug.com/140076 +uint64_t RandUint64() { + uint64_t number; + RandBytes(&number, sizeof(number)); + return number; +} + +void RandBytes(void* output, size_t output_length) { + char* output_ptr = static_cast<char*>(output); + while (output_length > 0) { + const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min( + output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max()))); + const bool success = + RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; + CHECK(success); + output_length -= output_bytes_this_pass; + output_ptr += output_bytes_this_pass; + } +} + +} // namespace base diff --git a/ipc/chromium/src/base/revocable_store.cc b/ipc/chromium/src/base/revocable_store.cc new file mode 100644 index 0000000000..9a586a5a61 --- /dev/null +++ b/ipc/chromium/src/base/revocable_store.cc @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/revocable_store.h" + +#include "base/logging.h" + +RevocableStore::Revocable::Revocable(RevocableStore* store) + : store_reference_(store->owning_reference_) { + // We AddRef() the owning reference. + DCHECK(store_reference_->store()); + store_reference_->store()->Add(this); +} + +RevocableStore::Revocable::~Revocable() { + if (!revoked()) { + // Notify the store of our destruction. + --(store_reference_->store()->count_); + } +} + +RevocableStore::RevocableStore() : count_(0) { + // Create a new owning reference. + owning_reference_ = new StoreRef(this); +} + +RevocableStore::~RevocableStore() { + // Revoke all the items in the store. + owning_reference_->set_store(NULL); +} + +void RevocableStore::Add(Revocable* item) { + DCHECK(!item->revoked()); + ++count_; +} + +void RevocableStore::RevokeAll() { + // We revoke all the existing items in the store and reset our count. + owning_reference_->set_store(NULL); + count_ = 0; + + // Then we create a new owning reference for new items that get added. + // This Release()s the old owning reference, allowing it to be freed after + // all the items that were in the store are eventually destroyed. + owning_reference_ = new StoreRef(this); +} diff --git a/ipc/chromium/src/base/revocable_store.h b/ipc/chromium/src/base/revocable_store.h new file mode 100644 index 0000000000..13095728b3 --- /dev/null +++ b/ipc/chromium/src/base/revocable_store.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_REVOCABLE_STORE_H_ +#define BASE_REVOCABLE_STORE_H_ + +#include "base/basictypes.h" +#include "nsISupportsImpl.h" +#include "nsAutoPtr.h" + +// |RevocableStore| is a container of items that can be removed from the store. +class RevocableStore { + public: + // A |StoreRef| is used to link the |RevocableStore| to its items. There is + // one StoreRef per store, and each item holds a reference to it. If the + // store wishes to revoke its items, it sets |store_| to null. Items are + // permitted to release their reference to the |StoreRef| when they no longer + // require the store. + class StoreRef final { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StoreRef) + explicit StoreRef(RevocableStore* aStore) : store_(aStore) { } + + void set_store(RevocableStore* aStore) { store_ = aStore; } + RevocableStore* store() const { return store_; } + + protected: + ~StoreRef() {} + private: + RevocableStore* store_; + + DISALLOW_EVIL_CONSTRUCTORS(StoreRef); + }; + + // An item in the store. On construction, the object adds itself to the + // store. + class Revocable { + public: + explicit Revocable(RevocableStore* store); + ~Revocable(); + + // This item has been revoked if it no longer has a pointer to the store. + bool revoked() const { return !store_reference_->store(); } + + private: + // We hold a reference to the store through this ref pointer. We release + // this reference on destruction. + RefPtr<StoreRef> store_reference_; + + DISALLOW_EVIL_CONSTRUCTORS(Revocable); + }; + + RevocableStore(); + ~RevocableStore(); + + // Revokes all the items in the store. + void RevokeAll(); + + // Returns true if there are no items in the store. + bool empty() const { return count_ == 0; } + + private: + friend class Revocable; + + // Adds an item to the store. To add an item to the store, construct it + // with a pointer to the store. + void Add(Revocable* item); + + // This is the reference the unrevoked items in the store hold. + RefPtr<StoreRef> owning_reference_; + + // The number of unrevoked items in the store. + int count_; + + DISALLOW_EVIL_CONSTRUCTORS(RevocableStore); +}; + +#endif // BASE_REVOCABLE_STORE_H_ diff --git a/ipc/chromium/src/base/scoped_bstr_win.h b/ipc/chromium/src/base/scoped_bstr_win.h new file mode 100644 index 0000000000..86cfb0254d --- /dev/null +++ b/ipc/chromium/src/base/scoped_bstr_win.h @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_SCOPED_BSTR_WIN_H_ +#define BASE_SCOPED_BSTR_WIN_H_ + +#include "base/basictypes.h" // needed to pick up OS_WIN + +#include "base/logging.h" + +#include <windows.h> +#include <oleauto.h> + +// Manages a BSTR string pointer. +// The class interface is based on scoped_ptr. +class ScopedBstr { + public: + ScopedBstr() : bstr_(NULL) { + } + + // Constructor to create a new BSTR. + // NOTE: Do not pass a BSTR to this constructor expecting ownership to + // be transferred - even though it compiles! ;-) + explicit ScopedBstr(const wchar_t* non_bstr); + ~ScopedBstr(); + + // Give ScopedBstr ownership over an already allocated BSTR or NULL. + // If you need to allocate a new BSTR instance, use |allocate| instead. + void Reset(BSTR bstr = NULL); + + // Releases ownership of the BSTR to the caller. + BSTR Release(); + + // Creates a new BSTR from a wide string. + // If you already have a BSTR and want to transfer ownership to the + // ScopedBstr instance, call |reset| instead. + // Returns a pointer to the new BSTR, or NULL if allocation failed. + BSTR Allocate(const wchar_t* wide_str); + + // Allocates a new BSTR with the specified number of bytes. + // Returns a pointer to the new BSTR, or NULL if allocation failed. + BSTR AllocateBytes(int bytes); + + // Sets the allocated length field of the already-allocated BSTR to be + // |bytes|. This is useful when the BSTR was preallocated with e.g. + // SysAllocStringLen or SysAllocStringByteLen (call |AllocateBytes|) and + // then not all the bytes are being used. + // Note that if you want to set the length to a specific number of characters, + // you need to multiply by sizeof(wchar_t). Oddly, there's no public API to + // set the length, so we do this ourselves by hand. + // + // NOTE: The actual allocated size of the BSTR MUST be >= bytes. + // That responsibility is with the caller. + void SetByteLen(uint32_t bytes); + + // Swap values of two ScopedBstr's. + void Swap(ScopedBstr& bstr2); + + // Retrieves the pointer address. + // Used to receive BSTRs as out arguments (and take ownership). + // The function DCHECKs on the current value being NULL. + // Usage: GetBstr(bstr.Receive()); + BSTR* Receive(); + + // Returns number of chars in the BSTR. + uint32_t Length() const; + + // Returns the number of bytes allocated for the BSTR. + uint32_t ByteLength() const; + + operator BSTR() const { + return bstr_; + } + + protected: + BSTR bstr_; + + private: + // Forbid comparison of ScopedBstr types. You should never have the same + // BSTR owned by two different scoped_ptrs. + bool operator==(const ScopedBstr& bstr2) const; + bool operator!=(const ScopedBstr& bstr2) const; + DISALLOW_COPY_AND_ASSIGN(ScopedBstr); +}; + +// Template class to generate a BSTR from a static wide string +// without touching the heap. Use this class via the StackBstrVar and +// StackBstr macros. +template <uint32_t string_bytes> +class StackBstrT { + public: + // Try to stay as const as we can in an attempt to avoid someone + // using the class incorrectly (e.g. by supplying a variable instead + // of a verbatim string. We also have an assert in the constructor + // as an extra runtime check since the const-ness only catches one case. + explicit StackBstrT(const wchar_t* const str) { + // The BSTR API uses UINT, but we prefer uint32_t. + // Make sure we'll know about it if these types don't match. + COMPILE_ASSERT(sizeof(uint32_t) == sizeof(UINT), UintToUint32); + COMPILE_ASSERT(sizeof(wchar_t) == sizeof(OLECHAR), WcharToOlechar); + + // You shouldn't pass string pointers to this constructor since + // there's no way for the compiler to calculate the length of the + // string (string_bytes will be equal to pointer size in those cases). + DCHECK(lstrlenW(str) == (string_bytes / sizeof(bstr_.str_[0])) - 1) << + "not expecting a string pointer"; + memcpy(bstr_.str_, str, string_bytes); + bstr_.len_ = string_bytes - sizeof(wchar_t); + } + + operator BSTR() { + return bstr_.str_; + } + + protected: + struct BstrInternal { + uint32_t len_; + wchar_t str_[string_bytes / sizeof(wchar_t)]; + } bstr_; +}; + +// Use this macro to generate an inline BSTR from a wide string. +// This is about 6 times faster than using the SysAllocXxx functions to +// allocate a BSTR and helps with keeping heap fragmentation down. +// Example: +// DoBstrStuff(StackBstr(L"This is my BSTR")); +// Where DoBstrStuff is: +// HRESULT DoBstrStuff(BSTR bstr) { ... } +#define StackBstr(str) \ + static_cast<BSTR>(StackBstrT<sizeof(str)>(str)) + +// If you need a named BSTR variable that's based on a fixed string +// (e.g. if the BSTR is used inside a loop or more than one place), +// use StackBstrVar to declare a variable. +// Example: +// StackBstrVar(L"my_property", myprop); +// for (int i = 0; i < objects.length(); ++i) +// ProcessValue(objects[i].GetProp(myprop)); // GetProp accepts BSTR +#define StackBstrVar(str, var) \ + StackBstrT<sizeof(str)> var(str) + +#endif // BASE_SCOPED_BSTR_WIN_H_ diff --git a/ipc/chromium/src/base/scoped_cftyperef.h b/ipc/chromium/src/base/scoped_cftyperef.h new file mode 100644 index 0000000000..703a5130b4 --- /dev/null +++ b/ipc/chromium/src/base/scoped_cftyperef.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_SCOPED_CFTYPEREF_H_ +#define BASE_SCOPED_CFTYPEREF_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include "base/basictypes.h" + +// scoped_cftyperef<> is patterned after scoped_ptr<>, but maintains ownership +// of a CoreFoundation object: any object that can be represented as a +// CFTypeRef. Style deviations here are solely for compatibility with +// scoped_ptr<>'s interface, with which everyone is already familiar. +// +// When scoped_cftyperef<> takes ownership of an object (in the constructor or +// in reset()), it takes over the caller's existing ownership claim. The +// caller must own the object it gives to scoped_cftyperef<>, and relinquishes +// an ownership claim to that object. scoped_cftyperef<> does not call +// CFRetain(). +template<typename CFT> +class scoped_cftyperef { + public: + typedef CFT element_type; + + explicit scoped_cftyperef(CFT object = NULL) + : object_(object) { + } + + ~scoped_cftyperef() { + if (object_) + CFRelease(object_); + } + + void reset(CFT object = NULL) { + if (object_) + CFRelease(object_); + object_ = object; + } + + bool operator==(CFT that) const { + return object_ == that; + } + + bool operator!=(CFT that) const { + return object_ != that; + } + + operator CFT() const { + return object_; + } + + CFT get() const { + return object_; + } + + void swap(scoped_cftyperef& that) { + CFT temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + // scoped_cftyperef<>::release() is like scoped_ptr<>::release. It is NOT + // a wrapper for CFRelease(). To force a scoped_cftyperef<> object to call + // CFRelease(), use scoped_cftyperef<>::reset(). + CFT release() { + CFT temp = object_; + object_ = NULL; + return temp; + } + + private: + CFT object_; + + DISALLOW_COPY_AND_ASSIGN(scoped_cftyperef); +}; + +#endif // BASE_SCOPED_CFTYPEREF_H_ diff --git a/ipc/chromium/src/base/scoped_handle.h b/ipc/chromium/src/base/scoped_handle.h new file mode 100644 index 0000000000..f4fc29b66a --- /dev/null +++ b/ipc/chromium/src/base/scoped_handle.h @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_SCOPED_HANDLE_H_ +#define BASE_SCOPED_HANDLE_H_ + +#include <stdio.h> + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include "base/scoped_handle_win.h" +#endif + +class ScopedStdioHandle { + public: + ScopedStdioHandle() + : handle_(NULL) { } + + explicit ScopedStdioHandle(FILE* handle) + : handle_(handle) { } + + ~ScopedStdioHandle() { + Close(); + } + + void Close() { + if (handle_) { + fclose(handle_); + handle_ = NULL; + } + } + + FILE* get() const { return handle_; } + + FILE* Take() { + FILE* temp = handle_; + handle_ = NULL; + return temp; + } + + void Set(FILE* newhandle) { + Close(); + handle_ = newhandle; + } + + private: + FILE* handle_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedStdioHandle); +}; + +#endif // BASE_SCOPED_HANDLE_H_ diff --git a/ipc/chromium/src/base/scoped_handle_win.h b/ipc/chromium/src/base/scoped_handle_win.h new file mode 100644 index 0000000000..d4d8fccc4f --- /dev/null +++ b/ipc/chromium/src/base/scoped_handle_win.h @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_SCOPED_HANDLE_WIN_H_ +#define BASE_SCOPED_HANDLE_WIN_H_ + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/logging.h" + +// Used so we always remember to close the handle. +// The class interface matches that of ScopedStdioHandle in addition to an +// IsValid() method since invalid handles on windows can be either NULL or +// INVALID_HANDLE_VALUE (-1). +// +// Example: +// ScopedHandle hfile(CreateFile(...)); +// if (!hfile.Get()) +// ...process error +// ReadFile(hfile.Get(), ...); +// +// To sqirrel the handle away somewhere else: +// secret_handle_ = hfile.Take(); +// +// To explicitly close the handle: +// hfile.Close(); +class ScopedHandle { + public: + ScopedHandle() : handle_(NULL) { + } + + explicit ScopedHandle(HANDLE h) : handle_(NULL) { + Set(h); + } + + ~ScopedHandle() { + Close(); + } + + // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL + // usage for errors. + bool IsValid() const { + return handle_ != NULL; + } + + void Set(HANDLE new_handle) { + Close(); + + // Windows is inconsistent about invalid handles, so we always use NULL + if (new_handle != INVALID_HANDLE_VALUE) + handle_ = new_handle; + } + + HANDLE Get() { + return handle_; + } + + operator HANDLE() { return handle_; } + + HANDLE Take() { + // transfers ownership away from this object + HANDLE h = handle_; + handle_ = NULL; + return h; + } + + void Close() { + if (handle_) { + if (!::CloseHandle(handle_)) { + NOTREACHED(); + } + handle_ = NULL; + } + } + + private: + HANDLE handle_; + DISALLOW_EVIL_CONSTRUCTORS(ScopedHandle); +}; + +// Like ScopedHandle, but for HANDLEs returned from FindFile(). +class ScopedFindFileHandle { + public: + explicit ScopedFindFileHandle(HANDLE handle) : handle_(handle) { + // Windows is inconsistent about invalid handles, so we always use NULL + if (handle_ == INVALID_HANDLE_VALUE) + handle_ = NULL; + } + + ~ScopedFindFileHandle() { + if (handle_) + FindClose(handle_); + } + + // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL + // usage for errors. + bool IsValid() const { return handle_ != NULL; } + + operator HANDLE() { return handle_; } + + private: + HANDLE handle_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedFindFileHandle); +}; + +// Like ScopedHandle but for HDC. Only use this on HDCs returned from +// CreateCompatibleDC. For an HDC returned by GetDC, use ReleaseDC instead. +class ScopedHDC { + public: + ScopedHDC() : hdc_(NULL) { } + explicit ScopedHDC(HDC h) : hdc_(h) { } + + ~ScopedHDC() { + Close(); + } + + HDC Get() { + return hdc_; + } + + void Set(HDC h) { + Close(); + hdc_ = h; + } + + operator HDC() { return hdc_; } + + private: + void Close() { +#ifdef NOGDI + assert(false); +#else + if (hdc_) + DeleteDC(hdc_); +#endif // NOGDI + } + + HDC hdc_; + DISALLOW_EVIL_CONSTRUCTORS(ScopedHDC); +}; + +// Like ScopedHandle but for GDI objects. +template<class T> +class ScopedGDIObject { + public: + ScopedGDIObject() : object_(NULL) {} + explicit ScopedGDIObject(T object) : object_(object) {} + + ~ScopedGDIObject() { + Close(); + } + + T Get() { + return object_; + } + + void Set(T object) { + if (object_ && object != object_) + Close(); + object_ = object; + } + + ScopedGDIObject& operator=(T object) { + Set(object); + return *this; + } + + operator T() { return object_; } + + private: + void Close() { + if (object_) + DeleteObject(object_); + } + + T object_; + DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject); +}; + +// Typedefs for some common use cases. +typedef ScopedGDIObject<HBITMAP> ScopedBitmap; +typedef ScopedGDIObject<HRGN> ScopedHRGN; +typedef ScopedGDIObject<HFONT> ScopedHFONT; + + +// Like ScopedHandle except for HGLOBAL. +template<class T> +class ScopedHGlobal { + public: + explicit ScopedHGlobal(HGLOBAL glob) : glob_(glob) { + data_ = static_cast<T*>(GlobalLock(glob_)); + } + ~ScopedHGlobal() { + GlobalUnlock(glob_); + } + + T* get() { return data_; } + + size_t Size() const { return GlobalSize(glob_); } + + T* operator->() const { + assert(data_ != 0); + return data_; + } + + private: + HGLOBAL glob_; + + T* data_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedHGlobal); +}; + +#endif // BASE_SCOPED_HANDLE_WIN_H_ diff --git a/ipc/chromium/src/base/scoped_nsautorelease_pool.h b/ipc/chromium/src/base/scoped_nsautorelease_pool.h new file mode 100644 index 0000000000..2136b91275 --- /dev/null +++ b/ipc/chromium/src/base/scoped_nsautorelease_pool.h @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 BASE_SCOPED_NSAUTORELEASE_POOL_H_ +#define BASE_SCOPED_NSAUTORELEASE_POOL_H_ + +#include "base/basictypes.h" + +#if defined(OS_MACOSX) +#if defined(__OBJC__) +@class NSAutoreleasePool; +#else // __OBJC__ +class NSAutoreleasePool; +#endif // __OBJC__ +#endif // OS_MACOSX + +namespace base { + +// On the Mac, ScopedNSAutoreleasePool allocates an NSAutoreleasePool when +// instantiated and sends it a -drain message when destroyed. This allows an +// autorelease pool to be maintained in ordinary C++ code without bringing in +// any direct Objective-C dependency. +// +// On other platforms, ScopedNSAutoreleasePool is an empty object with no +// effects. This allows it to be used directly in cross-platform code without +// ugly #ifdefs. +class ScopedNSAutoreleasePool { + public: +#if !defined(OS_MACOSX) + ScopedNSAutoreleasePool() {} + void Recycle() { } +#else // OS_MACOSX + ScopedNSAutoreleasePool(); + ~ScopedNSAutoreleasePool(); + + // Clear out the pool in case its position on the stack causes it to be + // alive for long periods of time (such as the entire length of the app). + // Only use then when you're certain the items currently in the pool are + // no longer needed. + void Recycle(); + private: + NSAutoreleasePool* autorelease_pool_; +#endif // OS_MACOSX + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedNSAutoreleasePool); +}; + +} // namespace base + +#endif // BASE_SCOPED_NSAUTORELEASE_POOL_H_ diff --git a/ipc/chromium/src/base/scoped_nsautorelease_pool.mm b/ipc/chromium/src/base/scoped_nsautorelease_pool.mm new file mode 100644 index 0000000000..174fb821d6 --- /dev/null +++ b/ipc/chromium/src/base/scoped_nsautorelease_pool.mm @@ -0,0 +1,30 @@ +// Copyright (c) 2008 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 "base/scoped_nsautorelease_pool.h" + +#import <Foundation/Foundation.h> + +#include "base/logging.h" + +namespace base { + +ScopedNSAutoreleasePool::ScopedNSAutoreleasePool() + : autorelease_pool_([[NSAutoreleasePool alloc] init]) { + DCHECK(autorelease_pool_); +} + +ScopedNSAutoreleasePool::~ScopedNSAutoreleasePool() { + [autorelease_pool_ drain]; +} + +// Cycle the internal pool, allowing everything there to get cleaned up and +// start anew. +void ScopedNSAutoreleasePool::Recycle() { + [autorelease_pool_ drain]; + autorelease_pool_ = [[NSAutoreleasePool alloc] init]; + DCHECK(autorelease_pool_); +} + +} // namespace base diff --git a/ipc/chromium/src/base/scoped_nsobject.h b/ipc/chromium/src/base/scoped_nsobject.h new file mode 100644 index 0000000000..2041140210 --- /dev/null +++ b/ipc/chromium/src/base/scoped_nsobject.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 BASE_SCOPED_NSOBJECT_H_ +#define BASE_SCOPED_NSOBJECT_H_ + +#import <Foundation/Foundation.h> +#include "base/basictypes.h" + +// scoped_nsobject<> is patterned after scoped_ptr<>, but maintains ownership +// of an NSObject subclass object. Style deviations here are solely for +// compatibility with scoped_ptr<>'s interface, with which everyone is already +// familiar. +// +// When scoped_nsobject<> takes ownership of an object (in the constructor or +// in reset()), it takes over the caller's existing ownership claim. The +// caller must own the object it gives to scoped_nsobject<>, and relinquishes +// an ownership claim to that object. scoped_nsobject<> does not call +// -retain. +template<typename NST> +class scoped_nsobject { + public: + typedef NST* element_type; + + explicit scoped_nsobject(NST* object = nil) + : object_(object) { + } + + ~scoped_nsobject() { + [object_ release]; + } + + void reset(NST* object = nil) { + [object_ release]; + object_ = object; + } + + bool operator==(NST* that) const { + return object_ == that; + } + + bool operator!=(NST* that) const { + return object_ != that; + } + + operator NST*() const { + return object_; + } + + NST* get() const { + return object_; + } + + void swap(scoped_nsobject& that) { + NST* temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + // scoped_nsobject<>::release() is like scoped_ptr<>::release. It is NOT + // a wrapper for [object_ release]. To force a scoped_nsobject<> object to + // call [object_ release], use scoped_nsobject<>::reset(). + NST* release() { + NST* temp = object_; + object_ = nil; + return temp; + } + + private: + NST* object_; + + DISALLOW_COPY_AND_ASSIGN(scoped_nsobject); +}; + +#endif // BASE_SCOPED_NSOBJECT_H_ diff --git a/ipc/chromium/src/base/shared_memory.h b/ipc/chromium/src/base/shared_memory.h new file mode 100644 index 0000000000..15a0a95fe5 --- /dev/null +++ b/ipc/chromium/src/base/shared_memory.h @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_SHARED_MEMORY_H_ +#define BASE_SHARED_MEMORY_H_ + +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include <sys/types.h> +#include <semaphore.h> +#include "base/file_descriptor_posix.h" +#endif +#include <string> + +#include "base/basictypes.h" +#include "base/process.h" + +namespace base { + +// SharedMemoryHandle is a platform specific type which represents +// the underlying OS handle to a shared memory segment. +#if defined(OS_WIN) +typedef HANDLE SharedMemoryHandle; +typedef HANDLE SharedMemoryLock; +#elif defined(OS_POSIX) +// A SharedMemoryId is sufficient to identify a given shared memory segment on a +// system, but insufficient to map it. +typedef FileDescriptor SharedMemoryHandle; +typedef ino_t SharedMemoryId; +// On POSIX, the lock is implemented as a lockf() on the mapped file, +// so no additional member (or definition of SharedMemoryLock) is +// needed. +#endif + +// Platform abstraction for shared memory. Provides a C++ wrapper +// around the OS primitive for a memory mapped file. +class SharedMemory { + public: + // Create a new SharedMemory object. + SharedMemory(); + + // Create a new SharedMemory object from an existing, open + // shared memory file. + SharedMemory(SharedMemoryHandle init_handle, bool read_only) + : SharedMemory() { + SetHandle(init_handle, read_only); + } + + // Destructor. Will close any open files. + ~SharedMemory(); + + // Initialize a new SharedMemory object from an existing, open + // shared memory file. + bool SetHandle(SharedMemoryHandle handle, bool read_only); + + // Return true iff the given handle is valid (i.e. not the distingished + // invalid value; NULL for a HANDLE and -1 for a file descriptor) + static bool IsHandleValid(const SharedMemoryHandle& handle); + + // Return invalid handle (see comment above for exact definition). + static SharedMemoryHandle NULLHandle(); + + // Creates or opens a shared memory segment based on a name. + // If read_only is true, opens the memory as read-only. + // If open_existing is true, and the shared memory already exists, + // opens the existing shared memory and ignores the size parameter. + // If name is the empty string, use a unique name. + // Returns true on success, false on failure. + bool Create(const std::string& name, bool read_only, bool open_existing, + size_t size); + + // Deletes resources associated with a shared memory segment based on name. + // Not all platforms require this call. + bool Delete(const std::wstring& name); + + // Opens a shared memory segment based on a name. + // If read_only is true, opens for read-only access. + // If name is the empty string, use a unique name. + // Returns true on success, false on failure. + bool Open(const std::wstring& name, bool read_only); + + // Maps the shared memory into the caller's address space. + // Returns true on success, false otherwise. The memory address + // is accessed via the memory() accessor. + bool Map(size_t bytes); + + // Unmaps the shared memory from the caller's address space. + // Returns true if successful; returns false on error or if the + // memory is not mapped. + bool Unmap(); + + // Get the size of the opened shared memory backing file. + // Note: This size is only available to the creator of the + // shared memory, and not to those that opened shared memory + // created externally. + // Returns 0 if not opened or unknown. + size_t max_size() const { return max_size_; } + + // Gets a pointer to the opened memory space if it has been + // Mapped via Map(). Returns NULL if it is not mapped. + void *memory() const { return memory_; } + + // Get access to the underlying OS handle for this segment. + // Use of this handle for anything other than an opaque + // identifier is not portable. + SharedMemoryHandle handle() const; + +#if defined(OS_POSIX) + // Return a unique identifier for this shared memory segment. Inode numbers + // are technically only unique to a single filesystem. However, we always + // allocate shared memory backing files from the same directory, so will end + // up on the same filesystem. + SharedMemoryId id() const { return inode_; } +#endif + + // Closes the open shared memory segment. + // It is safe to call Close repeatedly. + void Close(bool unmap_view = true); + + // Share the shared memory to another process. Attempts + // to create a platform-specific new_handle which can be + // used in a remote process to access the shared memory + // file. new_handle is an ouput parameter to receive + // the handle for use in the remote process. + // Returns true on success, false otherwise. + bool ShareToProcess(base::ProcessId target_pid, + SharedMemoryHandle* new_handle) { + return ShareToProcessCommon(target_pid, new_handle, false); + } + + // Logically equivalent to: + // bool ok = ShareToProcess(process, new_handle); + // Close(); + // return ok; + // Note that the memory is unmapped by calling this method, regardless of the + // return value. + bool GiveToProcess(ProcessId target_pid, + SharedMemoryHandle* new_handle) { + return ShareToProcessCommon(target_pid, new_handle, true); + } + + // Lock the shared memory. + // This is a cross-process lock which may be recursively + // locked by the same thread. + // TODO(port): + // WARNING: on POSIX the lock only works across processes, not + // across threads. 2 threads in the same process can both grab the + // lock at the same time. There are several solutions for this + // (futex, lockf+anon_semaphore) but none are both clean and common + // across Mac and Linux. + void Lock(); + + // Release the shared memory lock. + void Unlock(); + + private: +#if defined(OS_POSIX) + bool CreateOrOpen(const std::wstring &name, int posix_flags, size_t size); + bool FilenameForMemoryName(const std::wstring &memname, + std::wstring *filename); + void LockOrUnlockCommon(int function); + +#endif + bool ShareToProcessCommon(ProcessId target_pid, + SharedMemoryHandle* new_handle, + bool close_self); + +#if defined(OS_WIN) + std::wstring name_; + HANDLE mapped_file_; +#elif defined(OS_POSIX) + int mapped_file_; + ino_t inode_; +#endif + void* memory_; + bool read_only_; + size_t max_size_; +#if !defined(OS_POSIX) + SharedMemoryLock lock_; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(SharedMemory); +}; + +// A helper class that acquires the shared memory lock while +// the SharedMemoryAutoLock is in scope. +class SharedMemoryAutoLock { + public: + explicit SharedMemoryAutoLock(SharedMemory* shared_memory) + : shared_memory_(shared_memory) { + shared_memory_->Lock(); + } + + ~SharedMemoryAutoLock() { + shared_memory_->Unlock(); + } + + private: + SharedMemory* shared_memory_; + DISALLOW_EVIL_CONSTRUCTORS(SharedMemoryAutoLock); +}; + +} // namespace base + +#endif // BASE_SHARED_MEMORY_H_ diff --git a/ipc/chromium/src/base/shared_memory_posix.cc b/ipc/chromium/src/base/shared_memory_posix.cc new file mode 100644 index 0000000000..b02326df91 --- /dev/null +++ b/ipc/chromium/src/base/shared_memory_posix.cc @@ -0,0 +1,338 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/shared_memory.h" + +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/string_util.h" +#include "mozilla/UniquePtr.h" + +namespace base { + +SharedMemory::SharedMemory() + : mapped_file_(-1), + inode_(0), + memory_(NULL), + read_only_(false), + max_size_(0) { +} + +SharedMemory::~SharedMemory() { + Close(); +} + +bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) { + DCHECK(mapped_file_ == -1); + + struct stat st; + if (fstat(handle.fd, &st) < 0) { + return false; + } + + mapped_file_ = handle.fd; + inode_ = st.st_ino; + read_only_ = read_only; + return true; +} + +// static +bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { + return handle.fd >= 0; +} + +// static +SharedMemoryHandle SharedMemory::NULLHandle() { + return SharedMemoryHandle(); +} + +bool SharedMemory::Create(const std::string &cname, bool read_only, + bool open_existing, size_t size) { + read_only_ = read_only; + + std::wstring name = UTF8ToWide(cname); + + int posix_flags = 0; + posix_flags |= read_only ? O_RDONLY : O_RDWR; + if (!open_existing || mapped_file_ <= 0) + posix_flags |= O_CREAT; + + if (!CreateOrOpen(name, posix_flags, size)) + return false; + + max_size_ = size; + return true; +} + +// Our current implementation of shmem is with mmap()ing of files. +// These files need to be deleted explicitly. +// In practice this call is only needed for unit tests. +bool SharedMemory::Delete(const std::wstring& name) { + std::wstring mem_filename; + if (FilenameForMemoryName(name, &mem_filename) == false) + return false; + + FilePath path(WideToUTF8(mem_filename)); + if (file_util::PathExists(path)) { + return file_util::Delete(path); + } + + // Doesn't exist, so success. + return true; +} + +bool SharedMemory::Open(const std::wstring &name, bool read_only) { + read_only_ = read_only; + + int posix_flags = 0; + posix_flags |= read_only ? O_RDONLY : O_RDWR; + + return CreateOrOpen(name, posix_flags, 0); +} + +// For the given shmem named |memname|, return a filename to mmap() +// (and possibly create). Modifies |filename|. Return false on +// error, or true of we are happy. +bool SharedMemory::FilenameForMemoryName(const std::wstring &memname, + std::wstring *filename) { + std::wstring mem_filename; + + // mem_name will be used for a filename; make sure it doesn't + // contain anything which will confuse us. + DCHECK(memname.find_first_of(L"/") == std::string::npos); + DCHECK(memname.find_first_of(L"\0") == std::string::npos); + + FilePath temp_dir; + if (file_util::GetShmemTempDir(&temp_dir) == false) + return false; + + mem_filename = UTF8ToWide(temp_dir.value()); + file_util::AppendToPath(&mem_filename, L"com.google.chrome.shmem." + memname); + *filename = mem_filename; + return true; +} + +namespace { + +// A class to handle auto-closing of FILE*'s. +class ScopedFILEClose { + public: + inline void operator()(FILE* x) const { + if (x) { + fclose(x); + } + } +}; + +typedef mozilla::UniquePtr<FILE, ScopedFILEClose> ScopedFILE; + +} + +// Chromium mostly only use the unique/private shmem as specified by +// "name == L"". The exception is in the StatsTable. +// TODO(jrg): there is no way to "clean up" all unused named shmem if +// we restart from a crash. (That isn't a new problem, but it is a problem.) +// In case we want to delete it later, it may be useful to save the value +// of mem_filename after FilenameForMemoryName(). +bool SharedMemory::CreateOrOpen(const std::wstring &name, + int posix_flags, size_t size) { + DCHECK(mapped_file_ == -1); + + ScopedFILE file_closer; + FILE *fp; + + if (name == L"") { + // It doesn't make sense to have a read-only private piece of shmem + DCHECK(posix_flags & (O_RDWR | O_WRONLY)); + + FilePath path; + fp = file_util::CreateAndOpenTemporaryShmemFile(&path); + + // Deleting the file prevents anyone else from mapping it in + // (making it private), and prevents the need for cleanup (once + // the last fd is closed, it is truly freed). + file_util::Delete(path); + } else { + std::wstring mem_filename; + if (FilenameForMemoryName(name, &mem_filename) == false) + return false; + + std::string mode; + switch (posix_flags) { + case (O_RDWR | O_CREAT): + // Careful: "w+" will truncate if it already exists. + mode = "a+"; + break; + case O_RDWR: + mode = "r+"; + break; + case O_RDONLY: + mode = "r"; + break; + default: + NOTIMPLEMENTED(); + break; + } + + fp = file_util::OpenFile(mem_filename, mode.c_str()); + } + + if (fp == NULL) + return false; + file_closer.reset(fp); // close when we go out of scope + + // Make sure the (new) file is the right size. + // According to the man page, "Use of truncate() to extend a file is + // not portable." + if (size && (posix_flags & (O_RDWR | O_CREAT))) { + // Get current size. + struct stat stat; + if (fstat(fileno(fp), &stat) != 0) + return false; + size_t current_size = stat.st_size; + if (current_size != size) { + if (ftruncate(fileno(fp), size) != 0) + return false; + if (fseeko(fp, size, SEEK_SET) != 0) + return false; + } + } + + mapped_file_ = dup(fileno(fp)); + DCHECK(mapped_file_ >= 0); + + struct stat st; + if (fstat(mapped_file_, &st)) + NOTREACHED(); + inode_ = st.st_ino; + + return true; +} + +bool SharedMemory::Map(size_t bytes) { + if (mapped_file_ == -1) + return false; + + memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), + MAP_SHARED, mapped_file_, 0); + + if (memory_) + max_size_ = bytes; + + bool mmap_succeeded = (memory_ != (void*)-1); + DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno; + return mmap_succeeded; +} + +bool SharedMemory::Unmap() { + if (memory_ == NULL) + return false; + + munmap(memory_, max_size_); + memory_ = NULL; + max_size_ = 0; + return true; +} + +bool SharedMemory::ShareToProcessCommon(ProcessId processId, + SharedMemoryHandle *new_handle, + bool close_self) { + const int new_fd = dup(mapped_file_); + DCHECK(new_fd >= -1); + new_handle->fd = new_fd; + new_handle->auto_close = true; + + if (close_self) + Close(); + + return true; +} + + +void SharedMemory::Close(bool unmap_view) { + if (unmap_view) { + Unmap(); + } + + if (mapped_file_ >= 0) { + close(mapped_file_); + mapped_file_ = -1; + } +} + +#ifdef ANDROID +void SharedMemory::LockOrUnlockCommon(int function) { + DCHECK(mapped_file_ >= 0); + struct flock lockreq; + lockreq.l_type = function; + lockreq.l_whence = SEEK_SET; + lockreq.l_start = 0; + lockreq.l_len = 0; + while (fcntl(mapped_file_, F_SETLKW, &lockreq) < 0) { + if (errno == EINTR) { + continue; + } else if (errno == ENOLCK) { + // temporary kernel resource exaustion + PlatformThread::Sleep(500); + continue; + } else { + NOTREACHED() << "lockf() failed." + << " function:" << function + << " fd:" << mapped_file_ + << " errno:" << errno + << " msg:" << strerror(errno); + } + } +} + +void SharedMemory::Lock() { + LockOrUnlockCommon(F_WRLCK); +} + +void SharedMemory::Unlock() { + LockOrUnlockCommon(F_UNLCK); +} +#else +void SharedMemory::LockOrUnlockCommon(int function) { + DCHECK(mapped_file_ >= 0); + while (lockf(mapped_file_, function, 0) < 0) { + if (errno == EINTR) { + continue; + } else if (errno == ENOLCK) { + // temporary kernel resource exaustion + PlatformThread::Sleep(500); + continue; + } else { + NOTREACHED() << "lockf() failed." + << " function:" << function + << " fd:" << mapped_file_ + << " errno:" << errno + << " msg:" << strerror(errno); + } + } +} + +void SharedMemory::Lock() { + LockOrUnlockCommon(F_LOCK); +} + +void SharedMemory::Unlock() { + LockOrUnlockCommon(F_ULOCK); +} +#endif + +SharedMemoryHandle SharedMemory::handle() const { + return FileDescriptor(mapped_file_, false); +} + +} // namespace base diff --git a/ipc/chromium/src/base/shared_memory_win.cc b/ipc/chromium/src/base/shared_memory_win.cc new file mode 100644 index 0000000000..042adc834b --- /dev/null +++ b/ipc/chromium/src/base/shared_memory_win.cc @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/shared_memory.h" + +#include "base/logging.h" +#include "base/win_util.h" +#include "base/string_util.h" +#include "mozilla/ipc/ProtocolUtils.h" + +namespace base { + +SharedMemory::SharedMemory() + : mapped_file_(NULL), + memory_(NULL), + read_only_(false), + max_size_(0), + lock_(NULL) { +} + +SharedMemory::~SharedMemory() { + Close(); + if (lock_ != NULL) + CloseHandle(lock_); +} + +bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) { + DCHECK(mapped_file_ == NULL); + + mapped_file_ = handle; + read_only_ = read_only; + return true; +} + +// static +bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { + return handle != NULL; +} + +// static +SharedMemoryHandle SharedMemory::NULLHandle() { + return NULL; +} + +bool SharedMemory::Create(const std::string &cname, bool read_only, + bool open_existing, size_t size) { + DCHECK(mapped_file_ == NULL); + std::wstring name = UTF8ToWide(cname); + name_ = name; + read_only_ = read_only; + mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, + read_only_ ? PAGE_READONLY : PAGE_READWRITE, 0, static_cast<DWORD>(size), + name.empty() ? NULL : name.c_str()); + if (!mapped_file_) + return false; + + // Check if the shared memory pre-exists. + if (GetLastError() == ERROR_ALREADY_EXISTS && !open_existing) { + Close(); + return false; + } + max_size_ = size; + return true; +} + +bool SharedMemory::Delete(const std::wstring& name) { + // intentionally empty -- there is nothing for us to do on Windows. + return true; +} + +bool SharedMemory::Open(const std::wstring &name, bool read_only) { + DCHECK(mapped_file_ == NULL); + + name_ = name; + read_only_ = read_only; + mapped_file_ = OpenFileMapping( + read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, false, + name.empty() ? NULL : name.c_str()); + if (mapped_file_ != NULL) { + // Note: size_ is not set in this case. + return true; + } + return false; +} + +bool SharedMemory::Map(size_t bytes) { + if (mapped_file_ == NULL) + return false; + + memory_ = MapViewOfFile(mapped_file_, + read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 0, 0, bytes); + if (memory_ != NULL) { + return true; + } + return false; +} + +bool SharedMemory::Unmap() { + if (memory_ == NULL) + return false; + + UnmapViewOfFile(memory_); + memory_ = NULL; + return true; +} + +bool SharedMemory::ShareToProcessCommon(ProcessId processId, + SharedMemoryHandle *new_handle, + bool close_self) { + *new_handle = 0; + DWORD access = STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ; + DWORD options = 0; + HANDLE mapped_file = mapped_file_; + HANDLE result; + if (!read_only_) + access |= FILE_MAP_WRITE; + if (close_self) { + // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file. + options = DUPLICATE_CLOSE_SOURCE; + mapped_file_ = NULL; + Unmap(); + } + + if (processId == GetCurrentProcId() && close_self) { + *new_handle = mapped_file; + return true; + } + + if (!mozilla::ipc::DuplicateHandle(mapped_file, processId, &result, access, + options)) { + return false; + } + + *new_handle = result; + return true; +} + + +void SharedMemory::Close(bool unmap_view) { + if (unmap_view) { + Unmap(); + } + + if (mapped_file_ != NULL) { + CloseHandle(mapped_file_); + mapped_file_ = NULL; + } +} + +void SharedMemory::Lock() { + if (lock_ == NULL) { + std::wstring name = name_; + name.append(L"lock"); + lock_ = CreateMutex(NULL, FALSE, name.c_str()); + DCHECK(lock_ != NULL); + if (lock_ == NULL) { + DLOG(ERROR) << "Could not create mutex" << GetLastError(); + return; // there is nothing good we can do here. + } + } + WaitForSingleObject(lock_, INFINITE); +} + +void SharedMemory::Unlock() { + DCHECK(lock_ != NULL); + ReleaseMutex(lock_); +} + +SharedMemoryHandle SharedMemory::handle() const { + return mapped_file_; +} + +} // namespace base diff --git a/ipc/chromium/src/base/singleton.h b/ipc/chromium/src/base/singleton.h new file mode 100644 index 0000000000..9f333af974 --- /dev/null +++ b/ipc/chromium/src/base/singleton.h @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_SINGLETON_H_ +#define BASE_SINGLETON_H_ + +#include "base/at_exit.h" +#include "base/atomicops.h" +#include "base/platform_thread.h" + +// Default traits for Singleton<Type>. Calls operator new and operator delete on +// the object. Registers automatic deletion at process exit. +// Overload if you need arguments or another memory allocation function. +template<typename Type> +struct DefaultSingletonTraits { + // Allocates the object. + static Type* New() { + // The parenthesis is very important here; it forces POD type + // initialization. + return new Type(); + } + + // Destroys the object. + static void Delete(Type* x) { + delete x; + } + + // Set to true to automatically register deletion of the object on process + // exit. See below for the required call that makes this happen. + static const bool kRegisterAtExit = true; +}; + + +// Alternate traits for use with the Singleton<Type>. Identical to +// DefaultSingletonTraits except that the Singleton will not be cleaned up +// at exit. +template<typename Type> +struct LeakySingletonTraits : public DefaultSingletonTraits<Type> { + static const bool kRegisterAtExit = false; +}; + + +// The Singleton<Type, Traits, DifferentiatingType> class manages a single +// instance of Type which will be created on first use and will be destroyed at +// normal process exit). The Trait::Delete function will not be called on +// abnormal process exit. +// +// DifferentiatingType is used as a key to differentiate two different +// singletons having the same memory allocation functions but serving a +// different purpose. This is mainly used for Locks serving different purposes. +// +// Example usages: (none are preferred, they all result in the same code) +// 1. FooClass* ptr = Singleton<FooClass>::get(); +// ptr->Bar(); +// 2. Singleton<FooClass>()->Bar(); +// 3. Singleton<FooClass>::get()->Bar(); +// +// Singleton<> has no non-static members and doesn't need to actually be +// instantiated. It does no harm to instantiate it and use it as a class member +// or at global level since it is acting as a POD type. +// +// This class is itself thread-safe. The underlying Type must of course be +// thread-safe if you want to use it concurrently. Two parameters may be tuned +// depending on the user's requirements. +// +// Glossary: +// RAE = kRegisterAtExit +// +// On every platform, if Traits::RAE is true, the singleton will be destroyed at +// process exit. More precisely it uses base::AtExitManager which requires an +// object of this type to be instanciated. AtExitManager mimics the semantics +// of atexit() such as LIFO order but under Windows is safer to call. For more +// information see at_exit.h. +// +// If Traits::RAE is false, the singleton will not be freed at process exit, +// thus the singleton will be leaked if it is ever accessed. Traits::RAE +// shouldn't be false unless absolutely necessary. Remember that the heap where +// the object is allocated may be destroyed by the CRT anyway. +// +// If you want to ensure that your class can only exist as a singleton, make +// its constructors private, and make DefaultSingletonTraits<> a friend: +// +// #include "base/singleton.h" +// class FooClass { +// public: +// void Bar() { ... } +// private: +// FooClass() { ... } +// friend struct DefaultSingletonTraits<FooClass>; +// +// DISALLOW_EVIL_CONSTRUCTORS(FooClass); +// }; +// +// Caveats: +// (a) Every call to get(), operator->() and operator*() incurs some overhead +// (16ns on my P4/2.8GHz) to check whether the object has already been +// initialized. You may wish to cache the result of get(); it will not +// change. +// +// (b) Your factory function must never throw an exception. This class is not +// exception-safe. +// +template <typename Type, + typename Traits = DefaultSingletonTraits<Type>, + typename DifferentiatingType = Type> +class Singleton { + public: + // This class is safe to be constructed and copy-constructed since it has no + // member. + + // Return a pointer to the one true instance of the class. + static Type* get() { + // Our AtomicWord doubles as a spinlock, where a value of + // kBeingCreatedMarker means the spinlock is being held for creation. + static const base::subtle::AtomicWord kBeingCreatedMarker = 1; + + base::subtle::AtomicWord value = base::subtle::NoBarrier_Load(&instance_); + if (value != 0 && value != kBeingCreatedMarker) + return reinterpret_cast<Type*>(value); + + // Object isn't created yet, maybe we will get to create it, let's try... + if (base::subtle::Acquire_CompareAndSwap(&instance_, + 0, + kBeingCreatedMarker) == 0) { + // instance_ was NULL and is now kBeingCreatedMarker. Only one thread + // will ever get here. Threads might be spinning on us, and they will + // stop right after we do this store. + Type* newval = Traits::New(); + base::subtle::Release_Store( + &instance_, reinterpret_cast<base::subtle::AtomicWord>(newval)); + + if (Traits::kRegisterAtExit) + base::AtExitManager::RegisterCallback(OnExit, NULL); + + return newval; + } + + // We hit a race. Another thread beat us and either: + // - Has the object in BeingCreated state + // - Already has the object created... + // We know value != NULL. It could be kBeingCreatedMarker, or a valid ptr. + // Unless your constructor can be very time consuming, it is very unlikely + // to hit this race. When it does, we just spin and yield the thread until + // the object has been created. + while (true) { + value = base::subtle::NoBarrier_Load(&instance_); + if (value != kBeingCreatedMarker) + break; + PlatformThread::YieldCurrentThread(); + } + + return reinterpret_cast<Type*>(value); + } + + // Shortcuts. + Type& operator*() { + return *get(); + } + + Type* operator->() { + return get(); + } + + private: + // Adapter function for use with AtExit(). This should be called single + // threaded, but we might as well take the precautions anyway. + static void OnExit(void* unused) { + // AtExit should only ever be register after the singleton instance was + // created. We should only ever get here with a valid instance_ pointer. + Traits::Delete(reinterpret_cast<Type*>( + base::subtle::NoBarrier_AtomicExchange(&instance_, 0))); + } + static base::subtle::AtomicWord instance_; +}; + +template <typename Type, typename Traits, typename DifferentiatingType> +base::subtle::AtomicWord Singleton<Type, Traits, DifferentiatingType>:: + instance_ = 0; + +#endif // BASE_SINGLETON_H_ diff --git a/ipc/chromium/src/base/singleton_objc.h b/ipc/chromium/src/base/singleton_objc.h new file mode 100644 index 0000000000..07fc092ab8 --- /dev/null +++ b/ipc/chromium/src/base/singleton_objc.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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. + +// Support for using the Singleton<T> pattern with Objective-C objects. A +// SingletonObjC is the same as a Singleton, except the default traits are +// appropriate for Objective-C objects. A typical Objective-C object of type +// NSExampleType can be maintained as a singleton and accessed with: +// +// NSExampleType* exampleSingleton = SingletonObjC<NSExampleType>::get(); +// +// The first time this is used, it will create exampleSingleton as the result +// of [[NSExampleType alloc] init]. Subsequent calls will return the same +// NSExampleType* object. The object will be released by calling +// -[NSExampleType release] when Singleton's atexit routines run +// (see singleton.h). +// +// For Objective-C objects initialized through means other than the +// no-parameter -init selector, DefaultSingletonObjCTraits may be extended +// as needed: +// +// struct FooSingletonTraits : public DefaultSingletonObjCTraits<Foo> { +// static Foo* New() { +// return [[Foo alloc] initWithName:@"selecty"]; +// } +// } +// ... +// Foo* widgetSingleton = SingletonObjC<Foo, FooSingletonTraits>::get(); + +#ifndef BASE_SINGLETON_OBJC_H_ +#define BASE_SINGLETON_OBJC_H_ + +#import <Foundation/Foundation.h> +#include "base/singleton.h" + +// Singleton traits usable to manage traditional Objective-C objects, which +// are instantiated by sending |alloc| and |init| messages, and are deallocated +// in a memory-managed environment when their retain counts drop to 0 by +// sending |release| messages. +template<typename Type> +struct DefaultSingletonObjCTraits : public DefaultSingletonTraits<Type> { + static Type* New() { + return [[Type alloc] init]; + } + + static void Delete(Type* object) { + [object release]; + } +}; + +// Exactly like Singleton, but without the DefaultSingletonObjCTraits as the +// default trait class. This makes it straightforward for Objective-C++ code +// to hold Objective-C objects as singletons. +template<typename Type, + typename Traits = DefaultSingletonObjCTraits<Type>, + typename DifferentiatingType = Type> +class SingletonObjC : public Singleton<Type, Traits, DifferentiatingType> { +}; + +#endif // BASE_SINGLETON_OBJC_H_ diff --git a/ipc/chromium/src/base/stack_container.h b/ipc/chromium/src/base/stack_container.h new file mode 100644 index 0000000000..23991c2f90 --- /dev/null +++ b/ipc/chromium/src/base/stack_container.h @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_STACK_CONTAINER_H_ +#define BASE_STACK_CONTAINER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" + +// This allocator can be used with STL containers to provide a stack buffer +// from which to allocate memory and overflows onto the heap. This stack buffer +// would be allocated on the stack and allows us to avoid heap operations in +// some situations. +// +// STL likes to make copies of allocators, so the allocator itself can't hold +// the data. Instead, we make the creator responsible for creating a +// StackAllocator::Source which contains the data. Copying the allocator +// merely copies the pointer to this shared source, so all allocators created +// based on our allocator will share the same stack buffer. +// +// This stack buffer implementation is very simple. The first allocation that +// fits in the stack buffer will use the stack buffer. Any subsequent +// allocations will not use the stack buffer, even if there is unused room. +// This makes it appropriate for array-like containers, but the caller should +// be sure to reserve() in the container up to the stack buffer size. Otherwise +// the container will allocate a small array which will "use up" the stack +// buffer. +template<typename T, size_t stack_capacity> +class StackAllocator : public std::allocator<T> { + public: + typedef typename std::allocator<T>::pointer pointer; + typedef typename std::allocator<T>::size_type size_type; + + // Backing store for the allocator. The container owner is responsible for + // maintaining this for as long as any containers using this allocator are + // live. + struct Source { + Source() : used_stack_buffer_(false) { + } + + // Casts the buffer in its right type. + T* stack_buffer() { return reinterpret_cast<T*>(stack_buffer_); } + const T* stack_buffer() const { + return reinterpret_cast<const T*>(stack_buffer_); + } + + // + // IMPORTANT: Take care to ensure that stack_buffer_ is aligned + // since it is used to mimic an array of T. + // Be careful while declaring any unaligned types (like bool) + // before stack_buffer_. + // + + // The buffer itself. It is not of type T because we don't want the + // constructors and destructors to be automatically called. Define a POD + // buffer of the right size instead. + char stack_buffer_[sizeof(T[stack_capacity])]; + + // Set when the stack buffer is used for an allocation. We do not track + // how much of the buffer is used, only that somebody is using it. + bool used_stack_buffer_; + }; + + // Used by containers when they want to refer to an allocator of type U. + template<typename U> + struct rebind { + typedef StackAllocator<U, stack_capacity> other; + }; + + // For the straight up copy c-tor, we can share storage. + StackAllocator(const StackAllocator<T, stack_capacity>& rhs) + : source_(rhs.source_) { + } + + // ISO C++ requires the following constructor to be defined, + // and std::vector in VC++2008SP1 Release fails with an error + // in the class _Container_base_aux_alloc_real (from <xutility>) + // if the constructor does not exist. + // For this constructor, we cannot share storage; there's + // no guarantee that the Source buffer of Ts is large enough + // for Us. + // TODO: If we were fancy pants, perhaps we could share storage + // iff sizeof(T) == sizeof(U). + template<typename U, size_t other_capacity> + explicit StackAllocator(const StackAllocator<U, other_capacity>& other) + : source_(NULL) { + } + + explicit StackAllocator(Source* source) : source_(source) { + } + + // Actually do the allocation. Use the stack buffer if nobody has used it yet + // and the size requested fits. Otherwise, fall through to the standard + // allocator. + pointer allocate(size_type n, void* hint = 0) { + if (source_ != NULL && !source_->used_stack_buffer_ + && n <= stack_capacity) { + source_->used_stack_buffer_ = true; + return source_->stack_buffer(); + } else { + return std::allocator<T>::allocate(n, hint); + } + } + + // Free: when trying to free the stack buffer, just mark it as free. For + // non-stack-buffer pointers, just fall though to the standard allocator. + void deallocate(pointer p, size_type n) { + if (source_ != NULL && p == source_->stack_buffer()) + source_->used_stack_buffer_ = false; + else + std::allocator<T>::deallocate(p, n); + } + + private: + Source* source_; +}; + +// A wrapper around STL containers that maintains a stack-sized buffer that the +// initial capacity of the vector is based on. Growing the container beyond the +// stack capacity will transparently overflow onto the heap. The container must +// support reserve(). +// +// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this +// type. This object is really intended to be used only internally. You'll want +// to use the wrappers below for different types. +template<typename TContainerType, int stack_capacity> +class StackContainer { + public: + typedef TContainerType ContainerType; + typedef typename ContainerType::value_type ContainedType; + typedef StackAllocator<ContainedType, stack_capacity> Allocator; + + // Allocator must be constructed before the container! + StackContainer() : allocator_(&stack_data_), container_(allocator_) { + // Make the container use the stack allocation by reserving our buffer size + // before doing anything else. + container_.reserve(stack_capacity); + } + + // Getters for the actual container. + // + // Danger: any copies of this made using the copy constructor must have + // shorter lifetimes than the source. The copy will share the same allocator + // and therefore the same stack buffer as the original. Use std::copy to + // copy into a "real" container for longer-lived objects. + ContainerType& container() { return container_; } + const ContainerType& container() const { return container_; } + + // Support operator-> to get to the container. This allows nicer syntax like: + // StackContainer<...> foo; + // std::sort(foo->begin(), foo->end()); + ContainerType* operator->() { return &container_; } + const ContainerType* operator->() const { return &container_; } + +#ifdef UNIT_TEST + // Retrieves the stack source so that that unit tests can verify that the + // buffer is being used properly. + const typename Allocator::Source& stack_data() const { + return stack_data_; + } +#endif + + protected: + typename Allocator::Source stack_data_; + Allocator allocator_; + ContainerType container_; + + DISALLOW_EVIL_CONSTRUCTORS(StackContainer); +}; + +// StackString +template<size_t stack_capacity> +class StackString : public StackContainer< + std::basic_string<char, + std::char_traits<char>, + StackAllocator<char, stack_capacity> >, + stack_capacity> { + public: + StackString() : StackContainer< + std::basic_string<char, + std::char_traits<char>, + StackAllocator<char, stack_capacity> >, + stack_capacity>() { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(StackString); +}; + +// StackWString +template<size_t stack_capacity> +class StackWString : public StackContainer< + std::basic_string<wchar_t, + std::char_traits<wchar_t>, + StackAllocator<wchar_t, stack_capacity> >, + stack_capacity> { + public: + StackWString() : StackContainer< + std::basic_string<wchar_t, + std::char_traits<wchar_t>, + StackAllocator<wchar_t, stack_capacity> >, + stack_capacity>() { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(StackWString); +}; + +// StackVector +// +// Example: +// StackVector<int, 16> foo; +// foo->push_back(22); // we have overloaded operator-> +// foo[0] = 10; // as well as operator[] +template<typename T, size_t stack_capacity> +class StackVector : public StackContainer< + std::vector<T, StackAllocator<T, stack_capacity> >, + stack_capacity> { + public: + StackVector() : StackContainer< + std::vector<T, StackAllocator<T, stack_capacity> >, + stack_capacity>() { + } + + // We need to put this in STL containers sometimes, which requires a copy + // constructor. We can't call the regular copy constructor because that will + // take the stack buffer from the original. Here, we create an empty object + // and make a stack buffer of its own. + StackVector(const StackVector<T, stack_capacity>& other) + : StackContainer< + std::vector<T, StackAllocator<T, stack_capacity> >, + stack_capacity>() { + this->container().assign(other->begin(), other->end()); + } + + StackVector<T, stack_capacity>& operator=( + const StackVector<T, stack_capacity>& other) { + this->container().assign(other->begin(), other->end()); + return *this; + } + + // Vectors are commonly indexed, which isn't very convenient even with + // operator-> (using "->at()" does exception stuff we don't want). + T& operator[](size_t i) { return this->container().operator[](i); } + const T& operator[](size_t i) const { + return this->container().operator[](i); + } +}; + +#endif // BASE_STACK_CONTAINER_H_ diff --git a/ipc/chromium/src/base/stl_util-inl.h b/ipc/chromium/src/base/stl_util-inl.h new file mode 100644 index 0000000000..994263277e --- /dev/null +++ b/ipc/chromium/src/base/stl_util-inl.h @@ -0,0 +1,452 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// STL utility functions. Usually, these replace built-in, but slow(!), +// STL functions with more efficient versions. + +#ifndef BASE_STL_UTIL_INL_H_ +#define BASE_STL_UTIL_INL_H_ + +#include <string.h> // for memcpy +#include <functional> +#include <set> +#include <string> +#include <vector> +#include <cassert> + +// Clear internal memory of an STL object. +// STL clear()/reserve(0) does not always free internal memory allocated +// This function uses swap/destructor to ensure the internal memory is freed. +template<class T> void STLClearObject(T* obj) { + T tmp; + tmp.swap(*obj); + obj->reserve(0); // this is because sometimes "T tmp" allocates objects with + // memory (arena implementation?). use reserve() + // to clear() even if it doesn't always work +} + +// Reduce memory usage on behalf of object if it is using more than +// "bytes" bytes of space. By default, we clear objects over 1MB. +template <class T> inline void STLClearIfBig(T* obj, size_t limit = 1<<20) { + if (obj->capacity() >= limit) { + STLClearObject(obj); + } else { + obj->clear(); + } +} + +// Reserve space for STL object. +// STL's reserve() will always copy. +// This function avoid the copy if we already have capacity +template<class T> void STLReserveIfNeeded(T* obj, int new_size) { + if (obj->capacity() < new_size) // increase capacity + obj->reserve(new_size); + else if (obj->size() > new_size) // reduce size + obj->resize(new_size); +} + +// STLDeleteContainerPointers() +// For a range within a container of pointers, calls delete +// (non-array version) on these pointers. +// NOTE: for these three functions, we could just implement a DeleteObject +// functor and then call for_each() on the range and functor, but this +// requires us to pull in all of algorithm.h, which seems expensive. +// For hash_[multi]set, it is important that this deletes behind the iterator +// because the hash_set may call the hash function on the iterator when it is +// advanced, which could result in the hash function trying to deference a +// stale pointer. +template <class ForwardIterator> +void STLDeleteContainerPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete *temp; + } +} + +// STLDeleteContainerPairPointers() +// For a range within a container of pairs, calls delete +// (non-array version) on BOTH items in the pairs. +// NOTE: Like STLDeleteContainerPointers, it is important that this deletes +// behind the iterator because if both the key and value are deleted, the +// container may call the hash function on the iterator when it is advanced, +// which could result in the hash function trying to dereference a stale +// pointer. +template <class ForwardIterator> +void STLDeleteContainerPairPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + delete temp->second; + } +} + +// STLDeleteContainerPairFirstPointers() +// For a range within a container of pairs, calls delete (non-array version) +// on the FIRST item in the pairs. +// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. +template <class ForwardIterator> +void STLDeleteContainerPairFirstPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + } +} + +// STLDeleteContainerPairSecondPointers() +// For a range within a container of pairs, calls delete +// (non-array version) on the SECOND item in the pairs. +template <class ForwardIterator> +void STLDeleteContainerPairSecondPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + delete begin->second; + ++begin; + } +} + +template<typename T> +inline void STLAssignToVector(std::vector<T>* vec, + const T* ptr, + size_t n) { + vec->resize(n); + memcpy(&vec->front(), ptr, n*sizeof(T)); +} + +/***** Hack to allow faster assignment to a vector *****/ + +// This routine speeds up an assignment of 32 bytes to a vector from +// about 250 cycles per assignment to about 140 cycles. +// +// Usage: +// STLAssignToVectorChar(&vec, ptr, size); +// STLAssignToString(&str, ptr, size); + +inline void STLAssignToVectorChar(std::vector<char>* vec, + const char* ptr, + size_t n) { + STLAssignToVector(vec, ptr, n); +} + +inline void STLAssignToString(std::string* str, const char* ptr, size_t n) { + str->resize(n); + memcpy(&*str->begin(), ptr, n); +} + +// To treat a possibly-empty vector as an array, use these functions. +// If you know the array will never be empty, you can use &*v.begin() +// directly, but that is allowed to dump core if v is empty. This +// function is the most efficient code that will work, taking into +// account how our STL is actually implemented. THIS IS NON-PORTABLE +// CODE, so call us instead of repeating the nonportable code +// everywhere. If our STL implementation changes, we will need to +// change this as well. + +template<typename T> +inline T* vector_as_array(std::vector<T>* v) { +# ifdef NDEBUG + return &*v->begin(); +# else + return v->empty() ? NULL : &*v->begin(); +# endif +} + +template<typename T> +inline const T* vector_as_array(const std::vector<T>* v) { +# ifdef NDEBUG + return &*v->begin(); +# else + return v->empty() ? NULL : &*v->begin(); +# endif +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) +// proposes this as the method. According to Matt Austern, this should +// already work on all current implementations. +inline char* string_as_array(std::string* str) { + // DO NOT USE const_cast<char*>(str->data())! See the unittest for why. + return str->empty() ? NULL : &*str->begin(); +} + +// These are methods that test two hash maps/sets for equality. These exist +// because the == operator in the STL can return false when the maps/sets +// contain identical elements. This is because it compares the internal hash +// tables which may be different if the order of insertions and deletions +// differed. + +template <class HashSet> +inline bool +HashSetEquality(const HashSet& set_a, + const HashSet& set_b) { + if (set_a.size() != set_b.size()) return false; + for (typename HashSet::const_iterator i = set_a.begin(); + i != set_a.end(); + ++i) { + if (set_b.find(*i) == set_b.end()) + return false; + } + return true; +} + +template <class HashMap> +inline bool +HashMapEquality(const HashMap& map_a, + const HashMap& map_b) { + if (map_a.size() != map_b.size()) return false; + for (typename HashMap::const_iterator i = map_a.begin(); + i != map_a.end(); ++i) { + typename HashMap::const_iterator j = map_b.find(i->first); + if (j == map_b.end()) return false; + if (i->second != j->second) return false; + } + return true; +} + +// The following functions are useful for cleaning up STL containers +// whose elements point to allocated memory. + +// STLDeleteElements() deletes all the elements in an STL container and clears +// the container. This function is suitable for use with a vector, set, +// hash_set, or any other STL container which defines sensible begin(), end(), +// and clear() methods. +// +// If container is NULL, this function is a no-op. +// +// As an alternative to calling STLDeleteElements() directly, consider +// STLElementDeleter (defined below), which ensures that your container's +// elements are deleted when the STLElementDeleter goes out of scope. +template <class T> +void STLDeleteElements(T *container) { + if (!container) return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +// Given an STL container consisting of (key, value) pairs, STLDeleteValues +// deletes all the "value" components and clears the container. Does nothing +// in the case it's given a NULL pointer. + +template <class T> +void STLDeleteValues(T *v) { + if (!v) return; + for (typename T::iterator i = v->begin(); i != v->end(); ++i) { + delete i->second; + } + v->clear(); +} + + +// The following classes provide a convenient way to delete all elements or +// values from STL containers when they goes out of scope. This greatly +// simplifies code that creates temporary objects and has multiple return +// statements. Example: +// +// vector<MyProto *> tmp_proto; +// STLElementDeleter<vector<MyProto *> > d(&tmp_proto); +// if (...) return false; +// ... +// return success; + +// Given a pointer to an STL container this class will delete all the element +// pointers when it goes out of scope. + +template<class STLContainer> class STLElementDeleter { + public: + explicit STLElementDeleter(STLContainer *ptr) : container_ptr_(ptr) {} + ~STLElementDeleter() { STLDeleteElements(container_ptr_); } + private: + STLContainer *container_ptr_; +}; + +// Given a pointer to an STL container this class will delete all the value +// pointers when it goes out of scope. + +template<class STLContainer> class STLValueDeleter { + public: + explicit STLValueDeleter(STLContainer *ptr) : container_ptr_(ptr) {} + ~STLValueDeleter() { STLDeleteValues(container_ptr_); } + private: + STLContainer *container_ptr_; +}; + + +// Forward declare some callback classes in callback.h for STLBinaryFunction +template <class R, class T1, class T2> +class ResultCallback2; + +// STLBinaryFunction is a wrapper for the ResultCallback2 class in callback.h +// It provides an operator () method instead of a Run method, so it may be +// passed to STL functions in <algorithm>. +// +// The client should create callback with NewPermanentCallback, and should +// delete callback after it is done using the STLBinaryFunction. + +template <class Result, class Arg1, class Arg2> +class STLBinaryFunction : public std::binary_function<Arg1, Arg2, Result> { + public: + typedef ResultCallback2<Result, Arg1, Arg2> Callback; + + explicit STLBinaryFunction(Callback* callback) + : callback_(callback) { + assert(callback_); + } + + Result operator() (Arg1 arg1, Arg2 arg2) { + return callback_->Run(arg1, arg2); + } + + private: + Callback* callback_; +}; + +// STLBinaryPredicate is a specialized version of STLBinaryFunction, where the +// return type is bool and both arguments have type Arg. It can be used +// wherever STL requires a StrictWeakOrdering, such as in sort() or +// lower_bound(). +// +// templated typedefs are not supported, so instead we use inheritance. + +template <class Arg> +class STLBinaryPredicate : public STLBinaryFunction<bool, Arg, Arg> { + public: + typedef typename STLBinaryPredicate<Arg>::Callback Callback; + explicit STLBinaryPredicate(Callback* callback) + : STLBinaryFunction<bool, Arg, Arg>(callback) { + } +}; + +// Functors that compose arbitrary unary and binary functions with a +// function that "projects" one of the members of a pair. +// Specifically, if p1 and p2, respectively, are the functions that +// map a pair to its first and second, respectively, members, the +// table below summarizes the functions that can be constructed: +// +// * UnaryOperate1st<pair>(f) returns the function x -> f(p1(x)) +// * UnaryOperate2nd<pair>(f) returns the function x -> f(p2(x)) +// * BinaryOperate1st<pair>(f) returns the function (x,y) -> f(p1(x),p1(y)) +// * BinaryOperate2nd<pair>(f) returns the function (x,y) -> f(p2(x),p2(y)) +// +// A typical usage for these functions would be when iterating over +// the contents of an STL map. For other sample usage, see the unittest. + +template<typename Pair, typename UnaryOp> +class UnaryOperateOnFirst + : public std::unary_function<Pair, typename UnaryOp::result_type> { + public: + UnaryOperateOnFirst() { + } + + explicit UnaryOperateOnFirst(const UnaryOp& f) : f_(f) { + } + + typename UnaryOp::result_type operator()(const Pair& p) const { + return f_(p.first); + } + + private: + UnaryOp f_; +}; + +template<typename Pair, typename UnaryOp> +UnaryOperateOnFirst<Pair, UnaryOp> UnaryOperate1st(const UnaryOp& f) { + return UnaryOperateOnFirst<Pair, UnaryOp>(f); +} + +template<typename Pair, typename UnaryOp> +class UnaryOperateOnSecond + : public std::unary_function<Pair, typename UnaryOp::result_type> { + public: + UnaryOperateOnSecond() { + } + + explicit UnaryOperateOnSecond(const UnaryOp& f) : f_(f) { + } + + typename UnaryOp::result_type operator()(const Pair& p) const { + return f_(p.second); + } + + private: + UnaryOp f_; +}; + +template<typename Pair, typename UnaryOp> +UnaryOperateOnSecond<Pair, UnaryOp> UnaryOperate2nd(const UnaryOp& f) { + return UnaryOperateOnSecond<Pair, UnaryOp>(f); +} + +template<typename Pair, typename BinaryOp> +class BinaryOperateOnFirst + : public std::binary_function<Pair, Pair, typename BinaryOp::result_type> { + public: + BinaryOperateOnFirst() { + } + + explicit BinaryOperateOnFirst(const BinaryOp& f) : f_(f) { + } + + typename BinaryOp::result_type operator()(const Pair& p1, + const Pair& p2) const { + return f_(p1.first, p2.first); + } + + private: + BinaryOp f_; +}; + +template<typename Pair, typename BinaryOp> +BinaryOperateOnFirst<Pair, BinaryOp> BinaryOperate1st(const BinaryOp& f) { + return BinaryOperateOnFirst<Pair, BinaryOp>(f); +} + +template<typename Pair, typename BinaryOp> +class BinaryOperateOnSecond + : public std::binary_function<Pair, Pair, typename BinaryOp::result_type> { + public: + BinaryOperateOnSecond() { + } + + explicit BinaryOperateOnSecond(const BinaryOp& f) : f_(f) { + } + + typename BinaryOp::result_type operator()(const Pair& p1, + const Pair& p2) const { + return f_(p1.second, p2.second); + } + + private: + BinaryOp f_; +}; + +template<typename Pair, typename BinaryOp> +BinaryOperateOnSecond<Pair, BinaryOp> BinaryOperate2nd(const BinaryOp& f) { + return BinaryOperateOnSecond<Pair, BinaryOp>(f); +} + +// Translates a set into a vector. +template<typename T> +std::vector<T> SetToVector(const std::set<T>& values) { + std::vector<T> result; + result.reserve(values.size()); + result.insert(result.begin(), values.begin(), values.end()); + return result; +} + +#endif // BASE_STL_UTIL_INL_H_ diff --git a/ipc/chromium/src/base/string16.cc b/ipc/chromium/src/base/string16.cc new file mode 100644 index 0000000000..3d4efb0f01 --- /dev/null +++ b/ipc/chromium/src/base/string16.cc @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/string16.h" + +#if defined(WCHAR_T_IS_UTF16) + +#error This file should not be used on 2-byte wchar_t systems +// If this winds up being needed on 2-byte wchar_t systems, either the +// definitions below can be used, or the host system's wide character +// functions like wmemcmp can be wrapped. + +#elif defined(WCHAR_T_IS_UTF32) + +#include "base/string_util.h" + +namespace base { + +int c16memcmp(const char16* s1, const char16* s2, size_t n) { + // We cannot call memcmp because that changes the semantics. + while (n-- > 0) { + if (*s1 != *s2) { + // We cannot use (*s1 - *s2) because char16 is unsigned. + return ((*s1 < *s2) ? -1 : 1); + } + ++s1; + ++s2; + } + return 0; +} + +size_t c16len(const char16* s) { + const char16 *s_orig = s; + while (*s) { + ++s; + } + return s - s_orig; +} + +const char16* c16memchr(const char16* s, char16 c, size_t n) { + while (n-- > 0) { + if (*s == c) { + return s; + } + ++s; + } + return 0; +} + +char16* c16memmove(char16* s1, const char16* s2, size_t n) { + return reinterpret_cast<char16*>(memmove(s1, s2, n * sizeof(char16))); +} + +char16* c16memcpy(char16* s1, const char16* s2, size_t n) { + return reinterpret_cast<char16*>(memcpy(s1, s2, n * sizeof(char16))); +} + +char16* c16memset(char16* s, char16 c, size_t n) { + char16 *s_orig = s; + while (n-- > 0) { + *s = c; + ++s; + } + return s_orig; +} + +} // namespace base + +template class std::basic_string<char16, base::string16_char_traits>; + +#endif // WCHAR_T_IS_UTF32 diff --git a/ipc/chromium/src/base/string16.h b/ipc/chromium/src/base/string16.h new file mode 100644 index 0000000000..f383114808 --- /dev/null +++ b/ipc/chromium/src/base/string16.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_STRING16_H_ +#define BASE_STRING16_H_ + +// WHAT: +// A version of std::basic_string that provides 2-byte characters even when +// wchar_t is not implemented as a 2-byte type. You can access this class as +// string16. We also define char16, which string16 is based upon. +// +// WHY: +// On Windows, wchar_t is 2 bytes, and it can conveniently handle UTF-16/UCS-2 +// data. Plenty of existing code operates on strings encoded as UTF-16. +// +// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make +// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails +// at run time, because it calls some functions (like wcslen) that come from +// the system's native C library -- which was built with a 4-byte wchar_t! +// It's wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it's +// entirely improper on those systems where the encoding of wchar_t is defined +// as UTF-32. +// +// Here, we define string16, which is similar to std::wstring but replaces all +// libc functions with custom, 2-byte-char compatible routines. It is capable +// of carrying UTF-16-encoded data. + +#include <stdio.h> +#include <string> + +#include "base/basictypes.h" + +#if defined(WCHAR_T_IS_UTF16) + +typedef wchar_t char16; +typedef std::wstring string16; + +#elif defined(WCHAR_T_IS_UTF32) + +typedef uint16_t char16; + +namespace base { + +// char16 versions of the functions required by string16_char_traits; these +// are based on the wide character functions of similar names ("w" or "wcs" +// instead of "c16"). +int c16memcmp(const char16* s1, const char16* s2, size_t n); +size_t c16len(const char16* s); +const char16* c16memchr(const char16* s, char16 c, size_t n); +char16* c16memmove(char16* s1, const char16* s2, size_t n); +char16* c16memcpy(char16* s1, const char16* s2, size_t n); +char16* c16memset(char16* s, char16 c, size_t n); + +struct string16_char_traits { + typedef char16 char_type; + typedef int int_type; + + // int_type needs to be able to hold each possible value of char_type, and in + // addition, the distinct value of eof(). + COMPILE_ASSERT(sizeof(int_type) > sizeof(char_type), unexpected_type_width); + + typedef std::streamoff off_type; + typedef mbstate_t state_type; + typedef std::fpos<state_type> pos_type; + + static void assign(char_type& c1, const char_type& c2) { + c1 = c2; + } + + static bool eq(const char_type& c1, const char_type& c2) { + return c1 == c2; + } + static bool lt(const char_type& c1, const char_type& c2) { + return c1 < c2; + } + + static int compare(const char_type* s1, const char_type* s2, size_t n) { + return c16memcmp(s1, s2, n); + } + + static size_t length(const char_type* s) { + return c16len(s); + } + + static const char_type* find(const char_type* s, size_t n, + const char_type& a) { + return c16memchr(s, a, n); + } + + static char_type* move(char_type* s1, const char_type* s2, int_type n) { + return c16memmove(s1, s2, n); + } + + static char_type* copy(char_type* s1, const char_type* s2, size_t n) { + return c16memcpy(s1, s2, n); + } + + static char_type* assign(char_type* s, size_t n, char_type a) { + return c16memset(s, a, n); + } + + static int_type not_eof(const int_type& c) { + return eq_int_type(c, eof()) ? 0 : c; + } + + static char_type to_char_type(const int_type& c) { + return char_type(c); + } + + static int_type to_int_type(const char_type& c) { + return int_type(c); + } + + static bool eq_int_type(const int_type& c1, const int_type& c2) { + return c1 == c2; + } + + static int_type eof() { + return static_cast<int_type>(EOF); + } +}; + +} // namespace base + +// The string class will be explicitly instantiated only once, in string16.cc. +// +// std::basic_string<> in GNU libstdc++ contains a static data member, +// _S_empty_rep_storage, to represent empty strings. When an operation such +// as assignment or destruction is performed on a string, causing its existing +// data member to be invalidated, it must not be freed if this static data +// member is being used. Otherwise, it counts as an attempt to free static +// (and not allocated) data, which is a memory error. +// +// Generally, due to C++ template magic, _S_empty_rep_storage will be marked +// as a coalesced symbol, meaning that the linker will combine multiple +// instances into a single one when generating output. +// +// If a string class is used by multiple shared libraries, a problem occurs. +// Each library will get its own copy of _S_empty_rep_storage. When strings +// are passed across a library boundary for alteration or destruction, memory +// errors will result. GNU libstdc++ contains a configuration option, +// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which +// disables the static data member optimization, but it's a good optimization +// and non-STL code is generally at the mercy of the system's STL +// configuration. Fully-dynamic strings are not the default for GNU libstdc++ +// libstdc++ itself or for the libstdc++ installations on the systems we care +// about, such as Mac OS X and relevant flavors of Linux. +// +// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 . +// +// To avoid problems, string classes need to be explicitly instantiated only +// once, in exactly one library. All other string users see it via an "extern" +// declaration. This is precisely how GNU libstdc++ handles +// std::basic_string<char> (string) and std::basic_string<wchar_t> (wstring). +// +// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2), +// in which the linker does not fully coalesce symbols when dead code +// stripping is enabled. This bug causes the memory errors described above +// to occur even when a std::basic_string<> does not cross shared library +// boundaries, such as in statically-linked executables. +// +// TODO(mark): File this bug with Apple and update this note with a bug number. + +extern template class std::basic_string<char16, base::string16_char_traits>; + +typedef std::basic_string<char16, base::string16_char_traits> string16; + +extern std::ostream& operator<<(std::ostream& out, const string16& str); + +#endif // WCHAR_T_IS_UTF32 + +#endif // BASE_STRING16_H_ diff --git a/ipc/chromium/src/base/string_piece.cc b/ipc/chromium/src/base/string_piece.cc new file mode 100644 index 0000000000..169d6a49f0 --- /dev/null +++ b/ipc/chromium/src/base/string_piece.cc @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. +// Copied from strings/stringpiece.cc with modifications + +#include <algorithm> +#include <ostream> + +#include "base/string_piece.h" + +typedef StringPiece::size_type size_type; + +std::ostream& operator<<(std::ostream& o, const StringPiece& piece) { + o.write(piece.data(), static_cast<std::streamsize>(piece.size())); + return o; +} + +bool operator==(const StringPiece& x, const StringPiece& y) { + if (x.size() != y.size()) + return false; + + return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; +} + +void StringPiece::CopyToString(std::string* target) const { + target->assign(!empty() ? data() : "", size()); +} + +void StringPiece::AppendToString(std::string* target) const { + if (!empty()) + target->append(data(), size()); +} + +size_type StringPiece::copy(char* buf, size_type n, size_type pos) const { + size_type ret = std::min(length_ - pos, n); + memcpy(buf, ptr_ + pos, ret); + return ret; +} + +size_type StringPiece::find(const StringPiece& s, size_type pos) const { + if (pos > length_) + return npos; + + const char* result = std::search(ptr_ + pos, ptr_ + length_, + s.ptr_, s.ptr_ + s.length_); + const size_type xpos = result - ptr_; + return xpos + s.length_ <= length_ ? xpos : npos; +} + +size_type StringPiece::find(char c, size_type pos) const { + if (pos >= length_) + return npos; + + const char* result = std::find(ptr_ + pos, ptr_ + length_, c); + return result != ptr_ + length_ ? result - ptr_ : npos; +} + +size_type StringPiece::rfind(const StringPiece& s, size_type pos) const { + if (length_ < s.length_) + return npos; + + if (s.empty()) + return std::min(length_, pos); + + const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_; + const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_); + return result != last ? result - ptr_ : npos; +} + +size_type StringPiece::rfind(char c, size_type pos) const { + if (length_ == 0) + return npos; + + for (size_type i = std::min(pos, length_ - 1); ; --i) { + if (ptr_[i] == c) + return i; + if (i == 0) + break; + } + return npos; +} + +// For each character in characters_wanted, sets the index corresponding +// to the ASCII code of that character to 1 in table. This is used by +// the find_.*_of methods below to tell whether or not a character is in +// the lookup table in constant time. +// The argument `table' must be an array that is large enough to hold all +// the possible values of an unsigned char. Thus it should be be declared +// as follows: +// bool table[UCHAR_MAX + 1] +static inline void BuildLookupTable(const StringPiece& characters_wanted, + bool* table) { + const size_type length = characters_wanted.length(); + const char* const data = characters_wanted.data(); + for (size_type i = 0; i < length; ++i) { + table[static_cast<unsigned char>(data[i])] = true; + } +} + +size_type StringPiece::find_first_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0 || s.length_ == 0) + return npos; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) + return find_first_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = { false }; + BuildLookupTable(s, lookup); + for (size_type i = pos; i < length_; ++i) { + if (lookup[static_cast<unsigned char>(ptr_[i])]) { + return i; + } + } + return npos; +} + +size_type StringPiece::find_first_not_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0) + return npos; + + if (s.length_ == 0) + return 0; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) + return find_first_not_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = { false }; + BuildLookupTable(s, lookup); + for (size_type i = pos; i < length_; ++i) { + if (!lookup[static_cast<unsigned char>(ptr_[i])]) { + return i; + } + } + return npos; +} + +size_type StringPiece::find_first_not_of(char c, size_type pos) const { + if (length_ == 0) + return npos; + + for (; pos < length_; ++pos) { + if (ptr_[pos] != c) { + return pos; + } + } + return npos; +} + +size_type StringPiece::find_last_of(const StringPiece& s, size_type pos) const { + if (length_ == 0 || s.length_ == 0) + return npos; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) + return find_last_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = { false }; + BuildLookupTable(s, lookup); + for (size_type i = std::min(pos, length_ - 1); ; --i) { + if (lookup[static_cast<unsigned char>(ptr_[i])]) + return i; + if (i == 0) + break; + } + return npos; +} + +size_type StringPiece::find_last_not_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0) + return npos; + + size_type i = std::min(pos, length_ - 1); + if (s.length_ == 0) + return i; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) + return find_last_not_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = { false }; + BuildLookupTable(s, lookup); + for (; ; --i) { + if (!lookup[static_cast<unsigned char>(ptr_[i])]) + return i; + if (i == 0) + break; + } + return npos; +} + +size_type StringPiece::find_last_not_of(char c, size_type pos) const { + if (length_ == 0) + return npos; + + for (size_type i = std::min(pos, length_ - 1); ; --i) { + if (ptr_[i] != c) + return i; + if (i == 0) + break; + } + return npos; +} + +StringPiece StringPiece::substr(size_type pos, size_type n) const { + if (pos > length_) pos = length_; + if (n > length_ - pos) n = length_ - pos; + return StringPiece(ptr_ + pos, n); +} + +const StringPiece::size_type StringPiece::npos = size_type(-1); diff --git a/ipc/chromium/src/base/string_piece.h b/ipc/chromium/src/base/string_piece.h new file mode 100644 index 0000000000..36c39009cb --- /dev/null +++ b/ipc/chromium/src/base/string_piece.h @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. +// Copied from strings/stringpiece.h with modifications +// +// A string-like object that points to a sized piece of memory. +// +// Functions or methods may use const StringPiece& parameters to accept either +// a "const char*" or a "string" value that will be implicitly converted to +// a StringPiece. The implicit conversion means that it is often appropriate +// to include this .h file in other files rather than forward-declaring +// StringPiece as would be appropriate for most other Google classes. +// +// Systematic usage of StringPiece is encouraged as it will reduce unnecessary +// conversions from "const char*" to "string" and back again. +// + +#ifndef BASE_STRING_PIECE_H_ +#define BASE_STRING_PIECE_H_ + +#include <algorithm> +#include <iosfwd> +#include <string> + +#include "base/basictypes.h" + +class StringPiece { + public: + typedef size_t size_type; + + private: + const char* ptr_; + size_type length_; + + public: + // We provide non-explicit singleton constructors so users can pass + // in a "const char*" or a "string" wherever a "StringPiece" is + // expected. + StringPiece() : ptr_(NULL), length_(0) { } + MOZ_IMPLICIT StringPiece(const char* str) + : ptr_(str), length_((str == NULL) ? 0 : strlen(str)) { } + MOZ_IMPLICIT StringPiece(const std::string& str) + : ptr_(str.data()), length_(str.size()) { } + StringPiece(const char* offset, size_type len) + : ptr_(offset), length_(len) { } + + // data() may return a pointer to a buffer with embedded NULs, and the + // returned buffer may or may not be null terminated. Therefore it is + // typically a mistake to pass data() to a routine that expects a NUL + // terminated string. + const char* data() const { return ptr_; } + size_type size() const { return length_; } + size_type length() const { return length_; } + bool empty() const { return length_ == 0; } + + void clear() { ptr_ = NULL; length_ = 0; } + void set(const char* aData, size_type aLen) { ptr_ = aData; length_ = aLen; } + void set(const char* str) { + ptr_ = str; + length_ = str ? strlen(str) : 0; + } + void set(const void* aData, size_type aLen) { + ptr_ = reinterpret_cast<const char*>(aData); + length_ = aLen; + } + + char operator[](size_type i) const { return ptr_[i]; } + + void remove_prefix(size_type n) { + ptr_ += n; + length_ -= n; + } + + void remove_suffix(size_type n) { + length_ -= n; + } + + int compare(const StringPiece& x) const { + int r = wordmemcmp(ptr_, x.ptr_, std::min(length_, x.length_)); + if (r == 0) { + if (length_ < x.length_) r = -1; + else if (length_ > x.length_) r = +1; + } + return r; + } + + std::string as_string() const { + // std::string doesn't like to take a NULL pointer even with a 0 size. + return std::string(!empty() ? data() : "", size()); + } + + void CopyToString(std::string* target) const; + void AppendToString(std::string* target) const; + + // Does "this" start with "x" + bool starts_with(const StringPiece& x) const { + return ((length_ >= x.length_) && + (wordmemcmp(ptr_, x.ptr_, x.length_) == 0)); + } + + // Does "this" end with "x" + bool ends_with(const StringPiece& x) const { + return ((length_ >= x.length_) && + (wordmemcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0)); + } + + // standard STL container boilerplate + typedef char value_type; + typedef const char* pointer; + typedef const char& reference; + typedef const char& const_reference; + typedef ptrdiff_t difference_type; + static const size_type npos; + typedef const char* const_iterator; + typedef const char* iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + typedef std::reverse_iterator<iterator> reverse_iterator; + iterator begin() const { return ptr_; } + iterator end() const { return ptr_ + length_; } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(ptr_ + length_); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(ptr_); + } + + size_type max_size() const { return length_; } + size_type capacity() const { return length_; } + + size_type copy(char* buf, size_type n, size_type pos = 0) const; + + size_type find(const StringPiece& s, size_type pos = 0) const; + size_type find(char c, size_type pos = 0) const; + size_type rfind(const StringPiece& s, size_type pos = npos) const; + size_type rfind(char c, size_type pos = npos) const; + + size_type find_first_of(const StringPiece& s, size_type pos = 0) const; + size_type find_first_of(char c, size_type pos = 0) const { + return find(c, pos); + } + size_type find_first_not_of(const StringPiece& s, size_type pos = 0) const; + size_type find_first_not_of(char c, size_type pos = 0) const; + size_type find_last_of(const StringPiece& s, size_type pos = npos) const; + size_type find_last_of(char c, size_type pos = npos) const { + return rfind(c, pos); + } + size_type find_last_not_of(const StringPiece& s, size_type pos = npos) const; + size_type find_last_not_of(char c, size_type pos = npos) const; + + StringPiece substr(size_type pos, size_type n = npos) const; + + static int wordmemcmp(const char* p, const char* p2, size_type N) { + return memcmp(p, p2, N); + } +}; + +bool operator==(const ::StringPiece& x, const ::StringPiece& y); + +inline bool operator!=(const ::StringPiece& x, const ::StringPiece& y) { + return !(x == y); +} + +inline bool operator<(const ::StringPiece& x, const ::StringPiece& y) { + const int r = ::StringPiece::wordmemcmp(x.data(), y.data(), + std::min(x.size(), y.size())); + return ((r < 0) || ((r == 0) && (x.size() < y.size()))); +} + +inline bool operator>(const ::StringPiece& x, const ::StringPiece& y) { + return y < x; +} + +inline bool operator<=(const ::StringPiece& x, const ::StringPiece& y) { + return !(x > y); +} + +inline bool operator>=(const ::StringPiece& x, const ::StringPiece& y) { + return !(x < y); +} + +// allow StringPiece to be logged (needed for unit testing). +extern std::ostream& operator<<(std::ostream& o, const ::StringPiece& piece); + +#endif // BASE_STRING_PIECE_H_ diff --git a/ipc/chromium/src/base/string_util.cc b/ipc/chromium/src/base/string_util.cc new file mode 100644 index 0000000000..339b12271b --- /dev/null +++ b/ipc/chromium/src/base/string_util.cc @@ -0,0 +1,780 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/string_util.h" + +#include "build/build_config.h" + +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <wchar.h> +#include <wctype.h> + +#include <algorithm> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/singleton.h" + +namespace { + +// Force the singleton used by Empty[W]String[16] to be a unique type. This +// prevents other code that might accidentally use Singleton<string> from +// getting our internal one. +struct EmptyStrings { + EmptyStrings() {} + const std::string s; + const std::wstring ws; + const string16 s16; +}; + +// Hack to convert any char-like type to its unsigned counterpart. +// For example, it will convert char, signed char and unsigned char to unsigned +// char. +template<typename T> +struct ToUnsigned { + typedef T Unsigned; +}; + +template<> +struct ToUnsigned<char> { + typedef unsigned char Unsigned; +}; +template<> +struct ToUnsigned<signed char> { + typedef unsigned char Unsigned; +}; +template<> +struct ToUnsigned<wchar_t> { +#if defined(WCHAR_T_IS_UTF16) + typedef unsigned short Unsigned; +#elif defined(WCHAR_T_IS_UTF32) + typedef uint32_t Unsigned; +#endif +}; +template<> +struct ToUnsigned<short> { + typedef unsigned short Unsigned; +}; + +// Generalized string-to-number conversion. +// +// StringToNumberTraits should provide: +// - a typedef for string_type, the STL string type used as input. +// - a typedef for value_type, the target numeric type. +// - a static function, convert_func, which dispatches to an appropriate +// strtol-like function and returns type value_type. +// - a static function, valid_func, which validates |input| and returns a bool +// indicating whether it is in proper form. This is used to check for +// conditions that convert_func tolerates but should result in +// StringToNumber returning false. For strtol-like funtions, valid_func +// should check for leading whitespace. +template<typename StringToNumberTraits> +bool StringToNumber(const typename StringToNumberTraits::string_type& input, + typename StringToNumberTraits::value_type* output) { + typedef StringToNumberTraits traits; + + errno = 0; // Thread-safe? It is on at least Mac, Linux, and Windows. + typename traits::string_type::value_type* endptr = NULL; + typename traits::value_type value = traits::convert_func(input.c_str(), + &endptr); + *output = value; + + // Cases to return false: + // - If errno is ERANGE, there was an overflow or underflow. + // - If the input string is empty, there was nothing to parse. + // - If endptr does not point to the end of the string, there are either + // characters remaining in the string after a parsed number, or the string + // does not begin with a parseable number. endptr is compared to the + // expected end given the string's stated length to correctly catch cases + // where the string contains embedded NUL characters. + // - valid_func determines that the input is not in preferred form. + return errno == 0 && + !input.empty() && + input.c_str() + input.length() == endptr && + traits::valid_func(input); +} + +class StringToLongTraits { + public: + typedef std::string string_type; + typedef long value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { + return strtol(str, endptr, kBase); + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class String16ToLongTraits { + public: + typedef string16 string_type; + typedef long value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#if defined(WCHAR_T_IS_UTF16) + return wcstol(str, endptr, kBase); +#elif defined(WCHAR_T_IS_UTF32) + std::string ascii_string = UTF16ToASCII(string16(str)); + char* ascii_end = NULL; + value_type ret = strtol(ascii_string.c_str(), &ascii_end, kBase); + if (ascii_string.c_str() + ascii_string.length() == ascii_end) { + *endptr = + const_cast<string_type::value_type*>(str) + ascii_string.length(); + } + return ret; +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); + } +}; + +class StringToInt64Traits { + public: + typedef std::string string_type; + typedef int64_t value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#ifdef OS_WIN + return _strtoi64(str, endptr, kBase); +#else // assume OS_POSIX + return strtoll(str, endptr, kBase); +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class String16ToInt64Traits { + public: + typedef string16 string_type; + typedef int64_t value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#ifdef OS_WIN + return _wcstoi64(str, endptr, kBase); +#else // assume OS_POSIX + std::string ascii_string = UTF16ToASCII(string16(str)); + char* ascii_end = NULL; + value_type ret = strtoll(ascii_string.c_str(), &ascii_end, kBase); + if (ascii_string.c_str() + ascii_string.length() == ascii_end) { + *endptr = + const_cast<string_type::value_type*>(str) + ascii_string.length(); + } + return ret; +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); + } +}; + +} // namespace + + +namespace base { + +bool IsWprintfFormatPortable(const wchar_t* format) { + for (const wchar_t* position = format; *position != '\0'; ++position) { + + if (*position == '%') { + bool in_specification = true; + bool modifier_l = false; + while (in_specification) { + // Eat up characters until reaching a known specifier. + if (*++position == '\0') { + // The format string ended in the middle of a specification. Call + // it portable because no unportable specifications were found. The + // string is equally broken on all platforms. + return true; + } + + if (*position == 'l') { + // 'l' is the only thing that can save the 's' and 'c' specifiers. + modifier_l = true; + } else if (((*position == 's' || *position == 'c') && !modifier_l) || + *position == 'S' || *position == 'C' || *position == 'F' || + *position == 'D' || *position == 'O' || *position == 'U') { + // Not portable. + return false; + } + + if (wcschr(L"diouxXeEfgGaAcspn%", *position)) { + // Portable, keep scanning the rest of the format string. + in_specification = false; + } + } + } + + } + + return true; +} + + +} // namespace base + +static const wchar_t kWhitespaceWide[] = { + 0x0009, // <control-0009> to <control-000D> + 0x000A, + 0x000B, + 0x000C, + 0x000D, + 0x0020, // Space + 0x0085, // <control-0085> + 0x00A0, // No-Break Space + 0x1680, // Ogham Space Mark + 0x180E, // Mongolian Vowel Separator + 0x2000, // En Quad to Hair Space + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200A, + 0x200C, // Zero Width Non-Joiner + 0x2028, // Line Separator + 0x2029, // Paragraph Separator + 0x202F, // Narrow No-Break Space + 0x205F, // Medium Mathematical Space + 0x3000, // Ideographic Space + 0 +}; +static const char kWhitespaceASCII[] = { + 0x09, // <control-0009> to <control-000D> + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x20, // Space + 0 +}; + +template<typename STR> +TrimPositions TrimStringT(const STR& input, + const typename STR::value_type trim_chars[], + TrimPositions positions, + STR* output) { + // Find the edges of leading/trailing whitespace as desired. + const typename STR::size_type last_char = input.length() - 1; + const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ? + input.find_first_not_of(trim_chars) : 0; + const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ? + input.find_last_not_of(trim_chars) : last_char; + + // When the string was all whitespace, report that we stripped off whitespace + // from whichever position the caller was interested in. For empty input, we + // stripped no whitespace, but we still need to clear |output|. + if (input.empty() || + (first_good_char == STR::npos) || (last_good_char == STR::npos)) { + bool input_was_empty = input.empty(); // in case output == &input + output->clear(); + return input_was_empty ? TRIM_NONE : positions; + } + + // Trim the whitespace. + *output = + input.substr(first_good_char, last_good_char - first_good_char + 1); + + // Return where we trimmed from. + return static_cast<TrimPositions>( + ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | + ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); +} + +TrimPositions TrimWhitespace(const std::wstring& input, + TrimPositions positions, + std::wstring* output) { + return TrimStringT(input, kWhitespaceWide, positions, output); +} + +TrimPositions TrimWhitespaceASCII(const std::string& input, + TrimPositions positions, + std::string* output) { + return TrimStringT(input, kWhitespaceASCII, positions, output); +} + +// This function is only for backward-compatibility. +// To be removed when all callers are updated. +TrimPositions TrimWhitespace(const std::string& input, + TrimPositions positions, + std::string* output) { + return TrimWhitespaceASCII(input, positions, output); +} + +std::string WideToASCII(const std::wstring& wide) { + DCHECK(IsStringASCII(wide)); + return std::string(wide.begin(), wide.end()); +} + +std::wstring ASCIIToWide(const std::string& ascii) { + DCHECK(IsStringASCII(ascii)); + return std::wstring(ascii.begin(), ascii.end()); +} + +std::string UTF16ToASCII(const string16& utf16) { + DCHECK(IsStringASCII(utf16)); + return std::string(utf16.begin(), utf16.end()); +} + +string16 ASCIIToUTF16(const std::string& ascii) { + DCHECK(IsStringASCII(ascii)); + return string16(ascii.begin(), ascii.end()); +} + +template<class STR> +static bool DoIsStringASCII(const STR& str) { + for (size_t i = 0; i < str.length(); i++) { + typename ToUnsigned<typename STR::value_type>::Unsigned c = str[i]; + if (c > 0x7F) + return false; + } + return true; +} + +bool IsStringASCII(const std::wstring& str) { + return DoIsStringASCII(str); +} + +#if !defined(WCHAR_T_IS_UTF16) +bool IsStringASCII(const string16& str) { + return DoIsStringASCII(str); +} +#endif + +bool IsStringASCII(const std::string& str) { + return DoIsStringASCII(str); +} + +// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter +// is the size of the buffer. These return the number of characters in the +// formatted string excluding the NUL terminator. If the buffer is not +// large enough to accommodate the formatted string without truncation, they +// return the number of characters that would be in the fully-formatted string +// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms). +inline int vsnprintfT(char* buffer, + size_t buf_size, + const char* format, + va_list argptr) { + return base::vsnprintf(buffer, buf_size, format, argptr); +} + +inline int vsnprintfT(wchar_t* buffer, + size_t buf_size, + const wchar_t* format, + va_list argptr) { + return base::vswprintf(buffer, buf_size, format, argptr); +} + +// Templatized backend for StringPrintF/StringAppendF. This does not finalize +// the va_list, the caller is expected to do that. +template <class StringType> +static void StringAppendVT(StringType* dst, + const typename StringType::value_type* format, + va_list ap) { + // First try with a small fixed size buffer. + // This buffer size should be kept in sync with StringUtilTest.GrowBoundary + // and StringUtilTest.StringPrintfBounds. + typename StringType::value_type stack_buf[1024]; + + va_list backup_ap; + base_va_copy(backup_ap, ap); + +#if !defined(OS_WIN) + errno = 0; +#endif + int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, backup_ap); + va_end(backup_ap); + + if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) { + // It fit. + dst->append(stack_buf, result); + return; + } + + // Repeatedly increase buffer size until it fits. + int mem_length = arraysize(stack_buf); + while (true) { + if (result < 0) { +#if !defined(OS_WIN) + // On Windows, vsnprintfT always returns the number of characters in a + // fully-formatted string, so if we reach this point, something else is + // wrong and no amount of buffer-doubling is going to fix it. + if (errno != 0 && errno != EOVERFLOW) +#endif + { + // If an error other than overflow occurred, it's never going to work. + DLOG(WARNING) << "Unable to printf the requested string due to error."; + return; + } + // Try doubling the buffer size. + mem_length *= 2; + } else { + // We need exactly "result + 1" characters. + mem_length = result + 1; + } + + if (mem_length > 32 * 1024 * 1024) { + // That should be plenty, don't try anything larger. This protects + // against huge allocations when using vsnprintfT implementations that + // return -1 for reasons other than overflow without setting errno. + DLOG(WARNING) << "Unable to printf the requested string due to size."; + return; + } + + std::vector<typename StringType::value_type> mem_buf(mem_length); + + // Restore the va_list before we use it again. + base_va_copy(backup_ap, ap); + + result = vsnprintfT(&mem_buf[0], mem_length, format, ap); + va_end(backup_ap); + + if ((result >= 0) && (result < mem_length)) { + // It fit. + dst->append(&mem_buf[0], result); + return; + } + } +} + +namespace { + +template <typename STR, typename INT, typename UINT, bool NEG> +struct IntToStringT { + + // This is to avoid a compiler warning about unary minus on unsigned type. + // For example, say you had the following code: + // template <typename INT> + // INT abs(INT value) { return value < 0 ? -value : value; } + // Even though if INT is unsigned, it's impossible for value < 0, so the + // unary minus will never be taken, the compiler will still generate a + // warning. We do a little specialization dance... + template <typename INT2, typename UINT2, bool NEG2> + struct ToUnsignedT { }; + + template <typename INT2, typename UINT2> + struct ToUnsignedT<INT2, UINT2, false> { + static UINT2 ToUnsigned(INT2 value) { + return static_cast<UINT2>(value); + } + }; + + template <typename INT2, typename UINT2> + struct ToUnsignedT<INT2, UINT2, true> { + static UINT2 ToUnsigned(INT2 value) { + return static_cast<UINT2>(value < 0 ? -value : value); + } + }; + + // This set of templates is very similar to the above templates, but + // for testing whether an integer is negative. + template <typename INT2, bool NEG2> + struct TestNegT {}; + template <typename INT2> + struct TestNegT<INT2, false> { + static bool TestNeg(INT2 value) { + // value is unsigned, and can never be negative. + return false; + } + }; + template <typename INT2> + struct TestNegT<INT2, true> { + static bool TestNeg(INT2 value) { + return value < 0; + } + }; + + static STR IntToString(INT value) { + // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. + // So round up to allocate 3 output characters per byte, plus 1 for '-'. + const int kOutputBufSize = 3 * sizeof(INT) + 1; + + // Allocate the whole string right away, we will right back to front, and + // then return the substr of what we ended up using. + STR outbuf(kOutputBufSize, 0); + + bool is_neg = TestNegT<INT, NEG>::TestNeg(value); + // Even though is_neg will never be true when INT is parameterized as + // unsigned, even the presence of the unary operation causes a warning. + UINT res = ToUnsignedT<INT, UINT, NEG>::ToUnsigned(value); + + for (typename STR::iterator it = outbuf.end();;) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast<typename STR::value_type>((res % 10) + '0'); + res /= 10; + + // We're done.. + if (res == 0) { + if (is_neg) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast<typename STR::value_type>('-'); + } + return STR(it, outbuf.end()); + } + } + NOTREACHED(); + return STR(); + } +}; + +} + +std::string IntToString(int value) { + return IntToStringT<std::string, int, unsigned int, true>:: + IntToString(value); +} +std::wstring IntToWString(int value) { + return IntToStringT<std::wstring, int, unsigned int, true>:: + IntToString(value); +} +std::string UintToString(unsigned int value) { + return IntToStringT<std::string, unsigned int, unsigned int, false>:: + IntToString(value); +} +std::wstring UintToWString(unsigned int value) { + return IntToStringT<std::wstring, unsigned int, unsigned int, false>:: + IntToString(value); +} +std::string Int64ToString(int64_t value) { + return IntToStringT<std::string, int64_t, uint64_t, true>:: + IntToString(value); +} +std::wstring Int64ToWString(int64_t value) { + return IntToStringT<std::wstring, int64_t, uint64_t, true>:: + IntToString(value); +} +std::string Uint64ToString(uint64_t value) { + return IntToStringT<std::string, uint64_t, uint64_t, false>:: + IntToString(value); +} +std::wstring Uint64ToWString(uint64_t value) { + return IntToStringT<std::wstring, uint64_t, uint64_t, false>:: + IntToString(value); +} + +// Lower-level routine that takes a va_list and appends to a specified +// string. All other routines are just convenience wrappers around it. +static void StringAppendV(std::string* dst, const char* format, va_list ap) { + StringAppendVT(dst, format, ap); +} + +static void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { + StringAppendVT(dst, format, ap); +} + +std::string StringPrintf(const char* format, ...) { + va_list ap; + va_start(ap, format); + std::string result; + StringAppendV(&result, format, ap); + va_end(ap); + return result; +} + +std::wstring StringPrintf(const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + std::wstring result; + StringAppendV(&result, format, ap); + va_end(ap); + return result; +} + +const std::string& SStringPrintf(std::string* dst, const char* format, ...) { + va_list ap; + va_start(ap, format); + dst->clear(); + StringAppendV(dst, format, ap); + va_end(ap); + return *dst; +} + +const std::wstring& SStringPrintf(std::wstring* dst, + const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + dst->clear(); + StringAppendV(dst, format, ap); + va_end(ap); + return *dst; +} + +void StringAppendF(std::string* dst, const char* format, ...) { + va_list ap; + va_start(ap, format); + StringAppendV(dst, format, ap); + va_end(ap); +} + +void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + StringAppendV(dst, format, ap); + va_end(ap); +} + +template<typename STR> +static void SplitStringT(const STR& str, + const typename STR::value_type s, + bool trim_whitespace, + std::vector<STR>* r) { + size_t last = 0; + size_t i; + size_t c = str.size(); + for (i = 0; i <= c; ++i) { + if (i == c || str[i] == s) { + size_t len = i - last; + STR tmp = str.substr(last, len); + if (trim_whitespace) { + STR t_tmp; + TrimWhitespace(tmp, TRIM_ALL, &t_tmp); + r->push_back(t_tmp); + } else { + r->push_back(tmp); + } + last = i + 1; + } + } +} + +void SplitString(const std::wstring& str, + wchar_t s, + std::vector<std::wstring>* r) { + SplitStringT(str, s, true, r); +} + +void SplitString(const std::string& str, + char s, + std::vector<std::string>* r) { + SplitStringT(str, s, true, r); +} + +// For the various *ToInt conversions, there are no *ToIntTraits classes to use +// because there's no such thing as strtoi. Use *ToLongTraits through a cast +// instead, requiring that long and int are compatible and equal-width. They +// are on our target platforms. + +// XXX Sigh. + +#if !defined(ARCH_CPU_64_BITS) +bool StringToInt(const std::string& input, int* output) { + COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_strtol_to_int); + return StringToNumber<StringToLongTraits>(input, + reinterpret_cast<long*>(output)); +} + +bool StringToInt(const string16& input, int* output) { + COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_wcstol_to_int); + return StringToNumber<String16ToLongTraits>(input, + reinterpret_cast<long*>(output)); +} + +#else +bool StringToInt(const std::string& input, int* output) { + long tmp; + bool ok = StringToNumber<StringToLongTraits>(input, &tmp); + if (!ok || tmp > kint32max) { + return false; + } + *output = static_cast<int>(tmp); + return true; +} + +bool StringToInt(const string16& input, int* output) { + long tmp; + bool ok = StringToNumber<String16ToLongTraits>(input, &tmp); + if (!ok || tmp > kint32max) { + return false; + } + *output = static_cast<int>(tmp); + return true; +} +#endif // !defined(ARCH_CPU_64_BITS) + +bool StringToInt64(const std::string& input, int64_t* output) { + return StringToNumber<StringToInt64Traits>(input, output); +} + +bool StringToInt64(const string16& input, int64_t* output) { + return StringToNumber<String16ToInt64Traits>(input, output); +} + +int StringToInt(const std::string& value) { + int result; + StringToInt(value, &result); + return result; +} + +int StringToInt(const string16& value) { + int result; + StringToInt(value, &result); + return result; +} + +int64_t StringToInt64(const std::string& value) { + int64_t result; + StringToInt64(value, &result); + return result; +} + +int64_t StringToInt64(const string16& value) { + int64_t result; + StringToInt64(value, &result); + return result; +} + +// The following code is compatible with the OpenBSD lcpy interface. See: +// http://www.gratisoft.us/todd/papers/strlcpy.html +// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c + +namespace { + +template <typename CHAR> +size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { + for (size_t i = 0; i < dst_size; ++i) { + if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. + return i; + } + + // We were left off at dst_size. We over copied 1 byte. Null terminate. + if (dst_size != 0) + dst[dst_size - 1] = 0; + + // Count the rest of the |src|, and return it's length in characters. + while (src[dst_size]) ++dst_size; + return dst_size; +} + +} // namespace + +size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { + return lcpyT<char>(dst, src, dst_size); +} +size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { + return lcpyT<wchar_t>(dst, src, dst_size); +} diff --git a/ipc/chromium/src/base/string_util.h b/ipc/chromium/src/base/string_util.h new file mode 100644 index 0000000000..430b4a86bb --- /dev/null +++ b/ipc/chromium/src/base/string_util.h @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 defines utility functions for working with strings. + +#ifndef BASE_STRING_UTIL_H_ +#define BASE_STRING_UTIL_H_ + +#include <stdarg.h> // va_list +#include <ctype.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/string16.h" +#include "base/string_piece.h" // For implicit conversions. + +// Safe standard library wrappers for all platforms. + +namespace base { + +// C standard-library functions like "strncasecmp" and "snprintf" that aren't +// cross-platform are provided as "base::strncasecmp", and their prototypes +// are listed below. These functions are then implemented as inline calls +// to the platform-specific equivalents in the platform-specific headers. + +// Compare the two strings s1 and s2 without regard to case using +// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if +// s2 > s1 according to a lexicographic comparison. +int strcasecmp(const char* s1, const char* s2); + +// Compare up to count characters of s1 and s2 without regard to case using +// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if +// s2 > s1 according to a lexicographic comparison. +int strncasecmp(const char* s1, const char* s2, size_t count); + +// Wrapper for vsnprintf that always null-terminates and always returns the +// number of characters that would be in an untruncated formatted +// string, even when truncation occurs. +int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments); + +// vswprintf always null-terminates, but when truncation occurs, it will either +// return -1 or the number of characters that would be in an untruncated +// formatted string. The actual return value depends on the underlying +// C library's vswprintf implementation. +int vswprintf(wchar_t* buffer, size_t size, + const wchar_t* format, va_list arguments); + +// Some of these implementations need to be inlined. + +inline int snprintf(char* buffer, size_t size, const char* format, ...) { + va_list arguments; + va_start(arguments, format); + int result = vsnprintf(buffer, size, format, arguments); + va_end(arguments); + return result; +} + +inline int swprintf(wchar_t* buffer, size_t size, const wchar_t* format, ...) { + va_list arguments; + va_start(arguments, format); + int result = vswprintf(buffer, size, format, arguments); + va_end(arguments); + return result; +} + +// BSD-style safe and consistent string copy functions. +// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|. +// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as +// long as |dst_size| is not 0. Returns the length of |src| in characters. +// If the return value is >= dst_size, then the output was truncated. +// NOTE: All sizes are in number of characters, NOT in bytes. +size_t strlcpy(char* dst, const char* src, size_t dst_size); +size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size); + +// Scan a wprintf format string to determine whether it's portable across a +// variety of systems. This function only checks that the conversion +// specifiers used by the format string are supported and have the same meaning +// on a variety of systems. It doesn't check for other errors that might occur +// within a format string. +// +// Nonportable conversion specifiers for wprintf are: +// - 's' and 'c' without an 'l' length modifier. %s and %c operate on char +// data on all systems except Windows, which treat them as wchar_t data. +// Use %ls and %lc for wchar_t data instead. +// - 'S' and 'C', which operate on wchar_t data on all systems except Windows, +// which treat them as char data. Use %ls and %lc for wchar_t data +// instead. +// - 'F', which is not identified by Windows wprintf documentation. +// - 'D', 'O', and 'U', which are deprecated and not available on all systems. +// Use %ld, %lo, and %lu instead. +// +// Note that there is no portable conversion specifier for char data when +// working with wprintf. +// +// This function is intended to be called from base::vswprintf. +bool IsWprintfFormatPortable(const wchar_t* format); + +} // namespace base + +#if defined(OS_WIN) +#include "base/string_util_win.h" +#elif defined(OS_POSIX) +#include "base/string_util_posix.h" +#else +#error Define string operations appropriately for your platform +#endif + +// Trims any whitespace from either end of the input string. Returns where +// whitespace was found. +// The non-wide version has two functions: +// * TrimWhitespaceASCII() +// This function is for ASCII strings and only looks for ASCII whitespace; +// * TrimWhitespaceUTF8() +// This function is for UTF-8 strings and looks for Unicode whitespace. +// Please choose the best one according to your usage. +// NOTE: Safe to use the same variable for both input and output. +enum TrimPositions { + TRIM_NONE = 0, + TRIM_LEADING = 1 << 0, + TRIM_TRAILING = 1 << 1, + TRIM_ALL = TRIM_LEADING | TRIM_TRAILING +}; +TrimPositions TrimWhitespace(const std::wstring& input, + TrimPositions positions, + std::wstring* output); +TrimPositions TrimWhitespaceASCII(const std::string& input, + TrimPositions positions, + std::string* output); + +// Deprecated. This function is only for backward compatibility and calls +// TrimWhitespaceASCII(). +TrimPositions TrimWhitespace(const std::string& input, + TrimPositions positions, + std::string* output); + +// Searches for CR or LF characters. Removes all contiguous whitespace +// strings that contain them. This is useful when trying to deal with text +// copied from terminals. +// Returns |text, with the following three transformations: +// (1) Leading and trailing whitespace is trimmed. +// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace +// sequences containing a CR or LF are trimmed. +// (3) All other whitespace sequences are converted to single spaces. +std::wstring CollapseWhitespace(const std::wstring& text, + bool trim_sequences_with_line_breaks); + +// These convert between ASCII (7-bit) and Wide/UTF16 strings. +std::string WideToASCII(const std::wstring& wide); +std::wstring ASCIIToWide(const std::string& ascii); +std::string UTF16ToASCII(const string16& utf16); +string16 ASCIIToUTF16(const std::string& ascii); + +// These convert between UTF-8, -16, and -32 strings. They are potentially slow, +// so avoid unnecessary conversions. The low-level versions return a boolean +// indicating whether the conversion was 100% valid. In this case, it will still +// do the best it can and put the result in the output buffer. The versions that +// return strings ignore this error and just return the best conversion +// possible. +bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output); +std::string WideToUTF8(const std::wstring& wide); +bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output); +std::wstring UTF8ToWide(const ::StringPiece& utf8); + +bool IsStringASCII(const std::wstring& str); +bool IsStringASCII(const std::string& str); +bool IsStringASCII(const string16& str); + +// Specialized string-conversion functions. +std::string IntToString(int value); +std::wstring IntToWString(int value); +std::string UintToString(unsigned int value); +std::wstring UintToWString(unsigned int value); +std::string Int64ToString(int64_t value); +std::wstring Int64ToWString(int64_t value); +std::string Uint64ToString(uint64_t value); +std::wstring Uint64ToWString(uint64_t value); +// The DoubleToString methods convert the double to a string format that +// ignores the locale. If you want to use locale specific formatting, use ICU. +std::string DoubleToString(double value); +std::wstring DoubleToWString(double value); + +// Perform a best-effort conversion of the input string to a numeric type, +// setting |*output| to the result of the conversion. Returns true for +// "perfect" conversions; returns false in the following cases: +// - Overflow/underflow. |*output| will be set to the maximum value supported +// by the data type. +// - Trailing characters in the string after parsing the number. |*output| +// will be set to the value of the number that was parsed. +// - No characters parseable as a number at the beginning of the string. +// |*output| will be set to 0. +// - Empty string. |*output| will be set to 0. +bool StringToInt(const std::string& input, int* output); +bool StringToInt(const string16& input, int* output); +bool StringToInt64(const std::string& input, int64_t* output); +bool StringToInt64(const string16& input, int64_t* output); + +// Convenience forms of the above, when the caller is uninterested in the +// boolean return value. These return only the |*output| value from the +// above conversions: a best-effort conversion when possible, otherwise, 0. +int StringToInt(const std::string& value); +int StringToInt(const string16& value); +int64_t StringToInt64(const std::string& value); +int64_t StringToInt64(const string16& value); + +// Return a C++ string given printf-like input. +std::string StringPrintf(const char* format, ...); +std::wstring StringPrintf(const wchar_t* format, ...); + +// Store result into a supplied string and return it +const std::string& SStringPrintf(std::string* dst, const char* format, ...); +const std::wstring& SStringPrintf(std::wstring* dst, + const wchar_t* format, ...); + +// Append result to a supplied string +void StringAppendF(std::string* dst, const char* format, ...); +void StringAppendF(std::wstring* dst, const wchar_t* format, ...); + +//----------------------------------------------------------------------------- + +// Splits |str| into a vector of strings delimited by |s|. Append the results +// into |r| as they appear. If several instances of |s| are contiguous, or if +// |str| begins with or ends with |s|, then an empty string is inserted. +// +// Every substring is trimmed of any leading or trailing white space. +void SplitString(const std::wstring& str, + wchar_t s, + std::vector<std::wstring>* r); +void SplitString(const std::string& str, + char s, + std::vector<std::string>* r); + +#endif // BASE_STRING_UTIL_H_ diff --git a/ipc/chromium/src/base/string_util_posix.h b/ipc/chromium/src/base/string_util_posix.h new file mode 100644 index 0000000000..60c2d58b63 --- /dev/null +++ b/ipc/chromium/src/base/string_util_posix.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_STRING_UTIL_POSIX_H_ +#define BASE_STRING_UTIL_POSIX_H_ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <wchar.h> + +#include "base/logging.h" + +namespace base { + +// Chromium code style is to not use malloc'd strings; this is only for use +// for interaction with APIs that require it. +inline char* strdup(const char* str) { + return ::strdup(str); +} + +inline int strcasecmp(const char* string1, const char* string2) { + return ::strcasecmp(string1, string2); +} + +inline int strncasecmp(const char* string1, const char* string2, size_t count) { + return ::strncasecmp(string1, string2, count); +} + +inline int vsnprintf(char* buffer, size_t size, + const char* format, va_list arguments) { + return ::vsnprintf(buffer, size, format, arguments); +} + +inline int vswprintf(wchar_t* buffer, size_t size, + const wchar_t* format, va_list arguments) { + DCHECK(IsWprintfFormatPortable(format)); + return ::vswprintf(buffer, size, format, arguments); +} + +} // namespace base + +#endif // BASE_STRING_UTIL_POSIX_H_ diff --git a/ipc/chromium/src/base/string_util_win.h b/ipc/chromium/src/base/string_util_win.h new file mode 100644 index 0000000000..218bd6466b --- /dev/null +++ b/ipc/chromium/src/base/string_util_win.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_STRING_UTIL_WIN_H_ +#define BASE_STRING_UTIL_WIN_H_ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <wchar.h> + +#include "base/logging.h" + +namespace base { + +// Chromium code style is to not use malloc'd strings; this is only for use +// for interaction with APIs that require it. +inline char* strdup(const char* str) { + return _strdup(str); +} + +inline int strcasecmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} + +inline int strncasecmp(const char* s1, const char* s2, size_t count) { + return _strnicmp(s1, s2, count); +} + +inline int vsnprintf(char* buffer, size_t size, + const char* format, va_list arguments) { + int length = vsnprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) + return _vscprintf(format, arguments); + return length; +} + +inline int vswprintf(wchar_t* buffer, size_t size, + const wchar_t* format, va_list arguments) { + DCHECK(IsWprintfFormatPortable(format)); + + int length = _vsnwprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) + return _vscwprintf(format, arguments); + return length; +} + +} // namespace base + +#endif // BASE_STRING_UTIL_WIN_H_ diff --git a/ipc/chromium/src/base/sys_info.h b/ipc/chromium/src/base/sys_info.h new file mode 100644 index 0000000000..8c2e116f46 --- /dev/null +++ b/ipc/chromium/src/base/sys_info.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 BASE_SYS_INFO_H_ +#define BASE_SYS_INFO_H_ + +#include "base/basictypes.h" + +#include <string> + +namespace base { + +class SysInfo { + public: + // Return the number of logical processors/cores on the current machine. + // WARNING: On POSIX, this method uses static variables and is not threadsafe + // until it's been initialized by being called once without a race. + static int NumberOfProcessors(); + + // Return the number of bytes of physical memory on the current machine. + static int64_t AmountOfPhysicalMemory(); + + // Return the number of megabytes of physical memory on the current machine. + static int AmountOfPhysicalMemoryMB() { + return static_cast<int>(AmountOfPhysicalMemory() / 1024 / 1024); + } + + // Return the available disk space in bytes on the volume containing |path|, + // or -1 on failure. + static int64_t AmountOfFreeDiskSpace(const std::wstring& path); + + // Return true if the given environment variable is defined. + // TODO: find a better place for HasEnvVar. + static bool HasEnvVar(const wchar_t* var); + + // Return the value of the given environment variable + // or an empty string if not defined. + // TODO: find a better place for GetEnvVar. + static std::wstring GetEnvVar(const wchar_t* var); + + // Returns the name of the host operating system. + static std::string OperatingSystemName(); + + // Returns the CPU architecture of the system. Exact return value may differ + // across platforms. + static std::string CPUArchitecture(); + + // Returns the pixel dimensions of the primary display via the + // width and height parameters. + static void GetPrimaryDisplayDimensions(int* width, int* height); + + // Return the number of displays. + static int DisplayCount(); + + // Return the smallest amount of memory (in bytes) which the VM system will + // allocate. + static size_t VMAllocationGranularity(); +}; + +} // namespace base + +#endif // BASE_SYS_INFO_H_ diff --git a/ipc/chromium/src/base/sys_info_mac.cc b/ipc/chromium/src/base/sys_info_mac.cc new file mode 100644 index 0000000000..a0f82d0210 --- /dev/null +++ b/ipc/chromium/src/base/sys_info_mac.cc @@ -0,0 +1,13 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 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 "base/sys_info.h" + +#include <CoreServices/CoreServices.h> + +namespace base { + +} // namespace base diff --git a/ipc/chromium/src/base/sys_info_posix.cc b/ipc/chromium/src/base/sys_info_posix.cc new file mode 100644 index 0000000000..81ec78053d --- /dev/null +++ b/ipc/chromium/src/base/sys_info_posix.cc @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/sys_info.h" +#include "base/basictypes.h" + +#include <errno.h> +#include <string.h> +#ifndef ANDROID +#include <sys/statvfs.h> +#endif +#include <sys/utsname.h> +#include <unistd.h> + +#if defined(OS_MACOSX) +#include <mach/mach_host.h> +#include <mach/mach_init.h> +#endif + +#if defined(OS_NETBSD) +#include <sys/param.h> +#include <sys/sysctl.h> +#endif + +#include "base/logging.h" +#include "base/string_util.h" + +namespace base { + +int SysInfo::NumberOfProcessors() { + // It seems that sysconf returns the number of "logical" processors on both + // mac and linux. So we get the number of "online logical" processors. +#ifdef _SC_NPROCESSORS_ONLN + static long res = sysconf(_SC_NPROCESSORS_ONLN); +#else + static long res = 1; +#endif + if (res == -1) { + NOTREACHED(); + return 1; + } + + return static_cast<int>(res); +} + +// static +int64_t SysInfo::AmountOfPhysicalMemory() { + // _SC_PHYS_PAGES is not part of POSIX and not available on OS X +#if defined(OS_MACOSX) + struct host_basic_info hostinfo; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + int result = host_info(mach_host_self(), + HOST_BASIC_INFO, + reinterpret_cast<host_info_t>(&hostinfo), + &count); + DCHECK_EQ(HOST_BASIC_INFO_COUNT, count); + if (result != KERN_SUCCESS) { + NOTREACHED(); + return 0; + } + + return static_cast<int64_t>(hostinfo.max_mem); +#elif defined(OS_NETBSD) + int mib[2]; + int rc; + int64_t memSize; + size_t len = sizeof(memSize); + + mib[0] = CTL_HW; + mib[1] = HW_PHYSMEM64; + rc = sysctl( mib, 2, &memSize, &len, NULL, 0 ); + if (-1 != rc) { + return memSize; + } + return 0; + +#else + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + if (pages == -1 || page_size == -1) { + NOTREACHED(); + return 0; + } + + return static_cast<int64_t>(pages) * page_size; +#endif +} + +// static +int64_t SysInfo::AmountOfFreeDiskSpace(const std::wstring& path) { +#ifndef ANDROID + struct statvfs stats; + if (statvfs(WideToUTF8(path).c_str(), &stats) != 0) { + return -1; + } + return static_cast<int64_t>(stats.f_bavail) * stats.f_frsize; +#else + return -1; +#endif +} + +// static +bool SysInfo::HasEnvVar(const wchar_t* var) { + std::string var_utf8 = WideToUTF8(std::wstring(var)); + return getenv(var_utf8.c_str()) != NULL; +} + +// static +std::wstring SysInfo::GetEnvVar(const wchar_t* var) { + std::string var_utf8 = WideToUTF8(std::wstring(var)); + char* value = getenv(var_utf8.c_str()); + if (!value) { + return L""; + } else { + return UTF8ToWide(value); + } +} + +// static +std::string SysInfo::OperatingSystemName() { + utsname info; + if (uname(&info) < 0) { + NOTREACHED(); + return ""; + } + return std::string(info.sysname); +} + +// static +std::string SysInfo::CPUArchitecture() { + utsname info; + if (uname(&info) < 0) { + NOTREACHED(); + return ""; + } + return std::string(info.machine); +} + +// static +void SysInfo::GetPrimaryDisplayDimensions(int* width, int* height) { + NOTIMPLEMENTED(); +} + +// static +int SysInfo::DisplayCount() { + NOTIMPLEMENTED(); + return 1; +} + +// static +size_t SysInfo::VMAllocationGranularity() { + return getpagesize(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/sys_info_win.cc b/ipc/chromium/src/base/sys_info_win.cc new file mode 100644 index 0000000000..5fffd18e3f --- /dev/null +++ b/ipc/chromium/src/base/sys_info_win.cc @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/sys_info.h" + +#include <windows.h> + +#include "base/logging.h" +#include "base/string_util.h" +#include "mozilla/UniquePtr.h" + +namespace base { + +// static +int SysInfo::NumberOfProcessors() { + SYSTEM_INFO info; + GetSystemInfo(&info); + return static_cast<int>(info.dwNumberOfProcessors); +} + +// static +int64_t SysInfo::AmountOfPhysicalMemory() { + MEMORYSTATUSEX memory_info; + memory_info.dwLength = sizeof(memory_info); + if (!GlobalMemoryStatusEx(&memory_info)) { + NOTREACHED(); + return 0; + } + + int64_t rv = static_cast<int64_t>(memory_info.ullTotalPhys); + if (rv < 0) + rv = kint64max; + return rv; +} + +// static +int64_t SysInfo::AmountOfFreeDiskSpace(const std::wstring& path) { + ULARGE_INTEGER available, total, free; + if (!GetDiskFreeSpaceExW(path.c_str(), &available, &total, &free)) { + return -1; + } + int64_t rv = static_cast<int64_t>(available.QuadPart); + if (rv < 0) + rv = kint64max; + return rv; +} + +// static +bool SysInfo::HasEnvVar(const wchar_t* var) { + return GetEnvironmentVariable(var, NULL, 0) != 0; +} + +// static +std::wstring SysInfo::GetEnvVar(const wchar_t* var) { + DWORD value_length = GetEnvironmentVariable(var, NULL, 0); + if (value_length == 0) { + return L""; + } + mozilla::UniquePtr<wchar_t[]> value(new wchar_t[value_length]); + GetEnvironmentVariable(var, value.get(), value_length); + return std::wstring(value.get()); +} + +// static +std::string SysInfo::OperatingSystemName() { + return "Windows NT"; +} + +// static +std::string SysInfo::CPUArchitecture() { + // TODO: Make this vary when we support any other architectures. + return "x86"; +} + +// static +void SysInfo::GetPrimaryDisplayDimensions(int* width, int* height) { + if (width) + *width = GetSystemMetrics(SM_CXSCREEN); + + if (height) + *height = GetSystemMetrics(SM_CYSCREEN); +} + +// static +int SysInfo::DisplayCount() { + return GetSystemMetrics(SM_CMONITORS); +} + +// static +size_t SysInfo::VMAllocationGranularity() { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + + return sysinfo.dwAllocationGranularity; +} + +} // namespace base diff --git a/ipc/chromium/src/base/sys_string_conversions.h b/ipc/chromium/src/base/sys_string_conversions.h new file mode 100644 index 0000000000..e3a51964a7 --- /dev/null +++ b/ipc/chromium/src/base/sys_string_conversions.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_SYS_STRING_CONVERSIONS_H_ +#define BASE_SYS_STRING_CONVERSIONS_H_ + +// Provides system-dependent string type conversions for cases where it's +// necessary to not use ICU. Generally, you should not need this in Chrome, +// but it is used in some shared code. Dependencies should be minimal. + +#include <string> +#include "base/basictypes.h" +#include "base/string16.h" + +class StringPiece; + +namespace base { + +// Converts between wide and UTF-8 representations of a string. On error, the +// result is system-dependent. +std::string SysWideToUTF8(const std::wstring& wide); +std::wstring SysUTF8ToWide(const StringPiece& utf8); + +// Converts between wide and the system multi-byte representations of a string. +// DANGER: This will lose information and can change (on Windows, this can +// change between reboots). +std::string SysWideToNativeMB(const std::wstring& wide); +std::wstring SysNativeMBToWide(const StringPiece& native_mb); + +} // namespace base + +#endif // BASE_SYS_STRING_CONVERSIONS_H_ diff --git a/ipc/chromium/src/base/sys_string_conversions_mac.mm b/ipc/chromium/src/base/sys_string_conversions_mac.mm new file mode 100644 index 0000000000..cc5f58b704 --- /dev/null +++ b/ipc/chromium/src/base/sys_string_conversions_mac.mm @@ -0,0 +1,145 @@ +// Copyright (c) 2006-2008 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 "base/sys_string_conversions.h" + +#import <Foundation/Foundation.h> + +#include <vector> + +#include "base/scoped_cftyperef.h" +#include "base/string_piece.h" + +namespace base { + +namespace { + +// Convert the supplied CFString into the specified encoding, and return it as +// an STL string of the template type. Returns an empty string on failure. +// +// Do not assert in this function since it is used by the asssertion code! +template<typename StringType> +static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring, + CFStringEncoding encoding) { + CFIndex length = CFStringGetLength(cfstring); + if (length == 0) + return StringType(); + + CFRange whole_string = CFRangeMake(0, length); + CFIndex out_size; + CFIndex converted = CFStringGetBytes(cfstring, + whole_string, + encoding, + 0, // lossByte + false, // isExternalRepresentation + NULL, // buffer + 0, // maxBufLen + &out_size); + if (converted == 0 || out_size == 0) + return StringType(); + + // out_size is the number of UInt8-sized units needed in the destination. + // A buffer allocated as UInt8 units might not be properly aligned to + // contain elements of StringType::value_type. Use a container for the + // proper value_type, and convert out_size by figuring the number of + // value_type elements per UInt8. Leave room for a NUL terminator. + typename StringType::size_type elements = + out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1; + + std::vector<typename StringType::value_type> out_buffer(elements); + converted = CFStringGetBytes(cfstring, + whole_string, + encoding, + 0, // lossByte + false, // isExternalRepresentation + reinterpret_cast<UInt8*>(&out_buffer[0]), + out_size, + NULL); // usedBufLen + if (converted == 0) + return StringType(); + + out_buffer[elements - 1] = '\0'; + return StringType(&out_buffer[0], elements - 1); +} + +// Given an STL string |in| with an encoding specified by |in_encoding|, +// convert it to |out_encoding| and return it as an STL string of the +// |OutStringType| template type. Returns an empty string on failure. +// +// Do not assert in this function since it is used by the asssertion code! +template<typename InStringType, typename OutStringType> +static OutStringType STLStringToSTLStringWithEncodingsT( + const InStringType& in, + CFStringEncoding in_encoding, + CFStringEncoding out_encoding) { + typename InStringType::size_type in_length = in.length(); + if (in_length == 0) + return OutStringType(); + + scoped_cftyperef<CFStringRef> cfstring( + CFStringCreateWithBytesNoCopy(NULL, + reinterpret_cast<const UInt8*>(in.data()), + in_length * + sizeof(typename InStringType::value_type), + in_encoding, + false, + kCFAllocatorNull)); + if (!cfstring) + return OutStringType(); + + return CFStringToSTLStringWithEncodingT<OutStringType>(cfstring, + out_encoding); +} + +// Given an STL string |in| with an encoding specified by |in_encoding|, +// return it as a CFStringRef. Returns NULL on failure. +template<typename StringType> +static CFStringRef STLStringToCFStringWithEncodingsT( + const StringType& in, + CFStringEncoding in_encoding) { + typename StringType::size_type in_length = in.length(); + if (in_length == 0) + return CFSTR(""); + + return CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast<const UInt8*>(in.data()), + in_length * + sizeof(typename StringType::value_type), + in_encoding, + false); +} + +// Specify the byte ordering explicitly, otherwise CFString will be confused +// when strings don't carry BOMs, as they typically won't. +static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8; + +#ifdef __BIG_ENDIAN__ +static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE; +#elif defined(__LITTLE_ENDIAN__) +static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE; +#endif // __LITTLE_ENDIAN__ + +} // namespace + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToUTF8(const std::wstring& wide) { + return STLStringToSTLStringWithEncodingsT<std::wstring, std::string>( + wide, kWideStringEncoding, kNarrowStringEncoding); +} + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysUTF8ToWide(const StringPiece& utf8) { + return STLStringToSTLStringWithEncodingsT<StringPiece, std::wstring>( + utf8, kNarrowStringEncoding, kWideStringEncoding); +} + +std::string SysWideToNativeMB(const std::wstring& wide) { + return SysWideToUTF8(wide); +} + +std::wstring SysNativeMBToWide(const StringPiece& native_mb) { + return SysUTF8ToWide(native_mb); +} + +} // namespace base diff --git a/ipc/chromium/src/base/sys_string_conversions_win.cc b/ipc/chromium/src/base/sys_string_conversions_win.cc new file mode 100644 index 0000000000..198a14d212 --- /dev/null +++ b/ipc/chromium/src/base/sys_string_conversions_win.cc @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/sys_string_conversions.h" + +#include <windows.h> + +#include "base/string_piece.h" + +namespace base { + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysMultiByteToWide(const StringPiece& mb, uint32_t code_page) { + if (mb.empty()) + return std::wstring(); + + int mb_length = static_cast<int>(mb.length()); + // Compute the length of the buffer. + int charcount = MultiByteToWideChar(code_page, 0, + mb.data(), mb_length, NULL, 0); + if (charcount == 0) + return std::wstring(); + + std::wstring wide; + wide.resize(charcount); + MultiByteToWideChar(code_page, 0, mb.data(), mb_length, &wide[0], charcount); + + return wide; +} + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToMultiByte(const std::wstring& wide, uint32_t code_page) { + int wide_length = static_cast<int>(wide.length()); + if (wide_length == 0) + return std::string(); + + // Compute the length of the buffer we'll need. + int charcount = WideCharToMultiByte(code_page, 0, wide.data(), wide_length, + NULL, 0, NULL, NULL); + if (charcount == 0) + return std::string(); + + std::string mb; + mb.resize(charcount); + WideCharToMultiByte(code_page, 0, wide.data(), wide_length, + &mb[0], charcount, NULL, NULL); + + return mb; +} + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToUTF8(const std::wstring& wide) { + return SysWideToMultiByte(wide, CP_UTF8); +} + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysUTF8ToWide(const StringPiece& utf8) { + return SysMultiByteToWide(utf8, CP_UTF8); +} + +std::string SysWideToNativeMB(const std::wstring& wide) { + return SysWideToMultiByte(wide, CP_ACP); +} + +std::wstring SysNativeMBToWide(const StringPiece& native_mb) { + return SysMultiByteToWide(native_mb, CP_ACP); +} + +} // namespace base diff --git a/ipc/chromium/src/base/task.h b/ipc/chromium/src/base/task.h new file mode 100644 index 0000000000..10dc9c488b --- /dev/null +++ b/ipc/chromium/src/base/task.h @@ -0,0 +1,369 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_TASK_H_ +#define BASE_TASK_H_ + +#include "base/revocable_store.h" +#include "base/tuple.h" +#include "mozilla/IndexSequence.h" +#include "mozilla/Tuple.h" +#include "nsISupportsImpl.h" +#include "nsThreadUtils.h" + +// Helper functions so that we can call a function a pass it arguments that come +// from a Tuple. + +namespace details { + +// Call the given method on the given object. Arguments are passed by move +// semantics from the given tuple. If the tuple has length N, the sequence must +// be IndexSequence<0, 1, ..., N-1>. +template<size_t... Indices, class ObjT, class Method, typename... Args> +void CallMethod(mozilla::IndexSequence<Indices...>, ObjT* obj, Method method, + mozilla::Tuple<Args...>& arg) +{ + (obj->*method)(mozilla::Move(mozilla::Get<Indices>(arg))...); +} + +// Same as above, but call a function. +template<size_t... Indices, typename Function, typename... Args> +void CallFunction(mozilla::IndexSequence<Indices...>, Function function, + mozilla::Tuple<Args...>& arg) +{ + (*function)(mozilla::Move(mozilla::Get<Indices>(arg))...); +} + +} // namespace details + +// Call a method on the given object. Arguments are passed by move semantics +// from the given tuple. +template<class ObjT, class Method, typename... Args> +void DispatchTupleToMethod(ObjT* obj, Method method, mozilla::Tuple<Args...>& arg) +{ + details::CallMethod(typename mozilla::IndexSequenceFor<Args...>::Type(), + obj, method, arg); +} + +// Same as above, but call a function. +template<typename Function, typename... Args> +void DispatchTupleToFunction(Function function, mozilla::Tuple<Args...>& arg) +{ + details::CallFunction(typename mozilla::IndexSequenceFor<Args...>::Type(), + function, arg); +} + +// Scoped Factories ------------------------------------------------------------ +// +// These scoped factory objects can be used by non-refcounted objects to safely +// place tasks in a message loop. Each factory guarantees that the tasks it +// produces will not run after the factory is destroyed. Commonly, factories +// are declared as class members, so the class' tasks will automatically cancel +// when the class instance is destroyed. +// +// Exampe Usage: +// +// class MyClass { +// private: +// // This factory will be used to schedule invocations of SomeMethod. +// ScopedRunnableMethodFactory<MyClass> some_method_factory_; +// +// public: +// // It is safe to suppress warning 4355 here. +// MyClass() : some_method_factory_(this) { } +// +// void SomeMethod() { +// // If this function might be called directly, you might want to revoke +// // any outstanding runnable methods scheduled to call it. If it's not +// // referenced other than by the factory, this is unnecessary. +// some_method_factory_.RevokeAll(); +// ... +// } +// +// void ScheduleSomeMethod() { +// // If you'd like to only only have one pending task at a time, test for +// // |empty| before manufacturing another task. +// if (!some_method_factory_.empty()) +// return; +// +// // The factories are not thread safe, so always invoke on +// // |MessageLoop::current()|. +// MessageLoop::current()->PostDelayedTask( +// some_method_factory_.NewRunnableMethod(&MyClass::SomeMethod), +// kSomeMethodDelayMS); +// } +// }; + +// A ScopedTaskFactory produces tasks of type |TaskType| and prevents them from +// running after it is destroyed. +template<class TaskType> +class ScopedTaskFactory : public RevocableStore { + public: + ScopedTaskFactory() { } + + // Create a new task. + inline TaskType* NewTask() { + return new TaskWrapper(this); + } + + class TaskWrapper : public TaskType { + public: + explicit TaskWrapper(RevocableStore* store) : revocable_(store) { } + + NS_IMETHOD Run() override { + if (!revocable_.revoked()) + TaskType::Run(); + return NS_OK; + } + + ~TaskWrapper() { + NS_ASSERT_OWNINGTHREAD(TaskWrapper); + } + + private: + Revocable revocable_; + + NS_DECL_OWNINGTHREAD + + DISALLOW_EVIL_CONSTRUCTORS(TaskWrapper); + }; + + private: + DISALLOW_EVIL_CONSTRUCTORS(ScopedTaskFactory); +}; + +// A ScopedRunnableMethodFactory creates runnable methods for a specified +// object. This is particularly useful for generating callbacks for +// non-reference counted objects when the factory is a member of the object. +template<class T> +class ScopedRunnableMethodFactory : public RevocableStore { + public: + explicit ScopedRunnableMethodFactory(T* object) : object_(object) { } + + template <class Method, typename... Elements> + inline already_AddRefed<mozilla::Runnable> + NewRunnableMethod(Method method, Elements&&... elements) { + typedef mozilla::Tuple<typename mozilla::Decay<Elements>::Type...> ArgsTuple; + typedef RunnableMethod<Method, ArgsTuple> Runnable; + typedef typename ScopedTaskFactory<Runnable>::TaskWrapper TaskWrapper; + + RefPtr<TaskWrapper> task = new TaskWrapper(this); + task->Init(object_, method, mozilla::MakeTuple(mozilla::Forward<Elements>(elements)...)); + return task.forget(); + } + + protected: + template <class Method, class Params> + class RunnableMethod : public mozilla::Runnable { + public: + RunnableMethod() { } + + void Init(T* obj, Method meth, Params&& params) { + obj_ = obj; + meth_ = meth; + params_ = mozilla::Forward<Params>(params); + } + + NS_IMETHOD Run() override { + DispatchTupleToMethod(obj_, meth_, params_); + return NS_OK; + } + + private: + T* MOZ_UNSAFE_REF("The validity of this pointer must be enforced by " + "external factors.") obj_; + Method meth_; + Params params_; + + DISALLOW_EVIL_CONSTRUCTORS(RunnableMethod); + }; + + private: + T* object_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedRunnableMethodFactory); +}; + +// General task implementations ------------------------------------------------ + +// Task to delete an object +template<class T> +class DeleteTask : public mozilla::CancelableRunnable { + public: + explicit DeleteTask(T* obj) : obj_(obj) { + } + NS_IMETHOD Run() override { + delete obj_; + return NS_OK; + } + virtual nsresult Cancel() override { + obj_ = NULL; + return NS_OK; + } + private: + T* MOZ_UNSAFE_REF("The validity of this pointer must be enforced by " + "external factors.") obj_; +}; + +// RunnableMethodTraits -------------------------------------------------------- +// +// This traits-class is used by RunnableMethod to manage the lifetime of the +// callee object. By default, it is assumed that the callee supports AddRef +// and Release methods. A particular class can specialize this template to +// define other lifetime management. For example, if the callee is known to +// live longer than the RunnableMethod object, then a RunnableMethodTraits +// struct could be defined with empty RetainCallee and ReleaseCallee methods. + +template <class T> +struct RunnableMethodTraits { + static void RetainCallee(T* obj) { + obj->AddRef(); + } + static void ReleaseCallee(T* obj) { + obj->Release(); + } +}; + +// This allows using the NewRunnableMethod() functions with a const pointer +// to the callee object. See the similar support in nsRefPtr for a rationale +// of why this is reasonable. +template <class T> +struct RunnableMethodTraits<const T> { + static void RetainCallee(const T* obj) { + const_cast<T*>(obj)->AddRef(); + } + static void ReleaseCallee(const T* obj) { + const_cast<T*>(obj)->Release(); + } +}; + +// RunnableMethod and RunnableFunction ----------------------------------------- +// +// Runnable methods are a type of task that call a function on an object when +// they are run. We implement both an object and a set of NewRunnableMethod and +// NewRunnableFunction functions for convenience. These functions are +// overloaded and will infer the template types, simplifying calling code. +// +// The template definitions all use the following names: +// T - the class type of the object you're supplying +// this is not needed for the Static version of the call +// Method/Function - the signature of a pointer to the method or function you +// want to call +// Param - the parameter(s) to the method, possibly packed as a Tuple +// A - the first parameter (if any) to the method +// B - the second parameter (if any) to the mathod +// +// Put these all together and you get an object that can call a method whose +// signature is: +// R T::MyFunction([A[, B]]) +// +// Usage: +// PostTask(NewRunnableMethod(object, &Object::method[, a[, b]]) +// PostTask(NewRunnableFunction(&function[, a[, b]]) + +// RunnableMethod and NewRunnableMethod implementation ------------------------- + +template <class T, class Method, class Params> +class RunnableMethod : public mozilla::CancelableRunnable, + public RunnableMethodTraits<T> { + public: + RunnableMethod(T* obj, Method meth, Params&& params) + : obj_(obj), meth_(meth), params_(mozilla::Forward<Params>(params)) { + this->RetainCallee(obj_); + } + ~RunnableMethod() { + ReleaseCallee(); + } + + NS_IMETHOD Run() override { + if (obj_) + DispatchTupleToMethod(obj_, meth_, params_); + return NS_OK; + } + + virtual nsresult Cancel() override { + ReleaseCallee(); + return NS_OK; + } + + private: + void ReleaseCallee() { + if (obj_) { + RunnableMethodTraits<T>::ReleaseCallee(obj_); + obj_ = nullptr; + } + } + + // This is owning because of the RetainCallee and ReleaseCallee calls in the + // constructor and destructor. + T* MOZ_OWNING_REF obj_; + Method meth_; + Params params_; +}; + +namespace dont_add_new_uses_of_this { + +// Don't add new uses of this!!!! +template <class T, class Method, typename... Args> +inline already_AddRefed<mozilla::Runnable> +NewRunnableMethod(T* object, Method method, Args&&... args) { + typedef mozilla::Tuple<typename mozilla::Decay<Args>::Type...> ArgsTuple; + RefPtr<mozilla::Runnable> t = + new RunnableMethod<T, Method, ArgsTuple>(object, method, + mozilla::MakeTuple(mozilla::Forward<Args>(args)...)); + return t.forget(); +} + +} // namespace dont_add_new_uses_of_this + +// RunnableFunction and NewRunnableFunction implementation --------------------- + +template <class Function, class Params> +class RunnableFunction : public mozilla::CancelableRunnable { + public: + RunnableFunction(Function function, Params&& params) + : function_(function), params_(mozilla::Forward<Params>(params)) { + } + + ~RunnableFunction() { + } + + NS_IMETHOD Run() override { + if (function_) + DispatchTupleToFunction(function_, params_); + return NS_OK; + } + + virtual nsresult Cancel() override { + function_ = nullptr; + return NS_OK; + } + + Function function_; + Params params_; +}; + +template <class Function, typename... Args> +inline already_AddRefed<mozilla::CancelableRunnable> +NewCancelableRunnableFunction(Function function, Args&&... args) { + typedef mozilla::Tuple<typename mozilla::Decay<Args>::Type...> ArgsTuple; + RefPtr<mozilla::CancelableRunnable> t = + new RunnableFunction<Function, ArgsTuple>(function, + mozilla::MakeTuple(mozilla::Forward<Args>(args)...)); + return t.forget(); +} + +template <class Function, typename... Args> +inline already_AddRefed<mozilla::Runnable> +NewRunnableFunction(Function function, Args&&... args) { + typedef mozilla::Tuple<typename mozilla::Decay<Args>::Type...> ArgsTuple; + RefPtr<mozilla::Runnable> t = + new RunnableFunction<Function, ArgsTuple>(function, + mozilla::MakeTuple(mozilla::Forward<Args>(args)...)); + return t.forget(); +} + +#endif // BASE_TASK_H_ diff --git a/ipc/chromium/src/base/thread.cc b/ipc/chromium/src/base/thread.cc new file mode 100644 index 0000000000..feec3aedf7 --- /dev/null +++ b/ipc/chromium/src/base/thread.cc @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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 "base/thread.h" + +#include "base/string_util.h" +#include "base/thread_local.h" +#include "base/waitable_event.h" +#include "GeckoProfiler.h" +#include "mozilla/IOInterposer.h" +#include "nsThreadUtils.h" + +#ifdef MOZ_TASK_TRACER +#include "GeckoTaskTracer.h" +#endif + +namespace base { + +// This task is used to trigger the message loop to exit. +class ThreadQuitTask : public mozilla::Runnable { + public: + NS_IMETHOD Run() override { + MessageLoop::current()->Quit(); + Thread::SetThreadWasQuitProperly(true); + return NS_OK; + } +}; + +// Used to pass data to ThreadMain. This structure is allocated on the stack +// from within StartWithOptions. +struct Thread::StartupData { + // We get away with a const reference here because of how we are allocated. + const Thread::Options& options; + + // Used to synchronize thread startup. + WaitableEvent event; + + explicit StartupData(const Options& opt) + : options(opt), + event(false, false) {} +}; + +Thread::Thread(const char *name) + : startup_data_(NULL), + thread_(0), + message_loop_(NULL), + thread_id_(0), + name_(name) { + MOZ_COUNT_CTOR(base::Thread); +} + +Thread::~Thread() { + MOZ_COUNT_DTOR(base::Thread); + Stop(); +} + +namespace { + +// We use this thread-local variable to record whether or not a thread exited +// because its Stop method was called. This allows us to catch cases where +// MessageLoop::Quit() is called directly, which is unexpected when using a +// Thread to setup and run a MessageLoop. + +static base::ThreadLocalBoolean& get_tls_bool() { + static base::ThreadLocalBoolean tls_ptr; + return tls_ptr; +} + +} // namespace + +void Thread::SetThreadWasQuitProperly(bool flag) { + get_tls_bool().Set(flag); +} + +bool Thread::GetThreadWasQuitProperly() { + bool quit_properly = true; +#ifndef NDEBUG + quit_properly = get_tls_bool().Get(); +#endif + return quit_properly; +} + +bool Thread::Start() { + return StartWithOptions(Options()); +} + +bool Thread::StartWithOptions(const Options& options) { + DCHECK(!message_loop_); + + SetThreadWasQuitProperly(false); + + StartupData startup_data(options); + startup_data_ = &startup_data; + + if (!PlatformThread::Create(options.stack_size, this, &thread_)) { + DLOG(ERROR) << "failed to create thread"; + startup_data_ = NULL; // Record that we failed to start. + return false; + } + + // Wait for the thread to start and initialize message_loop_ + startup_data.event.Wait(); + + DCHECK(message_loop_); + return true; +} + +void Thread::Stop() { + if (!thread_was_started()) + return; + + // We should only be called on the same thread that started us. + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); + + // StopSoon may have already been called. + if (message_loop_) { + RefPtr<ThreadQuitTask> task = new ThreadQuitTask(); + message_loop_->PostTask(task.forget()); + } + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + // + // TODO(darin): Unfortunately, we need to keep message_loop_ around until + // the thread exits. Some consumers are abusing the API. Make them stop. + // + PlatformThread::Join(thread_); + + // The thread can't receive messages anymore. + message_loop_ = NULL; + + // The thread no longer needs to be joined. + startup_data_ = NULL; +} + +void Thread::StopSoon() { + if (!message_loop_) + return; + + // We should only be called on the same thread that started us. + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); + + // We had better have a message loop at this point! If we do not, then it + // most likely means that the thread terminated unexpectedly, probably due + // to someone calling Quit() on our message loop directly. + DCHECK(message_loop_); + + RefPtr<ThreadQuitTask> task = new ThreadQuitTask(); + message_loop_->PostTask(task.forget()); +} + +void Thread::ThreadMain() { + char aLocal; + profiler_register_thread(name_.c_str(), &aLocal); + mozilla::IOInterposer::RegisterCurrentThread(); + + // The message loop for this thread. + MessageLoop message_loop(startup_data_->options.message_loop_type, + NS_GetCurrentThread()); + + // Complete the initialization of our Thread object. + thread_id_ = PlatformThread::CurrentId(); + PlatformThread::SetName(name_.c_str()); + message_loop.set_thread_name(name_); + message_loop.set_hang_timeouts(startup_data_->options.transient_hang_timeout, + startup_data_->options.permanent_hang_timeout); + message_loop_ = &message_loop; + + // Let the thread do extra initialization. + // Let's do this before signaling we are started. + Init(); + + startup_data_->event.Signal(); + // startup_data_ can't be touched anymore since the starting thread is now + // unlocked. + + message_loop.Run(); + + // Let the thread do extra cleanup. + CleanUp(); + + // Assert that MessageLoop::Quit was called by ThreadQuitTask. + DCHECK(GetThreadWasQuitProperly()); + + mozilla::IOInterposer::UnregisterCurrentThread(); + profiler_unregister_thread(); + +#ifdef MOZ_TASK_TRACER + mozilla::tasktracer::FreeTraceInfo(); +#endif + + // We can't receive messages anymore. + message_loop_ = NULL; + thread_id_ = 0; +} + +} // namespace base diff --git a/ipc/chromium/src/base/thread.h b/ipc/chromium/src/base/thread.h new file mode 100644 index 0000000000..9e2a9bd185 --- /dev/null +++ b/ipc/chromium/src/base/thread.h @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 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 BASE_THREAD_H_ +#define BASE_THREAD_H_ + +#include <stdint.h> +#include <string> + +#include "base/message_loop.h" +#include "base/platform_thread.h" + +namespace base { + +// A simple thread abstraction that establishes a MessageLoop on a new thread. +// The consumer uses the MessageLoop of the thread to cause code to execute on +// the thread. When this object is destroyed the thread is terminated. All +// pending tasks queued on the thread's message loop will run to completion +// before the thread is terminated. +class Thread : PlatformThread::Delegate { + public: + struct Options { + // Specifies the type of message loop that will be allocated on the thread. + MessageLoop::Type message_loop_type; + + // Specifies the maximum stack size that the thread is allowed to use. + // This does not necessarily correspond to the thread's initial stack size. + // A value of 0 indicates that the default maximum should be used. + size_t stack_size; + + // Specifies the transient and permanent hang timeouts for background hang + // monitoring. A value of 0 indicates there is no timeout. + uint32_t transient_hang_timeout; + uint32_t permanent_hang_timeout; + + Options() + : message_loop_type(MessageLoop::TYPE_DEFAULT) + , stack_size(0) + , transient_hang_timeout(0) + , permanent_hang_timeout(0) {} + Options(MessageLoop::Type type, size_t size) + : message_loop_type(type) + , stack_size(size) + , transient_hang_timeout(0) + , permanent_hang_timeout(0) {} + }; + + // Constructor. + // name is a display string to identify the thread. + explicit Thread(const char *name); + + // Destroys the thread, stopping it if necessary. + // + // NOTE: If you are subclassing from Thread, and you wish for your CleanUp + // method to be called, then you need to call Stop() from your destructor. + // + virtual ~Thread(); + + // Starts the thread. Returns true if the thread was successfully started; + // otherwise, returns false. Upon successful return, the message_loop() + // getter will return non-null. + // + // Note: This function can't be called on Windows with the loader lock held; + // i.e. during a DllMain, global object construction or destruction, atexit() + // callback. + bool Start(); + + // Starts the thread. Behaves exactly like Start in addition to allow to + // override the default options. + // + // Note: This function can't be called on Windows with the loader lock held; + // i.e. during a DllMain, global object construction or destruction, atexit() + // callback. + bool StartWithOptions(const Options& options); + + // Signals the thread to exit and returns once the thread has exited. After + // this method returns, the Thread object is completely reset and may be used + // as if it were newly constructed (i.e., Start may be called again). + // + // Stop may be called multiple times and is simply ignored if the thread is + // already stopped. + // + // NOTE: This method is optional. It is not strictly necessary to call this + // method as the Thread's destructor will take care of stopping the thread if + // necessary. + // + void Stop(); + + // Signals the thread to exit in the near future. + // + // WARNING: This function is not meant to be commonly used. Use at your own + // risk. Calling this function will cause message_loop() to become invalid in + // the near future. This function was created to workaround a specific + // deadlock on Windows with printer worker thread. In any other case, Stop() + // should be used. + // + // StopSoon should not be called multiple times as it is risky to do so. It + // could cause a timing issue in message_loop() access. Call Stop() to reset + // the thread object once it is known that the thread has quit. + void StopSoon(); + + // Returns the message loop for this thread. Use the MessageLoop's + // PostTask methods to execute code on the thread. This only returns + // non-null after a successful call to Start. After Stop has been called, + // this will return NULL. + // + // NOTE: You must not call this MessageLoop's Quit method directly. Use + // the Thread's Stop method instead. + // + MessageLoop* message_loop() const { return message_loop_; } + + // Set the name of this thread (for display in debugger too). + const std::string &thread_name() { return name_; } + + // The native thread handle. + PlatformThreadHandle thread_handle() { return thread_; } + + // The thread ID. + PlatformThreadId thread_id() const { return thread_id_; } + + // Reset thread ID as current thread. + PlatformThreadId reset_thread_id() { + thread_id_ = PlatformThread::CurrentId(); + return thread_id_; + } + + // Returns true if the thread has been started, and not yet stopped. + // When a thread is running, the thread_id_ is non-zero. + bool IsRunning() const { return thread_id_ != 0; } + + protected: + // Called just prior to starting the message loop + virtual void Init() {} + + // Called just after the message loop ends + virtual void CleanUp() {} + + static void SetThreadWasQuitProperly(bool flag); + static bool GetThreadWasQuitProperly(); + + private: + // PlatformThread::Delegate methods: + virtual void ThreadMain(); + + // We piggy-back on the startup_data_ member to know if we successfully + // started the thread. This way we know that we need to call Join. + bool thread_was_started() const { return startup_data_ != NULL; } + + // Used to pass data to ThreadMain. + struct StartupData; + StartupData* startup_data_; + + // The thread's handle. + PlatformThreadHandle thread_; + + // The thread's message loop. Valid only while the thread is alive. Set + // by the created thread. + MessageLoop* message_loop_; + + // Our thread's ID. + PlatformThreadId thread_id_; + + // The name of the thread. Used for debugging purposes. + std::string name_; + + friend class ThreadQuitTask; + + DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +} // namespace base + +#endif // BASE_THREAD_H_ diff --git a/ipc/chromium/src/base/thread_local.h b/ipc/chromium/src/base/thread_local.h new file mode 100644 index 0000000000..d5c95a8bc4 --- /dev/null +++ b/ipc/chromium/src/base/thread_local.h @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// WARNING: Thread local storage is a bit tricky to get right. Please make +// sure that this is really the proper solution for what you're trying to +// achieve. Don't prematurely optimize, most likely you can just use a Lock. +// +// These classes implement a warpper around the platform's TLS storage +// mechanism. On construction, they will allocate a TLS slot, and free the +// TLS slot on destruction. No memory management (creation or destruction) is +// handled. This means for uses of ThreadLocalPointer, you must correctly +// manage the memory yourself, these classes will not destroy the pointer for +// you. There are no at-thread-exit actions taken by these classes. +// +// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or +// destruction, so memory management must be handled elsewhere. The first call +// to Get() on a thread will return NULL. You can update the pointer with a +// call to Set(). +// +// ThreadLocalBoolean wraps a bool. It will default to false if it has never +// been set otherwise with Set(). +// +// Thread Safety: An instance of ThreadLocalStorage is completely thread safe +// once it has been created. If you want to dynamically create an instance, +// you must of course properly deal with safety and race conditions. This +// means a function-level static initializer is generally inappropiate. +// +// Example usage: +// // My class is logically attached to a single thread. We cache a pointer +// // on the thread it was created on, so we can implement current(). +// MyClass::MyClass() { +// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL); +// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this); +// } +// +// MyClass::~MyClass() { +// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL); +// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL); +// } +// +// // Return the current MyClass associated with the calling thread, can be +// // NULL if there isn't a MyClass associated. +// MyClass* MyClass::current() { +// return Singleton<ThreadLocalPointer<MyClass> >::get()->Get(); +// } + +#ifndef BASE_THREAD_LOCAL_H_ +#define BASE_THREAD_LOCAL_H_ + +#include "base/basictypes.h" + +#if defined(OS_POSIX) +#include <pthread.h> +#endif + +namespace base { + +// Helper functions that abstract the cross-platform APIs. Do not use directly. +struct ThreadLocalPlatform { +#if defined(OS_WIN) + typedef int SlotType; +#elif defined(OS_POSIX) + typedef pthread_key_t SlotType; +#endif + + static void AllocateSlot(SlotType& slot); + static void FreeSlot(SlotType& slot); + static void* GetValueFromSlot(SlotType& slot); + static void SetValueInSlot(SlotType& slot, void* value); +}; + +template <typename Type> +class ThreadLocalPointer { + public: + ThreadLocalPointer() : slot_() { + ThreadLocalPlatform::AllocateSlot(slot_); + } + + ~ThreadLocalPointer() { + ThreadLocalPlatform::FreeSlot(slot_); + } + + Type* Get() { + return static_cast<Type*>(ThreadLocalPlatform::GetValueFromSlot(slot_)); + } + + void Set(Type* ptr) { + ThreadLocalPlatform::SetValueInSlot(slot_, ptr); + } + + private: + typedef ThreadLocalPlatform::SlotType SlotType; + + SlotType slot_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>); +}; + +class ThreadLocalBoolean { + public: + ThreadLocalBoolean() { } + ~ThreadLocalBoolean() { } + + bool Get() { + return tlp_.Get() != NULL; + } + + void Set(bool val) { + uintptr_t intVal = val ? 1 : 0; + tlp_.Set(reinterpret_cast<void*>(intVal)); + } + + private: + ThreadLocalPointer<void> tlp_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean); +}; + +} // namespace base + +#endif // BASE_THREAD_LOCAL_H_ diff --git a/ipc/chromium/src/base/thread_local_posix.cc b/ipc/chromium/src/base/thread_local_posix.cc new file mode 100644 index 0000000000..050a6e4b92 --- /dev/null +++ b/ipc/chromium/src/base/thread_local_posix.cc @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/thread_local.h" + +#include <pthread.h> + +#include "base/logging.h" + +namespace base { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + int error = pthread_key_create(&slot, NULL); + CHECK(error == 0); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType& slot) { + int error = pthread_key_delete(slot); + DCHECK(error == 0); +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { + return pthread_getspecific(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) { + int error = pthread_setspecific(slot, value); + CHECK(error == 0); +} + +} // namespace base diff --git a/ipc/chromium/src/base/thread_local_storage.h b/ipc/chromium/src/base/thread_local_storage.h new file mode 100644 index 0000000000..58d4e63560 --- /dev/null +++ b/ipc/chromium/src/base/thread_local_storage.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_THREAD_LOCAL_STORAGE_H_ +#define BASE_THREAD_LOCAL_STORAGE_H_ + +#include "base/basictypes.h" + +#if defined(OS_POSIX) +#include <pthread.h> +#endif + +// Wrapper for thread local storage. This class doesn't do much except provide +// an API for portability. +class ThreadLocalStorage { + public: + + // Prototype for the TLS destructor function, which can be optionally used to + // cleanup thread local storage on thread exit. 'value' is the data that is + // stored in thread local storage. + typedef void (*TLSDestructorFunc)(void* value); + + // A key representing one value stored in TLS. + class Slot { + public: + explicit Slot(TLSDestructorFunc destructor = NULL); + + // This constructor should be used for statics. + // It returns an uninitialized Slot. + explicit Slot(base::LinkerInitialized x) {} + + // Set up the TLS slot. Called by the constructor. + // 'destructor' is a pointer to a function to perform per-thread cleanup of + // this object. If set to NULL, no cleanup is done for this TLS slot. + // Returns false on error. + bool Initialize(TLSDestructorFunc destructor); + + // Free a previously allocated TLS 'slot'. + // If a destructor was set for this slot, removes + // the destructor so that remaining threads exiting + // will not free data. + void Free(); + + // Get the thread-local value stored in slot 'slot'. + // Values are guaranteed to initially be zero. + void* Get() const; + + // Set the thread-local value stored in slot 'slot' to + // value 'value'. + void Set(void* value); + + bool initialized() const { return initialized_; } + + private: + // The internals of this struct should be considered private. + bool initialized_; +#if defined(OS_WIN) + int slot_; +#elif defined(OS_POSIX) + pthread_key_t key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(Slot); + }; + +#if defined(OS_WIN) + // Function called when on thread exit to call TLS + // destructor functions. This function is used internally. + static void ThreadExit(); + + private: + // Function to lazily initialize our thread local storage. + static void **Initialize(); + + private: + // The maximum number of 'slots' in our thread local storage stack. + // For now, this is fixed. We could either increase statically, or + // we could make it dynamic in the future. + static const int kThreadLocalStorageSize = 64; + + static long tls_key_; + static long tls_max_; + static TLSDestructorFunc tls_destructors_[kThreadLocalStorageSize]; +#endif // OS_WIN + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); +}; + +// Temporary backwards-compatible name. +// TODO(evanm): replace all usage of TLSSlot. +typedef ThreadLocalStorage::Slot TLSSlot; + +#endif // BASE_THREAD_LOCAL_STORAGE_H_ diff --git a/ipc/chromium/src/base/thread_local_storage_posix.cc b/ipc/chromium/src/base/thread_local_storage_posix.cc new file mode 100644 index 0000000000..6f8367b661 --- /dev/null +++ b/ipc/chromium/src/base/thread_local_storage_posix.cc @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/thread_local_storage.h" + +#include "base/logging.h" + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) + : initialized_(false) { + Initialize(destructor); +} + +bool ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { + DCHECK(!initialized_); + int error = pthread_key_create(&key_, destructor); + if (error) { + NOTREACHED(); + return false; + } + + initialized_ = true; + return true; +} + +void ThreadLocalStorage::Slot::Free() { + DCHECK(initialized_); + int error = pthread_key_delete(key_); + if (error) + NOTREACHED(); + initialized_ = false; +} + +void* ThreadLocalStorage::Slot::Get() const { + DCHECK(initialized_); + return pthread_getspecific(key_); +} + +void ThreadLocalStorage::Slot::Set(void* value) { + DCHECK(initialized_); + int error = pthread_setspecific(key_, value); + if (error) + NOTREACHED(); +} diff --git a/ipc/chromium/src/base/thread_local_storage_win.cc b/ipc/chromium/src/base/thread_local_storage_win.cc new file mode 100644 index 0000000000..edd7a05401 --- /dev/null +++ b/ipc/chromium/src/base/thread_local_storage_win.cc @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/thread_local_storage.h" + +#include <windows.h> + +#include "base/logging.h" + +// In order to make TLS destructors work, we need to keep function +// pointers to the destructor for each TLS that we allocate. +// We make this work by allocating a single OS-level TLS, which +// contains an array of slots for the application to use. In +// parallel, we also allocate an array of destructors, which we +// keep track of and call when threads terminate. + +// tls_key_ is the one native TLS that we use. It stores our +// table. +long ThreadLocalStorage::tls_key_ = TLS_OUT_OF_INDEXES; + +// tls_max_ is the high-water-mark of allocated thread local storage. +// We intentionally skip 0 so that it is not confused with an +// unallocated TLS slot. +long ThreadLocalStorage::tls_max_ = 1; + +// An array of destructor function pointers for the slots. If +// a slot has a destructor, it will be stored in its corresponding +// entry in this array. +ThreadLocalStorage::TLSDestructorFunc + ThreadLocalStorage::tls_destructors_[kThreadLocalStorageSize]; + +void** ThreadLocalStorage::Initialize() { + if (tls_key_ == TLS_OUT_OF_INDEXES) { + long value = TlsAlloc(); + DCHECK(value != TLS_OUT_OF_INDEXES); + + // Atomically test-and-set the tls_key. If the key is TLS_OUT_OF_INDEXES, + // go ahead and set it. Otherwise, do nothing, as another + // thread already did our dirty work. + if (InterlockedCompareExchange(&tls_key_, value, TLS_OUT_OF_INDEXES) != + TLS_OUT_OF_INDEXES) { + // We've been shortcut. Another thread replaced tls_key_ first so we need + // to destroy our index and use the one the other thread got first. + TlsFree(value); + } + } + DCHECK(TlsGetValue(tls_key_) == NULL); + + // Create an array to store our data. + void** tls_data = new void*[kThreadLocalStorageSize]; + memset(tls_data, 0, sizeof(void*[kThreadLocalStorageSize])); + TlsSetValue(tls_key_, tls_data); + return tls_data; +} + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) + : initialized_(false) { + Initialize(destructor); +} + +bool ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { + if (tls_key_ == TLS_OUT_OF_INDEXES || !TlsGetValue(tls_key_)) + ThreadLocalStorage::Initialize(); + + // Grab a new slot. + slot_ = InterlockedIncrement(&tls_max_) - 1; + if (slot_ >= kThreadLocalStorageSize) { + NOTREACHED(); + return false; + } + + // Setup our destructor. + tls_destructors_[slot_] = destructor; + initialized_ = true; + return true; +} + +void ThreadLocalStorage::Slot::Free() { + // At this time, we don't reclaim old indices for TLS slots. + // So all we need to do is wipe the destructor. + tls_destructors_[slot_] = NULL; + initialized_ = false; +} + +void* ThreadLocalStorage::Slot::Get() const { + void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); + if (!tls_data) + tls_data = ThreadLocalStorage::Initialize(); + DCHECK(slot_ >= 0 && slot_ < kThreadLocalStorageSize); + return tls_data[slot_]; +} + +void ThreadLocalStorage::Slot::Set(void* value) { + void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); + if (!tls_data) + tls_data = ThreadLocalStorage::Initialize(); + DCHECK(slot_ >= 0 && slot_ < kThreadLocalStorageSize); + tls_data[slot_] = value; +} + +void ThreadLocalStorage::ThreadExit() { + if (tls_key_ == TLS_OUT_OF_INDEXES) + return; + + void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); + + // Maybe we have never initialized TLS for this thread. + if (!tls_data) + return; + + for (int slot = 0; slot < tls_max_; slot++) { + if (tls_destructors_[slot] != NULL) { + void* value = tls_data[slot]; + tls_destructors_[slot](value); + } + } + + delete[] tls_data; + + // In case there are other "onexit" handlers... + TlsSetValue(tls_key_, NULL); +} + +// Thread Termination Callbacks. +// Windows doesn't support a per-thread destructor with its +// TLS primitives. So, we build it manually by inserting a +// function to be called on each thread's exit. +// This magic is from http://www.codeproject.com/threads/tls.asp +// and it works for VC++ 7.0 and later. + +#ifdef _WIN64 + +// This makes the linker create the TLS directory if it's not already +// there. (e.g. if __declspec(thread) is not used). +#pragma comment(linker, "/INCLUDE:_tls_used") + +#else // _WIN64 + +// This makes the linker create the TLS directory if it's not already +// there. (e.g. if __declspec(thread) is not used). +#pragma comment(linker, "/INCLUDE:__tls_used") + +#endif // _WIN64 + +// Static callback function to call with each thread termination. +void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) +{ + // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+ + // and on W2K and W2K3. So don't assume it is sent. + if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason) + ThreadLocalStorage::ThreadExit(); +} + +// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are +// called automatically by the OS loader code (not the CRT) when the module is +// loaded and on thread creation. They are NOT called if the module has been +// loaded by a LoadLibrary() call. It must have implicitly been loaded at +// process startup. +// By implicitly loaded, I mean that it is directly referenced by the main EXE +// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being +// implicitly loaded. +// +// See VC\crt\src\tlssup.c for reference. +#ifdef _WIN64 + +// .CRT section is merged with .rdata on x64 so it must be constant data. +#pragma const_seg(".CRT$XLB") +// When defining a const variable, it must have external linkage to be sure the +// linker doesn't discard it. If this value is discarded, the OnThreadExit +// function will never be called. +extern const PIMAGE_TLS_CALLBACK p_thread_callback; +const PIMAGE_TLS_CALLBACK p_thread_callback = OnThreadExit; + +// Reset the default section. +#pragma const_seg() + +#else // _WIN64 + +#pragma data_seg(".CRT$XLB") +PIMAGE_TLS_CALLBACK p_thread_callback = OnThreadExit; + +// Reset the default section. +#pragma data_seg() + +#endif // _WIN64 diff --git a/ipc/chromium/src/base/thread_local_win.cc b/ipc/chromium/src/base/thread_local_win.cc new file mode 100644 index 0000000000..3f8c465d05 --- /dev/null +++ b/ipc/chromium/src/base/thread_local_win.cc @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/thread_local.h" + +#include <windows.h> + +#include "base/logging.h" + +namespace base { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + slot = TlsAlloc(); + CHECK(slot != TLS_OUT_OF_INDEXES); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType& slot) { + if (!TlsFree(slot)) { + NOTREACHED() << "Failed to deallocate tls slot with TlsFree()."; + } +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { + return TlsGetValue(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) { + if (!TlsSetValue(slot, value)) { + CHECK(false) << "Failed to TlsSetValue()."; + } +} + +} // namespace base diff --git a/ipc/chromium/src/base/time.cc b/ipc/chromium/src/base/time.cc new file mode 100644 index 0000000000..dca9c83d93 --- /dev/null +++ b/ipc/chromium/src/base/time.cc @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/time.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "prtime.h" + +#include "base/logging.h" + +namespace base { + +// TimeDelta ------------------------------------------------------------------ + +int TimeDelta::InDays() const { + return static_cast<int>(delta_ / Time::kMicrosecondsPerDay); +} + +int TimeDelta::InHours() const { + return static_cast<int>(delta_ / Time::kMicrosecondsPerHour); +} + +int TimeDelta::InMinutes() const { + return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute); +} + +double TimeDelta::InSecondsF() const { + return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond; +} + +int64_t TimeDelta::InSeconds() const { + return delta_ / Time::kMicrosecondsPerSecond; +} + +double TimeDelta::InMillisecondsF() const { + return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond; +} + +int64_t TimeDelta::InMilliseconds() const { + return delta_ / Time::kMicrosecondsPerMillisecond; +} + +int64_t TimeDelta::InMicroseconds() const { + return delta_; +} + +// Time ----------------------------------------------------------------------- + +// static +Time Time::FromTimeT(time_t tt) { + if (tt == 0) + return Time(); // Preserve 0 so we can tell it doesn't exist. + return Time((tt * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset); +} + +time_t Time::ToTimeT() const { + if (us_ == 0) + return 0; // Preserve 0 so we can tell it doesn't exist. + return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond; +} + +// static +Time Time::FromDoubleT(double dt) { + return Time((dt * static_cast<double>(kMicrosecondsPerSecond)) + + kTimeTToMicrosecondsOffset); +} + +double Time::ToDoubleT() const { + if (us_ == 0) + return 0; // Preserve 0 so we can tell it doesn't exist. + return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) / + static_cast<double>(kMicrosecondsPerSecond)); +} + +Time Time::LocalMidnight() const { + Exploded exploded; + LocalExplode(&exploded); + exploded.hour = 0; + exploded.minute = 0; + exploded.second = 0; + exploded.millisecond = 0; + return FromLocalExploded(exploded); +} + +// static +bool Time::FromString(const wchar_t* time_string, Time* parsed_time) { + DCHECK((time_string != NULL) && (parsed_time != NULL)); + std::string ascii_time_string = SysWideToUTF8(time_string); + if (ascii_time_string.length() == 0) + return false; + PRTime result_time = 0; + PRStatus result = PR_ParseTimeString(ascii_time_string.c_str(), PR_FALSE, + &result_time); + if (PR_SUCCESS != result) + return false; + result_time += kTimeTToMicrosecondsOffset; + *parsed_time = Time(result_time); + return true; +} + +} // namespace base diff --git a/ipc/chromium/src/base/time.h b/ipc/chromium/src/base/time.h new file mode 100644 index 0000000000..55bc7b4512 --- /dev/null +++ b/ipc/chromium/src/base/time.h @@ -0,0 +1,486 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// Time represents an absolute point in time, internally represented as +// microseconds (s/1,000,000) since a platform-dependent epoch. Each +// platform's epoch, along with other system-dependent clock interface +// routines, is defined in time_PLATFORM.cc. +// +// TimeDelta represents a duration of time, internally represented in +// microseconds. +// +// TimeTicks represents an abstract time that is always incrementing for use +// in measuring time durations. It is internally represented in microseconds. +// It can not be converted to a human-readable time, but is guaranteed not to +// decrease (if the user changes the computer clock, Time::Now() may actually +// decrease or jump). +// +// These classes are represented as only a 64-bit value, so they can be +// efficiently passed by value. + +#ifndef BASE_TIME_H_ +#define BASE_TIME_H_ + +#include <time.h> + +#include "base/basictypes.h" + +#if defined(OS_WIN) +// For FILETIME in FromFileTime, until it moves to a new converter class. +// See TODO(iyengar) below. +#include <windows.h> +#endif + +namespace base { + +class Time; +class TimeTicks; + +// This unit test does a lot of manual time manipulation. +class PageLoadTrackerUnitTest; + +// TimeDelta ------------------------------------------------------------------ + +class TimeDelta { + public: + TimeDelta() : delta_(0) { + } + + // Converts units of time to TimeDeltas. + static TimeDelta FromDays(int64_t days); + static TimeDelta FromHours(int64_t hours); + static TimeDelta FromMinutes(int64_t minutes); + static TimeDelta FromSeconds(int64_t secs); + static TimeDelta FromMilliseconds(int64_t ms); + static TimeDelta FromMicroseconds(int64_t us); + + // Returns the internal numeric value of the TimeDelta object. Please don't + // use this and do arithmetic on it, as it is more error prone than using the + // provided operators. + int64_t ToInternalValue() const { + return delta_; + } + + // Returns the time delta in some unit. The F versions return a floating + // point value, the "regular" versions return a rounded-down value. + int InDays() const; + int InHours() const; + int InMinutes() const; + double InSecondsF() const; + int64_t InSeconds() const; + double InMillisecondsF() const; + int64_t InMilliseconds() const; + int64_t InMicroseconds() const; + + TimeDelta& operator=(TimeDelta other) { + delta_ = other.delta_; + return *this; + } + + // Computations with other deltas. + TimeDelta operator+(TimeDelta other) const { + return TimeDelta(delta_ + other.delta_); + } + TimeDelta operator-(TimeDelta other) const { + return TimeDelta(delta_ - other.delta_); + } + + TimeDelta& operator+=(TimeDelta other) { + delta_ += other.delta_; + return *this; + } + TimeDelta& operator-=(TimeDelta other) { + delta_ -= other.delta_; + return *this; + } + TimeDelta operator-() const { + return TimeDelta(-delta_); + } + + // Computations with ints, note that we only allow multiplicative operations + // with ints, and additive operations with other deltas. + TimeDelta operator*(int64_t a) const { + return TimeDelta(delta_ * a); + } + TimeDelta operator/(int64_t a) const { + return TimeDelta(delta_ / a); + } + TimeDelta& operator*=(int64_t a) { + delta_ *= a; + return *this; + } + TimeDelta& operator/=(int64_t a) { + delta_ /= a; + return *this; + } + int64_t operator/(TimeDelta a) const { + return delta_ / a.delta_; + } + + // Defined below because it depends on the definition of the other classes. + Time operator+(Time t) const; + TimeTicks operator+(TimeTicks t) const; + + // Comparison operators. + bool operator==(TimeDelta other) const { + return delta_ == other.delta_; + } + bool operator!=(TimeDelta other) const { + return delta_ != other.delta_; + } + bool operator<(TimeDelta other) const { + return delta_ < other.delta_; + } + bool operator<=(TimeDelta other) const { + return delta_ <= other.delta_; + } + bool operator>(TimeDelta other) const { + return delta_ > other.delta_; + } + bool operator>=(TimeDelta other) const { + return delta_ >= other.delta_; + } + + private: + friend class Time; + friend class TimeTicks; + friend TimeDelta operator*(int64_t a, TimeDelta td); + + // Constructs a delta given the duration in microseconds. This is private + // to avoid confusion by callers with an integer constructor. Use + // FromSeconds, FromMilliseconds, etc. instead. + explicit TimeDelta(int64_t delta_us) : delta_(delta_us) { + } + + // Delta in microseconds. + int64_t delta_; +}; + +inline TimeDelta operator*(int64_t a, TimeDelta td) { + return TimeDelta(a * td.delta_); +} + +// Time ----------------------------------------------------------------------- + +// Represents a wall clock time. +class Time { + public: + static const int64_t kMillisecondsPerSecond = 1000; + static const int64_t kMicrosecondsPerMillisecond = 1000; + static const int64_t kMicrosecondsPerSecond = kMicrosecondsPerMillisecond * + kMillisecondsPerSecond; + static const int64_t kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60; + static const int64_t kMicrosecondsPerHour = kMicrosecondsPerMinute * 60; + static const int64_t kMicrosecondsPerDay = kMicrosecondsPerHour * 24; + static const int64_t kMicrosecondsPerWeek = kMicrosecondsPerDay * 7; + static const int64_t kNanosecondsPerMicrosecond = 1000; + static const int64_t kNanosecondsPerSecond = kNanosecondsPerMicrosecond * + kMicrosecondsPerSecond; + + // Represents an exploded time that can be formatted nicely. This is kind of + // like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few + // additions and changes to prevent errors. + struct Exploded { + int year; // Four digit year "2007" + signed char month; // 1-based month (values 1 = January, etc.) + signed char day_of_week; // 0-based day of week (0 = Sunday, etc.) + signed char day_of_month; // 1-based day of month (1-31) + signed char hour; // Hour within the current day (0-23) + signed char minute; // Minute within the current hour (0-59) + signed char second; // Second within the current minute (0-59 plus + // leap seconds which may take it up to 60). + int millisecond; // Milliseconds within the current second (0-999) + }; + + // Contains the NULL time. Use Time::Now() to get the current time. + explicit Time() : us_(0) { + } + + // Returns true if the time object has not been initialized. + bool is_null() const { + return us_ == 0; + } + + // Returns the current time. Watch out, the system might adjust its clock + // in which case time will actually go backwards. We don't guarantee that + // times are increasing, or that two calls to Now() won't be the same. + static Time Now(); + + // Returns the current time. Same as Now() except that this function always + // uses system time so that there are no discrepancies between the returned + // time and system time even on virtual environments including our test bot. + // For timing sensitive unittests, this function should be used. + static Time NowFromSystemTime(); + + // Converts to/from time_t in UTC and a Time class. + // TODO(brettw) this should be removed once everybody starts using the |Time| + // class. + static Time FromTimeT(time_t tt); + time_t ToTimeT() const; + + // Converts time to/from a double which is the number of seconds since epoch + // (Jan 1, 1970). Webkit uses this format to represent time. + static Time FromDoubleT(double dt); + double ToDoubleT() const; + + +#if defined(OS_WIN) + static Time FromFileTime(FILETIME ft); + FILETIME ToFileTime() const; +#endif + + // Converts an exploded structure representing either the local time or UTC + // into a Time class. + static Time FromUTCExploded(const Exploded& exploded) { + return FromExploded(false, exploded); + } + static Time FromLocalExploded(const Exploded& exploded) { + return FromExploded(true, exploded); + } + + // Converts an integer value representing Time to a class. This is used + // when deserializing a |Time| structure, using a value known to be + // compatible. It is not provided as a constructor because the integer type + // may be unclear from the perspective of a caller. + static Time FromInternalValue(int64_t us) { + return Time(us); + } + + // Converts a string representation of time to a Time object. + // An example of a time string which is converted is as below:- + // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified + // in the input string, we assume local time. + // TODO(iyengar) Move the FromString/FromTimeT/ToTimeT/FromFileTime to + // a new time converter class. + static bool FromString(const wchar_t* time_string, Time* parsed_time); + + // For serializing, use FromInternalValue to reconstitute. Please don't use + // this and do arithmetic on it, as it is more error prone than using the + // provided operators. + int64_t ToInternalValue() const { + return us_; + } + + // Fills the given exploded structure with either the local time or UTC from + // this time structure (containing UTC). + void UTCExplode(Exploded* exploded) const { + return Explode(false, exploded); + } + void LocalExplode(Exploded* exploded) const { + return Explode(true, exploded); + } + + // Rounds this time down to the nearest day in local time. It will represent + // midnight on that day. + Time LocalMidnight() const; + + Time& operator=(Time other) { + us_ = other.us_; + return *this; + } + + // Compute the difference between two times. + TimeDelta operator-(Time other) const { + return TimeDelta(us_ - other.us_); + } + + // Modify by some time delta. + Time& operator+=(TimeDelta delta) { + us_ += delta.delta_; + return *this; + } + Time& operator-=(TimeDelta delta) { + us_ -= delta.delta_; + return *this; + } + + // Return a new time modified by some delta. + Time operator+(TimeDelta delta) const { + return Time(us_ + delta.delta_); + } + Time operator-(TimeDelta delta) const { + return Time(us_ - delta.delta_); + } + + // Comparison operators + bool operator==(Time other) const { + return us_ == other.us_; + } + bool operator!=(Time other) const { + return us_ != other.us_; + } + bool operator<(Time other) const { + return us_ < other.us_; + } + bool operator<=(Time other) const { + return us_ <= other.us_; + } + bool operator>(Time other) const { + return us_ > other.us_; + } + bool operator>=(Time other) const { + return us_ >= other.us_; + } + + private: + friend class TimeDelta; + + // Explodes the given time to either local time |is_local = true| or UTC + // |is_local = false|. + void Explode(bool is_local, Exploded* exploded) const; + + // Unexplodes a given time assuming the source is either local time + // |is_local = true| or UTC |is_local = false|. + static Time FromExploded(bool is_local, const Exploded& exploded); + + explicit Time(int64_t us) : us_(us) { + } + + // The representation of Jan 1, 1970 UTC in microseconds since the + // platform-dependent epoch. + static const int64_t kTimeTToMicrosecondsOffset; + + // Time in microseconds in UTC. + int64_t us_; +}; + +inline Time TimeDelta::operator+(Time t) const { + return Time(t.us_ + delta_); +} + +// Inline the TimeDelta factory methods, for fast TimeDelta construction. + +// static +inline TimeDelta TimeDelta::FromDays(int64_t days) { + return TimeDelta(days * Time::kMicrosecondsPerDay); +} + +// static +inline TimeDelta TimeDelta::FromHours(int64_t hours) { + return TimeDelta(hours * Time::kMicrosecondsPerHour); +} + +// static +inline TimeDelta TimeDelta::FromMinutes(int64_t minutes) { + return TimeDelta(minutes * Time::kMicrosecondsPerMinute); +} + +// static +inline TimeDelta TimeDelta::FromSeconds(int64_t secs) { + return TimeDelta(secs * Time::kMicrosecondsPerSecond); +} + +// static +inline TimeDelta TimeDelta::FromMilliseconds(int64_t ms) { + return TimeDelta(ms * Time::kMicrosecondsPerMillisecond); +} + +// static +inline TimeDelta TimeDelta::FromMicroseconds(int64_t us) { + return TimeDelta(us); +} + +// TimeTicks ------------------------------------------------------------------ + +class TimeTicks { + public: + TimeTicks() : ticks_(0) { + } + + // Platform-dependent tick count representing "right now." + // The resolution of this clock is ~1-15ms. Resolution varies depending + // on hardware/operating system configuration. + static TimeTicks Now(); + + // Returns a platform-dependent high-resolution tick count. Implementation + // is hardware dependent and may or may not return sub-millisecond + // resolution. THIS CALL IS GENERALLY MUCH MORE EXPENSIVE THAN Now() AND + // SHOULD ONLY BE USED WHEN IT IS REALLY NEEDED. + static TimeTicks HighResNow(); + + // Returns true if this object has not been initialized. + bool is_null() const { + return ticks_ == 0; + } + + // Returns the internal numeric value of the TimeTicks object. + int64_t ToInternalValue() const { + return ticks_; + } + + TimeTicks& operator=(TimeTicks other) { + ticks_ = other.ticks_; + return *this; + } + + // Compute the difference between two times. + TimeDelta operator-(TimeTicks other) const { + return TimeDelta(ticks_ - other.ticks_); + } + + // Modify by some time delta. + TimeTicks& operator+=(TimeDelta delta) { + ticks_ += delta.delta_; + return *this; + } + TimeTicks& operator-=(TimeDelta delta) { + ticks_ -= delta.delta_; + return *this; + } + + // Return a new TimeTicks modified by some delta. + TimeTicks operator+(TimeDelta delta) const { + return TimeTicks(ticks_ + delta.delta_); + } + TimeTicks operator-(TimeDelta delta) const { + return TimeTicks(ticks_ - delta.delta_); + } + + // Comparison operators + bool operator==(TimeTicks other) const { + return ticks_ == other.ticks_; + } + bool operator!=(TimeTicks other) const { + return ticks_ != other.ticks_; + } + bool operator<(TimeTicks other) const { + return ticks_ < other.ticks_; + } + bool operator<=(TimeTicks other) const { + return ticks_ <= other.ticks_; + } + bool operator>(TimeTicks other) const { + return ticks_ > other.ticks_; + } + bool operator>=(TimeTicks other) const { + return ticks_ >= other.ticks_; + } + + protected: + friend class TimeDelta; + friend class PageLoadTrackerUnitTest; + + // Please use Now() to create a new object. This is for internal use + // and testing. Ticks is in microseconds. + explicit TimeTicks(int64_t ticks) : ticks_(ticks) { + } + + // Tick count in microseconds. + int64_t ticks_; + +#if defined(OS_WIN) + typedef DWORD (*TickFunctionType)(void); + static TickFunctionType SetMockTickFunction(TickFunctionType ticker); +#endif +}; + +inline TimeTicks TimeDelta::operator+(TimeTicks t) const { + return TimeTicks(t.ticks_ + delta_); +} + +} // namespace base + +#endif // BASE_TIME_H_ diff --git a/ipc/chromium/src/base/time_mac.cc b/ipc/chromium/src/base/time_mac.cc new file mode 100644 index 0000000000..a5a9a2a6eb --- /dev/null +++ b/ipc/chromium/src/base/time_mac.cc @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 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 "base/time.h" + +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFTimeZone.h> +#include <mach/mach_time.h> +#include <sys/time.h> +#include <time.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/scoped_cftyperef.h" + +namespace base { + +// The Time routines in this file use Mach and CoreFoundation APIs, since the +// POSIX definition of time_t in Mac OS X wraps around after 2038--and +// there are already cookie expiration dates, etc., past that time out in +// the field. Using CFDate prevents that problem, and using mach_absolute_time +// for TimeTicks gives us nice high-resolution interval timing. + +// Time ----------------------------------------------------------------------- + +// The internal representation of Time uses a 64-bit microsecond count +// from 1970-01-01 00:00:00 UTC. Core Foundation uses a double second count +// since 2001-01-01 00:00:00 UTC. + +// Some functions in time.cc use time_t directly, so we provide a zero offset +// for them. The epoch is 1970-01-01 00:00:00 UTC. +// static +const int64_t Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0); + +// static +Time Time::Now() { + CFAbsoluteTime now = + CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970; + return Time(static_cast<int64_t>(now * kMicrosecondsPerSecond)); +} + +// static +Time Time::NowFromSystemTime() { + // Just use Now() because Now() returns the system time. + return Now(); +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + CFGregorianDate date; + date.second = exploded.second + + exploded.millisecond / static_cast<double>(kMillisecondsPerSecond); + date.minute = exploded.minute; + date.hour = exploded.hour; + date.day = exploded.day_of_month; + date.month = exploded.month; + date.year = exploded.year; + + scoped_cftyperef<CFTimeZoneRef> + time_zone(is_local ? CFTimeZoneCopySystem() : NULL); + CFAbsoluteTime seconds = CFGregorianDateGetAbsoluteTime(date, time_zone) + + kCFAbsoluteTimeIntervalSince1970; + return Time(static_cast<int64_t>(seconds * kMicrosecondsPerSecond)); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + CFAbsoluteTime seconds = + (static_cast<double>(us_) / kMicrosecondsPerSecond) - + kCFAbsoluteTimeIntervalSince1970; + + scoped_cftyperef<CFTimeZoneRef> + time_zone(is_local ? CFTimeZoneCopySystem() : NULL); + CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(seconds, time_zone); + + exploded->year = date.year; + exploded->month = date.month; + exploded->day_of_month = date.day; + exploded->hour = date.hour; + exploded->minute = date.minute; + exploded->second = date.second; + exploded->millisecond = + static_cast<int>(date.second * kMillisecondsPerSecond) % + kMillisecondsPerSecond; +} + +// TimeTicks ------------------------------------------------------------------ + +// static +TimeTicks TimeTicks::Now() { + uint64_t absolute_micro; + + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + // Zero-initialization of statics guarantees that denom will be 0 before + // calling mach_timebase_info. mach_timebase_info will never set denom to + // 0 as that would be invalid, so the zero-check can be used to determine + // whether mach_timebase_info has already been called. This is + // recommended by Apple's QA1398. + kern_return_t kr = mach_timebase_info(&timebase_info); + DCHECK(kr == KERN_SUCCESS); + } + + // mach_absolute_time is it when it comes to ticks on the Mac. Other calls + // with less precision (such as TickCount) just call through to + // mach_absolute_time. + + // timebase_info converts absolute time tick units into nanoseconds. Convert + // to microseconds up front to stave off overflows. + absolute_micro = mach_absolute_time() / Time::kNanosecondsPerMicrosecond * + timebase_info.numer / timebase_info.denom; + + // Don't bother with the rollover handling that the Windows version does. + // With numer and denom = 1 (the expected case), the 64-bit absolute time + // reported in nanoseconds is enough to last nearly 585 years. + + return TimeTicks(absolute_micro); +} + +// static +TimeTicks TimeTicks::HighResNow() { + return Now(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/time_posix.cc b/ipc/chromium/src/base/time_posix.cc new file mode 100644 index 0000000000..2eb76a9894 --- /dev/null +++ b/ipc/chromium/src/base/time_posix.cc @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/time.h" + +#ifdef OS_MACOSX +#include <mach/mach_time.h> +#endif +#include <sys/time.h> +#if defined(ANDROID) && !defined(__LP64__) +#include <time64.h> +#else +#include <time.h> +#endif +#if defined(ANDROID) || defined(OS_POSIX) +#include <unistd.h> +#endif + +#include <limits> + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace base { + +// The Time routines in this file use standard POSIX routines, or almost- +// standard routines in the case of timegm. We need to use a Mach-specific +// function for TimeTicks::Now() on Mac OS X. + +// Time ----------------------------------------------------------------------- + +// Some functions in time.cc use time_t directly, so we provide a zero offset +// for them. The epoch is 1970-01-01 00:00:00 UTC. +// static +const int64_t Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0); + +// static +Time Time::Now() { + struct timeval tv; + struct timezone tz = { 0, 0 }; // UTC + if (gettimeofday(&tv, &tz) != 0) { + DCHECK(0) << "Could not determine time of day"; + } + // Combine seconds and microseconds in a 64-bit field containing microseconds + // since the epoch. That's enough for nearly 600 centuries. + return Time(tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec); +} + +// static +Time Time::NowFromSystemTime() { + // Just use Now() because Now() returns the system time. + return Now(); +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + struct tm timestruct; + timestruct.tm_sec = exploded.second; + timestruct.tm_min = exploded.minute; + timestruct.tm_hour = exploded.hour; + timestruct.tm_mday = exploded.day_of_month; + timestruct.tm_mon = exploded.month - 1; + timestruct.tm_year = exploded.year - 1900; + timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this + timestruct.tm_yday = 0; // mktime/timegm ignore this + timestruct.tm_isdst = -1; // attempt to figure it out + timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore + timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore + + time_t seconds; +#ifdef ANDROID + seconds = mktime(×truct); +#else + if (is_local) + seconds = mktime(×truct); + else + seconds = timegm(×truct); +#endif + + int64_t milliseconds; + // Handle overflow. Clamping the range to what mktime and timegm might + // return is the best that can be done here. It's not ideal, but it's better + // than failing here or ignoring the overflow case and treating each time + // overflow as one second prior to the epoch. + if (seconds == -1 && + (exploded.year < 1969 || exploded.year > 1970)) { + // If exploded.year is 1969 or 1970, take -1 as correct, with the + // time indicating 1 second prior to the epoch. (1970 is allowed to handle + // time zone and DST offsets.) Otherwise, return the most future or past + // time representable. Assumes the time_t epoch is 1970-01-01 00:00:00 UTC. + // + // The minimum and maximum representible times that mktime and timegm could + // return are used here instead of values outside that range to allow for + // proper round-tripping between exploded and counter-type time + // representations in the presence of possible truncation to time_t by + // division and use with other functions that accept time_t. + // + // When representing the most distant time in the future, add in an extra + // 999ms to avoid the time being less than any other possible value that + // this function can return. + + // Take care to avoid overflows when time_t is int64_t. + if (exploded.year < 1969) { + int64_t min_seconds = (sizeof(time_t) < sizeof(int64_t)) + ? std::numeric_limits<time_t>::min() + : std::numeric_limits<int32_t>::min(); + milliseconds = min_seconds * kMillisecondsPerSecond; + } else { + int64_t max_seconds = (sizeof(time_t) < sizeof(int64_t)) + ? std::numeric_limits<time_t>::max() + : std::numeric_limits<int32_t>::max(); + milliseconds = max_seconds * kMillisecondsPerSecond; + milliseconds += kMillisecondsPerSecond - 1; + } + } else { + milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond; + } + + return Time(milliseconds * kMicrosecondsPerMillisecond); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + // Time stores times with microsecond resolution, but Exploded only carries + // millisecond resolution, so begin by being lossy. + int64_t milliseconds = us_ / kMicrosecondsPerMillisecond; + time_t seconds = milliseconds / kMillisecondsPerSecond; + + struct tm timestruct; + if (is_local) + localtime_r(&seconds, ×truct); + else + gmtime_r(&seconds, ×truct); + + exploded->year = timestruct.tm_year + 1900; + exploded->month = timestruct.tm_mon + 1; + exploded->day_of_week = timestruct.tm_wday; + exploded->day_of_month = timestruct.tm_mday; + exploded->hour = timestruct.tm_hour; + exploded->minute = timestruct.tm_min; + exploded->second = timestruct.tm_sec; + exploded->millisecond = milliseconds % kMillisecondsPerSecond; +} + +// TimeTicks ------------------------------------------------------------------ + +// static +TimeTicks TimeTicks::Now() { + uint64_t absolute_micro; + +#if defined(OS_MACOSX) + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + // Zero-initialization of statics guarantees that denom will be 0 before + // calling mach_timebase_info. mach_timebase_info will never set denom to + // 0 as that would be invalid, so the zero-check can be used to determine + // whether mach_timebase_info has already been called. This is + // recommended by Apple's QA1398. + kern_return_t kr = mach_timebase_info(&timebase_info); + DCHECK(kr == KERN_SUCCESS); + } + + // mach_absolute_time is it when it comes to ticks on the Mac. Other calls + // with less precision (such as TickCount) just call through to + // mach_absolute_time. + + // timebase_info converts absolute time tick units into nanoseconds. Convert + // to microseconds up front to stave off overflows. + absolute_micro = mach_absolute_time() / Time::kNanosecondsPerMicrosecond * + timebase_info.numer / timebase_info.denom; + + // Don't bother with the rollover handling that the Windows version does. + // With numer and denom = 1 (the expected case), the 64-bit absolute time + // reported in nanoseconds is enough to last nearly 585 years. + +#elif defined(OS_OPENBSD) || defined(OS_POSIX) && \ + defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 + + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + NOTREACHED() << "clock_gettime(CLOCK_MONOTONIC) failed."; + return TimeTicks(); + } + + absolute_micro = + (static_cast<int64_t>(ts.tv_sec) * Time::kMicrosecondsPerSecond) + + (static_cast<int64_t>(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond); + +#else // _POSIX_MONOTONIC_CLOCK +#error No usable tick clock function on this platform. +#endif // _POSIX_MONOTONIC_CLOCK + + return TimeTicks(absolute_micro); +} + +// static +TimeTicks TimeTicks::HighResNow() { + return Now(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/time_win.cc b/ipc/chromium/src/base/time_win.cc new file mode 100644 index 0000000000..54252b1b43 --- /dev/null +++ b/ipc/chromium/src/base/time_win.cc @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + + +// Windows Timer Primer +// +// A good article: http://www.ddj.com/windows/184416651 +// A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 +// +// The default windows timer, GetSystemTimeAsFileTime is not very precise. +// It is only good to ~15.5ms. +// +// QueryPerformanceCounter is the logical choice for a high-precision timer. +// However, it is known to be buggy on some hardware. Specifically, it can +// sometimes "jump". On laptops, QPC can also be very expensive to call. +// It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower +// on laptops. A unittest exists which will show the relative cost of various +// timers on any system. +// +// The next logical choice is timeGetTime(). timeGetTime has a precision of +// 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other +// applications on the system. By default, precision is only 15.5ms. +// Unfortunately, we don't want to call timeBeginPeriod because we don't +// want to affect other applications. Further, on mobile platforms, use of +// faster multimedia timers can hurt battery life. See the intel +// article about this here: +// http://softwarecommunity.intel.com/articles/eng/1086.htm +// +// To work around all this, we're going to generally use timeGetTime(). We +// will only increase the system-wide timer if we're not running on battery +// power. Using timeBeginPeriod(1) is a requirement in order to make our +// message loop waits have the same resolution that our time measurements +// do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when +// there is nothing else to waken the Wait. + +#include "base/time.h" + +#pragma comment(lib, "winmm.lib") +#include <windows.h> +#include <mmsystem.h> + +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/cpu.h" +#include "base/singleton.h" +#include "mozilla/Casting.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; +using mozilla::BitwiseCast; + +namespace { + +// From MSDN, FILETIME "Contains a 64-bit value representing the number of +// 100-nanosecond intervals since January 1, 1601 (UTC)." +int64_t FileTimeToMicroseconds(const FILETIME& ft) { + // Need to BitwiseCast to fix alignment, then divide by 10 to convert + // 100-nanoseconds to milliseconds. This only works on little-endian + // machines. + return BitwiseCast<int64_t>(ft) / 10; +} + +void MicrosecondsToFileTime(int64_t us, FILETIME* ft) { + DCHECK(us >= 0) << "Time is less than 0, negative values are not " + "representable in FILETIME"; + + // Multiply by 10 to convert milliseconds to 100-nanoseconds. BitwiseCast will + // handle alignment problems. This only works on little-endian machines. + *ft = BitwiseCast<FILETIME>(us * 10); +} + +int64_t CurrentWallclockMicroseconds() { + FILETIME ft; + ::GetSystemTimeAsFileTime(&ft); + return FileTimeToMicroseconds(ft); +} + +// Time between resampling the un-granular clock for this API. 60 seconds. +const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond; + +int64_t initial_time = 0; +TimeTicks initial_ticks; + +void InitializeClock() { + initial_ticks = TimeTicks::Now(); + initial_time = CurrentWallclockMicroseconds(); +} + +} // namespace + +// Time ----------------------------------------------------------------------- + +// The internal representation of Time uses FILETIME, whose epoch is 1601-01-01 +// 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the +// number of leap year days between 1601 and 1970: (1970-1601)/4 excluding +// 1700, 1800, and 1900. +// static +const int64_t Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); + +// static +Time Time::Now() { + if (initial_time == 0) + InitializeClock(); + + // We implement time using the high-resolution timers so that we can get + // timeouts which are smaller than 10-15ms. If we just used + // CurrentWallclockMicroseconds(), we'd have the less-granular timer. + // + // To make this work, we initialize the clock (initial_time) and the + // counter (initial_ctr). To compute the initial time, we can check + // the number of ticks that have elapsed, and compute the delta. + // + // To avoid any drift, we periodically resync the counters to the system + // clock. + while(true) { + TimeTicks ticks = TimeTicks::Now(); + + // Calculate the time elapsed since we started our timer + TimeDelta elapsed = ticks - initial_ticks; + + // Check if enough time has elapsed that we need to resync the clock. + if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) { + InitializeClock(); + continue; + } + + return Time(elapsed + Time(initial_time)); + } +} + +// static +Time Time::NowFromSystemTime() { + // Force resync. + InitializeClock(); + return Time(initial_time); +} + +// static +Time Time::FromFileTime(FILETIME ft) { + return Time(FileTimeToMicroseconds(ft)); +} + +FILETIME Time::ToFileTime() const { + FILETIME utc_ft; + MicrosecondsToFileTime(us_, &utc_ft); + return utc_ft; +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + // Create the system struct representing our exploded time. It will either be + // in local time or UTC. + SYSTEMTIME st; + st.wYear = exploded.year; + st.wMonth = exploded.month; + st.wDayOfWeek = exploded.day_of_week; + st.wDay = exploded.day_of_month; + st.wHour = exploded.hour; + st.wMinute = exploded.minute; + st.wSecond = exploded.second; + st.wMilliseconds = exploded.millisecond; + + // Convert to FILETIME. + FILETIME ft; + if (!SystemTimeToFileTime(&st, &ft)) { + NOTREACHED() << "Unable to convert time"; + return Time(0); + } + + // Ensure that it's in UTC. + if (is_local) { + FILETIME utc_ft; + LocalFileTimeToFileTime(&ft, &utc_ft); + return Time(FileTimeToMicroseconds(utc_ft)); + } + return Time(FileTimeToMicroseconds(ft)); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + // FILETIME in UTC. + FILETIME utc_ft; + MicrosecondsToFileTime(us_, &utc_ft); + + // FILETIME in local time if necessary. + BOOL success = TRUE; + FILETIME ft; + if (is_local) + success = FileTimeToLocalFileTime(&utc_ft, &ft); + else + ft = utc_ft; + + // FILETIME in SYSTEMTIME (exploded). + SYSTEMTIME st; + if (!success || !FileTimeToSystemTime(&ft, &st)) { + NOTREACHED() << "Unable to convert time, don't know why"; + ZeroMemory(exploded, sizeof(*exploded)); + return; + } + + exploded->year = st.wYear; + exploded->month = st.wMonth; + exploded->day_of_week = st.wDayOfWeek; + exploded->day_of_month = st.wDay; + exploded->hour = st.wHour; + exploded->minute = st.wMinute; + exploded->second = st.wSecond; + exploded->millisecond = st.wMilliseconds; +} + +// TimeTicks ------------------------------------------------------------------ +namespace { + +// We define a wrapper to adapt between the __stdcall and __cdecl call of the +// mock function, and to avoid a static constructor. Assigning an import to a +// function pointer directly would require setup code to fetch from the IAT. +DWORD timeGetTimeWrapper() { + return timeGetTime(); +} + + +DWORD (*tick_function)(void) = &timeGetTimeWrapper; + +// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic +// because it returns the number of milliseconds since Windows has started, +// which will roll over the 32-bit value every ~49 days. We try to track +// rollover ourselves, which works if TimeTicks::Now() is called at least every +// 49 days. +class NowSingleton { + public: + NowSingleton() + : rollover_(TimeDelta::FromMilliseconds(0)), + last_seen_(0) { + } + + TimeDelta Now() { + AutoLock locked(lock_); + // We should hold the lock while calling tick_function to make sure that + // we keep our last_seen_ stay correctly in sync. + DWORD now = tick_function(); + if (now < last_seen_) + rollover_ += TimeDelta::FromMilliseconds(GG_LONGLONG(0x100000000)); // ~49.7 days. + last_seen_ = now; + return TimeDelta::FromMilliseconds(now) + rollover_; + } + + private: + Lock lock_; // To protected last_seen_ and rollover_. + TimeDelta rollover_; // Accumulation of time lost due to rollover. + DWORD last_seen_; // The last timeGetTime value we saw, to detect rollover. + + DISALLOW_COPY_AND_ASSIGN(NowSingleton); +}; + +// Overview of time counters: +// (1) CPU cycle counter. (Retrieved via RDTSC) +// The CPU counter provides the highest resolution time stamp and is the least +// expensive to retrieve. However, the CPU counter is unreliable and should not +// be used in production. Its biggest issue is that it is per processor and it +// is not synchronized between processors. Also, on some computers, the counters +// will change frequency due to thermal and power changes, and stop in some +// states. +// +// (2) QueryPerformanceCounter (QPC). The QPC counter provides a high- +// resolution (100 nanoseconds) time stamp but is comparatively more expensive +// to retrieve. What QueryPerformanceCounter actually does is up to the HAL. +// (with some help from ACPI). +// According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx +// in the worst case, it gets the counter from the rollover interrupt on the +// programmable interrupt timer. In best cases, the HAL may conclude that the +// RDTSC counter runs at a constant frequency, then it uses that instead. On +// multiprocessor machines, it will try to verify the values returned from +// RDTSC on each processor are consistent with each other, and apply a handful +// of workarounds for known buggy hardware. In other words, QPC is supposed to +// give consistent result on a multiprocessor computer, but it is unreliable in +// reality due to bugs in BIOS or HAL on some, especially old computers. +// With recent updates on HAL and newer BIOS, QPC is getting more reliable but +// it should be used with caution. +// +// (3) System time. The system time provides a low-resolution (typically 10ms +// to 55 milliseconds) time stamp but is comparatively less expensive to +// retrieve and more reliable. +class HighResNowSingleton { + public: + HighResNowSingleton() + : ticks_per_microsecond_(0.0), + skew_(0) { + InitializeClock(); + + // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is + // unreliable. Fallback to low-res clock. + base::CPU cpu; + if (cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15) + DisableHighResClock(); + } + + bool IsUsingHighResClock() { + return ticks_per_microsecond_ != 0.0; + } + + void DisableHighResClock() { + ticks_per_microsecond_ = 0.0; + } + + TimeDelta Now() { + // Our maximum tolerance for QPC drifting. + const int kMaxTimeDrift = 50 * Time::kMicrosecondsPerMillisecond; + + if (IsUsingHighResClock()) { + int64_t now = UnreliableNow(); + + // Verify that QPC does not seem to drift. + DCHECK(now - ReliableNow() - skew_ < kMaxTimeDrift); + + return TimeDelta::FromMicroseconds(now); + } + + // Just fallback to the slower clock. + return Singleton<NowSingleton>::get()->Now(); + } + + private: + // Synchronize the QPC clock with GetSystemTimeAsFileTime. + void InitializeClock() { + LARGE_INTEGER ticks_per_sec = {{0}}; + if (!QueryPerformanceFrequency(&ticks_per_sec)) + return; // Broken, we don't guarantee this function works. + ticks_per_microsecond_ = static_cast<float>(ticks_per_sec.QuadPart) / + static_cast<float>(Time::kMicrosecondsPerSecond); + + skew_ = UnreliableNow() - ReliableNow(); + } + + // Get the number of microseconds since boot in a reliable fashion + int64_t UnreliableNow() { + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return static_cast<int64_t>(now.QuadPart / ticks_per_microsecond_); + } + + // Get the number of microseconds since boot in a reliable fashion + int64_t ReliableNow() { + return Singleton<NowSingleton>::get()->Now().InMicroseconds(); + } + + // Cached clock frequency -> microseconds. This assumes that the clock + // frequency is faster than one microsecond (which is 1MHz, should be OK). + float ticks_per_microsecond_; // 0 indicates QPF failed and we're broken. + int64_t skew_; // Skew between lo-res and hi-res clocks (for debugging). + + DISALLOW_COPY_AND_ASSIGN(HighResNowSingleton); +}; + +} // namespace + +// static +TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( + TickFunctionType ticker) { + TickFunctionType old = tick_function; + tick_function = ticker; + return old; +} + +// static +TimeTicks TimeTicks::Now() { + return TimeTicks() + Singleton<NowSingleton>::get()->Now(); +} + +// static +TimeTicks TimeTicks::HighResNow() { + return TimeTicks() + Singleton<HighResNowSingleton>::get()->Now(); +} diff --git a/ipc/chromium/src/base/timer.cc b/ipc/chromium/src/base/timer.cc new file mode 100644 index 0000000000..b2d880797a --- /dev/null +++ b/ipc/chromium/src/base/timer.cc @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/timer.h" + +#include "base/message_loop.h" + +namespace base { + +void BaseTimer_Helper::OrphanDelayedTask() { + if (delayed_task_) { + delayed_task_->timer_ = nullptr; + delayed_task_ = nullptr; + } +} + +void BaseTimer_Helper::InitiateDelayedTask(TimerTask* timer_task) { + OrphanDelayedTask(); + + delayed_task_ = timer_task; + delayed_task_->timer_ = this; + RefPtr<TimerTask> addrefedTask = timer_task; + MessageLoop::current()->PostDelayedTask( + addrefedTask.forget(), + static_cast<int>(timer_task->delay_.InMilliseconds())); +} + +} // namespace base diff --git a/ipc/chromium/src/base/timer.h b/ipc/chromium/src/base/timer.h new file mode 100644 index 0000000000..b65f89392e --- /dev/null +++ b/ipc/chromium/src/base/timer.h @@ -0,0 +1,274 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// OneShotTimer and RepeatingTimer provide a simple timer API. As the names +// suggest, OneShotTimer calls you back once after a time delay expires. +// RepeatingTimer on the other hand calls you back periodically with the +// prescribed time interval. +// +// OneShotTimer and RepeatingTimer both cancel the timer when they go out of +// scope, which makes it easy to ensure that you do not get called when your +// object has gone out of scope. Just instantiate a OneShotTimer or +// RepeatingTimer as a member variable of the class for which you wish to +// receive timer events. +// +// Sample RepeatingTimer usage: +// +// class MyClass { +// public: +// void StartDoingStuff() { +// timer_.Start(TimeDelta::FromSeconds(1), this, &MyClass::DoStuff); +// } +// void StopDoingStuff() { +// timer_.Stop(); +// } +// private: +// void DoStuff() { +// // This method is called every second to do stuff. +// ... +// } +// base::RepeatingTimer<MyClass> timer_; +// }; +// +// Both OneShotTimer and RepeatingTimer also support a Reset method, which +// allows you to easily defer the timer event until the timer delay passes once +// again. So, in the above example, if 0.5 seconds have already passed, +// calling Reset on timer_ would postpone DoStuff by another 1 second. In +// other words, Reset is shorthand for calling Stop and then Start again with +// the same arguments. + +#ifndef BASE_TIMER_H_ +#define BASE_TIMER_H_ + +// IMPORTANT: If you change timer code, make sure that all tests (including +// disabled ones) from timer_unittests.cc pass locally. Some are disabled +// because they're flaky on the buildbot, but when you run them locally you +// should be able to tell the difference. + +#include "base/logging.h" +#include "base/task.h" +#include "base/time.h" + +class MessageLoop; + +namespace base { + +//----------------------------------------------------------------------------- +// This class is an implementation detail of OneShotTimer and RepeatingTimer. +// Please do not use this class directly. +// +// This class exists to share code between BaseTimer<T> template instantiations. +// +class BaseTimer_Helper { + public: + // Stops the timer. + ~BaseTimer_Helper() { + OrphanDelayedTask(); + } + + // Returns true if the timer is running (i.e., not stopped). + bool IsRunning() const { + return !!delayed_task_; + } + + // Returns the current delay for this timer. May only call this method when + // the timer is running! + TimeDelta GetCurrentDelay() const { + DCHECK(IsRunning()); + return delayed_task_->delay_; + } + + protected: + BaseTimer_Helper() {} + + // We have access to the timer_ member so we can orphan this task. + class TimerTask : public mozilla::Runnable { + public: + explicit TimerTask(TimeDelta delay) : delay_(delay) { + // timer_ is set in InitiateDelayedTask. + } + virtual ~TimerTask() {} + BaseTimer_Helper* timer_; + TimeDelta delay_; + }; + + // Used to orphan delayed_task_ so that when it runs it does nothing. + void OrphanDelayedTask(); + + // Used to initiated a new delayed task. This has the side-effect of + // orphaning delayed_task_ if it is non-null. + void InitiateDelayedTask(TimerTask* timer_task); + + RefPtr<TimerTask> delayed_task_; + + DISALLOW_COPY_AND_ASSIGN(BaseTimer_Helper); +}; + +//----------------------------------------------------------------------------- +// This class is an implementation detail of OneShotTimer and RepeatingTimer. +// Please do not use this class directly. +template <class Receiver, bool kIsRepeating> +class BaseTimer : public BaseTimer_Helper { + public: + typedef void (Receiver::*ReceiverMethod)(); + + // Call this method to start the timer. It is an error to call this method + // while the timer is already running. + void Start(TimeDelta delay, Receiver* receiver, ReceiverMethod method) { + DCHECK(!IsRunning()); + InitiateDelayedTask(new TimerTask(delay, receiver, method)); + } + + // Call this method to stop the timer. It is a no-op if the timer is not + // running. + void Stop() { + OrphanDelayedTask(); + } + + // Call this method to reset the timer delay of an already running timer. + void Reset() { + DCHECK(IsRunning()); + InitiateDelayedTask(static_cast<TimerTask*>(delayed_task_.get())->Clone()); + } + + private: + typedef BaseTimer<Receiver, kIsRepeating> SelfType; + + class TimerTask : public BaseTimer_Helper::TimerTask { + public: + TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method) + : BaseTimer_Helper::TimerTask(delay), + receiver_(receiver), + method_(method) { + } + + virtual ~TimerTask() { + // This task may be getting cleared because the MessageLoop has been + // destructed. If so, don't leave the Timer with a dangling pointer + // to this now-defunct task. + ClearBaseTimer(); + } + + NS_IMETHOD Run() override { + if (!timer_) // timer_ is null if we were orphaned. + return NS_OK; + if (kIsRepeating) + ResetBaseTimer(); + else + ClearBaseTimer(); + DispatchToMethod(receiver_, method_, Tuple0()); + return NS_OK; + } + + TimerTask* Clone() const { + return new TimerTask(delay_, receiver_, method_); + } + + private: + // Inform the Base that the timer is no longer active. + void ClearBaseTimer() { + if (timer_) { + SelfType* self = static_cast<SelfType*>(timer_); + // It is possible that the Timer has already been reset, and that this + // Task is old. So, if the Timer points to a different task, assume + // that the Timer has already taken care of properly setting the task. + if (self->delayed_task_ == this) + self->delayed_task_ = nullptr; + // By now the delayed_task_ in the Timer does not point to us anymore. + // We should reset our own timer_ because the Timer can not do this + // for us in its destructor. + timer_ = NULL; + } + } + + // Inform the Base that we're resetting the timer. + void ResetBaseTimer() { + DCHECK(timer_); + DCHECK(kIsRepeating); + SelfType* self = static_cast<SelfType*>(timer_); + self->Reset(); + } + + Receiver* receiver_; + ReceiverMethod method_; + }; +}; + +//----------------------------------------------------------------------------- +// A simple, one-shot timer. See usage notes at the top of the file. +template <class Receiver> +class OneShotTimer : public BaseTimer<Receiver, false> {}; + +//----------------------------------------------------------------------------- +// A simple, repeating timer. See usage notes at the top of the file. +template <class Receiver> +class RepeatingTimer : public BaseTimer<Receiver, true> {}; + +//----------------------------------------------------------------------------- +// A Delay timer is like The Button from Lost. Once started, you have to keep +// calling Reset otherwise it will call the given method in the MessageLoop +// thread. +// +// Once created, it is inactive until Reset is called. Once |delay| seconds have +// passed since the last call to Reset, the callback is made. Once the callback +// has been made, it's inactive until Reset is called again. +// +// If destroyed, the timeout is canceled and will not occur even if already +// inflight. +template <class Receiver> +class DelayTimer { + public: + typedef void (Receiver::*ReceiverMethod)(); + + DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method) + : receiver_(receiver), + method_(method), + delay_(delay) { + } + + void Reset() { + DelayFor(delay_); + } + + private: + void DelayFor(TimeDelta delay) { + trigger_time_ = Time::Now() + delay; + + // If we already have a timer that will expire at or before the given delay, + // then we have nothing more to do now. + if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay) + return; + + // The timer isn't running, or will expire too late, so restart it. + timer_.Stop(); + timer_.Start(delay, this, &DelayTimer<Receiver>::Check); + } + + void Check() { + if (trigger_time_.is_null()) + return; + + // If we have not waited long enough, then wait some more. + const Time now = Time::Now(); + if (now < trigger_time_) { + DelayFor(trigger_time_ - now); + return; + } + + (receiver_->*method_)(); + } + + Receiver *const receiver_; + const ReceiverMethod method_; + const TimeDelta delay_; + + OneShotTimer<DelayTimer<Receiver> > timer_; + Time trigger_time_; +}; + +} // namespace base + +#endif // BASE_TIMER_H_ diff --git a/ipc/chromium/src/base/tuple.h b/ipc/chromium/src/base/tuple.h new file mode 100644 index 0000000000..d7c5a008fd --- /dev/null +++ b/ipc/chromium/src/base/tuple.h @@ -0,0 +1,884 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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. + +// A Tuple is a generic templatized container, similar in concept to std::pair. +// There are classes Tuple0 to Tuple6, cooresponding to the number of elements +// it contains. The convenient MakeTuple() function takes 0 to 6 arguments, +// and will construct and return the appropriate Tuple object. The functions +// DispatchToMethod and DispatchToFunction take a function pointer or instance +// and method pointer, and unpack a tuple into arguments to the call. +// +// Tuple elements are copied by value, and stored in the tuple. See the unit +// tests for more details of how/when the values are copied. +// +// Example usage: +// // These two methods of creating a Tuple are identical. +// Tuple2<int, const char*> tuple_a(1, "wee"); +// Tuple2<int, const char*> tuple_b = MakeTuple(1, "wee"); +// +// void SomeFunc(int a, const char* b) { } +// DispatchToFunction(&SomeFunc, tuple_a); // SomeFunc(1, "wee") +// DispatchToFunction( +// &SomeFunc, MakeTuple(10, "foo")); // SomeFunc(10, "foo") +// +// struct { void SomeMeth(int a, int b, int c) { } } foo; +// DispatchToMethod(&foo, &Foo::SomeMeth, MakeTuple(1, 2, 3)); +// // foo->SomeMeth(1, 2, 3); + +#ifndef BASE_TUPLE_H__ +#define BASE_TUPLE_H__ + +// Traits ---------------------------------------------------------------------- +// +// A simple traits class for tuple arguments. +// +// ValueType: the bare, nonref version of a type (same as the type for nonrefs). +// RefType: the ref version of a type (same as the type for refs). +// ParamType: what type to pass to functions (refs should not be constified). + +template <class P> +struct TupleTraits { + typedef P ValueType; + typedef P& RefType; + typedef const P& ParamType; +}; + +template <class P> +struct TupleTraits<P&> { + typedef P ValueType; + typedef P& RefType; + typedef P& ParamType; +}; + +// Tuple ----------------------------------------------------------------------- +// +// This set of classes is useful for bundling 0 or more heterogeneous data types +// into a single variable. The advantage of this is that it greatly simplifies +// function objects that need to take an arbitrary number of parameters; see +// RunnableMethod and IPC::MessageWithTuple. +// +// Tuple0 is supplied to act as a 'void' type. It can be used, for example, +// when dispatching to a function that accepts no arguments (see the +// Dispatchers below). +// Tuple1<A> is rarely useful. One such use is when A is non-const ref that you +// want filled by the dispatchee, and the tuple is merely a container for that +// output (a "tier"). See MakeRefTuple and its usages. + +struct Tuple0 { + typedef Tuple0 ValueTuple; + typedef Tuple0 RefTuple; +}; + +template <class A> +struct Tuple1 { + public: + typedef A TypeA; + typedef Tuple1<typename TupleTraits<A>::ValueType> ValueTuple; + typedef Tuple1<typename TupleTraits<A>::RefType> RefTuple; + + Tuple1() {} + explicit Tuple1(typename TupleTraits<A>::ParamType aA) : a(aA) {} + + A a; +}; + +template <class A, class B> +struct Tuple2 { + public: + typedef A TypeA; + typedef B TypeB; + typedef Tuple2<typename TupleTraits<A>::ValueType, + typename TupleTraits<B>::ValueType> ValueTuple; + typedef Tuple2<typename TupleTraits<A>::RefType, + typename TupleTraits<B>::RefType> RefTuple; + + Tuple2() {} + Tuple2(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB) + : a(aA), b(aB) { + } + + A a; + B b; +}; + +template <class A, class B, class C> +struct Tuple3 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef Tuple3<typename TupleTraits<A>::ValueType, + typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType> ValueTuple; + typedef Tuple3<typename TupleTraits<A>::RefType, + typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType> RefTuple; + + Tuple3() {} + Tuple3(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC) + : a(aA), b(aB), c(aC) { + } + + A a; + B b; + C c; +}; + +template <class A, class B, class C, class D> +struct Tuple4 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef Tuple4<typename TupleTraits<A>::ValueType, + typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType, + typename TupleTraits<D>::ValueType> ValueTuple; + typedef Tuple4<typename TupleTraits<A>::RefType, + typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType, + typename TupleTraits<D>::RefType> RefTuple; + + Tuple4() {} + Tuple4(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC, + typename TupleTraits<D>::ParamType aD) + : a(aA), b(aB), c(aC), d(aD) { + } + + A a; + B b; + C c; + D d; +}; + +template <class A, class B, class C, class D, class E> +struct Tuple5 { +public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef Tuple5<typename TupleTraits<A>::ValueType, + typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType, + typename TupleTraits<D>::ValueType, + typename TupleTraits<E>::ValueType> ValueTuple; + typedef Tuple5<typename TupleTraits<A>::RefType, + typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType, + typename TupleTraits<D>::RefType, + typename TupleTraits<E>::RefType> RefTuple; + + Tuple5() {} + Tuple5(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC, + typename TupleTraits<D>::ParamType aD, + typename TupleTraits<E>::ParamType aE) + : a(aA), b(aB), c(aC), d(aD), e(aE) { + } + + A a; + B b; + C c; + D d; + E e; +}; + +template <class A, class B, class C, class D, class E, class F> +struct Tuple6 { +public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + typedef Tuple6<typename TupleTraits<A>::ValueType, + typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType, + typename TupleTraits<D>::ValueType, + typename TupleTraits<E>::ValueType, + typename TupleTraits<F>::ValueType> ValueTuple; + typedef Tuple6<typename TupleTraits<A>::RefType, + typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType, + typename TupleTraits<D>::RefType, + typename TupleTraits<E>::RefType, + typename TupleTraits<F>::RefType> RefTuple; + + Tuple6() {} + Tuple6(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC, + typename TupleTraits<D>::ParamType aD, + typename TupleTraits<E>::ParamType aE, + typename TupleTraits<F>::ParamType aF) + : a(aA), b(aB), c(aC), d(aD), e(aE), f(aF) { + } + + A a; + B b; + C c; + D d; + E e; + F f; +}; + +template <class A, class B, class C, class D, class E, class F, class G> +struct Tuple7 { +public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + typedef G TypeG; + typedef Tuple7<typename TupleTraits<A>::ValueType, + typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType, + typename TupleTraits<D>::ValueType, + typename TupleTraits<E>::ValueType, + typename TupleTraits<F>::ValueType, + typename TupleTraits<G>::ValueType> ValueTuple; + typedef Tuple7<typename TupleTraits<A>::RefType, + typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType, + typename TupleTraits<D>::RefType, + typename TupleTraits<E>::RefType, + typename TupleTraits<F>::RefType, + typename TupleTraits<G>::RefType> RefTuple; + + Tuple7() {} + Tuple7(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC, + typename TupleTraits<D>::ParamType aD, + typename TupleTraits<E>::ParamType aE, + typename TupleTraits<F>::ParamType aF, + typename TupleTraits<G>::ParamType aG) + : a(aA), b(aB), c(aC), d(aD), e(aE), f(aF), g(aG) { + } + + A a; + B b; + C c; + D d; + E e; + F f; + G g; +}; + +// Tuple creators ------------------------------------------------------------- +// +// Helper functions for constructing tuples while inferring the template +// argument types. + +namespace base { + +inline Tuple0 MakeTuple() { + return Tuple0(); +} + +template <class A> +inline Tuple1<A> MakeTuple(const A& a) { + return Tuple1<A>(a); +} + +template <class A, class B> +inline Tuple2<A, B> MakeTuple(const A& a, const B& b) { + return Tuple2<A, B>(a, b); +} + +template <class A, class B, class C> +inline Tuple3<A, B, C> MakeTuple(const A& a, const B& b, const C& c) { + return Tuple3<A, B, C>(a, b, c); +} + +template <class A, class B, class C, class D> +inline Tuple4<A, B, C, D> MakeTuple(const A& a, const B& b, const C& c, + const D& d) { + return Tuple4<A, B, C, D>(a, b, c, d); +} + +template <class A, class B, class C, class D, class E> +inline Tuple5<A, B, C, D, E> MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e) { + return Tuple5<A, B, C, D, E>(a, b, c, d, e); +} + +template <class A, class B, class C, class D, class E, class F> +inline Tuple6<A, B, C, D, E, F> MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e, const F& f) { + return Tuple6<A, B, C, D, E, F>(a, b, c, d, e, f); +} + +template <class A, class B, class C, class D, class E, class F, class G> +inline Tuple7<A, B, C, D, E, F, G> MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e, const F& f, + const G& g) { + return Tuple7<A, B, C, D, E, F, G>(a, b, c, d, e, f, g); +} + +} // end namespace base + +// The following set of helpers make what Boost refers to as "Tiers" - a tuple +// of references. + +template <class A> +inline Tuple1<A&> MakeRefTuple(A& a) { + return Tuple1<A&>(a); +} + +template <class A, class B> +inline Tuple2<A&, B&> MakeRefTuple(A& a, B& b) { + return Tuple2<A&, B&>(a, b); +} + +template <class A, class B, class C> +inline Tuple3<A&, B&, C&> MakeRefTuple(A& a, B& b, C& c) { + return Tuple3<A&, B&, C&>(a, b, c); +} + +template <class A, class B, class C, class D> +inline Tuple4<A&, B&, C&, D&> MakeRefTuple(A& a, B& b, C& c, D& d) { + return Tuple4<A&, B&, C&, D&>(a, b, c, d); +} + +template <class A, class B, class C, class D, class E> +inline Tuple5<A&, B&, C&, D&, E&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e) { + return Tuple5<A&, B&, C&, D&, E&>(a, b, c, d, e); +} + +template <class A, class B, class C, class D, class E, class F> +inline Tuple6<A&, B&, C&, D&, E&, F&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e, + F& f) { + return Tuple6<A&, B&, C&, D&, E&, F&>(a, b, c, d, e, f); +} + +template <class A, class B, class C, class D, class E, class F, class G> +inline Tuple7<A&, B&, C&, D&, E&, F&, G&> MakeRefTuple(A& a, B& b, C& c, D& d, + E& e, F& f, G& g) { + return Tuple7<A&, B&, C&, D&, E&, F&, G&>(a, b, c, d, e, f, g); +} + +// Dispatchers ---------------------------------------------------------------- +// +// Helper functions that call the given method on an object, with the unpacked +// tuple arguments. Notice that they all have the same number of arguments, +// so you need only write: +// DispatchToMethod(object, &Object::method, args); +// This is very useful for templated dispatchers, since they don't need to know +// what type |args| is. + +// Non-Static Dispatchers with no out params. + +template <class ObjT, class Method> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) { + (obj->*method)(); +} + +template <class ObjT, class Method, class A> +inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) { + (obj->*method)(arg); +} + +template <class ObjT, class Method, class A> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) { + (obj->*method)(arg.a); +} + +template<class ObjT, class Method, class A, class B> +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple2<A, B>& arg) { + (obj->*method)(arg.a, arg.b); +} + +template<class ObjT, class Method, class A, class B, class C> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<A, B, C>& arg) { + (obj->*method)(arg.a, arg.b, arg.c); +} + +template<class ObjT, class Method, class A, class B, class C, class D> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<A, B, C, D>& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d); +} + +template<class ObjT, class Method, class A, class B, class C, class D, class E> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<A, B, C, D, E>& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template<class ObjT, class Method, class A, class B, class C, class D, class E, + class F> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<A, B, C, D, E, F>& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +template<class ObjT, class Method, class A, class B, class C, class D, class E, + class F, class G> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple7<A, B, C, D, E, F, G>& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g); +} + +// Static Dispatchers with no out params. + +template <class Function> +inline void DispatchToFunction(Function function, const Tuple0& arg) { + (*function)(); +} + +template <class Function, class A> +inline void DispatchToFunction(Function function, const A& arg) { + (*function)(arg); +} + +template <class Function, class A> +inline void DispatchToFunction(Function function, const Tuple1<A>& arg) { + (*function)(arg.a); +} + +template<class Function, class A, class B> +inline void DispatchToFunction(Function function, const Tuple2<A, B>& arg) { + (*function)(arg.a, arg.b); +} + +template<class Function, class A, class B, class C> +inline void DispatchToFunction(Function function, const Tuple3<A, B, C>& arg) { + (*function)(arg.a, arg.b, arg.c); +} + +template<class Function, class A, class B, class C, class D> +inline void DispatchToFunction(Function function, + const Tuple4<A, B, C, D>& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d); +} + +template<class Function, class A, class B, class C, class D, class E> +inline void DispatchToFunction(Function function, + const Tuple5<A, B, C, D, E>& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template<class Function, class A, class B, class C, class D, class E, class F> +inline void DispatchToFunction(Function function, + const Tuple6<A, B, C, D, E, F>& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +// Dispatchers with 0 out param (as a Tuple0). + +template <class ObjT, class Method> +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple0& arg, Tuple0*) { + (obj->*method)(); +} + +template <class ObjT, class Method, class A> +inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) { + (obj->*method)(arg); +} + +template <class ObjT, class Method, class A> +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple1<A>& arg, Tuple0*) { + (obj->*method)(arg.a); +} + +template<class ObjT, class Method, class A, class B> +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple2<A, B>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b); +} + +template<class ObjT, class Method, class A, class B, class C> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<A, B, C>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c); +} + +template<class ObjT, class Method, class A, class B, class C, class D> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<A, B, C, D>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d); +} + +template<class ObjT, class Method, class A, class B, class C, class D, class E> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<A, B, C, D, E>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template<class ObjT, class Method, class A, class B, class C, class D, class E, + class F> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<A, B, C, D, E, F>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +// Dispatchers with 1 out param. + +template<class ObjT, class Method, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple1<OutA>* out) { + (obj->*method)(&out->a); +} + +template<class ObjT, class Method, class InA, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple1<OutA>* out) { + (obj->*method)(in, &out->a); +} + +template<class ObjT, class Method, class InA, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1<InA>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, &out->a); +} + +template<class ObjT, class Method, class InA, class InB, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, &out->a); +} + +template<class ObjT, class Method, class InA, class InB, class InC, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a); +} + +template<class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, class InF, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a); +} + +// Dispatchers with 2 out params. + +template<class ObjT, class Method, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(&out->a, &out->b); +} + +template<class ObjT, class Method, class InA, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in, &out->a, &out->b); +} + +template<class ObjT, class Method, class InA, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1<InA>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, &out->a, &out->b); +} + +template<class ObjT, class Method, class InA, class InB, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b); +} + +template<class ObjT, class Method, class InA, class InB, class InC, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b); +} + +template<class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, class InF, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b); +} + +// Dispatchers with 3 out params. + +template<class ObjT, class Method, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(&out->a, &out->b, &out->c); +} + +template<class ObjT, class Method, class InA, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in, &out->a, &out->b, &out->c); +} + +template<class ObjT, class Method, class InA, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1<InA>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c); +} + +template<class ObjT, class Method, class InA, class InB, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c); +} + +template<class ObjT, class Method, class InA, class InB, class InC, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c); +} + +template<class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, class InF, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b, &out->c); +} + +// Dispatchers with 4 out params. + +template<class ObjT, class Method, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(&out->a, &out->b, &out->c, &out->d); +} + +template<class ObjT, class Method, class InA, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in, &out->a, &out->b, &out->c, &out->d); +} + +template<class ObjT, class Method, class InA, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1<InA>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d); +} + +template<class ObjT, class Method, class InA, class InB, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d); +} + +template<class ObjT, class Method, class InA, class InB, class InC, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d); +} + +template<class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, + &out->a, &out->b, &out->c, &out->d); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, class InF, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, + &out->a, &out->b, &out->c, &out->d); +} + +// Dispatchers with 5 out params. + +template<class ObjT, class Method, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e); +} + +template<class ObjT, class Method, class InA, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template<class ObjT, class Method, class InA, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1<InA>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template<class ObjT, class Method, class InA, class InB, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template<class ObjT, class Method, class InA, class InB, class InC, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template<class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d, + &out->e); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, + &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template<class ObjT, class Method, + class InA, class InB, class InC, class InD, class InE, class InF, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, + &out->a, &out->b, &out->c, &out->d, &out->e); +} + +#endif // BASE_TUPLE_H__ diff --git a/ipc/chromium/src/base/waitable_event.h b/ipc/chromium/src/base/waitable_event.h new file mode 100644 index 0000000000..87eaff7c77 --- /dev/null +++ b/ipc/chromium/src/base/waitable_event.h @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_WAITABLE_EVENT_H_ +#define BASE_WAITABLE_EVENT_H_ + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif + +#if defined(OS_POSIX) +#include <list> +#include <utility> +#include "base/condition_variable.h" +#include "base/lock.h" +#include "nsISupportsImpl.h" +#include "nsAutoPtr.h" +#endif + +#include "base/message_loop.h" + +namespace base { + +// This replaces INFINITE from Win32 +static const int kNoTimeout = -1; + +class TimeDelta; + +// A WaitableEvent can be a useful thread synchronization tool when you want to +// allow one thread to wait for another thread to finish some work. For +// non-Windows systems, this can only be used from within a single address +// space. +// +// Use a WaitableEvent when you would otherwise use a Lock+ConditionVariable to +// protect a simple boolean value. However, if you find yourself using a +// WaitableEvent in conjunction with a Lock to wait for a more complex state +// change (e.g., for an item to be added to a queue), then you should probably +// be using a ConditionVariable instead of a WaitableEvent. +// +// NOTE: On Windows, this class provides a subset of the functionality afforded +// by a Windows event object. This is intentional. If you are writing Windows +// specific code and you need other features of a Windows event, then you might +// be better off just using an Windows event directly. +class WaitableEvent { + public: + // If manual_reset is true, then to set the event state to non-signaled, a + // consumer must call the Reset method. If this parameter is false, then the + // system automatically resets the event state to non-signaled after a single + // waiting thread has been released. + WaitableEvent(bool manual_reset, bool initially_signaled); + + ~WaitableEvent(); + + // Put the event in the un-signaled state. + void Reset(); + + // Put the event in the signaled state. Causing any thread blocked on Wait + // to be woken up. + void Signal(); + + // Returns true if the event is in the signaled state, else false. If this + // is not a manual reset event, then this test will cause a reset. + bool IsSignaled(); + + // Wait indefinitely for the event to be signaled. Returns true if the event + // was signaled, else false is returned to indicate that waiting failed. + bool Wait(); + + // Wait up until max_time has passed for the event to be signaled. Returns + // true if the event was signaled. If this method returns false, then it + // does not necessarily mean that max_time was exceeded. + bool TimedWait(const TimeDelta& max_time); + +#if defined(OS_WIN) + HANDLE handle() const { return handle_; } +#endif + + // Wait, synchronously, on multiple events. + // waitables: an array of WaitableEvent pointers + // count: the number of elements in @waitables + // + // returns: the index of a WaitableEvent which has been signaled. + // + // You MUST NOT delete any of the WaitableEvent objects while this wait is + // happening. + static size_t WaitMany(WaitableEvent** waitables, size_t count); + + // For asynchronous waiting, see WaitableEventWatcher + + // This is a private helper class. It's here because it's used by friends of + // this class (such as WaitableEventWatcher) to be able to enqueue elements + // of the wait-list + class Waiter { + public: + // Signal the waiter to wake up. + // + // Consider the case of a Waiter which is in multiple WaitableEvent's + // wait-lists. Each WaitableEvent is automatic-reset and two of them are + // signaled at the same time. Now, each will wake only the first waiter in + // the wake-list before resetting. However, if those two waiters happen to + // be the same object (as can happen if another thread didn't have a chance + // to dequeue the waiter from the other wait-list in time), two auto-resets + // will have happened, but only one waiter has been signaled! + // + // Because of this, a Waiter may "reject" a wake by returning false. In + // this case, the auto-reset WaitableEvent shouldn't act as if anything has + // been notified. + virtual bool Fire(WaitableEvent* signaling_event) = 0; + + // Waiters may implement this in order to provide an extra condition for + // two Waiters to be considered equal. In WaitableEvent::Dequeue, if the + // pointers match then this function is called as a final check. See the + // comments in ~Handle for why. + virtual bool Compare(void* tag) = 0; + }; + + private: + friend class WaitableEventWatcher; + +#if defined(OS_WIN) + HANDLE handle_; +#else + // On Windows, one can close a HANDLE which is currently being waited on. The + // MSDN documentation says that the resulting behaviour is 'undefined', but + // it doesn't crash. However, if we were to include the following members + // directly then, on POSIX, one couldn't use WaitableEventWatcher to watch an + // event which gets deleted. This mismatch has bitten us several times now, + // so we have a kernel of the WaitableEvent, which is reference counted. + // WaitableEventWatchers may then take a reference and thus match the Windows + // behaviour. + struct WaitableEventKernel final { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaitableEventKernel) + WaitableEventKernel(bool manual_reset, bool initially_signaled) + : manual_reset_(manual_reset), + signaled_(initially_signaled) { + } + + bool Dequeue(Waiter* waiter, void* tag); + + Lock lock_; + const bool manual_reset_; + bool signaled_; + std::list<Waiter*> waiters_; + protected: + ~WaitableEventKernel() {} + }; + + RefPtr<WaitableEventKernel> kernel_; + + bool SignalAll(); + bool SignalOne(); + void Enqueue(Waiter* waiter); + + // When dealing with arrays of WaitableEvent*, we want to sort by the address + // of the WaitableEvent in order to have a globally consistent locking order. + // In that case we keep them, in sorted order, in an array of pairs where the + // second element is the index of the WaitableEvent in the original, + // unsorted, array. + typedef std::pair<WaitableEvent*, size_t> WaiterAndIndex; + static size_t EnqueueMany(WaiterAndIndex* waitables, + size_t count, Waiter* waiter); +#endif + + DISALLOW_COPY_AND_ASSIGN(WaitableEvent); +}; + +} // namespace base + +#endif // BASE_WAITABLE_EVENT_H_ diff --git a/ipc/chromium/src/base/waitable_event_posix.cc b/ipc/chromium/src/base/waitable_event_posix.cc new file mode 100644 index 0000000000..ed137c8454 --- /dev/null +++ b/ipc/chromium/src/base/waitable_event_posix.cc @@ -0,0 +1,392 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/waitable_event.h" + +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/message_loop.h" + +// ----------------------------------------------------------------------------- +// A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't +// support cross-process events (where one process can signal an event which +// others are waiting on). Because of this, we can avoid having one thread per +// listener in several cases. +// +// The WaitableEvent maintains a list of waiters, protected by a lock. Each +// waiter is either an async wait, in which case we have a Task and the +// MessageLoop to run it on, or a blocking wait, in which case we have the +// condition variable to signal. +// +// Waiting involves grabbing the lock and adding oneself to the wait list. Async +// waits can be canceled, which means grabbing the lock and removing oneself +// from the list. +// +// Waiting on multiple events is handled by adding a single, synchronous wait to +// the wait-list of many events. An event passes a pointer to itself when +// firing a waiter and so we can store that pointer to find out which event +// triggered. +// ----------------------------------------------------------------------------- + +namespace base { + +// ----------------------------------------------------------------------------- +// This is just an abstract base class for waking the two types of waiters +// ----------------------------------------------------------------------------- +WaitableEvent::WaitableEvent(bool manual_reset, bool initially_signaled) + : kernel_(new WaitableEventKernel(manual_reset, initially_signaled)) { +} + +WaitableEvent::~WaitableEvent() { +} + +void WaitableEvent::Reset() { + AutoLock locked(kernel_->lock_); + kernel_->signaled_ = false; +} + +void WaitableEvent::Signal() { + AutoLock locked(kernel_->lock_); + + if (kernel_->signaled_) + return; + + if (kernel_->manual_reset_) { + SignalAll(); + kernel_->signaled_ = true; + } else { + // In the case of auto reset, if no waiters were woken, we remain + // signaled. + if (!SignalOne()) + kernel_->signaled_ = true; + } +} + +bool WaitableEvent::IsSignaled() { + AutoLock locked(kernel_->lock_); + + const bool result = kernel_->signaled_; + if (result && !kernel_->manual_reset_) + kernel_->signaled_ = false; + return result; +} + +// ----------------------------------------------------------------------------- +// Synchronous waits + +// ----------------------------------------------------------------------------- +// This is an synchronous waiter. The thread is waiting on the given condition +// variable and the fired flag in this object. +// ----------------------------------------------------------------------------- +class SyncWaiter : public WaitableEvent::Waiter { + public: + SyncWaiter(ConditionVariable* cv, Lock* lock) + : fired_(false), + cv_(cv), + lock_(lock), + signaling_event_(NULL) { + } + + bool Fire(WaitableEvent *signaling_event) { + lock_->Acquire(); + const bool previous_value = fired_; + fired_ = true; + if (!previous_value) + signaling_event_ = signaling_event; + lock_->Release(); + + if (previous_value) + return false; + + cv_->Broadcast(); + + // SyncWaiters are stack allocated on the stack of the blocking thread. + return true; + } + + WaitableEvent* signaled_event() const { + return signaling_event_; + } + + // --------------------------------------------------------------------------- + // These waiters are always stack allocated and don't delete themselves. Thus + // there's no problem and the ABA tag is the same as the object pointer. + // --------------------------------------------------------------------------- + bool Compare(void* tag) { + return this == tag; + } + + // --------------------------------------------------------------------------- + // Called with lock held. + // --------------------------------------------------------------------------- + bool fired() const { + return fired_; + } + + // --------------------------------------------------------------------------- + // During a TimedWait, we need a way to make sure that an auto-reset + // WaitableEvent doesn't think that this event has been signaled between + // unlocking it and removing it from the wait-list. Called with lock held. + // --------------------------------------------------------------------------- + void Disable() { + fired_ = true; + } + + private: + bool fired_; + ConditionVariable *const cv_; + Lock *const lock_; + WaitableEvent* signaling_event_; // The WaitableEvent which woke us +}; + +bool WaitableEvent::TimedWait(const TimeDelta& max_time) { + const TimeTicks end_time(TimeTicks::Now() + max_time); + const bool finite_time = max_time.ToInternalValue() >= 0; + + kernel_->lock_.Acquire(); + if (kernel_->signaled_) { + if (!kernel_->manual_reset_) { + // In this case we were signaled when we had no waiters. Now that + // someone has waited upon us, we can automatically reset. + kernel_->signaled_ = false; + } + + kernel_->lock_.Release(); + return true; + } + + Lock lock; + lock.Acquire(); + ConditionVariable cv(&lock); + SyncWaiter sw(&cv, &lock); + + Enqueue(&sw); + kernel_->lock_.Release(); + // We are violating locking order here by holding the SyncWaiter lock but not + // the WaitableEvent lock. However, this is safe because we don't lock @lock_ + // again before unlocking it. + + for (;;) { + const TimeTicks current_time(TimeTicks::Now()); + + if (sw.fired() || (finite_time && current_time >= end_time)) { + const bool return_value = sw.fired(); + + // We can't acquire @lock_ before releasing @lock (because of locking + // order), however, inbetween the two a signal could be fired and @sw + // would accept it, however we will still return false, so the signal + // would be lost on an auto-reset WaitableEvent. Thus we call Disable + // which makes sw::Fire return false. + sw.Disable(); + lock.Release(); + + kernel_->lock_.Acquire(); + kernel_->Dequeue(&sw, &sw); + kernel_->lock_.Release(); + + return return_value; + } + + if (finite_time) { + const TimeDelta max_wait(end_time - current_time); + cv.TimedWait(max_wait); + } else { + cv.Wait(); + } + } +} + +bool WaitableEvent::Wait() { + return TimedWait(TimeDelta::FromSeconds(-1)); +} + +// ----------------------------------------------------------------------------- + + +// ----------------------------------------------------------------------------- +// Synchronous waiting on multiple objects. + +static bool // StrictWeakOrdering +cmp_fst_addr(const std::pair<WaitableEvent*, unsigned> &a, + const std::pair<WaitableEvent*, unsigned> &b) { + return a.first < b.first; +} + +// static +size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables, + size_t count) { + DCHECK(count) << "Cannot wait on no events"; + + // We need to acquire the locks in a globally consistent order. Thus we sort + // the array of waitables by address. We actually sort a pairs so that we can + // map back to the original index values later. + std::vector<std::pair<WaitableEvent*, size_t> > waitables; + waitables.reserve(count); + for (size_t i = 0; i < count; ++i) + waitables.push_back(std::make_pair(raw_waitables[i], i)); + + DCHECK_EQ(count, waitables.size()); + + sort(waitables.begin(), waitables.end(), cmp_fst_addr); + + // The set of waitables must be distinct. Since we have just sorted by + // address, we can check this cheaply by comparing pairs of consecutive + // elements. + for (size_t i = 0; i < waitables.size() - 1; ++i) { + DCHECK(waitables[i].first != waitables[i+1].first); + } + + Lock lock; + ConditionVariable cv(&lock); + SyncWaiter sw(&cv, &lock); + + const size_t r = EnqueueMany(&waitables[0], count, &sw); + if (r) { + // One of the events is already signaled. The SyncWaiter has not been + // enqueued anywhere. EnqueueMany returns the count of remaining waitables + // when the signaled one was seen, so the index of the signaled event is + // @count - @r. + return waitables[count - r].second; + } + + // At this point, we hold the locks on all the WaitableEvents and we have + // enqueued our waiter in them all. + lock.Acquire(); + // Release the WaitableEvent locks in the reverse order + for (size_t i = 0; i < count; ++i) { + waitables[count - (1 + i)].first->kernel_->lock_.Release(); + } + + for (;;) { + if (sw.fired()) + break; + + cv.Wait(); + } + lock.Release(); + + // The address of the WaitableEvent which fired is stored in the SyncWaiter. + WaitableEvent *const signaled_event = sw.signaled_event(); + // This will store the index of the raw_waitables which fired. + size_t signaled_index = 0; + + // Take the locks of each WaitableEvent in turn (except the signaled one) and + // remove our SyncWaiter from the wait-list + for (size_t i = 0; i < count; ++i) { + if (raw_waitables[i] != signaled_event) { + raw_waitables[i]->kernel_->lock_.Acquire(); + // There's no possible ABA issue with the address of the SyncWaiter here + // because it lives on the stack. Thus the tag value is just the pointer + // value again. + raw_waitables[i]->kernel_->Dequeue(&sw, &sw); + raw_waitables[i]->kernel_->lock_.Release(); + } else { + signaled_index = i; + } + } + + return signaled_index; +} + +// ----------------------------------------------------------------------------- +// If return value == 0: +// The locks of the WaitableEvents have been taken in order and the Waiter has +// been enqueued in the wait-list of each. None of the WaitableEvents are +// currently signaled +// else: +// None of the WaitableEvent locks are held. The Waiter has not been enqueued +// in any of them and the return value is the index of the first WaitableEvent +// which was signaled, from the end of the array. +// ----------------------------------------------------------------------------- +// static +size_t WaitableEvent::EnqueueMany + (std::pair<WaitableEvent*, size_t>* waitables, + size_t count, Waiter* waiter) { + if (!count) + return 0; + + waitables[0].first->kernel_->lock_.Acquire(); + if (waitables[0].first->kernel_->signaled_) { + if (!waitables[0].first->kernel_->manual_reset_) + waitables[0].first->kernel_->signaled_ = false; + waitables[0].first->kernel_->lock_.Release(); + return count; + } + + const size_t r = EnqueueMany(waitables + 1, count - 1, waiter); + if (r) { + waitables[0].first->kernel_->lock_.Release(); + } else { + waitables[0].first->Enqueue(waiter); + } + + return r; +} + +// ----------------------------------------------------------------------------- + + +// ----------------------------------------------------------------------------- +// Private functions... + +// ----------------------------------------------------------------------------- +// Wake all waiting waiters. Called with lock held. +// ----------------------------------------------------------------------------- +bool WaitableEvent::SignalAll() { + bool signaled_at_least_one = false; + + for (std::list<Waiter*>::iterator + i = kernel_->waiters_.begin(); i != kernel_->waiters_.end(); ++i) { + if ((*i)->Fire(this)) + signaled_at_least_one = true; + } + + kernel_->waiters_.clear(); + return signaled_at_least_one; +} + +// --------------------------------------------------------------------------- +// Try to wake a single waiter. Return true if one was woken. Called with lock +// held. +// --------------------------------------------------------------------------- +bool WaitableEvent::SignalOne() { + for (;;) { + if (kernel_->waiters_.empty()) + return false; + + const bool r = (*kernel_->waiters_.begin())->Fire(this); + kernel_->waiters_.pop_front(); + if (r) + return true; + } +} + +// ----------------------------------------------------------------------------- +// Add a waiter to the list of those waiting. Called with lock held. +// ----------------------------------------------------------------------------- +void WaitableEvent::Enqueue(Waiter* waiter) { + kernel_->waiters_.push_back(waiter); +} + +// ----------------------------------------------------------------------------- +// Remove a waiter from the list of those waiting. Return true if the waiter was +// actually removed. Called with lock held. +// ----------------------------------------------------------------------------- +bool WaitableEvent::WaitableEventKernel::Dequeue(Waiter* waiter, void* tag) { + for (std::list<Waiter*>::iterator + i = waiters_.begin(); i != waiters_.end(); ++i) { + if (*i == waiter && (*i)->Compare(tag)) { + waiters_.erase(i); + return true; + } + } + + return false; +} + +// ----------------------------------------------------------------------------- + +} // namespace base diff --git a/ipc/chromium/src/base/waitable_event_win.cc b/ipc/chromium/src/base/waitable_event_win.cc new file mode 100644 index 0000000000..77a1f20564 --- /dev/null +++ b/ipc/chromium/src/base/waitable_event_win.cc @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/waitable_event.h" + +#include <math.h> +#include <windows.h> + +#include "base/logging.h" +#include "base/time.h" + +namespace base { + +WaitableEvent::WaitableEvent(bool manual_reset, bool signaled) + : handle_(CreateEvent(NULL, manual_reset, signaled, NULL)) { + // We're probably going to crash anyways if this is ever NULL, so we might as + // well make our stack reports more informative by crashing here. + CHECK(handle_); +} + +WaitableEvent::~WaitableEvent() { + CloseHandle(handle_); +} + +void WaitableEvent::Reset() { + ResetEvent(handle_); +} + +void WaitableEvent::Signal() { + SetEvent(handle_); +} + +bool WaitableEvent::IsSignaled() { + return TimedWait(TimeDelta::FromMilliseconds(0)); +} + +bool WaitableEvent::Wait() { + DWORD result = WaitForSingleObject(handle_, INFINITE); + // It is most unexpected that this should ever fail. Help consumers learn + // about it if it should ever fail. + DCHECK(result == WAIT_OBJECT_0) << "WaitForSingleObject failed"; + return result == WAIT_OBJECT_0; +} + +bool WaitableEvent::TimedWait(const TimeDelta& max_time) { + DCHECK(max_time >= TimeDelta::FromMicroseconds(0)); + // Be careful here. TimeDelta has a precision of microseconds, but this API + // is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6? + // It should be 6 to avoid returning too early. + double timeout = ceil(max_time.InMillisecondsF()); + DWORD result = WaitForSingleObject(handle_, static_cast<DWORD>(timeout)); + switch (result) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + } + // It is most unexpected that this should ever fail. Help consumers learn + // about it if it should ever fail. + NOTREACHED() << "WaitForSingleObject failed"; + return false; +} + +// static +size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) { + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + CHECK(count <= MAXIMUM_WAIT_OBJECTS) + << "Can only wait on " << MAXIMUM_WAIT_OBJECTS << " with WaitMany"; + + for (size_t i = 0; i < count; ++i) + handles[i] = events[i]->handle(); + + DWORD result = + WaitForMultipleObjects(count, handles, + FALSE, // don't wait for all the objects + INFINITE); // no timeout + if (result >= WAIT_OBJECT_0 + count) { + NOTREACHED() << "WaitForMultipleObjects failed: " << GetLastError(); + return 0; + } + + return result - WAIT_OBJECT_0; +} + +} // namespace base diff --git a/ipc/chromium/src/base/win_util.cc b/ipc/chromium/src/base/win_util.cc new file mode 100644 index 0000000000..9b709ec98e --- /dev/null +++ b/ipc/chromium/src/base/win_util.cc @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 "base/win_util.h" + +#include <map> +#include <sddl.h> + +#include "base/logging.h" +#include "base/singleton.h" +#include "base/string_util.h" + +namespace win_util { + +std::wstring FormatMessage(unsigned messageid) { + wchar_t* string_buffer = NULL; + unsigned string_length = ::FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, messageid, 0, + reinterpret_cast<wchar_t *>(&string_buffer), 0, NULL); + + std::wstring formatted_string; + if (string_buffer) { + formatted_string = string_buffer; + LocalFree(reinterpret_cast<HLOCAL>(string_buffer)); + } else { + // The formating failed. simply convert the message value into a string. + SStringPrintf(&formatted_string, L"message number %d", messageid); + } + return formatted_string; +} + +std::wstring FormatLastWin32Error() { + return FormatMessage(GetLastError()); +} + +} // namespace win_util diff --git a/ipc/chromium/src/base/win_util.h b/ipc/chromium/src/base/win_util.h new file mode 100644 index 0000000000..b37411f60d --- /dev/null +++ b/ipc/chromium/src/base/win_util.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 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 BASE_WIN_UTIL_H__ +#define BASE_WIN_UTIL_H__ + +#include <windows.h> +#include <aclapi.h> + +#include <string> + +namespace win_util { + +// Uses the last Win32 error to generate a human readable message string. +std::wstring FormatLastWin32Error(); + +} // namespace win_util + +#endif // BASE_WIN_UTIL_H__ |