From: Sehong Na Date: Sat, 31 May 2014 03:34:24 +0000 (+0900) Subject: Initialize Tizen 2.3 X-Git-Tag: 2.3a_release X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Ftags%2F2.3a_release;p=external%2Fchromium.git Initialize Tizen 2.3 --- 2f15b23d6b15b41a315196af599e512bb705acd9 diff --git a/.merge_20130204.054301 b/.merge_20130204.054301 new file mode 100644 index 0000000..e69de29 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..a02ca6c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,77 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +PROJECT(chromium) + +SET(CMAKE_INSTALL_PREFIX /usr) +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) + +SET(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/cmake_build_tmp/output) +set(MODULE_DIR ${CMAKE_SOURCE_DIR}) + +SET (${PROJECT_NAME}_SOURCE_FILES + ${MODULE_DIR}/base/pickle.cc + ${MODULE_DIR}/ipc/ipc_message.cc + ${MODULE_DIR}/ipc/ipc_message_utils.cc + ${MODULE_DIR}/ipc/ipc_sync_message.cc + ) + +LIST (APPEND SRCS ${${PROJECT_NAME}_SOURCE_FILES}) + +INCLUDE_DIRECTORIES( + ${MODULE_DIR} +) + +add_library(${PROJECT_NAME} SHARED ${SRCS}) + +#FIND_LIBRARY(MYAPILIB NAMES pthread PATHS /usr/lib) + +SET(CMAKE_C_FLAGS "${OSP_DEBUG_FLAGS} ${OSP_OPT_FLAGS} ${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} ${OSP_COMPILER_FLAGS}") +SET(CMAKE_CXX_FLAGS "${OSP_DEBUG_FLAGS} ${OSP_OPT_FLAGS} ${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} ${OSP_COMPILER_FLAGS}") + + +SET_TARGET_PROPERTIES(${PROJECT_NAME} + PROPERTIES + VERSION ${FULLVER} + SOVERSION ${MAJORVER} + CLEAN_DIRECT_OUTPUT 1 + ) + +SET(PC_NAME ${PROJECT_NAME}) +SET(PC_REQUIRED ${pc_requires}) +SET(PC_LDFLAGS -l${PROJECT_NAME}) + +CONFIGURE_FILE( + chromium.pc.in + ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.pc + @ONLY +) + +INSTALL(FILES ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) +INSTALL(TARGETS ${PROJECT_NAME} DESTINATION lib${LIB_SUFFIX}) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/log.h DESTINATION include/${PROJECT_NAME}) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/atomic_ref_count.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/atomicops.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/atomicops_internals_arm_gcc.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/atomicops_internals_atomicword_compat.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/atomicops_internals_x86_gcc.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/atomicops_internals_x86_macosx.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/atomicops_internals_x86_msvc.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/basictypes.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/base_export.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/compiler_specific.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/file_descriptor_posix.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/memory/ref_counted.h DESTINATION include/${PROJECT_NAME}/base/memory) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/third_party/dynamic_annotations/dynamic_annotations.h DESTINATION include/${PROJECT_NAME}/base/third_party/dynamic_annotations) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/threading/thread_collision_warner.h DESTINATION include/${PROJECT_NAME}/base/threading) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/hash_tables.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/logging.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/pickle.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/port.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/string16.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/string_number_conversions.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/string_piece.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/time.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/tuple.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/base/values.h DESTINATION include/${PROJECT_NAME}/base) +INSTALL(FILES ${CMAKE_SOURCE_DIR}/build/build_config.h DESTINATION include/${PROJECT_NAME}/build) +INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/ipc/ DESTINATION include/${PROJECT_NAME}/ipc FILES_MATCHING PATTERN "*.h") diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..2fe19f6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +// Copyright (c) 2011 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. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..5f4a4c5 --- /dev/null +++ b/NOTICE @@ -0,0 +1,3 @@ +Copyright (c) 2011 The Chromium Authors. All rights reserved. +This software is excerpted from the Chromium project. +Please, see the LICENSE file for licenses. diff --git a/base/OWNERS b/base/OWNERS new file mode 100644 index 0000000..41dd3c9 --- /dev/null +++ b/base/OWNERS @@ -0,0 +1,6 @@ +set noparent +mark@chromium.org +darin@chromium.org +brettw@chromium.org +evan@chromium.org +willchan@chromium.org diff --git a/base/atomic_ref_count.h b/base/atomic_ref_count.h new file mode 100644 index 0000000..985c42c --- /dev/null +++ b/base/atomic_ref_count.h @@ -0,0 +1,81 @@ +// 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. + +// This is a low level implementation of atomic semantics for reference +// counting. Please use base/memory/ref_counted.h directly instead. +// +// The implementation includes annotations to avoid some false positives +// when using data race detection tools. + +#ifndef BASE_ATOMIC_REF_COUNT_H_ +#define BASE_ATOMIC_REF_COUNT_H_ +#pragma once + +#include "base/atomicops.h" +#include "base/third_party/dynamic_annotations/dynamic_annotations.h" + +namespace base { + +typedef subtle::Atomic32 AtomicRefCount; + +// Increment a reference count by "increment", which must exceed 0. +inline void AtomicRefCountIncN(volatile AtomicRefCount *ptr, + AtomicRefCount increment) { + subtle::NoBarrier_AtomicIncrement(ptr, increment); +} + +// Decrement a reference count by "decrement", which must exceed 0, +// and return whether the result is non-zero. +// Insert barriers to ensure that state written before the reference count +// became zero will be visible to a thread that has just made the count zero. +inline bool AtomicRefCountDecN(volatile AtomicRefCount *ptr, + AtomicRefCount decrement) { + ANNOTATE_HAPPENS_BEFORE(ptr); + bool res = (subtle::Barrier_AtomicIncrement(ptr, -decrement) != 0); + if (!res) { + ANNOTATE_HAPPENS_AFTER(ptr); + } + return res; +} + +// Increment a reference count by 1. +inline void AtomicRefCountInc(volatile AtomicRefCount *ptr) { + base::AtomicRefCountIncN(ptr, 1); +} + +// Decrement a reference count by 1 and return whether the result is non-zero. +// Insert barriers to ensure that state written before the reference count +// became zero will be visible to a thread that has just made the count zero. +inline bool AtomicRefCountDec(volatile AtomicRefCount *ptr) { + return base::AtomicRefCountDecN(ptr, 1); +} + +// Return whether the reference count is one. If the reference count is used +// in the conventional way, a refrerence count of 1 implies that the current +// thread owns the reference and no other thread shares it. This call performs +// the test for a reference count of one, and performs the memory barrier +// needed for the owning thread to act on the object, knowing that it has +// exclusive access to the object. +inline bool AtomicRefCountIsOne(volatile AtomicRefCount *ptr) { + bool res = (subtle::Acquire_Load(ptr) == 1); + if (res) { + ANNOTATE_HAPPENS_AFTER(ptr); + } + return res; +} + +// Return whether the reference count is zero. With conventional object +// referencing counting, the object will be destroyed, so the reference count +// should never be zero. Hence this is generally used for a debug check. +inline bool AtomicRefCountIsZero(volatile AtomicRefCount *ptr) { + bool res = (subtle::Acquire_Load(ptr) == 0); + if (res) { + ANNOTATE_HAPPENS_AFTER(ptr); + } + return res; +} + +} // namespace base + +#endif // BASE_ATOMIC_REF_COUNT_H_ diff --git a/base/atomic_sequence_num.h b/base/atomic_sequence_num.h new file mode 100644 index 0000000..11805a0 --- /dev/null +++ b/base/atomic_sequence_num.h @@ -0,0 +1,31 @@ +// 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_ATOMIC_SEQUENCE_NUM_H_ +#define BASE_ATOMIC_SEQUENCE_NUM_H_ +#pragma once + +#include "base/atomicops.h" +#include "base/basictypes.h" + +namespace base { + +class AtomicSequenceNumber { + public: + AtomicSequenceNumber() : seq_(0) { } + explicit AtomicSequenceNumber(base::LinkerInitialized x) { /* seq_ is 0 */ } + + int GetNext() { + return static_cast( + base::subtle::NoBarrier_AtomicIncrement(&seq_, 1) - 1); + } + + private: + base::subtle::Atomic32 seq_; + DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber); +}; + +} // namespace base + +#endif // BASE_ATOMIC_SEQUENCE_NUM_H_ diff --git a/base/atomicops.h b/base/atomicops.h new file mode 100644 index 0000000..a087e3d --- /dev/null +++ b/base/atomicops.h @@ -0,0 +1,150 @@ +// 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. + +// 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_ +#pragma once + +#include "base/basictypes.h" +#include "build/build_config.h" + +namespace base { +namespace subtle { + +typedef int32 Atomic32; +#ifdef ARCH_CPU_64_BITS +// We need to be able to go between Atomic64 and AtomicWord implicitly. This +// means Atomic64 and AtomicWord should be the same type on 64-bit. +#if defined(OS_NACL) +// NaCl's intptr_t is not actually 64-bits on 64-bit! +// http://code.google.com/p/nativeclient/issues/detail?id=1162 +typedef int64_t Atomic64; +#else +typedef intptr_t Atomic64; +#endif +#endif + +// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or +// Atomic64 routines below, depending on your architecture. +typedef intptr_t AtomicWord; + +// 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 // ARCH_CPU_64_BITS + +} // namespace base::subtle +} // namespace base + +// Include our platform specific implementation. +#if defined(OS_WIN) && defined(COMPILER_MSVC) && 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_ARM_FAMILY) +#include "base/atomicops_internals_arm_gcc.h" +#else +#error "Atomic operations are not supported on your platform" +#endif + +// On some platforms we need additional declarations to make +// AtomicWord compatible with our other Atomic* types. +#if defined(OS_MACOSX) || defined(OS_OPENBSD) +#include "base/atomicops_internals_atomicword_compat.h" +#endif + +#endif // BASE_ATOMICOPS_H_ diff --git a/base/atomicops_internals_arm_gcc.h b/base/atomicops_internals_arm_gcc.h new file mode 100644 index 0000000..091b34d --- /dev/null +++ b/base/atomicops_internals_arm_gcc.h @@ -0,0 +1,125 @@ +// 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_ +#pragma once + +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(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(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(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/base/atomicops_internals_atomicword_compat.h b/base/atomicops_internals_atomicword_compat.h new file mode 100644 index 0000000..8382fe1 --- /dev/null +++ b/base/atomicops_internals_atomicword_compat.h @@ -0,0 +1,101 @@ +// 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. + +// This file is an internal atomic implementation, use base/atomicops.h instead. + +#ifndef BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ +#define BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ +#pragma once + +// AtomicWord is a synonym for intptr_t, and Atomic32 is a synonym for int32, +// which in turn means int. On some LP32 platforms, intptr_t is an int, but +// on others, it's a long. When AtomicWord and Atomic32 are based on different +// fundamental types, their pointers are incompatible. +// +// This file defines function overloads to allow both AtomicWord and Atomic32 +// data to be used with this interface. +// +// On LP64 platforms, AtomicWord and Atomic64 are both always long, +// so this problem doesn't occur. + +#if !defined(ARCH_CPU_64_BITS) + +namespace base { +namespace subtle { + +inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return NoBarrier_CompareAndSwap( + reinterpret_cast(ptr), old_value, new_value); +} + +inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr, + AtomicWord new_value) { + return NoBarrier_AtomicExchange( + reinterpret_cast(ptr), new_value); +} + +inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return NoBarrier_AtomicIncrement( + reinterpret_cast(ptr), increment); +} + +inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return Barrier_AtomicIncrement( + reinterpret_cast(ptr), increment); +} + +inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return base::subtle::Acquire_CompareAndSwap( + reinterpret_cast(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(ptr), old_value, new_value); +} + +inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) { + NoBarrier_Store( + reinterpret_cast(ptr), value); +} + +inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) { + return base::subtle::Acquire_Store( + reinterpret_cast(ptr), value); +} + +inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) { + return base::subtle::Release_Store( + reinterpret_cast(ptr), value); +} + +inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) { + return NoBarrier_Load( + reinterpret_cast(ptr)); +} + +inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) { + return base::subtle::Acquire_Load( + reinterpret_cast(ptr)); +} + +inline AtomicWord Release_Load(volatile const AtomicWord* ptr) { + return base::subtle::Release_Load( + reinterpret_cast(ptr)); +} + +} // namespace base::subtle +} // namespace base + +#endif // !defined(ARCH_CPU_64_BITS) + +#endif // BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ diff --git a/base/atomicops_internals_x86_gcc.cc b/base/atomicops_internals_x86_gcc.cc new file mode 100644 index 0000000..933ca51 --- /dev/null +++ b/base/atomicops_internals_x86_gcc.cc @@ -0,0 +1,104 @@ +// 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 + +#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 eax; + uint32 ebx; + uint32 ecx; + uint32 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/base/atomicops_internals_x86_gcc.h b/base/atomicops_internals_x86_gcc.h new file mode 100644 index 0000000..72fd350 --- /dev/null +++ b/base/atomicops_internals_x86_gcc.h @@ -0,0 +1,270 @@ +// 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. + +// 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_ +#pragma once + +#include "base/base_export.h" + +// This struct is not part of the public API of this module; clients may not +// use it. (However, it's exported via BASE_EXPORT because clients implicitly +// do use it at link time by inlining these functions.) +// 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. +}; +BASE_EXPORT 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 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; +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return x; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +#endif // defined(__x86_64__) + +} // namespace base::subtle +} // namespace base + +#undef ATOMICOPS_COMPILER_BARRIER + +#endif // BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ diff --git a/base/atomicops_internals_x86_macosx.h b/base/atomicops_internals_x86_macosx.h new file mode 100644 index 0000000..4b7cec8 --- /dev/null +++ b/base/atomicops_internals_x86_macosx.h @@ -0,0 +1,198 @@ +// 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. + +// 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_ +#pragma once + +#include + +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(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(ptr))); + return old_value; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr, + Atomic32 increment) { + return OSAtomicAdd32(increment, const_cast(ptr)); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr, + Atomic32 increment) { + return OSAtomicAdd32Barrier(increment, const_cast(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(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, + reinterpret_cast(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, + reinterpret_cast(ptr))); + return old_value; +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr, + Atomic64 increment) { + return OSAtomicAdd64(increment, reinterpret_cast(ptr)); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr, + Atomic64 increment) { + return OSAtomicAdd64Barrier(increment, + reinterpret_cast(ptr)); +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev_value; + do { + if (OSAtomicCompareAndSwap64Barrier( + old_value, new_value, reinterpret_cast(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__) + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_ diff --git a/base/atomicops_internals_x86_msvc.h b/base/atomicops_internals_x86_msvc.h new file mode 100644 index 0000000..1574528 --- /dev/null +++ b/base/atomicops_internals_x86_msvc.h @@ -0,0 +1,181 @@ +// 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_ +#pragma once + +#include + +namespace base { +namespace subtle { + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + LONG result = InterlockedCompareExchange( + reinterpret_cast(ptr), + static_cast(new_value), + static_cast(old_value)); + return static_cast(result); +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + LONG result = InterlockedExchange( + reinterpret_cast(ptr), + static_cast(new_value)); + return static_cast(result); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return InterlockedExchangeAdd( + reinterpret_cast(ptr), + static_cast(increment)) + increment; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} + +#if !(defined(_MSC_VER) && _MSC_VER >= 1400) +#error "We require at least vs2005 for MemoryBarrier" +#endif +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(ptr), + reinterpret_cast(new_value), reinterpret_cast(old_value)); + return reinterpret_cast(result); +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + PVOID result = InterlockedExchangePointer( + reinterpret_cast(ptr), + reinterpret_cast(new_value)); + return reinterpret_cast(result); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return InterlockedExchangeAdd64( + reinterpret_cast(ptr), + static_cast(increment)) + increment; +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} + +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; +} + +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); +} + + +#endif // defined(_WIN64) + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ diff --git a/base/base_export.h b/base/base_export.h new file mode 100644 index 0000000..c3bff9f --- /dev/null +++ b/base/base_export.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef BASE_BASE_OSP_EXPORT_ +#define BASE_BASE_OSP_EXPORT_ +#pragma once + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(BASE_IMPLEMENTATION) +#define BASE_EXPORT __declspec(dllexport) +#else +#define BASE_EXPORT __declspec(dllimport) +#endif // defined(BASE_IMPLEMENTATION) + +#else // defined(WIN32) +#define BASE_EXPORT __attribute__((visibility("default"))) +#endif + +#else // defined(COMPONENT_BUILD) +#define BASE_EXPORT +#endif + +#endif // BASE_BASE_OSP_EXPORT_ diff --git a/base/basictypes.h b/base/basictypes.h new file mode 100644 index 0000000..c6f5097 --- /dev/null +++ b/base/basictypes.h @@ -0,0 +1,367 @@ +// 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. + +#ifndef BASE_BASICTYPES_H_ +#define BASE_BASICTYPES_H_ +#pragma once + +#include // So we can set the bounds of our types +#include // For size_t +#include // for memcpy + +#include "base/port.h" // Types that only need exist on certain systems + +#ifndef COMPILER_MSVC +// stdint.h is part of C99 but MSVC doesn't have it. +#include // For intptr_t. +#endif + +typedef signed char schar; +typedef signed char int8; +typedef short int16; +// TODO: Remove these type guards. These are to avoid conflicts with +// obsolete/protypes.h in the Gecko SDK. +#ifndef _INT32 +#define _INT32 +typedef int int32; +#endif + +// The NSPR system headers define 64-bit as |long| when possible, except on +// Mac OS X. In order to not have typedef mismatches, we do the same on LP64. +// +// On Mac OS X, |long long| is used for 64-bit types for compatibility with +// format macros even in the LP64 model. +#if defined(__LP64__) && !defined(OS_MACOSX) +typedef long int64; +#else +typedef long long int64; +#endif + +// NOTE: unsigned types are DANGEROUS in loops and other arithmetical +// places. Use the signed types unless your variable represents a bit +// pattern (eg a hash value) or you really need the extra bit. Do NOT +// use 'unsigned' to express "this value should always be positive"; +// use assertions for this. + +typedef unsigned char uint8; +typedef unsigned short uint16; +// TODO: Remove these type guards. These are to avoid conflicts with +// obsolete/protypes.h in the Gecko SDK. +#ifndef _UINT32 +#define _UINT32 +typedef unsigned int uint32; +#endif + +// See the comment above about NSPR and 64-bit. +#if defined(__LP64__) && !defined(OS_MACOSX) +typedef unsigned long uint64; +#else +typedef unsigned long long uint64; +#endif + +// 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 is.) +typedef signed int char32; + +const uint8 kuint8max = (( uint8) 0xFF); +const uint16 kuint16max = ((uint16) 0xFFFF); +const uint32 kuint32max = ((uint32) 0xFFFFFFFF); +const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF)); +const int8 kint8min = (( int8) 0x80); +const int8 kint8max = (( int8) 0x7F); +const int16 kint16min = (( int16) 0x8000); +const int16 kint16max = (( int16) 0x7FFF); +const int32 kint32min = (( int32) 0x80000000); +const int32 kint32max = (( int32) 0x7FFFFFFF); +const int64 kint64min = (( int64) GG_LONGLONG(0x8000000000000000)); +const int64 kint64max = (( int64) GG_LONGLONG(0x7FFFFFFFFFFFFFFF)); + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// An older, deprecated, politically incorrect name for the above. +// NOTE: The usage of this macro was baned from our code base, but some +// third_party libraries are yet using it. +// TODO(tfarina): Figure out how to fix the usage of this macro in the +// third_party libraries and get rid of it. +#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. +#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 +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 +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(!(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(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 +inline To implicit_cast(From const &f) { + return f; +} + +// 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. + +template +struct CompileAssert { +}; + +#undef COMPILE_ASSERT +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +// 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 outer 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 +// +// 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 pair.. +typedef uint32 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 +}; + +// bit_cast is a template function that implements the +// equivalent of "*reinterpret_cast(&source)". We need this in +// very low-level functions like the protobuf library and fast math +// support. +// +// float f = 3.14159265358979; +// int i = bit_cast(f); +// // i = 0x40490fdb +// +// The classical address-casting method is: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast(&f); // WRONG +// +// The address-casting method actually produces undefined behavior +// according to ISO C++ specification section 3.10 -15 -. Roughly, this +// section says: if an object in memory has one type, and a program +// accesses it with a different type, then the result is undefined +// behavior for most values of "different type". +// +// This is true for any cast syntax, either *(int*)&f or +// *reinterpret_cast(&f). And it is particularly true for +// conversions betweeen integral lvalues and floating-point lvalues. +// +// The purpose of 3.10 -15- is to allow optimizing compilers to assume +// that expressions with different types refer to different memory. gcc +// 4.0.1 has an optimizer that takes advantage of this. So a +// non-conforming program quietly produces wildly incorrect output. +// +// The problem is not the use of reinterpret_cast. The problem is type +// punning: holding an object in memory of one type and reading its bits +// back using a different type. +// +// The C++ standard is more subtle and complex than this, but that +// is the basic idea. +// +// Anyways ... +// +// bit_cast<> calls memcpy() which is blessed by the standard, +// especially by the example in section 3.9 . Also, of course, +// bit_cast<> wraps up the nasty logic in one place. +// +// Fortunately memcpy() is very fast. In optimized mode, with a +// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline +// code with the minimal amount of data movement. On a 32-bit system, +// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8) +// compiles to two loads and two stores. +// +// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1. +// +// WARNING: if Dest or Source is a non-POD type, the result of the memcpy +// is likely to surprise you. + +template +inline Dest bit_cast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1]; + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// Used to explicitly mark the return value of a function as unused. If you are +// really sure you don't want to do anything with the return value of a function +// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example: +// +// scoped_ptr my_var = ...; +// if (TakeOwnership(my_var.get()) == SUCCESS) +// ignore_result(my_var.release()); +// +template +inline void ignore_result(const T& ignored) { +} + +// 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 + +#endif // BASE_BASICTYPES_H_ diff --git a/base/compiler_specific.h b/base/compiler_specific.h new file mode 100644 index 0000000..0a6e05a --- /dev/null +++ b/base/compiler_specific.h @@ -0,0 +1,140 @@ +// 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. + +#ifndef BASE_COMPILER_SPECIFIC_H_ +#define BASE_COMPILER_SPECIFIC_H_ +#pragma once + +#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() + +// Allows exporting a class that inherits from a non-exported base class. +// This uses suppress instead of push/pop because the delimiter after the +// declaration (either "," or "{") has to be placed before the pop macro. +// +// Example usage: +// class EXPORT_API Foo : NON_EXPORTED_BASE(public Bar) { +// +// MSVC Compiler warning C4275: +// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'. +// Note that this is intended to be used only when no access to the base class +// can be gained through the derived class. For more info, see +// http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx +#define NON_EXPORTED_BASE(code) MSVC_SUPPRESS_WARNING(4275) \ + code + +#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 +#define NON_EXPORTED_BASE(code) code + +#endif // COMPILER_MSVC + + +// Annotate a variable indicating it's ok if the variable is not used. +// (Typically used to silence a compiler warning when the assignment +// is important for some other reason.) +// Use like: +// int x ALLOW_UNUSED = ...; +#if defined(COMPILER_GCC) +#define ALLOW_UNUSED __attribute__((unused)) +#define NOINLINE __attribute__((noinline)) +#else +#define ALLOW_UNUSED +#define NOINLINE +#endif + +// Annotate a virtual method indicating it must be overriding a virtual +// method in the parent class. +// Use like: +// virtual void foo() OVERRIDE; +#if defined(COMPILER_MSVC) +#define OVERRIDE override +#elif defined(__clang__) +#define OVERRIDE override +#else +#define OVERRIDE +#endif + +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() WARN_UNUSED_RESULT; +// To explicitly ignore a result, see |ignore_result()| in . +#if defined(COMPILER_GCC) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +#define WARN_UNUSED_RESULT +#endif + +// Tell the compiler a function is using a printf-style format string. +// |format_param| is the one-based index of the format string parameter; +// |dots_param| is the one-based index of the "..." parameter. +// For v*printf functions (which take a va_list), pass 0 for dots_param. +// (This is undocumented but matches what the system C headers do.) +#if defined(COMPILER_GCC) +#define PRINTF_FORMAT(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#else +#define PRINTF_FORMAT(format_param, dots_param) +#endif + +// WPRINTF_FORMAT is the same, but for wide format strings. +// This doesn't appear to yet be implemented in any compiler. +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 . +#define WPRINTF_FORMAT(format_param, dots_param) +// If available, it would look like: +// __attribute__((format(wprintf, format_param, dots_param))) + +#endif // BASE_COMPILER_SPECIFIC_H_ diff --git a/base/file_descriptor_posix.h b/base/file_descriptor_posix.h new file mode 100644 index 0000000..6f46ab4 --- /dev/null +++ b/base/file_descriptor_posix.h @@ -0,0 +1,46 @@ +// 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_ +#pragma once + +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 ipc/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) { } + + bool operator==(const FileDescriptor& other) const { + return (fd == other.fd && auto_close == other.auto_close); + } + + // A comparison operator so that we can use these as keys in a std::map. + bool operator<(const FileDescriptor& other) const { + return other.fd < fd; + } + + 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/base/hash_tables.h b/base/hash_tables.h new file mode 100644 index 0000000..b91d5d4 --- /dev/null +++ b/base/hash_tables.h @@ -0,0 +1,120 @@ +// 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. +// + +// +// 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 my_map; +// base::hash_set my_set; +// +// NOTE: It is an explicit non-goal of this class to provide a generic hash +// function for pointers. If you want to hash a pointers to a particular class, +// please define the template specialization elsewhere (for example, in its +// header file) and keep it specific to just pointers to that class. This is +// because identity hashes are not desirable for all types that might show up +// in containers as pointers. + +#ifndef BASE_HASH_TABLES_H_ +#define BASE_HASH_TABLES_H_ +#pragma once + +#include "build/build_config.h" + +#include "base/string16.h" + +#if defined(COMPILER_MSVC) +#include +#include + +#define BASE_HASH_NAMESPACE stdext + +#elif defined(COMPILER_GCC) +#if defined(OS_ANDROID) +#define BASE_HASH_NAMESPACE std +#else +#define BASE_HASH_NAMESPACE __gnu_cxx +#endif + +// 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 and . +#ifdef __DEPRECATED +#define CHROME_OLD__DEPRECATED __DEPRECATED +#undef __DEPRECATED +#endif + +#if defined(OS_ANDROID) +#include +#include +#else +#include +#include +#endif + +#include + +#ifdef CHROME_OLD__DEPRECATED +#define __DEPRECATED CHROME_OLD__DEPRECATED +#undef CHROME_OLD__DEPRECATED +#endif + +namespace BASE_HASH_NAMESPACE { + +#if !defined(OS_ANDROID) +// The GNU C++ library provides identity 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 { \ + std::size_t operator()(integral_type value) const { \ + return static_cast(value); \ + } \ + } + +DEFINE_TRIVIAL_HASH(long long); +DEFINE_TRIVIAL_HASH(unsigned long long); + +#undef DEFINE_TRIVIAL_HASH +#endif // !defined(OS_ANDROID) + +// 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 . It is duplicated here because GCC +// versions prior to 4.3.2 are unable to compile when RTTI +// is disabled, as it is in our build. + +#define DEFINE_STRING_HASH(string_type) \ + template<> \ + struct hash { \ + 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(string16); + +#undef DEFINE_STRING_HASH + +} // namespace BASE_HASH_NAMESPACE + +#else // COMPILER +#error define BASE_HASH_NAMESPACE for your compiler +#endif // COMPILER + +namespace base { +using BASE_HASH_NAMESPACE::hash_map; +using BASE_HASH_NAMESPACE::hash_set; +} + +#endif // BASE_HASH_TABLES_H_ diff --git a/base/logging.h b/base/logging.h new file mode 100644 index 0000000..c18fada --- /dev/null +++ b/base/logging.h @@ -0,0 +1,952 @@ +// 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. + +#ifndef BASE_LOGGING_H_ +#define BASE_LOGGING_H_ +#pragma once + +#include +#include +#include +#include + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "build/build_config.h" + +// +// Optional message capabilities +// ----------------------------- +// Assertion failed messages and fatal errors are displayed in a dialog box +// before the application exits. However, running this UI creates a message +// loop, which causes application messages to be processed and potentially +// dispatched to existing application windows. Since the application is in a +// bad state when this assertion dialog is displayed, these messages may not +// get processed and hang the dialog, or the application might go crazy. +// +// Therefore, it can be beneficial to display the error dialog in a separate +// process from the main application. When the logging system needs to display +// a fatal error dialog box, it will look for a program called +// "DebugMessage.exe" in the same directory as the application executable. It +// will run this application with the message as the command line, and will +// not include the name of the application as is traditional for easier +// parsing. +// +// The code for DebugMessage.exe is only one line. In WinMain, do: +// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0); +// +// If DebugMessage.exe is not found, the logging code will use a normal +// MessageBox, potentially causing the problems discussed above. + + +// Instructions +// ------------ +// +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// The above will cause log messages to be output on the 1st, 11th, 21st, ... +// times it is executed. Note that the special COUNTER value is used to +// identify which repetition is happening. +// +// The CHECK(condition) macro is active in both debug and release builds and +// effectively performs a LOG(FATAL) which terminates the process and +// generates a crashdump unless a debugger is attached. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. LOG_IF and development flags also work well together +// because the code can be compiled away sometimes. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// The verbose logging can also be turned on module-by-module. For instance, +// --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0 +// will cause: +// a. VLOG(2) and lower messages to be printed from profile.{h,cc} +// b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc} +// c. VLOG(3) and lower messages to be printed from files prefixed with +// "browser" +// d. VLOG(4) and lower messages to be printed from files under a +// "chromeos" directory. +// e. VLOG(0) and lower messages to be printed from elsewhere +// +// The wildcarding functionality shown by (c) supports both '*' (match +// 0 or more characters) and '?' (match any single character) +// wildcards. Any pattern containing a forward or backward slash will +// be tested against the whole pathname and not just the module. +// E.g., "*/foo/bar/*=2" would change the logging level for all code +// in source files under a "foo/bar" directory. +// +// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just VLOG(2) << ...; +// } +// +// There is also a VLOG_IF "verbose level" condition macro for sample +// cases, when some extra computation and preparation for logs is not +// needed. +// +// VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// +// We also override the standard 'assert' to use 'DLOG_ASSERT'. +// +// Lastly, there is: +// +// PLOG(ERROR) << "Couldn't do foo"; +// DPLOG(ERROR) << "Couldn't do foo"; +// PLOG_IF(ERROR, cond) << "Couldn't do foo"; +// DPLOG_IF(ERROR, cond) << "Couldn't do foo"; +// PCHECK(condition) << "Couldn't do foo"; +// DPCHECK(condition) << "Couldn't do foo"; +// +// which append the last system error to the message in string form (taken from +// GetLastError() on Windows and errno on POSIX). +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, ERROR_REPORT, +// and FATAL. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// Note the special severity of ERROR_REPORT only available/relevant in normal +// mode, which displays error dialog without terminating the program. There is +// no error dialog for severity ERROR or below in normal mode. +// +// There is also the special severity of DFATAL, which logs FATAL in +// debug mode, ERROR in normal mode. + +namespace logging { + +// Where to record logging output? A flat file and/or system debug log via +// OutputDebugString. Defaults on Windows to LOG_ONLY_TO_FILE, and on +// POSIX to LOG_ONLY_TO_SYSTEM_DEBUG_LOG (aka stderr). +enum LoggingDestination { LOG_NONE, + LOG_ONLY_TO_FILE, + LOG_ONLY_TO_SYSTEM_DEBUG_LOG, + LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG }; + +// Indicates that the log file should be locked when being written to. +// Often, there is no locking, which is fine for a single threaded program. +// If logging is being done from multiple threads or there can be more than +// one process doing the logging, the file should be locked during writes to +// make each log outut atomic. Other writers will block. +// +// All processes writing to the log file must have their locking set for it to +// work properly. Defaults to DONT_LOCK_LOG_FILE. +enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE }; + +// On startup, should we delete or append to an existing log file (if any)? +// Defaults to APPEND_TO_OLD_LOG_FILE. +enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE }; + +enum DcheckState { + DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS, + ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS +}; + +// TODO(avi): do we want to do a unification of character types here? +#if defined(OS_WIN) +typedef wchar_t PathChar; +#else +typedef char PathChar; +#endif + +// Define different names for the BaseInitLoggingImpl() function depending on +// whether NDEBUG is defined or not so that we'll fail to link if someone tries +// to compile logging.cc with NDEBUG but includes logging.h without defining it, +// or vice versa. +#if NDEBUG +#define BaseInitLoggingImpl BaseInitLoggingImpl_built_with_NDEBUG +#else +#define BaseInitLoggingImpl BaseInitLoggingImpl_built_without_NDEBUG +#endif + +// Implementation of the InitLogging() method declared below. We use a +// more-specific name so we can #define it above without affecting other code +// that has named stuff "InitLogging". +BASE_EXPORT bool BaseInitLoggingImpl(const PathChar* log_file, + LoggingDestination logging_dest, + LogLockingState lock_log, + OldFileDeletionState delete_old, + DcheckState dcheck_state); + +// Sets the log file name and other global logging state. Calling this function +// is recommended, and is normally done at the beginning of application init. +// If you don't call it, all the flags will be initialized to their default +// values, and there is a race condition that may leak a critical section +// object if two threads try to do the first log at the same time. +// See the definition of the enums above for descriptions and default values. +// +// The default log file is initialized to "debug.log" in the application +// directory. You probably don't want this, especially since the program +// directory may not be writable on an enduser's system. +inline bool InitLogging(const PathChar* log_file, + LoggingDestination logging_dest, + LogLockingState lock_log, + OldFileDeletionState delete_old, + DcheckState dcheck_state) { + return BaseInitLoggingImpl(log_file, logging_dest, lock_log, + delete_old, dcheck_state); +} + +// Sets the log level. Anything at or above this level will be written to the +// log file/displayed to the user (if applicable). Anything below this level +// will be silently ignored. The log level defaults to 0 (everything is logged +// up to level INFO) if this function is not called. +// Note that log messages for VLOG(x) are logged at level -x, so setting +// the min log level to negative values enables verbose logging. +BASE_EXPORT void SetMinLogLevel(int level); + +// Gets the current log level. +BASE_EXPORT int GetMinLogLevel(); + +// Gets the VLOG default verbosity level. +BASE_EXPORT int GetVlogVerbosity(); + +// Gets the current vlog level for the given file (usually taken from +// __FILE__). + +// Note that |N| is the size *with* the null terminator. +BASE_EXPORT int GetVlogLevelHelper(const char* file_start, size_t N); + +template +int GetVlogLevel(const char (&file)[N]) { + return GetVlogLevelHelper(file, N); +} + +// Sets the common items you want to be prepended to each log message. +// process and thread IDs default to off, the timestamp defaults to on. +// If this function is not called, logging defaults to writing the timestamp +// only. +BASE_EXPORT void SetLogItems(bool enable_process_id, bool enable_thread_id, + bool enable_timestamp, bool enable_tickcount); + +// Sets whether or not you'd like to see fatal debug messages popped up in +// a dialog box or not. +// Dialogs are not shown by default. +BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs); + +// Sets the Log Assert Handler that will be used to notify of check failures. +// The default handler shows a dialog box and then terminate the process, +// however clients can use this function to override with their own handling +// (e.g. a silent one for Unit Tests) +typedef void (*LogAssertHandlerFunction)(const std::string& str); +BASE_EXPORT void SetLogAssertHandler(LogAssertHandlerFunction handler); + +// Sets the Log Report Handler that will be used to notify of check failures +// in non-debug mode. The default handler shows a dialog box and continues +// the execution, however clients can use this function to override with their +// own handling. +typedef void (*LogReportHandlerFunction)(const std::string& str); +BASE_EXPORT void SetLogReportHandler(LogReportHandlerFunction handler); + +// Sets the Log Message Handler that gets passed every log message before +// it's sent to other log destinations (if any). +// Returns true to signal that it handled the message and the message +// should not be sent to other log destinations. +typedef bool (*LogMessageHandlerFunction)(int severity, + const char* file, int line, size_t message_start, const std::string& str); +BASE_EXPORT void SetLogMessageHandler(LogMessageHandlerFunction handler); +BASE_EXPORT LogMessageHandlerFunction GetLogMessageHandler(); + +typedef int LogSeverity; +const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity +// Note: the log severities are used to index into the array of names, +// see log_severity_names. +const LogSeverity LOG_INFO = 0; +const LogSeverity LOG_WARNING = 1; +const LogSeverity LOG_ERROR = 2; +const LogSeverity LOG_ERROR_REPORT = 3; +const LogSeverity LOG_FATAL = 4; +const LogSeverity LOG_NUM_SEVERITIES = 5; + +// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode +#ifdef NDEBUG +const LogSeverity LOG_DFATAL = LOG_ERROR; +#else +const LogSeverity LOG_DFATAL = LOG_FATAL; +#endif + +// A few definitions of macros that don't generate much code. These are used +// by LOG() and LOG_IF, etc. Since these are used all over our code, it's +// better to have compact code for these operations. +#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \ + logging::ClassName(__FILE__, __LINE__, logging::LOG_INFO , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \ + logging::ClassName(__FILE__, __LINE__, logging::LOG_WARNING , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \ + logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName, ...) \ + logging::ClassName(__FILE__, __LINE__, \ + logging::LOG_ERROR_REPORT , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \ + logging::ClassName(__FILE__, __LINE__, logging::LOG_FATAL , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ + logging::ClassName(__FILE__, __LINE__, logging::LOG_DFATAL , ##__VA_ARGS__) + +#define COMPACT_GOOGLE_LOG_INFO \ + COMPACT_GOOGLE_LOG_EX_INFO(LogMessage) +#define COMPACT_GOOGLE_LOG_WARNING \ + COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) +#define COMPACT_GOOGLE_LOG_ERROR \ + COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) +#define COMPACT_GOOGLE_LOG_ERROR_REPORT \ + COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(LogMessage) +#define COMPACT_GOOGLE_LOG_FATAL \ + COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) +#define COMPACT_GOOGLE_LOG_DFATAL \ + COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) + +// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets +// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that +// the Windows SDK does for consistency. +#define ERROR 0 +#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_ERROR(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR +// Needed for LOG_IS_ON(ERROR). +const LogSeverity LOG_0 = LOG_ERROR; + +// As special cases, we can assume that LOG_IS_ON(ERROR_REPORT) and +// LOG_IS_ON(FATAL) always hold. Also, LOG_IS_ON(DFATAL) always holds +// in debug mode. In particular, CHECK()s will always fire if they +// fail. +#define LOG_IS_ON(severity) \ + ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel()) + +// We can't do any caching tricks with VLOG_IS_ON() like the +// google-glog version since it requires GCC extensions. This means +// that using the v-logging functions in conjunction with --vmodule +// may be slow. +#define VLOG_IS_ON(verboselevel) \ + ((verboselevel) <= ::logging::GetVlogLevel(__FILE__)) + +// Helper macro which avoids evaluating the arguments to a stream if +// the condition doesn't hold. +#define LAZY_STREAM(stream, condition) \ + !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() + +#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity)) +#define LOG_IF(severity, condition) \ + LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) + +#define SYSLOG(severity) LOG(severity) +#define SYSLOG_IF(severity, condition) LOG_IF(severity, condition) + +// The VLOG macros log with negative verbosities. +#define VLOG_STREAM(verbose_level) \ + logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream() + +#define VLOG(verbose_level) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) + +#define VLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#if defined (OS_WIN) +#define VPLOG_STREAM(verbose_level) \ + logging::Win32ErrorLogMessage(__FILE__, __LINE__, -verbose_level, \ + ::logging::GetLastSystemErrorCode()).stream() +#elif defined(OS_POSIX) +#define VPLOG_STREAM(verbose_level) \ + logging::ErrnoLogMessage(__FILE__, __LINE__, -verbose_level, \ + ::logging::GetLastSystemErrorCode()).stream() +#endif + +#define VPLOG(verbose_level) \ + LAZY_STREAM(VPLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) + +#define VPLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VPLOG_STREAM(verbose_level), \ + VLOG_IS_ON(verbose_level) && (condition)) + +// TODO(akalin): Add more VLOG variants, e.g. VPLOG. + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". " +#define SYSLOG_ASSERT(condition) \ + SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". " + +#if defined(OS_WIN) +#define LOG_GETLASTERROR_STREAM(severity) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \ + ::logging::GetLastSystemErrorCode()).stream() +#define LOG_GETLASTERROR(severity) \ + LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity), LOG_IS_ON(severity)) +#define LOG_GETLASTERROR_MODULE_STREAM(severity, module) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(Win32ErrorLogMessage, \ + ::logging::GetLastSystemErrorCode(), module).stream() +#define LOG_GETLASTERROR_MODULE(severity, module) \ + LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity, module), \ + LOG_IS_ON(severity)) +// PLOG_STREAM is used by PLOG, which is the usual error logging macro +// for each platform. +#define PLOG_STREAM(severity) LOG_GETLASTERROR_STREAM(severity) +#elif defined(OS_POSIX) +#define LOG_ERRNO_STREAM(severity) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(ErrnoLogMessage, \ + ::logging::GetLastSystemErrorCode()).stream() +#define LOG_ERRNO(severity) \ + LAZY_STREAM(LOG_ERRNO_STREAM(severity), LOG_IS_ON(severity)) +// PLOG_STREAM is used by PLOG, which is the usual error logging macro +// for each platform. +#define PLOG_STREAM(severity) LOG_ERRNO_STREAM(severity) +// TODO(tschmelcher): Should we add OSStatus logging for Mac? +#endif + +#define PLOG(severity) \ + LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity)) + +#define PLOG_IF(severity, condition) \ + LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG, so the check will be executed regardless of +// compilation mode. +// +// We make sure CHECK et al. always evaluates their arguments, as +// doing CHECK(FunctionWithSideEffect()) is a common idiom. +#define CHECK(condition) \ + LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \ + << "Check failed: " #condition ". " + +#define PCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \ + << "Check failed: " #condition ". " + +// Build the error message string. This is separate from the "Impl" +// function template because it is not performance critical and so can +// be out of line, while the "Impl" code should be inline. Caller +// takes ownership of the returned string. +template +std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { + std::ostringstream ss; + ss << names << " (" << v1 << " vs. " << v2 << ")"; + std::string* msg = new std::string(ss.str()); + return msg; +} + +// MSVC doesn't like complex extern templates and DLLs. +#if !defined(COMPILER_MSVC) && !defined(COMPONENT_BUILD) +// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated +// in logging.cc. +extern template std::string* MakeCheckOpString( + const int&, const int&, const char* names); +extern template std::string* MakeCheckOpString( + const unsigned long&, const unsigned long&, const char* names); +extern template std::string* MakeCheckOpString( + const unsigned long&, const unsigned int&, const char* names); +extern template std::string* MakeCheckOpString( + const unsigned int&, const unsigned long&, const char* names); +extern template std::string* MakeCheckOpString( + const std::string&, const std::string&, const char* name); +#endif + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. +// +// TODO(akalin): Rewrite this so that constructs like if (...) +// CHECK_EQ(...) else { ... } work properly. +#define CHECK_OP(name, op, val1, val2) \ + if (std::string* _result = \ + logging::Check##name##Impl((val1), (val2), \ + #val1 " " #op " " #val2)) \ + logging::LogMessage(__FILE__, __LINE__, _result).stream() + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (v1 op v2) return NULL; \ + else return MakeCheckOpString(v1, v2, names); \ + } \ + inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ + if (v1 op v2) return NULL; \ + else return MakeCheckOpString(v1, v2, names); \ + } +DEFINE_CHECK_OP_IMPL(EQ, ==) +DEFINE_CHECK_OP_IMPL(NE, !=) +DEFINE_CHECK_OP_IMPL(LE, <=) +DEFINE_CHECK_OP_IMPL(LT, < ) +DEFINE_CHECK_OP_IMPL(GE, >=) +DEFINE_CHECK_OP_IMPL(GT, > ) +#undef DEFINE_CHECK_OP_IMPL + +#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2) + +// http://crbug.com/16512 is open for a real fix for this. For now, Windows +// uses OFFICIAL_BUILD and other platforms use the branding flag when NDEBUG is +// defined. +#if ( defined(OS_WIN) && defined(OFFICIAL_BUILD)) || \ + (!defined(OS_WIN) && defined(NDEBUG) && defined(GOOGLE_CHROME_BUILD)) +// Used by unit tests. +#define LOGGING_IS_OFFICIAL_BUILD + +// In order to have optimized code for official builds, remove DLOGs and +// DCHECKs. +#define ENABLE_DLOG 0 +#define ENABLE_DCHECK 0 + +#elif defined(NDEBUG) +// Otherwise, if we're a release build, remove DLOGs but not DCHECKs +// (since those can still be turned on via a command-line flag). +#define ENABLE_DLOG 0 +#define ENABLE_DCHECK 1 + +#else +// Otherwise, we're a debug build so enable DLOGs and DCHECKs. +#define ENABLE_DLOG 1 +#define ENABLE_DCHECK 1 +#endif + +// Definitions for DLOG et al. + +#if ENABLE_DLOG + +#define DLOG_IS_ON(severity) LOG_IS_ON(severity) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) +#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition) +#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition) +#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition) + +#else // ENABLE_DLOG + +// If ENABLE_DLOG is off, we want to avoid emitting any references to +// |condition| (which may reference a variable defined only if NDEBUG +// is not defined). Contrast this with DCHECK et al., which has +// different behavior. + +#define DLOG_EAT_STREAM_PARAMETERS \ + true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL) + +#define DLOG_IS_ON(severity) false +#define DLOG_IF(severity, condition) DLOG_EAT_STREAM_PARAMETERS +#define DLOG_ASSERT(condition) DLOG_EAT_STREAM_PARAMETERS +#define DPLOG_IF(severity, condition) DLOG_EAT_STREAM_PARAMETERS +#define DVLOG_IF(verboselevel, condition) DLOG_EAT_STREAM_PARAMETERS +#define DVPLOG_IF(verboselevel, condition) DLOG_EAT_STREAM_PARAMETERS + +#endif // ENABLE_DLOG + +// DEBUG_MODE is for uses like +// if (DEBUG_MODE) foo.CheckThatFoo(); +// instead of +// #ifndef NDEBUG +// foo.CheckThatFoo(); +// #endif +// +// We tie its state to ENABLE_DLOG. +enum { DEBUG_MODE = ENABLE_DLOG }; + +#undef ENABLE_DLOG + +#define DLOG(severity) \ + LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity)) + +#if defined(OS_WIN) +#define DLOG_GETLASTERROR(severity) \ + LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity), DLOG_IS_ON(severity)) +#define DLOG_GETLASTERROR_MODULE(severity, module) \ + LAZY_STREAM(LOG_GETLASTERROR_STREAM(severity, module), \ + DLOG_IS_ON(severity)) +#elif defined(OS_POSIX) +#define DLOG_ERRNO(severity) \ + LAZY_STREAM(LOG_ERRNO_STREAM(severity), DLOG_IS_ON(severity)) +#endif + +#define DPLOG(severity) \ + LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity)) + +#define DVLOG(verboselevel) DLOG_IF(INFO, VLOG_IS_ON(verboselevel)) + +#define DVPLOG(verboselevel) DVPLOG_IF(verboselevel, VLOG_IS_ON(verboselevel)) + +// Definitions for DCHECK et al. + +#if ENABLE_DCHECK + +#if defined(NDEBUG) + +#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_ERROR_REPORT +const LogSeverity LOG_DCHECK = LOG_ERROR_REPORT; +BASE_EXPORT extern DcheckState g_dcheck_state; +#define DCHECK_IS_ON() \ + ((::logging::g_dcheck_state == \ + ::logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS) && \ + LOG_IS_ON(DCHECK)) + +#else // defined(NDEBUG) + +// On a regular debug build, we want to have DCHECKs enabled. +#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_FATAL(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL +const LogSeverity LOG_DCHECK = LOG_FATAL; +#define DCHECK_IS_ON() true + +#endif // defined(NDEBUG) + +#else // ENABLE_DCHECK + +// These are just dummy values since DCHECK_IS_ON() is always false in +// this case. +#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_INFO(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO +const LogSeverity LOG_DCHECK = LOG_INFO; +#define DCHECK_IS_ON() false + +#endif // ENABLE_DCHECK +#undef ENABLE_DCHECK + +// DCHECK et al. make sure to reference |condition| regardless of +// whether DCHECKs are enabled; this is so that we don't get unused +// variable warnings if the only use of a variable is in a DCHECK. +// This behavior is different from DLOG_IF et al. + +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \ + << "Check failed: " #condition ". " + +#define DPCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \ + << "Check failed: " #condition ". " + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use DCHECK_EQ et al below. +#define DCHECK_OP(name, op, val1, val2) \ + if (DCHECK_IS_ON()) \ + if (std::string* _result = \ + logging::Check##name##Impl((val1), (val2), \ + #val1 " " #op " " #val2)) \ + logging::LogMessage( \ + __FILE__, __LINE__, ::logging::LOG_DCHECK, \ + _result).stream() + +// Equality/Inequality checks - compare two values, and log a +// LOG_DCHECK message including the two values when the result is not +// as expected. The values must have operator<<(ostream, ...) +// defined. +// +// You may append to the error message like so: +// DCHECK_NE(1, 2) << ": The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// DCHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These may not compile correctly if one of the arguments is a pointer +// and the other is NULL. To work around this, simply static_cast NULL to the +// type of the desired pointer. + +#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2) +#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2) +#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2) +#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2) +#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) +#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) + +#if defined(OS_ANDROID) && !defined(OFFICIAL_BUILD) +// TODO(port): fix once "enough" works +#define NOTREACHED() LOG(ERROR) << "NOTREACHED()" +#else +#define NOTREACHED() DCHECK(false) +#endif + +// Redefine the standard assert to use our nice log files +#undef assert +#define assert(x) DLOG_ASSERT(x) + +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class BASE_EXPORT LogMessage { + public: + LogMessage(const char* file, int line, LogSeverity severity, int ctr); + + // Two special constructors that generate reduced amounts of code at + // LOG call sites for common cases. + // + // Used for LOG(INFO): Implied are: + // severity = LOG_INFO, ctr = 0 + // + // Using this constructor instead of the more complex constructor above + // saves a couple of bytes per call site. + LogMessage(const char* file, int line); + + // Used for LOG(severity) where severity != INFO. Implied + // are: ctr = 0 + // + // Using this constructor instead of the more complex constructor above + // saves a couple of bytes per call site. + LogMessage(const char* file, int line, LogSeverity severity); + + // A special constructor used for check failures. Takes ownership + // of the given string. + // Implied severity = LOG_FATAL + LogMessage(const char* file, int line, std::string* result); + + // A special constructor used for check failures, with the option to + // specify severity. Takes ownership of the given string. + LogMessage(const char* file, int line, LogSeverity severity, + std::string* result); + + ~LogMessage(); + + std::ostream& stream() { return stream_; } + + private: + void Init(const char* file, int line); + + LogSeverity severity_; + std::ostringstream stream_; + size_t message_start_; // Offset of the start of the message (past prefix + // info). + // The file and line information passed in to the constructor. + const char* file_; + const int line_; + +#if defined(OS_WIN) + // Stores the current value of GetLastError in the constructor and restores + // it in the destructor by calling SetLastError. + // This is useful since the LogMessage class uses a lot of Win32 calls + // that will lose the value of GLE and the code that called the log function + // will have lost the thread error value when the log call returns. + class SaveLastError { + public: + SaveLastError(); + ~SaveLastError(); + + unsigned long get_error() const { return last_error_; } + + protected: + unsigned long last_error_; + }; + + SaveLastError last_error_; +#endif + + DISALLOW_COPY_AND_ASSIGN(LogMessage); +}; + +// A non-macro interface to the log facility; (useful +// when the logging level is not a compile-time constant). +inline void LogAtLevel(int const log_level, std::string const &msg) { + LogMessage(__FILE__, __LINE__, log_level).stream() << msg; +} + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + +#if defined(OS_WIN) +typedef unsigned long SystemErrorCode; +#elif defined(OS_POSIX) +typedef int SystemErrorCode; +#endif + +// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to +// pull in windows.h just for GetLastError() and DWORD. +BASE_EXPORT SystemErrorCode GetLastSystemErrorCode(); + +#if defined(OS_WIN) +// Appends a formatted system message of the GetLastError() type. +class BASE_EXPORT Win32ErrorLogMessage { + public: + Win32ErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err, + const char* module); + + Win32ErrorLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + + // Appends the error message before destructing the encapsulated class. + ~Win32ErrorLogMessage(); + + std::ostream& stream() { return log_message_.stream(); } + + private: + SystemErrorCode err_; + // Optional name of the module defining the error. + const char* module_; + LogMessage log_message_; + + DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage); +}; +#elif defined(OS_POSIX) +// Appends a formatted system message of the errno type +class BASE_EXPORT ErrnoLogMessage { + public: + ErrnoLogMessage(const char* file, + int line, + LogSeverity severity, + SystemErrorCode err); + + // Appends the error message before destructing the encapsulated class. + ~ErrnoLogMessage(); + + std::ostream& stream() { return log_message_.stream(); } + + private: + SystemErrorCode err_; + LogMessage log_message_; + + DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage); +}; +#endif // OS_WIN + +// Closes the log file explicitly if open. +// NOTE: Since the log file is opened as necessary by the action of logging +// statements, there's no guarantee that it will stay closed +// after this call. +BASE_EXPORT void CloseLogFile(); + +// Async signal safe logging mechanism. +BASE_EXPORT void RawLog(int level, const char* message); + +#define RAW_LOG(level, message) logging::RawLog(logging::LOG_ ## level, message) + +#define RAW_CHECK(condition) \ + do { \ + if (!(condition)) \ + logging::RawLog(logging::LOG_FATAL, "Check failed: " #condition "\n"); \ + } while (0) + +} // namespace logging + +// These functions are provided as a convenience for logging, which is where we +// use streams (it is against Google style to use streams in other places). It +// is designed to allow you to emit non-ASCII Unicode strings to the log file, +// which is normally ASCII. It is relatively slow, so try not to use it for +// common cases. Non-ASCII characters will be converted to UTF-8 by these +// operators. +BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr); +inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) { + return out << wstr.c_str(); +} + +// The NOTIMPLEMENTED() macro annotates codepaths which have +// not been implemented yet. +// +// The implementation of this macro is controlled by NOTIMPLEMENTED_POLICY: +// 0 -- Do nothing (stripped by compiler) +// 1 -- Warn at compile time +// 2 -- Fail at compile time +// 3 -- Fail at runtime (DCHECK) +// 4 -- [default] LOG(ERROR) at runtime +// 5 -- LOG(ERROR) at runtime, only once per call-site + +#ifndef NOTIMPLEMENTED_POLICY +// Select default policy: LOG(ERROR) +#define NOTIMPLEMENTED_POLICY 4 +#endif + +#if defined(COMPILER_GCC) +// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name +// of the current function in the NOTIMPLEMENTED message. +#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__ +#else +#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED" +#endif + +#if NOTIMPLEMENTED_POLICY == 0 +#define NOTIMPLEMENTED() ; +#elif NOTIMPLEMENTED_POLICY == 1 +// TODO, figure out how to generate a warning +#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED) +#elif NOTIMPLEMENTED_POLICY == 2 +#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED) +#elif NOTIMPLEMENTED_POLICY == 3 +#define NOTIMPLEMENTED() NOTREACHED() +#elif NOTIMPLEMENTED_POLICY == 4 +#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG +#elif NOTIMPLEMENTED_POLICY == 5 +#define NOTIMPLEMENTED() do {\ + static int count = 0;\ + LOG_IF(ERROR, 0 == count++) << NOTIMPLEMENTED_MSG;\ +} while(0) +#endif + +namespace base { + +class StringPiece; + +// Allows StringPiece to be logged. +BASE_EXPORT std::ostream& operator<<(std::ostream& o, const StringPiece& piece); + +} // namespace base + +#endif // BASE_LOGGING_H_ diff --git a/base/memory/ref_counted.cc b/base/memory/ref_counted.cc new file mode 100644 index 0000000..31ad509 --- /dev/null +++ b/base/memory/ref_counted.cc @@ -0,0 +1,95 @@ +// 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. + +#include "base/memory/ref_counted.h" + +#include "base/logging.h" +#include "base/threading/thread_collision_warner.h" + +namespace base { + +namespace subtle { + +RefCountedBase::RefCountedBase() + : ref_count_(0) +#ifndef NDEBUG + , in_dtor_(false) +#endif + { +} + +RefCountedBase::~RefCountedBase() { +#ifndef NDEBUG + DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; +#endif +} + +void RefCountedBase::AddRef() const { + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); +#ifndef NDEBUG + DCHECK(!in_dtor_); +#endif + ++ref_count_; +} + +bool RefCountedBase::Release() const { + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); +#ifndef NDEBUG + DCHECK(!in_dtor_); +#endif + if (--ref_count_ == 0) { +#ifndef NDEBUG + in_dtor_ = true; +#endif + return true; + } + return false; +} + +bool RefCountedThreadSafeBase::HasOneRef() const { + return AtomicRefCountIsOne( + &const_cast(this)->ref_count_); +} + +RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) { +#ifndef NDEBUG + in_dtor_ = false; +#endif +} + +RefCountedThreadSafeBase::~RefCountedThreadSafeBase() { +#ifndef NDEBUG + DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " + "calling Release()"; +#endif +} + +void RefCountedThreadSafeBase::AddRef() const { +#ifndef NDEBUG + DCHECK(!in_dtor_); +#endif + AtomicRefCountInc(&ref_count_); +} + +bool RefCountedThreadSafeBase::Release() const { +#ifndef NDEBUG + DCHECK(!in_dtor_); + DCHECK(!AtomicRefCountIsZero(&ref_count_)); +#endif + if (!AtomicRefCountDec(&ref_count_)) { +#ifndef NDEBUG + in_dtor_ = true; +#endif + return true; + } + return false; +} + +} // namespace subtle + +} // namespace base diff --git a/base/memory/ref_counted.h b/base/memory/ref_counted.h new file mode 100644 index 0000000..439fda4 --- /dev/null +++ b/base/memory/ref_counted.h @@ -0,0 +1,299 @@ +// 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. + +#ifndef BASE_MEMORY_REF_COUNTED_H_ +#define BASE_MEMORY_REF_COUNTED_H_ +#pragma once + +#include "base/atomic_ref_count.h" +#include "base/base_export.h" +#include "base/threading/thread_collision_warner.h" + +namespace base { + +namespace subtle { + +class BASE_EXPORT RefCountedBase { + public: + static bool ImplementsThreadSafeReferenceCounting() { return false; } + + bool HasOneRef() const { return ref_count_ == 1; } + + protected: + RefCountedBase(); + ~RefCountedBase(); + + void AddRef() const; + + // Returns true if the object should self-delete. + bool Release() const; + + private: + mutable int ref_count_; +#ifndef NDEBUG + mutable bool in_dtor_; +#endif + + DFAKE_MUTEX(add_release_); + + DISALLOW_COPY_AND_ASSIGN(RefCountedBase); +}; + +class BASE_EXPORT RefCountedThreadSafeBase { + public: + static bool ImplementsThreadSafeReferenceCounting() { return true; } + + bool HasOneRef() const; + + protected: + RefCountedThreadSafeBase(); + ~RefCountedThreadSafeBase(); + + void AddRef() const; + + // Returns true if the object should self-delete. + bool Release() const; + + private: + mutable AtomicRefCount ref_count_; +#ifndef NDEBUG + mutable bool in_dtor_; +#endif + + DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase); +}; + +} // namespace subtle + +// +// A base class for reference counted classes. Otherwise, known as a cheap +// knock-off of WebKit's RefCounted class. To use this guy just extend your +// class from it like so: +// +// class MyFoo : public base::RefCounted { +// ... +// private: +// friend class base::RefCounted; +// ~MyFoo(); +// }; +// +// You should always make your destructor private, to avoid any code deleting +// the object accidently while there are references to it. +template +class RefCounted : public subtle::RefCountedBase { + public: + RefCounted() { } + ~RefCounted() { } + + void AddRef() const { + subtle::RefCountedBase::AddRef(); + } + + void Release() const { + if (subtle::RefCountedBase::Release()) { + delete static_cast(this); + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(RefCounted); +}; + +// Forward declaration. +template class RefCountedThreadSafe; + +// Default traits for RefCountedThreadSafe. Deletes the object when its ref +// count reaches 0. Overload to delete it on a different thread etc. +template +struct DefaultRefCountedThreadSafeTraits { + static void Destruct(const T* x) { + // Delete through RefCountedThreadSafe to make child classes only need to be + // friend with RefCountedThreadSafe instead of this struct, which is an + // implementation detail. + RefCountedThreadSafe::DeleteInternal(x); + } +}; + +// +// A thread-safe variant of RefCounted +// +// class MyFoo : public base::RefCountedThreadSafe { +// ... +// }; +// +// If you're using the default trait, then you should add compile time +// asserts that no one else is deleting your object. i.e. +// private: +// friend class base::RefCountedThreadSafe; +// ~MyFoo(); +template > +class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase { + public: + RefCountedThreadSafe() { } + ~RefCountedThreadSafe() { } + + void AddRef() const { + subtle::RefCountedThreadSafeBase::AddRef(); + } + + void Release() const { + if (subtle::RefCountedThreadSafeBase::Release()) { + Traits::Destruct(static_cast(this)); + } + } + + private: + friend struct DefaultRefCountedThreadSafeTraits; + static void DeleteInternal(const T* x) { delete x; } + + DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe); +}; + +// +// A wrapper for some piece of data so we can place other things in +// scoped_refptrs<>. +// +template +class RefCountedData : public base::RefCounted< base::RefCountedData > { + public: + RefCountedData() : data() {} + RefCountedData(const T& in_value) : data(in_value) {} + + T data; +}; + +} // namespace base + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// }; +// +// void some_function() { +// scoped_refptr foo = new MyFoo(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = new MyFoo(); +// ... +// foo = NULL; // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references NULL. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// +template +class scoped_refptr { + public: + scoped_refptr() : ptr_(NULL) { + } + + scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + ptr_->AddRef(); + } + + scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + template + scoped_refptr(const scoped_refptr& r) : ptr_(r.get()) { + if (ptr_) + ptr_->AddRef(); + } + + ~scoped_refptr() { + if (ptr_) + ptr_->Release(); + } + + T* get() const { return ptr_; } + operator T*() const { return ptr_; } + T* operator->() const { return ptr_; } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + T* release() { + T* retVal = ptr_; + ptr_ = NULL; + return retVal; + } + + scoped_refptr& operator=(T* p) { + // AddRef first so that self assignment should work + if (p) + p->AddRef(); + if (ptr_ ) + ptr_ ->Release(); + ptr_ = p; + return *this; + } + + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.ptr_; + } + + template + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.get(); + } + + void swap(T** pp) { + T* p = ptr_; + ptr_ = *pp; + *pp = p; + } + + void swap(scoped_refptr& r) { + swap(&r.ptr_); + } + + protected: + T* ptr_; +}; + +// Handy utility for creating a scoped_refptr out of a T* explicitly without +// having to retype all the template arguments +template +scoped_refptr make_scoped_refptr(T* t) { + return scoped_refptr(t); +} + +#endif // BASE_MEMORY_REF_COUNTED_H_ diff --git a/base/memory/ref_counted_memory.cc b/base/memory/ref_counted_memory.cc new file mode 100644 index 0000000..7e034f9 --- /dev/null +++ b/base/memory/ref_counted_memory.cc @@ -0,0 +1,72 @@ +// 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. + +#include "base/memory/ref_counted_memory.h" + +#include "base/logging.h" + +RefCountedMemory::RefCountedMemory() { +} + +RefCountedMemory::~RefCountedMemory() { +} + +const unsigned char* RefCountedStaticMemory::front() const { + return data_; +} + +size_t RefCountedStaticMemory::size() const { + return length_; +} + +RefCountedBytes::RefCountedBytes() { +} + +RefCountedBytes::RefCountedBytes(const std::vector& initializer) + : data_(initializer) { +} + +RefCountedBytes* RefCountedBytes::TakeVector( + std::vector* to_destroy) { + RefCountedBytes* bytes = new RefCountedBytes; + bytes->data_.swap(*to_destroy); + return bytes; +} + +const unsigned char* RefCountedBytes::front() const { + // STL will assert if we do front() on an empty vector, but calling code + // expects a NULL. + return size() ? &data_.front() : NULL; +} + +size_t RefCountedBytes::size() const { + return data_.size(); +} + +RefCountedBytes::~RefCountedBytes() { +} + +namespace base { + +RefCountedString::RefCountedString() {} + +RefCountedString::~RefCountedString() {} + +// static +RefCountedString* RefCountedString::TakeString(std::string* to_destroy) { + RefCountedString* self = new RefCountedString; + to_destroy->swap(self->data_); + return self; +} + +const unsigned char* RefCountedString::front() const { + return data_.empty() ? NULL : + reinterpret_cast(data_.data()); +} + +size_t RefCountedString::size() const { + return data_.size(); +} + +} // namespace base diff --git a/base/memory/ref_counted_memory.h b/base/memory/ref_counted_memory.h new file mode 100644 index 0000000..7b55d14 --- /dev/null +++ b/base/memory/ref_counted_memory.h @@ -0,0 +1,119 @@ +// 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. + +#ifndef BASE_MEMORY_REF_COUNTED_MEMORY_H_ +#define BASE_MEMORY_REF_COUNTED_MEMORY_H_ +#pragma once + +#include +#include + +#include "base/base_export.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" + +// TODO(erg): The contents of this file should be in a namespace. This would +// require touching >100 files in chrome/ though. + +// A generic interface to memory. This object is reference counted because one +// of its two subclasses own the data they carry, and we need to have +// heterogeneous containers of these two types of memory. +class BASE_EXPORT RefCountedMemory + : public base::RefCountedThreadSafe { + public: + // Retrieves a pointer to the beginning of the data we point to. If the data + // is empty, this will return NULL. + virtual const unsigned char* front() const = 0; + + // Size of the memory pointed to. + virtual size_t size() const = 0; + + protected: + friend class base::RefCountedThreadSafe; + RefCountedMemory(); + virtual ~RefCountedMemory(); +}; + +// An implementation of RefCountedMemory, where the ref counting does not +// matter. +class BASE_EXPORT RefCountedStaticMemory : public RefCountedMemory { + public: + RefCountedStaticMemory() + : data_(NULL), length_(0) {} + RefCountedStaticMemory(const unsigned char* data, size_t length) + : data_(length ? data : NULL), length_(length) {} + + // Overridden from RefCountedMemory: + virtual const unsigned char* front() const OVERRIDE; + virtual size_t size() const OVERRIDE; + + private: + const unsigned char* data_; + size_t length_; + + DISALLOW_COPY_AND_ASSIGN(RefCountedStaticMemory); +}; + +// An implementation of RefCountedMemory, where we own our the data in a +// vector. +class BASE_EXPORT RefCountedBytes : public RefCountedMemory { + public: + RefCountedBytes(); + + // Constructs a RefCountedBytes object by _copying_ from |initializer|. + RefCountedBytes(const std::vector& initializer); + + // Constructs a RefCountedBytes object by performing a swap. (To non + // destructively build a RefCountedBytes, use the constructor that takes a + // vector.) + static RefCountedBytes* TakeVector(std::vector* to_destroy); + + // Overridden from RefCountedMemory: + virtual const unsigned char* front() const OVERRIDE; + virtual size_t size() const OVERRIDE; + + const std::vector& data() const { return data_; } + std::vector& data() { return data_; } + + private: + friend class base::RefCountedThreadSafe; + virtual ~RefCountedBytes(); + + std::vector data_; + + DISALLOW_COPY_AND_ASSIGN(RefCountedBytes); +}; + +namespace base { + +// An implementation of RefCountedMemory, where the bytes are stored in an STL +// string. Use this if your data naturally arrives in that format. +class BASE_EXPORT RefCountedString : public RefCountedMemory { + public: + RefCountedString(); + + // Constructs a RefCountedString object by performing a swap. (To non + // destructively build a RefCountedString, use the default constructor and + // copy into object->data()). + static RefCountedString* TakeString(std::string* to_destroy); + + // Overridden from RefCountedMemory: + virtual const unsigned char* front() const OVERRIDE; + virtual size_t size() const OVERRIDE; + + const std::string& data() const { return data_; } + std::string& data() { return data_; } + + private: + friend class base::RefCountedThreadSafe; + virtual ~RefCountedString(); + + std::string data_; + + DISALLOW_COPY_AND_ASSIGN(RefCountedString); +}; + +} // namespace base + +#endif // BASE_MEMORY_REF_COUNTED_MEMORY_H_ diff --git a/base/memory/ref_counted_memory_unittest.cc b/base/memory/ref_counted_memory_unittest.cc new file mode 100644 index 0000000..1936040 --- /dev/null +++ b/base/memory/ref_counted_memory_unittest.cc @@ -0,0 +1,45 @@ +// 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. + +#include "base/memory/ref_counted_memory.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +TEST(RefCountedMemoryUnitTest, RefCountedStaticMemory) { + scoped_refptr mem = new RefCountedStaticMemory( + reinterpret_cast("static mem00"), 10); + + EXPECT_EQ(10U, mem->size()); + EXPECT_EQ("static mem", + std::string(reinterpret_cast(mem->front()), + mem->size())); +} + +TEST(RefCountedMemoryUnitTest, RefCountedBytes) { + std::vector data; + data.push_back(45); + data.push_back(99); + scoped_refptr mem = RefCountedBytes::TakeVector(&data); + + EXPECT_EQ(0U, data.size()); + + EXPECT_EQ(2U, mem->size()); + EXPECT_EQ(45U, mem->front()[0]); + EXPECT_EQ(99U, mem->front()[1]); +} + +TEST(RefCountedMemoryUnitTest, RefCountedString) { + std::string s("destroy me"); + scoped_refptr mem = RefCountedString::TakeString(&s); + + EXPECT_EQ(0U, s.size()); + + EXPECT_EQ(10U, mem->size()); + EXPECT_EQ('d', mem->front()[0]); + EXPECT_EQ('e', mem->front()[1]); +} + +} // namespace base diff --git a/base/memory/ref_counted_unittest.cc b/base/memory/ref_counted_unittest.cc new file mode 100644 index 0000000..dcc292f --- /dev/null +++ b/base/memory/ref_counted_unittest.cc @@ -0,0 +1,36 @@ +// 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. + +#include "base/memory/ref_counted.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class SelfAssign : public base::RefCounted { + friend class base::RefCounted; + + ~SelfAssign() {} +}; + +class CheckDerivedMemberAccess : public scoped_refptr { + public: + CheckDerivedMemberAccess() { + // This shouldn't compile if we don't have access to the member variable. + SelfAssign** pptr = &ptr_; + EXPECT_EQ(*pptr, ptr_); + } +}; + +} // end namespace + +TEST(RefCountedUnitTest, TestSelfAssignment) { + SelfAssign* p = new SelfAssign; + scoped_refptr var(p); + var = var; + EXPECT_EQ(var.get(), p); +} + +TEST(RefCountedUnitTest, ScopedRefPtrMemberAccess) { + CheckDerivedMemberAccess check; +} diff --git a/base/pickle.cc b/base/pickle.cc new file mode 100644 index 0000000..f42f249 --- /dev/null +++ b/base/pickle.cc @@ -0,0 +1,449 @@ +// 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. + +#include "base/pickle.h" + +#include + +#include // for max() +#include + +//------------------------------------------------------------------------------ + +// static +const int Pickle::kPayloadUnit = 64; + +// We mark a read only pickle with a special capacity_. +static const size_t kCapacityReadOnly = std::numeric_limits::max(); + +// Payload is uint32 aligned. + +Pickle::Pickle() + : header_(NULL), + header_size_(sizeof(Header)), + capacity_(0), + variable_buffer_offset_(0) { + Resize(kPayloadUnit); + header_->payload_size = 0; +} + +Pickle::Pickle(int header_size) + : header_(NULL), + header_size_(AlignInt(header_size, sizeof(uint32))), + capacity_(0), + variable_buffer_offset_(0) { + DCHECK_GE(static_cast(header_size), sizeof(Header)); + DCHECK_LE(header_size, kPayloadUnit); + Resize(kPayloadUnit); + header_->payload_size = 0; +} + +Pickle::Pickle(const char* data, int data_len) + : header_(reinterpret_cast(const_cast(data))), + header_size_(0), + capacity_(kCapacityReadOnly), + variable_buffer_offset_(0) +{ + + if (data_len >= static_cast(sizeof(Header))) + { + header_size_ = data_len - header_->payload_size; + } + + if (header_size_ > static_cast(data_len)) + { + header_size_ = 0; + } + + + if (header_size_ != AlignInt(header_size_, sizeof(uint32))) + { + header_size_ = 0; + } + + + // If there is anything wrong with the data, we're not going to use it. + if (!header_size_) + header_ = NULL; +} + +Pickle::Pickle(const Pickle& other) + : header_(NULL), + header_size_(other.header_size_), + capacity_(0), + variable_buffer_offset_(other.variable_buffer_offset_) { + size_t payload_size = header_size_ + other.header_->payload_size; + bool resized = Resize(payload_size); + CHECK(resized); // Realloc failed. + memcpy(header_, other.header_, payload_size); +} + +Pickle::~Pickle() { + if (capacity_ != kCapacityReadOnly) + free(header_); +} + +Pickle& Pickle::operator=(const Pickle& other) { + if (this == &other) { + NOTREACHED(); + return *this; + } + if (capacity_ == kCapacityReadOnly) { + header_ = NULL; + capacity_ = 0; + } + if (header_size_ != other.header_size_) { + free(header_); + header_ = NULL; + header_size_ = other.header_size_; + } + bool resized = Resize(other.header_size_ + other.header_->payload_size); + CHECK(resized); // Realloc failed. + memcpy(header_, other.header_, + other.header_size_ + other.header_->payload_size); + variable_buffer_offset_ = other.variable_buffer_offset_; + return *this; +} + +bool Pickle::ReadBool(void** 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::ReadInt(void** iter, int* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + // TODO(jar): http://crbug.com/13108 Pickle should be cleaned up, and not + // dependent on alignment. + // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result)); + *result = *reinterpret_cast(*iter); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadLong(void** iter, long* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + // TODO(jar): http://crbug.com/13108 Pickle should be cleaned up, and not + // dependent on alignment. + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadSize(void** iter, size_t* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + // TODO(jar): http://crbug.com/13108 Pickle should be cleaned up, and not + // dependent on alignment. + // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result)); + *result = *reinterpret_cast(*iter); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadUInt16(void** iter, uint16* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadUInt32(void** iter, uint32* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadInt64(void** iter, int64* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadUInt64(void** iter, uint64* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + + +bool Pickle::ReadString(void** iter, std::string* result) const { + DCHECK(iter); + + int len; + if (!ReadLength(iter, &len)) + return false; + if (!IteratorHasRoomFor(*iter, len)) + return false; + + char* chars = reinterpret_cast(*iter); + result->assign(chars, len); + + UpdateIter(iter, len); + return true; +} + +/* +bool Pickle::ReadWString(void** iter, std::wstring* result) const { + DCHECK(iter); + + int len; + if (!ReadLength(iter, &len)) + return false; + // Avoid integer overflow. + if (len > INT_MAX / static_cast(sizeof(wchar_t))) + return false; + if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t))) + return false; + + wchar_t* chars = reinterpret_cast(*iter); + result->assign(chars, len); + + UpdateIter(iter, len * sizeof(wchar_t)); + return true; +} + +bool Pickle::ReadString16(void** iter, string16* result) const { + DCHECK(iter); + + int len; + if (!ReadLength(iter, &len)) + return false; + if (!IteratorHasRoomFor(*iter, len * sizeof(char16))) + return false; + + char16* chars = reinterpret_cast(*iter); + result->assign(chars, len); + + UpdateIter(iter, len * sizeof(char16)); + return true; +} +*/ + +bool Pickle::ReadData(void** iter, const char** data, int* length) const { + DCHECK(iter); + DCHECK(data); + DCHECK(length); + *length = 0; + *data = 0; + + if (!ReadLength(iter, length)) + return false; + + return ReadBytes(iter, data, *length); +} + +bool Pickle::ReadBytes(void** iter, const char** data, int length) const { + DCHECK(iter); + DCHECK(data); + *data = 0; + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, length)) + return false; + + *data = reinterpret_cast(*iter); + + UpdateIter(iter, length); + return true; +} + +bool Pickle::ReadLength(void** iter, int* result) const { + if (!ReadInt(iter, result)) + return false; + return ((*result) >= 0); +} + + +bool Pickle::WriteString(const std::string& value) { + if (!WriteInt(static_cast(value.size()))) + return false; + + return WriteBytes(value.data(), static_cast(value.size())); +} + +/* +bool Pickle::WriteWString(const std::wstring& value) { + if (!WriteInt(static_cast(value.size()))) + return false; + + return WriteBytes(value.data(), + static_cast(value.size() * sizeof(wchar_t))); +} + +bool Pickle::WriteString16(const string16& value) { + if (!WriteInt(static_cast(value.size()))) + return false; + + return WriteBytes(value.data(), + static_cast(value.size()) * sizeof(char16)); +} +*/ + +bool Pickle::WriteData(const char* data, int length) { + return length >= 0 && WriteInt(length) && WriteBytes(data, length); +} + +bool Pickle::WriteBytes(const void* data, int data_len) { + DCHECK_NE(kCapacityReadOnly, capacity_) << "oops: pickle is readonly"; + + char* dest = BeginWrite(data_len); + if (!dest) + return false; + + memcpy(dest, data, data_len); + + EndWrite(dest, data_len); + return true; +} + +char* Pickle::BeginWriteData(int length) { + DCHECK_EQ(variable_buffer_offset_, 0U) << + "There can only be one variable buffer in a Pickle"; + + if (length < 0 || !WriteInt(length)) + return NULL; + + char *data_ptr = BeginWrite(length); + if (!data_ptr) + return NULL; + + variable_buffer_offset_ = + data_ptr - reinterpret_cast(header_) - sizeof(int); + + // EndWrite doesn't necessarily have to be called after the write operation, + // so we call it here to pad out what the caller will eventually write. + EndWrite(data_ptr, length); + return data_ptr; +} + +void Pickle::TrimWriteData(int new_length) { + DCHECK_NE(variable_buffer_offset_, 0U); + + // Fetch the the variable buffer size + int* cur_length = reinterpret_cast( + reinterpret_cast(header_) + variable_buffer_offset_); + + if (new_length < 0 || new_length > *cur_length) { + NOTREACHED() << "Invalid length in TrimWriteData."; + return; + } + + // Update the payload size and variable buffer size + header_->payload_size -= (*cur_length - new_length); + *cur_length = new_length; +} + +char* Pickle::BeginWrite(size_t length) { + // write at a uint32-aligned offset from the beginning of the header + size_t offset = AlignInt(header_->payload_size, sizeof(uint32)); + + size_t new_size = offset + length; + size_t needed_size = header_size_ + new_size; + if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size))) + return NULL; + +#ifdef ARCH_CPU_64_BITS + DCHECK_LE(length, std::numeric_limits::max()); +#endif + + header_->payload_size = static_cast(new_size); + return payload() + offset; +} + +void Pickle::EndWrite(char* dest, int length) { + // Zero-pad to keep tools like purify from complaining about uninitialized + // memory. + if (length % sizeof(uint32)) + memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32))); +} + +bool Pickle::Resize(size_t new_capacity) { + new_capacity = AlignInt(new_capacity, kPayloadUnit); + + CHECK_NE(capacity_, kCapacityReadOnly); + void* p = realloc(header_, new_capacity); + if (!p) + return false; + + header_ = reinterpret_cast(p); + capacity_ = new_capacity; + return true; +} + +// static +const char* Pickle::FindNext(size_t header_size, + const char* start, + const char* end) { + DCHECK_EQ(header_size, AlignInt(header_size, sizeof(uint32))); + DCHECK_LE(header_size, static_cast(kPayloadUnit)); + + if (static_cast(end - start) < sizeof(Header)) + return NULL; + + const Header* hdr = reinterpret_cast(start); + const char* payload_base = start + header_size; + const char* payload_end = payload_base + hdr->payload_size; + if (payload_end < payload_base) + return NULL; + + return (payload_end > end) ? NULL : payload_end; +} diff --git a/base/pickle.h b/base/pickle.h new file mode 100644 index 0000000..e7ececf --- /dev/null +++ b/base/pickle.h @@ -0,0 +1,249 @@ +// 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. + +#ifndef BASE_PICKLE_H__ +#define BASE_PICKLE_H__ +#pragma once + +#include +#include + +#include "base/base_export.h" +#include "base/basictypes.h" +//#include "base/gtest_prod_util.h" // jaehwa +//#include "base/logging.h" // jaehwa +#include "log.h" // jaehwa +//#include "base/string16.h" // jaehwa + +// 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 BASE_EXPORT Pickle { + public: + // Initialize a Pickle object using the default header size. + Pickle(); + + // 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(int header_size); + + // Initializes a Pickle from a const block of data. The data is not copied; + // instead the data is merely referenced by this Pickle. Only const methods + // should be used on the Pickle when initialized this way. The header + // padding size is deduced from the data length. + Pickle(const char* data, int data_len); + + // Initializes a Pickle as a deep copy of another Pickle. + Pickle(const Pickle& other); + + virtual ~Pickle(); + + // Performs a deep copy. + Pickle& operator=(const Pickle& other); + + // Returns the size of the Pickle's data. + size_t size() const { return header_size_ + header_->payload_size; } + + // Returns the data for this Pickle. + const void* data() const { return header_; } + + // 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. + bool ReadBool(void** iter, bool* result) const; + bool ReadInt(void** iter, int* result) const; + bool ReadLong(void** iter, long* result) const; + bool ReadSize(void** iter, size_t* result) const; + bool ReadUInt16(void** iter, uint16* result) const; + bool ReadUInt32(void** iter, uint32* result) const; + bool ReadInt64(void** iter, int64* result) const; + bool ReadUInt64(void** iter, uint64* result) const; + bool ReadString(void** iter, std::string* result) const; +// bool ReadWString(void** iter, std::wstring* result) const; +// bool ReadString16(void** iter, string16* result) const; + bool ReadData(void** iter, const char** data, int* length) const; + bool ReadBytes(void** iter, const char** data, int length) const; + + // Safer version of ReadInt() checks for the result not being negative. + // Use it for reading the object sizes. + bool ReadLength(void** iter, int* result) 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) { + return WriteInt(value ? 1 : 0); + } + bool WriteInt(int value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteLong(long value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteSize(size_t value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt16(uint16 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt32(uint32 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteInt64(int64 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt64(uint64 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteString(const std::string& value); +// bool WriteWString(const std::wstring& value); +// bool WriteString16(const string16& value); + bool WriteData(const char* data, int length); + bool WriteBytes(const void* data, int data_len); + + // Same as WriteData, but allows the caller to write directly into the + // Pickle. This saves a copy in cases where the data is not already + // available in a buffer. The caller should take care to not write more + // than the length it declares it will. Use ReadData to get the data. + // Returns NULL on failure. + // + // The returned pointer will only be valid until the next write operation + // on this Pickle. + char* BeginWriteData(int length); + + // For Pickles which contain variable length buffers (e.g. those created + // with BeginWriteData), the Pickle can + // be 'trimmed' if the amount of data required is less than originally + // requested. For example, you may have created a buffer with 10K of data, + // but decided to only fill 10 bytes of that data. Use this function + // to trim the buffer so that we don't send 9990 bytes of unused data. + // You cannot increase the size of the variable buffer; only shrink it. + // This function assumes that the length of the variable buffer has + // not been changed. + void TrimWriteData(int length); + + // Payload follows after allocation of Header (header size is customizable). + struct Header { + uint32 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 + T* headerT() { + DCHECK_EQ(header_size_, sizeof(T)); + return static_cast(header_); + } + template + const T* headerT() const { + DCHECK_EQ(header_size_, sizeof(T)); + return static_cast(header_); + } + + // 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 void* iter, int len) const { + if ((len < 0) || (iter < header_) || iter > end_of_payload()) + return false; + const char* end_of_region = reinterpret_cast(iter) + len; + // Watch out for overflow in pointer calculation, which wraps. + return (iter <= end_of_region) && (end_of_region <= end_of_payload()); + } + + protected: + size_t payload_size() const { return header_->payload_size; } + + char* payload() { + return reinterpret_cast(header_) + header_size_; + } + const char* payload() const { + return reinterpret_cast(header_) + header_size_; + } + + // Returns the address of the byte immediately following the currently valid + // header + payload. + char* end_of_payload() { + // We must have a valid header_. + return payload() + payload_size(); + } + const char* end_of_payload() const { + // This object may be invalid. + return header_ ? payload() + payload_size() : NULL; + } + + size_t capacity() const { + return capacity_; + } + + // 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. + char* BeginWrite(size_t length); + + // 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(char* dest, int length); + + // Resize the capacity, note that the input value should include the size of + // the header: new_capacity = sizeof(Header) + desired_payload_capacity. + // A realloc() failure will cause a Resize failure... and caller should check + // the return result for true (i.e., successful resizing). + bool Resize(size_t new_capacity); + + // Aligns 'i' by rounding it up to the next multiple of 'alignment' + static size_t AlignInt(size_t i, int alignment) { + return i + (alignment - (i % alignment)) % alignment; + } + + // 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(uint32). + static void UpdateIter(void** iter, int bytes) { + *iter = static_cast(*iter) + AlignInt(bytes, sizeof(uint32)); + } + + // Find the end of the pickled data that starts at range_start. Returns NULL + // if the entire Pickle is not found in the given data range. + static const char* FindNext(size_t header_size, + const char* range_start, + const char* range_end); + + // The allocation granularity of the payload. + static const int kPayloadUnit; + + private: + Header* header_; + size_t header_size_; // Supports extra data between header and payload. + // Allocation size of payload (or -1 if allocation is const). + size_t capacity_; + size_t variable_buffer_offset_; // IF non-zero, then offset to a buffer. + +// FRIEND_TEST_ALL_PREFIXES(PickleTest, Resize); +// FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNext); +// FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNextWithIncompleteHeader); +// FRIEND_TEST_ALL_PREFIXES(PickleTest, IteratorHasRoom); +}; + +#endif // BASE_PICKLE_H__ diff --git a/base/platform_file_posix.cc b/base/platform_file_posix.cc new file mode 100644 index 0000000..fed3696 --- /dev/null +++ b/base/platform_file_posix.cc @@ -0,0 +1,200 @@ +// 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. + +#include "base/platform_file.h" + +#include +#include +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/utf_string_conversions.h" + +#if defined(OS_ANDROID) +#include "base/os_compat_android.h" +#endif + +namespace base { + +#if defined(OS_OPENBSD) || defined(OS_FREEBSD) || \ + (defined(OS_MACOSX) && \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) +typedef struct stat stat_wrapper_t; +static int CallFstat(int fd, stat_wrapper_t *sb) { + return fstat(fd, sb); +} +#else +typedef struct stat64 stat_wrapper_t; +static int CallFstat(int fd, stat_wrapper_t *sb) { + return fstat64(fd, sb); +} +#endif + +// TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here? +PlatformFile CreatePlatformFile(const FilePath& name, int flags, + bool* created, PlatformFileError* error_code) { + int open_flags = 0; + if (flags & PLATFORM_FILE_CREATE) + open_flags = O_CREAT | O_EXCL; + + if (created) + *created = false; + + if (flags & PLATFORM_FILE_CREATE_ALWAYS) { + DCHECK(!open_flags); + open_flags = O_CREAT | O_TRUNC; + } + + if (flags & PLATFORM_FILE_OPEN_TRUNCATED) { + DCHECK(!open_flags); + DCHECK(flags & PLATFORM_FILE_WRITE); + open_flags = O_TRUNC; + } + + if (!open_flags && !(flags & PLATFORM_FILE_OPEN) && + !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { + NOTREACHED(); + errno = EOPNOTSUPP; + if (error_code) + *error_code = PLATFORM_FILE_ERROR_FAILED; + 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) && + !(flags & PLATFORM_FILE_WRITE_ATTRIBUTES) && + !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { + NOTREACHED(); + } + + COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero); + + int descriptor = + HANDLE_EINTR(open(name.value().c_str(), open_flags, S_IRUSR | S_IWUSR)); + + if (flags & PLATFORM_FILE_OPEN_ALWAYS) { + if (descriptor <= 0) { + open_flags |= O_CREAT; + if (flags & PLATFORM_FILE_EXCLUSIVE_READ || + flags & PLATFORM_FILE_EXCLUSIVE_WRITE) { + open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW + } + descriptor = HANDLE_EINTR( + open(name.value().c_str(), open_flags, S_IRUSR | S_IWUSR)); + if (created && descriptor > 0) + *created = true; + } + } + + if (created && (descriptor > 0) && + (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE))) + *created = true; + + if ((descriptor > 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) { + unlink(name.value().c_str()); + } + + if (error_code) { + if (descriptor >= 0) + *error_code = PLATFORM_FILE_OK; + else { + switch (errno) { + case EACCES: + case EISDIR: + case EROFS: + case EPERM: + *error_code = PLATFORM_FILE_ERROR_ACCESS_DENIED; + break; + case ETXTBSY: + *error_code = PLATFORM_FILE_ERROR_IN_USE; + break; + case EEXIST: + *error_code = PLATFORM_FILE_ERROR_EXISTS; + break; + case ENOENT: + *error_code = PLATFORM_FILE_ERROR_NOT_FOUND; + break; + case EMFILE: + *error_code = PLATFORM_FILE_ERROR_TOO_MANY_OPENED; + break; + case ENOMEM: + *error_code = PLATFORM_FILE_ERROR_NO_MEMORY; + break; + case ENOSPC: + *error_code = PLATFORM_FILE_ERROR_NO_SPACE; + break; + case ENOTDIR: + *error_code = PLATFORM_FILE_ERROR_NOT_A_DIRECTORY; + break; + default: + *error_code = PLATFORM_FILE_ERROR_FAILED; + } + } + } + + return descriptor; +} + +bool ClosePlatformFile(PlatformFile file) { + return !HANDLE_EINTR(close(file)); +} + +int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) { + if (file < 0) + return -1; + + return HANDLE_EINTR(pread(file, data, size, offset)); +} + +int WritePlatformFile(PlatformFile file, int64 offset, + const char* data, int size) { + if (file < 0) + return -1; + + return HANDLE_EINTR(pwrite(file, data, size, offset)); +} + +bool TruncatePlatformFile(PlatformFile file, int64 length) { + return ((file >= 0) && !HANDLE_EINTR(ftruncate(file, length))); +} + +bool FlushPlatformFile(PlatformFile file) { + return !HANDLE_EINTR(fsync(file)); +} + +bool TouchPlatformFile(PlatformFile file, const base::Time& last_access_time, + const base::Time& last_modified_time) { + if (file < 0) + return false; + + timeval times[2]; + times[0] = last_access_time.ToTimeVal(); + times[1] = last_modified_time.ToTimeVal(); + return !futimes(file, times); +} + +bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) { + if (!info) + return false; + + stat_wrapper_t file_info; + if (CallFstat(file, &file_info)) + return false; + + info->is_directory = S_ISDIR(file_info.st_mode); + info->is_symbolic_link = S_ISLNK(file_info.st_mode); + info->size = file_info.st_size; + info->last_modified = base::Time::FromTimeT(file_info.st_mtime); + info->last_accessed = base::Time::FromTimeT(file_info.st_atime); + info->creation_time = base::Time::FromTimeT(file_info.st_ctime); + return true; +} + +} // namespace base diff --git a/base/port.h b/base/port.h new file mode 100644 index 0000000..2e66057 --- /dev/null +++ b/base/port.h @@ -0,0 +1,55 @@ +// 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_ +#pragma once + +#include +#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 +// 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 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) + +// 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. +#if defined(COMPILER_GCC) +#define GG_VA_COPY(a, b) (va_copy(a, b)) +#elif defined(COMPILER_MSVC) +#define GG_VA_COPY(a, b) (a = b) +#endif + +// Define an OS-neutral wrapper for shared library entry points +#if defined(OS_WIN) +#define API_CALL __stdcall +#else +#define API_CALL +#endif + +#endif // BASE_PORT_H_ diff --git a/base/string16.cc b/base/string16.cc new file mode 100644 index 0000000..6fc38ce --- /dev/null +++ b/base/string16.cc @@ -0,0 +1,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/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 + +#include "base/utf_string_conversions.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(memmove(s1, s2, n * sizeof(char16))); +} + +char16* c16memcpy(char16* s1, const char16* s2, size_t n) { + return reinterpret_cast(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; + +namespace base { +std::ostream& operator<<(std::ostream& out, const string16& str) { + return out << UTF16ToUTF8(str); +} +} + +#endif // WCHAR_T_IS_UTF32 diff --git a/base/string16.h b/base/string16.h new file mode 100644 index 0000000..4c7b21f --- /dev/null +++ b/base/string16.h @@ -0,0 +1,178 @@ +// 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. + +#ifndef BASE_STRING16_H_ +#define BASE_STRING16_H_ +#pragma once + +// 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 +#include + +#include "base/base_export.h" +#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 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"). +BASE_EXPORT int c16memcmp(const char16* s1, const char16* s2, size_t n); +BASE_EXPORT size_t c16len(const char16* s); +BASE_EXPORT const char16* c16memchr(const char16* s, char16 c, size_t n); +BASE_EXPORT char16* c16memmove(char16* s1, const char16* s2, size_t n); +BASE_EXPORT char16* c16memcpy(char16* s1, const char16* s2, size_t n); +BASE_EXPORT 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 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(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 (string) and std::basic_string (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; + +typedef std::basic_string string16; + +namespace base { +BASE_EXPORT extern std::ostream& operator<<(std::ostream& out, + const string16& str); +} + +#endif // WCHAR_T_IS_UTF32 + +#endif // BASE_STRING16_H_ diff --git a/base/string_number_conversions.cc b/base/string_number_conversions.cc new file mode 100644 index 0000000..54eca17 --- /dev/null +++ b/base/string_number_conversions.cc @@ -0,0 +1,545 @@ +// 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. + +#include "base/string_number_conversions.h" + +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/third_party/dmg_fp/dmg_fp.h" +#include "base/utf_string_conversions.h" + +namespace base { + +namespace { + +template +struct IntToStringT { + // This is to avoid a compiler warning about unary minus on unsigned type. + // For example, say you had the following code: + // template + // 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 + struct ToUnsignedT {}; + + template + struct ToUnsignedT { + static UINT2 ToUnsigned(INT2 value) { + return static_cast(value); + } + }; + + template + struct ToUnsignedT { + static UINT2 ToUnsigned(INT2 value) { + return static_cast(value < 0 ? -value : value); + } + }; + + // This set of templates is very similar to the above templates, but + // for testing whether an integer is negative. + template + struct TestNegT {}; + template + struct TestNegT { + static bool TestNeg(INT2 value) { + // value is unsigned, and can never be negative. + return false; + } + }; + template + struct TestNegT { + 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::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::ToUnsigned(value); + + for (typename STR::iterator it = outbuf.end();;) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast((res % 10) + '0'); + res /= 10; + + // We're done.. + if (res == 0) { + if (is_neg) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast('-'); + } + return STR(it, outbuf.end()); + } + } + NOTREACHED(); + return STR(); + } +}; + +// Utility to convert a character to a digit in a given base +template class BaseCharToDigit { +}; + +// Faster specialization for bases <= 10 +template class BaseCharToDigit { + public: + static bool Convert(CHAR c, uint8* digit) { + if (c >= '0' && c < '0' + BASE) { + *digit = c - '0'; + return true; + } + return false; + } +}; + +// Specialization for bases where 10 < base <= 36 +template class BaseCharToDigit { + public: + static bool Convert(CHAR c, uint8* digit) { + if (c >= '0' && c <= '9') { + *digit = c - '0'; + } else if (c >= 'a' && c < 'a' + BASE - 10) { + *digit = c - 'a' + 10; + } else if (c >= 'A' && c < 'A' + BASE - 10) { + *digit = c - 'A' + 10; + } else { + return false; + } + return true; + } +}; + +template bool CharToDigit(CHAR c, uint8* digit) { + return BaseCharToDigit::Convert(c, digit); +} + +// There is an IsWhitespace for wchars defined in string_util.h, but it is +// locale independent, whereas the functions we are replacing were +// locale-dependent. TBD what is desired, but for the moment let's not introduce +// a change in behaviour. +template class WhitespaceHelper { +}; + +template<> class WhitespaceHelper { + public: + static bool Invoke(char c) { + return 0 != isspace(static_cast(c)); + } +}; + +template<> class WhitespaceHelper { + public: + static bool Invoke(char16 c) { + return 0 != iswspace(c); + } +}; + +template bool LocalIsWhitespace(CHAR c) { + return WhitespaceHelper::Invoke(c); +} + +// IteratorRangeToNumberTraits should provide: +// - a typedef for iterator_type, the iterator type used as input. +// - a typedef for value_type, the target numeric type. +// - static functions min, max (returning the minimum and maximum permitted +// values) +// - constant kBase, the base in which to interpret the input +template +class IteratorRangeToNumber { + public: + typedef IteratorRangeToNumberTraits traits; + typedef typename traits::iterator_type const_iterator; + typedef typename traits::value_type value_type; + + // Generalized iterator-range-to-number conversion. + // + static bool Invoke(const_iterator begin, + const_iterator end, + value_type* output) { + bool valid = true; + + while (begin != end && LocalIsWhitespace(*begin)) { + valid = false; + ++begin; + } + + if (begin != end && *begin == '-') { + if (!Negative::Invoke(begin + 1, end, output)) { + valid = false; + } + } else { + if (begin != end && *begin == '+') { + ++begin; + } + if (!Positive::Invoke(begin, end, output)) { + valid = false; + } + } + + return valid; + } + + private: + // Sign provides: + // - a static function, CheckBounds, that determines whether the next digit + // causes an overflow/underflow + // - a static function, Increment, that appends the next digit appropriately + // according to the sign of the number being parsed. + template + class Base { + public: + static bool Invoke(const_iterator begin, const_iterator end, + typename traits::value_type* output) { + *output = 0; + + if (begin == end) { + return false; + } + + // Note: no performance difference was found when using template + // specialization to remove this check in bases other than 16 + if (traits::kBase == 16 && end - begin > 2 && *begin == '0' && + (*(begin + 1) == 'x' || *(begin + 1) == 'X')) { + begin += 2; + } + + for (const_iterator current = begin; current != end; ++current) { + uint8 new_digit = 0; + + if (!CharToDigit(*current, &new_digit)) { + return false; + } + + if (current != begin) { + if (!Sign::CheckBounds(output, new_digit)) { + return false; + } + *output *= traits::kBase; + } + + Sign::Increment(new_digit, output); + } + return true; + } + }; + + class Positive : public Base { + public: + static bool CheckBounds(value_type* output, uint8 new_digit) { + if (*output > static_cast(traits::max() / traits::kBase) || + (*output == static_cast(traits::max() / traits::kBase) && + new_digit > traits::max() % traits::kBase)) { + *output = traits::max(); + return false; + } + return true; + } + static void Increment(uint8 increment, value_type* output) { + *output += increment; + } + }; + + class Negative : public Base { + public: + static bool CheckBounds(value_type* output, uint8 new_digit) { + if (*output < traits::min() / traits::kBase || + (*output == traits::min() / traits::kBase && + new_digit > 0 - traits::min() % traits::kBase)) { + *output = traits::min(); + return false; + } + return true; + } + static void Increment(uint8 increment, value_type* output) { + *output -= increment; + } + }; +}; + +template +class BaseIteratorRangeToNumberTraits { + public: + typedef ITERATOR iterator_type; + typedef VALUE value_type; + static value_type min() { + return std::numeric_limits::min(); + } + static value_type max() { + return std::numeric_limits::max(); + } + static const int kBase = BASE; +}; + +typedef BaseIteratorRangeToNumberTraits + IteratorRangeToIntTraits; +typedef BaseIteratorRangeToNumberTraits + WideIteratorRangeToIntTraits; +typedef BaseIteratorRangeToNumberTraits + IteratorRangeToInt64Traits; +typedef BaseIteratorRangeToNumberTraits + WideIteratorRangeToInt64Traits; + +typedef BaseIteratorRangeToNumberTraits + CharBufferToIntTraits; +typedef BaseIteratorRangeToNumberTraits + WideCharBufferToIntTraits; +typedef BaseIteratorRangeToNumberTraits + CharBufferToInt64Traits; +typedef BaseIteratorRangeToNumberTraits + WideCharBufferToInt64Traits; + +template +class BaseHexIteratorRangeToIntTraits + : public BaseIteratorRangeToNumberTraits { + public: + // Allow parsing of 0xFFFFFFFF, which is technically an overflow + static unsigned int max() { + return std::numeric_limits::max(); + } +}; + +typedef BaseHexIteratorRangeToIntTraits + HexIteratorRangeToIntTraits; +typedef BaseHexIteratorRangeToIntTraits + HexCharBufferToIntTraits; + +template +bool HexStringToBytesT(const STR& input, std::vector* output) { + DCHECK_EQ(output->size(), 0u); + size_t count = input.size(); + if (count == 0 || (count % 2) != 0) + return false; + for (uintptr_t i = 0; i < count / 2; ++i) { + uint8 msb = 0; // most significant 4 bits + uint8 lsb = 0; // least significant 4 bits + if (!CharToDigit<16>(input[i * 2], &msb) || + !CharToDigit<16>(input[i * 2 + 1], &lsb)) + return false; + output->push_back((msb << 4) | lsb); + } + return true; +} + +} // namespace + +std::string IntToString(int value) { + return IntToStringT:: + IntToString(value); +} + +string16 IntToString16(int value) { + return IntToStringT:: + IntToString(value); +} + +std::string UintToString(unsigned int value) { + return IntToStringT:: + IntToString(value); +} + +string16 UintToString16(unsigned int value) { + return IntToStringT:: + IntToString(value); +} + +std::string Int64ToString(int64 value) { + return IntToStringT:: + IntToString(value); +} + +string16 Int64ToString16(int64 value) { + return IntToStringT::IntToString(value); +} + +std::string Uint64ToString(uint64 value) { + return IntToStringT:: + IntToString(value); +} + +string16 Uint64ToString16(uint64 value) { + return IntToStringT:: + IntToString(value); +} + +std::string DoubleToString(double value) { + // According to g_fmt.cc, it is sufficient to declare a buffer of size 32. + char buffer[32]; + dmg_fp::g_fmt(buffer, value); + return std::string(buffer); +} + +bool StringToInt(const std::string& input, int* output) { + return IteratorRangeToNumber::Invoke(input.begin(), + input.end(), + output); +} + +bool StringToInt(std::string::const_iterator begin, + std::string::const_iterator end, + int* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} + +#if !defined(STD_STRING_ITERATOR_IS_CHAR_POINTER) +bool StringToInt(const char* begin, const char* end, int* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} +#endif + +bool StringToInt(const string16& input, int* output) { + return IteratorRangeToNumber::Invoke( + input.begin(), input.end(), output); +} + +bool StringToInt(string16::const_iterator begin, + string16::const_iterator end, + int* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} + +#if !defined(BASE_STRING16_ITERATOR_IS_CHAR16_POINTER) +bool StringToInt(const char16* begin, const char16* end, int* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} +#endif + +bool StringToInt64(const std::string& input, int64* output) { + return IteratorRangeToNumber::Invoke( + input.begin(), input.end(), output); +} + +bool StringToInt64(std::string::const_iterator begin, + std::string::const_iterator end, + int64* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} + +#if !defined(STD_STRING_ITERATOR_IS_CHAR_POINTER) +bool StringToInt64(const char* begin, const char* end, int64* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} +#endif + +bool StringToInt64(const string16& input, int64* output) { + return IteratorRangeToNumber::Invoke( + input.begin(), input.end(), output); +} + +bool StringToInt64(string16::const_iterator begin, + string16::const_iterator end, + int64* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} + +#if !defined(BASE_STRING16_ITERATOR_IS_CHAR16_POINTER) +bool StringToInt64(const char16* begin, const char16* end, int64* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} +#endif + +bool StringToDouble(const std::string& input, double* output) { + errno = 0; // Thread-safe? It is on at least Mac, Linux, and Windows. + char* endptr = NULL; + *output = dmg_fp::strtod(input.c_str(), &endptr); + + // 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. + // - If the first character is a space, there was leading whitespace + return errno == 0 && + !input.empty() && + input.c_str() + input.length() == endptr && + !isspace(input[0]); +} + +// Note: if you need to add String16ToDouble, first ask yourself if it's +// really necessary. If it is, probably the best implementation here is to +// convert to 8-bit and then use the 8-bit version. + +// Note: if you need to add an iterator range version of StringToDouble, first +// ask yourself if it's really necessary. If it is, probably the best +// implementation here is to instantiate a string and use the string version. + +std::string HexEncode(const void* bytes, size_t size) { + static const char kHexChars[] = "0123456789ABCDEF"; + + // Each input byte creates two output hex characters. + std::string ret(size * 2, '\0'); + + for (size_t i = 0; i < size; ++i) { + char b = reinterpret_cast(bytes)[i]; + ret[(i * 2)] = kHexChars[(b >> 4) & 0xf]; + ret[(i * 2) + 1] = kHexChars[b & 0xf]; + } + return ret; +} + +bool HexStringToInt(const std::string& input, int* output) { + return IteratorRangeToNumber::Invoke( + input.begin(), input.end(), output); +} + +bool HexStringToInt(std::string::const_iterator begin, + std::string::const_iterator end, + int* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} + +#if !defined(STD_STRING_ITERATOR_IS_CHAR_POINTER) +bool HexStringToInt(const char* begin, const char* end, int* output) { + return IteratorRangeToNumber::Invoke(begin, + end, + output); +} +#endif + +bool HexStringToBytes(const std::string& input, std::vector* output) { + return HexStringToBytesT(input, output); +} + +} // namespace base diff --git a/base/string_number_conversions.h b/base/string_number_conversions.h new file mode 100644 index 0000000..6cf6a76 --- /dev/null +++ b/base/string_number_conversions.h @@ -0,0 +1,124 @@ +// 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. + +#ifndef BASE_STRING_NUMBER_CONVERSIONS_H_ +#define BASE_STRING_NUMBER_CONVERSIONS_H_ + +#include +#include + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/string16.h" + +// ---------------------------------------------------------------------------- +// IMPORTANT MESSAGE FROM YOUR SPONSOR +// +// This file contains no "wstring" variants. New code should use string16. If +// you need to make old code work, use the UTF8 version and convert. Please do +// not add wstring variants. +// +// Please do not add "convenience" functions for converting strings to integers +// that return the value and ignore success/failure. That encourages people to +// write code that doesn't properly handle the error conditions. +// ---------------------------------------------------------------------------- + +namespace base { + +// Number -> string conversions ------------------------------------------------ + +BASE_EXPORT std::string IntToString(int value); +BASE_EXPORT string16 IntToString16(int value); + +BASE_EXPORT std::string UintToString(unsigned value); +BASE_EXPORT string16 UintToString16(unsigned value); + +BASE_EXPORT std::string Int64ToString(int64 value); +BASE_EXPORT string16 Int64ToString16(int64 value); + +BASE_EXPORT std::string Uint64ToString(uint64 value); +BASE_EXPORT string16 Uint64ToString16(uint64 value); + +// DoubleToString converts the double to a string format that ignores the +// locale. If you want to use locale specific formatting, use ICU. +BASE_EXPORT std::string DoubleToString(double value); + +// String -> number conversions ------------------------------------------------ + +// 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. +// - Leading whitespace in the string before 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. +BASE_EXPORT bool StringToInt(const std::string& input, int* output); +BASE_EXPORT bool StringToInt(std::string::const_iterator begin, + std::string::const_iterator end, + int* output); +BASE_EXPORT bool StringToInt(const char* begin, const char* end, int* output); + +BASE_EXPORT bool StringToInt(const string16& input, int* output); +BASE_EXPORT bool StringToInt(string16::const_iterator begin, + string16::const_iterator end, + int* output); +BASE_EXPORT bool StringToInt(const char16* begin, const char16* end, + int* output); + +BASE_EXPORT bool StringToInt64(const std::string& input, int64* output); +BASE_EXPORT bool StringToInt64(std::string::const_iterator begin, + std::string::const_iterator end, + int64* output); +BASE_EXPORT bool StringToInt64(const char* begin, const char* end, + int64* output); + +BASE_EXPORT bool StringToInt64(const string16& input, int64* output); +BASE_EXPORT bool StringToInt64(string16::const_iterator begin, + string16::const_iterator end, + int64* output); +BASE_EXPORT bool StringToInt64(const char16* begin, const char16* end, + int64* output); + +// For floating-point conversions, only conversions of input strings in decimal +// form are defined to work. Behavior with strings representing floating-point +// numbers in hexadecimal, and strings representing non-fininte values (such as +// NaN and inf) is undefined. Otherwise, these behave the same as the integral +// variants. This expects the input string to NOT be specific to the locale. +// If your input is locale specific, use ICU to read the number. +BASE_EXPORT bool StringToDouble(const std::string& input, double* output); + +// Hex encoding ---------------------------------------------------------------- + +// Returns a hex string representation of a binary buffer. The returned hex +// string will be in upper case. This function does not check if |size| is +// within reasonable limits since it's written with trusted data in mind. If +// you suspect that the data you want to format might be large, the absolute +// max size for |size| should be is +// std::numeric_limits::max() / 2 +BASE_EXPORT std::string HexEncode(const void* bytes, size_t size); + +// Best effort conversion, see StringToInt above for restrictions. +BASE_EXPORT bool HexStringToInt(const std::string& input, int* output); +BASE_EXPORT bool HexStringToInt(std::string::const_iterator begin, + std::string::const_iterator end, + int* output); +BASE_EXPORT bool HexStringToInt(const char* begin, const char* end, + int* output); + +// Similar to the previous functions, except that output is a vector of bytes. +// |*output| will contain as many bytes as were successfully parsed prior to the +// error. There is no overflow, but input.size() must be evenly divisible by 2. +// Leading 0x or +/- are not allowed. +BASE_EXPORT bool HexStringToBytes(const std::string& input, + std::vector* output); + +} // namespace base + +#endif // BASE_STRING_NUMBER_CONVERSIONS_H_ + diff --git a/base/string_piece.cc b/base/string_piece.cc new file mode 100644 index 0000000..bf6291c --- /dev/null +++ b/base/string_piece.cc @@ -0,0 +1,214 @@ +// 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. +// Copied from strings/stringpiece.cc with modifications + +#include +#include + +#include "base/string_piece.h" + +namespace base { + +typedef StringPiece::size_type size_type; + +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_ ? static_cast(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 ? static_cast(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(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(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(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(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(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); + +} // namespace base diff --git a/base/string_piece.h b/base/string_piece.h new file mode 100644 index 0000000..278c7b6 --- /dev/null +++ b/base/string_piece.h @@ -0,0 +1,349 @@ +// 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. +// 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. +// +// StringPiece16 is similar to StringPiece but for base::string16 instead of +// std::string. We do not define as large of a subset of the STL functions +// from basic_string as in StringPiece, but this can be changed if these +// functions (find, find_first_of, etc.) are found to be useful in this context. +// + +#ifndef BASE_STRING_PIECE_H_ +#define BASE_STRING_PIECE_H_ +#pragma once + +#include + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/string16.h" + +namespace base { + +class BASE_EXPORT StringPiece { + public: + // standard STL container boilerplate + typedef size_t size_type; + typedef char value_type; + typedef const char* pointer; + typedef const char& reference; + typedef const char& const_reference; + typedef ptrdiff_t difference_type; + typedef const char* const_iterator; + typedef const char* iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + + static const size_type npos; + + 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) { } + StringPiece(const char* str) + : ptr_(str), length_((str == NULL) ? 0 : strlen(str)) { } + 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* data, size_type len) { + ptr_ = data; + length_ = len; + } + void set(const char* str) { + ptr_ = str; + length_ = str ? strlen(str) : 0; + } + void set(const void* data, size_type len) { + ptr_ = reinterpret_cast(data); + length_ = len; + } + + 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_, (length_ < x.length_ ? 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)); + } + + 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); + } + + private: + const char* ptr_; + size_type length_; +}; + +class BASE_EXPORT StringPiece16 { + public: + // standard STL container boilerplate + typedef size_t size_type; + typedef char16 value_type; + typedef const char16* pointer; + typedef const char16& reference; + typedef const char16& const_reference; + typedef ptrdiff_t difference_type; + typedef const char16* const_iterator; + typedef const char16* iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + + public: + // We provide non-explicit singleton constructors so users can pass + // in a "const char16*" or a "string16" wherever a "StringPiece16" is + // expected. + StringPiece16() : ptr_(NULL), length_(0) { } + StringPiece16(const char16* str) + : ptr_(str), + length_((str == NULL) ? 0 : string16::traits_type::length(str)) { } + StringPiece16(const string16& str) + : ptr_(str.data()), length_(str.size()) { } + StringPiece16(const char16* 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 char16* 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 char16* data, size_type len) { + ptr_ = data; + length_ = len; + } + void set(const char16* str) { + ptr_ = str; + length_ = str ? string16::traits_type::length(str) : 0; + } + + char16 operator[](size_type i) const { return ptr_[i]; } + + string16 as_string16() const { + // StringPiece claims that this is bad when data() is NULL, but unittesting + // seems to say otherwise. + return string16(data(), size()); + } + + 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_; } + + static int wordmemcmp(const char16* p, const char16* p2, size_type N) { + return string16::traits_type::compare(p, p2, N); + } + + private: + const char16* ptr_; + size_type length_; +}; + +BASE_EXPORT 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(), (x.size() < y.size() ? 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); +} + +inline bool operator==(const StringPiece16& x, const StringPiece16& y) { + if (x.size() != y.size()) + return false; + + return StringPiece16::wordmemcmp(x.data(), y.data(), x.size()) == 0; +} + +inline bool operator!=(const StringPiece16& x, const StringPiece16& y) { + return !(x == y); +} + +inline bool operator<(const StringPiece16& x, const StringPiece16& y) { + const int r = StringPiece16::wordmemcmp( + x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size())); + return ((r < 0) || ((r == 0) && (x.size() < y.size()))); +} + +inline bool operator>(const StringPiece16& x, const StringPiece16& y) { + return y < x; +} + +inline bool operator<=(const StringPiece16& x, const StringPiece16& y) { + return !(x > y); +} + +inline bool operator>=(const StringPiece16& x, const StringPiece16& y) { + return !(x < y); +} + +} // namespace base + +// We provide appropriate hash functions so StringPiece and StringPiece16 can +// be used as keys in hash sets and maps. + +// This hash function is copied from base/hash_tables.h. We don't use the +// ones already defined for string and string16 directly because it would +// require the string constructors to be called, which we don't want. +#define HASH_STRING_PIECE(StringPieceType, string_piece) \ + std::size_t result = 0; \ + for (StringPieceType::const_iterator i = string_piece.begin(); \ + i != string_piece.end(); ++i) \ + result = (result * 131) + *i; \ + return result; \ + +namespace BASE_HASH_NAMESPACE { +#if defined(COMPILER_GCC) + +template<> +struct hash { + std::size_t operator()(const base::StringPiece& sp) const { + HASH_STRING_PIECE(base::StringPiece, sp); + } +}; +template<> +struct hash { + std::size_t operator()(const base::StringPiece16& sp16) const { + HASH_STRING_PIECE(base::StringPiece16, sp16); + } +}; + +#elif defined(COMPILER_MSVC) + +inline size_t hash_value(const base::StringPiece& sp) { + HASH_STRING_PIECE(base::StringPiece, sp); +} +inline size_t hash_value(const base::StringPiece16& sp16) { + HASH_STRING_PIECE(base::StringPiece16, sp16); +} + +#endif // COMPILER + +} // namespace BASE_HASH_NAMESPACE + +#endif // BASE_STRING_PIECE_H_ diff --git a/base/synchronization/lock.h b/base/synchronization/lock.h new file mode 100644 index 0000000..f3a685e --- /dev/null +++ b/base/synchronization/lock.h @@ -0,0 +1,132 @@ +// 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. + +#ifndef BASE_SYNCHRONIZATION_LOCK_H_ +#define BASE_SYNCHRONIZATION_LOCK_H_ +#pragma once + +#include "base/base_export.h" +#include "base/synchronization/lock_impl.h" +#include "base/threading/platform_thread.h" + +namespace base { + +// A convenient wrapper for an OS specific critical section. The only real +// intelligence in this class is in debug mode for the support for the +// AssertAcquired() method. +class BASE_EXPORT Lock { + public: +#if defined(NDEBUG) // Optimized wrapper implementation + 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. This must not be called + // by a thread already holding the lock (what happens is undefined and an + // assertion may fail). + bool Try() { return lock_.Try(); } + + // Null implementation if not debug. + void AssertAcquired() const {} +#else + Lock(); + ~Lock() {} + + // 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). + void Acquire() { + lock_.Lock(); + CheckUnheldAndMark(); + } + void Release() { + CheckHeldAndUnmark(); + lock_.Unlock(); + } + + bool Try() { + bool rv = lock_.Try(); + if (rv) { + CheckUnheldAndMark(); + } + return rv; + } + + void AssertAcquired() const; +#endif // NDEBUG + +#if defined(OS_POSIX) + // The posix implementation of ConditionVariable needs to be able + // to see our lock and tweak our debugging counters, as it releases + // and acquires locks inside of pthread_cond_{timed,}wait. + // Windows doesn't need to do this as it calls the Lock::* methods. + friend class ConditionVariable; +#endif + + private: +#if !defined(NDEBUG) + // Members and routines taking care of locks assertions. + // Note that this checks for recursive locks and allows them + // if the variable is set. This is allowed by the underlying implementation + // on windows but not on Posix, so we're doing unneeded checks on Posix. + // It's worth it to share the code. + void CheckHeldAndUnmark(); + void CheckUnheldAndMark(); + + // All private data is implicitly protected by lock_. + // Be VERY careful to only access members under that lock. + + // Determines validity of owning_thread_id_. Needed as we don't have + // a null owning_thread_id_ value. + bool owned_by_thread_; + base::PlatformThreadId owning_thread_id_; +#endif // NDEBUG + + // Platform specific underlying lock implementation. + internal::LockImpl lock_; + + 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); +}; + +} // namespace base + +#endif // BASE_SYNCHRONIZATION_LOCK_H_ diff --git a/base/synchronization/lock_impl.h b/base/synchronization/lock_impl.h new file mode 100644 index 0000000..63efc5f --- /dev/null +++ b/base/synchronization/lock_impl.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef BASE_SYNCHRONIZATION_LOCK_IMPL_H_ +#define BASE_SYNCHRONIZATION_LOCK_IMPL_H_ +#pragma once + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) +#include +#endif + +#include "base/base_export.h" +#include "base/basictypes.h" + +namespace base { +namespace internal { + +// 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 BASE_EXPORT 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(); + + // 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_; + + DISALLOW_COPY_AND_ASSIGN(LockImpl); +}; + +} // namespace internal +} // namespace base + +#endif // BASE_SYNCHRONIZATION_LOCK_IMPL_H_ diff --git a/base/synchronization/waitable_event.h b/base/synchronization/waitable_event.h new file mode 100644 index 0000000..62712ed --- /dev/null +++ b/base/synchronization/waitable_event.h @@ -0,0 +1,181 @@ +// 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. + +#ifndef BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ +#define BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ +#pragma once + +#include "base/base_export.h" +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include +#endif + +#if defined(OS_POSIX) +#include +#include +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#endif + +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 BASE_EXPORT 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); + +#if defined(OS_WIN) + // Create a WaitableEvent from an Event HANDLE which has already been + // created. This objects takes ownership of the HANDLE and will close it when + // deleted. + explicit WaitableEvent(HANDLE event_handle); + + // Releases ownership of the handle from this object. + HANDLE Release(); +#endif + + ~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; + + protected: + virtual ~Waiter() {} + }; + + 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 : + public RefCountedThreadSafe { + public: + WaitableEventKernel(bool manual_reset, bool initially_signaled); + virtual ~WaitableEventKernel(); + + bool Dequeue(Waiter* waiter, void* tag); + + base::Lock lock_; + const bool manual_reset_; + bool signaled_; + std::list waiters_; + }; + + typedef std::pair WaiterAndIndex; + + // 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. + static size_t EnqueueMany(WaiterAndIndex* waitables, + size_t count, Waiter* waiter); + + bool SignalAll(); + bool SignalOne(); + void Enqueue(Waiter* waiter); + + scoped_refptr kernel_; +#endif + + DISALLOW_COPY_AND_ASSIGN(WaitableEvent); +}; + +} // namespace base + +#endif // BASE_SYNCHRONIZATION_WAITABLE_EVENT_H_ diff --git a/base/template_util.h b/base/template_util.h new file mode 100644 index 0000000..f19fb4b --- /dev/null +++ b/base/template_util.h @@ -0,0 +1,109 @@ +// 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. + +#ifndef BASE_TEMPLATE_UTIL_H_ +#define BASE_TEMPLATE_UTIL_H_ +#pragma once + +#include // For size_t. + +#include "build/build_config.h" + +namespace base { + +// template definitions from tr1 + +template +struct integral_constant { + static const T value = v; + typedef T value_type; + typedef integral_constant type; +}; + +template const T integral_constant::value; + +typedef integral_constant true_type; +typedef integral_constant false_type; + +template struct is_pointer : false_type {}; +template struct is_pointer : true_type {}; + +template struct is_same : public false_type {}; +template struct is_same : true_type {}; + +template struct is_array : public false_type {}; +template struct is_array : public true_type {}; +template struct is_array : public true_type {}; + +template struct is_non_const_reference : false_type {}; +template struct is_non_const_reference : true_type {}; +template struct is_non_const_reference : false_type {}; + +template struct is_void : false_type {}; +template <> struct is_void : true_type {}; + +namespace internal { + +// Types YesType and NoType are guaranteed such that sizeof(YesType) < +// sizeof(NoType). +typedef char YesType; + +struct NoType { + YesType dummy[2]; +}; + +// This class is an implementation detail for is_convertible, and you +// don't need to know how it works to use is_convertible. For those +// who care: we declare two different functions, one whose argument is +// of type To and one with a variadic argument list. We give them +// return types of different size, so we can use sizeof to trick the +// compiler into telling us which function it would have chosen if we +// had called it with an argument of type From. See Alexandrescu's +// _Modern C++ Design_ for more details on this sort of trick. + +struct ConvertHelper { + template + static YesType Test(To); + + template + static NoType Test(...); + + template + static From Create(); +}; + +// Used to determine if a type is a struct/union/class. Inspired by Boost's +// is_class type_trait implementation. +struct IsClassHelper { + template + static YesType Test(void(C::*)(void)); + + template + static NoType Test(...); +}; + +} // namespace internal + +// Inherits from true_type if From is convertible to To, false_type otherwise. +// +// Note that if the type is convertible, this will be a true_type REGARDLESS +// of whether or not the conversion would emit a warning. +template +struct is_convertible + : integral_constant( + internal::ConvertHelper::Create())) == + sizeof(internal::YesType)> { +}; + +template +struct is_class + : integral_constant(0)) == + sizeof(internal::YesType)> { +}; + +} // namespace base + +#endif // BASE_TEMPLATE_UTIL_H_ diff --git a/base/third_party/dynamic_annotations/LICENSE b/base/third_party/dynamic_annotations/LICENSE new file mode 100644 index 0000000..5c581a9 --- /dev/null +++ b/base/third_party/dynamic_annotations/LICENSE @@ -0,0 +1,28 @@ +/* Copyright (c) 2008-2009, Google Inc. + * 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. + * * 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. + * + * --- + * Author: Kostya Serebryany + */ diff --git a/base/third_party/dynamic_annotations/README.chromium b/base/third_party/dynamic_annotations/README.chromium new file mode 100644 index 0000000..f78a982 --- /dev/null +++ b/base/third_party/dynamic_annotations/README.chromium @@ -0,0 +1,11 @@ +Name: dynamic annotations +URL: http://code.google.com/p/data-race-test/wiki/DynamicAnnotations + +One header and one source file (dynamic_annotations.h and dynamic_annotations.c) +in this directory define runtime macros useful for annotating synchronization +utilities and benign data races so data race detectors can handle Chromium code +with better precision. + +These files were taken from +http://code.google.com/p/data-race-test/source/browse/?r=2062#svn/trunk/dynamic_annotations +The files are covered under BSD license as described within the files. diff --git a/base/third_party/dynamic_annotations/dynamic_annotations.c b/base/third_party/dynamic_annotations/dynamic_annotations.c new file mode 100644 index 0000000..e7d44f4 --- /dev/null +++ b/base/third_party/dynamic_annotations/dynamic_annotations.c @@ -0,0 +1,257 @@ +/* Copyright (c) 2011, Google Inc. + * 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. + * * 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. + */ + +#ifdef _MSC_VER +# include +#endif + +#ifdef __cplusplus +# error "This file should be built as pure C to avoid name mangling" +#endif + +#include +#include + +#include "base/third_party/dynamic_annotations/dynamic_annotations.h" + +#ifdef __GNUC__ +/* valgrind.h uses gcc extensions so it won't build with other compilers */ +# include "base/third_party/valgrind/valgrind.h" +#endif + +/* Each function is empty and called (via a macro) only in debug mode. + The arguments are captured by dynamic tools at runtime. */ + +#if DYNAMIC_ANNOTATIONS_ENABLED == 1 + +/* Identical code folding(-Wl,--icf=all) countermeasures. + This makes all Annotate* functions different, which prevents the linker from + folding them. */ +#ifdef __COUNTER__ +#define DYNAMIC_ANNOTATIONS_IMPL \ + volatile short lineno = (__LINE__ << 8) + __COUNTER__; (void)lineno; +#else +#define DYNAMIC_ANNOTATIONS_IMPL \ + volatile short lineno = (__LINE__ << 8); (void)lineno; +#endif + +/* WARNING: always add new annotations to the end of the list. + Otherwise, lineno (see above) numbers for different Annotate* functions may + conflict. */ +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)( + const char *file, int line, const volatile void *lock) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)( + const char *file, int line, const volatile void *lock) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)( + const char *file, int line, const volatile void *lock, long is_w) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)( + const char *file, int line, const volatile void *lock, long is_w) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)( + const char *file, int line, const volatile void *barrier, long count, + long reinitialization_allowed) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)( + const char *file, int line, const volatile void *barrier) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)( + const char *file, int line, const volatile void *barrier) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)( + const char *file, int line, const volatile void *barrier) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)( + const char *file, int line, const volatile void *cv, + const volatile void *lock) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)( + const char *file, int line, const volatile void *cv) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)( + const char *file, int line, const volatile void *cv) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)( + const char *file, int line, const volatile void *obj) +{DYNAMIC_ANNOTATIONS_IMPL}; + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)( + const char *file, int line, const volatile void *obj) +{DYNAMIC_ANNOTATIONS_IMPL}; + +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)( + const char *file, int line, const volatile void *address, long size) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)( + const char *file, int line, const volatile void *address, long size) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)( + const char *file, int line, const volatile void *pcq) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)( + const char *file, int line, const volatile void *pcq) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)( + const char *file, int line, const volatile void *pcq) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)( + const char *file, int line, const volatile void *pcq) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)( + const char *file, int line, const volatile void *mem, long size) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)( + const char *file, int line, const volatile void *mem, + const char *description) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)( + const char *file, int line) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRace)( + const char *file, int line, const volatile void *mem, + const char *description) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)( + const char *file, int line, const volatile void *mem, long size, + const char *description) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)( + const char *file, int line, const volatile void *mu) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)( + const char *file, int line, const volatile void *mu) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)( + const char *file, int line, const volatile void *arg) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)( + const char *file, int line, const char *name) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)( + const char *file, int line) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)( + const char *file, int line) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)( + const char *file, int line) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)( + const char *file, int line) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)( + const char *file, int line) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)( + const char *file, int line) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)( + const char *file, int line, int enable) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)( + const char *file, int line, const volatile void *arg) +{DYNAMIC_ANNOTATIONS_IMPL} + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)( + const char *file, int line) +{DYNAMIC_ANNOTATIONS_IMPL} + +#endif /* DYNAMIC_ANNOTATIONS_ENABLED == 1 */ + +#if DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 +static int GetRunningOnValgrind(void) { +#ifdef RUNNING_ON_VALGRIND + if (RUNNING_ON_VALGRIND) return 1; +#endif + +#ifndef _MSC_VER + char *running_on_valgrind_str = getenv("RUNNING_ON_VALGRIND"); + if (running_on_valgrind_str) { + return strcmp(running_on_valgrind_str, "0") != 0; + } +#else + /* Visual Studio issues warnings if we use getenv, + * so we use GetEnvironmentVariableA instead. + */ + char value[100] = "1"; + int res = GetEnvironmentVariableA("RUNNING_ON_VALGRIND", + value, sizeof(value)); + /* value will remain "1" if res == 0 or res >= sizeof(value). The latter + * can happen only if the given value is long, in this case it can't be "0". + */ + if (res > 0 && strcmp(value, "0") != 0) + return 1; +#endif + return 0; +} + +/* See the comments in dynamic_annotations.h */ +int RunningOnValgrind(void) { + static volatile int running_on_valgrind = -1; + /* C doesn't have thread-safe initialization of statics, and we + don't want to depend on pthread_once here, so hack it. */ + int local_running_on_valgrind = running_on_valgrind; + if (local_running_on_valgrind == -1) + running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind(); + return local_running_on_valgrind; +} + +#endif /* DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 */ diff --git a/base/third_party/dynamic_annotations/dynamic_annotations.gyp b/base/third_party/dynamic_annotations/dynamic_annotations.gyp new file mode 100644 index 0000000..47334d8 --- /dev/null +++ b/base/third_party/dynamic_annotations/dynamic_annotations.gyp @@ -0,0 +1,45 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'dynamic_annotations', + 'type': 'static_library', + 'include_dirs': [ + '../../../', + ], + 'sources': [ + 'dynamic_annotations.c', + 'dynamic_annotations.h', + ], + }, + ], + 'conditions': [ + ['OS == "win"', { + 'targets': [ + { + 'target_name': 'dynamic_annotations_win64', + 'type': 'static_library', + # We can't use dynamic_annotations target for win64 build since it is + # a 32-bit library. + # TODO(gregoryd): merge with dynamic_annotations when + # the win32/64 targets are merged. + 'include_dirs': [ + '../../../', + ], + 'sources': [ + 'dynamic_annotations.c', + 'dynamic_annotations.h', + ], + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + }, + ], + }], + ], +} diff --git a/base/third_party/dynamic_annotations/dynamic_annotations.h b/base/third_party/dynamic_annotations/dynamic_annotations.h new file mode 100644 index 0000000..8d7f052 --- /dev/null +++ b/base/third_party/dynamic_annotations/dynamic_annotations.h @@ -0,0 +1,595 @@ +/* Copyright (c) 2011, Google Inc. + * 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. + * * 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 defines dynamic annotations for use with dynamic analysis + tool such as valgrind, PIN, etc. + + Dynamic annotation is a source code annotation that affects + the generated code (that is, the annotation is not a comment). + Each such annotation is attached to a particular + instruction and/or to a particular object (address) in the program. + + The annotations that should be used by users are macros in all upper-case + (e.g., ANNOTATE_NEW_MEMORY). + + Actual implementation of these macros may differ depending on the + dynamic analysis tool being used. + + See http://code.google.com/p/data-race-test/ for more information. + + This file supports the following dynamic analysis tools: + - None (DYNAMIC_ANNOTATIONS_ENABLED is not defined or zero). + Macros are defined empty. + - ThreadSanitizer, Helgrind, DRD (DYNAMIC_ANNOTATIONS_ENABLED is 1). + Macros are defined as calls to non-inlinable empty functions + that are intercepted by Valgrind. */ + +#ifndef __DYNAMIC_ANNOTATIONS_H__ +#define __DYNAMIC_ANNOTATIONS_H__ + +#ifndef DYNAMIC_ANNOTATIONS_PREFIX +# define DYNAMIC_ANNOTATIONS_PREFIX +#endif + +#ifndef DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND +# define DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND 1 +#endif + +#ifdef DYNAMIC_ANNOTATIONS_WANT_ATTRIBUTE_WEAK +# ifdef __GNUC__ +# define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK __attribute__((weak)) +# else +/* TODO(glider): for Windows support we may want to change this macro in order + to prepend __declspec(selectany) to the annotations' declarations. */ +# error weak annotations are not supported for your compiler +# endif +#else +# define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK +#endif + +/* The following preprocessor magic prepends the value of + DYNAMIC_ANNOTATIONS_PREFIX to annotation function names. */ +#define DYNAMIC_ANNOTATIONS_GLUE0(A, B) A##B +#define DYNAMIC_ANNOTATIONS_GLUE(A, B) DYNAMIC_ANNOTATIONS_GLUE0(A, B) +#define DYNAMIC_ANNOTATIONS_NAME(name) \ + DYNAMIC_ANNOTATIONS_GLUE(DYNAMIC_ANNOTATIONS_PREFIX, name) + +#ifndef DYNAMIC_ANNOTATIONS_ENABLED +# define DYNAMIC_ANNOTATIONS_ENABLED 0 +#endif + +#if DYNAMIC_ANNOTATIONS_ENABLED != 0 + + /* ------------------------------------------------------------- + Annotations useful when implementing condition variables such as CondVar, + using conditional critical sections (Await/LockWhen) and when constructing + user-defined synchronization mechanisms. + + The annotations ANNOTATE_HAPPENS_BEFORE() and ANNOTATE_HAPPENS_AFTER() can + be used to define happens-before arcs in user-defined synchronization + mechanisms: the race detector will infer an arc from the former to the + latter when they share the same argument pointer. + + Example 1 (reference counting): + + void Unref() { + ANNOTATE_HAPPENS_BEFORE(&refcount_); + if (AtomicDecrementByOne(&refcount_) == 0) { + ANNOTATE_HAPPENS_AFTER(&refcount_); + delete this; + } + } + + Example 2 (message queue): + + void MyQueue::Put(Type *e) { + MutexLock lock(&mu_); + ANNOTATE_HAPPENS_BEFORE(e); + PutElementIntoMyQueue(e); + } + + Type *MyQueue::Get() { + MutexLock lock(&mu_); + Type *e = GetElementFromMyQueue(); + ANNOTATE_HAPPENS_AFTER(e); + return e; + } + + Note: when possible, please use the existing reference counting and message + queue implementations instead of inventing new ones. */ + + /* Report that wait on the condition variable at address "cv" has succeeded + and the lock at address "lock" is held. */ + #define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(__FILE__, __LINE__, cv, lock) + + /* Report that wait on the condition variable at "cv" has succeeded. Variant + w/o lock. */ + #define ANNOTATE_CONDVAR_WAIT(cv) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(__FILE__, __LINE__, cv, NULL) + + /* Report that we are about to signal on the condition variable at address + "cv". */ + #define ANNOTATE_CONDVAR_SIGNAL(cv) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)(__FILE__, __LINE__, cv) + + /* Report that we are about to signal_all on the condition variable at address + "cv". */ + #define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)(__FILE__, __LINE__, cv) + + /* Annotations for user-defined synchronization mechanisms. */ + #define ANNOTATE_HAPPENS_BEFORE(obj) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)(__FILE__, __LINE__, obj) + #define ANNOTATE_HAPPENS_AFTER(obj) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)(__FILE__, __LINE__, obj) + + /* DEPRECATED. Don't use it. */ + #define ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)(__FILE__, __LINE__, \ + pointer, size) + + /* DEPRECATED. Don't use it. */ + #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)(__FILE__, __LINE__, \ + pointer, size) + + /* DEPRECATED. Don't use it. */ + #define ANNOTATE_SWAP_MEMORY_RANGE(pointer, size) \ + do { \ + ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size); \ + ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size); \ + } while (0) + + /* Instruct the tool to create a happens-before arc between mu->Unlock() and + mu->Lock(). This annotation may slow down the race detector and hide real + races. Normally it is used only when it would be difficult to annotate each + of the mutex's critical sections individually using the annotations above. + This annotation makes sense only for hybrid race detectors. For pure + happens-before detectors this is a no-op. For more details see + http://code.google.com/p/data-race-test/wiki/PureHappensBeforeVsHybrid . */ + #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(__FILE__, __LINE__, \ + mu) + + /* Opposite to ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. + Instruct the tool to NOT create h-b arcs between Unlock and Lock, even in + pure happens-before mode. For a hybrid mode this is a no-op. */ + #define ANNOTATE_NOT_HAPPENS_BEFORE_MUTEX(mu) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)(__FILE__, __LINE__, mu) + + /* Deprecated. Use ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. */ + #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(__FILE__, __LINE__, \ + mu) + + /* ------------------------------------------------------------- + Annotations useful when defining memory allocators, or when memory that + was protected in one way starts to be protected in another. */ + + /* Report that a new memory at "address" of size "size" has been allocated. + This might be used when the memory has been retrieved from a free list and + is about to be reused, or when a the locking discipline for a variable + changes. */ + #define ANNOTATE_NEW_MEMORY(address, size) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)(__FILE__, __LINE__, address, \ + size) + + /* ------------------------------------------------------------- + Annotations useful when defining FIFO queues that transfer data between + threads. */ + + /* Report that the producer-consumer queue (such as ProducerConsumerQueue) at + address "pcq" has been created. The ANNOTATE_PCQ_* annotations + should be used only for FIFO queues. For non-FIFO queues use + ANNOTATE_HAPPENS_BEFORE (for put) and ANNOTATE_HAPPENS_AFTER (for get). */ + #define ANNOTATE_PCQ_CREATE(pcq) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)(__FILE__, __LINE__, pcq) + + /* Report that the queue at address "pcq" is about to be destroyed. */ + #define ANNOTATE_PCQ_DESTROY(pcq) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)(__FILE__, __LINE__, pcq) + + /* Report that we are about to put an element into a FIFO queue at address + "pcq". */ + #define ANNOTATE_PCQ_PUT(pcq) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)(__FILE__, __LINE__, pcq) + + /* Report that we've just got an element from a FIFO queue at address + "pcq". */ + #define ANNOTATE_PCQ_GET(pcq) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)(__FILE__, __LINE__, pcq) + + /* ------------------------------------------------------------- + Annotations that suppress errors. It is usually better to express the + program's synchronization using the other annotations, but these can + be used when all else fails. */ + + /* Report that we may have a benign race at "pointer", with size + "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the + point where "pointer" has been allocated, preferably close to the point + where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. */ + #define ANNOTATE_BENIGN_RACE(pointer, description) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(__FILE__, __LINE__, \ + pointer, sizeof(*(pointer)), description) + + /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to + the memory range [address, address+size). */ + #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(__FILE__, __LINE__, \ + address, size, description) + + /* Request the analysis tool to ignore all reads in the current thread + until ANNOTATE_IGNORE_READS_END is called. + Useful to ignore intentional racey reads, while still checking + other reads and all writes. + See also ANNOTATE_UNPROTECTED_READ. */ + #define ANNOTATE_IGNORE_READS_BEGIN() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__) + + /* Stop ignoring reads. */ + #define ANNOTATE_IGNORE_READS_END() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__) + + /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes. */ + #define ANNOTATE_IGNORE_WRITES_BEGIN() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__) + + /* Stop ignoring writes. */ + #define ANNOTATE_IGNORE_WRITES_END() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__) + + /* Start ignoring all memory accesses (reads and writes). */ + #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ + do {\ + ANNOTATE_IGNORE_READS_BEGIN();\ + ANNOTATE_IGNORE_WRITES_BEGIN();\ + }while(0)\ + + /* Stop ignoring all memory accesses. */ + #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ + do {\ + ANNOTATE_IGNORE_WRITES_END();\ + ANNOTATE_IGNORE_READS_END();\ + }while(0)\ + + /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore synchronization events: + RWLOCK* and CONDVAR*. */ + #define ANNOTATE_IGNORE_SYNC_BEGIN() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)(__FILE__, __LINE__) + + /* Stop ignoring sync events. */ + #define ANNOTATE_IGNORE_SYNC_END() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)(__FILE__, __LINE__) + + + /* Enable (enable!=0) or disable (enable==0) race detection for all threads. + This annotation could be useful if you want to skip expensive race analysis + during some period of program execution, e.g. during initialization. */ + #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)(__FILE__, __LINE__, \ + enable) + + /* ------------------------------------------------------------- + Annotations useful for debugging. */ + + /* Request to trace every access to "address". */ + #define ANNOTATE_TRACE_MEMORY(address) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)(__FILE__, __LINE__, address) + + /* Report the current thread name to a race detector. */ + #define ANNOTATE_THREAD_NAME(name) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)(__FILE__, __LINE__, name) + + /* ------------------------------------------------------------- + Annotations useful when implementing locks. They are not + normally needed by modules that merely use locks. + The "lock" argument is a pointer to the lock object. */ + + /* Report that a lock has been created at address "lock". */ + #define ANNOTATE_RWLOCK_CREATE(lock) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)(__FILE__, __LINE__, lock) + + /* Report that the lock at address "lock" is about to be destroyed. */ + #define ANNOTATE_RWLOCK_DESTROY(lock) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock) + + /* Report that the lock at address "lock" has been acquired. + is_w=1 for writer lock, is_w=0 for reader lock. */ + #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)(__FILE__, __LINE__, lock, \ + is_w) + + /* Report that the lock at address "lock" is about to be released. */ + #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)(__FILE__, __LINE__, lock, \ + is_w) + + /* ------------------------------------------------------------- + Annotations useful when implementing barriers. They are not + normally needed by modules that merely use barriers. + The "barrier" argument is a pointer to the barrier object. */ + + /* Report that the "barrier" has been initialized with initial "count". + If 'reinitialization_allowed' is true, initialization is allowed to happen + multiple times w/o calling barrier_destroy() */ + #define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)(__FILE__, __LINE__, barrier, \ + count, reinitialization_allowed) + + /* Report that we are about to enter barrier_wait("barrier"). */ + #define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)(__FILE__, __LINE__, \ + barrier) + + /* Report that we just exited barrier_wait("barrier"). */ + #define ANNOTATE_BARRIER_WAIT_AFTER(barrier) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)(__FILE__, __LINE__, \ + barrier) + + /* Report that the "barrier" has been destroyed. */ + #define ANNOTATE_BARRIER_DESTROY(barrier) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)(__FILE__, __LINE__, \ + barrier) + + /* ------------------------------------------------------------- + Annotations useful for testing race detectors. */ + + /* Report that we expect a race on the variable at "address". + Use only in unit tests for a race detector. */ + #define ANNOTATE_EXPECT_RACE(address, description) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)(__FILE__, __LINE__, address, \ + description) + + #define ANNOTATE_FLUSH_EXPECTED_RACES() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)(__FILE__, __LINE__) + + /* A no-op. Insert where you like to test the interceptors. */ + #define ANNOTATE_NO_OP(arg) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)(__FILE__, __LINE__, arg) + + /* Force the race detector to flush its state. The actual effect depends on + * the implementation of the detector. */ + #define ANNOTATE_FLUSH_STATE() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)(__FILE__, __LINE__) + + +#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ + + #define ANNOTATE_RWLOCK_CREATE(lock) /* empty */ + #define ANNOTATE_RWLOCK_DESTROY(lock) /* empty */ + #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) /* empty */ + #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) /* empty */ + #define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) /* */ + #define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) /* empty */ + #define ANNOTATE_BARRIER_WAIT_AFTER(barrier) /* empty */ + #define ANNOTATE_BARRIER_DESTROY(barrier) /* empty */ + #define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) /* empty */ + #define ANNOTATE_CONDVAR_WAIT(cv) /* empty */ + #define ANNOTATE_CONDVAR_SIGNAL(cv) /* empty */ + #define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) /* empty */ + #define ANNOTATE_HAPPENS_BEFORE(obj) /* empty */ + #define ANNOTATE_HAPPENS_AFTER(obj) /* empty */ + #define ANNOTATE_PUBLISH_MEMORY_RANGE(address, size) /* empty */ + #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(address, size) /* empty */ + #define ANNOTATE_SWAP_MEMORY_RANGE(address, size) /* empty */ + #define ANNOTATE_PCQ_CREATE(pcq) /* empty */ + #define ANNOTATE_PCQ_DESTROY(pcq) /* empty */ + #define ANNOTATE_PCQ_PUT(pcq) /* empty */ + #define ANNOTATE_PCQ_GET(pcq) /* empty */ + #define ANNOTATE_NEW_MEMORY(address, size) /* empty */ + #define ANNOTATE_EXPECT_RACE(address, description) /* empty */ + #define ANNOTATE_FLUSH_EXPECTED_RACES(address, description) /* empty */ + #define ANNOTATE_BENIGN_RACE(address, description) /* empty */ + #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */ + #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) /* empty */ + #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) /* empty */ + #define ANNOTATE_TRACE_MEMORY(arg) /* empty */ + #define ANNOTATE_THREAD_NAME(name) /* empty */ + #define ANNOTATE_IGNORE_READS_BEGIN() /* empty */ + #define ANNOTATE_IGNORE_READS_END() /* empty */ + #define ANNOTATE_IGNORE_WRITES_BEGIN() /* empty */ + #define ANNOTATE_IGNORE_WRITES_END() /* empty */ + #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() /* empty */ + #define ANNOTATE_IGNORE_READS_AND_WRITES_END() /* empty */ + #define ANNOTATE_IGNORE_SYNC_BEGIN() /* empty */ + #define ANNOTATE_IGNORE_SYNC_END() /* empty */ + #define ANNOTATE_ENABLE_RACE_DETECTION(enable) /* empty */ + #define ANNOTATE_NO_OP(arg) /* empty */ + #define ANNOTATE_FLUSH_STATE() /* empty */ + +#endif /* DYNAMIC_ANNOTATIONS_ENABLED */ + +/* Use the macros above rather than using these functions directly. */ +#ifdef __cplusplus +extern "C" { +#endif + + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)( + const char *file, int line, + const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)( + const char *file, int line, + const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)( + const char *file, int line, + const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)( + const char *file, int line, + const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)( + const char *file, int line, const volatile void *barrier, long count, + long reinitialization_allowed) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)( + const char *file, int line, + const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)( + const char *file, int line, + const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)( + const char *file, int line, + const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)( + const char *file, int line, const volatile void *cv, + const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)( + const char *file, int line, + const volatile void *cv) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)( + const char *file, int line, + const volatile void *cv) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensBefore)( + const char *file, int line, + const volatile void *obj) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateHappensAfter)( + const char *file, int line, + const volatile void *obj) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)( + const char *file, int line, + const volatile void *address, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)( + const char *file, int line, + const volatile void *address, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)( + const char *file, int line, + const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)( + const char *file, int line, + const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)( + const char *file, int line, + const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)( + const char *file, int line, + const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)( + const char *file, int line, + const volatile void *mem, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)( + const char *file, int line, const volatile void *mem, + const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRace)( + const char *file, int line, const volatile void *mem, + const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)( + const char *file, int line, const volatile void *mem, long size, + const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)( + const char *file, int line, + const volatile void *mu) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)( + const char *file, int line, + const volatile void *mu) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)( + const char *file, int line, + const volatile void *arg) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)( + const char *file, int line, + const char *name) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)( + const char *file, int line, int enable) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)( + const char *file, int line, + const volatile void *arg) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; + +#if DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 +/* Return non-zero value if running under valgrind. + + If "valgrind.h" is included into dynamic_annotations.c, + the regular valgrind mechanism will be used. + See http://valgrind.org/docs/manual/manual-core-adv.html about + RUNNING_ON_VALGRIND and other valgrind "client requests". + The file "valgrind.h" may be obtained by doing + svn co svn://svn.valgrind.org/valgrind/trunk/include + + If for some reason you can't use "valgrind.h" or want to fake valgrind, + there are two ways to make this function return non-zero: + - Use environment variable: export RUNNING_ON_VALGRIND=1 + - Make your tool intercept the function RunningOnValgrind() and + change its return value. + */ +int RunningOnValgrind(void) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +#endif /* DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 */ + +#ifdef __cplusplus +} +#endif + +#if DYNAMIC_ANNOTATIONS_ENABLED != 0 && defined(__cplusplus) + + /* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racey reads. + + Instead of doing + ANNOTATE_IGNORE_READS_BEGIN(); + ... = x; + ANNOTATE_IGNORE_READS_END(); + one can use + ... = ANNOTATE_UNPROTECTED_READ(x); */ + template + inline T ANNOTATE_UNPROTECTED_READ(const volatile T &x) { + ANNOTATE_IGNORE_READS_BEGIN(); + T res = x; + ANNOTATE_IGNORE_READS_END(); + return res; + } + /* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */ + #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ + namespace { \ + class static_var ## _annotator { \ + public: \ + static_var ## _annotator() { \ + ANNOTATE_BENIGN_RACE_SIZED(&static_var, \ + sizeof(static_var), \ + # static_var ": " description); \ + } \ + }; \ + static static_var ## _annotator the ## static_var ## _annotator;\ + } +#else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ + + #define ANNOTATE_UNPROTECTED_READ(x) (x) + #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) /* empty */ + +#endif /* DYNAMIC_ANNOTATIONS_ENABLED */ + +#endif /* __DYNAMIC_ANNOTATIONS_H__ */ diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h new file mode 100644 index 0000000..8382bbe --- /dev/null +++ b/base/threading/platform_thread.h @@ -0,0 +1,115 @@ +// 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. + +// 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_THREADING_PLATFORM_THREAD_H_ +#define BASE_THREADING_PLATFORM_THREAD_H_ +#pragma once + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) +#include +#if defined(OS_MACOSX) +#include +#else // OS_POSIX && !OS_MACOSX +#include +#endif +#endif + +namespace base { + +// 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) +typedef DWORD PlatformThreadId; +typedef void* PlatformThreadHandle; // HANDLE +const PlatformThreadHandle kNullThreadHandle = NULL; +#elif defined(OS_POSIX) +typedef pthread_t PlatformThreadHandle; +const PlatformThreadHandle kNullThreadHandle = 0; +#if defined(OS_MACOSX) +typedef mach_port_t PlatformThreadId; +#else // OS_POSIX && !OS_MACOSX +typedef pid_t PlatformThreadId; +#endif +#endif + +const PlatformThreadId kInvalidThreadId = 0; + +// Valid values for SetThreadPriority() +enum ThreadPriority{ + kThreadPriority_Normal, + // Suitable for low-latency, glitch-resistant audio. + kThreadPriority_RealtimeAudio +}; + +// A namespace for low-level thread functions. +class BASE_EXPORT PlatformThread { + public: + // Implement this interface to run code on a background thread. Your + // ThreadMain method will be called on the newly created thread. + class BASE_EXPORT Delegate { + public: + virtual ~Delegate() {} + virtual void ThreadMain() = 0; + }; + + // 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 debuggers/tools. This has no effect + // otherwise. This name pointer is not copied internally. Thus, it must stay + // valid until the thread ends. + static void SetName(const char* name); + + // Gets the thread name, if previously set by SetName. + static const char* GetName(); + + // 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); + + static void SetThreadPriority(PlatformThreadHandle handle, + ThreadPriority priority); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); +}; + +} // namespace base + +#endif // BASE_THREADING_PLATFORM_THREAD_H_ diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc new file mode 100644 index 0000000..c924a16 --- /dev/null +++ b/base/threading/platform_thread_posix.cc @@ -0,0 +1,262 @@ +// 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. + +#include "base/threading/platform_thread.h" + +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/safe_strerror_posix.h" +#include "base/threading/thread_local.h" +#include "base/threading/thread_restrictions.h" + +#if defined(OS_MACOSX) +#include +#include +#include +#endif + +#if defined(OS_LINUX) +#include +#include +#include +#include +#endif + +#if defined(OS_ANDROID) +#include "base/android/jni_android.h" +#endif + +#if defined(OS_NACL) +#include +#endif + +namespace base { + +#if defined(OS_MACOSX) +void InitThreading(); +#endif + +namespace { + +static ThreadLocalPointer current_thread_name; + +struct ThreadParams { + PlatformThread::Delegate* delegate; + bool joinable; +}; + +void* ThreadFunc(void* params) { + ThreadParams* thread_params = static_cast(params); + PlatformThread::Delegate* delegate = thread_params->delegate; + if (!thread_params->joinable) + base::ThreadRestrictions::SetSingletonAllowed(false); + delete thread_params; + delegate->ThreadMain(); +#if defined(OS_ANDROID) + base::android::DetachFromVM(); +#endif + return NULL; +} + +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 defined(OS_MACOSX) + // The Mac OS X default for a pthread stack size is 512kB. + // Libc-594.1.4/pthreads/pthread.c's pthread_attr_init uses + // DEFAULT_STACK_SIZE for this purpose. + // + // 512kB isn't quite generous enough for some deeply recursive threads that + // otherwise request the default stack size by specifying 0. Here, adopt + // glibc's behavior as on Linux, which is to use the current stack size + // limit (ulimit -s) as the default stack size. See + // glibc-2.11.1/nptl/nptl-init.c's __pthread_initialize_minimal_internal. To + // avoid setting the limit below the Mac OS X default or the minimum usable + // stack size, these values are also considered. If any of these values + // can't be determined, or if stack size is unlimited (ulimit -s unlimited), + // stack_size is left at 0 to get the system default. + // + // Mac OS X normally only applies ulimit -s to the main thread stack. On + // contemporary OS X and Linux systems alike, this value is generally 8MB + // or in that neighborhood. + if (stack_size == 0) { + size_t default_stack_size; + struct rlimit stack_rlimit; + if (pthread_attr_getstacksize(&attributes, &default_stack_size) == 0 && + getrlimit(RLIMIT_STACK, &stack_rlimit) == 0 && + stack_rlimit.rlim_cur != RLIM_INFINITY) { + stack_size = std::max(std::max(default_stack_size, + static_cast(PTHREAD_STACK_MIN)), + static_cast(stack_rlimit.rlim_cur)); + } + } +#endif // OS_MACOSX + + if (stack_size > 0) + pthread_attr_setstacksize(&attributes, stack_size); + + ThreadParams* params = new ThreadParams; + params->delegate = delegate; + params->joinable = joinable; + success = !pthread_create(thread_handle, &attributes, ThreadFunc, params); + + pthread_attr_destroy(&attributes); + if (!success) + delete params; + return success; +} + +} // namespace + +// 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) + return mach_thread_self(); +#elif defined(OS_LINUX) + return syscall(__NR_gettid); +#elif defined(OS_ANDROID) + return gettid(); +#elif defined(OS_FREEBSD) + // TODO(BSD): find a better thread ID + return reinterpret_cast(pthread_self()); +#elif defined(OS_NACL) || defined(OS_SOLARIS) + return pthread_self(); +#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; +} + +// Linux SetName is currently disabled, as we need to distinguish between +// helper threads (where it's ok to make this call) and the main thread +// (where making this call renames our process, causing tools like killall +// to stop working). +#if 0 && defined(OS_LINUX) +// static +void PlatformThread::SetName(const char* name) { + // have to cast away const because ThreadLocalPointer does not support const + // void* + current_thread_name.Set(const_cast(name)); + + // http://0pointer.de/blog/projects/name-your-threads.html + + // glibc recently added support for pthread_setname_np, but it's not + // commonly available yet. So test for it at runtime. + int (*dynamic_pthread_setname_np)(pthread_t, const char*); + *reinterpret_cast(&dynamic_pthread_setname_np) = + dlsym(RTLD_DEFAULT, "pthread_setname_np"); + + if (dynamic_pthread_setname_np) { + // This limit comes from glibc, which gets it from the kernel + // (TASK_COMM_LEN). + const int kMaxNameLength = 15; + std::string shortened_name = std::string(name).substr(0, kMaxNameLength); + int err = dynamic_pthread_setname_np(pthread_self(), + shortened_name.c_str()); + if (err < 0) + LOG(ERROR) << "pthread_setname_np: " << safe_strerror(err); + } else { + // Implementing this function without glibc is simple enough. (We + // don't do the name length clipping as above because it will be + // truncated by the callee (see TASK_COMM_LEN above).) + int err = prctl(PR_SET_NAME, name); + if (err < 0) + PLOG(ERROR) << "prctl(PR_SET_NAME)"; + } +} +#elif defined(OS_MACOSX) +// Mac is implemented in platform_thread_mac.mm. +#else +// static +void PlatformThread::SetName(const char* name) { + // have to cast away const because ThreadLocalPointer does not support const + // void* + current_thread_name.Set(const_cast(name)); + + // (This should be relatively simple to implement for the BSDs; I + // just don't have one handy to test the code on.) +} +#endif // defined(OS_LINUX) + + +#if !defined(OS_MACOSX) +// Mac is implemented in platform_thread_mac.mm. +// static +const char* PlatformThread::GetName() { + return current_thread_name.Get(); +} +#endif + +// 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) { + // Joining another thread may block the current thread for a long time, since + // the thread referred to by |thread_handle| may still be running long-lived / + // blocking tasks. + base::ThreadRestrictions::AssertIOAllowed(); + pthread_join(thread_handle, NULL); +} + +#if !defined(OS_MACOSX) +// Mac OS X uses lower-level mach APIs + +// static +void PlatformThread::SetThreadPriority(PlatformThreadHandle, ThreadPriority) { + // TODO(crogers): implement + NOTIMPLEMENTED(); +} +#endif + +} // namespace base diff --git a/base/threading/thread.cc b/base/threading/thread.cc new file mode 100644 index 0000000..616aac8 --- /dev/null +++ b/base/threading/thread.cc @@ -0,0 +1,177 @@ +// 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. + +#include "base/threading/thread.h" + +#include "base/lazy_instance.h" +#include "base/third_party/dynamic_annotations/dynamic_annotations.h" +#include "base/threading/thread_local.h" +#include "base/synchronization/waitable_event.h" + +namespace base { + +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. +base::LazyInstance lazy_tls_bool( + base::LINKER_INITIALIZED); + +} // namespace + +// This task is used to trigger the message loop to exit. +class ThreadQuitTask : public Task { + public: + virtual void Run() { + MessageLoop::current()->Quit(); + Thread::SetThreadWasQuitProperly(true); + } +}; + +// 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) + : started_(false), + stopping_(false), + startup_data_(NULL), + thread_(0), + message_loop_(NULL), + thread_id_(kInvalidThreadId), + name_(name) { +} + +Thread::~Thread() { + Stop(); +} + +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; + return false; + } + + // Wait for the thread to start and initialize message_loop_ + startup_data.event.Wait(); + + // set it to NULL so we don't keep a pointer to some object on the stack. + startup_data_ = NULL; + started_ = true; + + DCHECK(message_loop_); + return true; +} + +void Thread::Stop() { + if (!thread_was_started()) + return; + + StopSoon(); + + // Wait for the thread to exit. + // + // 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 should NULL message_loop_ on exit. + DCHECK(!message_loop_); + + // The thread no longer needs to be joined. + started_ = false; + + stopping_ = false; +} + +void Thread::StopSoon() { + // We should only be called on the same thread that started us. + + // Reading thread_id_ without a lock can lead to a benign data race + // with ThreadMain, so we annotate it to stay silent under ThreadSanitizer. + DCHECK_NE(ANNOTATE_UNPROTECTED_READ(thread_id_), PlatformThread::CurrentId()); + + if (stopping_ || !message_loop_) + return; + + stopping_ = true; + message_loop_->PostTask(FROM_HERE, new ThreadQuitTask()); +} + +void Thread::Run(MessageLoop* message_loop) { + message_loop->Run(); +} + +void Thread::SetThreadWasQuitProperly(bool flag) { + lazy_tls_bool.Pointer()->Set(flag); +} + +bool Thread::GetThreadWasQuitProperly() { + bool quit_properly = true; +#ifndef NDEBUG + quit_properly = lazy_tls_bool.Pointer()->Get(); +#endif + return quit_properly; +} + +void Thread::ThreadMain() { + { + // The message loop for this thread. + MessageLoop message_loop(startup_data_->options.message_loop_type); + + // Complete the initialization of our Thread object. + thread_id_ = PlatformThread::CurrentId(); + PlatformThread::SetName(name_.c_str()); + ANNOTATE_THREAD_NAME(name_.c_str()); // Tell the name to race detector. + message_loop.set_thread_name(name_); + 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. + + Run(message_loop_); + + // Let the thread do extra cleanup. + CleanUp(); + + // Assert that MessageLoop::Quit was called by ThreadQuitTask. + DCHECK(GetThreadWasQuitProperly()); + + // We can't receive messages anymore. + message_loop_ = NULL; + } + thread_id_ = kInvalidThreadId; +} + +} // namespace base diff --git a/base/threading/thread.h b/base/threading/thread.h new file mode 100644 index 0000000..d7451ec --- /dev/null +++ b/base/threading/thread.h @@ -0,0 +1,191 @@ +// 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. + +#ifndef BASE_THREAD_H_ +#define BASE_THREAD_H_ +#pragma once + +#include + +#include "base/base_export.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "base/threading/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. +// +// After the thread is stopped, the destruction sequence is: +// +// (1) Thread::CleanUp() +// (2) MessageLoop::~MessageLoop +// (3.b) MessageLoop::DestructionObserver::WillDestroyCurrentMessageLoop +class BASE_EXPORT Thread : PlatformThread::Delegate { + public: + struct Options { + Options() : message_loop_type(MessageLoop::TYPE_DEFAULT), stack_size(0) {} + Options(MessageLoop::Type type, size_t size) + : message_loop_type(type), stack_size(size) {} + + // 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; + }; + + // 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_; } + + // Returns a MessageLoopProxy for this thread. Use the MessageLoopProxy'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. Callers can hold on to this even after the thread + // is gone. + // TODO(sanjeevr): Look into merging MessageLoop and MessageLoopProxy. + scoped_refptr message_loop_proxy() const { + return message_loop_->message_loop_proxy(); + } + + // 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_; } + + // Returns true if the thread has been started, and not yet stopped. + // When a thread is running, |thread_id_| is a valid id. + bool IsRunning() const { return thread_id_ != kInvalidThreadId; } + + protected: + // Called just prior to starting the message loop + virtual void Init() {} + + // Called to start the message loop + virtual void Run(MessageLoop* message_loop); + + // Called just after the message loop ends + virtual void CleanUp() {} + + // Called after the message loop has been deleted. In general clients + // should prefer to use CleanUp(). This method is used when code needs to + // be run after all of the MessageLoop::DestructionObservers have completed. + virtual void CleanUpAfterMessageLoopDestruction() {} + + static void SetThreadWasQuitProperly(bool flag); + static bool GetThreadWasQuitProperly(); + + void set_message_loop(MessageLoop* message_loop) { + message_loop_ = message_loop; + } + + private: + bool thread_was_started() const { return started_; } + + // PlatformThread::Delegate methods: + virtual void ThreadMain(); + + // Whether we successfully started the thread. + bool started_; + + // If true, we're in the middle of stopping, and shouldn't access + // |message_loop_|. It may non-NULL and invalid. + bool stopping_; + + // 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/base/threading/thread_collision_warner.cc b/base/threading/thread_collision_warner.cc new file mode 100644 index 0000000..547e11c --- /dev/null +++ b/base/threading/thread_collision_warner.cc @@ -0,0 +1,64 @@ +// 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/threading/thread_collision_warner.h" + +#include "base/logging.h" +#include "base/threading/platform_thread.h" + +namespace base { + +void DCheckAsserter::warn() { + NOTREACHED() << "Thread Collision"; +} + +static subtle::Atomic32 CurrentThread() { + const PlatformThreadId current_thread_id = PlatformThread::CurrentId(); + // We need to get the thread id into an atomic data type. This might be a + // truncating conversion, but any loss-of-information just increases the + // chance of a fault negative, not a false positive. + const subtle::Atomic32 atomic_thread_id = + static_cast(current_thread_id); + + return atomic_thread_id; +} + +void ThreadCollisionWarner::EnterSelf() { + // If the active thread is 0 then I'll write the current thread ID + // if two or more threads arrive here only one will succeed to + // write on valid_thread_id_ the current thread ID. + subtle::Atomic32 current_thread_id = CurrentThread(); + + int previous_value = subtle::NoBarrier_CompareAndSwap(&valid_thread_id_, + 0, + current_thread_id); + if (previous_value != 0 && previous_value != current_thread_id) { + // gotcha! a thread is trying to use the same class and that is + // not current thread. + asserter_->warn(); + } + + subtle::NoBarrier_AtomicIncrement(&counter_, 1); +} + +void ThreadCollisionWarner::Enter() { + subtle::Atomic32 current_thread_id = CurrentThread(); + + if (subtle::NoBarrier_CompareAndSwap(&valid_thread_id_, + 0, + current_thread_id) != 0) { + // gotcha! another thread is trying to use the same class. + asserter_->warn(); + } + + subtle::NoBarrier_AtomicIncrement(&counter_, 1); +} + +void ThreadCollisionWarner::Leave() { + if (subtle::Barrier_AtomicIncrement(&counter_, -1) == 0) { + subtle::NoBarrier_Store(&valid_thread_id_, 0); + } +} + +} // namespace base diff --git a/base/threading/thread_collision_warner.h b/base/threading/thread_collision_warner.h new file mode 100644 index 0000000..4460602 --- /dev/null +++ b/base/threading/thread_collision_warner.h @@ -0,0 +1,244 @@ +// 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. + +#ifndef BASE_THREADING_THREAD_COLLISION_WARNER_H_ +#define BASE_THREADING_THREAD_COLLISION_WARNER_H_ +#pragma once + +#include + +#include "base/base_export.h" +#include "base/atomicops.h" + +// A helper class alongside macros to be used to verify assumptions about thread +// safety of a class. +// +// Example: Queue implementation non thread-safe but still usable if clients +// are synchronized somehow. +// +// In this case the macro DFAKE_SCOPED_LOCK has to be +// used, it checks that if a thread is inside the push/pop then +// noone else is still inside the pop/push +// +// class NonThreadSafeQueue { +// public: +// ... +// void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } +// int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Queue implementation non thread-safe but still usable if clients +// are synchronized somehow, it calls a method to "protect" from +// a "protected" method +// +// In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK +// has to be used, it checks that if a thread is inside the push/pop +// then noone else is still inside the pop/push +// +// class NonThreadSafeQueue { +// public: +// void push(int) { +// DFAKE_SCOPED_LOCK(push_pop_); +// ... +// } +// int pop() { +// DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); +// bar(); +// ... +// } +// void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Queue implementation not usable even if clients are synchronized, +// so only one thread in the class life cycle can use the two members +// push/pop. +// +// In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the +// specified +// critical section the first time a thread enters push or pop, from +// that time on only that thread is allowed to execute push or pop. +// +// class NonThreadSafeQueue { +// public: +// ... +// void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } +// int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Class that has to be contructed/destroyed on same thread, it has +// a "shareable" method (with external syncronization) and a not +// shareable method (even with external synchronization). +// +// In this case 3 Critical sections have to be defined +// +// class ExoticClass { +// public: +// ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// +// void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } +// void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// ... +// private: +// DFAKE_MUTEX(ctor_dtor_); +// DFAKE_MUTEX(shareable_section_); +// }; + + +#if !defined(NDEBUG) + +// Defines a class member that acts like a mutex. It is used only as a +// verification tool. +#define DFAKE_MUTEX(obj) \ + mutable base::ThreadCollisionWarner obj +// Asserts the call is never called simultaneously in two threads. Used at +// member function scope. +#define DFAKE_SCOPED_LOCK(obj) \ + base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj) +// Asserts the call is never called simultaneously in two threads. Used at +// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ + base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj) +// Asserts the code is always executed in the same thread. +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ + base::ThreadCollisionWarner::Check check_##obj(&obj) + +#else + +#define DFAKE_MUTEX(obj) +#define DFAKE_SCOPED_LOCK(obj) ((void)0) +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) + +#endif + +namespace base { + +// The class ThreadCollisionWarner uses an Asserter to notify the collision +// AsserterBase is the interfaces and DCheckAsserter is the default asserter +// used. During the unit tests is used another class that doesn't "DCHECK" +// in case of collision (check thread_collision_warner_unittests.cc) +struct BASE_EXPORT AsserterBase { + virtual ~AsserterBase() {} + virtual void warn() = 0; +}; + +struct BASE_EXPORT DCheckAsserter : public AsserterBase { + virtual ~DCheckAsserter() {} + virtual void warn(); +}; + +class BASE_EXPORT ThreadCollisionWarner { + public: + // The parameter asserter is there only for test purpose + ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) + : valid_thread_id_(0), + counter_(0), + asserter_(asserter) {} + + ~ThreadCollisionWarner() { + delete asserter_; + } + + // This class is meant to be used through the macro + // DFAKE_SCOPED_LOCK_THREAD_LOCKED + // it doesn't leave the critical section, as opposed to ScopedCheck, + // because the critical section being pinned is allowed to be used only + // from one thread + class BASE_EXPORT Check { + public: + explicit Check(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->EnterSelf(); + } + + ~Check() {} + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(Check); + }; + + // This class is meant to be used through the macro + // DFAKE_SCOPED_LOCK + class BASE_EXPORT ScopedCheck { + public: + explicit ScopedCheck(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->Enter(); + } + + ~ScopedCheck() { + warner_->Leave(); + } + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCheck); + }; + + // This class is meant to be used through the macro + // DFAKE_SCOPED_RECURSIVE_LOCK + class BASE_EXPORT ScopedRecursiveCheck { + public: + explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->EnterSelf(); + } + + ~ScopedRecursiveCheck() { + warner_->Leave(); + } + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck); + }; + + private: + // This method stores the current thread identifier and does a DCHECK + // if a another thread has already done it, it is safe if same thread + // calls this multiple time (recursion allowed). + void EnterSelf(); + + // Same as EnterSelf but recursion is not allowed. + void Enter(); + + // Removes the thread_id stored in order to allow other threads to + // call EnterSelf or Enter. + void Leave(); + + // This stores the thread id that is inside the critical section, if the + // value is 0 then no thread is inside. + volatile subtle::Atomic32 valid_thread_id_; + + // Counter to trace how many time a critical section was "pinned" + // (when allowed) in order to unpin it when counter_ reaches 0. + volatile subtle::Atomic32 counter_; + + // Here only for class unit tests purpose, during the test I need to not + // DCHECK but notify the collision with something else. + AsserterBase* asserter_; + + DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner); +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_COLLISION_WARNER_H_ diff --git a/base/time.h b/base/time.h new file mode 100644 index 0000000..aa97f0a --- /dev/null +++ b/base/time.h @@ -0,0 +1,566 @@ +// 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. + +// 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_ +#pragma once + +#include + +#include "base/atomicops.h" +#include "base/base_export.h" +#include "base/basictypes.h" + +#if defined(OS_POSIX) +// For struct timeval. +#include +#endif + +#if defined(OS_WIN) +// For FILETIME in FromFileTime, until it moves to a new converter class. +// See TODO(iyengar) below. +#include +#endif + +namespace base { + +class Time; +class TimeTicks; + +// TimeDelta ------------------------------------------------------------------ + +class BASE_EXPORT TimeDelta { + public: + TimeDelta() : delta_(0) { + } + + // Converts units of time to TimeDeltas. + static TimeDelta FromDays(int64 days); + static TimeDelta FromHours(int64 hours); + static TimeDelta FromMinutes(int64 minutes); + static TimeDelta FromSeconds(int64 secs); + static TimeDelta FromMilliseconds(int64 ms); + static TimeDelta FromMicroseconds(int64 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 ToInternalValue() const { + return delta_; + } + +#if defined(OS_POSIX) + struct timespec ToTimeSpec() const; +#endif + + // Returns the time delta in some unit. The F versions return a floating + // point value, the "regular" versions return a rounded-down value. + // + // InMillisecondsRoundedUp() instead returns an integer that is rounded up + // to the next full millisecond. + int InDays() const; + int InHours() const; + int InMinutes() const; + double InSecondsF() const; + int64 InSeconds() const; + double InMillisecondsF() const; + int64 InMilliseconds() const; + int64 InMillisecondsRoundedUp() const; + int64 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 a) const { + return TimeDelta(delta_ * a); + } + TimeDelta operator/(int64 a) const { + return TimeDelta(delta_ / a); + } + TimeDelta& operator*=(int64 a) { + delta_ *= a; + return *this; + } + TimeDelta& operator/=(int64 a) { + delta_ /= a; + return *this; + } + int64 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 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 delta_us) : delta_(delta_us) { + } + + // Delta in microseconds. + int64 delta_; +}; + +inline TimeDelta operator*(int64 a, TimeDelta td) { + return TimeDelta(a * td.delta_); +} + +// Time ----------------------------------------------------------------------- + +// Represents a wall clock time. +class BASE_EXPORT Time { + public: + static const int64 kMillisecondsPerSecond = 1000; + static const int64 kMicrosecondsPerMillisecond = 1000; + static const int64 kMicrosecondsPerSecond = kMicrosecondsPerMillisecond * + kMillisecondsPerSecond; + static const int64 kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60; + static const int64 kMicrosecondsPerHour = kMicrosecondsPerMinute * 60; + static const int64 kMicrosecondsPerDay = kMicrosecondsPerHour * 24; + static const int64 kMicrosecondsPerWeek = kMicrosecondsPerDay * 7; + static const int64 kNanosecondsPerMicrosecond = 1000; + static const int64 kNanosecondsPerSecond = kNanosecondsPerMicrosecond * + kMicrosecondsPerSecond; + +#if !defined(OS_WIN) + // On Mac & Linux, this value is the delta from the Windows epoch of 1601 to + // the Posix delta of 1970. This is used for migrating between the old + // 1970-based epochs to the new 1601-based ones. It should be removed from + // this global header and put in the platform-specific ones when we remove the + // migration code. + static const int64 kWindowsEpochDeltaMicroseconds; +#endif + + // 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 BASE_EXPORT Exploded { + int year; // Four digit year "2007" + int month; // 1-based month (values 1 = January, etc.) + int day_of_week; // 0-based day of week (0 = Sunday, etc.) + int day_of_month; // 1-based day of month (1-31) + int hour; // Hour within the current day (0-23) + int minute; // Minute within the current hour (0-59) + int 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) + + // A cursory test for whether the data members are within their + // respective ranges. A 'true' return value does not guarantee the + // Exploded value can be successfully converted to a Time value. + bool HasValidValues() const; + }; + + // 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 time for epoch in Unix-like system (Jan 1, 1970). + static Time UnixEpoch(); + + // 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. + // Because WebKit initializes double time value to 0 to indicate "not + // initialized", we map it to empty Time object that also means "not + // initialized". + static Time FromDoubleT(double dt); + double ToDoubleT() const; + +#if defined(OS_POSIX) + struct timeval ToTimeVal() const; +#endif + +#if defined(OS_WIN) + static Time FromFileTime(FILETIME ft); + FILETIME ToFileTime() const; + + // The minimum time of a low resolution timer. This is basically a windows + // constant of ~15.6ms. While it does vary on some older OS versions, we'll + // treat it as static across all windows versions. + static const int kMinLowResolutionThresholdMs = 16; + + // Enable or disable Windows high resolution timer. If the high resolution + // timer is not enabled, calls to ActivateHighResolutionTimer will fail. + // When disabling the high resolution timer, this function will not cause + // the high resolution timer to be deactivated, but will prevent future + // activations. + // Must be called from the main thread. + // For more details see comments in time_win.cc. + static void EnableHighResolutionTimer(bool enable); + + // Activates or deactivates the high resolution timer based on the |activate| + // flag. If the HighResolutionTimer is not Enabled (see + // EnableHighResolutionTimer), this function will return false. Otherwise + // returns true. Each successful activate call must be paired with a + // subsequent deactivate call. + // All callers to activate the high resolution timer must eventually call + // this function to deactivate the high resolution timer. + static bool ActivateHighResolutionTimer(bool activate); + + // Returns true if the high resolution timer is both enabled and activated. + // This is provided for testing only, and is not tracked in a thread-safe + // way. + static bool IsHighResolutionTimerInUse(); +#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 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 char* 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 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; + + explicit Time(int64 us) : us_(us) { + } + + // 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); + + // The representation of Jan 1, 1970 UTC in microseconds since the + // platform-dependent epoch. + static const int64 kTimeTToMicrosecondsOffset; + +#if defined(OS_WIN) + // Indicates whether fast timers are usable right now. For instance, + // when using battery power, we might elect to prevent high speed timers + // which would draw more power. + static bool high_resolution_timer_enabled_; + // Count of activations on the high resolution timer. Only use in tests + // which are single threaded. + static int high_resolution_timer_activated_; +#endif + + // Time in microseconds in UTC. + int64 us_; +}; + +// Inline the TimeDelta factory methods, for fast TimeDelta construction. + +// static +inline TimeDelta TimeDelta::FromDays(int64 days) { + return TimeDelta(days * Time::kMicrosecondsPerDay); +} + +// static +inline TimeDelta TimeDelta::FromHours(int64 hours) { + return TimeDelta(hours * Time::kMicrosecondsPerHour); +} + +// static +inline TimeDelta TimeDelta::FromMinutes(int64 minutes) { + return TimeDelta(minutes * Time::kMicrosecondsPerMinute); +} + +// static +inline TimeDelta TimeDelta::FromSeconds(int64 secs) { + return TimeDelta(secs * Time::kMicrosecondsPerSecond); +} + +// static +inline TimeDelta TimeDelta::FromMilliseconds(int64 ms) { + return TimeDelta(ms * Time::kMicrosecondsPerMillisecond); +} + +// static +inline TimeDelta TimeDelta::FromMicroseconds(int64 us) { + return TimeDelta(us); +} + +inline Time TimeDelta::operator+(Time t) const { + return Time(t.us_ + delta_); +} + +// TimeTicks ------------------------------------------------------------------ + +class BASE_EXPORT 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(); + +#if defined(OS_WIN) + // Get the absolute value of QPC time drift. For testing. + static int64 GetQPCDriftMicroseconds(); + + // Returns true if the high resolution clock is working on this system. + // This is only for testing. + static bool IsHighResClockWorking(); +#endif + + // 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 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; + + // Please use Now() to create a new object. This is for internal use + // and testing. Ticks is in microseconds. + explicit TimeTicks(int64 ticks) : ticks_(ticks) { + } + + // Tick count in microseconds. + int64 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/base/tuple.h b/base/tuple.h new file mode 100644 index 0000000..65d0908 --- /dev/null +++ b/base/tuple.h @@ -0,0 +1,1056 @@ +// 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 tuple_a(1, "wee"); +// Tuple2 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__ +#pragma once + +#if defined(OS_CHROMEOS) +// To troubleshoot crosbug.com/7327. +#include "base/logging.h" +#endif +// 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 +struct TupleTraits { + typedef P ValueType; + typedef P& RefType; + typedef const P& ParamType; +}; + +template +struct TupleTraits { + typedef P ValueType; + typedef P& RefType; + typedef P& ParamType; +}; + +template +struct TupleTypes { }; + +// 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 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; + typedef Tuple0 ParamTuple; +}; + +template +struct Tuple1 { + public: + typedef A TypeA; + + Tuple1() {} + explicit Tuple1(typename TupleTraits::ParamType a) : a(a) {} + + A a; +}; + +template +struct Tuple2 { + public: + typedef A TypeA; + typedef B TypeB; + + Tuple2() {} + Tuple2(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b) + : a(a), b(b) { + } + + A a; + B b; +}; + +template +struct Tuple3 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + + Tuple3() {} + Tuple3(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c) + : a(a), b(b), c(c){ + } + + A a; + B b; + C c; +}; + +template +struct Tuple4 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + + Tuple4() {} + Tuple4(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d) + : a(a), b(b), c(c), d(d) { + } + + A a; + B b; + C c; + D d; +}; + +template +struct Tuple5 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + + Tuple5() {} + Tuple5(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d, + typename TupleTraits::ParamType e) + : a(a), b(b), c(c), d(d), e(e) { + } + + A a; + B b; + C c; + D d; + E e; +}; + +template +struct Tuple6 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + + Tuple6() {} + Tuple6(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d, + typename TupleTraits::ParamType e, + typename TupleTraits::ParamType f) + : a(a), b(b), c(c), d(d), e(e), f(f) { + } + + A a; + B b; + C c; + D d; + E e; + F f; +}; + +template +struct Tuple7 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + typedef G TypeG; + + Tuple7() {} + Tuple7(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d, + typename TupleTraits::ParamType e, + typename TupleTraits::ParamType f, + typename TupleTraits::ParamType g) + : a(a), b(b), c(c), d(d), e(e), f(f), g(g) { + } + + A a; + B b; + C c; + D d; + E e; + F f; + G g; +}; + +template +struct Tuple8 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + typedef G TypeG; + typedef H TypeH; + + Tuple8() {} + Tuple8(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d, + typename TupleTraits::ParamType e, + typename TupleTraits::ParamType f, + typename TupleTraits::ParamType g, + typename TupleTraits::ParamType h) + : a(a), b(b), c(c), d(d), e(e), f(f), g(g), h(h) { + } + + A a; + B b; + C c; + D d; + E e; + F f; + G g; + H h; +}; + +// Tuple types ---------------------------------------------------------------- +// +// Allows for selection of ValueTuple/RefTuple/ParamTuple without needing the +// definitions of class types the tuple takes as parameters. + +template <> +struct TupleTypes< Tuple0 > { + typedef Tuple0 ValueTuple; + typedef Tuple0 RefTuple; + typedef Tuple0 ParamTuple; +}; + +template +struct TupleTypes< Tuple1 > { + typedef Tuple1::ValueType> ValueTuple; + typedef Tuple1::RefType> RefTuple; + typedef Tuple1::ParamType> ParamTuple; +}; + +template +struct TupleTypes< Tuple2 > { + typedef Tuple2::ValueType, + typename TupleTraits::ValueType> ValueTuple; +typedef Tuple2::RefType, + typename TupleTraits::RefType> RefTuple; + typedef Tuple2::ParamType, + typename TupleTraits::ParamType> ParamTuple; +}; + +template +struct TupleTypes< Tuple3 > { + typedef Tuple3::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; +typedef Tuple3::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + typedef Tuple3::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType> ParamTuple; +}; + +template +struct TupleTypes< Tuple4 > { + typedef Tuple4::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; +typedef Tuple4::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + typedef Tuple4::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType> ParamTuple; +}; + +template +struct TupleTypes< Tuple5 > { + typedef Tuple5::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; +typedef Tuple5::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + typedef Tuple5::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType> ParamTuple; +}; + +template +struct TupleTypes< Tuple6 > { + typedef Tuple6::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; +typedef Tuple6::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + typedef Tuple6::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType> ParamTuple; +}; + +template +struct TupleTypes< Tuple7 > { + typedef Tuple7::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; +typedef Tuple7::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + typedef Tuple7::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType> ParamTuple; +}; + +template +struct TupleTypes< Tuple8 > { + typedef Tuple8::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; +typedef Tuple8::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + typedef Tuple8::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType, + typename TupleTraits::ParamType> ParamTuple; +}; + +// Tuple creators ------------------------------------------------------------- +// +// Helper functions for constructing tuples while inferring the template +// argument types. + +inline Tuple0 MakeTuple() { + return Tuple0(); +} + +template +inline Tuple1 MakeTuple(const A& a) { + return Tuple1(a); +} + +template +inline Tuple2 MakeTuple(const A& a, const B& b) { + return Tuple2(a, b); +} + +template +inline Tuple3 MakeTuple(const A& a, const B& b, const C& c) { + return Tuple3(a, b, c); +} + +template +inline Tuple4 MakeTuple(const A& a, const B& b, const C& c, + const D& d) { + return Tuple4(a, b, c, d); +} + +template +inline Tuple5 MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e) { + return Tuple5(a, b, c, d, e); +} + +template +inline Tuple6 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); +} + +template +inline Tuple7 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); +} + +template +inline Tuple8 MakeTuple(const A& a, const B& b, + const C& c, const D& d, + const E& e, const F& f, + const G& g, const H& h) { + return Tuple8(a, b, c, d, e, f, g, h); +} + +// The following set of helpers make what Boost refers to as "Tiers" - a tuple +// of references. + +template +inline Tuple1 MakeRefTuple(A& a) { + return Tuple1(a); +} + +template +inline Tuple2 MakeRefTuple(A& a, B& b) { + return Tuple2(a, b); +} + +template +inline Tuple3 MakeRefTuple(A& a, B& b, C& c) { + return Tuple3(a, b, c); +} + +template +inline Tuple4 MakeRefTuple(A& a, B& b, C& c, D& d) { + return Tuple4(a, b, c, d); +} + +template +inline Tuple5 MakeRefTuple(A& a, B& b, C& c, D& d, E& e) { + return Tuple5(a, b, c, d, e); +} + +template +inline Tuple6 MakeRefTuple(A& a, B& b, C& c, D& d, E& e, + F& f) { + return Tuple6(a, b, c, d, e, f); +} + +template +inline Tuple7 MakeRefTuple(A& a, B& b, C& c, D& d, + E& e, F& f, G& g) { + return Tuple7(a, b, c, d, e, f, g); +} + +template +inline Tuple8 MakeRefTuple(A& a, B& b, C& c, + D& d, E& e, F& f, + G& g, H& h) { + return Tuple8(a, b, c, d, e, f, g, h); +} + +// 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 +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) { + (obj->*method)(); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) { + (obj->*method)(arg); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1& arg) { + (obj->*method)(arg.a); +} + +template +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple2& arg) { + (obj->*method)(arg.a, arg.b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& arg) { + (obj->*method)(arg.a, arg.b, arg.c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple7& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple8& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g, arg.h); +} + +// Static Dispatchers with no out params. + +template +inline void DispatchToFunction(Function function, const Tuple0& arg) { + (*function)(); +} + +template +inline void DispatchToFunction(Function function, const A& arg) { + (*function)(arg); +} + +template +inline void DispatchToFunction(Function function, const Tuple1& arg) { + (*function)(arg.a); +} + +template +inline void DispatchToFunction(Function function, const Tuple2& arg) { + (*function)(arg.a, arg.b); +} + +template +inline void DispatchToFunction(Function function, const Tuple3& arg) { + (*function)(arg.a, arg.b, arg.c); +} + +template +inline void DispatchToFunction(Function function, + const Tuple4& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d); +} + +template +inline void DispatchToFunction(Function function, + const Tuple5& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template +inline void DispatchToFunction(Function function, + const Tuple6& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +template +inline void DispatchToFunction(Function function, + const Tuple7& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g); +} + +template +inline void DispatchToFunction(Function function, + const Tuple8& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g, arg.h); +} + +// Dispatchers with 0 out param (as a Tuple0). + +template +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple0& arg, Tuple0*) { + (obj->*method)(); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) { + (obj->*method)(arg); +} + +template +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple1& arg, Tuple0*) { + (obj->*method)(arg.a); +} + +template +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple2& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +// Dispatchers with 1 out param. + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple1* out) { + (obj->*method)(&out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple1* out) { + (obj->*method)(in, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple1* out) { + (obj->*method)(in.a, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, in.c, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a); +} + +// Dispatchers with 2 out params. + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple2* out) { + (obj->*method)(&out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple2* out) { + (obj->*method)(in, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple2* out) { + (obj->*method)(in.a, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple2* 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 +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple3* out) { + (obj->*method)(&out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple3* out) { + (obj->*method)(in, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple3* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple3* 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 +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple4* out) { + (obj->*method)(&out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple4* out) { + (obj->*method)(in, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple4* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, + &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple4* 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 +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple5* out) { + (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple5* out) { + (obj->*method)(in, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple5* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d, + &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, + &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple5* 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/base/values.cc b/base/values.cc new file mode 100644 index 0000000..7a364bd --- /dev/null +++ b/base/values.cc @@ -0,0 +1,905 @@ +// 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. + +#include "base/values.h" + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" + +namespace { + +// Make a deep copy of |node|, but don't include empty lists or dictionaries +// in the copy. It's possible for this function to return NULL and it +// expects |node| to always be non-NULL. +Value* CopyWithoutEmptyChildren(Value* node) { + DCHECK(node); + switch (node->GetType()) { + case Value::TYPE_LIST: { + ListValue* list = static_cast(node); + ListValue* copy = new ListValue; + for (ListValue::const_iterator it = list->begin(); it != list->end(); + ++it) { + Value* child_copy = CopyWithoutEmptyChildren(*it); + if (child_copy) + copy->Append(child_copy); + } + if (!copy->empty()) + return copy; + + delete copy; + return NULL; + } + + case Value::TYPE_DICTIONARY: { + DictionaryValue* dict = static_cast(node); + DictionaryValue* copy = new DictionaryValue; + for (DictionaryValue::key_iterator it = dict->begin_keys(); + it != dict->end_keys(); ++it) { + Value* child = NULL; + bool rv = dict->GetWithoutPathExpansion(*it, &child); + DCHECK(rv); + Value* child_copy = CopyWithoutEmptyChildren(child); + if (child_copy) + copy->SetWithoutPathExpansion(*it, child_copy); + } + if (!copy->empty()) + return copy; + + delete copy; + return NULL; + } + + default: + // For everything else, just make a copy. + return node->DeepCopy(); + } +} + +} // namespace + +namespace base { + +///////////////////// Value //////////////////// + +Value::~Value() { +} + +// static +Value* Value::CreateNullValue() { + return new Value(TYPE_NULL); +} + +// static +FundamentalValue* Value::CreateBooleanValue(bool in_value) { + return new FundamentalValue(in_value); +} + +// static +FundamentalValue* Value::CreateIntegerValue(int in_value) { + return new FundamentalValue(in_value); +} + +// static +FundamentalValue* Value::CreateDoubleValue(double in_value) { + return new FundamentalValue(in_value); +} + +// static +StringValue* Value::CreateStringValue(const std::string& in_value) { + return new StringValue(in_value); +} + +// static +StringValue* Value::CreateStringValue(const string16& in_value) { + return new StringValue(in_value); +} + +bool Value::GetAsBoolean(bool* out_value) const { + return false; +} + +bool Value::GetAsInteger(int* out_value) const { + return false; +} + +bool Value::GetAsDouble(double* out_value) const { + return false; +} + +bool Value::GetAsString(std::string* out_value) const { + return false; +} + +bool Value::GetAsString(string16* out_value) const { + return false; +} + +bool Value::GetAsList(ListValue** out_value) { + return false; +} + +bool Value::GetAsList(const ListValue** out_value) const { + return false; +} + +Value* Value::DeepCopy() const { + // This method should only be getting called for null Values--all subclasses + // need to provide their own implementation;. + DCHECK(IsType(TYPE_NULL)); + return CreateNullValue(); +} + +bool Value::Equals(const Value* other) const { + // This method should only be getting called for null Values--all subclasses + // need to provide their own implementation;. + DCHECK(IsType(TYPE_NULL)); + return other->IsType(TYPE_NULL); +} + +// static +bool Value::Equals(const Value* a, const Value* b) { + if ((a == NULL) && (b == NULL)) return true; + if ((a == NULL) ^ (b == NULL)) return false; + return a->Equals(b); +} + +Value::Value(Type type) : type_(type) { +} + +///////////////////// FundamentalValue //////////////////// + +FundamentalValue::FundamentalValue(bool in_value) + : Value(TYPE_BOOLEAN), boolean_value_(in_value) { +} + +FundamentalValue::FundamentalValue(int in_value) + : Value(TYPE_INTEGER), integer_value_(in_value) { +} + +FundamentalValue::FundamentalValue(double in_value) + : Value(TYPE_DOUBLE), double_value_(in_value) { +} + +FundamentalValue::~FundamentalValue() { +} + +bool FundamentalValue::GetAsBoolean(bool* out_value) const { + if (out_value && IsType(TYPE_BOOLEAN)) + *out_value = boolean_value_; + return (IsType(TYPE_BOOLEAN)); +} + +bool FundamentalValue::GetAsInteger(int* out_value) const { + if (out_value && IsType(TYPE_INTEGER)) + *out_value = integer_value_; + return (IsType(TYPE_INTEGER)); +} + +bool FundamentalValue::GetAsDouble(double* out_value) const { + if (out_value && IsType(TYPE_DOUBLE)) + *out_value = double_value_; + else if (out_value && IsType(TYPE_INTEGER)) + *out_value = integer_value_; + return (IsType(TYPE_DOUBLE) || IsType(TYPE_INTEGER)); +} + +FundamentalValue* FundamentalValue::DeepCopy() const { + switch (GetType()) { + case TYPE_BOOLEAN: + return CreateBooleanValue(boolean_value_); + + case TYPE_INTEGER: + return CreateIntegerValue(integer_value_); + + case TYPE_DOUBLE: + return CreateDoubleValue(double_value_); + + default: + NOTREACHED(); + return NULL; + } +} + +bool FundamentalValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + + switch (GetType()) { + case TYPE_BOOLEAN: { + bool lhs, rhs; + return GetAsBoolean(&lhs) && other->GetAsBoolean(&rhs) && lhs == rhs; + } + case TYPE_INTEGER: { + int lhs, rhs; + return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs; + } + case TYPE_DOUBLE: { + double lhs, rhs; + return GetAsDouble(&lhs) && other->GetAsDouble(&rhs) && lhs == rhs; + } + default: + NOTREACHED(); + return false; + } +} + +///////////////////// StringValue //////////////////// + +StringValue::StringValue(const std::string& in_value) + : Value(TYPE_STRING), + value_(in_value) { + DCHECK(IsStringUTF8(in_value)); +} + +StringValue::StringValue(const string16& in_value) + : Value(TYPE_STRING), + value_(UTF16ToUTF8(in_value)) { +} + +StringValue::~StringValue() { +} + +bool StringValue::GetAsString(std::string* out_value) const { + if (out_value) + *out_value = value_; + return true; +} + +bool StringValue::GetAsString(string16* out_value) const { + if (out_value) + *out_value = UTF8ToUTF16(value_); + return true; +} + +StringValue* StringValue::DeepCopy() const { + return CreateStringValue(value_); +} + +bool StringValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + std::string lhs, rhs; + return GetAsString(&lhs) && other->GetAsString(&rhs) && lhs == rhs; +} + +///////////////////// BinaryValue //////////////////// + +BinaryValue::~BinaryValue() { + DCHECK(buffer_); + if (buffer_) + delete[] buffer_; +} + +// static +BinaryValue* BinaryValue::Create(char* buffer, size_t size) { + if (!buffer) + return NULL; + + return new BinaryValue(buffer, size); +} + +// static +BinaryValue* BinaryValue::CreateWithCopiedBuffer(const char* buffer, + size_t size) { + if (!buffer) + return NULL; + + char* buffer_copy = new char[size]; + memcpy(buffer_copy, buffer, size); + return new BinaryValue(buffer_copy, size); +} + +BinaryValue* BinaryValue::DeepCopy() const { + return CreateWithCopiedBuffer(buffer_, size_); +} + +bool BinaryValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + const BinaryValue* other_binary = static_cast(other); + if (other_binary->size_ != size_) + return false; + return !memcmp(buffer_, other_binary->buffer_, size_); +} + +BinaryValue::BinaryValue(char* buffer, size_t size) + : Value(TYPE_BINARY), + buffer_(buffer), + size_(size) { + DCHECK(buffer_); +} + +///////////////////// DictionaryValue //////////////////// + +DictionaryValue::DictionaryValue() + : Value(TYPE_DICTIONARY) { +} + +DictionaryValue::~DictionaryValue() { + Clear(); +} + +bool DictionaryValue::HasKey(const std::string& key) const { + DCHECK(IsStringUTF8(key)); + ValueMap::const_iterator current_entry = dictionary_.find(key); + DCHECK((current_entry == dictionary_.end()) || current_entry->second); + return current_entry != dictionary_.end(); +} + +void DictionaryValue::Clear() { + ValueMap::iterator dict_iterator = dictionary_.begin(); + while (dict_iterator != dictionary_.end()) { + delete dict_iterator->second; + ++dict_iterator; + } + + dictionary_.clear(); +} + +void DictionaryValue::Set(const std::string& path, Value* in_value) { + DCHECK(IsStringUTF8(path)); + DCHECK(in_value); + + std::string current_path(path); + DictionaryValue* current_dictionary = this; + for (size_t delimiter_position = current_path.find('.'); + delimiter_position != std::string::npos; + delimiter_position = current_path.find('.')) { + // Assume that we're indexing into a dictionary. + std::string key(current_path, 0, delimiter_position); + DictionaryValue* child_dictionary = NULL; + if (!current_dictionary->GetDictionary(key, &child_dictionary)) { + child_dictionary = new DictionaryValue; + current_dictionary->SetWithoutPathExpansion(key, child_dictionary); + } + + current_dictionary = child_dictionary; + current_path.erase(0, delimiter_position + 1); + } + + current_dictionary->SetWithoutPathExpansion(current_path, in_value); +} + +void DictionaryValue::SetBoolean(const std::string& path, bool in_value) { + Set(path, CreateBooleanValue(in_value)); +} + +void DictionaryValue::SetInteger(const std::string& path, int in_value) { + Set(path, CreateIntegerValue(in_value)); +} + +void DictionaryValue::SetDouble(const std::string& path, double in_value) { + Set(path, CreateDoubleValue(in_value)); +} + +void DictionaryValue::SetString(const std::string& path, + const std::string& in_value) { + Set(path, CreateStringValue(in_value)); +} + +void DictionaryValue::SetString(const std::string& path, + const string16& in_value) { + Set(path, CreateStringValue(in_value)); +} + +void DictionaryValue::SetWithoutPathExpansion(const std::string& key, + Value* in_value) { + // If there's an existing value here, we need to delete it, because + // we own all our children. + if (HasKey(key)) { + DCHECK(dictionary_[key] != in_value); // This would be bogus + delete dictionary_[key]; + } + + dictionary_[key] = in_value; +} + +bool DictionaryValue::Get(const std::string& path, Value** out_value) const { + DCHECK(IsStringUTF8(path)); + std::string current_path(path); + const DictionaryValue* current_dictionary = this; + for (size_t delimiter_position = current_path.find('.'); + delimiter_position != std::string::npos; + delimiter_position = current_path.find('.')) { + DictionaryValue* child_dictionary = NULL; + if (!current_dictionary->GetDictionary( + current_path.substr(0, delimiter_position), &child_dictionary)) + return false; + + current_dictionary = child_dictionary; + current_path.erase(0, delimiter_position + 1); + } + + return current_dictionary->GetWithoutPathExpansion(current_path, out_value); +} + +bool DictionaryValue::GetBoolean(const std::string& path, + bool* bool_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsBoolean(bool_value); +} + +bool DictionaryValue::GetInteger(const std::string& path, + int* out_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsInteger(out_value); +} + +bool DictionaryValue::GetDouble(const std::string& path, + double* out_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsDouble(out_value); +} + +bool DictionaryValue::GetString(const std::string& path, + std::string* out_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool DictionaryValue::GetString(const std::string& path, + string16* out_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool DictionaryValue::GetStringASCII(const std::string& path, + std::string* out_value) const { + std::string out; + if (!GetString(path, &out)) + return false; + + if (!IsStringASCII(out)) { + NOTREACHED(); + return false; + } + + out_value->assign(out); + return true; +} + +bool DictionaryValue::GetBinary(const std::string& path, + BinaryValue** out_value) const { + Value* value; + bool result = Get(path, &value); + if (!result || !value->IsType(TYPE_BINARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool DictionaryValue::GetDictionary(const std::string& path, + DictionaryValue** out_value) const { + Value* value; + bool result = Get(path, &value); + if (!result || !value->IsType(TYPE_DICTIONARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool DictionaryValue::GetList(const std::string& path, + ListValue** out_value) const { + Value* value; + bool result = Get(path, &value); + if (!result || !value->IsType(TYPE_LIST)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool DictionaryValue::GetWithoutPathExpansion(const std::string& key, + Value** out_value) const { + DCHECK(IsStringUTF8(key)); + ValueMap::const_iterator entry_iterator = dictionary_.find(key); + if (entry_iterator == dictionary_.end()) + return false; + + Value* entry = entry_iterator->second; + if (out_value) + *out_value = entry; + return true; +} + +bool DictionaryValue::GetIntegerWithoutPathExpansion(const std::string& key, + int* out_value) const { + Value* value; + if (!GetWithoutPathExpansion(key, &value)) + return false; + + return value->GetAsInteger(out_value); +} + +bool DictionaryValue::GetDoubleWithoutPathExpansion(const std::string& key, + double* out_value) const { + Value* value; + if (!GetWithoutPathExpansion(key, &value)) + return false; + + return value->GetAsDouble(out_value); +} + +bool DictionaryValue::GetStringWithoutPathExpansion( + const std::string& key, + std::string* out_value) const { + Value* value; + if (!GetWithoutPathExpansion(key, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool DictionaryValue::GetStringWithoutPathExpansion( + const std::string& key, + string16* out_value) const { + Value* value; + if (!GetWithoutPathExpansion(key, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool DictionaryValue::GetDictionaryWithoutPathExpansion( + const std::string& key, + DictionaryValue** out_value) const { + Value* value; + bool result = GetWithoutPathExpansion(key, &value); + if (!result || !value->IsType(TYPE_DICTIONARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool DictionaryValue::GetListWithoutPathExpansion(const std::string& key, + ListValue** out_value) const { + Value* value; + bool result = GetWithoutPathExpansion(key, &value); + if (!result || !value->IsType(TYPE_LIST)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool DictionaryValue::Remove(const std::string& path, Value** out_value) { + DCHECK(IsStringUTF8(path)); + std::string current_path(path); + DictionaryValue* current_dictionary = this; + size_t delimiter_position = current_path.rfind('.'); + if (delimiter_position != std::string::npos) { + if (!GetDictionary(current_path.substr(0, delimiter_position), + ¤t_dictionary)) + return false; + current_path.erase(0, delimiter_position + 1); + } + + return current_dictionary->RemoveWithoutPathExpansion(current_path, + out_value); +} + +bool DictionaryValue::RemoveWithoutPathExpansion(const std::string& key, + Value** out_value) { + DCHECK(IsStringUTF8(key)); + ValueMap::iterator entry_iterator = dictionary_.find(key); + if (entry_iterator == dictionary_.end()) + return false; + + Value* entry = entry_iterator->second; + if (out_value) + *out_value = entry; + else + delete entry; + dictionary_.erase(entry_iterator); + return true; +} + +DictionaryValue* DictionaryValue::DeepCopyWithoutEmptyChildren() { + Value* copy = CopyWithoutEmptyChildren(this); + return copy ? static_cast(copy) : new DictionaryValue; +} + +void DictionaryValue::MergeDictionary(const DictionaryValue* dictionary) { + for (DictionaryValue::key_iterator key(dictionary->begin_keys()); + key != dictionary->end_keys(); ++key) { + Value* merge_value; + if (dictionary->GetWithoutPathExpansion(*key, &merge_value)) { + // Check whether we have to merge dictionaries. + if (merge_value->IsType(Value::TYPE_DICTIONARY)) { + DictionaryValue* sub_dict; + if (GetDictionaryWithoutPathExpansion(*key, &sub_dict)) { + sub_dict->MergeDictionary( + static_cast(merge_value)); + continue; + } + } + // All other cases: Make a copy and hook it up. + SetWithoutPathExpansion(*key, merge_value->DeepCopy()); + } + } +} + +DictionaryValue* DictionaryValue::DeepCopy() const { + DictionaryValue* result = new DictionaryValue; + + for (ValueMap::const_iterator current_entry(dictionary_.begin()); + current_entry != dictionary_.end(); ++current_entry) { + result->SetWithoutPathExpansion(current_entry->first, + current_entry->second->DeepCopy()); + } + + return result; +} + +bool DictionaryValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + + const DictionaryValue* other_dict = + static_cast(other); + key_iterator lhs_it(begin_keys()); + key_iterator rhs_it(other_dict->begin_keys()); + while (lhs_it != end_keys() && rhs_it != other_dict->end_keys()) { + Value* lhs; + Value* rhs; + if (*lhs_it != *rhs_it || + !GetWithoutPathExpansion(*lhs_it, &lhs) || + !other_dict->GetWithoutPathExpansion(*rhs_it, &rhs) || + !lhs->Equals(rhs)) { + return false; + } + ++lhs_it; + ++rhs_it; + } + if (lhs_it != end_keys() || rhs_it != other_dict->end_keys()) + return false; + + return true; +} + +///////////////////// ListValue //////////////////// + +ListValue::ListValue() : Value(TYPE_LIST) { +} + +ListValue::~ListValue() { + Clear(); +} + +void ListValue::Clear() { + for (ValueVector::iterator i(list_.begin()); i != list_.end(); ++i) + delete *i; + list_.clear(); +} + +bool ListValue::Set(size_t index, Value* in_value) { + if (!in_value) + return false; + + if (index >= list_.size()) { + // Pad out any intermediate indexes with null settings + while (index > list_.size()) + Append(CreateNullValue()); + Append(in_value); + } else { + DCHECK(list_[index] != in_value); + delete list_[index]; + list_[index] = in_value; + } + return true; +} + +bool ListValue::Get(size_t index, Value** out_value) const { + if (index >= list_.size()) + return false; + + if (out_value) + *out_value = list_[index]; + + return true; +} + +bool ListValue::GetBoolean(size_t index, bool* bool_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsBoolean(bool_value); +} + +bool ListValue::GetInteger(size_t index, int* out_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsInteger(out_value); +} + +bool ListValue::GetDouble(size_t index, double* out_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsDouble(out_value); +} + +bool ListValue::GetString(size_t index, std::string* out_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool ListValue::GetString(size_t index, string16* out_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool ListValue::GetBinary(size_t index, BinaryValue** out_value) const { + Value* value; + bool result = Get(index, &value); + if (!result || !value->IsType(TYPE_BINARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) const { + Value* value; + bool result = Get(index, &value); + if (!result || !value->IsType(TYPE_DICTIONARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool ListValue::GetList(size_t index, ListValue** out_value) const { + Value* value; + bool result = Get(index, &value); + if (!result || !value->IsType(TYPE_LIST)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool ListValue::Remove(size_t index, Value** out_value) { + if (index >= list_.size()) + return false; + + if (out_value) + *out_value = list_[index]; + else + delete list_[index]; + + list_.erase(list_.begin() + index); + return true; +} + +bool ListValue::Remove(const Value& value, size_t* index) { + for (ValueVector::iterator i(list_.begin()); i != list_.end(); ++i) { + if ((*i)->Equals(&value)) { + size_t previous_index = i - list_.begin(); + delete *i; + list_.erase(i); + + if (index) + *index = previous_index; + return true; + } + } + return false; +} + +void ListValue::Append(Value* in_value) { + DCHECK(in_value); + list_.push_back(in_value); +} + +bool ListValue::AppendIfNotPresent(Value* in_value) { + DCHECK(in_value); + for (ValueVector::const_iterator i(list_.begin()); i != list_.end(); ++i) { + if ((*i)->Equals(in_value)) { + delete in_value; + return false; + } + } + list_.push_back(in_value); + return true; +} + +bool ListValue::Insert(size_t index, Value* in_value) { + DCHECK(in_value); + if (index > list_.size()) + return false; + + list_.insert(list_.begin() + index, in_value); + return true; +} + +bool ListValue::GetAsList(ListValue** out_value) { + if (out_value) + *out_value = this; + return true; +} + +bool ListValue::GetAsList(const ListValue** out_value) const { + if (out_value) + *out_value = this; + return true; +} + +ListValue* ListValue::DeepCopy() const { + ListValue* result = new ListValue; + + for (ValueVector::const_iterator i(list_.begin()); i != list_.end(); ++i) + result->Append((*i)->DeepCopy()); + + return result; +} + +bool ListValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + + const ListValue* other_list = + static_cast(other); + const_iterator lhs_it, rhs_it; + for (lhs_it = begin(), rhs_it = other_list->begin(); + lhs_it != end() && rhs_it != other_list->end(); + ++lhs_it, ++rhs_it) { + if (!(*lhs_it)->Equals(*rhs_it)) + return false; + } + if (lhs_it != end() || rhs_it != other_list->end()) + return false; + + return true; +} + +ValueSerializer::~ValueSerializer() { +} + +} // namespace base diff --git a/base/values.h b/base/values.h new file mode 100644 index 0000000..a30791b --- /dev/null +++ b/base/values.h @@ -0,0 +1,470 @@ +// 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. + +// This file specifies a recursive data storage class called Value intended for +// storing setting and other persistable data. It includes the ability to +// specify (recursive) lists and dictionaries, so it's fairly expressive. +// However, the API is optimized for the common case, namely storing a +// hierarchical tree of simple values. Given a DictionaryValue root, you can +// easily do things like: +// +// root->SetString("global.pages.homepage", "http://goateleporter.com"); +// std::string homepage = "http://google.com"; // default/fallback value +// root->GetString("global.pages.homepage", &homepage); +// +// where "global" and "pages" are also DictionaryValues, and "homepage" is a +// string setting. If some elements of the path didn't exist yet, the +// SetString() method would create the missing elements and attach them to root +// before attaching the homepage value. + +#ifndef BASE_VALUES_H_ +#define BASE_VALUES_H_ +#pragma once + +#include +#include +#include +#include + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/string16.h" + +// This file declares "using base::Value", etc. at the bottom, so that +// current code can use these classes without the base namespace. In +// new code, please always use base::Value, etc. or add your own +// "using" declaration. +// http://crbug.com/88666 +namespace base { + +class BinaryValue; +class DictionaryValue; +class FundamentalValue; +class ListValue; +class StringValue; +class Value; + +typedef std::vector ValueVector; +typedef std::map ValueMap; + +// The Value class is the base class for Values. A Value can be instantiated +// via the Create*Value() factory methods, or by directly creating instances of +// the subclasses. +class BASE_EXPORT Value { + public: + enum Type { + TYPE_NULL = 0, + TYPE_BOOLEAN, + TYPE_INTEGER, + TYPE_DOUBLE, + TYPE_STRING, + TYPE_BINARY, + TYPE_DICTIONARY, + TYPE_LIST + }; + + virtual ~Value(); + + // Convenience methods for creating Value objects for various + // kinds of values without thinking about which class implements them. + // These can always be expected to return a valid Value*. + static Value* CreateNullValue(); + static FundamentalValue* CreateBooleanValue(bool in_value); + static FundamentalValue* CreateIntegerValue(int in_value); + static FundamentalValue* CreateDoubleValue(double in_value); + static StringValue* CreateStringValue(const std::string& in_value); + static StringValue* CreateStringValue(const string16& in_value); + + // Returns the type of the value stored by the current Value object. + // Each type will be implemented by only one subclass of Value, so it's + // safe to use the Type to determine whether you can cast from + // Value* to (Implementing Class)*. Also, a Value object never changes + // its type after construction. + Type GetType() const { return type_; } + + // Returns true if the current object represents a given type. + bool IsType(Type type) const { return type == type_; } + + // These methods allow the convenient retrieval of settings. + // If the current setting object can be converted into the given type, + // the value is returned through the |out_value| parameter and true is + // returned; otherwise, false is returned and |out_value| is unchanged. + virtual bool GetAsBoolean(bool* out_value) const; + virtual bool GetAsInteger(int* out_value) const; + virtual bool GetAsDouble(double* out_value) const; + virtual bool GetAsString(std::string* out_value) const; + virtual bool GetAsString(string16* out_value) const; + virtual bool GetAsList(ListValue** out_value); + virtual bool GetAsList(const ListValue** out_value) const; + + // This creates a deep copy of the entire Value tree, and returns a pointer + // to the copy. The caller gets ownership of the copy, of course. + // + // Subclasses return their own type directly in their overrides; + // this works because C++ supports covariant return types. + virtual Value* DeepCopy() const; + + // Compares if two Value objects have equal contents. + virtual bool Equals(const Value* other) const; + + // Compares if two Value objects have equal contents. Can handle NULLs. + // NULLs are considered equal but different from Value::CreateNullValue(). + static bool Equals(const Value* a, const Value* b); + + protected: + // This isn't safe for end-users (they should use the Create*Value() + // static methods above), but it's useful for subclasses. + explicit Value(Type type); + + private: + Value(); + + Type type_; + + DISALLOW_COPY_AND_ASSIGN(Value); +}; + +// FundamentalValue represents the simple fundamental types of values. +class BASE_EXPORT FundamentalValue : public Value { + public: + explicit FundamentalValue(bool in_value); + explicit FundamentalValue(int in_value); + explicit FundamentalValue(double in_value); + virtual ~FundamentalValue(); + + // Overridden from Value: + virtual bool GetAsBoolean(bool* out_value) const OVERRIDE; + virtual bool GetAsInteger(int* out_value) const OVERRIDE; + virtual bool GetAsDouble(double* out_value) const OVERRIDE; + virtual FundamentalValue* DeepCopy() const OVERRIDE; + virtual bool Equals(const Value* other) const OVERRIDE; + + private: + union { + bool boolean_value_; + int integer_value_; + double double_value_; + }; + + DISALLOW_COPY_AND_ASSIGN(FundamentalValue); +}; + +class BASE_EXPORT StringValue : public Value { + public: + // Initializes a StringValue with a UTF-8 narrow character string. + explicit StringValue(const std::string& in_value); + + // Initializes a StringValue with a string16. + explicit StringValue(const string16& in_value); + + virtual ~StringValue(); + + // Overridden from Value: + virtual bool GetAsString(std::string* out_value) const OVERRIDE; + virtual bool GetAsString(string16* out_value) const OVERRIDE; + virtual StringValue* DeepCopy() const OVERRIDE; + virtual bool Equals(const Value* other) const OVERRIDE; + + private: + std::string value_; + + DISALLOW_COPY_AND_ASSIGN(StringValue); +}; + +class BASE_EXPORT BinaryValue: public Value { + public: + virtual ~BinaryValue(); + + // Creates a Value to represent a binary buffer. The new object takes + // ownership of the pointer passed in, if successful. + // Returns NULL if buffer is NULL. + static BinaryValue* Create(char* buffer, size_t size); + + // For situations where you want to keep ownership of your buffer, this + // factory method creates a new BinaryValue by copying the contents of the + // buffer that's passed in. + // Returns NULL if buffer is NULL. + static BinaryValue* CreateWithCopiedBuffer(const char* buffer, size_t size); + + size_t GetSize() const { return size_; } + char* GetBuffer() { return buffer_; } + const char* GetBuffer() const { return buffer_; } + + // Overridden from Value: + virtual BinaryValue* DeepCopy() const OVERRIDE; + virtual bool Equals(const Value* other) const OVERRIDE; + + private: + // Constructor is private so that only objects with valid buffer pointers + // and size values can be created. + BinaryValue(char* buffer, size_t size); + + char* buffer_; + size_t size_; + + DISALLOW_COPY_AND_ASSIGN(BinaryValue); +}; + +// DictionaryValue provides a key-value dictionary with (optional) "path" +// parsing for recursive access; see the comment at the top of the file. Keys +// are |std::string|s and should be UTF-8 encoded. +class BASE_EXPORT DictionaryValue : public Value { + public: + DictionaryValue(); + virtual ~DictionaryValue(); + + // Returns true if the current dictionary has a value for the given key. + bool HasKey(const std::string& key) const; + + // Returns the number of Values in this dictionary. + size_t size() const { return dictionary_.size(); } + + // Returns whether the dictionary is empty. + bool empty() const { return dictionary_.empty(); } + + // Clears any current contents of this dictionary. + void Clear(); + + // Sets the Value associated with the given path starting from this object. + // A path has the form "" or "..[...]", where "." indexes + // into the next DictionaryValue down. Obviously, "." can't be used + // within a key, but there are no other restrictions on keys. + // If the key at any step of the way doesn't exist, or exists but isn't + // a DictionaryValue, a new DictionaryValue will be created and attached + // to the path in that location. + // Note that the dictionary takes ownership of the value referenced by + // |in_value|, and therefore |in_value| must be non-NULL. + void Set(const std::string& path, Value* in_value); + + // Convenience forms of Set(). These methods will replace any existing + // value at that path, even if it has a different type. + void SetBoolean(const std::string& path, bool in_value); + void SetInteger(const std::string& path, int in_value); + void SetDouble(const std::string& path, double in_value); + void SetString(const std::string& path, const std::string& in_value); + void SetString(const std::string& path, const string16& in_value); + + // Like Set(), but without special treatment of '.'. This allows e.g. URLs to + // be used as paths. + void SetWithoutPathExpansion(const std::string& key, Value* in_value); + + // Gets the Value associated with the given path starting from this object. + // A path has the form "" or "..[...]", where "." indexes + // into the next DictionaryValue down. If the path can be resolved + // successfully, the value for the last key in the path will be returned + // through the |out_value| parameter, and the function will return true. + // Otherwise, it will return false and |out_value| will be untouched. + // Note that the dictionary always owns the value that's returned. + bool Get(const std::string& path, Value** out_value) const; + + // These are convenience forms of Get(). The value will be retrieved + // and the return value will be true if the path is valid and the value at + // the end of the path can be returned in the form specified. + bool GetBoolean(const std::string& path, bool* out_value) const; + bool GetInteger(const std::string& path, int* out_value) const; + bool GetDouble(const std::string& path, double* out_value) const; + bool GetString(const std::string& path, std::string* out_value) const; + bool GetString(const std::string& path, string16* out_value) const; + bool GetStringASCII(const std::string& path, std::string* out_value) const; + bool GetBinary(const std::string& path, BinaryValue** out_value) const; + bool GetDictionary(const std::string& path, + DictionaryValue** out_value) const; + bool GetList(const std::string& path, ListValue** out_value) const; + + // Like Get(), but without special treatment of '.'. This allows e.g. URLs to + // be used as paths. + bool GetWithoutPathExpansion(const std::string& key, + Value** out_value) const; + bool GetIntegerWithoutPathExpansion(const std::string& key, + int* out_value) const; + bool GetDoubleWithoutPathExpansion(const std::string& key, + double* out_value) const; + bool GetStringWithoutPathExpansion(const std::string& key, + std::string* out_value) const; + bool GetStringWithoutPathExpansion(const std::string& key, + string16* out_value) const; + bool GetDictionaryWithoutPathExpansion(const std::string& key, + DictionaryValue** out_value) const; + bool GetListWithoutPathExpansion(const std::string& key, + ListValue** out_value) const; + + // Removes the Value with the specified path from this dictionary (or one + // of its child dictionaries, if the path is more than just a local key). + // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be + // passed out via out_value. If |out_value| is NULL, the removed value will + // be deleted. This method returns true if |path| is a valid path; otherwise + // it will return false and the DictionaryValue object will be unchanged. + bool Remove(const std::string& path, Value** out_value); + + // Like Remove(), but without special treatment of '.'. This allows e.g. URLs + // to be used as paths. + bool RemoveWithoutPathExpansion(const std::string& key, Value** out_value); + + // Makes a copy of |this| but doesn't include empty dictionaries and lists in + // the copy. This never returns NULL, even if |this| itself is empty. + DictionaryValue* DeepCopyWithoutEmptyChildren(); + + // Merge a given dictionary into this dictionary. This is done recursively, + // i.e. any subdictionaries will be merged as well. In case of key collisions, + // the passed in dictionary takes precedence and data already present will be + // replaced. + void MergeDictionary(const DictionaryValue* dictionary); + + // Swaps contents with the |other| dictionary. + void Swap(DictionaryValue* other) { + dictionary_.swap(other->dictionary_); + } + + // This class provides an iterator for the keys in the dictionary. + // It can't be used to modify the dictionary. + // + // YOU SHOULD ALWAYS USE THE XXXWithoutPathExpansion() APIs WITH THESE, NOT + // THE NORMAL XXX() APIs. This makes sure things will work correctly if any + // keys have '.'s in them. + class key_iterator + : private std::iterator { + public: + explicit key_iterator(ValueMap::const_iterator itr) { itr_ = itr; } + key_iterator operator++() { + ++itr_; + return *this; + } + const std::string& operator*() { return itr_->first; } + bool operator!=(const key_iterator& other) { return itr_ != other.itr_; } + bool operator==(const key_iterator& other) { return itr_ == other.itr_; } + + private: + ValueMap::const_iterator itr_; + }; + + key_iterator begin_keys() const { return key_iterator(dictionary_.begin()); } + key_iterator end_keys() const { return key_iterator(dictionary_.end()); } + + // Overridden from Value: + virtual DictionaryValue* DeepCopy() const OVERRIDE; + virtual bool Equals(const Value* other) const OVERRIDE; + + private: + ValueMap dictionary_; + + DISALLOW_COPY_AND_ASSIGN(DictionaryValue); +}; + +// This type of Value represents a list of other Value values. +class BASE_EXPORT ListValue : public Value { + public: + typedef ValueVector::iterator iterator; + typedef ValueVector::const_iterator const_iterator; + + ListValue(); + virtual ~ListValue(); + + // Clears the contents of this ListValue + void Clear(); + + // Returns the number of Values in this list. + size_t GetSize() const { return list_.size(); } + + // Returns whether the list is empty. + bool empty() const { return list_.empty(); } + + // Sets the list item at the given index to be the Value specified by + // the value given. If the index beyond the current end of the list, null + // Values will be used to pad out the list. + // Returns true if successful, or false if the index was negative or + // the value is a null pointer. + bool Set(size_t index, Value* in_value); + + // Gets the Value at the given index. Modifies |out_value| (and returns true) + // only if the index falls within the current list range. + // Note that the list always owns the Value passed out via |out_value|. + bool Get(size_t index, Value** out_value) const; + + // Convenience forms of Get(). Modifies |out_value| (and returns true) + // only if the index is valid and the Value at that index can be returned + // in the specified form. + bool GetBoolean(size_t index, bool* out_value) const; + bool GetInteger(size_t index, int* out_value) const; + bool GetDouble(size_t index, double* out_value) const; + bool GetString(size_t index, std::string* out_value) const; + bool GetString(size_t index, string16* out_value) const; + bool GetBinary(size_t index, BinaryValue** out_value) const; + bool GetDictionary(size_t index, DictionaryValue** out_value) const; + bool GetList(size_t index, ListValue** out_value) const; + + // Removes the Value with the specified index from this list. + // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be + // passed out via |out_value|. If |out_value| is NULL, the removed value will + // be deleted. This method returns true if |index| is valid; otherwise + // it will return false and the ListValue object will be unchanged. + bool Remove(size_t index, Value** out_value); + + // Removes the first instance of |value| found in the list, if any, and + // deletes it. |index| is the location where |value| was found. Returns false + // if not found. + bool Remove(const Value& value, size_t* index); + + // Appends a Value to the end of the list. + void Append(Value* in_value); + + // Appends a Value if it's not already present. Takes ownership of the + // |in_value|. Returns true if successful, or false if the value was already + // present. If the value was already present the |in_value| is deleted. + bool AppendIfNotPresent(Value* in_value); + + // Insert a Value at index. + // Returns true if successful, or false if the index was out of range. + bool Insert(size_t index, Value* in_value); + + // Swaps contents with the |other| list. + void Swap(ListValue* other) { + list_.swap(other->list_); + } + + // Iteration. + iterator begin() { return list_.begin(); } + iterator end() { return list_.end(); } + + const_iterator begin() const { return list_.begin(); } + const_iterator end() const { return list_.end(); } + + // Overridden from Value: + virtual bool GetAsList(ListValue** out_value) OVERRIDE; + virtual bool GetAsList(const ListValue** out_value) const OVERRIDE; + virtual ListValue* DeepCopy() const OVERRIDE; + virtual bool Equals(const Value* other) const OVERRIDE; + + private: + ValueVector list_; + + DISALLOW_COPY_AND_ASSIGN(ListValue); +}; + +// This interface is implemented by classes that know how to serialize and +// deserialize Value objects. +class BASE_EXPORT ValueSerializer { + public: + virtual ~ValueSerializer(); + + virtual bool Serialize(const Value& root) = 0; + + // This method deserializes the subclass-specific format into a Value object. + // If the return value is non-NULL, the caller takes ownership of returned + // Value. If the return value is NULL, and if error_code is non-NULL, + // error_code will be set with the underlying error. + // If |error_message| is non-null, it will be filled in with a formatted + // error message including the location of the error if appropriate. + virtual Value* Deserialize(int* error_code, std::string* error_str) = 0; +}; + +} // namespace base + +// http://crbug.com/88666 +using base::DictionaryValue; +using base::ListValue; +using base::StringValue; +using base::Value; + +#endif // BASE_VALUES_H_ diff --git a/build/build_config.h b/build/build_config.h new file mode 100644 index 0000000..ce5ac6a --- /dev/null +++ b/build/build_config.h @@ -0,0 +1,151 @@ +// 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. + +// This file adds defines about the platform we're currently building on. +// Operating System: +// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) +// Compiler: +// COMPILER_MSVC / COMPILER_GCC +// Processor: +// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64) +// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS + +#ifndef BUILD_BUILD_CONFIG_H_ +#define BUILD_BUILD_CONFIG_H_ + +// A set of macros to use for platform detection. +#if defined(__APPLE__) +#define OS_MACOSX 1 +#elif defined(ANDROID) +#define OS_ANDROID 1 +#elif defined(__native_client__) +#define OS_NACL 1 +#elif defined(__linux__) +#define OS_LINUX 1 +// Use TOOLKIT_GTK on linux if TOOLKIT_VIEWS isn't defined. +#if !defined(TOOLKIT_VIEWS) +#define TOOLKIT_GTK +#endif +#elif defined(_WIN32) +#define OS_WIN 1 +#define TOOLKIT_VIEWS 1 +#elif defined(__FreeBSD__) +#define OS_FREEBSD 1 +#define TOOLKIT_GTK +#elif defined(__OpenBSD__) +#define OS_OPENBSD 1 +#define TOOLKIT_GTK +#elif defined(__sun) +#define OS_SOLARIS 1 +#define TOOLKIT_GTK +#else +#error Please add support for your platform in build/build_config.h +#endif + +// A flag derived from the above flags, used to cover GTK code in +// both TOOLKIT_GTK and TOOLKIT_VIEWS. +#if defined(TOOLKIT_GTK) || (defined(TOOLKIT_VIEWS) && !defined(OS_WIN)) +#define TOOLKIT_USES_GTK 1 +#endif + +#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_OPENBSD) || \ + defined(OS_SOLARIS) || defined(OS_ANDROID) +#if !defined(USE_OPENSSL) +#define USE_NSS 1 // Default to use NSS for crypto, unless OpenSSL is chosen. +#endif +#ifndef OS_ANDROID +#define USE_X11 1 // Use X for graphics. +#endif +#endif + +#if defined(USE_OPENSSL) && defined(USE_NSS) +#error Cannot use both OpenSSL and NSS +#endif + +// For access to standard POSIXish features, use OS_POSIX instead of a +// more specific macro. +#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_FREEBSD) || \ + defined(OS_OPENBSD) || defined(OS_SOLARIS) || defined(OS_ANDROID) || \ + defined(OS_NACL) +#define OS_POSIX 1 +#endif + +// Use tcmalloc +#if (defined(OS_WIN) || defined(OS_LINUX)) && !defined(NO_TCMALLOC) +#define USE_TCMALLOC 1 +#endif + +// Use heapchecker. +#if defined(OS_LINUX) && !defined(NO_HEAPCHECKER) +#define USE_HEAPCHECKER 1 +#endif + +// Compiler detection. +#if defined(__GNUC__) +#define COMPILER_GCC 1 +#elif defined(_MSC_VER) +#define COMPILER_MSVC 1 +#else +#error Please add support for your compiler in build/build_config.h +#endif + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86_64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(_M_IX86) || defined(__i386__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARMEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#define WCHAR_T_IS_UNSIGNED 1 +#else +#error Please add support for your architecture in build/build_config.h +#endif + +// Type detection for wchar_t. +#if defined(OS_WIN) +#define WCHAR_T_IS_UTF16 +#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \ + defined(__WCHAR_MAX__) && \ + (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) +#define WCHAR_T_IS_UTF32 +#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \ + defined(__WCHAR_MAX__) && \ + (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff) +// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to +// compile in this mode (in particular, Chrome doesn't). This is intended for +// other projects using base who manage their own dependencies and make sure +// short wchar works for them. +#define WCHAR_T_IS_UTF16 +#else +#error Please add support for your compiler in build/build_config.h +#endif + +#if defined(OS_CHROMEOS) +// Single define to trigger whether CrOS fonts have BCI on. +// In that case font sizes/deltas should be adjusted. +//define CROS_FONTS_USING_BCI +#endif + +#if defined(OS_ANDROID) +// The compiler thinks std::string::const_iterator and "const char*" are +// equivalent types. +#define STD_STRING_ITERATOR_IS_CHAR_POINTER +// The compiler thinks base::string16::const_iterator and "char16*" are +// equivalent types. +#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER +#endif + +#endif // BUILD_BUILD_CONFIG_H_ diff --git a/chromium.manifest b/chromium.manifest new file mode 100644 index 0000000..ae3e6f7 --- /dev/null +++ b/chromium.manifest @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/chromium.pc.in b/chromium.pc.in new file mode 100755 index 0000000..b6609e0 --- /dev/null +++ b/chromium.pc.in @@ -0,0 +1,14 @@ +# Package Information for pkg-config + +prefix=@PREFIX@ +exec_prefix=/usr +libdir=/usr/lib +includedir=/usr/include/chromium + +Name: @PC_NAME@ +Description: @PACKAGE_DESCRIPTION@ +Version: @VERSION@ +Requires: @PC_REQUIRED@ +Libs: -L${libdir} @PC_LDFLAGS@ +Cflags: -I${includedir} + diff --git a/ipc/OWNERS b/ipc/OWNERS new file mode 100644 index 0000000..fc187f8 --- /dev/null +++ b/ipc/OWNERS @@ -0,0 +1,7 @@ +agl@chromium.org +cpu@chromium.org +darin@chromium.org +dmaclach@chromium.org +jam@chromium.org +jeremy@chromium.org +tsepez@chromium.org diff --git a/ipc/file_descriptor_set_posix.cc b/ipc/file_descriptor_set_posix.cc new file mode 100644 index 0000000..7f17322 --- /dev/null +++ b/ipc/file_descriptor_set_posix.cc @@ -0,0 +1,136 @@ +// 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 "ipc/file_descriptor_set_posix.h" + +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/logging.h" + +FileDescriptorSet::FileDescriptorSet() + : consumed_descriptor_highwater_(0) { +} + +FileDescriptorSet::~FileDescriptorSet() { + if (consumed_descriptor_highwater_ == descriptors_.size()) + return; + + LOG(WARNING) << "FileDescriptorSet destroyed with unconsumed descriptors"; + // We close all the descriptors where the close flag is set. If this + // message should have been transmitted, then closing those with close + // flags set mirrors the expected behaviour. + // + // If this message was received with more descriptors than expected + // (which could a DOS against the browser by a rogue renderer) then all + // the descriptors have their close flag set and we free all the extra + // kernel resources. + for (unsigned i = consumed_descriptor_highwater_; + i < descriptors_.size(); ++i) { + if (descriptors_[i].auto_close) + if (HANDLE_EINTR(close(descriptors_[i].fd)) < 0) + PLOG(ERROR) << "close"; + } +} + +bool FileDescriptorSet::Add(int fd) { + if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) + return false; + + struct base::FileDescriptor sd; + sd.fd = fd; + sd.auto_close = false; + descriptors_.push_back(sd); + return true; +} + +bool FileDescriptorSet::AddAndAutoClose(int fd) { + if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) + return false; + + struct base::FileDescriptor sd; + sd.fd = fd; + sd.auto_close = true; + descriptors_.push_back(sd); + DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE); + return true; +} + +int FileDescriptorSet::GetDescriptorAt(unsigned index) const { + if (index >= descriptors_.size()) + return -1; + + // We should always walk the descriptors in order, so it's reasonable to + // enforce this. Consider the case where a compromised renderer sends us + // the following message: + // + // ExampleMsg: + // num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m} + // + // Here the renderer sent us a message which should have a descriptor, but + // actually sent two in an attempt to fill our fd table and kill us. By + // setting the index of the descriptor in the message to 1 (it should be + // 0), we would record a highwater of 1 and then consider all the + // descriptors to have been used. + // + // So we can either track of the use of each descriptor in a bitset, or we + // can enforce that we walk the indexes strictly in order. + // + // There's one more wrinkle: When logging messages, we may reparse them. So + // we have an exception: When the consumed_descriptor_highwater_ is at the + // end of the array and index 0 is requested, we reset the highwater value. + if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size()) + consumed_descriptor_highwater_ = 0; + + if (index != consumed_descriptor_highwater_) + return -1; + + consumed_descriptor_highwater_ = index + 1; + return descriptors_[index].fd; +} + +void FileDescriptorSet::GetDescriptors(int* buffer) const { + for (std::vector::const_iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + *(buffer++) = i->fd; + } +} + +bool FileDescriptorSet::ContainsDirectoryDescriptor() const { + struct stat st; + + for (std::vector::const_iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + if (fstat(i->fd, &st) == 0 && S_ISDIR(st.st_mode)) + return true; + } + + return false; +} + +void FileDescriptorSet::CommitAll() { + for (std::vector::iterator + i = descriptors_.begin(); i != descriptors_.end(); ++i) { + if (i->auto_close) + if (HANDLE_EINTR(close(i->fd)) < 0) + PLOG(ERROR) << "close"; + } + descriptors_.clear(); + consumed_descriptor_highwater_ = 0; +} + +void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) { + DCHECK_LE(count, MAX_DESCRIPTORS_PER_MESSAGE); + DCHECK_EQ(descriptors_.size(), 0u); + DCHECK_EQ(consumed_descriptor_highwater_, 0u); + + descriptors_.reserve(count); + for (unsigned i = 0; i < count; ++i) { + struct base::FileDescriptor sd; + sd.fd = buffer[i]; + sd.auto_close = true; + descriptors_.push_back(sd); + } +} diff --git a/ipc/file_descriptor_set_posix.h b/ipc/file_descriptor_set_posix.h new file mode 100644 index 0000000..1554c38 --- /dev/null +++ b/ipc/file_descriptor_set_posix.h @@ -0,0 +1,117 @@ +// 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. + +#ifndef IPC_FILE_DESCRIPTOR_SET_POSIX_H_ +#define IPC_FILE_DESCRIPTOR_SET_POSIX_H_ +#pragma once + +#include + +#include "base/basictypes.h" +#include "base/file_descriptor_posix.h" +#include "base/memory/ref_counted.h" +#include "ipc/ipc_export.h" + +// ----------------------------------------------------------------------------- +// A FileDescriptorSet is an ordered set of POSIX file descriptors. These are +// associated with IPC messages so that descriptors can be transmitted over a +// UNIX domain socket. +// ----------------------------------------------------------------------------- +class IPC_EXPORT FileDescriptorSet + : public base::RefCountedThreadSafe { + public: + FileDescriptorSet(); + + // This is the maximum number of descriptors per message. We need to know this + // because the control message kernel interface has to be given a buffer which + // is large enough to store all the descriptor numbers. Otherwise the kernel + // tells us that it truncated the control data and the extra descriptors are + // lost. + // + // In debugging mode, it's a fatal error to try and add more than this number + // of descriptors to a FileDescriptorSet. + enum { + MAX_DESCRIPTORS_PER_MESSAGE = 5, + }; + + // --------------------------------------------------------------------------- + // Interfaces for building during message serialisation... + + // Add a descriptor to the end of the set. Returns false iff the set is full. + bool Add(int fd); + // Add a descriptor to the end of the set and automatically close it after + // transmission. Returns false iff the set is full. + bool AddAndAutoClose(int fd); + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for accessing during message deserialisation... + + // Return the number of descriptors + unsigned size() const { return descriptors_.size(); } + // Return true if no unconsumed descriptors remain + bool empty() const { return descriptors_.empty(); } + // Fetch the nth descriptor from the beginning of the set. Code using this + // /must/ access the descriptors in order, except that it may wrap from the + // end to index 0 again. + // + // This interface is designed for the deserialising code as it doesn't + // support close flags. + // returns: file descriptor, or -1 on error + int GetDescriptorAt(unsigned n) const; + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for transmission... + + // Fill an array with file descriptors without 'consuming' them. CommitAll + // must be called after these descriptors have been transmitted. + // buffer: (output) a buffer of, at least, size() integers. + void GetDescriptors(int* buffer) const; + // This must be called after transmitting the descriptors returned by + // GetDescriptors. It marks all the descriptors as consumed and closes those + // which are auto-close. + void CommitAll(); + // Returns true if any contained file descriptors appear to be handles to a + // directory. + bool ContainsDirectoryDescriptor() const; + + // --------------------------------------------------------------------------- + + + // --------------------------------------------------------------------------- + // Interfaces for receiving... + + // Set the contents of the set from the given buffer. This set must be empty + // before calling. The auto-close flag is set on all the descriptors so that + // unconsumed descriptors are closed on destruction. + void SetDescriptors(const int* buffer, unsigned count); + + // --------------------------------------------------------------------------- + + private: + friend class base::RefCountedThreadSafe; + + ~FileDescriptorSet(); + + // A vector of descriptors and close flags. If this message is sent, then + // these descriptors are sent as control data. After sending, any descriptors + // with a true flag are closed. If this message has been received, then these + // are the descriptors which were received and all close flags are true. + std::vector descriptors_; + + // This contains the index of the next descriptor which should be consumed. + // It's used in a couple of ways. Firstly, at destruction we can check that + // all the descriptors have been read (with GetNthDescriptor). Secondly, we + // can check that they are read in order. + mutable unsigned consumed_descriptor_highwater_; + + DISALLOW_COPY_AND_ASSIGN(FileDescriptorSet); +}; + +#endif // IPC_FILE_DESCRIPTOR_SET_POSIX_H_ diff --git a/ipc/file_descriptor_set_posix_unittest.cc b/ipc/file_descriptor_set_posix_unittest.cc new file mode 100644 index 0000000..5fc8c50 --- /dev/null +++ b/ipc/file_descriptor_set_posix_unittest.cc @@ -0,0 +1,179 @@ +// 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. + +// This test is POSIX only. + +#include "ipc/file_descriptor_set_posix.h" + +#include +#include + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Get a safe file descriptor for test purposes. +int GetSafeFd() { + return open("/dev/null", O_RDONLY); +} + +// Returns true if fd was already closed. Closes fd if not closed. +bool VerifyClosed(int fd) { + const int duped = dup(fd); + if (duped != -1) { + EXPECT_NE(HANDLE_EINTR(close(duped)), -1); + EXPECT_NE(HANDLE_EINTR(close(fd)), -1); + return false; + } + return true; +} + +// The FileDescriptorSet will try and close some of the descriptor numbers +// which we given it. This is the base descriptor value. It's great enough such +// that no real descriptor will accidently be closed. +static const int kFDBase = 50000; + +TEST(FileDescriptorSet, BasicAdd) { + scoped_refptr set(new FileDescriptorSet); + + ASSERT_EQ(set->size(), 0u); + ASSERT_TRUE(set->empty()); + ASSERT_TRUE(set->Add(kFDBase)); + ASSERT_EQ(set->size(), 1u); + ASSERT_TRUE(!set->empty()); + + // Empties the set and stops a warning about deleting a set with unconsumed + // descriptors + set->CommitAll(); +} + +TEST(FileDescriptorSet, BasicAddAndClose) { + scoped_refptr set(new FileDescriptorSet); + + ASSERT_EQ(set->size(), 0u); + ASSERT_TRUE(set->empty()); + const int fd = GetSafeFd(); + ASSERT_TRUE(set->AddAndAutoClose(fd)); + ASSERT_EQ(set->size(), 1u); + ASSERT_TRUE(!set->empty()); + + set->CommitAll(); + + ASSERT_TRUE(VerifyClosed(fd)); +} +TEST(FileDescriptorSet, MaxSize) { + scoped_refptr set(new FileDescriptorSet); + + for (unsigned i = 0; + i < FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE; ++i) { + ASSERT_TRUE(set->Add(kFDBase + 1 + i)); + } + + ASSERT_TRUE(!set->Add(kFDBase)); + + set->CommitAll(); +} + +TEST(FileDescriptorSet, SetDescriptors) { + scoped_refptr set(new FileDescriptorSet); + + ASSERT_TRUE(set->empty()); + set->SetDescriptors(NULL, 0); + ASSERT_TRUE(set->empty()); + + const int fd = GetSafeFd(); + static const int fds[] = {fd}; + set->SetDescriptors(fds, 1); + ASSERT_TRUE(!set->empty()); + ASSERT_EQ(set->size(), 1u); + + set->CommitAll(); + + ASSERT_TRUE(VerifyClosed(fd)); +} + +TEST(FileDescriptorSet, GetDescriptors) { + scoped_refptr set(new FileDescriptorSet); + + set->GetDescriptors(NULL); + ASSERT_TRUE(set->Add(kFDBase)); + + int fds[1]; + fds[0] = 0; + set->GetDescriptors(fds); + ASSERT_EQ(fds[0], kFDBase); + set->CommitAll(); + ASSERT_TRUE(set->empty()); +} + +TEST(FileDescriptorSet, WalkInOrder) { + scoped_refptr set(new FileDescriptorSet); + + ASSERT_TRUE(set->Add(kFDBase)); + ASSERT_TRUE(set->Add(kFDBase + 1)); + ASSERT_TRUE(set->Add(kFDBase + 2)); + + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1); + ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2); + + set->CommitAll(); +} + +TEST(FileDescriptorSet, WalkWrongOrder) { + scoped_refptr set(new FileDescriptorSet); + + ASSERT_TRUE(set->Add(kFDBase)); + ASSERT_TRUE(set->Add(kFDBase + 1)); + ASSERT_TRUE(set->Add(kFDBase + 2)); + + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(2), -1); + + set->CommitAll(); +} + +TEST(FileDescriptorSet, WalkCycle) { + scoped_refptr set(new FileDescriptorSet); + + ASSERT_TRUE(set->Add(kFDBase)); + ASSERT_TRUE(set->Add(kFDBase + 1)); + ASSERT_TRUE(set->Add(kFDBase + 2)); + + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1); + ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2); + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1); + ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2); + ASSERT_EQ(set->GetDescriptorAt(0), kFDBase); + ASSERT_EQ(set->GetDescriptorAt(1), kFDBase + 1); + ASSERT_EQ(set->GetDescriptorAt(2), kFDBase + 2); + + set->CommitAll(); +} + +TEST(FileDescriptorSet, DontClose) { + scoped_refptr set(new FileDescriptorSet); + + const int fd = GetSafeFd(); + ASSERT_TRUE(set->Add(fd)); + set->CommitAll(); + + ASSERT_FALSE(VerifyClosed(fd)); +} + +TEST(FileDescriptorSet, DoClose) { + scoped_refptr set(new FileDescriptorSet); + + const int fd = GetSafeFd(); + ASSERT_TRUE(set->AddAndAutoClose(fd)); + set->CommitAll(); + + ASSERT_TRUE(VerifyClosed(fd)); +} + +} // namespace diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp new file mode 100644 index 0000000..ab57cd9 --- /dev/null +++ b/ipc/ipc.gyp @@ -0,0 +1,90 @@ +# 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. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'target_defaults': { + 'sources/': [ + ['exclude', '/win/'], + ['exclude', '_(posix|win)(_unittest)?\\.(cc|mm?)$'], + ['exclude', '/win_[^/]*\\.cc$'], + ], + 'conditions': [ + ['os_posix == 1 and OS != "mac"', {'sources/': [ + ['include', '_posix(_unittest)?\\.cc$'], + ]}], + ['OS=="mac"', {'sources/': [ + ['include', '_posix(_unittest)?\\.(cc|mm?)$'], + ]}], + ['OS=="win"', {'sources/': [ + ['include', '_win(_unittest)?\\.cc$'], + ['include', '/win/'], + ['include', '/win_[^/]*\\.cc$'], + ]}], + ], + }, + 'includes': [ + 'ipc.gypi', + ], + 'targets': [ + { + 'target_name': 'ipc_tests', + 'type': 'executable', + 'dependencies': [ + 'ipc', + '../base/base.gyp:base', + '../base/base.gyp:base_i18n', + '../base/base.gyp:test_support_base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../testing/gtest.gyp:gtest', + ], + 'include_dirs': [ + '..' + ], + 'sources': [ + 'file_descriptor_set_posix_unittest.cc', + 'ipc_channel_posix_unittest.cc', + 'ipc_fuzzing_tests.cc', + 'ipc_message_unittest.cc', + 'ipc_send_fds_test.cc', + 'ipc_sync_channel_unittest.cc', + 'ipc_sync_message_unittest.cc', + 'ipc_sync_message_unittest.h', + 'ipc_tests.cc', + 'ipc_tests.h', + 'sync_socket_unittest.cc', + ], + 'conditions': [ + ['toolkit_uses_gtk == 1', { + 'dependencies': [ + '../build/linux/system.gyp:gtk', + ], + }], + ['os_posix == 1 and OS != "mac"', { + 'conditions': [ + ['linux_use_tcmalloc==1', { + 'dependencies': [ + '../base/allocator/allocator.gyp:allocator', + ], + }], + ], + }] + ], + }, + { + 'target_name': 'test_support_ipc', + 'type': 'static_library', + 'dependencies': [ + 'ipc', + '../base/base.gyp:base', + ], + 'sources': [ + 'ipc_test_sink.cc', + 'ipc_test_sink.h', + ], + }, + ], +} diff --git a/ipc/ipc.gypi b/ipc/ipc.gypi new file mode 100644 index 0000000..7791934 --- /dev/null +++ b/ipc/ipc.gypi @@ -0,0 +1,109 @@ +# 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. + +{ + 'target_defaults': { + 'variables': { + 'ipc_target': 0, + }, + 'target_conditions': [ + # This part is shared between the targets defined below. + ['ipc_target==1', { + 'sources': [ + 'file_descriptor_set_posix.cc', + 'file_descriptor_set_posix.h', + 'ipc_channel.h', + 'ipc_channel_handle.h', + 'ipc_channel_posix.cc', + 'ipc_channel_posix.h', + 'ipc_channel_proxy.cc', + 'ipc_channel_proxy.h', + 'ipc_channel_win.cc', + 'ipc_channel_win.h', + 'ipc_descriptors.h', + 'ipc_export.h', + 'ipc_logging.cc', + 'ipc_logging.h', + 'ipc_message.cc', + 'ipc_message.h', + 'ipc_message_macros.h', + 'ipc_message_utils.cc', + 'ipc_message_utils.h', + 'ipc_param_traits.h', + 'ipc_platform_file.cc', + 'ipc_platform_file.h', + 'ipc_switches.cc', + 'ipc_switches.h', + 'ipc_sync_channel.cc', + 'ipc_sync_channel.h', + 'ipc_sync_message.cc', + 'ipc_sync_message.h', + 'ipc_sync_message_filter.cc', + 'ipc_sync_message_filter.h', + 'param_traits_log_macros.h', + 'param_traits_macros.h', + 'param_traits_read_macros.h', + 'param_traits_write_macros.h', + 'struct_constructor_macros.h', + 'struct_destructor_macros.h', + ], + 'defines': [ + 'IPC_IMPLEMENTATION', + ], + 'include_dirs': [ + '..', + ], + }], + ], + }, + 'targets': [ + { + 'target_name': 'ipc', + 'type': '<(component)', + 'variables': { + 'ipc_target': 1, + }, + 'dependencies': [ + '../base/base.gyp:base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + ], + # TODO(gregoryd): direct_dependent_settings should be shared with the + # 64-bit target, but it doesn't work due to a bug in gyp + 'direct_dependent_settings': { + 'include_dirs': [ + '..', + ], + }, + }, + ], + 'conditions': [ + ['OS=="win"', { + 'targets': [ + { + 'target_name': 'ipc_win64', + 'type': '<(component)', + 'variables': { + 'ipc_target': 1, + }, + 'dependencies': [ + '../base/base.gyp:base_nacl_win64', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64', + ], + # TODO(gregoryd): direct_dependent_settings should be shared with the + # 32-bit target, but it doesn't work due to a bug in gyp + 'direct_dependent_settings': { + 'include_dirs': [ + '..', + ], + }, + 'configurations': { + 'Common_Base': { + 'msvs_target_platform': 'x64', + }, + }, + }, + ], + }], + ], +} diff --git a/ipc/ipc_channel.h b/ipc/ipc_channel.h new file mode 100644 index 0000000..cd57b4b --- /dev/null +++ b/ipc/ipc_channel.h @@ -0,0 +1,204 @@ +// 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. + +#ifndef IPC_IPC_CHANNEL_H_ +#define IPC_IPC_CHANNEL_H_ +#pragma once + +#include "base/compiler_specific.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_message.h" + +namespace IPC { + +//------------------------------------------------------------------------------ +// See +// http://www.chromium.org/developers/design-documents/inter-process-communication +// for overview of IPC in Chromium. + +// Channels are implemented using named pipes on Windows, and +// socket pairs (or in some special cases unix domain sockets) on POSIX. +// On Windows we access pipes in various processes by name. +// On POSIX we pass file descriptors to child processes and assign names to them +// in a lookup table. +// In general on POSIX we do not use unix domain sockets due to security +// concerns and the fact that they can leave garbage around the file system +// (MacOS does not support abstract named unix domain sockets). +// You can use unix domain sockets if you like on POSIX by constructing the +// the channel with the mode set to one of the NAMED modes. NAMED modes are +// currently used by automation and service processes. + +class IPC_EXPORT Channel : public Message::Sender { + // Security tests need access to the pipe handle. + friend class ChannelTest; + + public: + // Implemented by consumers of a Channel to receive messages. + class IPC_EXPORT Listener { + public: + virtual ~Listener() {} + + // Called when a message is received. Returns true iff the message was + // handled. + virtual bool OnMessageReceived(const Message& message) = 0; + + // Called when the channel is connected and we have received the internal + // Hello message from the peer. + virtual void OnChannelConnected(int32 peer_pid) {} + + // Called when an error is detected that causes the channel to close. + // This method is not called when a channel is closed normally. + virtual void OnChannelError() {} + +#if defined(OS_POSIX) + // Called on the server side when a channel that listens for connections + // denies an attempt to connect. + virtual void OnChannelDenied() {} + + // Called on the server side when a channel that listens for connections + // has an error that causes the listening channel to close. + virtual void OnChannelListenError() {} +#endif // OS_POSIX + }; + + // Flags to test modes + enum ModeFlags { + MODE_NO_FLAG = 0x0, + MODE_SERVER_FLAG = 0x1, + MODE_CLIENT_FLAG = 0x2, + MODE_NAMED_FLAG = 0x4, +#if defined(OS_POSIX) + MODE_OPEN_ACCESS_FLAG = 0x8, // Don't restrict access based on client UID. +#endif + }; + + // Some Standard Modes + enum Mode { + MODE_NONE = MODE_NO_FLAG, + MODE_SERVER = MODE_SERVER_FLAG, + MODE_CLIENT = MODE_CLIENT_FLAG, + // Channels on Windows are named by default and accessible from other + // processes. On POSIX channels are anonymous by default and not accessible + // from other processes. Named channels work via named unix domain sockets. + // On Windows MODE_NAMED_SERVER is equivalent to MODE_SERVER and + // MODE_NAMED_CLIENT is equivalent to MODE_CLIENT. + MODE_NAMED_SERVER = MODE_SERVER_FLAG | MODE_NAMED_FLAG, + MODE_NAMED_CLIENT = MODE_CLIENT_FLAG | MODE_NAMED_FLAG, +#if defined(OS_POSIX) + // An "open" named server accepts connections from ANY client. + // The caller must then implement their own access-control based on the + // client process' user Id. + MODE_OPEN_NAMED_SERVER = MODE_OPEN_ACCESS_FLAG | MODE_SERVER_FLAG | + MODE_NAMED_FLAG +#endif + }; + + enum { + // The maximum message size in bytes. Attempting to receive a + // message of this size or bigger results in a channel error. + kMaximumMessageSize = 128 * 1024 * 1024, + + // Ammount of data to read at once from the pipe. + kReadBufferSize = 4 * 1024 + }; + + // Initialize a Channel. + // + // |channel_handle| identifies the communication Channel. For POSIX, if + // the file descriptor in the channel handle is != -1, the channel takes + // ownership of the file descriptor and will close it appropriately, otherwise + // it will create a new descriptor internally. + // |mode| specifies whether this Channel is to operate in server mode or + // client mode. In server mode, the Channel is responsible for setting up the + // IPC object, whereas in client mode, the Channel merely connects to the + // already established IPC object. + // |listener| receives a callback on the current thread for each newly + // received message. + // + Channel(const IPC::ChannelHandle &channel_handle, Mode mode, + Listener* listener); + + virtual ~Channel(); + + // Connect the pipe. On the server side, this will initiate + // waiting for connections. On the client, it attempts to + // connect to a pre-existing pipe. Note, calling Connect() + // will not block the calling thread and may complete + // asynchronously. + bool Connect() WARN_UNUSED_RESULT; + + // Close this Channel explicitly. May be called multiple times. + // On POSIX calling close on an IPC channel that listens for connections will + // cause it to close any accepted connections, and it will stop listening for + // new connections. If you just want to close the currently accepted + // connection and listen for new ones, use ResetToAcceptingConnectionState. + void Close(); + + // Modify the Channel's listener. + void set_listener(Listener* listener); + + // Send a message over the Channel to the listener on the other end. + // + // |message| must be allocated using operator new. This object will be + // deleted once the contents of the Message have been sent. + virtual bool Send(Message* message); + +#if defined(OS_POSIX) && !defined(OS_NACL) + // On POSIX an IPC::Channel wraps a socketpair(), this method returns the + // FD # for the client end of the socket. + // This method may only be called on the server side of a channel. + int GetClientFileDescriptor() const; + + // On POSIX an IPC::Channel can either wrap an established socket, or it + // can wrap a socket that is listening for connections. Currently an + // IPC::Channel that listens for connections can only accept one connection + // at a time. + + // Returns true if the channel supports listening for connections. + bool AcceptsConnections() const; + + // Returns true if the channel supports listening for connections and is + // currently connected. + bool HasAcceptedConnection() const; + + // Returns true if the peer process' effective user id can be determined, in + // which case the supplied client_euid is updated with it. + bool GetClientEuid(uid_t* client_euid) const; + + // Closes any currently connected socket, and returns to a listening state + // for more connections. + void ResetToAcceptingConnectionState(); +#endif // defined(OS_POSIX) && !defined(OS_NACL) + + // Returns true if a named server channel is initialized on the given channel + // ID. Even if true, the server may have already accepted a connection. + static bool IsNamedServerInitialized(const std::string& channel_id); + + protected: + // Used in Chrome by the TestSink to provide a dummy channel implementation + // for testing. TestSink overrides the "interesting" functions in Channel so + // no actual implementation is needed. This will cause un-overridden calls to + // segfault. Do not use outside of test code! + Channel() : channel_impl_(0) { } + + private: + // PIMPL to which all channel calls are delegated. + class ChannelImpl; + ChannelImpl *channel_impl_; + + // The Hello message is internal to the Channel class. It is sent + // by the peer when the channel is connected. The message contains + // just the process id (pid). The message has a special routing_id + // (MSG_ROUTING_NONE) and type (HELLO_MESSAGE_TYPE). + enum { + HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type (uint16), + // to avoid conflicting with normal + // message types, which are enumeration + // constants starting from 0. + }; +}; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_H_ diff --git a/ipc/ipc_channel_handle.h b/ipc/ipc_channel_handle.h new file mode 100644 index 0000000..ba034cc --- /dev/null +++ b/ipc/ipc_channel_handle.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef IPC_IPC_CHANNEL_HANDLE_H_ +#define IPC_IPC_CHANNEL_HANDLE_H_ +#pragma once + +#include + +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif + +// On Windows, any process can create an IPC channel and others can fetch +// it by name. We pass around the channel names over IPC. +// On POSIX, we instead pass around handles to channel endpoints via IPC. +// When it's time to IPC a new channel endpoint around, we send both the +// channel name as well as a base::FileDescriptor, which is itself a special +// type that knows how to copy a socket endpoint over IPC. +// +// In sum, when passing a handle to a channel over IPC, use this data structure +// to work on both Windows and POSIX. + +namespace IPC { + +struct ChannelHandle { + // Note that serialization for this object is defined in the ParamTraits + // template specialization in ipc_message_utils.h. + ChannelHandle() {} + // The name that is passed in should be an absolute path for Posix. + // Otherwise there may be a problem in IPC communication between + // processes with different working directories. + ChannelHandle(const std::string& n) : name(n) {} + ChannelHandle(const char* n) : name(n) {} +#if defined(OS_POSIX) + ChannelHandle(const std::string& n, const base::FileDescriptor& s) + : name(n), socket(s) {} +#endif // defined(OS_POSIX) + + std::string name; +#if defined(OS_POSIX) + base::FileDescriptor socket; +#endif // defined(OS_POSIX) + +}; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_HANDLE_H_ diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc new file mode 100644 index 0000000..c358d35 --- /dev/null +++ b/ipc/ipc_channel_posix.cc @@ -0,0 +1,1217 @@ +// 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. + +#include "ipc/ipc_channel_posix.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/global_descriptors_posix.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/process_util.h" +#include "base/stl_util.h" +#include "base/string_util.h" +#include "base/synchronization/lock.h" +#include "ipc/ipc_descriptors.h" +#include "ipc/ipc_switches.h" +#include "ipc/file_descriptor_set_posix.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_message_utils.h" + +namespace IPC { + +// IPC channels on Windows use named pipes (CreateNamedPipe()) with +// channel ids as the pipe names. Channels on POSIX use sockets as +// pipes These don't quite line up. +// +// When creating a child subprocess we use a socket pair and the parent side of +// the fork arranges it such that the initial control channel ends up on the +// magic file descriptor kPrimaryIPCChannel in the child. Future +// connections (file descriptors) can then be passed via that +// connection via sendmsg(). +// +// A POSIX IPC channel can also be set up as a server for a bound UNIX domain +// socket, and will handle multiple connect and disconnect sequences. Currently +// it is limited to one connection at a time. + +//------------------------------------------------------------------------------ +namespace { + +// The PipeMap class works around this quirk related to unit tests: +// +// When running as a server, we install the client socket in a +// specific file descriptor number (@kPrimaryIPCChannel). However, we +// also have to support the case where we are running unittests in the +// same process. (We do not support forking without execing.) +// +// Case 1: normal running +// The IPC server object will install a mapping in PipeMap from the +// name which it was given to the client pipe. When forking the client, the +// GetClientFileDescriptorMapping will ensure that the socket is installed in +// the magic slot (@kPrimaryIPCChannel). The client will search for the +// mapping, but it won't find any since we are in a new process. Thus the +// magic fd number is returned. Once the client connects, the server will +// close its copy of the client socket and remove the mapping. +// +// Case 2: unittests - client and server in the same process +// The IPC server will install a mapping as before. The client will search +// for a mapping and find out. It duplicates the file descriptor and +// connects. Once the client connects, the server will close the original +// copy of the client socket and remove the mapping. Thus, when the client +// object closes, it will close the only remaining copy of the client socket +// in the fd table and the server will see EOF on its side. +// +// TODO(port): a client process cannot connect to multiple IPC channels with +// this scheme. + +class PipeMap { + public: + static PipeMap* GetInstance() { + return Singleton::get(); + } + + ~PipeMap() { + // Shouldn't have left over pipes. + DCHECK(map_.empty()); + } + + // Lookup a given channel id. Return -1 if not found. + int Lookup(const std::string& channel_id) { + base::AutoLock locked(lock_); + + ChannelToFDMap::const_iterator i = map_.find(channel_id); + if (i == map_.end()) + return -1; + return i->second; + } + + // Remove the mapping for the given channel id. No error is signaled if the + // channel_id doesn't exist + void RemoveAndClose(const std::string& channel_id) { + base::AutoLock locked(lock_); + + ChannelToFDMap::iterator i = map_.find(channel_id); + if (i != map_.end()) { + if (HANDLE_EINTR(close(i->second)) < 0) + PLOG(ERROR) << "close " << channel_id; + map_.erase(i); + } + } + + // Insert a mapping from @channel_id to @fd. It's a fatal error to insert a + // mapping if one already exists for the given channel_id + void Insert(const std::string& channel_id, int fd) { + base::AutoLock locked(lock_); + DCHECK_NE(-1, fd); + + ChannelToFDMap::const_iterator i = map_.find(channel_id); + CHECK(i == map_.end()) << "Creating second IPC server (fd " << fd << ") " + << "for '" << channel_id << "' while first " + << "(fd " << i->second << ") still exists"; + map_[channel_id] = fd; + } + + private: + base::Lock lock_; + typedef std::map ChannelToFDMap; + ChannelToFDMap map_; + + friend struct DefaultSingletonTraits; +}; + +//------------------------------------------------------------------------------ +// Verify that kMaxPipeNameLength is a decent size. +COMPILE_ASSERT(sizeof(((sockaddr_un*)0)->sun_path) >= kMaxPipeNameLength, + BAD_SUN_PATH_LENGTH); + +// Creates a unix domain socket bound to the specified name that is listening +// for connections. +bool CreateServerUnixDomainSocket(const std::string& pipe_name, + int* server_listen_fd) { + DCHECK(server_listen_fd); + DCHECK_GT(pipe_name.length(), 0u); + DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); + + if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { + return false; + } + + // Create socket. + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + return false; + } + + // Make socket non-blocking + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Delete any old FS instances. + unlink(pipe_name.c_str()); + + // Make sure the path we need exists. + FilePath path(pipe_name); + FilePath dir_path = path.DirName(); + if (!file_util::CreateDirectory(dir_path)) { + return false; + } + + // Create unix_addr structure. + struct sockaddr_un unix_addr; + memset(&unix_addr, 0, sizeof(unix_addr)); + unix_addr.sun_family = AF_UNIX; + int path_len = snprintf(unix_addr.sun_path, IPC::kMaxPipeNameLength, + "%s", pipe_name.c_str()); + DCHECK_EQ(static_cast(pipe_name.length()), path_len); + size_t unix_addr_len = offsetof(struct sockaddr_un, + sun_path) + path_len + 1; + + // Bind the socket. + if (bind(fd, reinterpret_cast(&unix_addr), + unix_addr_len) != 0) { + PLOG(ERROR) << "bind " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Start listening on the socket. + const int listen_queue_length = 1; + if (listen(fd, listen_queue_length) != 0) { + PLOG(ERROR) << "listen " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + *server_listen_fd = fd; + return true; +} + +// Accept a connection on a socket we are listening to. +bool ServerAcceptConnection(int server_listen_fd, int* server_socket) { + DCHECK(server_socket); + + int accept_fd = HANDLE_EINTR(accept(server_listen_fd, NULL, 0)); + if (accept_fd < 0) + return false; + if (fcntl(accept_fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << accept_fd; + if (HANDLE_EINTR(close(accept_fd)) < 0) + PLOG(ERROR) << "close " << accept_fd; + return false; + } + + *server_socket = accept_fd; + return true; +} + +bool CreateClientUnixDomainSocket(const std::string& pipe_name, + int* client_socket) { + DCHECK(client_socket); + DCHECK_GT(pipe_name.length(), 0u); + DCHECK_LT(pipe_name.length(), kMaxPipeNameLength); + + if (pipe_name.length() == 0 || pipe_name.length() >= kMaxPipeNameLength) { + return false; + } + + // Create socket. + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + PLOG(ERROR) << "socket " << pipe_name; + return false; + } + + // Make socket non-blocking + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK) " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + // Create server side of socket. + struct sockaddr_un server_unix_addr; + memset(&server_unix_addr, 0, sizeof(server_unix_addr)); + server_unix_addr.sun_family = AF_UNIX; + int path_len = snprintf(server_unix_addr.sun_path, IPC::kMaxPipeNameLength, + "%s", pipe_name.c_str()); + DCHECK_EQ(static_cast(pipe_name.length()), path_len); + size_t server_unix_addr_len = offsetof(struct sockaddr_un, + sun_path) + path_len + 1; + + if (HANDLE_EINTR(connect(fd, reinterpret_cast(&server_unix_addr), + server_unix_addr_len)) != 0) { + PLOG(ERROR) << "connect " << pipe_name; + if (HANDLE_EINTR(close(fd)) < 0) + PLOG(ERROR) << "close " << pipe_name; + return false; + } + + *client_socket = fd; + return true; +} + +bool SocketWriteErrorIsRecoverable() { +#if defined(OS_MACOSX) + // On OS X if sendmsg() is trying to send fds between processes and there + // isn't enough room in the output buffer to send the fd structure over + // atomically then EMSGSIZE is returned. + // + // EMSGSIZE presents a problem since the system APIs can only call us when + // there's room in the socket buffer and not when there is "enough" room. + // + // The current behavior is to return to the event loop when EMSGSIZE is + // received and hopefull service another FD. This is however still + // technically a busy wait since the event loop will call us right back until + // the receiver has read enough data to allow passing the FD over atomically. + return errno == EAGAIN || errno == EMSGSIZE; +#else + return errno == EAGAIN; +#endif // OS_MACOSX +} + +} // namespace +//------------------------------------------------------------------------------ + +Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle, + Mode mode, Listener* listener) + : mode_(mode), + is_blocked_on_write_(false), + waiting_connect_(true), + message_send_bytes_written_(0), + server_listen_pipe_(-1), + pipe_(-1), + client_pipe_(-1), +#if defined(IPC_USES_READWRITE) + fd_pipe_(-1), + remote_fd_pipe_(-1), +#endif // IPC_USES_READWRITE + pipe_name_(channel_handle.name), + listener_(listener), + must_unlink_(false) { + memset(input_buf_, 0, sizeof(input_buf_)); + memset(input_cmsg_buf_, 0, sizeof(input_cmsg_buf_)); + if (!CreatePipe(channel_handle)) { + // The pipe may have been closed already. + const char *modestr = (mode_ & MODE_SERVER_FLAG) ? "server" : "client"; + // The pipe may have been closed already. + LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name + << "\" in " << modestr << " mode"; + } +} + +Channel::ChannelImpl::~ChannelImpl() { + Close(); +} + +bool SocketPair(int* fd1, int* fd2) { + int pipe_fds[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) { + PLOG(ERROR) << "socketpair()"; + return false; + } + + // Set both ends to be non-blocking. + if (fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == -1 || + fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == -1) { + PLOG(ERROR) << "fcntl(O_NONBLOCK)"; + if (HANDLE_EINTR(close(pipe_fds[0])) < 0) + PLOG(ERROR) << "close"; + if (HANDLE_EINTR(close(pipe_fds[1])) < 0) + PLOG(ERROR) << "close"; + return false; + } + + *fd1 = pipe_fds[0]; + *fd2 = pipe_fds[1]; + + return true; +} + +bool Channel::ChannelImpl::CreatePipe( + const IPC::ChannelHandle& channel_handle) { + DCHECK(server_listen_pipe_ == -1 && pipe_ == -1); + + // Four possible cases: + // 1) It's a channel wrapping a pipe that is given to us. + // 2) It's for a named channel, so we create it. + // 3) It's for a client that we implement ourself. This is used + // in unittesting. + // 4) It's the initial IPC channel: + // 4a) Client side: Pull the pipe out of the GlobalDescriptors set. + // 4b) Server side: create the pipe. + + int local_pipe = -1; + if (channel_handle.socket.fd != -1) { + // Case 1 from comment above. + local_pipe = channel_handle.socket.fd; +#if defined(IPC_USES_READWRITE) + // Test the socket passed into us to make sure it is nonblocking. + // We don't want to call read/write on a blocking socket. + int value = fcntl(local_pipe, F_GETFL); + if (value == -1) { + PLOG(ERROR) << "fcntl(F_GETFL) " << pipe_name_; + return false; + } + if (!(value & O_NONBLOCK)) { + LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK"; + return false; + } +#endif // IPC_USES_READWRITE + } else if (mode_ & MODE_NAMED_FLAG) { + // Case 2 from comment above. + if (mode_ & MODE_SERVER_FLAG) { + if (!CreateServerUnixDomainSocket(pipe_name_, &local_pipe)) { + return false; + } + must_unlink_ = true; + } else if (mode_ & MODE_CLIENT_FLAG) { + if (!CreateClientUnixDomainSocket(pipe_name_, &local_pipe)) { + return false; + } + } else { + LOG(ERROR) << "Bad mode: " << mode_; + return false; + } + } else { + local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_); + if (mode_ & MODE_CLIENT_FLAG) { + if (local_pipe != -1) { + // Case 3 from comment above. + // We only allow one connection. + local_pipe = HANDLE_EINTR(dup(local_pipe)); + PipeMap::GetInstance()->RemoveAndClose(pipe_name_); + } else { + // Case 4a from comment above. + // Guard against inappropriate reuse of the initial IPC channel. If + // an IPC channel closes and someone attempts to reuse it by name, the + // initial channel must not be recycled here. http://crbug.com/26754. + static bool used_initial_channel = false; + if (used_initial_channel) { + LOG(FATAL) << "Denying attempt to reuse initial IPC channel for " + << pipe_name_; + return false; + } + used_initial_channel = true; + + local_pipe = + base::GlobalDescriptors::GetInstance()->Get(kPrimaryIPCChannel); + } + } else if (mode_ & MODE_SERVER_FLAG) { + // Case 4b from comment above. + if (local_pipe != -1) { + LOG(ERROR) << "Server already exists for " << pipe_name_; + return false; + } + if (!SocketPair(&local_pipe, &client_pipe_)) + return false; + PipeMap::GetInstance()->Insert(pipe_name_, client_pipe_); + } else { + LOG(ERROR) << "Bad mode: " << mode_; + return false; + } + } + +#if defined(IPC_USES_READWRITE) + // Create a dedicated socketpair() for exchanging file descriptors. + // See comments for IPC_USES_READWRITE for details. + if (mode_ & MODE_CLIENT_FLAG) { + if (!SocketPair(&fd_pipe_, &remote_fd_pipe_)) { + return false; + } + } +#endif // IPC_USES_READWRITE + + if ((mode_ & MODE_SERVER_FLAG) && (mode_ & MODE_NAMED_FLAG)) { + server_listen_pipe_ = local_pipe; + local_pipe = -1; + } + + pipe_ = local_pipe; + return true; +} + +bool Channel::ChannelImpl::Connect() { + if (server_listen_pipe_ == -1 && pipe_ == -1) { + DLOG(INFO) << "Channel creation failed: " << pipe_name_; + return false; + } + + bool did_connect = true; + if (server_listen_pipe_ != -1) { + // Watch the pipe for connections, and turn any connections into + // active sockets. + MessageLoopForIO::current()->WatchFileDescriptor( + server_listen_pipe_, + true, + MessageLoopForIO::WATCH_READ, + &server_listen_connection_watcher_, + this); + } else { + did_connect = AcceptConnection(); + } + return did_connect; +} + +bool Channel::ChannelImpl::ProcessIncomingMessages() { + ssize_t bytes_read = 0; + + struct msghdr msg = {0}; + struct iovec iov = {input_buf_, Channel::kReadBufferSize}; + + msg.msg_iovlen = 1; + msg.msg_control = input_cmsg_buf_; + + for (;;) { + msg.msg_iov = &iov; + + if (bytes_read == 0) { + if (pipe_ == -1) + return false; + + // Read from pipe. + // recvmsg() returns 0 if the connection has closed or EAGAIN if no data + // is waiting on the pipe. +#if defined(IPC_USES_READWRITE) + if (fd_pipe_ >= 0) { + bytes_read = HANDLE_EINTR(read(pipe_, input_buf_, + Channel::kReadBufferSize)); + msg.msg_controllen = 0; + } else +#endif // IPC_USES_READWRITE + { + msg.msg_controllen = sizeof(input_cmsg_buf_); + bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT)); + } + if (bytes_read < 0) { + if (errno == EAGAIN) { + return true; +#if defined(OS_MACOSX) + } else if (errno == EPERM) { + // On OSX, reading from a pipe with no listener returns EPERM + // treat this as a special case to prevent spurious error messages + // to the console. + return false; +#endif // OS_MACOSX + } else if (errno == ECONNRESET || errno == EPIPE) { + return false; + } else { + PLOG(ERROR) << "pipe error (" << pipe_ << ")"; + return false; + } + } else if (bytes_read == 0) { + // The pipe has closed... + return false; + } + } + DCHECK(bytes_read); + + if (client_pipe_ != -1) { + PipeMap::GetInstance()->RemoveAndClose(pipe_name_); + client_pipe_ = -1; + } + + // a pointer to an array of |num_wire_fds| file descriptors from the read + const int* wire_fds = NULL; + unsigned num_wire_fds = 0; + + // walk the list of control messages and, if we find an array of file + // descriptors, save a pointer to the array + + // This next if statement is to work around an OSX issue where + // CMSG_FIRSTHDR will return non-NULL in the case that controllen == 0. + // Here's a test case: + // + // int main() { + // struct msghdr msg; + // msg.msg_control = &msg; + // msg.msg_controllen = 0; + // if (CMSG_FIRSTHDR(&msg)) + // printf("Bug found!\n"); + // } + if (msg.msg_controllen > 0) { + // On OSX, CMSG_FIRSTHDR doesn't handle the case where controllen is 0 + // and will return a pointer into nowhere. + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); + DCHECK_EQ(0U, payload_len % sizeof(int)); + wire_fds = reinterpret_cast(CMSG_DATA(cmsg)); + num_wire_fds = payload_len / 4; + + if (msg.msg_flags & MSG_CTRUNC) { + LOG(ERROR) << "SCM_RIGHTS message was truncated" + << " cmsg_len:" << cmsg->cmsg_len + << " fd:" << pipe_; + for (unsigned i = 0; i < num_wire_fds; ++i) + if (HANDLE_EINTR(close(wire_fds[i])) < 0) + PLOG(ERROR) << "close " << i; + return false; + } + break; + } + } + } + + // Process messages from input buffer. + const char *p; + const char *end; + if (input_overflow_buf_.empty()) { + p = input_buf_; + end = p + bytes_read; + } else { + if (input_overflow_buf_.size() > + static_cast(kMaximumMessageSize - bytes_read)) { + input_overflow_buf_.clear(); + LOG(ERROR) << "IPC message is too big"; + return false; + } + input_overflow_buf_.append(input_buf_, bytes_read); + p = input_overflow_buf_.data(); + end = p + input_overflow_buf_.size(); + } + + // A pointer to an array of |num_fds| file descriptors which includes any + // fds that have spilled over from a previous read. + const int* fds = NULL; + unsigned num_fds = 0; + unsigned fds_i = 0; // the index of the first unused descriptor + + if (input_overflow_fds_.empty()) { + fds = wire_fds; + num_fds = num_wire_fds; + } else { + if (num_wire_fds > 0) { + const size_t prev_size = input_overflow_fds_.size(); + input_overflow_fds_.resize(prev_size + num_wire_fds); + memcpy(&input_overflow_fds_[prev_size], wire_fds, + num_wire_fds * sizeof(int)); + } + fds = &input_overflow_fds_[0]; + num_fds = input_overflow_fds_.size(); + } + + while (p < end) { + const char* message_tail = Message::FindNext(p, end); + if (message_tail) { + int len = static_cast(message_tail - p); + Message m(p, len); + const uint16 header_fds = m.header()->num_fds; + if (header_fds) { + // the message has file descriptors + const char* error = NULL; + if (header_fds > num_fds - fds_i) { + // the message has been completely received, but we didn't get + // enough file descriptors. +#if defined(IPC_USES_READWRITE) + char dummy; + struct iovec fd_pipe_iov = { &dummy, 1 }; + msg.msg_iov = &fd_pipe_iov; + msg.msg_controllen = sizeof(input_cmsg_buf_); + ssize_t n = HANDLE_EINTR(recvmsg(fd_pipe_, &msg, MSG_DONTWAIT)); + if (n == 1 && msg.msg_controllen > 0) { + for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0); + DCHECK_EQ(0U, payload_len % sizeof(int)); + wire_fds = reinterpret_cast(CMSG_DATA(cmsg)); + num_wire_fds = payload_len / 4; + + if (msg.msg_flags & MSG_CTRUNC) { + LOG(ERROR) << "SCM_RIGHTS message was truncated" + << " cmsg_len:" << cmsg->cmsg_len + << " fd:" << pipe_; + for (unsigned i = 0; i < num_wire_fds; ++i) + if (HANDLE_EINTR(close(wire_fds[i])) < 0) + PLOG(ERROR) << "close " << i; + return false; + } + break; + } + } + if (input_overflow_fds_.empty()) { + fds = wire_fds; + num_fds = num_wire_fds; + } else { + if (num_wire_fds > 0) { + const size_t prev_size = input_overflow_fds_.size(); + input_overflow_fds_.resize(prev_size + num_wire_fds); + memcpy(&input_overflow_fds_[prev_size], wire_fds, + num_wire_fds * sizeof(int)); + } + fds = &input_overflow_fds_[0]; + num_fds = input_overflow_fds_.size(); + } + } + if (header_fds > num_fds - fds_i) +#endif // IPC_USES_READWRITE + error = "Message needs unreceived descriptors"; + } + + if (header_fds > + FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) { + // There are too many descriptors in this message + error = "Message requires an excessive number of descriptors"; + } + + if (error) { + LOG(WARNING) << error + << " channel:" << this + << " message-type:" << m.type() + << " header()->num_fds:" << header_fds + << " num_fds:" << num_fds + << " fds_i:" << fds_i; +#if defined(CHROMIUM_SELINUX) + LOG(WARNING) << "In the case of SELinux this can be caused when " + "using a --user-data-dir to which the default " + "policy doesn't give the renderer access to. "; +#endif // CHROMIUM_SELINUX + // close the existing file descriptors so that we don't leak them + for (unsigned i = fds_i; i < num_fds; ++i) + if (HANDLE_EINTR(close(fds[i])) < 0) + PLOG(ERROR) << "close " << i; + input_overflow_fds_.clear(); + // abort the connection + return false; + } + + m.file_descriptor_set()->SetDescriptors( + &fds[fds_i], header_fds); + fds_i += header_fds; + } + DVLOG(2) << "received message on channel @" << this + << " with type " << m.type() << " on fd " << pipe_; + if (IsHelloMessage(&m)) { + // The Hello message contains only the process id. + void *iter = NULL; + int pid; + if (!m.ReadInt(&iter, &pid)) { + NOTREACHED(); + } +#if defined(IPC_USES_READWRITE) + if (mode_ & MODE_SERVER_FLAG) { + // With IPC_USES_READWRITE, the Hello message from the client to the + // server also contains the fd_pipe_, which will be used for all + // subsequent file descriptor passing. + DCHECK_EQ(m.file_descriptor_set()->size(), 1U); + base::FileDescriptor descriptor; + if (!m.ReadFileDescriptor(&iter, &descriptor)) { + NOTREACHED(); + } + fd_pipe_ = descriptor.fd; + CHECK(descriptor.auto_close); + } +#endif // IPC_USES_READWRITE + listener_->OnChannelConnected(pid); + } else { + listener_->OnMessageReceived(m); + } + p = message_tail; + } else { + // Last message is partial. + break; + } + input_overflow_fds_ = std::vector(&fds[fds_i], &fds[num_fds]); + fds_i = 0; + fds = vector_as_array(&input_overflow_fds_); + num_fds = input_overflow_fds_.size(); + } + input_overflow_buf_.assign(p, end - p); + input_overflow_fds_ = std::vector(&fds[fds_i], &fds[num_fds]); + + // When the input data buffer is empty, the overflow fds should be too. If + // this is not the case, we probably have a rogue renderer which is trying + // to fill our descriptor table. + if (input_overflow_buf_.empty() && !input_overflow_fds_.empty()) { + // We close these descriptors in Close() + return false; + } + + bytes_read = 0; // Get more data. + } +} + +bool Channel::ChannelImpl::ProcessOutgoingMessages() { + DCHECK(!waiting_connect_); // Why are we trying to send messages if there's + // no connection? + if (output_queue_.empty()) + return true; + + if (pipe_ == -1) + return false; + + // Write out all the messages we can till the write blocks or there are no + // more outgoing messages. + while (!output_queue_.empty()) { + Message* msg = output_queue_.front(); + + size_t amt_to_write = msg->size() - message_send_bytes_written_; + DCHECK_NE(0U, amt_to_write); + const char* out_bytes = reinterpret_cast(msg->data()) + + message_send_bytes_written_; + + struct msghdr msgh = {0}; + struct iovec iov = {const_cast(out_bytes), amt_to_write}; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + char buf[CMSG_SPACE( + sizeof(int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]))]; + + ssize_t bytes_written = 1; + int fd_written = -1; +/* + if (message_send_bytes_written_ == 0 && + !msg->file_descriptor_set()->empty()) { + // This is the first chunk of a message which has descriptors to send + struct cmsghdr *cmsg; + const unsigned num_fds = msg->file_descriptor_set()->size(); + + DCHECK_LE(num_fds, FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE); + if (msg->file_descriptor_set()->ContainsDirectoryDescriptor()) { + LOG(FATAL) << "Panic: attempting to transport directory descriptor over" + " IPC. Aborting to maintain sandbox isolation."; + // If you have hit this then something tried to send a file descriptor + // to a directory over an IPC channel. Since IPC channels span + // sandboxes this is very bad: the receiving process can use openat + // with ".." elements in the path in order to reach the real + // filesystem. + } + + msgh.msg_control = buf; + msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds); + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds); + msg->file_descriptor_set()->GetDescriptors( + reinterpret_cast(CMSG_DATA(cmsg))); + msgh.msg_controllen = cmsg->cmsg_len; + + // DCHECK_LE above already checks that + // num_fds < MAX_DESCRIPTORS_PER_MESSAGE so no danger of overflow. + msg->header()->num_fds = static_cast(num_fds); + +#if defined(IPC_USES_READWRITE) + if (!IsHelloMessage(msg)) { + // Only the Hello message sends the file descriptor with the message. + // Subsequently, we can send file descriptors on the dedicated + // fd_pipe_ which makes Seccomp sandbox operation more efficient. + struct iovec fd_pipe_iov = { const_cast(""), 1 }; + msgh.msg_iov = &fd_pipe_iov; + fd_written = fd_pipe_; + bytes_written = HANDLE_EINTR(sendmsg(fd_pipe_, &msgh, MSG_DONTWAIT)); + msgh.msg_iov = &iov; + msgh.msg_controllen = 0; + if (bytes_written > 0) { + msg->file_descriptor_set()->CommitAll(); + } + } +#endif // IPC_USES_READWRITE + } + +*/ + if (bytes_written == 1) { + fd_written = pipe_; +#if defined(IPC_USES_READWRITE) + if ((mode_ & MODE_CLIENT_FLAG) && IsHelloMessage(msg)) { + DCHECK_EQ(msg->file_descriptor_set()->size(), 1U); + } + if (!msgh.msg_controllen) { + bytes_written = HANDLE_EINTR(write(pipe_, out_bytes, amt_to_write)); + } else +#endif // IPC_USES_READWRITE + { + bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT)); + } + } + if (bytes_written > 0) + msg->file_descriptor_set()->CommitAll(); + + if (bytes_written < 0 && !SocketWriteErrorIsRecoverable()) { +#if defined(OS_MACOSX) + // On OSX writing to a pipe with no listener returns EPERM. + if (errno == EPERM) { + Close(); + return false; + } +#endif // OS_MACOSX + if (errno == EPIPE) { + Close(); + return false; + } + PLOG(ERROR) << "pipe error on " + << fd_written + << " Currently writing message of size: " + << msg->size(); + return false; + } + + if (static_cast(bytes_written) != amt_to_write) { + if (bytes_written > 0) { + // If write() fails with EAGAIN then bytes_written will be -1. + message_send_bytes_written_ += bytes_written; + } + + // Tell libevent to call us back once things are unblocked. + is_blocked_on_write_ = true; + MessageLoopForIO::current()->WatchFileDescriptor( + pipe_, + false, // One shot + MessageLoopForIO::WATCH_WRITE, + &write_watcher_, + this); + return true; + } else { + message_send_bytes_written_ = 0; + + // Message sent OK! + DVLOG(2) << "sent message @" << msg << " on channel @" << this + << " with type " << msg->type() << " on fd " << pipe_; + delete output_queue_.front(); + output_queue_.pop(); + } + } + return true; +} + +bool Channel::ChannelImpl::Send(Message* message) { + DVLOG(2) << "sending message @" << message << " on channel @" << this + << " with type " << message->type() + << " (" << output_queue_.size() << " in queue)"; + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::GetInstance()->OnSendMessage(message, ""); +#endif // IPC_MESSAGE_LOG_ENABLED + + output_queue_.push(message); + if (!is_blocked_on_write_ && !waiting_connect_) { + return ProcessOutgoingMessages(); + } + + return true; +} + +int Channel::ChannelImpl::GetClientFileDescriptor() const { + return client_pipe_; +} + +bool Channel::ChannelImpl::AcceptsConnections() const { + return server_listen_pipe_ != -1; +} + +bool Channel::ChannelImpl::HasAcceptedConnection() const { + return AcceptsConnections() && pipe_ != -1; +} + +bool Channel::ChannelImpl::GetClientEuid(uid_t* client_euid) const { + DCHECK(HasAcceptedConnection()); +#if defined(OS_MACOSX) + uid_t peer_euid; + gid_t peer_gid; + if (getpeereid(pipe_, &peer_euid, &peer_gid) != 0) { + PLOG(ERROR) << "getpeereid " << pipe_; + return false; + } + *client_euid = peer_euid; + return true; +#elif defined(OS_SOLARIS) + return false; +#else + struct ucred cred; + socklen_t cred_len = sizeof(cred); + if (getsockopt(pipe_, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len) != 0) { + PLOG(ERROR) << "getsockopt " << pipe_; + return false; + } + if (cred_len < sizeof(cred)) { + NOTREACHED() << "Truncated ucred from SO_PEERCRED?"; + return false; + } + *client_euid = cred.uid; + return true; +#endif +} + +void Channel::ChannelImpl::ResetToAcceptingConnectionState() { + // Unregister libevent for the unix domain socket and close it. + read_watcher_.StopWatchingFileDescriptor(); + write_watcher_.StopWatchingFileDescriptor(); + if (pipe_ != -1) { + if (HANDLE_EINTR(close(pipe_)) < 0) + PLOG(ERROR) << "close pipe_ " << pipe_name_; + pipe_ = -1; + } +#if defined(IPC_USES_READWRITE) + if (fd_pipe_ != -1) { + if (HANDLE_EINTR(close(fd_pipe_)) < 0) + PLOG(ERROR) << "close fd_pipe_ " << pipe_name_; + fd_pipe_ = -1; + } + if (remote_fd_pipe_ != -1) { + if (HANDLE_EINTR(close(remote_fd_pipe_)) < 0) + PLOG(ERROR) << "close remote_fd_pipe_ " << pipe_name_; + remote_fd_pipe_ = -1; + } +#endif // IPC_USES_READWRITE + + while (!output_queue_.empty()) { + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; + } + + // Close any outstanding, received file descriptors. + for (std::vector::iterator + i = input_overflow_fds_.begin(); i != input_overflow_fds_.end(); ++i) { + if (HANDLE_EINTR(close(*i)) < 0) + PLOG(ERROR) << "close"; + } + input_overflow_fds_.clear(); +} + +// static +bool Channel::ChannelImpl::IsNamedServerInitialized( + const std::string& channel_id) { + return file_util::PathExists(FilePath(channel_id)); +} + +// Called by libevent when we can read from the pipe without blocking. +void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) { + bool send_server_hello_msg = false; + if (fd == server_listen_pipe_) { + int new_pipe = 0; + if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe)) { + Close(); + listener_->OnChannelListenError(); + } + + if (pipe_ != -1) { + // We already have a connection. We only handle one at a time. + // close our new descriptor. + if (HANDLE_EINTR(shutdown(new_pipe, SHUT_RDWR)) < 0) + PLOG(ERROR) << "shutdown " << pipe_name_; + if (HANDLE_EINTR(close(new_pipe)) < 0) + PLOG(ERROR) << "close " << pipe_name_; + listener_->OnChannelDenied(); + return; + } + pipe_ = new_pipe; + + if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) { + // Verify that the IPC channel peer is running as the same user. + uid_t client_euid; + if (!GetClientEuid(&client_euid)) { + LOG(ERROR) << "Unable to query client euid"; + ResetToAcceptingConnectionState(); + return; + } + if (client_euid != geteuid()) { + LOG(WARNING) << "Client euid is not authorised"; + ResetToAcceptingConnectionState(); + return; + } + } + + if (!AcceptConnection()) { + NOTREACHED() << "AcceptConnection should not fail on server"; + } + send_server_hello_msg = true; + waiting_connect_ = false; + } else if (fd == pipe_) { + if (waiting_connect_ && (mode_ & MODE_SERVER_FLAG)) { + send_server_hello_msg = true; + waiting_connect_ = false; + } + if (!ProcessIncomingMessages()) { + // ClosePipeOnError may delete this object, so we mustn't call + // ProcessOutgoingMessages. + send_server_hello_msg = false; + ClosePipeOnError(); + } + } else { + NOTREACHED() << "Unknown pipe " << fd; + } + + // If we're a server and handshaking, then we want to make sure that we + // only send our handshake message after we've processed the client's. + // This gives us a chance to kill the client if the incoming handshake + // is invalid. + if (send_server_hello_msg) { + ProcessOutgoingMessages(); + } +} + +// Called by libevent when we can write to the pipe without blocking. +void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) { + DCHECK_EQ(pipe_, fd); + is_blocked_on_write_ = false; + if (!ProcessOutgoingMessages()) { + ClosePipeOnError(); + } +} + +bool Channel::ChannelImpl::AcceptConnection() { + MessageLoopForIO::current()->WatchFileDescriptor(pipe_, + true, + MessageLoopForIO::WATCH_READ, + &read_watcher_, + this); + QueueHelloMessage(); + + if (mode_ & MODE_CLIENT_FLAG) { + // If we are a client we want to send a hello message out immediately. + // In server mode we will send a hello message when we receive one from a + // client. + waiting_connect_ = false; + return ProcessOutgoingMessages(); + } else if (mode_ & MODE_SERVER_FLAG) { + waiting_connect_ = true; + return true; + } else { + NOTREACHED(); + return false; + } +} + +void Channel::ChannelImpl::ClosePipeOnError() { + if (HasAcceptedConnection()) { + ResetToAcceptingConnectionState(); + listener_->OnChannelError(); + } else { + Close(); + if (AcceptsConnections()) { + listener_->OnChannelListenError(); + } else { + listener_->OnChannelError(); + } + } +} + +void Channel::ChannelImpl::QueueHelloMessage() { + // Create the Hello message + scoped_ptr msg(new Message(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE, + IPC::Message::PRIORITY_NORMAL)); + + if (!msg->WriteInt(base::GetCurrentProcId())) { + NOTREACHED() << "Unable to pickle hello message proc id"; + } +#if defined(IPC_USES_READWRITE) + scoped_ptr hello; + if (remote_fd_pipe_ != -1) { + if (!msg->WriteFileDescriptor(base::FileDescriptor(remote_fd_pipe_, + false))) { + NOTREACHED() << "Unable to pickle hello message file descriptors"; + } + DCHECK_EQ(msg->file_descriptor_set()->size(), 1U); + } +#endif // IPC_USES_READWRITE + output_queue_.push(msg.release()); +} + +bool Channel::ChannelImpl::IsHelloMessage(const Message* m) const { + return m->routing_id() == MSG_ROUTING_NONE && m->type() == HELLO_MESSAGE_TYPE; +} + +void Channel::ChannelImpl::Close() { + // Close can be called multiple time, so we need to make sure we're + // idempotent. + + ResetToAcceptingConnectionState(); + + if (must_unlink_) { + unlink(pipe_name_.c_str()); + must_unlink_ = false; + } + if (server_listen_pipe_ != -1) { + if (HANDLE_EINTR(close(server_listen_pipe_)) < 0) + PLOG(ERROR) << "close " << server_listen_pipe_; + server_listen_pipe_ = -1; + // Unregister libevent for the listening socket and close it. + server_listen_connection_watcher_.StopWatchingFileDescriptor(); + } + + if (client_pipe_ != -1) { + PipeMap::GetInstance()->RemoveAndClose(pipe_name_); + client_pipe_ = -1; + } +} + +//------------------------------------------------------------------------------ +// Channel's methods simply call through to ChannelImpl. +Channel::Channel(const IPC::ChannelHandle& channel_handle, Mode mode, + Listener* listener) + : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { +} + +Channel::~Channel() { + delete channel_impl_; +} + +bool Channel::Connect() { + return channel_impl_->Connect(); +} + +void Channel::Close() { + channel_impl_->Close(); +} + +void Channel::set_listener(Listener* listener) { + channel_impl_->set_listener(listener); +} + +bool Channel::Send(Message* message) { + return channel_impl_->Send(message); +} + +int Channel::GetClientFileDescriptor() const { + return channel_impl_->GetClientFileDescriptor(); +} + +bool Channel::AcceptsConnections() const { + return channel_impl_->AcceptsConnections(); +} + +bool Channel::HasAcceptedConnection() const { + return channel_impl_->HasAcceptedConnection(); +} + +bool Channel::GetClientEuid(uid_t* client_euid) const { + return channel_impl_->GetClientEuid(client_euid); +} + +void Channel::ResetToAcceptingConnectionState() { + channel_impl_->ResetToAcceptingConnectionState(); +} + +// static +bool Channel::IsNamedServerInitialized(const std::string& channel_id) { + return ChannelImpl::IsNamedServerInitialized(channel_id); +} + +} // namespace IPC diff --git a/ipc/ipc_channel_posix.h b/ipc/ipc_channel_posix.h new file mode 100644 index 0000000..b66b1fc --- /dev/null +++ b/ipc/ipc_channel_posix.h @@ -0,0 +1,161 @@ +// 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. + +#ifndef IPC_IPC_CHANNEL_POSIX_H_ +#define IPC_IPC_CHANNEL_POSIX_H_ +#pragma once + +#include "ipc/ipc_channel.h" + +#include // for CMSG macros + +#include +#include +#include + +#include "base/message_loop.h" +#include "ipc/file_descriptor_set_posix.h" + +#if !defined(OS_MACOSX) +// On Linux, the seccomp sandbox makes it very expensive to call +// recvmsg() and sendmsg(). The restriction on calling read() and write(), which +// are cheap, is that we can't pass file descriptors over them. +// +// As we cannot anticipate when the sender will provide us with file +// descriptors, we have to make the decision about whether we call read() or +// recvmsg() before we actually make the call. The easiest option is to +// create a dedicated socketpair() for exchanging file descriptors. Any file +// descriptors are split out of a message, with the non-file-descriptor payload +// going over the normal connection, and the file descriptors being sent +// separately over the other channel. When read()ing from a channel, we'll +// notice if the message was supposed to have come with file descriptors and +// use recvmsg on the other socketpair to retrieve them and combine them +// back with the rest of the message. +// +// Mac can also run in IPC_USES_READWRITE mode if necessary, but at this time +// doesn't take a performance hit from recvmsg and sendmsg, so it doesn't +// make sense to waste resources on having the separate dedicated socketpair. +// It is however useful for debugging between Linux and Mac to be able to turn +// this switch 'on' on the Mac as well. +// +// The HELLO message from the client to the server is always sent using +// sendmsg because it will contain the file descriptor that the server +// needs to send file descriptors in later messages. +#define IPC_USES_READWRITE 1 +#endif + +namespace IPC { + +class Channel::ChannelImpl : public MessageLoopForIO::Watcher { + public: + // Mirror methods of Channel, see ipc_channel.h for description. + ChannelImpl(const IPC::ChannelHandle& channel_handle, Mode mode, + Listener* listener); + virtual ~ChannelImpl(); + bool Connect(); + void Close(); + void set_listener(Listener* listener) { listener_ = listener; } + bool Send(Message* message); + int GetClientFileDescriptor() const; + bool AcceptsConnections() const; + bool HasAcceptedConnection() const; + bool GetClientEuid(uid_t* client_euid) const; + void ResetToAcceptingConnectionState(); + static bool IsNamedServerInitialized(const std::string& channel_id); + + private: + bool CreatePipe(const IPC::ChannelHandle& channel_handle); + + bool ProcessIncomingMessages(); + bool ProcessOutgoingMessages(); + + bool AcceptConnection(); + void ClosePipeOnError(); + void QueueHelloMessage(); + bool IsHelloMessage(const Message* m) const; + + // MessageLoopForIO::Watcher implementation. + virtual void OnFileCanReadWithoutBlocking(int fd); + virtual void OnFileCanWriteWithoutBlocking(int fd); + + Mode mode_; + + // After accepting one client connection on our server socket we want to + // stop listening. + MessageLoopForIO::FileDescriptorWatcher server_listen_connection_watcher_; + MessageLoopForIO::FileDescriptorWatcher read_watcher_; + MessageLoopForIO::FileDescriptorWatcher write_watcher_; + + // Indicates whether we're currently blocked waiting for a write to complete. + bool is_blocked_on_write_; + bool waiting_connect_; + + // If sending a message blocks then we use this variable + // to keep track of where we are. + size_t message_send_bytes_written_; + + // File descriptor we're listening on for new connections if we listen + // for connections. + int server_listen_pipe_; + + // The pipe used for communication. + int pipe_; + + // For a server, the client end of our socketpair() -- the other end of our + // pipe_ that is passed to the client. + int client_pipe_; + +#if defined(IPC_USES_READWRITE) + // Linux/BSD use a dedicated socketpair() for passing file descriptors. + int fd_pipe_; + int remote_fd_pipe_; +#endif + + // The "name" of our pipe. On Windows this is the global identifier for + // the pipe. On POSIX it's used as a key in a local map of file descriptors. + std::string pipe_name_; + + Listener* listener_; + + // Messages to be sent are queued here. + std::queue output_queue_; + + // We read from the pipe into this buffer + char input_buf_[Channel::kReadBufferSize]; + + enum { + // We assume a worst case: kReadBufferSize bytes of messages, where each + // message has no payload and a full complement of descriptors. + MAX_READ_FDS = (Channel::kReadBufferSize / sizeof(IPC::Message::Header)) * + FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE, + }; + + // This is a control message buffer large enough to hold kMaxReadFDs +#if defined(OS_MACOSX) + // TODO(agl): OSX appears to have non-constant CMSG macros! + char input_cmsg_buf_[1024]; +#else + char input_cmsg_buf_[CMSG_SPACE(sizeof(int) * MAX_READ_FDS)]; +#endif + + // Large messages that span multiple pipe buffers, get built-up using + // this buffer. + std::string input_overflow_buf_; + std::vector input_overflow_fds_; + + // True if we are responsible for unlinking the unix domain socket file. + bool must_unlink_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ChannelImpl); +}; + +// The maximum length of the name of a pipe for MODE_NAMED_SERVER or +// MODE_NAMED_CLIENT if you want to pass in your own socket. +// The standard size on linux is 108, mac is 104. To maintain consistency +// across platforms we standardize on the smaller value. +static const size_t kMaxPipeNameLength = 104; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_POSIX_H_ diff --git a/ipc/ipc_channel_posix_unittest.cc b/ipc/ipc_channel_posix_unittest.cc new file mode 100644 index 0000000..14ecc60 --- /dev/null +++ b/ipc/ipc_channel_posix_unittest.cc @@ -0,0 +1,389 @@ +// 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. + +// These tests are POSIX only. + +#include "ipc/ipc_channel_posix.h" + +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/test/multiprocess_test.h" +#include "base/test/test_timeouts.h" +#include "testing/multiprocess_func_list.h" + +namespace { + +enum { + QUIT_MESSAGE = 47 +}; + +class IPCChannelPosixTestListener : public IPC::Channel::Listener { + public: + enum STATUS { + DISCONNECTED, + MESSAGE_RECEIVED, + CHANNEL_ERROR, + CONNECTED, + DENIED, + LISTEN_ERROR + }; + + IPCChannelPosixTestListener(bool quit_only_on_message) + : status_(DISCONNECTED), quit_only_on_message_(quit_only_on_message) {} + + virtual ~IPCChannelPosixTestListener() {} + + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { + EXPECT_EQ(message.type(), QUIT_MESSAGE); + status_ = MESSAGE_RECEIVED; + QuitRunLoop(); + return true; + } + + virtual void OnChannelConnected(int32 peer_pid) OVERRIDE { + status_ = CONNECTED; + if (!quit_only_on_message_) { + QuitRunLoop(); + } + } + + virtual void OnChannelError() OVERRIDE { + status_ = CHANNEL_ERROR; + if (!quit_only_on_message_) { + QuitRunLoop(); + } + } + + virtual void OnChannelDenied() OVERRIDE { + status_ = DENIED; + if (!quit_only_on_message_) { + QuitRunLoop(); + } + } + + virtual void OnChannelListenError() OVERRIDE { + status_ = LISTEN_ERROR; + if (!quit_only_on_message_) { + QuitRunLoop(); + } + } + + STATUS status() { return status_; } + + void QuitRunLoop() { + MessageLoopForIO::current()->QuitNow(); + } + + private: + // The current status of the listener. + STATUS status_; + // If |quit_only_on_message_| then the listener will only break out of + // the run loop when the QUIT_MESSAGE is received. + bool quit_only_on_message_; +}; + +} // namespace + +class IPCChannelPosixTest : public base::MultiProcessTest { + public: + static const char kConnectionSocketTestName[]; + static void SetUpSocket(IPC::ChannelHandle *handle, + IPC::Channel::Mode mode); + static void SpinRunLoop(int milliseconds); + + protected: + virtual void SetUp(); + virtual void TearDown(); + +private: + scoped_ptr message_loop_; +}; + +const char IPCChannelPosixTest::kConnectionSocketTestName[] = + "/var/tmp/chrome_IPCChannelPosixTest__ConnectionSocket"; + +void IPCChannelPosixTest::SetUp() { + MultiProcessTest::SetUp(); + // Construct a fresh IO Message loop for the duration of each test. + message_loop_.reset(new MessageLoopForIO()); +} + +void IPCChannelPosixTest::TearDown() { + message_loop_.reset(NULL); + MultiProcessTest::TearDown(); +} + +// Create up a socket and bind and listen to it, or connect it +// depending on the |mode|. +void IPCChannelPosixTest::SetUpSocket(IPC::ChannelHandle *handle, + IPC::Channel::Mode mode) { + const std::string& name = handle->name; + + int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); + ASSERT_GE(socket_fd, 0) << name; + ASSERT_GE(fcntl(socket_fd, F_SETFL, O_NONBLOCK), 0); + struct sockaddr_un server_address = { 0 }; + memset(&server_address, 0, sizeof(server_address)); + server_address.sun_family = AF_UNIX; + int path_len = snprintf(server_address.sun_path, IPC::kMaxPipeNameLength, + "%s", name.c_str()); + DCHECK_EQ(static_cast(name.length()), path_len); + size_t server_address_len = offsetof(struct sockaddr_un, + sun_path) + path_len + 1; + + if (mode == IPC::Channel::MODE_NAMED_SERVER) { + // Only one server at a time. Cleanup garbage if it exists. + unlink(name.c_str()); + // Make sure the path we need exists. + FilePath path(name); + FilePath dir_path = path.DirName(); + ASSERT_TRUE(file_util::CreateDirectory(dir_path)); + ASSERT_GE(bind(socket_fd, + reinterpret_cast(&server_address), + server_address_len), 0) << server_address.sun_path + << ": " << strerror(errno) + << "(" << errno << ")"; + ASSERT_GE(listen(socket_fd, SOMAXCONN), 0) << server_address.sun_path + << ": " << strerror(errno) + << "(" << errno << ")"; + } else if (mode == IPC::Channel::MODE_NAMED_CLIENT) { + ASSERT_GE(connect(socket_fd, + reinterpret_cast(&server_address), + server_address_len), 0) << server_address.sun_path + << ": " << strerror(errno) + << "(" << errno << ")"; + } else { + FAIL() << "Unknown mode " << mode; + } + handle->socket.fd = socket_fd; +} + +void IPCChannelPosixTest::SpinRunLoop(int milliseconds) { + MessageLoopForIO *loop = MessageLoopForIO::current(); + // Post a quit task so that this loop eventually ends and we don't hang + // in the case of a bad test. Usually, the run loop will quit sooner than + // that because all tests use a IPCChannelPosixTestListener which quits the + // current run loop on any channel activity. + loop->PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask(), milliseconds); + loop->Run(); +} + +TEST_F(IPCChannelPosixTest, BasicListen) { + // Test creating a socket that is listening. + IPC::ChannelHandle handle("/var/tmp/IPCChannelPosixTest_BasicListen"); + SetUpSocket(&handle, IPC::Channel::MODE_NAMED_SERVER); + unlink(handle.name.c_str()); + IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL); + ASSERT_TRUE(channel.Connect()); + ASSERT_TRUE(channel.AcceptsConnections()); + ASSERT_FALSE(channel.HasAcceptedConnection()); + channel.ResetToAcceptingConnectionState(); + ASSERT_FALSE(channel.HasAcceptedConnection()); +} + +TEST_F(IPCChannelPosixTest, BasicConnected) { + // Test creating a socket that is connected. + int pipe_fds[2]; + ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)); + std::string socket_name("/var/tmp/IPCChannelPosixTest_BasicConnected"); + ASSERT_GE(fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK), 0); + + base::FileDescriptor fd(pipe_fds[0], false); + IPC::ChannelHandle handle(socket_name, fd); + IPC::Channel channel(handle, IPC::Channel::MODE_SERVER, NULL); + ASSERT_TRUE(channel.Connect()); + ASSERT_FALSE(channel.AcceptsConnections()); + channel.Close(); + ASSERT_TRUE(HANDLE_EINTR(close(pipe_fds[1])) == 0); + + // Make sure that we can use the socket that is created for us by + // a standard channel. + IPC::Channel channel2(socket_name, IPC::Channel::MODE_SERVER, NULL); + ASSERT_TRUE(channel2.Connect()); + ASSERT_FALSE(channel2.AcceptsConnections()); +} + +TEST_F(IPCChannelPosixTest, AdvancedConnected) { + // Test creating a connection to an external process. + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + ASSERT_TRUE(channel.Connect()); + ASSERT_TRUE(channel.AcceptsConnections()); + ASSERT_FALSE(channel.HasAcceptedConnection()); + + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", + false); + ASSERT_TRUE(handle); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + IPC::Message* message = new IPC::Message(0, // routing_id + QUIT_MESSAGE, // message type + IPC::Message::PRIORITY_NORMAL); + channel.Send(message); + SpinRunLoop(TestTimeouts::action_timeout_ms()); + int exit_code = 0; + EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code)); + EXPECT_EQ(0, exit_code); + ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); + ASSERT_FALSE(channel.HasAcceptedConnection()); +} + +TEST_F(IPCChannelPosixTest, ResetState) { + // Test creating a connection to an external process. Close the connection, + // but continue to listen and make sure another external process can connect + // to us. + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + ASSERT_TRUE(channel.Connect()); + ASSERT_TRUE(channel.AcceptsConnections()); + ASSERT_FALSE(channel.HasAcceptedConnection()); + + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", + false); + ASSERT_TRUE(handle); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + channel.ResetToAcceptingConnectionState(); + ASSERT_FALSE(channel.HasAcceptedConnection()); + + base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixTestConnectionProc", + false); + ASSERT_TRUE(handle2); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + IPC::Message* message = new IPC::Message(0, // routing_id + QUIT_MESSAGE, // message type + IPC::Message::PRIORITY_NORMAL); + channel.Send(message); + SpinRunLoop(TestTimeouts::action_timeout_ms()); + EXPECT_TRUE(base::KillProcess(handle, 0, false)); + int exit_code = 0; + EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code)); + EXPECT_EQ(0, exit_code); + ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); + ASSERT_FALSE(channel.HasAcceptedConnection()); +} + +TEST_F(IPCChannelPosixTest, MultiConnection) { + // Test setting up a connection to an external process, and then have + // another external process attempt to connect to us. + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + ASSERT_TRUE(channel.Connect()); + ASSERT_TRUE(channel.AcceptsConnections()); + ASSERT_FALSE(channel.HasAcceptedConnection()); + + base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc", + false); + ASSERT_TRUE(handle); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc", + false); + ASSERT_TRUE(handle2); + SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + int exit_code = 0; + EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code)); + EXPECT_EQ(exit_code, 0); + ASSERT_EQ(IPCChannelPosixTestListener::DENIED, listener.status()); + ASSERT_TRUE(channel.HasAcceptedConnection()); + IPC::Message* message = new IPC::Message(0, // routing_id + QUIT_MESSAGE, // message type + IPC::Message::PRIORITY_NORMAL); + channel.Send(message); + SpinRunLoop(TestTimeouts::action_timeout_ms()); + EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code)); + EXPECT_EQ(exit_code, 0); + ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); + ASSERT_FALSE(channel.HasAcceptedConnection()); +} + +TEST_F(IPCChannelPosixTest, DoubleServer) { + // Test setting up two servers with the same name. + IPCChannelPosixTestListener listener(false); + IPCChannelPosixTestListener listener2(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_SERVER, &listener); + IPC::Channel channel2(chan_handle, IPC::Channel::MODE_SERVER, &listener2); + ASSERT_TRUE(channel.Connect()); + ASSERT_FALSE(channel2.Connect()); +} + +TEST_F(IPCChannelPosixTest, BadMode) { + // Test setting up two servers with a bad mode. + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NONE, &listener); + ASSERT_FALSE(channel.Connect()); +} + +TEST_F(IPCChannelPosixTest, IsNamedServerInitialized) { + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle chan_handle(kConnectionSocketTestName); + ASSERT_TRUE(file_util::Delete(FilePath(kConnectionSocketTestName), false)); + ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized( + kConnectionSocketTestName)); + IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener); + ASSERT_TRUE(IPC::Channel::IsNamedServerInitialized( + kConnectionSocketTestName)); + channel.Close(); + ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized( + kConnectionSocketTestName)); +} + +// A long running process that connects to us +MULTIPROCESS_TEST_MAIN(IPCChannelPosixTestConnectionProc) { + MessageLoopForIO message_loop; + IPCChannelPosixTestListener listener(true); + IPC::ChannelHandle handle(IPCChannelPosixTest::kConnectionSocketTestName); + IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT); + IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener); + EXPECT_TRUE(channel.Connect()); + IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + EXPECT_EQ(IPCChannelPosixTestListener::MESSAGE_RECEIVED, listener.status()); + return 0; +} + +// Simple external process that shouldn't be able to connect to us. +MULTIPROCESS_TEST_MAIN(IPCChannelPosixFailConnectionProc) { + MessageLoopForIO message_loop; + IPCChannelPosixTestListener listener(false); + IPC::ChannelHandle handle(IPCChannelPosixTest::kConnectionSocketTestName); + IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT); + IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener); + + // In this case connect may succeed or fail depending on if the packet + // actually gets sent at sendmsg. Since we never delay on send, we may not + // see the error. However even if connect succeeds, eventually we will get an + // error back since the channel will be closed when we attempt to read from + // it. + bool connected = channel.Connect(); + if (connected) { + IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout_ms()); + EXPECT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status()); + } else { + EXPECT_EQ(IPCChannelPosixTestListener::DISCONNECTED, listener.status()); + } + return 0; +} + diff --git a/ipc/ipc_channel_proxy.cc b/ipc/ipc_channel_proxy.cc new file mode 100644 index 0000000..dc990c2 --- /dev/null +++ b/ipc/ipc_channel_proxy.cc @@ -0,0 +1,397 @@ +// 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. + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_message_utils.h" + +namespace IPC { + +//------------------------------------------------------------------------------ + +// This task ensures the message is deleted if the task is deleted without +// having been run. +class SendTask : public Task { + public: + SendTask(ChannelProxy::Context* context, Message* message) + : context_(context), + message_(message) { + } + + virtual void Run() { + context_->OnSendMessage(message_.release()); + } + + private: + scoped_refptr context_; + scoped_ptr message_; + + DISALLOW_COPY_AND_ASSIGN(SendTask); +}; + +//------------------------------------------------------------------------------ + +ChannelProxy::MessageFilter::MessageFilter() {} + +ChannelProxy::MessageFilter::~MessageFilter() {} + +void ChannelProxy::MessageFilter::OnFilterAdded(Channel* channel) {} + +void ChannelProxy::MessageFilter::OnFilterRemoved() {} + +void ChannelProxy::MessageFilter::OnChannelConnected(int32 peer_pid) {} + +void ChannelProxy::MessageFilter::OnChannelError() {} + +void ChannelProxy::MessageFilter::OnChannelClosing() {} + +bool ChannelProxy::MessageFilter::OnMessageReceived(const Message& message) { + return false; +} + +void ChannelProxy::MessageFilter::OnDestruct() const { + delete this; +} + +//------------------------------------------------------------------------------ + +ChannelProxy::Context::Context(Channel::Listener* listener, + base::MessageLoopProxy* ipc_message_loop) + : listener_message_loop_(base::MessageLoopProxy::current()), + listener_(listener), + ipc_message_loop_(ipc_message_loop), + peer_pid_(0), + channel_connected_called_(false) { +} + +ChannelProxy::Context::~Context() { +} + +void ChannelProxy::Context::CreateChannel(const IPC::ChannelHandle& handle, + const Channel::Mode& mode) { + DCHECK(channel_.get() == NULL); + channel_id_ = handle.name; + channel_.reset(new Channel(handle, mode, this)); +} + +bool ChannelProxy::Context::TryFilters(const Message& message) { +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging* logger = Logging::GetInstance(); + if (logger->Enabled()) + logger->OnPreDispatchMessage(message); +#endif + + for (size_t i = 0; i < filters_.size(); ++i) { + if (filters_[i]->OnMessageReceived(message)) { +#ifdef IPC_MESSAGE_LOG_ENABLED + if (logger->Enabled()) + logger->OnPostDispatchMessage(message, channel_id_); +#endif + return true; + } + } + return false; +} + +// Called on the IPC::Channel thread +bool ChannelProxy::Context::OnMessageReceived(const Message& message) { + // First give a chance to the filters to process this message. + if (!TryFilters(message)) + OnMessageReceivedNoFilter(message); + return true; +} + +// Called on the IPC::Channel thread +bool ChannelProxy::Context::OnMessageReceivedNoFilter(const Message& message) { + // NOTE: This code relies on the listener's message loop not going away while + // this thread is active. That should be a reasonable assumption, but it + // feels risky. We may want to invent some more indirect way of referring to + // a MessageLoop if this becomes a problem. + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchMessage, message)); + return true; +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelConnected(int32 peer_pid) { + // Add any pending filters. This avoids a race condition where someone + // creates a ChannelProxy, calls AddFilter, and then right after starts the + // peer process. The IO thread could receive a message before the task to add + // the filter is run on the IO thread. + OnAddFilter(); + + peer_pid_ = peer_pid; + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnChannelConnected(peer_pid); + + // See above comment about using listener_message_loop_ here. + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchConnected)); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelError() { + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnChannelError(); + + // See above comment about using listener_message_loop_ here. + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Context::OnDispatchError)); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelOpened() { + DCHECK(channel_ != NULL); + + // Assume a reference to ourselves on behalf of this thread. This reference + // will be released when we are closed. + AddRef(); + + if (!channel_->Connect()) { + OnChannelError(); + return; + } + + for (size_t i = 0; i < filters_.size(); ++i) + filters_[i]->OnFilterAdded(channel_.get()); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnChannelClosed() { + // It's okay for IPC::ChannelProxy::Close to be called more than once, which + // would result in this branch being taken. + if (!channel_.get()) + return; + + for (size_t i = 0; i < filters_.size(); ++i) { + filters_[i]->OnChannelClosing(); + filters_[i]->OnFilterRemoved(); + } + + // We don't need the filters anymore. + filters_.clear(); + + channel_.reset(); + + // Balance with the reference taken during startup. This may result in + // self-destruction. + Release(); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnSendMessage(Message* message) { + if (!channel_.get()) { + delete message; + OnChannelClosed(); + return; + } + if (!channel_->Send(message)) + OnChannelError(); +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnAddFilter() { + std::vector > new_filters; + { + base::AutoLock auto_lock(pending_filters_lock_); + new_filters.swap(pending_filters_); + } + + for (size_t i = 0; i < new_filters.size(); ++i) { + filters_.push_back(new_filters[i]); + + // If the channel has already been created, then we need to send this + // message so that the filter gets access to the Channel. + if (channel_.get()) + new_filters[i]->OnFilterAdded(channel_.get()); + // Ditto for if the channel has been connected. + if (peer_pid_) + new_filters[i]->OnChannelConnected(peer_pid_); + } +} + +// Called on the IPC::Channel thread +void ChannelProxy::Context::OnRemoveFilter(MessageFilter* filter) { + for (size_t i = 0; i < filters_.size(); ++i) { + if (filters_[i].get() == filter) { + filter->OnFilterRemoved(); + filters_.erase(filters_.begin() + i); + return; + } + } + + NOTREACHED() << "filter to be removed not found"; +} + +// Called on the listener's thread +void ChannelProxy::Context::AddFilter(MessageFilter* filter) { + base::AutoLock auto_lock(pending_filters_lock_); + pending_filters_.push_back(make_scoped_refptr(filter)); + ipc_message_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &Context::OnAddFilter)); +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchMessage(const Message& message) { + if (!listener_) + return; + + OnDispatchConnected(); + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging* logger = Logging::GetInstance(); + if (message.type() == IPC_LOGGING_ID) { + logger->OnReceivedLoggingMessage(message); + return; + } + + if (logger->Enabled()) + logger->OnPreDispatchMessage(message); +#endif + + listener_->OnMessageReceived(message); + +#ifdef IPC_MESSAGE_LOG_ENABLED + if (logger->Enabled()) + logger->OnPostDispatchMessage(message, channel_id_); +#endif +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchConnected() { + if (channel_connected_called_) + return; + + channel_connected_called_ = true; + if (listener_) + listener_->OnChannelConnected(peer_pid_); +} + +// Called on the listener's thread +void ChannelProxy::Context::OnDispatchError() { + if (listener_) + listener_->OnChannelError(); +} + +//----------------------------------------------------------------------------- + +ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Channel::Listener* listener, + base::MessageLoopProxy* ipc_thread) + : context_(new Context(listener, ipc_thread)), + outgoing_message_filter_(NULL) { + Init(channel_handle, mode, ipc_thread, true); +} + +ChannelProxy::ChannelProxy(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + base::MessageLoopProxy* ipc_thread, + Context* context, + bool create_pipe_now) + : context_(context), + outgoing_message_filter_(NULL) { + Init(channel_handle, mode, ipc_thread, create_pipe_now); +} + +ChannelProxy::~ChannelProxy() { + Close(); +} + +void ChannelProxy::Init(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + base::MessageLoopProxy* ipc_thread_loop, + bool create_pipe_now) { +#if defined(OS_POSIX) + // When we are creating a server on POSIX, we need its file descriptor + // to be created immediately so that it can be accessed and passed + // to other processes. Forcing it to be created immediately avoids + // race conditions that may otherwise arise. + if (mode & Channel::MODE_SERVER_FLAG) { + create_pipe_now = true; + } +#endif // defined(OS_POSIX) + + if (create_pipe_now) { + // Create the channel immediately. This effectively sets up the + // low-level pipe so that the client can connect. Without creating + // the pipe immediately, it is possible for a listener to attempt + // to connect and get an error since the pipe doesn't exist yet. + context_->CreateChannel(channel_handle, mode); + } else { + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::CreateChannel, channel_handle, mode)); + } + + // complete initialization on the background thread + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnChannelOpened)); +} + +void ChannelProxy::Close() { + // Clear the backpointer to the listener so that any pending calls to + // Context::OnDispatchMessage or OnDispatchError will be ignored. It is + // possible that the channel could be closed while it is receiving messages! + context_->Clear(); + + if (context_->ipc_message_loop()) { + context_->ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + context_.get(), &Context::OnChannelClosed)); + } +} + +bool ChannelProxy::Send(Message* message) { + if (outgoing_message_filter()) + message = outgoing_message_filter()->Rewrite(message); + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::GetInstance()->OnSendMessage(message, context_->channel_id()); +#endif + + context_->ipc_message_loop()->PostTask(FROM_HERE, + new SendTask(context_.get(), message)); + return true; +} + +void ChannelProxy::AddFilter(MessageFilter* filter) { + context_->AddFilter(filter); +} + +void ChannelProxy::RemoveFilter(MessageFilter* filter) { + context_->ipc_message_loop()->PostTask( + FROM_HERE, NewRunnableMethod( + context_.get(), + &Context::OnRemoveFilter, + make_scoped_refptr(filter))); +} + +void ChannelProxy::ClearIPCMessageLoop() { + context()->ClearIPCMessageLoop(); +} + +#if defined(OS_POSIX) && !defined(OS_NACL) +// See the TODO regarding lazy initialization of the channel in +// ChannelProxy::Init(). +// We assume that IPC::Channel::GetClientFileDescriptorMapping() is thread-safe. +int ChannelProxy::GetClientFileDescriptor() const { + Channel *channel = context_.get()->channel_.get(); + // Channel must have been created first. + DCHECK(channel) << context_.get()->channel_id_; + return channel->GetClientFileDescriptor(); +} + +bool ChannelProxy::GetClientEuid(uid_t* client_euid) const { + Channel *channel = context_.get()->channel_.get(); + // Channel must have been created first. + DCHECK(channel) << context_.get()->channel_id_; + return channel->GetClientEuid(client_euid); +} +#endif + +//----------------------------------------------------------------------------- + +} // namespace IPC diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h new file mode 100644 index 0000000..792e3d6 --- /dev/null +++ b/ipc/ipc_channel_proxy.h @@ -0,0 +1,272 @@ +// 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. + +#ifndef IPC_IPC_CHANNEL_PROXY_H_ +#define IPC_IPC_CHANNEL_PROXY_H_ +#pragma once + +#include + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop_proxy.h" +#include "base/synchronization/lock.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_channel_handle.h" + +namespace IPC { + +class SendTask; + +//----------------------------------------------------------------------------- +// IPC::ChannelProxy +// +// This class is a helper class that is useful when you wish to run an IPC +// channel on a background thread. It provides you with the option of either +// handling IPC messages on that background thread or having them dispatched to +// your main thread (the thread on which the IPC::ChannelProxy is created). +// +// The API for an IPC::ChannelProxy is very similar to that of an IPC::Channel. +// When you send a message to an IPC::ChannelProxy, the message is routed to +// the background thread, where it is then passed to the IPC::Channel's Send +// method. This means that you can send a message from your thread and your +// message will be sent over the IPC channel when possible instead of being +// delayed until your thread returns to its message loop. (Often IPC messages +// will queue up on the IPC::Channel when there is a lot of traffic, and the +// channel will not get cycles to flush its message queue until the thread, on +// which it is running, returns to its message loop.) +// +// An IPC::ChannelProxy can have a MessageFilter associated with it, which will +// be notified of incoming messages on the IPC::Channel's thread. This gives +// the consumer of IPC::ChannelProxy the ability to respond to incoming +// messages on this background thread instead of on their own thread, which may +// be bogged down with other processing. The result can be greatly improved +// latency for messages that can be handled on a background thread. +// +// The consumer of IPC::ChannelProxy is responsible for allocating the Thread +// instance where the IPC::Channel will be created and operated. +// +class IPC_EXPORT ChannelProxy : public Message::Sender { + public: + + struct MessageFilterTraits; + + // A class that receives messages on the thread where the IPC channel is + // running. It can choose to prevent the default action for an IPC message. + class IPC_EXPORT MessageFilter + : public base::RefCountedThreadSafe { + public: + MessageFilter(); + virtual ~MessageFilter(); + + // Called on the background thread to provide the filter with access to the + // channel. Called when the IPC channel is initialized or when AddFilter + // is called if the channel is already initialized. + virtual void OnFilterAdded(Channel* channel); + + // Called on the background thread when the filter has been removed from + // the ChannelProxy and when the Channel is closing. After a filter is + // removed, it will not be called again. + virtual void OnFilterRemoved(); + + // Called to inform the filter that the IPC channel is connected and we + // have received the internal Hello message from the peer. + virtual void OnChannelConnected(int32 peer_pid); + + // Called when there is an error on the channel, typically that the channel + // has been closed. + virtual void OnChannelError(); + + // Called to inform the filter that the IPC channel will be destroyed. + // OnFilterRemoved is called immediately after this. + virtual void OnChannelClosing(); + + // Return true to indicate that the message was handled, or false to let + // the message be handled in the default way. + virtual bool OnMessageReceived(const Message& message); + + // Called when the message filter is about to be deleted. This gives + // derived classes the option of controlling which thread they're deleted + // on etc. + virtual void OnDestruct() const; + }; + + struct MessageFilterTraits { + static void Destruct(const MessageFilter* filter) { + filter->OnDestruct(); + } + }; + + // Interface for a filter to be imposed on outgoing messages which can + // re-write the message. Used mainly for testing. + class OutgoingMessageFilter { + public: + // Returns a re-written message, freeing the original, or simply the + // original unchanged if no rewrite indicated. + virtual Message *Rewrite(Message *message) = 0; + }; + + // Initializes a channel proxy. The channel_handle and mode parameters are + // passed directly to the underlying IPC::Channel. The listener is called on + // the thread that creates the ChannelProxy. The filter's OnMessageReceived + // method is called on the thread where the IPC::Channel is running. The + // filter may be null if the consumer is not interested in handling messages + // on the background thread. Any message not handled by the filter will be + // dispatched to the listener. The given message loop indicates where the + // IPC::Channel should be created. + ChannelProxy(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Channel::Listener* listener, + base::MessageLoopProxy* ipc_thread_loop); + + virtual ~ChannelProxy(); + + // Close the IPC::Channel. This operation completes asynchronously, once the + // background thread processes the command to close the channel. It is ok to + // call this method multiple times. Redundant calls are ignored. + // + // WARNING: The MessageFilter object held by the ChannelProxy is also + // released asynchronously, and it may in fact have its final reference + // released on the background thread. The caller should be careful to deal + // with / allow for this possibility. + void Close(); + + // Send a message asynchronously. The message is routed to the background + // thread where it is passed to the IPC::Channel's Send method. + virtual bool Send(Message* message); + + // Used to intercept messages as they are received on the background thread. + // + // Ordinarily, messages sent to the ChannelProxy are routed to the matching + // listener on the worker thread. This API allows code to intercept messages + // before they are sent to the worker thread. + // If you call this before the target process is launched, then you're + // guaranteed to not miss any messages. But if you call this anytime after, + // then some messages might be missed since the filter is added internally on + // the IO thread. + void AddFilter(MessageFilter* filter); + void RemoveFilter(MessageFilter* filter); + + void set_outgoing_message_filter(OutgoingMessageFilter* filter) { + outgoing_message_filter_ = filter; + } + + // Called to clear the pointer to the IPC message loop when it's going away. + void ClearIPCMessageLoop(); + +#if defined(OS_POSIX) + // Calls through to the underlying channel's methods. + int GetClientFileDescriptor() const; + bool GetClientEuid(uid_t* client_euid) const; +#endif // defined(OS_POSIX) + + protected: + class Context; + // A subclass uses this constructor if it needs to add more information + // to the internal state. If create_pipe_now is true, the pipe is created + // immediately. Otherwise it's created on the IO thread. + ChannelProxy(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + base::MessageLoopProxy* ipc_thread_loop, + Context* context, + bool create_pipe_now); + + // Used internally to hold state that is referenced on the IPC thread. + class Context : public base::RefCountedThreadSafe, + public Channel::Listener { + public: + Context(Channel::Listener* listener, base::MessageLoopProxy* ipc_thread); + void ClearIPCMessageLoop() { ipc_message_loop_ = NULL; } + base::MessageLoopProxy* ipc_message_loop() const { + return ipc_message_loop_.get(); + } + const std::string& channel_id() const { return channel_id_; } + + // Dispatches a message on the listener thread. + void OnDispatchMessage(const Message& message); + + protected: + friend class base::RefCountedThreadSafe; + virtual ~Context(); + + // IPC::Channel::Listener methods: + virtual bool OnMessageReceived(const Message& message) OVERRIDE; + virtual void OnChannelConnected(int32 peer_pid) OVERRIDE; + virtual void OnChannelError() OVERRIDE; + + // Like OnMessageReceived but doesn't try the filters. + bool OnMessageReceivedNoFilter(const Message& message); + + // Gives the filters a chance at processing |message|. + // Returns true if the message was processed, false otherwise. + bool TryFilters(const Message& message); + + // Like Open and Close, but called on the IPC thread. + virtual void OnChannelOpened(); + virtual void OnChannelClosed(); + + // Called on the consumers thread when the ChannelProxy is closed. At that + // point the consumer is telling us that they don't want to receive any + // more messages, so we honor that wish by forgetting them! + virtual void Clear() { listener_ = NULL; } + + private: + friend class ChannelProxy; + friend class SendTask; + + // Create the Channel + void CreateChannel(const IPC::ChannelHandle& channel_handle, + const Channel::Mode& mode); + + // Methods called on the IO thread. + void OnSendMessage(Message* message_ptr); + void OnAddFilter(); + void OnRemoveFilter(MessageFilter* filter); + + // Methods called on the listener thread. + void AddFilter(MessageFilter* filter); + void OnDispatchConnected(); + void OnDispatchError(); + + scoped_refptr listener_message_loop_; + Channel::Listener* listener_; + + // List of filters. This is only accessed on the IPC thread. + std::vector > filters_; + scoped_refptr ipc_message_loop_; + scoped_ptr channel_; + std::string channel_id_; + int peer_pid_; + bool channel_connected_called_; + + // Holds filters between the AddFilter call on the listerner thread and the + // IPC thread when they're added to filters_. + std::vector > pending_filters_; + // Lock for pending_filters_. + base::Lock pending_filters_lock_; + }; + + Context* context() { return context_; } + + OutgoingMessageFilter* outgoing_message_filter() { + return outgoing_message_filter_; + } + + private: + friend class SendTask; + + void Init(const IPC::ChannelHandle& channel_handle, Channel::Mode mode, + base::MessageLoopProxy* ipc_thread_loop, bool create_pipe_now); + + // By maintaining this indirection (ref-counted) to our internal state, we + // can safely be destroyed while the background thread continues to do stuff + // that involves this data. + scoped_refptr context_; + + OutgoingMessageFilter* outgoing_message_filter_; +}; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_PROXY_H_ diff --git a/ipc/ipc_channel_win.cc b/ipc/ipc_channel_win.cc new file mode 100644 index 0000000..3a85a16 --- /dev/null +++ b/ipc/ipc_channel_win.cc @@ -0,0 +1,430 @@ +// 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. + +#include "ipc/ipc_channel_win.h" + +#include + +#include "base/auto_reset.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/threading/non_thread_safe.h" +#include "base/utf_string_conversions.h" +#include "base/win/scoped_handle.h" +#include "ipc/ipc_logging.h" +#include "ipc/ipc_message_utils.h" + +namespace IPC { + +Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) { + memset(&context.overlapped, 0, sizeof(context.overlapped)); + context.handler = channel; +} + +Channel::ChannelImpl::State::~State() { + COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context), + starts_with_io_context); +} + +Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle &channel_handle, + Mode mode, Listener* listener) + : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)), + pipe_(INVALID_HANDLE_VALUE), + listener_(listener), + waiting_connect_(mode & MODE_SERVER_FLAG), + processing_incoming_(false), + ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) { + CreatePipe(channel_handle, mode); +} + +Channel::ChannelImpl::~ChannelImpl() { + Close(); +} + +void Channel::ChannelImpl::Close() { + if (thread_check_.get()) { + DCHECK(thread_check_->CalledOnValidThread()); + } + + if (input_state_.is_pending || output_state_.is_pending) + CancelIo(pipe_); + + // Closing the handle at this point prevents us from issuing more requests + // form OnIOCompleted(). + if (pipe_ != INVALID_HANDLE_VALUE) { + CloseHandle(pipe_); + pipe_ = INVALID_HANDLE_VALUE; + } + + // Make sure all IO has completed. + base::Time start = base::Time::Now(); + while (input_state_.is_pending || output_state_.is_pending) { + MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this); + } + + while (!output_queue_.empty()) { + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; + } +} + +bool Channel::ChannelImpl::Send(Message* message) { + DCHECK(thread_check_->CalledOnValidThread()); + DVLOG(2) << "sending message @" << message << " on channel @" << this + << " with type " << message->type() + << " (" << output_queue_.size() << " in queue)"; + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::GetInstance()->OnSendMessage(message, ""); +#endif + + output_queue_.push(message); + // ensure waiting to write + if (!waiting_connect_) { + if (!output_state_.is_pending) { + if (!ProcessOutgoingMessages(NULL, 0)) + return false; + } + } + + return true; +} + +// static +bool Channel::ChannelImpl::IsNamedServerInitialized( + const std::string& channel_id) { + if (WaitNamedPipe(PipeName(channel_id).c_str(), 1)) + return true; + // If ERROR_SEM_TIMEOUT occurred, the pipe exists but is handling another + // connection. + return GetLastError() == ERROR_SEM_TIMEOUT; +} + +// static +const std::wstring Channel::ChannelImpl::PipeName( + const std::string& channel_id) { + std::string name("\\\\.\\pipe\\chrome."); + return ASCIIToWide(name.append(channel_id)); +} + +bool Channel::ChannelImpl::CreatePipe(const IPC::ChannelHandle &channel_handle, + Mode mode) { + DCHECK_EQ(INVALID_HANDLE_VALUE, pipe_); + const std::wstring pipe_name = PipeName(channel_handle.name); + if (mode & MODE_SERVER_FLAG) { + pipe_ = CreateNamedPipeW(pipe_name.c_str(), + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | + FILE_FLAG_FIRST_PIPE_INSTANCE, + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, + 1, + Channel::kReadBufferSize, + Channel::kReadBufferSize, + 5000, + NULL); + } else if (mode & MODE_CLIENT_FLAG) { + pipe_ = CreateFileW(pipe_name.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | + FILE_FLAG_OVERLAPPED, + NULL); + } else { + NOTREACHED(); + } + if (pipe_ == INVALID_HANDLE_VALUE) { + // If this process is being closed, the pipe may be gone already. + LOG(WARNING) << "Unable to create pipe \"" << pipe_name << + "\" in " << (mode == 0 ? "server" : "client") + << " mode. Error :" << GetLastError(); + return false; + } + + // Create the Hello message to be sent when Connect is called + scoped_ptr m(new Message(MSG_ROUTING_NONE, + HELLO_MESSAGE_TYPE, + IPC::Message::PRIORITY_NORMAL)); + if (!m->WriteInt(GetCurrentProcessId())) { + CloseHandle(pipe_); + pipe_ = INVALID_HANDLE_VALUE; + return false; + } + + output_queue_.push(m.release()); + return true; +} + +bool Channel::ChannelImpl::Connect() { + DLOG_IF(WARNING, thread_check_.get()) << "Connect called more than once"; + + if (!thread_check_.get()) + thread_check_.reset(new base::NonThreadSafe()); + + if (pipe_ == INVALID_HANDLE_VALUE) + return false; + + MessageLoopForIO::current()->RegisterIOHandler(pipe_, this); + + // Check to see if there is a client connected to our pipe... + if (waiting_connect_) + ProcessConnection(); + + if (!input_state_.is_pending) { + // Complete setup asynchronously. By not setting input_state_.is_pending + // to true, we indicate to OnIOCompleted that this is the special + // initialization signal. + MessageLoopForIO::current()->PostTask(FROM_HERE, factory_.NewRunnableMethod( + &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0)); + } + + if (!waiting_connect_) + ProcessOutgoingMessages(NULL, 0); + return true; +} + +bool Channel::ChannelImpl::ProcessConnection() { + DCHECK(thread_check_->CalledOnValidThread()); + if (input_state_.is_pending) + input_state_.is_pending = false; + + // Do we have a client connected to our pipe? + if (INVALID_HANDLE_VALUE == pipe_) + return false; + + BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped); + + DWORD err = GetLastError(); + if (ok) { + // Uhm, the API documentation says that this function should never + // return success when used in overlapped mode. + NOTREACHED(); + return false; + } + + switch (err) { + case ERROR_IO_PENDING: + input_state_.is_pending = true; + break; + case ERROR_PIPE_CONNECTED: + waiting_connect_ = false; + break; + case ERROR_NO_DATA: + // The pipe is being closed. + return false; + default: + NOTREACHED(); + return false; + } + + return true; +} + +bool Channel::ChannelImpl::ProcessIncomingMessages( + MessageLoopForIO::IOContext* context, + DWORD bytes_read) { + DCHECK(thread_check_->CalledOnValidThread()); + if (input_state_.is_pending) { + input_state_.is_pending = false; + DCHECK(context); + + if (!context || !bytes_read) + return false; + } else { + // This happens at channel initialization. + DCHECK(!bytes_read && context == &input_state_.context); + } + + for (;;) { + if (bytes_read == 0) { + if (INVALID_HANDLE_VALUE == pipe_) + return false; + + // Read from pipe... + BOOL ok = ReadFile(pipe_, + input_buf_, + Channel::kReadBufferSize, + &bytes_read, + &input_state_.context.overlapped); + if (!ok) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + input_state_.is_pending = true; + return true; + } + LOG(ERROR) << "pipe error: " << err; + return false; + } + input_state_.is_pending = true; + return true; + } + DCHECK(bytes_read); + + // Process messages from input buffer. + + const char* p, *end; + if (input_overflow_buf_.empty()) { + p = input_buf_; + end = p + bytes_read; + } else { + if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) { + input_overflow_buf_.clear(); + LOG(ERROR) << "IPC message is too big"; + return false; + } + input_overflow_buf_.append(input_buf_, bytes_read); + p = input_overflow_buf_.data(); + end = p + input_overflow_buf_.size(); + } + + while (p < end) { + const char* message_tail = Message::FindNext(p, end); + if (message_tail) { + int len = static_cast(message_tail - p); + const Message m(p, len); + DVLOG(2) << "received message on channel @" << this + << " with type " << m.type(); + if (m.routing_id() == MSG_ROUTING_NONE && + m.type() == HELLO_MESSAGE_TYPE) { + // The Hello message contains only the process id. + listener_->OnChannelConnected(MessageIterator(m).NextInt()); + } else { + listener_->OnMessageReceived(m); + } + p = message_tail; + } else { + // Last message is partial. + break; + } + } + input_overflow_buf_.assign(p, end - p); + + bytes_read = 0; // Get more data. + } + + return true; +} + +bool Channel::ChannelImpl::ProcessOutgoingMessages( + MessageLoopForIO::IOContext* context, + DWORD bytes_written) { + DCHECK(!waiting_connect_); // Why are we trying to send messages if there's + // no connection? + DCHECK(thread_check_->CalledOnValidThread()); + + if (output_state_.is_pending) { + DCHECK(context); + output_state_.is_pending = false; + if (!context || bytes_written == 0) { + DWORD err = GetLastError(); + LOG(ERROR) << "pipe error: " << err; + return false; + } + // Message was sent. + DCHECK(!output_queue_.empty()); + Message* m = output_queue_.front(); + output_queue_.pop(); + delete m; + } + + if (output_queue_.empty()) + return true; + + if (INVALID_HANDLE_VALUE == pipe_) + return false; + + // Write to pipe... + Message* m = output_queue_.front(); + DCHECK(m->size() <= INT_MAX); + BOOL ok = WriteFile(pipe_, + m->data(), + static_cast(m->size()), + &bytes_written, + &output_state_.context.overlapped); + if (!ok) { + DWORD err = GetLastError(); + if (err == ERROR_IO_PENDING) { + output_state_.is_pending = true; + + DVLOG(2) << "sent pending message @" << m << " on channel @" << this + << " with type " << m->type(); + + return true; + } + LOG(ERROR) << "pipe error: " << err; + return false; + } + + DVLOG(2) << "sent message @" << m << " on channel @" << this + << " with type " << m->type(); + + output_state_.is_pending = true; + return true; +} + +void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error) { + bool ok; + DCHECK(thread_check_->CalledOnValidThread()); + if (context == &input_state_.context) { + if (waiting_connect_) { + if (!ProcessConnection()) + return; + // We may have some messages queued up to send... + if (!output_queue_.empty() && !output_state_.is_pending) + ProcessOutgoingMessages(NULL, 0); + if (input_state_.is_pending) + return; + // else, fall-through and look for incoming messages... + } + // we don't support recursion through OnMessageReceived yet! + DCHECK(!processing_incoming_); + AutoReset auto_reset_processing_incoming(&processing_incoming_, true); + ok = ProcessIncomingMessages(context, bytes_transfered); + } else { + DCHECK(context == &output_state_.context); + ok = ProcessOutgoingMessages(context, bytes_transfered); + } + if (!ok && INVALID_HANDLE_VALUE != pipe_) { + // We don't want to re-enter Close(). + Close(); + listener_->OnChannelError(); + } +} + +//------------------------------------------------------------------------------ +// Channel's methods simply call through to ChannelImpl. +Channel::Channel(const IPC::ChannelHandle &channel_handle, Mode mode, + Listener* listener) + : channel_impl_(new ChannelImpl(channel_handle, mode, listener)) { +} + +Channel::~Channel() { + delete channel_impl_; +} + +bool Channel::Connect() { + return channel_impl_->Connect(); +} + +void Channel::Close() { + channel_impl_->Close(); +} + +void Channel::set_listener(Listener* listener) { + channel_impl_->set_listener(listener); +} + +bool Channel::Send(Message* message) { + return channel_impl_->Send(message); +} + +// static +bool Channel::IsNamedServerInitialized(const std::string& channel_id) { + return ChannelImpl::IsNamedServerInitialized(channel_id); +} + +} // namespace IPC diff --git a/ipc/ipc_channel_win.h b/ipc/ipc_channel_win.h new file mode 100644 index 0000000..ada88ac --- /dev/null +++ b/ipc/ipc_channel_win.h @@ -0,0 +1,91 @@ +// 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. + +#ifndef IPC_IPC_CHANNEL_WIN_H_ +#define IPC_IPC_CHANNEL_WIN_H_ +#pragma once + +#include "ipc/ipc_channel.h" + +#include +#include + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" + +namespace base { +class NonThreadSafe; +} + +namespace IPC { + +class Channel::ChannelImpl : public MessageLoopForIO::IOHandler { + public: + // Mirror methods of Channel, see ipc_channel.h for description. + ChannelImpl(const IPC::ChannelHandle &channel_handle, Mode mode, + Listener* listener); + ~ChannelImpl(); + bool Connect(); + void Close(); + void set_listener(Listener* listener) { listener_ = listener; } + bool Send(Message* message); + static bool IsNamedServerInitialized(const std::string& channel_id); + private: + static const std::wstring PipeName(const std::string& channel_id); + bool CreatePipe(const IPC::ChannelHandle &channel_handle, Mode mode); + + bool ProcessConnection(); + bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context, + DWORD bytes_read); + bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context, + DWORD bytes_written); + + // MessageLoop::IOHandler implementation. + virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error); + private: + struct State { + explicit State(ChannelImpl* channel); + ~State(); + MessageLoopForIO::IOContext context; + bool is_pending; + }; + + State input_state_; + State output_state_; + + HANDLE pipe_; + + Listener* listener_; + + // Messages to be sent are queued here. + std::queue output_queue_; + + // We read from the pipe into this buffer + char input_buf_[Channel::kReadBufferSize]; + + // Large messages that span multiple pipe buffers, get built-up using + // this buffer. + std::string input_overflow_buf_; + + // In server-mode, we have to wait for the client to connect before we + // can begin reading. We make use of the input_state_ when performing + // the connect operation in overlapped mode. + bool waiting_connect_; + + // This flag is set when processing incoming messages. It is used to + // avoid recursing through ProcessIncomingMessages, which could cause + // problems. TODO(darin): make this unnecessary + bool processing_incoming_; + + ScopedRunnableMethodFactory factory_; + + scoped_ptr thread_check_; + + DISALLOW_COPY_AND_ASSIGN(ChannelImpl); +}; + +} // namespace IPC + +#endif // IPC_IPC_CHANNEL_WIN_H_ diff --git a/ipc/ipc_descriptors.h b/ipc/ipc_descriptors.h new file mode 100644 index 0000000..4750fa8 --- /dev/null +++ b/ipc/ipc_descriptors.h @@ -0,0 +1,15 @@ +// 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 IPC_IPC_DESCRIPTORS_H_ +#define IPC_IPC_DESCRIPTORS_H_ +#pragma once + +// This is a list of global descriptor keys to be used with the +// base::GlobalDescriptors object (see base/global_descriptors_posix.h) +enum { + kPrimaryIPCChannel = 0, +}; + +#endif // IPC_IPC_DESCRIPTORS_H_ diff --git a/ipc/ipc_export.h b/ipc/ipc_export.h new file mode 100644 index 0000000..dc38fff --- /dev/null +++ b/ipc/ipc_export.h @@ -0,0 +1,29 @@ +// 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. + +#ifndef IPC_IPC_OSP_EXPORT_ +#define IPC_IPC_OSP_EXPORT_ +#pragma once + +// Defines IPC_EXPORT so that functionality implemented by the IPC module can be +// exported to consumers. + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(IPC_IMPLEMENTATION) +#define IPC_EXPORT __declspec(dllexport) +#else +#define IPC_EXPORT __declspec(dllimport) +#endif // defined(IPC_IMPLEMENTATION) + +#else // defined(WIN32) +#define IPC_EXPORT __attribute__((visibility("default"))) +#endif + +#else // defined(COMPONENT_BUILD) +#define IPC_EXPORT +#endif + +#endif // IPC_IPC_OSP_EXPORT_ diff --git a/ipc/ipc_logging.cc b/ipc/ipc_logging.cc new file mode 100644 index 0000000..138e6ac --- /dev/null +++ b/ipc/ipc_logging.cc @@ -0,0 +1,251 @@ +// 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 "ipc/ipc_logging.h" + +#ifdef IPC_MESSAGE_LOG_ENABLED +#define IPC_MESSAGE_MACROS_LOG_ENABLED +#endif + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "ipc/ipc_switches.h" +#include "ipc/ipc_sync_message.h" +#include "ipc/ipc_message_utils.h" + +#if defined(OS_POSIX) +#include +#endif + +#ifdef IPC_MESSAGE_LOG_ENABLED + +using base::Time; + +// IPC::Logging is allocated as a singleton, so we don't need any kind of +// special retention program. +DISABLE_RUNNABLE_METHOD_REFCOUNT(IPC::Logging); + +namespace IPC { + +const int kLogSendDelayMs = 100; + +// We use a pointer to the function table to avoid any linker dependencies on +// all the traits used as IPC message parameters. +LogFunctionMap* Logging::log_function_map_; + +Logging::Logging() + : enabled_(false), + enabled_on_stderr_(false), + queue_invoke_later_pending_(false), + sender_(NULL), + main_thread_(MessageLoop::current()), + consumer_(NULL) { +#if defined(OS_WIN) + // getenv triggers an unsafe warning. Simply check how big of a buffer + // would be needed to fetch the value to see if the enviornment variable is + // set. + size_t requiredSize = 0; + getenv_s(&requiredSize, NULL, 0, "CHROME_IPC_LOGGING"); + bool logging_env_var_set = (requiredSize != 0); +#else // !defined(OS_WIN) + bool logging_env_var_set = (getenv("CHROME_IPC_LOGGING") != NULL); +#endif //defined(OS_WIN) + if (logging_env_var_set) { + enabled_ = true; + enabled_on_stderr_ = true; + } +} + +Logging::~Logging() { +} + +Logging* Logging::GetInstance() { + return Singleton::get(); +} + +void Logging::SetConsumer(Consumer* consumer) { + consumer_ = consumer; +} + +void Logging::Enable() { + enabled_ = true; +} + +void Logging::Disable() { + enabled_ = false; +} + +void Logging::OnSendLogs() { + queue_invoke_later_pending_ = false; + if (!sender_) + return; + + Message* msg = new Message( + MSG_ROUTING_CONTROL, IPC_LOGGING_ID, Message::PRIORITY_NORMAL); + WriteParam(msg, queued_logs_); + queued_logs_.clear(); + sender_->Send(msg); +} + +void Logging::SetIPCSender(IPC::Message::Sender* sender) { + sender_ = sender; +} + +void Logging::OnReceivedLoggingMessage(const Message& message) { + std::vector data; + void* iter = NULL; + if (!ReadParam(&message, &iter, &data)) + return; + + for (size_t i = 0; i < data.size(); ++i) { + Log(data[i]); + } +} + +void Logging::OnSendMessage(Message* message, const std::string& channel_id) { + if (!Enabled()) + return; + + if (message->is_reply()) { + LogData* data = message->sync_log_data(); + if (!data) + return; + + // This is actually the delayed reply to a sync message. Create a string + // of the output parameters, add it to the LogData that was earlier stashed + // with the reply, and log the result. + data->channel = channel_id; + GenerateLogData("", *message, data); + Log(*data); + delete data; + message->set_sync_log_data(NULL); + } else { + // If the time has already been set (i.e. by ChannelProxy), keep that time + // instead as it's more accurate. + if (!message->sent_time()) + message->set_sent_time(Time::Now().ToInternalValue()); + } +} + +void Logging::OnPreDispatchMessage(const Message& message) { + message.set_received_time(Time::Now().ToInternalValue()); +} + +void Logging::OnPostDispatchMessage(const Message& message, + const std::string& channel_id) { + if (!Enabled() || + !message.sent_time() || + !message.received_time() || + message.dont_log()) + return; + + LogData data; + GenerateLogData(channel_id, message, &data); + + if (MessageLoop::current() == main_thread_) { + Log(data); + } else { + main_thread_->PostTask(FROM_HERE, NewRunnableMethod( + this, &Logging::Log, data)); + } +} + +void Logging::GetMessageText(uint32 type, std::string* name, + const Message* message, + std::string* params) { + if (!log_function_map_) + return; + + LogFunctionMap::iterator it = log_function_map_->find(type); + if (it == log_function_map_->end()) { + if (name) { + *name = "[UNKNOWN MSG "; + *name += base::IntToString(type); + *name += " ]"; + } + return; + } + + (*it->second)(name, message, params); +} + +void Logging::Log(const LogData& data) { + if (consumer_) { + // We're in the browser process. + consumer_->Log(data); + } else { + // We're in the renderer or plugin processes. + if (sender_) { + queued_logs_.push_back(data); + if (!queue_invoke_later_pending_) { + queue_invoke_later_pending_ = true; + MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod( + this, &Logging::OnSendLogs), kLogSendDelayMs); + } + } + } + if (enabled_on_stderr_) { + std::string message_name; + if (data.message_name.empty()) { + message_name = StringPrintf("[unknown type %d]", data.type); + } else { + message_name = data.message_name; + } + fprintf(stderr, "ipc %s %d %s %s %s\n", + data.channel.c_str(), + data.routing_id, + data.flags.c_str(), + message_name.c_str(), + data.params.c_str()); + } +} + +void GenerateLogData(const std::string& channel, const Message& message, + LogData* data) { + if (message.is_reply()) { + // "data" should already be filled in. + std::string params; + Logging::GetMessageText(data->type, NULL, &message, ¶ms); + + if (!data->params.empty() && !params.empty()) + data->params += ", "; + + data->flags += " DR"; + + data->params += params; + } else { + std::string flags; + if (message.is_sync()) + flags = "S"; + + if (message.is_reply()) + flags += "R"; + + if (message.is_reply_error()) + flags += "E"; + + std::string params, message_name; + Logging::GetMessageText(message.type(), &message_name, &message, ¶ms); + + data->channel = channel; + data->routing_id = message.routing_id(); + data->type = message.type(); + data->flags = flags; + data->sent = message.sent_time(); + data->receive = message.received_time(); + data->dispatch = Time::Now().ToInternalValue(); + data->params = params; + data->message_name = message_name; + } +} + +} + +#endif // IPC_MESSAGE_LOG_ENABLED diff --git a/ipc/ipc_logging.h b/ipc/ipc_logging.h new file mode 100644 index 0000000..42237c7 --- /dev/null +++ b/ipc/ipc_logging.h @@ -0,0 +1,112 @@ +// 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. + +#ifndef IPC_IPC_LOGGING_H_ +#define IPC_IPC_LOGGING_H_ +#pragma once + +#include "ipc/ipc_message.h" // For IPC_MESSAGE_LOG_ENABLED. + +#ifdef IPC_MESSAGE_LOG_ENABLED + +#include + +#include "base/hash_tables.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/singleton.h" +#include "base/message_loop.h" +#include "ipc/ipc_export.h" + +// Logging function. |name| is a string in ASCII and |params| is a string in +// UTF-8. +typedef void (*LogFunction)(std::string* name, + const IPC::Message* msg, + std::string* params); + +typedef base::hash_map LogFunctionMap; + +namespace IPC { + +class Message; + +// One instance per process. Needs to be created on the main thread (the UI +// thread in the browser) but OnPreDispatchMessage/OnPostDispatchMessage +// can be called on other threads. +class IPC_EXPORT Logging { + public: + // Implemented by consumers of log messages. + class Consumer { + public: + virtual void Log(const LogData& data) = 0; + + protected: + virtual ~Consumer() {} + }; + + void SetConsumer(Consumer* consumer); + + ~Logging(); + static Logging* GetInstance(); + + // Enable and Disable are NOT cross-process; they only affect the + // current thread/process. If you want to modify the value for all + // processes, perhaps your intent is to call + // g_browser_process->SetIPCLoggingEnabled(). + void Enable(); + void Disable(); + bool Enabled() const { return enabled_; } + + // Called by child processes to give the logger object the channel to send + // logging data to the browser process. + void SetIPCSender(Message::Sender* sender); + + // Called in the browser process when logging data from a child process is + // received. + void OnReceivedLoggingMessage(const Message& message); + + void OnSendMessage(Message* message, const std::string& channel_id); + void OnPreDispatchMessage(const Message& message); + void OnPostDispatchMessage(const Message& message, + const std::string& channel_id); + + // Like the *MsgLog functions declared for each message class, except this + // calls the correct one based on the message type automatically. Defined in + // ipc_logging.cc. + static void GetMessageText(uint32 type, std::string* name, + const Message* message, std::string* params); + + static void set_log_function_map(LogFunctionMap* functions) { + log_function_map_ = functions; + } + + static LogFunctionMap* log_function_map() { + return log_function_map_; + } + + private: + friend struct DefaultSingletonTraits; + Logging(); + + void OnSendLogs(); + void Log(const LogData& data); + + bool enabled_; + bool enabled_on_stderr_; // only used on POSIX for now + + std::vector queued_logs_; + bool queue_invoke_later_pending_; + + Message::Sender* sender_; + MessageLoop* main_thread_; + + Consumer* consumer_; + + static LogFunctionMap* log_function_map_; +}; + +} // namespace IPC + +#endif // IPC_MESSAGE_LOG_ENABLED + +#endif // IPC_IPC_LOGGING_H_ diff --git a/ipc/ipc_message.cc b/ipc/ipc_message.cc new file mode 100644 index 0000000..235a7d5 --- /dev/null +++ b/ipc/ipc_message.cc @@ -0,0 +1,128 @@ +// 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 "ipc/ipc_message.h" + +//#include "base/logging.h" +#include "log.h" +#include "build/build_config.h" + +#if defined(OS_POSIX) +//#include "ipc/file_descriptor_set_posix.h" +#endif + +namespace IPC { + +//------------------------------------------------------------------------------ + +Message::~Message() { +} + +Message::Message() + : Pickle(sizeof(Header)) { + header()->routing = header()->type = header()->flags = 0; +#if defined(OS_POSIX) + header()->num_fds = 0; + header()->pad = 0; +#endif + InitLoggingVariables(); +} + +Message::Message(int32 routing_id, uint32 type, PriorityValue priority) + : Pickle(sizeof(Header)) { + header()->routing = routing_id; + header()->type = type; + header()->flags = priority; +#if defined(OS_POSIX) + header()->num_fds = 0; + header()->pad = 0; +#endif + InitLoggingVariables(); +} + +Message::Message(const char* data, int data_len) : Pickle(data, data_len) { + InitLoggingVariables(); +} + +Message::Message(const Message& other) : Pickle(other) { + InitLoggingVariables(); +#if defined(OS_POSIX) +// file_descriptor_set_ = other.file_descriptor_set_; +#endif +} + +void Message::InitLoggingVariables() { +#ifdef IPC_MESSAGE_LOG_ENABLED + received_time_ = 0; + dont_log_ = false; + log_data_ = NULL; +#endif +} + +Message& Message::operator=(const Message& other) { + *static_cast(this) = other; +#if defined(OS_POSIX) +// file_descriptor_set_ = other.file_descriptor_set_; +#endif + return *this; +} + +#ifdef IPC_MESSAGE_LOG_ENABLED +void Message::set_sent_time(int64 time) { + DCHECK((header()->flags & HAS_SENT_TIME_BIT) == 0); + header()->flags |= HAS_SENT_TIME_BIT; + WriteInt64(time); +} + +int64 Message::sent_time() const { + if ((header()->flags & HAS_SENT_TIME_BIT) == 0) + return 0; + + const char* data = end_of_payload(); + data -= sizeof(int64); + return *(reinterpret_cast(data)); +} + +void Message::set_received_time(int64 time) const { + received_time_ = time; +} +#endif + +#if defined(OS_POSIX) +/* +bool Message::WriteFileDescriptor(const base::FileDescriptor& descriptor) { + // We write the index of the descriptor so that we don't have to + // keep the current descriptor as extra decoding state when deserialising. + WriteInt(file_descriptor_set()->size()); + if (descriptor.auto_close) { + return file_descriptor_set()->AddAndAutoClose(descriptor.fd); + } else { + return file_descriptor_set()->Add(descriptor.fd); + } +} + +bool Message::ReadFileDescriptor(void** iter, + base::FileDescriptor* descriptor) const { + int descriptor_index; + if (!ReadInt(iter, &descriptor_index)) + return false; + + FileDescriptorSet* file_descriptor_set = file_descriptor_set_.get(); + if (!file_descriptor_set) + return false; + + descriptor->fd = file_descriptor_set->GetDescriptorAt(descriptor_index); + descriptor->auto_close = true; + + return descriptor->fd >= 0; +} + +void Message::EnsureFileDescriptorSet() { + if (file_descriptor_set_.get() == NULL) + file_descriptor_set_ = new FileDescriptorSet; +} +*/ +#endif + +} // namespace IPC diff --git a/ipc/ipc_message.h b/ipc/ipc_message.h new file mode 100644 index 0000000..11ed17c --- /dev/null +++ b/ipc/ipc_message.h @@ -0,0 +1,284 @@ +// 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. + +#ifndef IPC_IPC_MESSAGE_H_ +#define IPC_IPC_MESSAGE_H_ +#pragma once + +#include + +#include "base/basictypes.h" +#include "base/pickle.h" +#include "ipc/ipc_export.h" + +#ifndef NDEBUG +//#define IPC_MESSAGE_LOG_ENABLED +#endif + +#if defined(OS_POSIX) +#include "base/memory/ref_counted.h" +#endif + +namespace base { +struct FileDescriptor; +} + +class FileDescriptorSet; + +namespace IPC { + +//------------------------------------------------------------------------------ + +class Channel; +class Message; +struct LogData; + +class IPC_EXPORT Message : public Pickle { + public: + // Implemented by objects that can send IPC messages across a channel. + class IPC_EXPORT Sender { + public: + virtual ~Sender() {} + + // Sends the given IPC message. The implementor takes ownership of the + // given Message regardless of whether or not this method succeeds. This + // is done to make this method easier to use. Returns true on success and + // false otherwise. + virtual bool Send(Message* msg) = 0; + }; + + enum PriorityValue { + PRIORITY_LOW = 1, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + virtual ~Message(); + + Message(); + + // Initialize a message with a user-defined type, priority value, and + // destination WebView ID. + Message(int32 routing_id, uint32 type, PriorityValue priority); + + // Initializes a message from a const block of data. The data is not copied; + // instead the data is merely referenced by this message. Only const methods + // should be used on the message when initialized this way. + Message(const char* data, int data_len); + + Message(const Message& other); + Message& operator=(const Message& other); + + PriorityValue priority() const { + return static_cast(header()->flags & PRIORITY_MASK); + } + + // True if this is a synchronous message. + bool is_sync() const { + return (header()->flags & SYNC_BIT) != 0; + } + + // Set this on a reply to a synchronous message. + void set_reply() { + header()->flags |= REPLY_BIT; + } + + bool is_reply() const { + return (header()->flags & REPLY_BIT) != 0; + } + + // Set this on a reply to a synchronous message to indicate that no receiver + // was found. + void set_reply_error() { + header()->flags |= REPLY_ERROR_BIT; + } + + bool is_reply_error() const { + return (header()->flags & REPLY_ERROR_BIT) != 0; + } + + // Normally when a receiver gets a message and they're blocked on a + // synchronous message Send, they buffer a message. Setting this flag causes + // the receiver to be unblocked and the message to be dispatched immediately. + void set_unblock(bool unblock) { + if (unblock) { + header()->flags |= UNBLOCK_BIT; + } else { + header()->flags &= ~UNBLOCK_BIT; + } + } + + bool should_unblock() const { + return (header()->flags & UNBLOCK_BIT) != 0; + } + + // Tells the receiver that the caller is pumping messages while waiting + // for the result. + bool is_caller_pumping_messages() const { + return (header()->flags & PUMPING_MSGS_BIT) != 0; + } + + uint32 type() const { + return header()->type; + } + + int32 routing_id() const { + return header()->routing; + } + + void set_routing_id(int32 new_id) { + header()->routing = new_id; + } + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)()) { + (obj->*func)(); + return true; + } + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)() const) { + (obj->*func)(); + return true; + } + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&)) { + (obj->*func)(*msg); + return true; + } + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&) const) { + (obj->*func)(*msg); + return true; + } + + // Used for async messages with no parameters. + static void Log(std::string* name, const Message* msg, std::string* l) { + } + + // Find the end of the message data that starts at range_start. Returns NULL + // if the entire message is not found in the given data range. + static const char* FindNext(const char* range_start, const char* range_end) { + return Pickle::FindNext(sizeof(Header), range_start, range_end); + } + +#if defined(OS_POSIX) + // On POSIX, a message supports reading / writing FileDescriptor objects. + // This is used to pass a file descriptor to the peer of an IPC channel. + + // Add a descriptor to the end of the set. Returns false iff the set is full. +// bool WriteFileDescriptor(const base::FileDescriptor& descriptor); + // Get a file descriptor from the message. Returns false on error. + // iter: a Pickle iterator to the current location in the message. +// bool ReadFileDescriptor(void** iter, base::FileDescriptor* descriptor) const; +#endif + +#ifdef IPC_MESSAGE_LOG_ENABLED + // Adds the outgoing time from Time::Now() at the end of the message and sets + // a bit to indicate that it's been added. + void set_sent_time(int64 time); + int64 sent_time() const; + + void set_received_time(int64 time) const; + int64 received_time() const { return received_time_; } + void set_output_params(const std::string& op) const { output_params_ = op; } + const std::string& output_params() const { return output_params_; } + // The following four functions are needed so we can log sync messages with + // delayed replies. We stick the log data from the sent message into the + // reply message, so that when it's sent and we have the output parameters + // we can log it. As such, we set a flag on the sent message to not log it. + void set_sync_log_data(LogData* data) const { log_data_ = data; } + LogData* sync_log_data() const { return log_data_; } + void set_dont_log() const { dont_log_ = true; } + bool dont_log() const { return dont_log_; } +#endif + + protected: + friend class Channel; + friend class MessageReplyDeserializer; + friend class SyncMessage; + + void set_sync() { + header()->flags |= SYNC_BIT; + } + + // flags + enum { + PRIORITY_MASK = 0x0003, + SYNC_BIT = 0x0004, + REPLY_BIT = 0x0008, + REPLY_ERROR_BIT = 0x0010, + UNBLOCK_BIT = 0x0020, + PUMPING_MSGS_BIT= 0x0040, + HAS_SENT_TIME_BIT = 0x0080, + }; + +#pragma pack(push, 4) + struct Header : Pickle::Header { + int32 routing; // ID of the view that this message is destined for + uint32 type; // specifies the user-defined message type + uint32 flags; // specifies control flags for the message +#if defined(OS_POSIX) + uint16 num_fds; // the number of descriptors included with this message + uint16 pad; // explicitly initialize this to appease valgrind +#endif + }; +#pragma pack(pop) + + Header* header() { + return headerT
(); + } + const Header* header() const { + return headerT
(); + } + + void InitLoggingVariables(); + +#if defined(OS_POSIX) + // The set of file descriptors associated with this message. +// scoped_refptr file_descriptor_set_; + + // Ensure that a FileDescriptorSet is allocated +// void EnsureFileDescriptorSet(); + +// FileDescriptorSet* file_descriptor_set() { +// EnsureFileDescriptorSet(); +// return file_descriptor_set_.get(); +// } +// const FileDescriptorSet* file_descriptor_set() const { +// return file_descriptor_set_.get(); +// } +#endif + +#ifdef IPC_MESSAGE_LOG_ENABLED + // Used for logging. + mutable int64 received_time_; + mutable std::string output_params_; + mutable LogData* log_data_; + mutable bool dont_log_; +#endif +}; + +//------------------------------------------------------------------------------ + +} // namespace IPC + +enum SpecialRoutingIDs { + // indicates that we don't have a routing ID yet. + MSG_ROUTING_NONE = -2, + + // indicates a general message not sent to a particular tab. + MSG_ROUTING_CONTROL = kint32max, +}; + +#define IPC_REPLY_ID 0xFFFFFFF0 // Special message id for replies +#define IPC_LOGGING_ID 0xFFFFFFF1 // Special message id for logging + +#endif // IPC_IPC_MESSAGE_H_ diff --git a/ipc/ipc_message_macros.h b/ipc/ipc_message_macros.h new file mode 100644 index 0000000..302d125 --- /dev/null +++ b/ipc/ipc_message_macros.h @@ -0,0 +1,822 @@ +// 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. + +// Defining IPC Messages +// +// Your IPC messages will be defined by macros inside of an XXX_messages.h +// header file. Most of the time, the system can automatically generate all +// of messaging mechanism from these definitions, but sometimes some manual +// coding is required. In these cases, you will also have an XXX_messages.cc +// implemation file as well. +// +// The senders of your messages will include your XXX_messages.h file to +// get the full set of definitions they need to send your messages. +// +// Each XXX_messages.h file must be registered with the IPC system. This +// requires adding two things: +// - An XXXMsgStart value to the IPCMessageStart enum in ipc_message_utils.h +// - An inclusion of XXX_messages.h file in a message generator .h file +// +// The XXXMsgStart value is an enumeration that ensures uniqueness for +// each different message file. Later, you will use this inside your +// XXX_messages.h file before invoking message declatation macros: +// #define IPC_MESSAGE_START XXXMsgStart +// ( ... your macro invocations go here ... ) +// +// Message Generator Files +// +// A message generator .h header file pulls in all other message-declaring +// headers for a given component. It is included by a message generator +// .cc file, which is where all the generated code will wind up. Typically, +// you will use an existing generator (e.g. common_message_generator.cc +// in /chrome/common), but there are circumstances where you may add a +// new one. +// +// In the rare cicrucmstances where you can't re-use an existing file, +// your YYY_message_generator.cc file for a component YYY would contain +// the following code: +// // Get basic type definitions. +// #define IPC_MESSAGE_IMPL +// #include "path/to/YYY_message_generator.h" +// // Generate constructors. +// #include "ipc/struct_constructor_macros.h" +// #include "path/to/YYY_message_generator.h" +// // Generate destructors. +// #include "ipc/struct_destructor_macros.h" +// #include "path/to/YYY_message_generator.h" +// // Generate param traits write methods. +// #include "ipc/param_traits_write_macros.h" +// namespace IPC { +// #include "path/to/YYY_message_generator.h" +// } // namespace IPC +// // Generate param traits read methods. +// #include "ipc/param_traits_read_macros.h" +// namespace IPC { +// #include "path/to/YYY_message_generator.h" +// } // namespace IPC +// // Generate param traits log methods. +// #include "ipc/param_traits_log_macros.h" +// namespace IPC { +// #include "path/to/YYY_message_generator.h" +// } // namespace IPC +// +// In cases where manual generation is required, in your XXX_messages.cc +// file, put the following after all the includes for param types: +// #define IPC_MESSAGE_IMPL +// #include "XXX_messages.h" +// (... implementation of traits not auto-generated ...) +// +// Multiple Inclusion +// +// The XXX_messages.h file will be multiply-included by the +// YYY_message_generator.cc file, so your XXX_messages file can't be +// guarded in the usual manner. Ideally, there will be no need for any +// inclusion guard, since the XXX_messages.h file should consist soley +// of inclusions of other headers (which are self-guarding) and IPC +// macros (which are multiply evaluating). +// +// Note that there is no #pragma once either; doing so would mark the whole +// file as being singly-included. Since your XXX_messages.h file is only +// partially-guarded, care must be taken to ensure that it is only included +// by other .cc files (and the YYY_message_generator.h file). Including an +// XXX_messages.h file in some other .h file may result in duplicate +// declarations and a compilation failure. +// +// Type Declarations +// +// It is generally a bad idea to have type definitions in a XXX_messages.h +// file; most likely the typedef will then be used in the message, as opposed +// to the struct iself. Later, an IPC message dispatcher wil need to call +// a function taking that type, and that function is declared in some other +// header. Thus, in order to get the type definition, the other header +// would have to include the XXX_messages.h file, violating the rule above +// about not including XXX_messages.h file in other .h files. +// +// One approach here is to move these type definitions to another (guarded) +// .h file and include this second .h in your XXX_messages.h file. This +// is still less than ideal, because the dispatched function would have to +// redeclare the typedef or include this second header. This may be +// reasonable in a few cases. +// +// Failing all of the above, then you will want to bracket the smallest +// possible section of your XXX_messages.h file containing these types +// with an include guard macro. Be aware that providing an incomplete +// class type declaration to avoid pulling in a long chain of headers is +// acceptable when your XXX_messages.h header is being included by the +// message sending caller's code, but not when the YYY_message_generator.c +// is building the messages. In addtion, due to the multiple inclusion +// restriction, these type ought to be guarded. Follow a convention like: +// #ifndef SOME_GUARD_MACRO +// #define SOME_GUARD_MACRO +// class some_class; // One incomplete class declaration +// class_some_other_class; // Another incomplete class declaration +// #endif // SOME_GUARD_MACRO +// #ifdef IPC_MESSAGE_IMPL +// #inlcude "path/to/some_class.h" // Full class declaration +// #inlcude "path/to/some_other_class.h" // Full class declaration +// #endif // IPC_MESSAGE_IMPL +// (.. IPC macros using some_class and some_other_class ...) +// +// Macro Invocations +// +// You will use IPC message macro invocations for three things: +// - New struct definitions for IPC +// - Registering existing struct and enum definitions with IPC +// - Defining the messages themselves +// +// New structs are defined with IPC_STRUCT_BEGIN(), IPC_STRUCT_MEMBER(), +// IPC_STRUCT_END() family of macros. These cause the XXX_messages.h +// to proclaim equivalent struct declarations for use by callers, as well +// as later registering the type with the message generation. Note that +// IPC_STRUCT_MEMBER() is only permitted inside matching calls to +// IPC_STRUCT_BEGIN() / IPC_STRUCT_END(). +// +// Externally-defined structs are registered with IPC_STRUCT_TRAITS_BEGIN(), +// IPC_STRUCT_TRAITS_MEMBER(), and IPC_STRUCT_TRAITS_END() macros. These +// cause registration of the types with message generation only. +// There's also IPC_STRUCT_TRAITS_PARENT, which is used to register a parent +// class (whose own traits are already defined). Note that +// IPC_STRUCT_TRAITS_MEMBER() and IPC_STRUCT_TRAITS_PARENT are only permitted +// inside matching calls to IPC_STRUCT_TRAITS_BEGIN() / +// IPC_STRUCT_TRAITS_END(). +// +// Enum types are registered with a single IPC_ENUM_TRAITS() macro. There +// is no need to enumerate each value to the IPC mechanism. +// +// Do not place semicolons following these IPC_ macro invocations. There +// is no reason to expect that their expansion corresponds one-to-one with +// C++ statements. +// +// Once the types have been declared / registered, message definitions follow. +// "Sync" messages are just synchronous calls, the Send() call doesn't return +// until a reply comes back. Input parameters are first (const TYPE&), and +// To declare a sync message, use the IPC_SYNC_ macros. The numbers at the +// end show how many input/output parameters there are (i.e. 1_2 is 1 in, 2 +// out). The caller does a Send([route id, ], in1, &out1, &out2). +// The receiver's handler function will be +// void OnSyncMessageName(const type1& in1, type2* out1, type3* out2) +// +// A caller can also send a synchronous message, while the receiver can respond +// at a later time. This is transparent from the sender's side. The receiver +// needs to use a different handler that takes in a IPC::Message* as the output +// type, stash the message, and when it has the data it can Send the message. +// +// Use the IPC_MESSAGE_HANDLER_DELAY_REPLY macro instead of IPC_MESSAGE_HANDLER +// IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_SyncMessageName, +// OnSyncMessageName) +// +// The handler function will look like: +// void OnSyncMessageName(const type1& in1, IPC::Message* reply_msg); +// +// Receiver stashes the IPC::Message* pointer, and when it's ready, it does: +// ViewHostMsg_SyncMessageName::WriteReplyParams(reply_msg, out1, out2); +// Send(reply_msg); + +#ifndef IPC_IPC_MESSAGE_MACROS_H_ +#define IPC_IPC_MESSAGE_MACROS_H_ + +#include "ipc/ipc_message_utils.h" +#include "ipc/param_traits_macros.h" + +#if defined(IPC_MESSAGE_IMPL) +#include "ipc/ipc_message_utils_impl.h" +#endif + +// Macros for defining structs. May be subsequently redefined. +#define IPC_STRUCT_BEGIN(struct_name) \ + struct struct_name; \ + IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + IPC_STRUCT_TRAITS_END() \ + struct struct_name : IPC::NoParams { \ + struct_name(); \ + ~struct_name(); +#define IPC_STRUCT_MEMBER(type, name) type name; +#define IPC_STRUCT_END() }; + +// Message macros collect specific numbers of arguments and funnel them into +// the common message generation macro. These should never be redefined. +#define IPC_MESSAGE_CONTROL0(msg_class) \ + IPC_MESSAGE_DECL(EMPTY, CONTROL, msg_class, 0, 0, (), ()) + +#define IPC_MESSAGE_CONTROL1(msg_class, type1) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 1, 0, (type1), ()) + +#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 2, 0, (type1, type2), ()) + +#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 3, 0, (type1, type2, type3), ()) + +#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 4, 0, (type1, type2, type3, type4), ()) + +#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 5, 0, (type1, type2, type3, type4, type5), ()) + +#define IPC_MESSAGE_CONTROL6(msg_class, type1, type2, type3, type4, type5, type6) \ + IPC_MESSAGE_DECL(ASYNC, CONTROL, msg_class, 6, 0, (type1, type2, type3, type4, type5, type6), ()) + + +#define IPC_MESSAGE_ROUTED0(msg_class) \ + IPC_MESSAGE_DECL(EMPTY, ROUTED, msg_class, 0, 0, (), ()) + +#define IPC_MESSAGE_ROUTED1(msg_class, type1) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 1, 0, (type1), ()) + +#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 2, 0, (type1, type2), ()) + +#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 3, 0, (type1, type2, type3), ()) + +#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 4, 0, (type1, type2, type3, type4), ()) + +#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 5, 0, (type1, type2, type3, type4, type5), ()) + +#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, type6) \ + IPC_MESSAGE_DECL(ASYNC, ROUTED, msg_class, 6, 0, (type1, type2, type3, type4, type5, type6), ()) + +#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 0, (), ()) + +#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 1, (), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 2, (), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 3, (), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL0_4(msg_class, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 0, 4, (), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 0, (type1_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 1, (type1_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 2, (type1_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 3, (type1_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 1, 4, (type1_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 0, (type1_in, type2_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 1, (type1_in, type2_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 2, (type1_in, type2_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 3, (type1_in, type2_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL2_4(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 2, 4, (type1_in, type2_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL3_0(msg_class, type1_in, type2_in, type3_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 0, (type1_in, type2_in, type3_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 1, (type1_in, type2_in, type3_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 2, (type1_in, type2_in, type3_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 3, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL3_4(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 3, 4, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 0, (type1_in, type2_in, type3_in, type4_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 2, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 3, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL4_4(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 4, 4, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 0, (type1_in, type2_in, type3_in, type4_in, type5_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_CONTROL5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 2, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_CONTROL5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 3, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_CONTROL5_4(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 5, 4, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_CONTROL6_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type6_in) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 6, 0, (type1_in, type2_in, type3_in, type4_in, type5_in, type6_in), ()) + +#define IPC_SYNC_MESSAGE_CONTROL6_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type6_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, CONTROL, msg_class, 6, 1, (type1_in, type2_in, type3_in, type4_in, type5_in, type6_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 0, (), ()) + +#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 1, (), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 2, (), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 3, (), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED0_4(msg_class, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 0, 4, (), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 0, (type1_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 1, (type1_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 2, (type1_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 3, (type1_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 1, 4, (type1_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 0, (type1_in, type2_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 1, (type1_in, type2_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 2, (type1_in, type2_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 3, (type1_in, type2_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED2_4(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 2, 4, (type1_in, type2_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 0, (type1_in, type2_in, type3_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 1, (type1_in, type2_in, type3_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 2, (type1_in, type2_in, type3_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 3, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED3_4(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 3, 4, (type1_in, type2_in, type3_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 1, (type1_in, type2_in, type3_in, type4_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 2, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED4_3(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 3, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED4_4(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 4, 4, (type1_in, type2_in, type3_in, type4_in), (type1_out, type2_out, type3_out, type4_out)) + +#define IPC_SYNC_MESSAGE_ROUTED5_0(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), ()) + +#define IPC_SYNC_MESSAGE_ROUTED5_1(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 1, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out)) + +#define IPC_SYNC_MESSAGE_ROUTED5_2(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 2, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out)) + +#define IPC_SYNC_MESSAGE_ROUTED5_3(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 3, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out)) + +#define IPC_SYNC_MESSAGE_ROUTED5_4(msg_class, type1_in, type2_in, type3_in, type4_in, type5_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_DECL(SYNC, ROUTED, msg_class, 5, 4, (type1_in, type2_in, type3_in, type4_in, type5_in), (type1_out, type2_out, type3_out, type4_out)) + +// Common message macro which dispatches into one of the 6 (sync x kind) +// routines. There is a way that these 6 cases can be lumped together, +// but the macros get very complicated in that case. +// Note: intended be redefined to generate other information. +#define IPC_MESSAGE_DECL(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) \ + IPC_##sync##_##kind##_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + IPC_MESSAGE_EXTRA(sync, kind, msg_class, in_cnt, out_cnt, in_list, out_list) + +#define IPC_EMPTY_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : public IPC::Message\ + { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class() : IPC::Message(MSG_ROUTING_CONTROL, ID, PRIORITY_NORMAL) {} \ + }; + +#define IPC_EMPTY_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : public IPC::Message\ + { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(int32 routing_id) \ + : IPC::Message(routing_id, ID, PRIORITY_NORMAL) {} \ + }; + +#define IPC_ASYNC_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : \ + public IPC::MessageWithTuple { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(IPC_TYPE_IN_##in_cnt in_list); \ + virtual ~msg_class(); \ + static void Log(std::string* name, const Message* msg, std::string* l); \ + }; + +#define IPC_ASYNC_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : \ + public IPC::MessageWithTuple { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(int32 routing_id IPC_COMMA_##in_cnt \ + IPC_TYPE_IN_##in_cnt in_list); \ + virtual ~msg_class(); \ + static void Log(std::string* name, const Message* msg, std::string* l); \ + }; + +#define IPC_SYNC_CONTROL_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : \ + public IPC::MessageWithReply { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(IPC_TYPE_IN_##in_cnt in_list \ + IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_OUT_##out_cnt out_list); \ + virtual ~msg_class(); \ + static void Log(std::string* name, const Message* msg, std::string* l); \ + }; + +#define IPC_SYNC_ROUTED_DECL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) msg_class : \ + public IPC::MessageWithReply { \ + public: \ + enum { ID = IPC_MESSAGE_ID() }; \ + msg_class(int32 routing_id \ + IPC_COMMA_OR_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_IN_##in_cnt in_list \ + IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_OUT_##out_cnt out_list); \ + virtual ~msg_class(); \ + static void Log(std::string* name, const Message* msg, std::string* l); \ + }; + +#if defined(IPC_MESSAGE_IMPL) + +// "Implementation" inclusion produces constructors, destructors, and +// logging functions, except for the no-arg special cases, where the +// implementation occurs in the declaration, and there is no special +// logging function. +#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) \ + IPC_##sync##_##kind##_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + IPC_##sync##_MESSAGE_LOG(msg_class) + +#define IPC_EMPTY_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) +#define IPC_EMPTY_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) + +#define IPC_ASYNC_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + msg_class::msg_class(IPC_TYPE_IN_##in_cnt in_list) : \ + IPC::MessageWithTuple \ + (MSG_ROUTING_CONTROL, ID, IPC_NAME_IN_##in_cnt in_list) \ + {} \ + msg_class::~msg_class() {} + +#define IPC_ASYNC_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + msg_class::msg_class(int32 routing_id IPC_COMMA_##in_cnt \ + IPC_TYPE_IN_##in_cnt in_list) : \ + IPC::MessageWithTuple \ + (routing_id, ID, IPC_NAME_IN_##in_cnt in_list) \ + {} \ + msg_class::~msg_class() {} + +#define IPC_SYNC_CONTROL_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + msg_class::msg_class(IPC_TYPE_IN_##in_cnt in_list \ + IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_OUT_##out_cnt out_list) : \ + IPC::MessageWithReply \ + (MSG_ROUTING_CONTROL, ID, \ + IPC_NAME_IN_##in_cnt in_list, \ + IPC_NAME_OUT_##out_cnt out_list) \ + { \ + } \ + msg_class::~msg_class() {} + +#define IPC_SYNC_ROUTED_IMPL(msg_class, in_cnt, out_cnt, in_list, out_list) \ + msg_class::msg_class(int32 routing_id \ + IPC_COMMA_OR_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_IN_##in_cnt in_list \ + IPC_COMMA_AND_##in_cnt(IPC_COMMA_##out_cnt) \ + IPC_TYPE_OUT_##out_cnt out_list) : \ + IPC::MessageWithReply \ + (routing_id, ID, \ + IPC_NAME_IN_##in_cnt in_list, \ + IPC_NAME_OUT_##out_cnt out_list) \ + {} \ + msg_class::~msg_class() {} + +#define IPC_EMPTY_MESSAGE_LOG(msg_class) + +#define IPC_ASYNC_MESSAGE_LOG(msg_class) \ + void msg_class::Log(std::string* name, \ + const Message* msg, \ + std::string* l) { \ + if (name) \ + { \ + *name = #msg_class; \ + } \ + if (!msg || !l) \ + { \ + return; \ + } \ + Param p; \ + if (Read(msg, &p)) \ + { \ + IPC::LogParam(p, l); \ + } \ + } + +#define IPC_SYNC_MESSAGE_LOG(msg_class) \ + void msg_class::Log(std::string* name, \ + const Message* msg, \ + std::string* l) { \ + if (name) \ + { \ + *name = #msg_class; \ + } \ + if (!msg || !l) \ + { \ + return; \ + } \ + if (msg->is_sync()) { \ + TupleTypes::ValueTuple p; \ + if (ReadSendParam(msg, &p)) \ + { \ + IPC::LogParam(p, l); \ + } \ + AddOutputParamsToLog(msg, l); \ + } else { \ + TupleTypes::ValueTuple p; \ + if (ReadReplyParam(msg, &p)) \ + { \ + IPC::LogParam(p, l); \ + } \ + } \ + } + +#elif defined(IPC_MESSAGE_MACROS_LOG_ENABLED) + +#ifndef IPC_LOG_TABLE_CREATED +#define IPC_LOG_TABLE_CREATED + +#include "base/hash_tables.h" + +typedef void (*LogFunction)(std::string* name, + const IPC::Message* msg, + std::string* params); + +typedef base::hash_map LogFunctionMap; +LogFunctionMap g_log_function_mapping; + +#endif // IPC_LOG_TABLE_CREATED + +// "Log table" inclusion produces extra logging registration code. +#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) \ + class __attribute__((visibility("default"))) LoggerRegisterHelper##msg_class { \ + public: \ + LoggerRegisterHelper##msg_class() { \ + g_log_function_mapping[msg_class::ID] = msg_class::Log; \ + } \ + }; \ + LoggerRegisterHelper##msg_class g_LoggerRegisterHelper##msg_class; + +#else + +// Normal inclusion produces nothing extra. +#define IPC_MESSAGE_EXTRA(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) + +#endif // defined(IPC_MESSAGE_IMPL) + +// Handle variable sized argument lists. These are usually invoked by token +// pasting against the argument counts. +#define IPC_TYPE_IN_0() +#define IPC_TYPE_IN_1(t1) const t1& arg1 +#define IPC_TYPE_IN_2(t1, t2) const t1& arg1, const t2& arg2 +#define IPC_TYPE_IN_3(t1, t2, t3) const t1& arg1, const t2& arg2, const t3& arg3 +#define IPC_TYPE_IN_4(t1, t2, t3, t4) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4 +#define IPC_TYPE_IN_5(t1, t2, t3, t4, t5) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4, const t5& arg5 +#define IPC_TYPE_IN_6(t1, t2, t3, t4, t5, t6) const t1& arg1, const t2& arg2, const t3& arg3, const t4& arg4, const t5& arg5, const t6& arg6 + +#define IPC_TYPE_OUT_0() +#define IPC_TYPE_OUT_1(t1) t1* arg7 +#define IPC_TYPE_OUT_2(t1, t2) t1* arg7, t2* arg8 +#define IPC_TYPE_OUT_3(t1, t2, t3) t1* arg7, t2* arg8, t3* arg9 +#define IPC_TYPE_OUT_4(t1, t2, t3, t4) t1* arg7, t2* arg8, t3* arg9, t4* arg10 + +#define IPC_TUPLE_IN_0() Tuple0 +#define IPC_TUPLE_IN_1(t1) Tuple1 +#define IPC_TUPLE_IN_2(t1, t2) Tuple2 +#define IPC_TUPLE_IN_3(t1, t2, t3) Tuple3 +#define IPC_TUPLE_IN_4(t1, t2, t3, t4) Tuple4 +#define IPC_TUPLE_IN_5(t1, t2, t3, t4, t5) Tuple5 +#define IPC_TUPLE_IN_6(t1, t2, t3, t4, t5, t6) Tuple6 + +#define IPC_TUPLE_OUT_0() Tuple0 +#define IPC_TUPLE_OUT_1(t1) Tuple1 +#define IPC_TUPLE_OUT_2(t1, t2) Tuple2 +#define IPC_TUPLE_OUT_3(t1, t2, t3) Tuple3 +#define IPC_TUPLE_OUT_4(t1, t2, t3, t4) Tuple4 + +#define IPC_NAME_IN_0() MakeTuple() +#define IPC_NAME_IN_1(t1) MakeRefTuple(arg1) +#define IPC_NAME_IN_2(t1, t2) MakeRefTuple(arg1, arg2) +#define IPC_NAME_IN_3(t1, t2, t3) MakeRefTuple(arg1, arg2, arg3) +#define IPC_NAME_IN_4(t1, t2, t3, t4) MakeRefTuple(arg1, arg2, arg3, arg4) +#define IPC_NAME_IN_5(t1, t2, t3, t4, t5) MakeRefTuple(arg1, arg2, arg3, arg4, arg5) +#define IPC_NAME_IN_6(t1, t2, t3, t4, t5, t6) MakeRefTuple(arg1, arg2, arg3, arg4, arg5, arg6) + +#define IPC_NAME_OUT_0() MakeTuple() +#define IPC_NAME_OUT_1(t1) MakeRefTuple(*arg7) +#define IPC_NAME_OUT_2(t1, t2) MakeRefTuple(*arg7, *arg8) +#define IPC_NAME_OUT_3(t1, t2, t3) MakeRefTuple(*arg7, *arg8, *arg9) +#define IPC_NAME_OUT_4(t1, t2, t3, t4) MakeRefTuple(*arg7, *arg8, *arg9, *arg10) + +// There are places where the syntax requires a comma if there are input args, +// if there are input args and output args, or if there are input args or +// output args. These macros allow generation of the comma as needed; invoke +// by token pasting against the argument counts. +#define IPC_COMMA_0 +#define IPC_COMMA_1 , +#define IPC_COMMA_2 , +#define IPC_COMMA_3 , +#define IPC_COMMA_4 , +#define IPC_COMMA_5 , +#define IPC_COMMA_6 , + +#define IPC_COMMA_AND_0(x) +#define IPC_COMMA_AND_1(x) x +#define IPC_COMMA_AND_2(x) x +#define IPC_COMMA_AND_3(x) x +#define IPC_COMMA_AND_4(x) x +#define IPC_COMMA_AND_5(x) x +#define IPC_COMMA_AND_6(x) x + +#define IPC_COMMA_OR_0(x) x +#define IPC_COMMA_OR_1(x) , +#define IPC_COMMA_OR_2(x) , +#define IPC_COMMA_OR_3(x) , +#define IPC_COMMA_OR_4(x) , +#define IPC_COMMA_OR_5(x) , +#define IPC_COMMA_OR_6(x) , + +// Message IDs +// Note: we currently use __LINE__ to give unique IDs to messages within +// a file. They're globally unique since each file defines its own +// IPC_MESSAGE_START. Ideally, we wouldn't use line numbers (a possibility +// is to instead use the __COUNTER__ macro, but it needs gcc 4.3 and xcode +// doesn't use it yet). +#define IPC_MESSAGE_ID() ((IPC_MESSAGE_START << 16) + __LINE__) +#define IPC_MESSAGE_ID_CLASS(id) ((id) >> 16) +#define IPC_MESSAGE_ID_LINE(id) ((id) & 0xffff) + +// Message crackers and handlers. +// Prefer to use the IPC_BEGIN_MESSAGE_MAP_EX to the older macros since they +// allow you to detect when a message could not be de-serialized. Usage: +// +// bool MyClass::OnMessageReceived(const IPC::Message& msg) { +// bool handled = true; +// bool msg_is_good = false; +// IPC_BEGIN_MESSAGE_MAP_EX(MyClass, msg, msg_is_good) +// IPC_MESSAGE_HANDLER(MsgClassOne, OnMsgClassOne) +// ...more handlers here ... +// IPC_MESSAGE_HANDLER(MsgClassTen, OnMsgClassTen) +// IPC_MESSAGE_UNHANDLED(handled = false) +// IPC_END_MESSAGE_MAP_EX() +// if (!msg_is_good) { +// // Signal error here or terminate offending process. +// } +// return handled; +// } + + +#define IPC_BEGIN_MESSAGE_MAP_EX(class_name, msg, msg_is_ok) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + const IPC::Message& ipc_message__ = msg; \ + bool& msg_is_ok__ = msg_is_ok; \ + switch (ipc_message__.type()) { \ + +#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + const IPC::Message& ipc_message__ = msg; \ + bool msg_is_ok__ = true; \ + switch (ipc_message__.type()) { \ + +#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func) \ + case msg_class::ID: \ + msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, this, &member_func); \ + break; + +#define IPC_MESSAGE_FORWARD_EX(msg_class, obj, sender, member_func) \ + case msg_class::ID: \ + msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, sender, &member_func); \ + break; + +#define IPC_MESSAGE_HANDLER(msg_class, member_func, sender) \ + IPC_MESSAGE_FORWARD_EX(msg_class, this, sender, _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_HANDLER_EX(msg_class, sender, member_func) \ + IPC_MESSAGE_FORWARD_EX(msg_class, this, sender, _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \ + case msg_class::ID: \ + msg_is_ok__ = msg_class::DispatchDelayReply(&ipc_message__, obj, &member_func); \ + break; + +#define IPC_MESSAGE_HANDLER_DELAY_REPLY(msg_class, member_func) \ + IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, this, \ + _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_HANDLER_GENERIC(msg_class, code) \ + case msg_class::ID: \ + code; \ + break; + +#define IPC_REPLY_HANDLER(func) \ + case IPC_REPLY_ID: \ + func(ipc_message__); \ + break; + + +#define IPC_MESSAGE_UNHANDLED(code) \ + default: \ + code; \ + break; + +#define IPC_MESSAGE_UNHANDLED_ERROR() \ + IPC_MESSAGE_UNHANDLED(NOTREACHED() << \ + "Invalid message with type = " << \ + ipc_message__.type()) + +#define IPC_END_MESSAGE_MAP() \ + } \ +} + +#define IPC_END_MESSAGE_MAP_EX() \ + } \ +} + +// This corresponds to an enum value from IPCMessageStart. +#define IPC_MESSAGE_CLASS(message) \ + IPC_MESSAGE_ID_CLASS(message.type()) + +#endif // IPC_IPC_MESSAGE_MACROS_H_ + +// Clean up IPC_MESSAGE_START in this unguarded section so that the +// XXX_messages.h files need not do so themselves. This makes the +// XXX_messages.h files easier to write. +#undef IPC_MESSAGE_START diff --git a/ipc/ipc_message_null_macros.h b/ipc/ipc_message_null_macros.h new file mode 100644 index 0000000..7cd02f5 --- /dev/null +++ b/ipc/ipc_message_null_macros.h @@ -0,0 +1,29 @@ +// 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. + +// No include guard, may be included multiple times. + +// NULL out all the macros that need NULLing, so that multiple includes of +// the XXXX_messages_internal.h files will not generate noise. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#undef IPC_ENUM_TRAITS +#undef IPC_MESSAGE_DECL + +#define IPC_STRUCT_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) +#define IPC_STRUCT_END() +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_TRAITS_PARENT(type) +#define IPC_STRUCT_TRAITS_END() +#define IPC_ENUM_TRAITS(enum_name) +#define IPC_MESSAGE_DECL(sync, kind, msg_class, \ + in_cnt, out_cnt, in_list, out_list) + diff --git a/ipc/ipc_message_utils.cc b/ipc/ipc_message_utils.cc new file mode 100644 index 0000000..2e3f042 --- /dev/null +++ b/ipc/ipc_message_utils.cc @@ -0,0 +1,485 @@ +// 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. + +#include "ipc/ipc_message_utils.h" + +//#include "base/file_path.h" +//#include "base/json/json_writer.h" +//#include "base/memory/scoped_ptr.h" +//#include "base/nullable_string16.h" +#include "base/string_number_conversions.h" +#include "base/time.h" +//#include "base/utf_string_conversions.h" +#include "base/values.h" +#if defined(OS_POSIX) +//#include "ipc/file_descriptor_set_posix.h" +#endif +//#include "ipc/ipc_channel_handle.h" + +namespace IPC { + +const int kMaxRecursionDepth = 100; + +// Value serialization + +/* +static bool ReadValue(const Message* m, void** iter, Value** value, + int recursion); + +static void WriteValue(Message* m, const Value* value, int recursion) { + if (recursion > kMaxRecursionDepth) { + LOG(WARNING) << "Max recursion depth hit in WriteValue."; + return; + } + + m->WriteInt(value->GetType()); + + switch (value->GetType()) { + case Value::TYPE_NULL: + break; + case Value::TYPE_BOOLEAN: { + bool val; + value->GetAsBoolean(&val); + WriteParam(m, val); + break; + } + case Value::TYPE_INTEGER: { + int val; + value->GetAsInteger(&val); + WriteParam(m, val); + break; + } + case Value::TYPE_DOUBLE: { + double val; + value->GetAsDouble(&val); + WriteParam(m, val); + break; + } + case Value::TYPE_STRING: { + std::string val; + value->GetAsString(&val); + WriteParam(m, val); + break; + } + case Value::TYPE_BINARY: { + const base::BinaryValue* binary = + static_cast(value); + m->WriteData(binary->GetBuffer(), static_cast(binary->GetSize())); + break; + } + case Value::TYPE_DICTIONARY: { + const DictionaryValue* dict = static_cast(value); + + WriteParam(m, static_cast(dict->size())); + + for (DictionaryValue::key_iterator it = dict->begin_keys(); + it != dict->end_keys(); ++it) { + Value* subval; + if (dict->GetWithoutPathExpansion(*it, &subval)) { + WriteParam(m, *it); + WriteValue(m, subval, recursion + 1); + } else { + NOTREACHED() << "DictionaryValue iterators are filthy liars."; + } + } + break; + } + case Value::TYPE_LIST: { + const ListValue* list = static_cast(value); + WriteParam(m, static_cast(list->GetSize())); + for (size_t i = 0; i < list->GetSize(); ++i) { + Value* subval; + if (list->Get(i, &subval)) { + WriteValue(m, subval, recursion + 1); + } else { + NOTREACHED() << "ListValue::GetSize is a filthy liar."; + } + } + break; + } + } +} + +// Helper for ReadValue that reads a DictionaryValue into a pre-allocated +// object. +static bool ReadDictionaryValue(const Message* m, void** iter, + DictionaryValue* value, int recursion) { + int size; + if (!ReadParam(m, iter, &size)) + return false; + + for (int i = 0; i < size; ++i) { + std::string key; + Value* subval; + if (!ReadParam(m, iter, &key) || + !ReadValue(m, iter, &subval, recursion + 1)) + return false; + value->Set(key, subval); + } + + return true; +} + +// Helper for ReadValue that reads a ReadListValue into a pre-allocated +// object. +static bool ReadListValue(const Message* m, void** iter, + ListValue* value, int recursion) { + int size; + if (!ReadParam(m, iter, &size)) + return false; + + for (int i = 0; i < size; ++i) { + Value* subval; + if (!ReadValue(m, iter, &subval, recursion + 1)) + return false; + value->Set(i, subval); + } + + return true; +} + +static bool ReadValue(const Message* m, void** iter, Value** value, + int recursion) { + if (recursion > kMaxRecursionDepth) { + LOG(WARNING) << "Max recursion depth hit in ReadValue."; + return false; + } + + int type; + if (!ReadParam(m, iter, &type)) + return false; + + switch (type) { + case Value::TYPE_NULL: + *value = Value::CreateNullValue(); + break; + case Value::TYPE_BOOLEAN: { + bool val; + if (!ReadParam(m, iter, &val)) + return false; + *value = Value::CreateBooleanValue(val); + break; + } + case Value::TYPE_INTEGER: { + int val; + if (!ReadParam(m, iter, &val)) + return false; + *value = Value::CreateIntegerValue(val); + break; + } + case Value::TYPE_DOUBLE: { + double val; + if (!ReadParam(m, iter, &val)) + return false; + *value = Value::CreateDoubleValue(val); + break; + } + case Value::TYPE_STRING: { + std::string val; + if (!ReadParam(m, iter, &val)) + return false; + *value = Value::CreateStringValue(val); + break; + } + case Value::TYPE_BINARY: { + const char* data; + int length; + if (!m->ReadData(iter, &data, &length)) + return false; + *value = base::BinaryValue::CreateWithCopiedBuffer(data, length); + break; + } + case Value::TYPE_DICTIONARY: { + scoped_ptr val(new DictionaryValue()); + if (!ReadDictionaryValue(m, iter, val.get(), recursion)) + return false; + *value = val.release(); + break; + } + case Value::TYPE_LIST: { + scoped_ptr val(new ListValue()); + if (!ReadListValue(m, iter, val.get(), recursion)) + return false; + *value = val.release(); + break; + } + default: + return false; + } + + return true; +} +*/ + +void ParamTraits::Log(const param_type& p, std::string* l) { + //l->append(base::IntToString(p)); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + //l->append(base::UintToString(p)); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + //l->append(base::Int64ToString(static_cast(p))); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + //l->append(base::Uint64ToString(static_cast(p))); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + //l->append(base::Int64ToString(static_cast(p))); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + //l->append(base::Uint64ToString(p)); +} + + +void ParamTraits::Write(Message* m, const param_type& p) { + m->WriteBytes(&p, sizeof(param_type)); +} + +bool ParamTraits::Read(const Message* m, void** iter, + param_type* r) { + const char* data; + if (!m->ReadBytes(iter, &data, sizeof(param_type))) + return false; + memcpy(r, data, sizeof(param_type)); + return true; +} + +/* +void ParamTraits::Log(const param_type& p, std::string* l) { + l->append(base::UintToString(p)); +} + + +void ParamTraits::Write(Message* m, const param_type& p) { + ParamTraits::Write(m, p.ToInternalValue()); +} + +bool ParamTraits::Read(const Message* m, void** iter, + param_type* r) { + int64 value; + if (!ParamTraits::Read(m, iter, &value)) + return false; + *r = base::Time::FromInternalValue(value); + return true; +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + ParamTraits::Log(p.ToInternalValue(), l); +} + +void ParamTraits ::Write(Message* m, const param_type& p) { + ParamTraits ::Write(m, p.InMicroseconds()); +} + +bool ParamTraits ::Read(const Message* m, + void** iter, + param_type* r) { + int64 value; + bool ret = ParamTraits ::Read(m, iter, &value); + if (ret) + *r = base::TimeDelta::FromMicroseconds(value); + + return ret; +} + +void ParamTraits ::Log(const param_type& p, std::string* l) { + ParamTraits ::Log(p.InMicroseconds(), l); +} + +void ParamTraits::Write(Message* m, const param_type& p) { + WriteValue(m, &p, 0); +} + +bool ParamTraits::Read( + const Message* m, void** iter, param_type* r) { + int type; + if (!ReadParam(m, iter, &type) || type != Value::TYPE_DICTIONARY) + return false; + + return ReadDictionaryValue(m, iter, r, 0); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + std::string json; + base::JSONWriter::Write(&p, false, &json); + l->append(json); +} + +void ParamTraits::Write(Message* m, const param_type& p) { + WriteValue(m, &p, 0); +} + +bool ParamTraits::Read( + const Message* m, void** iter, param_type* r) { + int type; + if (!ReadParam(m, iter, &type) || type != Value::TYPE_LIST) + return false; + + return ReadListValue(m, iter, r, 0); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + std::string json; + base::JSONWriter::Write(&p, false, &json); + l->append(json); +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + l->append(WideToUTF8(p)); +} + +void ParamTraits::Write(Message* m, const param_type& p) { + WriteParam(m, p.string()); + WriteParam(m, p.is_null()); +} + +bool ParamTraits::Read(const Message* m, void** iter, + param_type* r) { + string16 string; + if (!ReadParam(m, iter, &string)) + return false; + bool is_null; + if (!ReadParam(m, iter, &is_null)) + return false; + *r = NullableString16(string, is_null); + return true; +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + l->append("("); + LogParam(p.string(), l); + l->append(", "); + LogParam(p.is_null(), l); + l->append(")"); +} + +#if !defined(WCHAR_T_IS_UTF16) +void ParamTraits::Log(const param_type& p, std::string* l) { + l->append(UTF16ToUTF8(p)); +} +#endif + +*/ + +/* +void ParamTraits::Write(Message* m, const param_type& p) { + ParamTraits::Write(m, p.value()); +} + +bool ParamTraits::Read(const Message* m, void** iter, param_type* r) { + FilePath::StringType value; + if (!ParamTraits::Read(m, iter, &value)) + return false; + *r = FilePath(value); + return true; +} + +void ParamTraits::Log(const param_type& p, std::string* l) { + ParamTraits::Log(p.value(), l); +} + +#if defined(OS_POSIX) +void ParamTraits::Write(Message* m, const param_type& p) { + const bool valid = p.fd >= 0; + WriteParam(m, valid); + + if (valid) { + if (!m->WriteFileDescriptor(p)) + NOTREACHED(); + } +} + +bool ParamTraits::Read(const Message* m, void** iter, + param_type* r) { + bool valid; + if (!ReadParam(m, iter, &valid)) + return false; + + if (!valid) { + r->fd = -1; + r->auto_close = false; + return true; + } + + return m->ReadFileDescriptor(iter, r); +} + +void ParamTraits::Log(const param_type& p, + std::string* l) { + if (p.auto_close) { + l->append(StringPrintf("FD(%d auto-close)", p.fd)); + } else { + l->append(StringPrintf("FD(%d)", p.fd)); + } +} +#endif // defined(OS_POSIX) + +void ParamTraits::Write(Message* m, const param_type& p) { + WriteParam(m, p.name); +#if defined(OS_POSIX) + WriteParam(m, p.socket); +#endif +} + +bool ParamTraits::Read(const Message* m, void** iter, + param_type* r) { + return ReadParam(m, iter, &r->name) +#if defined(OS_POSIX) + && ReadParam(m, iter, &r->socket) +#endif + ; +} + +void ParamTraits::Log(const param_type& p, + std::string* l) { + l->append(StringPrintf("ChannelHandle(%s", p.name.c_str())); +#if defined(OS_POSIX) + ParamTraits::Log(p.socket, l); +#endif + l->append(")"); +} + +LogData::LogData() + : routing_id(0), + type(0), + sent(0), + receive(0), + dispatch(0) { +} + +LogData::~LogData() { +} + +void ParamTraits::Write(Message* m, const param_type& p) { + WriteParam(m, p.channel); + WriteParam(m, p.routing_id); + WriteParam(m, static_cast(p.type)); + WriteParam(m, p.flags); + WriteParam(m, p.sent); + WriteParam(m, p.receive); + WriteParam(m, p.dispatch); + WriteParam(m, p.params); +} + +bool ParamTraits::Read(const Message* m, void** iter, param_type* r) { + int type = -1; + bool result = + ReadParam(m, iter, &r->channel) && + ReadParam(m, iter, &r->routing_id) && + ReadParam(m, iter, &type) && + ReadParam(m, iter, &r->flags) && + ReadParam(m, iter, &r->sent) && + ReadParam(m, iter, &r->receive) && + ReadParam(m, iter, &r->dispatch) && + ReadParam(m, iter, &r->params); + r->type = static_cast(type); + return result; +} +*/ +} // namespace IPC diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h new file mode 100755 index 0000000..3b5ff6f --- /dev/null +++ b/ipc/ipc_message_utils.h @@ -0,0 +1,1248 @@ +// 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. + +#ifndef IPC_IPC_MESSAGE_UTILS_H_ +#define IPC_IPC_MESSAGE_UTILS_H_ +#pragma once + +#include +#include +#include +#include +#include + +#include "base/compiler_specific.h" +//#include "base/format_macros.h" +//#include "base/string16.h" +//#include "base/stringprintf.h" +//#include "base/string_util.h" +#include "base/tuple.h" +#include "ipc/ipc_param_traits.h" +#include "ipc/ipc_sync_message.h" + +#if defined(COMPILER_GCC) +// GCC "helpfully" tries to inline template methods in release mode. Except we +// want the majority of the template junk being expanded once in the +// implementation file (and only provide the definitions in +// ipc_message_utils_impl.h in those files) and exported, instead of expanded +// at every call site. Special note: GCC happily accepts the attribute before +// the method declaration, but only acts on it if it is after. +#if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40500 +// Starting in gcc 4.5, the noinline no longer implies the concept covered by +// the introduced noclone attribute, which will create specialized versions of +// functions/methods when certain types are constant. +// www.gnu.org/software/gcc/gcc-4.5/changes.html +#define IPC_MSG_NOINLINE __attribute__((noinline, noclone)); +#else +#define IPC_MSG_NOINLINE __attribute__((noinline)); +#endif +#elif defined(COMPILER_MSVC) +// MSVC++ doesn't do this. +#define IPC_MSG_NOINLINE +#else +#error "Please add the noinline property for your new compiler here." +#endif + +// Used by IPC_BEGIN_MESSAGES so that each message class starts from a unique +// base. Messages have unique IDs across channels in order for the IPC logging +// code to figure out the message class from its ID. +enum IPCMessageStart { + AutomationMsgStart = 0, + CmcStrRegistryManagerStart, + LastIPCMsgStart +}; + +class FilePath; +class NullableString16; + +namespace base { +class DictionaryValue; +class ListValue; +class Time; +class TimeDelta; +struct FileDescriptor; +} + +namespace IPC { + +struct ChannelHandle; + +//----------------------------------------------------------------------------- +// An iterator class for reading the fields contained within a Message. + +class MessageIterator { + public: + explicit MessageIterator(const Message& m) : msg_(m), iter_(NULL) { + } + int NextInt() const { + int val = -1; + if (!msg_.ReadInt(&iter_, &val)) + NOTREACHED(); + return val; + } +/* + const std::string NextString() const { + std::string val; + if (!msg_.ReadString(&iter_, &val)) + NOTREACHED(); + return val; + } + const std::wstring NextWString() const { + std::wstring val; + if (!msg_.ReadWString(&iter_, &val)) + NOTREACHED(); + return val; + } +*/ + void NextData(const char** data, int* length) const { + if (!msg_.ReadData(&iter_, data, length)) { + NOTREACHED(); + } + } + private: + const Message& msg_; + mutable void* iter_; +}; + +//----------------------------------------------------------------------------- +// A dummy struct to place first just to allow leading commas for all +// members in the macro-generated constructor initializer lists. +struct NoParams { +}; + +//----------------------------------------------------------------------------- +// ParamTraits specializations, etc. + +template +static inline void WriteParam(Message* m, const P& p) { + typedef typename SimilarTypeTraits

::Type Type; + ParamTraits::Write(m, static_cast(p)); +} + +template +static inline bool WARN_UNUSED_RESULT ReadParam(const Message* m, void** iter, + P* p) { + typedef typename SimilarTypeTraits

::Type Type; + return ParamTraits::Read(m, iter, reinterpret_cast(p)); +} + + +template +static inline void LogParam(const P& p, std::string* l) { + typedef typename SimilarTypeTraits

::Type Type; + ParamTraits::Log(static_cast(p), l); +} + + +template <> +struct ParamTraits { + typedef bool param_type; + static void Write(Message* m, const param_type& p) { + m->WriteBool(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadBool(iter, r); + } + static void Log(const param_type& p, std::string* l) { + l->append(p ? "true" : "false"); + } +}; + +template <> +struct ParamTraits { + typedef int param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt(iter, r); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits { + typedef unsigned int param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt(iter, reinterpret_cast(r)); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits { + typedef long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteLong(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadLong(iter, r); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits { + typedef unsigned long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteLong(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadLong(iter, reinterpret_cast(r)); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits { + typedef long long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(static_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt64(iter, reinterpret_cast(r)); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct ParamTraits { + typedef unsigned long long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt64(iter, reinterpret_cast(r)); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template <> +struct IPC_EXPORT ParamTraits { + typedef unsigned short param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +// Note that the IPC layer doesn't sanitize NaNs and +/- INF values. Clients +// should be sure to check the sanity of these values after receiving them over +// IPC. +template <> +struct ParamTraits { + typedef float param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(param_type)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size; + if (!m->ReadData(iter, &data, &data_size) || + data_size != sizeof(param_type)) { + NOTREACHED(); + return false; + } + memcpy(r, data, sizeof(param_type)); + return true; + } + + static void Log(const param_type& p, std::string* l) { + //l->append(StringPrintf("%e", p)); + } + +}; + +template <> +struct ParamTraits { + typedef double param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(param_type)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size; + if (!m->ReadData(iter, &data, &data_size) || + data_size != sizeof(param_type)) { + NOTREACHED(); + return false; + } + memcpy(r, data, sizeof(param_type)); + return true; + } + + static void Log(const param_type& p, std::string* l) { + //l->append(StringPrintf("%e", p)); + } + +}; + +/* +template <> +struct IPC_EXPORT ParamTraits { + typedef base::Time param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct IPC_EXPORT ParamTraits { + typedef base::TimeDelta param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +#if defined(OS_WIN) +template <> +struct ParamTraits { + typedef LOGFONT param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(LOGFONT)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(LOGFONT)) { + memcpy(r, data, sizeof(LOGFONT)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::string* l) { +// l->append(StringPrintf("")); + } +}; + +template <> +struct ParamTraits { + typedef MSG param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(MSG)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(MSG)) { + memcpy(r, data, sizeof(MSG)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::string* l) { + l->append(""); + } +}; +#endif // defined(OS_WIN) + +template <> +struct IPC_EXPORT ParamTraits { + typedef base::DictionaryValue param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> +struct IPC_EXPORT ParamTraits { + typedef base::ListValue param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +*/ +template <> +struct ParamTraits { + typedef std::string param_type; + static void Write(Message* m, const param_type& p) { + m->WriteString(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadString(iter, r); + } + static void Log(const param_type& p, std::string* l) { + l->append(p); + } +}; + +/* +template +static void LogBytes(const std::vector& data, std::string* out) { +#if defined(OS_WIN) + // Windows has a GUI for logging, which can handle arbitrary binary data. + for (size_t i = 0; i < data.size(); ++i) + out->push_back(data[i]); +#else + // On POSIX, we log to stdout, which we assume can display ASCII. + static const size_t kMaxBytesToLog = 100; + for (size_t i = 0; i < std::min(data.size(), kMaxBytesToLog); ++i) { + if (isprint(data[i])) + out->push_back(data[i]); + else + { + // out->append(StringPrintf("[%02X]", static_cast(data[i]))); + } + } + if (data.size() > kMaxBytesToLog) { +// out->append( +// StringPrintf(" and %u more bytes", +// static_cast(data.size() - kMaxBytesToLog))); + } +#endif +} +*/ + +template <> +struct ParamTraits > { + typedef std::vector param_type; + static void Write(Message* m, const param_type& p) { + if (p.empty()) { + m->WriteData(NULL, 0); + } else { + m->WriteData(reinterpret_cast(&p.front()), + static_cast(p.size())); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + if (!m->ReadData(iter, &data, &data_size) || data_size < 0) + return false; + r->resize(data_size); + if (data_size) + memcpy(&r->front(), data, data_size); + return true; + } + static void Log(const param_type& p, std::string* l) { + // LogBytes(p, l); + } +}; + +template <> +struct ParamTraits > { + typedef std::vector param_type; + static void Write(Message* m, const param_type& p) { + if (p.empty()) { + m->WriteData(NULL, 0); + } else { + m->WriteData(&p.front(), static_cast(p.size())); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + if (!m->ReadData(iter, &data, &data_size) || data_size < 0) + return false; + r->resize(data_size); + if (data_size) + memcpy(&r->front(), data, data_size); + return true; + } + static void Log(const param_type& p, std::string* l) { + //LogBytes(p, l); + } +}; + +template +struct ParamTraits > { + typedef std::vector

param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast(p.size())); + for (size_t i = 0; i < p.size(); i++) + WriteParam(m, p[i]); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + // ReadLength() checks for < 0 itself. + if (!m->ReadLength(iter, &size)) + return false; + // Resizing beforehand is not safe, see BUG 1006367 for details. + if (INT_MAX / sizeof(P) <= static_cast(size)) + return false; + r->resize(size); + for (int i = 0; i < size; i++) { + if (!ReadParam(m, iter, &(*r)[i])) + return false; + } + return true; + } + static void Log(const param_type& p, std::string* l) { +// for (size_t i = 0; i < p.size(); ++i) { +// if (i != 0) +// l->append(" "); +// LogParam((p[i]), l); +// } + } +}; + +/* +template +struct ParamTraits > { + typedef std::set

param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast(p.size())); + typename param_type::const_iterator iter; + for (iter = p.begin(); iter != p.end(); ++iter) + WriteParam(m, *iter); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + if (!m->ReadLength(iter, &size)) + return false; + for (int i = 0; i < size; ++i) { + P item; + if (!ReadParam(m, iter, &item)) + return false; + r->insert(item); + } + return true; + } + static void Log(const param_type& p, std::string* l) { + l->append(""); + } +}; + + +template +struct ParamTraits > { + typedef std::map param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast(p.size())); + typename param_type::const_iterator iter; + for (iter = p.begin(); iter != p.end(); ++iter) { + WriteParam(m, iter->first); + WriteParam(m, iter->second); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + if (!ReadParam(m, iter, &size) || size < 0) + return false; + for (int i = 0; i < size; ++i) { + K k; + if (!ReadParam(m, iter, &k)) + return false; + V& value = (*r)[k]; + if (!ReadParam(m, iter, &value)) + return false; + } + return true; + } + static void Log(const param_type& p, std::string* l) { + l->append(""); + } +}; + + +template <> +struct ParamTraits { + typedef std::wstring param_type; + static void Write(Message* m, const param_type& p) { + m->WriteWString(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadWString(iter, r); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; + +template +struct ParamTraits > { + typedef std::pair param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.first); + WriteParam(m, p.second); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return ReadParam(m, iter, &r->first) && ReadParam(m, iter, &r->second); + } + static void Log(const param_type& p, std::string* l) { + l->append("("); + LogParam(p.first, l); + l->append(", "); + LogParam(p.second, l); + l->append(")"); + } +}; + +template <> +struct IPC_EXPORT ParamTraits { + typedef NullableString16 param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +// If WCHAR_T_IS_UTF16 is defined, then string16 is a std::wstring so we don't +// need this trait. +#if !defined(WCHAR_T_IS_UTF16) +template <> +struct ParamTraits { + typedef string16 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteString16(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadString16(iter, r); + } + IPC_EXPORT static void Log(const param_type& p, std::string* l); +}; +#endif + +// and, a few more useful types... +#if defined(OS_WIN) +template <> +struct ParamTraits { + typedef HANDLE param_type; + static void Write(Message* m, const param_type& p) { + // Note that HWNDs/HANDLE/HCURSOR/HACCEL etc are always 32 bits, even on 64 + // bit systems. + m->WriteUInt32(reinterpret_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(uint32)); + return m->ReadUInt32(iter, reinterpret_cast(r)); + } + static void Log(const param_type& p, std::string* l) { +// l->append(StringPrintf("0x%X", p)); + } +}; + +template <> +struct ParamTraits { + typedef HCURSOR param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt32(reinterpret_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(uint32)); + return m->ReadUInt32(iter, reinterpret_cast(r)); + } + static void Log(const param_type& p, std::string* l) { +// l->append(StringPrintf("0x%X", p)); + } +}; + +template <> +struct ParamTraits { + typedef HACCEL param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt32(reinterpret_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(uint32)); + return m->ReadUInt32(iter, reinterpret_cast(r)); + } +}; + +template <> +struct ParamTraits { + typedef POINT param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p.x); + m->WriteInt(p.y); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int x, y; + if (!m->ReadInt(iter, &x) || !m->ReadInt(iter, &y)) + return false; + r->x = x; + r->y = y; + return true; + } + static void Log(const param_type& p, std::string* l) { +// l->append(StringPrintf("(%d, %d)", p.x, p.y)); + } +}; +#endif // defined(OS_WIN) + +template <> +struct IPC_EXPORT ParamTraits { + typedef FilePath param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +#if defined(OS_POSIX) +// FileDescriptors may be serialised over IPC channels on POSIX. On the +// receiving side, the FileDescriptor is a valid duplicate of the file +// descriptor which was transmitted: *it is not just a copy of the integer like +// HANDLEs on Windows*. The only exception is if the file descriptor is < 0. In +// this case, the receiving end will see a value of -1. *Zero is a valid file +// descriptor*. +// +// The received file descriptor will have the |auto_close| flag set to true. The +// code which handles the message is responsible for taking ownership of it. +// File descriptors are OS resources and must be closed when no longer needed. +// +// When sending a file descriptor, the file descriptor must be valid at the time +// of transmission. Since transmission is not synchronous, one should consider +// dup()ing any file descriptors to be transmitted and setting the |auto_close| +// flag, which causes the file descriptor to be closed after writing. +template<> +struct IPC_EXPORT ParamTraits { + typedef base::FileDescriptor param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; +#endif // defined(OS_POSIX) + +// A ChannelHandle is basically a platform-inspecific wrapper around the +// fact that IPC endpoints are handled specially on POSIX. See above comments +// on FileDescriptor for more background. +template<> +struct IPC_EXPORT ParamTraits { + typedef ChannelHandle param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +#if defined(OS_WIN) +template <> +struct ParamTraits { + typedef XFORM param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(XFORM)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(XFORM)) { + memcpy(r, data, sizeof(XFORM)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::string* l) { + l->append(""); + } +}; +#endif // defined(OS_WIN) + +struct IPC_EXPORT LogData { + LogData(); + ~LogData(); + + std::string channel; + int32 routing_id; + uint32 type; // "User-defined" message type, from ipc_message.h. + std::string flags; + int64 sent; // Time that the message was sent (i.e. at Send()). + int64 receive; // Time before it was dispatched (i.e. before calling + // OnMessageReceived). + int64 dispatch; // Time after it was dispatched (i.e. after calling + // OnMessageReceived). + std::string message_name; + std::string params; +}; + +template <> +struct IPC_EXPORT ParamTraits { + typedef LogData param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::string* l) { + // Doesn't make sense to implement this! + } +}; + +*/ +template <> +struct ParamTraits { + static void Write(Message* m, const Message& p) { + DCHECK(p.size() <= INT_MAX); + int message_size = static_cast(p.size()); + m->WriteInt(message_size); + m->WriteData(reinterpret_cast(p.data()), message_size); + } + static bool Read(const Message* m, void** iter, Message* r) { + int size; + if (!m->ReadInt(iter, &size)) + return false; + const char* data; + if (!m->ReadData(iter, &data, &size)) + return false; + *r = Message(data, size); + return true; + } + static void Log(const Message& p, std::string* l) { + l->append(""); + } +}; + +template <> +struct ParamTraits { + typedef Tuple0 param_type; + static void Write(Message* m, const param_type& p) { + } + static bool Read(const Message* m, void** iter, param_type* r) { + return true; + } + static void Log(const param_type& p, std::string* l) { + } +}; + +template +struct ParamTraits< Tuple1 > { + typedef Tuple1 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return ReadParam(m, iter, &r->a); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + } +}; + +template +struct ParamTraits< Tuple2 > { + typedef Tuple2 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + } +}; + +template +struct ParamTraits< Tuple3 > { + typedef Tuple3 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + l->append(", "); + LogParam(p.c, l); + } +}; + +template +struct ParamTraits< Tuple4 > { + typedef Tuple4 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + l->append(", "); + LogParam(p.c, l); + l->append(", "); + LogParam(p.d, l); + } +}; + +template +struct ParamTraits< Tuple5 > { + typedef Tuple5 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + WriteParam(m, p.e); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d) && + ReadParam(m, iter, &r->e)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + l->append(", "); + LogParam(p.c, l); + l->append(", "); + LogParam(p.d, l); + l->append(", "); + LogParam(p.e, l); + } +}; + +template +struct ParamTraits< Tuple6 > { +typedef Tuple6 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + WriteParam(m, p.e); + WriteParam(m, p.f); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d) && + ReadParam(m, iter, &r->e) && + ReadParam(m, iter, &r->f)); + } + static void Log(const param_type& p, std::string* l) { + LogParam(p.a, l); + l->append(", "); + LogParam(p.b, l); + l->append(", "); + LogParam(p.c, l); + l->append(", "); + LogParam(p.d, l); + l->append(", "); + LogParam(p.e, l); + l->append(", "); + LogParam(p.f, l); + } +}; + +//----------------------------------------------------------------------------- +// Generic message subclasses + +// Used for asynchronous messages. +template +class __attribute__((visibility("default"))) MessageWithTuple : public Message { + public: + typedef ParamType Param; + typedef typename TupleTypes::ParamTuple RefParam; + + // The constructor and the Read() method's templated implementations are in + // ipc_message_utils_impl.h. The subclass constructor and Log() methods call + // the templated versions of these and make sure there are instantiations in + // those translation units. + MessageWithTuple(int32 routing_id, uint32 type, const RefParam& p); + + static bool Read(const Message* msg, Param* p) + { // Put definition into class so that Dispatch method could use it. + void* iter = NULL; + if (ReadParam(msg, &iter, p)) + return true; + NOTREACHED() << "Error deserializing message " << msg->type(); + return false; + } IPC_MSG_NOINLINE; + + // Generic dispatcher. Should cover most cases. + template + static bool Dispatch(const Message* msg, T* obj, S* sender, Method func) { + Param p; + if (Read(msg, &p)) { + DispatchToMethod(obj, func, p); + return true; + } + return false; + } + + // The following dispatchers exist for the case where the callback function + // needs the message as well. They assume that "Param" is a type of Tuple + // (except the one arg case, as there is no Tuple1). + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a); + return true; + } + return false; + } + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA, TB)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b); + return true; + } + return false; + } + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA, TB, TC)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c); + return true; + } + return false; + } + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA, TB, TC, TD)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c, p.d); + return true; + } + return false; + } + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, + void (T::*func)(const Message&, TA, TB, TC, TD, TE)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c, p.d, p.e); + return true; + } + return false; + } + + // Functions used to do manual unpacking. Only used by the automation code, + // these should go away once that code uses SyncChannel. + template + static bool Read(const IPC::Message* msg, TA* a, TB* b) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + return true; + } + + template + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + return true; + } + + template + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + *d = params.d; + return true; + } + + template + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d, TE* e) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + *d = params.d; + *e = params.e; + return true; + } +}; + +// defined in ipc_logging.cc +IPC_EXPORT void GenerateLogData(const std::string& channel, + const Message& message, + LogData* data); + + +#if defined(IPC_MESSAGE_LOG_ENABLED) +inline void AddOutputParamsToLog(const Message* msg, std::string* l) { + const std::string& output_params = msg->output_params(); + if (!l->empty() && !output_params.empty()) + l->append(", "); + + l->append(output_params); +} + +template +inline void LogReplyParamsToMessage(const ReplyParamType& reply_params, + const Message* msg) { + if (msg->received_time() != 0) { + std::string output_params; + LogParam(reply_params, &output_params); + msg->set_output_params(output_params); + } +} + +inline void ConnectMessageAndReply(const Message* msg, Message* reply) { + if (msg->sent_time()) { + // Don't log the sync message after dispatch, as we don't have the + // output parameters at that point. Instead, save its data and log it + // with the outgoing reply message when it's sent. + LogData* data = new LogData; + GenerateLogData("", *msg, data); + msg->set_dont_log(); + reply->set_sync_log_data(data); + } +} +#else +inline void AddOutputParamsToLog(const Message* msg, std::string* l) {} + +template +inline void LogReplyParamsToMessage(const ReplyParamType& reply_params, + const Message* msg) {} + +inline void ConnectMessageAndReply(const Message* msg, Message* reply) {} +#endif + +// This class assumes that its template argument is a RefTuple (a Tuple with +// reference elements). This would go into ipc_message_utils_impl.h, but it is +// also used by chrome_frame. +template +class ParamDeserializer : public MessageReplyDeserializer { + public: + explicit ParamDeserializer(const RefTuple& out) : out_(out) { } + + bool SerializeOutputParameters(const IPC::Message& msg, void* iter) { + return ReadParam(&msg, &iter, &out_); + } + + RefTuple out_; +}; + +// Used for synchronous messages. +template +class __attribute__((visibility("default"))) MessageWithReply : public SyncMessage { + public: + typedef SendParamType SendParam; + typedef typename TupleTypes::ParamTuple RefSendParam; + typedef ReplyParamType ReplyParam; + + MessageWithReply(int32 routing_id, uint32 type, + const RefSendParam& send, const ReplyParam& reply); + static bool ReadSendParam(const Message* msg, SendParam* p) + { // Put definition into class so that Dispatch method could use it. + void* iter = SyncMessage::GetDataIterator(msg); + return ReadParam(msg, &iter, p); + } IPC_MSG_NOINLINE; + static bool ReadReplyParam( + const Message* msg, + typename TupleTypes::ValueTuple* p) IPC_MSG_NOINLINE; + + template + static bool Dispatch(const Message* msg, T* obj, S* sender, Method func) { + SendParam send_params; + Message* reply = GenerateReply(msg); + bool error; + if (ReadSendParam(msg, &send_params)) { + typename TupleTypes::ValueTuple reply_params; + DispatchToMethod(obj, func, send_params, &reply_params); + WriteParam(reply, reply_params); + error = false; + LogReplyParamsToMessage(reply_params, msg); + } else { + NOTREACHED() << "Error deserializing message " << msg->type(); + reply->set_reply_error(); + error = true; + } + + sender->Send(reply); + return !error; + } + + template + static bool DispatchDelayReply(const Message* msg, T* obj, Method func) { + SendParam send_params; + Message* reply = GenerateReply(msg); + bool error; + if (ReadSendParam(msg, &send_params)) { + Tuple1 t = MakeRefTuple(*reply); + ConnectMessageAndReply(msg, reply); + DispatchToMethod(obj, func, send_params, &t); + error = false; + } else { + NOTREACHED() << "Error deserializing message " << msg->type(); + reply->set_reply_error(); + obj->Send(reply); + error = true; + } + return !error; + } + + template + static void WriteReplyParams(Message* reply, TA a) { + ReplyParam p(a); + WriteParam(reply, p); + } + + template + static void WriteReplyParams(Message* reply, TA a, TB b) { + ReplyParam p(a, b); + WriteParam(reply, p); + } + + template + static void WriteReplyParams(Message* reply, TA a, TB b, TC c) { + ReplyParam p(a, b, c); + WriteParam(reply, p); + } + + template + static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d) { + ReplyParam p(a, b, c, d); + WriteParam(reply, p); + } + + template + static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d, TE e) { + ReplyParam p(a, b, c, d, e); + WriteParam(reply, p); + } +}; + +//----------------------------------------------------------------------------- + +} // namespace IPC + +#endif // IPC_IPC_MESSAGE_UTILS_H_ diff --git a/ipc/ipc_message_utils_impl.h b/ipc/ipc_message_utils_impl.h new file mode 100644 index 0000000..2e571ed --- /dev/null +++ b/ipc/ipc_message_utils_impl.h @@ -0,0 +1,45 @@ +// 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. +// +// This file contains templates forward declared (but not defined) in +// ipc_message_utils.h so that they are only instantiated in certain files, +// notably a few IPC unit tests. + +#ifndef IPC_IPC_MESSAGE_UTILS_IMPL_H_ +#define IPC_IPC_MESSAGE_UTILS_IMPL_H_ + +namespace IPC { + +template +MessageWithTuple::MessageWithTuple( + int32 routing_id, uint32 type, const RefParam& p) + : Message(routing_id, type, PRIORITY_NORMAL) { + WriteParam(this, p); +} + +// We can't migrate the template for Log() to MessageWithTuple, because each +// subclass needs to have Log() to call Read(), which instantiates the above +// template. + +template +MessageWithReply::MessageWithReply( + int32 routing_id, uint32 type, + const RefSendParam& send, + const ReplyParam& reply) + : SyncMessage(routing_id, type, PRIORITY_NORMAL, + new ParamDeserializer(reply)) +{ + WriteParam(this, send); +} + +template +bool MessageWithReply::ReadReplyParam( + const Message* msg, typename TupleTypes::ValueTuple* p) { + void* iter = SyncMessage::GetDataIterator(msg); + return ReadParam(msg, &iter, p); +} + +} // namespace IPC + +#endif // IPC_IPC_MESSAGE_UTILS_IMPL_H_ diff --git a/ipc/ipc_param_traits.h b/ipc/ipc_param_traits.h new file mode 100644 index 0000000..e90be18 --- /dev/null +++ b/ipc/ipc_param_traits.h @@ -0,0 +1,24 @@ +// 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 IPC_IPC_PARAM_TRAITS_H_ +#define IPC_IPC_PARAM_TRAITS_H_ +#pragma once + +// Our IPC system uses the following partially specialized header to define how +// a data type is read, written and logged in the IPC system. + +namespace IPC { + +template struct ParamTraits { +}; + +template +struct SimilarTypeTraits { + typedef P Type; +}; + +} // namespace IPC + +#endif // IPC_IPC_PARAM_TRAITS_H_ diff --git a/ipc/ipc_platform_file.cc b/ipc/ipc_platform_file.cc new file mode 100644 index 0000000..2b15ceb --- /dev/null +++ b/ipc/ipc_platform_file.cc @@ -0,0 +1,43 @@ +// 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. + +#include "ipc/ipc_platform_file.h" + +namespace IPC { + +PlatformFileForTransit GetFileHandleForProcess(base::PlatformFile handle, + base::ProcessHandle process, + bool close_source_handle) { + IPC::PlatformFileForTransit out_handle; +#if defined(OS_WIN) + DWORD options = DUPLICATE_SAME_ACCESS; + if (close_source_handle) + options |= DUPLICATE_CLOSE_SOURCE; + if (!::DuplicateHandle(::GetCurrentProcess(), + handle, + process, + &out_handle, + 0, + FALSE, + options)) { + out_handle = IPC::InvalidPlatformFileForTransit(); + } +#elif defined(OS_POSIX) + // If asked to close the source, we can simply re-use the source fd instead of + // dup()ing and close()ing. + // When we're not closing the source, we need to duplicate the handle and take + // ownership of that. The reason is that this function is often used to + // generate IPC messages, and the handle must remain valid until it's sent to + // the other process from the I/O thread. Without the dup, calling code might + // close the source handle before the message is sent, creating a race + // condition. + int fd = close_source_handle ? handle : ::dup(handle); + out_handle = base::FileDescriptor(fd, true); +#else + #error Not implemented. +#endif + return out_handle; +} + +} // namespace IPC diff --git a/ipc/ipc_platform_file.h b/ipc/ipc_platform_file.h new file mode 100644 index 0000000..daaf21c --- /dev/null +++ b/ipc/ipc_platform_file.h @@ -0,0 +1,51 @@ +// 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. + +#ifndef IPC_IPC_PLATFORM_FILE_H_ +#define IPC_IPC_PLATFORM_FILE_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/platform_file.h" +#include "base/process.h" +#include "ipc/ipc_export.h" + +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif + +namespace IPC { + +#if defined(OS_WIN) +typedef base::PlatformFile PlatformFileForTransit; +#elif defined(OS_POSIX) +typedef base::FileDescriptor PlatformFileForTransit; +#endif + +inline PlatformFileForTransit InvalidPlatformFileForTransit() { +#if defined(OS_WIN) + return base::kInvalidPlatformFileValue; +#elif defined(OS_POSIX) + return base::FileDescriptor(); +#endif +} + +inline base::PlatformFile PlatformFileForTransitToPlatformFile( + const PlatformFileForTransit& transit) { +#if defined(OS_WIN) + return transit; +#elif defined(OS_POSIX) + return transit.fd; +#endif +} + +// Returns a file handle equivalent to |file| that can be used in |process|. +IPC_EXPORT PlatformFileForTransit GetFileHandleForProcess( + base::PlatformFile file, + base::ProcessHandle process, + bool close_source_handle); + +} // namespace IPC + +#endif // IPC_IPC_PLATFORM_FILE_H_ diff --git a/ipc/ipc_switches.cc b/ipc/ipc_switches.cc new file mode 100644 index 0000000..bcb1225 --- /dev/null +++ b/ipc/ipc_switches.cc @@ -0,0 +1,24 @@ +// 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 "ipc/ipc_switches.h" + +#include "base/base_switches.h" + +namespace switches { + +// Can't find the switch you are looking for? try looking in +// base/base_switches.cc instead. + +// The value of this switch tells the child process which +// IPC channel the browser expects to use to communicate with it. +const char kProcessChannelID[] = "channel"; + +// Will add kDebugOnStart to every child processes. If a value is passed, it +// will be used as a filter to determine if the child process should have the +// kDebugOnStart flag passed on or not. +const char kDebugChildren[] = "debug-children"; + +} // namespace switches + diff --git a/ipc/ipc_switches.h b/ipc/ipc_switches.h new file mode 100644 index 0000000..7c7a833 --- /dev/null +++ b/ipc/ipc_switches.h @@ -0,0 +1,20 @@ +// 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. + +// Defines all the command-line switches used by the IPC infrastructure. + +#ifndef IPC_IPC_SWITCHES_H_ +#define IPC_IPC_SWITCHES_H_ +#pragma once + +#include "ipc/ipc_export.h" + +namespace switches { + +IPC_EXPORT extern const char kProcessChannelID[]; +IPC_EXPORT extern const char kDebugChildren[]; + +} // namespace switches + +#endif // IPC_IPC_SWITCHES_H_ diff --git a/ipc/ipc_sync_channel.cc b/ipc/ipc_sync_channel.cc new file mode 100644 index 0000000..c135ef3 --- /dev/null +++ b/ipc/ipc_sync_channel.cc @@ -0,0 +1,513 @@ +// 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. + +#include "ipc/ipc_sync_channel.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/threading/thread_local.h" +#include "base/synchronization/waitable_event.h" +#include "base/synchronization/waitable_event_watcher.h" +#include "ipc/ipc_sync_message.h" + +using base::TimeDelta; +using base::TimeTicks; +using base::WaitableEvent; + +namespace IPC { +// When we're blocked in a Send(), we need to process incoming synchronous +// messages right away because it could be blocking our reply (either +// directly from the same object we're calling, or indirectly through one or +// more other channels). That means that in SyncContext's OnMessageReceived, +// we need to process sync message right away if we're blocked. However a +// simple check isn't sufficient, because the listener thread can be in the +// process of calling Send. +// To work around this, when SyncChannel filters a sync message, it sets +// an event that the listener thread waits on during its Send() call. This +// allows us to dispatch incoming sync messages when blocked. The race +// condition is handled because if Send is in the process of being called, it +// will check the event. In case the listener thread isn't sending a message, +// we queue a task on the listener thread to dispatch the received messages. +// The messages are stored in this queue object that's shared among all +// SyncChannel objects on the same thread (since one object can receive a +// sync message while another one is blocked). + +class SyncChannel::ReceivedSyncMsgQueue : + public base::RefCountedThreadSafe { + public: + // Returns the ReceivedSyncMsgQueue instance for this thread, creating one + // if necessary. Call RemoveContext on the same thread when done. + static ReceivedSyncMsgQueue* AddContext() { + // We want one ReceivedSyncMsgQueue per listener thread (i.e. since multiple + // SyncChannel objects can block the same thread). + ReceivedSyncMsgQueue* rv = lazy_tls_ptr_.Pointer()->Get(); + if (!rv) { + rv = new ReceivedSyncMsgQueue(); + ReceivedSyncMsgQueue::lazy_tls_ptr_.Pointer()->Set(rv); + } + rv->listener_count_++; + return rv; + } + + // Called on IPC thread when a synchronous message or reply arrives. + void QueueMessage(const Message& msg, SyncChannel::SyncContext* context) { + bool was_task_pending; + { + base::AutoLock auto_lock(message_lock_); + + was_task_pending = task_pending_; + task_pending_ = true; + + // We set the event in case the listener thread is blocked (or is about + // to). In case it's not, the PostTask dispatches the messages. + message_queue_.push_back(QueuedMessage(new Message(msg), context)); + } + + dispatch_event_.Signal(); + if (!was_task_pending) { + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, + &ReceivedSyncMsgQueue::DispatchMessagesTask, + scoped_refptr(context))); + } + } + + void QueueReply(const Message &msg, SyncChannel::SyncContext* context) { + received_replies_.push_back(QueuedMessage(new Message(msg), context)); + } + + // Called on the listener's thread to process any queues synchronous + // messages. + void DispatchMessagesTask(SyncContext* context) { + { + base::AutoLock auto_lock(message_lock_); + task_pending_ = false; + } + context->DispatchMessages(); + } + + void DispatchMessages(SyncContext* dispatching_context) { + SyncMessageQueue delayed_queue; + while (true) { + Message* message; + scoped_refptr context; + { + base::AutoLock auto_lock(message_lock_); + if (message_queue_.empty()) { + message_queue_ = delayed_queue; + break; + } + + message = message_queue_.front().message; + context = message_queue_.front().context; + message_queue_.pop_front(); + } + if (context->restrict_dispatch() && context != dispatching_context) { + delayed_queue.push_back(QueuedMessage(message, context)); + } else { + context->OnDispatchMessage(*message); + delete message; + } + } + } + + // SyncChannel calls this in its destructor. + void RemoveContext(SyncContext* context) { + base::AutoLock auto_lock(message_lock_); + + SyncMessageQueue::iterator iter = message_queue_.begin(); + while (iter != message_queue_.end()) { + if (iter->context == context) { + delete iter->message; + iter = message_queue_.erase(iter); + } else { + iter++; + } + } + + if (--listener_count_ == 0) { + DCHECK(lazy_tls_ptr_.Pointer()->Get()); + lazy_tls_ptr_.Pointer()->Set(NULL); + } + } + + WaitableEvent* dispatch_event() { return &dispatch_event_; } + base::MessageLoopProxy* listener_message_loop() { + return listener_message_loop_; + } + + // Holds a pointer to the per-thread ReceivedSyncMsgQueue object. + static base::LazyInstance > + lazy_tls_ptr_; + + // Called on the ipc thread to check if we can unblock any current Send() + // calls based on a queued reply. + void DispatchReplies() { + for (size_t i = 0; i < received_replies_.size(); ++i) { + Message* message = received_replies_[i].message; + if (received_replies_[i].context->TryToUnblockListener(message)) { + delete message; + received_replies_.erase(received_replies_.begin() + i); + return; + } + } + } + + base::WaitableEventWatcher* top_send_done_watcher() { + return top_send_done_watcher_; + } + + void set_top_send_done_watcher(base::WaitableEventWatcher* watcher) { + top_send_done_watcher_ = watcher; + } + + private: + friend class base::RefCountedThreadSafe; + + // See the comment in SyncChannel::SyncChannel for why this event is created + // as manual reset. + ReceivedSyncMsgQueue() : + dispatch_event_(true, false), + listener_message_loop_(base::MessageLoopProxy::current()), + task_pending_(false), + listener_count_(0), + top_send_done_watcher_(NULL) { + } + + ~ReceivedSyncMsgQueue() {} + + // Holds information about a queued synchronous message or reply. + struct QueuedMessage { + QueuedMessage(Message* m, SyncContext* c) : message(m), context(c) { } + Message* message; + scoped_refptr context; + }; + + typedef std::deque SyncMessageQueue; + SyncMessageQueue message_queue_; + + std::vector received_replies_; + + // Set when we got a synchronous message that we must respond to as the + // sender needs its reply before it can reply to our original synchronous + // message. + WaitableEvent dispatch_event_; + scoped_refptr listener_message_loop_; + base::Lock message_lock_; + bool task_pending_; + int listener_count_; + + // The current send done event watcher for this thread. Used to maintain + // a local global stack of send done watchers to ensure that nested sync + // message loops complete correctly. + base::WaitableEventWatcher* top_send_done_watcher_; +}; + +base::LazyInstance > + SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_(base::LINKER_INITIALIZED); + +SyncChannel::SyncContext::SyncContext( + Channel::Listener* listener, + base::MessageLoopProxy* ipc_thread, + WaitableEvent* shutdown_event) + : ChannelProxy::Context(listener, ipc_thread), + received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()), + shutdown_event_(shutdown_event), + restrict_dispatch_(false) { +} + +SyncChannel::SyncContext::~SyncContext() { + while (!deserializers_.empty()) + Pop(); +} + +// Adds information about an outgoing sync message to the context so that +// we know how to deserialize the reply. Returns a handle that's set when +// the reply has arrived. +void SyncChannel::SyncContext::Push(SyncMessage* sync_msg) { + // Create the tracking information for this message. This object is stored + // by value since all members are pointers that are cheap to copy. These + // pointers are cleaned up in the Pop() function. + // + // The event is created as manual reset because in between Signal and + // OnObjectSignalled, another Send can happen which would stop the watcher + // from being called. The event would get watched later, when the nested + // Send completes, so the event will need to remain set. + PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg), + sync_msg->GetReplyDeserializer(), + new WaitableEvent(true, false)); + base::AutoLock auto_lock(deserializers_lock_); + deserializers_.push_back(pending); +} + +bool SyncChannel::SyncContext::Pop() { + bool result; + { + base::AutoLock auto_lock(deserializers_lock_); + PendingSyncMsg msg = deserializers_.back(); + delete msg.deserializer; + delete msg.done_event; + msg.done_event = NULL; + deserializers_.pop_back(); + result = msg.send_result; + } + + // We got a reply to a synchronous Send() call that's blocking the listener + // thread. However, further down the call stack there could be another + // blocking Send() call, whose reply we received after we made this last + // Send() call. So check if we have any queued replies available that + // can now unblock the listener thread. + ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + received_sync_msgs_.get(), &ReceivedSyncMsgQueue::DispatchReplies)); + + return result; +} + +WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() { + base::AutoLock auto_lock(deserializers_lock_); + return deserializers_.back().done_event; +} + +WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() { + return received_sync_msgs_->dispatch_event(); +} + +void SyncChannel::SyncContext::DispatchMessages() { + received_sync_msgs_->DispatchMessages(this); +} + +bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) { + base::AutoLock auto_lock(deserializers_lock_); + if (deserializers_.empty() || + !SyncMessage::IsMessageReplyTo(*msg, deserializers_.back().id)) { + return false; + } + + if (!msg->is_reply_error()) { + deserializers_.back().send_result = deserializers_.back().deserializer-> + SerializeOutputParameters(*msg); + } + deserializers_.back().done_event->Signal(); + + return true; +} + +void SyncChannel::SyncContext::Clear() { + CancelPendingSends(); + received_sync_msgs_->RemoveContext(this); + Context::Clear(); +} + +bool SyncChannel::SyncContext::OnMessageReceived(const Message& msg) { + // Give the filters a chance at processing this message. + if (TryFilters(msg)) + return true; + + if (TryToUnblockListener(&msg)) + return true; + + if (msg.should_unblock()) { + received_sync_msgs_->QueueMessage(msg, this); + return true; + } + + if (msg.is_reply()) { + received_sync_msgs_->QueueReply(msg, this); + return true; + } + + return Context::OnMessageReceivedNoFilter(msg); +} + +void SyncChannel::SyncContext::OnChannelError() { + CancelPendingSends(); + shutdown_watcher_.StopWatching(); + Context::OnChannelError(); +} + +void SyncChannel::SyncContext::OnChannelOpened() { + shutdown_watcher_.StartWatching(shutdown_event_, this); + Context::OnChannelOpened(); +} + +void SyncChannel::SyncContext::OnChannelClosed() { + CancelPendingSends(); + shutdown_watcher_.StopWatching(); + Context::OnChannelClosed(); +} + +void SyncChannel::SyncContext::OnSendTimeout(int message_id) { + base::AutoLock auto_lock(deserializers_lock_); + PendingSyncMessageQueue::iterator iter; + for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) { + if (iter->id == message_id) { + iter->done_event->Signal(); + break; + } + } +} + +void SyncChannel::SyncContext::CancelPendingSends() { + base::AutoLock auto_lock(deserializers_lock_); + PendingSyncMessageQueue::iterator iter; + for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) + iter->done_event->Signal(); +} + +void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) { + if (event == shutdown_event_) { + // Process shut down before we can get a reply to a synchronous message. + // Cancel pending Send calls, which will end up setting the send done event. + CancelPendingSends(); + } else { + // We got the reply, timed out or the process shutdown. + DCHECK_EQ(GetSendDoneEvent(), event); + MessageLoop::current()->QuitNow(); + } +} + + +SyncChannel::SyncChannel( + const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Channel::Listener* listener, + base::MessageLoopProxy* ipc_message_loop, + bool create_pipe_now, + WaitableEvent* shutdown_event) + : ChannelProxy( + channel_handle, mode, ipc_message_loop, + new SyncContext(listener, ipc_message_loop, shutdown_event), + create_pipe_now), + sync_messages_with_no_timeout_allowed_(true) { + // Ideally we only want to watch this object when running a nested message + // loop. However, we don't know when it exits if there's another nested + // message loop running under it or not, so we wouldn't know whether to + // stop or keep watching. So we always watch it, and create the event as + // manual reset since the object watcher might otherwise reset the event + // when we're doing a WaitMany. + dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(), this); +} + +SyncChannel::~SyncChannel() { +} + +void SyncChannel::SetRestrictDispatchToSameChannel(bool value) { + sync_context()->set_restrict_dispatch(value); +} + +bool SyncChannel::Send(Message* message) { + return SendWithTimeout(message, base::kNoTimeout); +} + +bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) { + if (!message->is_sync()) { + ChannelProxy::Send(message); + return true; + } + + // *this* might get deleted in WaitForReply. + scoped_refptr context(sync_context()); + if (context->shutdown_event()->IsSignaled()) { + delete message; + return false; + } + + DCHECK(sync_messages_with_no_timeout_allowed_ || + timeout_ms != base::kNoTimeout); + SyncMessage* sync_msg = static_cast(message); + context->Push(sync_msg); + int message_id = SyncMessage::GetMessageId(*sync_msg); + WaitableEvent* pump_messages_event = sync_msg->pump_messages_event(); + + ChannelProxy::Send(message); + + if (timeout_ms != base::kNoTimeout) { + // We use the sync message id so that when a message times out, we don't + // confuse it with another send that is either above/below this Send in + // the call stack. + context->ipc_message_loop()->PostDelayedTask(FROM_HERE, + NewRunnableMethod(context.get(), + &SyncContext::OnSendTimeout, message_id), timeout_ms); + } + + // Wait for reply, or for any other incoming synchronous messages. + // *this* might get deleted, so only call static functions at this point. + WaitForReply(context, pump_messages_event); + + return context->Pop(); +} + +void SyncChannel::WaitForReply( + SyncContext* context, WaitableEvent* pump_messages_event) { + context->DispatchMessages(); + while (true) { + WaitableEvent* objects[] = { + context->GetDispatchEvent(), + context->GetSendDoneEvent(), + pump_messages_event + }; + + unsigned count = pump_messages_event ? 3: 2; + size_t result = WaitableEvent::WaitMany(objects, count); + if (result == 0 /* dispatch event */) { + // We're waiting for a reply, but we received a blocking synchronous + // call. We must process it or otherwise a deadlock might occur. + context->GetDispatchEvent()->Reset(); + context->DispatchMessages(); + continue; + } + + if (result == 2 /* pump_messages_event */) + WaitForReplyWithNestedMessageLoop(context); // Run a nested message loop. + + break; + } +} + +void SyncChannel::WaitForReplyWithNestedMessageLoop(SyncContext* context) { + base::WaitableEventWatcher send_done_watcher; + + ReceivedSyncMsgQueue* sync_msg_queue = context->received_sync_msgs(); + DCHECK(sync_msg_queue != NULL); + + base::WaitableEventWatcher* old_send_done_event_watcher = + sync_msg_queue->top_send_done_watcher(); + + base::WaitableEventWatcher::Delegate* old_delegate = NULL; + base::WaitableEvent* old_event = NULL; + + // Maintain a local global stack of send done delegates to ensure that + // nested sync calls complete in the correct sequence, i.e. the + // outermost call completes first, etc. + if (old_send_done_event_watcher) { + old_delegate = old_send_done_event_watcher->delegate(); + old_event = old_send_done_event_watcher->GetWatchedEvent(); + old_send_done_event_watcher->StopWatching(); + } + + sync_msg_queue->set_top_send_done_watcher(&send_done_watcher); + + send_done_watcher.StartWatching(context->GetSendDoneEvent(), context); + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->Run(); + MessageLoop::current()->SetNestableTasksAllowed(old_state); + + sync_msg_queue->set_top_send_done_watcher(old_send_done_event_watcher); + if (old_send_done_event_watcher && old_event) { + old_send_done_event_watcher->StartWatching(old_event, old_delegate); + } +} + +void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) { + DCHECK(event == sync_context()->GetDispatchEvent()); + // The call to DispatchMessages might delete this object, so reregister + // the object watcher first. + event->Reset(); + dispatch_watcher_.StartWatching(event, this); + sync_context()->DispatchMessages(); +} + +} // namespace IPC diff --git a/ipc/ipc_sync_channel.h b/ipc/ipc_sync_channel.h new file mode 100644 index 0000000..29844da --- /dev/null +++ b/ipc/ipc_sync_channel.h @@ -0,0 +1,200 @@ +// 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. + +#ifndef IPC_IPC_SYNC_SENDER_H__ +#define IPC_IPC_SYNC_SENDER_H__ +#pragma once + +#include +#include + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "base/synchronization/waitable_event_watcher.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_sync_message.h" + +namespace base { +class WaitableEvent; +}; + +namespace IPC { + +class SyncMessage; +class MessageReplyDeserializer; + +// This is similar to ChannelProxy, with the added feature of supporting sending +// synchronous messages. +// +// Overview of how the sync channel works +// -------------------------------------- +// When the sending thread sends a synchronous message, we create a bunch +// of tracking info (created in SendWithTimeout, stored in the PendingSyncMsg +// structure) associated with the message that we identify by the unique +// "MessageId" on the SyncMessage. Among the things we save is the +// "Deserializer" which is provided by the sync message. This object is in +// charge of reading the parameters from the reply message and putting them in +// the output variables provided by its caller. +// +// The info gets stashed in a queue since we could have a nested stack of sync +// messages (each side could send sync messages in response to sync messages, +// so it works like calling a function). The message is sent to the I/O thread +// for dispatch and the original thread blocks waiting for the reply. +// +// SyncContext maintains the queue in a threadsafe way and listens for replies +// on the I/O thread. When a reply comes in that matches one of the messages +// it's looking for (using the unique message ID), it will execute the +// deserializer stashed from before, and unblock the original thread. +// +// +// Significant complexity results from the fact that messages are still coming +// in while the original thread is blocked. Normal async messages are queued +// and dispatched after the blocking call is complete. Sync messages must +// be dispatched in a reentrant manner to avoid deadlock. +// +// +// Note that care must be taken that the lifetime of the ipc_thread argument +// is more than this object. If the message loop goes away while this object +// is running and it's used to send a message, then it will use the invalid +// message loop pointer to proxy it to the ipc thread. +class IPC_EXPORT SyncChannel : public ChannelProxy, + public base::WaitableEventWatcher::Delegate { + public: + SyncChannel(const IPC::ChannelHandle& channel_handle, + Channel::Mode mode, + Channel::Listener* listener, + base::MessageLoopProxy* ipc_message_loop, + bool create_pipe_now, + base::WaitableEvent* shutdown_event); + virtual ~SyncChannel(); + + virtual bool Send(Message* message); + virtual bool SendWithTimeout(Message* message, int timeout_ms); + + // Whether we allow sending messages with no time-out. + void set_sync_messages_with_no_timeout_allowed(bool value) { + sync_messages_with_no_timeout_allowed_ = value; + } + + // Sets this channel to only dispatch its incoming unblocking messages when it + // is itself blocked on sending a sync message, not when other channels are. + // + // Normally, any unblocking message coming from any channel can be dispatched + // when any (possibly other) channel is blocked on sending a message. This is + // needed in some cases to unblock certain loops (e.g. necessary when some + // processes share a window hierarchy), but may cause re-entrancy issues in + // some cases where such loops are not possible. This flags allows the tagging + // of some particular channels to not re-enter in such cases. + void SetRestrictDispatchToSameChannel(bool value); + + protected: + class ReceivedSyncMsgQueue; + friend class ReceivedSyncMsgQueue; + + // SyncContext holds the per object data for SyncChannel, so that SyncChannel + // can be deleted while it's being used in a different thread. See + // ChannelProxy::Context for more information. + class SyncContext : public Context, + public base::WaitableEventWatcher::Delegate { + public: + SyncContext(Channel::Listener* listener, + base::MessageLoopProxy* ipc_thread, + base::WaitableEvent* shutdown_event); + + // Adds information about an outgoing sync message to the context so that + // we know how to deserialize the reply. + void Push(SyncMessage* sync_msg); + + // Cleanly remove the top deserializer (and throw it away). Returns the + // result of the Send call for that message. + bool Pop(); + + // Returns an event that's set when the send is complete, timed out or the + // process shut down. + base::WaitableEvent* GetSendDoneEvent(); + + // Returns an event that's set when an incoming message that's not the reply + // needs to get dispatched (by calling SyncContext::DispatchMessages). + base::WaitableEvent* GetDispatchEvent(); + + void DispatchMessages(); + + // Checks if the given message is blocking the listener thread because of a + // synchronous send. If it is, the thread is unblocked and true is + // returned. Otherwise the function returns false. + bool TryToUnblockListener(const Message* msg); + + // Called on the IPC thread when a sync send that runs a nested message loop + // times out. + void OnSendTimeout(int message_id); + + base::WaitableEvent* shutdown_event() { return shutdown_event_; } + + ReceivedSyncMsgQueue* received_sync_msgs() { + return received_sync_msgs_; + } + + void set_restrict_dispatch(bool value) { restrict_dispatch_ = value; } + bool restrict_dispatch() const { return restrict_dispatch_; } + + private: + virtual ~SyncContext(); + // ChannelProxy methods that we override. + + // Called on the listener thread. + virtual void Clear(); + + // Called on the IPC thread. + virtual bool OnMessageReceived(const Message& msg); + virtual void OnChannelError(); + virtual void OnChannelOpened(); + virtual void OnChannelClosed(); + + // Cancels all pending Send calls. + void CancelPendingSends(); + + // WaitableEventWatcher::Delegate implementation. + virtual void OnWaitableEventSignaled(base::WaitableEvent* arg); + + typedef std::deque PendingSyncMessageQueue; + PendingSyncMessageQueue deserializers_; + base::Lock deserializers_lock_; + + scoped_refptr received_sync_msgs_; + + base::WaitableEvent* shutdown_event_; + base::WaitableEventWatcher shutdown_watcher_; + bool restrict_dispatch_; + }; + + private: + // WaitableEventWatcher::Delegate implementation. + virtual void OnWaitableEventSignaled(base::WaitableEvent* arg); + + SyncContext* sync_context() { + return reinterpret_cast(context()); + } + + // Both these functions wait for a reply, timeout or process shutdown. The + // latter one also runs a nested message loop in the meantime. + static void WaitForReply( + SyncContext* context, base::WaitableEvent* pump_messages_event); + + // Runs a nested message loop until a reply arrives, times out, or the process + // shuts down. + static void WaitForReplyWithNestedMessageLoop(SyncContext* context); + + bool sync_messages_with_no_timeout_allowed_; + + // Used to signal events between the IPC and listener threads. + base::WaitableEventWatcher dispatch_watcher_; + + DISALLOW_COPY_AND_ASSIGN(SyncChannel); +}; + +} // namespace IPC + +#endif // IPC_IPC_SYNC_SENDER_H__ diff --git a/ipc/ipc_sync_message.cc b/ipc/ipc_sync_message.cc new file mode 100644 index 0000000..3a36bf3 --- /dev/null +++ b/ipc/ipc_sync_message.cc @@ -0,0 +1,130 @@ +// 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 "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif +#include + +#include "base/atomic_sequence_num.h" +//#include "base/logging.h" +#include "log.h" +#include "base/synchronization/waitable_event.h" +#include "ipc/ipc_sync_message.h" + +namespace IPC { + +#define kSyncMessageHeaderSize 4 + +static base::AtomicSequenceNumber g_next_id(base::LINKER_INITIALIZED); + + static base::WaitableEvent* dummy_event = NULL; //new base::WaitableEvent(true, true); + +SyncMessage::SyncMessage( + int32 routing_id, + uint32 type, + PriorityValue priority, + MessageReplyDeserializer* deserializer) + : Message(routing_id, type, priority), + deserializer_(deserializer), + pump_messages_event_(NULL) + { + set_sync(); + set_unblock(true); + + + // Add synchronous message data before the message payload. + SyncHeader header; + header.message_id = g_next_id.GetNext(); + WriteSyncHeader(this, header); +} + +MessageReplyDeserializer* SyncMessage::GetReplyDeserializer() { + MessageReplyDeserializer* rv = deserializer_; + DCHECK(rv); + deserializer_ = NULL; + return rv; +} + +void SyncMessage::EnableMessagePumping() { + DCHECK(!pump_messages_event_); + set_pump_messages_event(dummy_event); +} + +bool SyncMessage::IsMessageReplyTo(const Message& msg, int request_id) { + if (!msg.is_reply()) + return false; + + return GetMessageId(msg) == request_id; +} + +void* SyncMessage::GetDataIterator(const Message* msg) { + void* iter = const_cast(msg->payload()); + UpdateIter(&iter, kSyncMessageHeaderSize); + return iter; +} + +int SyncMessage::GetMessageId(const Message& msg) { + if (!msg.is_sync() && !msg.is_reply()) + return 0; + + SyncHeader header; + if (!ReadSyncHeader(msg, &header)) + return 0; + + return header.message_id; +} + +Message* SyncMessage::GenerateReply(const Message* msg) { + DCHECK(msg->is_sync()) << "is_sync(" << msg->is_sync() <<")"<routing_id(), IPC_REPLY_ID, + msg->priority()); + reply->set_reply(); + + SyncHeader header; + + // use the same message id, but this time reply bit is set + header.message_id = GetMessageId(*msg); + WriteSyncHeader(reply, header); + + return reply; +} + +bool SyncMessage::ReadSyncHeader(const Message& msg, SyncHeader* header) { + DCHECK(msg.is_sync() || msg.is_reply()); + + void* iter = NULL; + bool result = msg.ReadInt(&iter, &header->message_id); + if (!result) { + NOTREACHED(); + return false; + } + + return true; +} + +bool SyncMessage::WriteSyncHeader(Message* msg, const SyncHeader& header) { + DCHECK(msg->is_sync() || msg->is_reply()); + DCHECK(msg->payload_size() == 0); + bool result = msg->WriteInt(header.message_id); + if (!result) { + NOTREACHED(); + return false; + } + + // Note: if you add anything here, you need to update kSyncMessageHeaderSize. + DCHECK(kSyncMessageHeaderSize == msg->payload_size()); + + return true; +} + + +bool MessageReplyDeserializer::SerializeOutputParameters(const Message& msg) { + return SerializeOutputParameters(msg, SyncMessage::GetDataIterator(&msg)); +} + +} // namespace IPC diff --git a/ipc/ipc_sync_message.h b/ipc/ipc_sync_message.h new file mode 100644 index 0000000..2c6ed8b --- /dev/null +++ b/ipc/ipc_sync_message.h @@ -0,0 +1,109 @@ +// 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. + +#ifndef IPC_IPC_SYNC_MESSAGE_H_ +#define IPC_IPC_SYNC_MESSAGE_H_ +#pragma once + +#if defined(OS_WIN) +#include +#endif +#include +#include "base/basictypes.h" +#include "ipc/ipc_message.h" + +namespace base { +class WaitableEvent; +} + +namespace IPC { + +class MessageReplyDeserializer; + +class IPC_EXPORT SyncMessage : public Message { + public: + SyncMessage(int32 routing_id, uint32 type, PriorityValue priority, + MessageReplyDeserializer* deserializer); + + // Call this to get a deserializer for the output parameters. + // Note that this can only be called once, and the caller is responsible + // for deleting the deserializer when they're done. + MessageReplyDeserializer* GetReplyDeserializer(); + + // If this message can cause the receiver to block while waiting for user + // input (i.e. by calling MessageBox), then the caller needs to pump window + // messages and dispatch asynchronous messages while waiting for the reply. + // If this event is passed in, then window messages will start being pumped + // when it's set. Note that this behavior will continue even if the event is + // later reset. The event must be valid until after the Send call returns. + void set_pump_messages_event(base::WaitableEvent* event) { + pump_messages_event_ = event; + if (event) { + header()->flags |= PUMPING_MSGS_BIT; + } else { + header()->flags &= ~PUMPING_MSGS_BIT; + } + } + + // Call this if you always want to pump messages. You can call this method + // or set_pump_messages_event but not both. + void EnableMessagePumping(); + + base::WaitableEvent* pump_messages_event() const { + return pump_messages_event_; + } + + // Returns true if the message is a reply to the given request id. + static bool IsMessageReplyTo(const Message& msg, int request_id); + + // Given a reply message, returns an iterator to the beginning of the data + // (i.e. skips over the synchronous specific data). + static void* GetDataIterator(const Message* msg); + + // Given a synchronous message (or its reply), returns its id. + static int GetMessageId(const Message& msg); + + // Generates a reply message to the given message. + static Message* GenerateReply(const Message* msg); + + private: + struct SyncHeader { + // unique ID (unique per sender) + int message_id; + }; + + static bool ReadSyncHeader(const Message& msg, SyncHeader* header); + static bool WriteSyncHeader(Message* msg, const SyncHeader& header); + + MessageReplyDeserializer* deserializer_; + base::WaitableEvent* pump_messages_event_; +}; + +// Used to deserialize parameters from a reply to a synchronous message +class IPC_EXPORT MessageReplyDeserializer { + public: + virtual ~MessageReplyDeserializer() {} + bool SerializeOutputParameters(const Message& msg); + private: + // Derived classes need to implement this, using the given iterator (which + // is skipped past the header for synchronous messages). + virtual bool SerializeOutputParameters(const Message& msg, void* iter) = 0; +}; + +// When sending a synchronous message, this structure contains an object +// that knows how to deserialize the response. +struct PendingSyncMsg { + PendingSyncMsg(int id, + MessageReplyDeserializer* d, + base::WaitableEvent* e) + : id(id), deserializer(d), done_event(e), send_result(false) { } + int id; + MessageReplyDeserializer* deserializer; + base::WaitableEvent* done_event; + bool send_result; +}; + +} // namespace IPC + +#endif // IPC_IPC_SYNC_MESSAGE_H_ diff --git a/ipc/ipc_sync_message_filter.cc b/ipc/ipc_sync_message_filter.cc new file mode 100644 index 0000000..cffbaa6 --- /dev/null +++ b/ipc/ipc_sync_message_filter.cc @@ -0,0 +1,127 @@ +// 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 "ipc/ipc_sync_message_filter.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/synchronization/waitable_event.h" +#include "ipc/ipc_sync_message.h" + +namespace IPC { + +SyncMessageFilter::SyncMessageFilter(base::WaitableEvent* shutdown_event) + : channel_(NULL), + listener_loop_(MessageLoop::current()), + io_loop_(NULL), + shutdown_event_(shutdown_event) { +} + +SyncMessageFilter::~SyncMessageFilter() { +} + +bool SyncMessageFilter::Send(Message* message) { + { + base::AutoLock auto_lock(lock_); + if (!io_loop_) { + delete message; + return false; + } + } + + if (!message->is_sync()) { + io_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &SyncMessageFilter::SendOnIOThread, message)); + return true; + } + + base::WaitableEvent done_event(true, false); + PendingSyncMsg pending_message( + SyncMessage::GetMessageId(*message), + reinterpret_cast(message)->GetReplyDeserializer(), + &done_event); + + { + base::AutoLock auto_lock(lock_); + // Can't use this class on the main thread or else it can lead to deadlocks. + // Also by definition, can't use this on IO thread since we're blocking it. + DCHECK(MessageLoop::current() != listener_loop_); + DCHECK(MessageLoop::current() != io_loop_); + pending_sync_messages_.insert(&pending_message); + } + + io_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &SyncMessageFilter::SendOnIOThread, message)); + + base::WaitableEvent* events[2] = { shutdown_event_, &done_event }; + base::WaitableEvent::WaitMany(events, 2); + + { + base::AutoLock auto_lock(lock_); + delete pending_message.deserializer; + pending_sync_messages_.erase(&pending_message); + } + + return pending_message.send_result; +} + +void SyncMessageFilter::SendOnIOThread(Message* message) { + if (channel_) { + channel_->Send(message); + return; + } + + if (message->is_sync()) { + // We don't know which thread sent it, but it doesn't matter, just signal + // them all. + SignalAllEvents(); + } + + delete message; +} + +void SyncMessageFilter::SignalAllEvents() { + base::AutoLock auto_lock(lock_); + for (PendingSyncMessages::iterator iter = pending_sync_messages_.begin(); + iter != pending_sync_messages_.end(); ++iter) { + (*iter)->done_event->Signal(); + } +} + +void SyncMessageFilter::OnFilterAdded(Channel* channel) { + channel_ = channel; + base::AutoLock auto_lock(lock_); + io_loop_ = MessageLoop::current(); +} + +void SyncMessageFilter::OnChannelError() { + channel_ = NULL; + SignalAllEvents(); +} + +void SyncMessageFilter::OnChannelClosing() { + channel_ = NULL; + SignalAllEvents(); +} + +bool SyncMessageFilter::OnMessageReceived(const Message& message) { + base::AutoLock auto_lock(lock_); + for (PendingSyncMessages::iterator iter = pending_sync_messages_.begin(); + iter != pending_sync_messages_.end(); ++iter) { + if (SyncMessage::IsMessageReplyTo(message, (*iter)->id)) { + if (!message.is_reply_error()) { + (*iter)->send_result = + (*iter)->deserializer->SerializeOutputParameters(message); + } + (*iter)->done_event->Signal(); + return true; + } + } + + return false; +} + +} // namespace IPC diff --git a/ipc/ipc_sync_message_filter.h b/ipc/ipc_sync_message_filter.h new file mode 100644 index 0000000..0dc471a --- /dev/null +++ b/ipc/ipc_sync_message_filter.h @@ -0,0 +1,70 @@ +// 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. + +#ifndef IPC_IPC_SYNC_MESSAGE_FILTER_H_ +#define IPC_IPC_SYNC_MESSAGE_FILTER_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_sync_message.h" +#include + +namespace base { +class WaitableEvent; +} + +class MessageLoop; + +namespace IPC { + +class MessageReplyDeserializer; + +// This MessageFilter allows sending synchronous IPC messages from a thread +// other than the listener thread associated with the SyncChannel. It does not +// support fancy features that SyncChannel does, such as handling recursion or +// receiving messages while waiting for a response. Note that this object can +// be used to send simultaneous synchronous messages from different threads. +class IPC_EXPORT SyncMessageFilter : public ChannelProxy::MessageFilter, + public Message::Sender { + public: + explicit SyncMessageFilter(base::WaitableEvent* shutdown_event); + virtual ~SyncMessageFilter(); + + // Message::Sender implementation. + virtual bool Send(Message* message); + + // ChannelProxy::MessageFilter implementation. + virtual void OnFilterAdded(Channel* channel); + virtual void OnChannelError(); + virtual void OnChannelClosing(); + virtual bool OnMessageReceived(const Message& message); + + private: + void SendOnIOThread(Message* message); + // Signal all the pending sends as done, used in an error condition. + void SignalAllEvents(); + + // The channel to which this filter was added. + Channel* channel_; + + MessageLoop* listener_loop_; // The process's main thread. + MessageLoop* io_loop_; // The message loop where the Channel lives. + + typedef std::set PendingSyncMessages; + PendingSyncMessages pending_sync_messages_; + + // Locks data members above. + base::Lock lock_; + + base::WaitableEvent* shutdown_event_; + + DISALLOW_COPY_AND_ASSIGN(SyncMessageFilter); +}; + +} // namespace IPC + +#endif // IPC_IPC_SYNC_MESSAGE_FILTER_H_ diff --git a/ipc/param_traits_log_macros.h b/ipc/param_traits_log_macros.h new file mode 100644 index 0000000..8259a0d --- /dev/null +++ b/ipc/param_traits_log_macros.h @@ -0,0 +1,52 @@ +// 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. + +#ifndef IPC_PARAM_TRAITS_LOG_MACROS_H_ +#define IPC_PARAM_TRAITS_LOG_MACROS_H_ +#pragma once + +#include + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END() + +// Set up so next include will generate log methods. +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + void ParamTraits::Log(const param_type& p, std::string* l) { \ + bool needs_comma = false; \ + l->append("("); +#define IPC_STRUCT_TRAITS_MEMBER(name) \ + if (needs_comma) \ + l->append(", "); \ + LogParam(p.name, l); \ + needs_comma = true; +#define IPC_STRUCT_TRAITS_PARENT(type) \ + if (needs_comma) \ + l->append(", "); \ + ParamTraits::Log(p, l); \ + needs_comma = true; +#define IPC_STRUCT_TRAITS_END() \ + l->append(")"); \ + } + +#undef IPC_ENUM_TRAITS +#define IPC_ENUM_TRAITS(enum_name) \ + void ParamTraits::Log(const param_type& p, std::string* l) { \ + LogParam(static_cast(p), l); \ + } + +#endif // IPC_PARAM_TRAITS_LOG_MACROS_H_ + diff --git a/ipc/param_traits_macros.h b/ipc/param_traits_macros.h new file mode 100644 index 0000000..df57adf --- /dev/null +++ b/ipc/param_traits_macros.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef IPC_PARAM_TRAITS_MACROS_H_ +#define IPC_PARAM_TRAITS_MACROS_H_ + +#include + +// Traits generation for structs. +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + namespace IPC { \ + template <> \ + struct ParamTraits { \ + typedef struct_name param_type; \ + static void Write(Message* m, const param_type& p); \ + static bool Read(const Message* m, void** iter, param_type* p); \ + static void Log(const param_type& p, std::string* l); \ + }; \ + } + +#define IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_TRAITS_PARENT(type) +#define IPC_STRUCT_TRAITS_END() + +// Traits generation for enums. +#define IPC_ENUM_TRAITS(enum_name) \ + namespace IPC { \ + template <> \ + struct ParamTraits { \ + typedef enum_name param_type; \ + static void Write(Message* m, const param_type& p); \ + static bool Read(const Message* m, void** iter, param_type* p); \ + static void Log(const param_type& p, std::string* l); \ + }; \ + } + +#endif // IPC_PARAM_TRAITS_MACROS_H_ + diff --git a/ipc/param_traits_read_macros.h b/ipc/param_traits_read_macros.h new file mode 100644 index 0000000..f0b88bc --- /dev/null +++ b/ipc/param_traits_read_macros.h @@ -0,0 +1,45 @@ +// 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. + +#ifndef IPC_PARAM_TRAITS_READ_MACROS_H_ +#define IPC_PARAM_TRAITS_READ_MACROS_H_ +#pragma once + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END() + +// Set up so next include will generate read methods. +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + bool ParamTraits:: \ + Read(const Message* m, void** iter, param_type* p) { \ + return +#define IPC_STRUCT_TRAITS_MEMBER(name) ReadParam(m, iter, &p->name) && +#define IPC_STRUCT_TRAITS_PARENT(type) ParamTraits::Read(m, iter, p) && +#define IPC_STRUCT_TRAITS_END() 1; } + +#undef IPC_ENUM_TRAITS +#define IPC_ENUM_TRAITS(enum_name) \ + bool ParamTraits:: \ + Read(const Message* m, void** iter, param_type* p) { \ + int type; \ + if (!m->ReadInt(iter, &type)) \ + return false; \ + *p = static_cast(type); \ + return true; \ + } + +#endif // IPC_PARAM_TRAITS_READ_MACROS_H_ + diff --git a/ipc/param_traits_write_macros.h b/ipc/param_traits_write_macros.h new file mode 100644 index 0000000..844c385 --- /dev/null +++ b/ipc/param_traits_write_macros.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef IPC_PARAM_TRAITS_WRITE_MACROS_H_ +#define IPC_PARAM_TRAITS_WRITE_MACROS_H_ +#pragma once + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// STRUCT declarations cause corresponding STRUCT_TRAITS declarations to occur. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) IPC_STRUCT_TRAITS_BEGIN(struct_name) +#define IPC_STRUCT_MEMBER(type, name) IPC_STRUCT_TRAITS_MEMBER(name) +#define IPC_STRUCT_END() IPC_STRUCT_TRAITS_END() + +// Set up so next include will generate write methods. +#undef IPC_STRUCT_TRAITS_BEGIN +#undef IPC_STRUCT_TRAITS_MEMBER +#undef IPC_STRUCT_TRAITS_PARENT +#undef IPC_STRUCT_TRAITS_END +#define IPC_STRUCT_TRAITS_BEGIN(struct_name) \ + void ParamTraits::Write(Message* m, const param_type& p) { +#define IPC_STRUCT_TRAITS_MEMBER(name) WriteParam(m, p.name); +#define IPC_STRUCT_TRAITS_PARENT(type) ParamTraits::Write(m, p); +#define IPC_STRUCT_TRAITS_END() } + +#undef IPC_ENUM_TRAITS +#define IPC_ENUM_TRAITS(enum_name) \ + void ParamTraits::Write(Message* m, const param_type& p) { \ + m->WriteInt(static_cast(p)); \ + } + +#endif // IPC_PARAM_TRAITS_WRITE_MACROS_H_ + diff --git a/ipc/struct_constructor_macros.h b/ipc/struct_constructor_macros.h new file mode 100644 index 0000000..67bfcfb --- /dev/null +++ b/ipc/struct_constructor_macros.h @@ -0,0 +1,20 @@ +// 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 IPC_STRUCT_CONSTRUCTOR_MACROS_H_ +#define IPC_STRUCT_CONSTRUCTOR_MACROS_H_ + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// Set up so next include will generate constructors. +#undef IPC_STRUCT_BEGIN +#undef IPC_STRUCT_MEMBER +#undef IPC_STRUCT_END +#define IPC_STRUCT_BEGIN(struct_name) struct_name::struct_name() : NoParams() +#define IPC_STRUCT_MEMBER(type, name) , name() +#define IPC_STRUCT_END() {} + +#endif // IPC_STRUCT_CONSTRUCTOR_MACROS_H_ + diff --git a/ipc/struct_destructor_macros.h b/ipc/struct_destructor_macros.h new file mode 100644 index 0000000..bf3dc95 --- /dev/null +++ b/ipc/struct_destructor_macros.h @@ -0,0 +1,16 @@ +// 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. + +#ifndef IPC_STRUCT_DESTRUCTOR_MACROS_H_ +#define IPC_STRUCT_DESTRUCTOR_MACROS_H_ + +// Null out all the macros that need nulling. +#include "ipc/ipc_message_null_macros.h" + +// Set up so next include will generate destructors. +#undef IPC_STRUCT_BEGIN +#define IPC_STRUCT_BEGIN(struct_name) struct_name::~struct_name() {} + +#endif // IPC_STRUCT_DESTRUCTOR_MACROS_H_ + diff --git a/ipc/sync_socket_unittest.cc b/ipc/sync_socket_unittest.cc new file mode 100644 index 0000000..1a4ae13 --- /dev/null +++ b/ipc/sync_socket_unittest.cc @@ -0,0 +1,250 @@ +// 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/sync_socket.h" + +#include +#include +#include + +#include "base/message_loop.h" +#include "base/process_util.h" +#include "build/build_config.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_channel_proxy.h" +#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_message_utils.h" +#include "ipc/ipc_message_utils_impl.h" +#include "ipc/ipc_tests.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" + +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#endif + +enum IPCMessageIds { + UNUSED_IPC_TYPE, + SERVER_FIRST_IPC_TYPE, // SetHandle message sent to server. + SERVER_SECOND_IPC_TYPE, // Shutdown message sent to server. + CLIENT_FIRST_IPC_TYPE // Response message sent to client. +}; + +namespace { +const char kHelloString[] = "Hello, SyncSocket Client"; +const size_t kHelloStringLength = arraysize(kHelloString); +} // namespace + +// Message class to pass a base::SyncSocket::Handle to another process. +// This is not as easy as it sounds, because of the differences in transferring +// Windows HANDLEs versus posix file descriptors. +#if defined(OS_WIN) +class MsgClassSetHandle + : public IPC::MessageWithTuple< Tuple1 > { + public: + enum { ID = SERVER_FIRST_IPC_TYPE }; + explicit MsgClassSetHandle(const base::SyncSocket::Handle arg1) + : IPC::MessageWithTuple< Tuple1 >( + MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MsgClassSetHandle); +}; +#elif defined(OS_POSIX) +class MsgClassSetHandle + : public IPC::MessageWithTuple< Tuple1 > { + public: + enum { ID = SERVER_FIRST_IPC_TYPE }; + explicit MsgClassSetHandle(const base::FileDescriptor& arg1) + : IPC::MessageWithTuple< Tuple1 >( + MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MsgClassSetHandle); +}; +#else +# error "What platform?" +#endif // defined(OS_WIN) + +// Message class to pass a response to the server. +class MsgClassResponse + : public IPC::MessageWithTuple< Tuple1 > { + public: + enum { ID = CLIENT_FIRST_IPC_TYPE }; + explicit MsgClassResponse(const std::string& arg1) + : IPC::MessageWithTuple< Tuple1 >( + MSG_ROUTING_CONTROL, ID, MakeRefTuple(arg1)) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MsgClassResponse); +}; + +// Message class to tell the server to shut down. +class MsgClassShutdown + : public IPC::MessageWithTuple< Tuple0 > { + public: + enum { ID = SERVER_SECOND_IPC_TYPE }; + MsgClassShutdown() + : IPC::MessageWithTuple< Tuple0 >( + MSG_ROUTING_CONTROL, ID, MakeTuple()) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MsgClassShutdown); +}; + +// The SyncSocket server listener class processes two sorts of +// messages from the client. +class SyncSocketServerListener : public IPC::Channel::Listener { + public: + SyncSocketServerListener() : chan_(NULL) { + } + + void Init(IPC::Channel* chan) { + chan_ = chan; + } + + virtual bool OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + IPC_BEGIN_MESSAGE_MAP(SyncSocketServerListener, msg) + IPC_MESSAGE_HANDLER(MsgClassSetHandle, OnMsgClassSetHandle) + IPC_MESSAGE_HANDLER(MsgClassShutdown, OnMsgClassShutdown) + IPC_END_MESSAGE_MAP() + } + return true; + } + + private: + // This sort of message is sent first, causing the transfer of + // the handle for the SyncSocket. This message sends a buffer + // on the SyncSocket and then sends a response to the client. +#if defined(OS_WIN) + void OnMsgClassSetHandle(const base::SyncSocket::Handle handle) { + SetHandle(handle); + } +#elif defined(OS_POSIX) + void OnMsgClassSetHandle(const base::FileDescriptor& fd_struct) { + SetHandle(fd_struct.fd); + } +#else +# error "What platform?" +#endif // defined(OS_WIN) + + void SetHandle(base::SyncSocket::Handle handle) { + base::SyncSocket sync_socket(handle); + EXPECT_EQ(sync_socket.Send(static_cast(kHelloString), + kHelloStringLength), kHelloStringLength); + IPC::Message* msg = new MsgClassResponse(kHelloString); + EXPECT_TRUE(chan_->Send(msg)); + } + + // When the client responds, it sends back a shutdown message, + // which causes the message loop to exit. + void OnMsgClassShutdown() { + MessageLoop::current()->Quit(); + } + + IPC::Channel* chan_; + + DISALLOW_COPY_AND_ASSIGN(SyncSocketServerListener); +}; + +// Runs the fuzzing server child mode. Returns when the preset number +// of messages have been received. +MULTIPROCESS_TEST_MAIN(RunSyncSocketServer) { + MessageLoopForIO main_message_loop; + SyncSocketServerListener listener; + IPC::Channel chan(kSyncSocketChannel, IPC::Channel::MODE_CLIENT, &listener); + EXPECT_TRUE(chan.Connect()); + listener.Init(&chan); + MessageLoop::current()->Run(); + return 0; +} + +// The SyncSocket client listener only processes one sort of message, +// a response from the server. +class SyncSocketClientListener : public IPC::Channel::Listener { + public: + SyncSocketClientListener() { + } + + void Init(base::SyncSocket* socket, IPC::Channel* chan) { + socket_ = socket; + chan_ = chan; + } + + virtual bool OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + IPC_BEGIN_MESSAGE_MAP(SyncSocketClientListener, msg) + IPC_MESSAGE_HANDLER(MsgClassResponse, OnMsgClassResponse) + IPC_END_MESSAGE_MAP() + } + return true; + } + + private: + // When a response is received from the server, it sends the same + // string as was written on the SyncSocket. These are compared + // and a shutdown message is sent back to the server. + void OnMsgClassResponse(const std::string& str) { + // We rely on the order of sync_socket.Send() and chan_->Send() in + // the SyncSocketServerListener object. + EXPECT_EQ(kHelloStringLength, socket_->Peek()); + char buf[kHelloStringLength]; + socket_->Receive(static_cast(buf), kHelloStringLength); + EXPECT_EQ(strcmp(str.c_str(), buf), 0); + // After receiving from the socket there should be no bytes left. + EXPECT_EQ(0U, socket_->Peek()); + IPC::Message* msg = new MsgClassShutdown(); + EXPECT_TRUE(chan_->Send(msg)); + MessageLoop::current()->Quit(); + } + + base::SyncSocket* socket_; + IPC::Channel* chan_; + + DISALLOW_COPY_AND_ASSIGN(SyncSocketClientListener); +}; + +class SyncSocketTest : public IPCChannelTest { +}; + +TEST_F(SyncSocketTest, SanityTest) { + SyncSocketClientListener listener; + IPC::Channel chan(kSyncSocketChannel, IPC::Channel::MODE_SERVER, + &listener); + base::ProcessHandle server_process = SpawnChild(SYNC_SOCKET_SERVER, &chan); + ASSERT_TRUE(server_process); + // Create a pair of SyncSockets. + base::SyncSocket* pair[2]; + base::SyncSocket::CreatePair(pair); + // Immediately after creation there should be no pending bytes. + EXPECT_EQ(0U, pair[0]->Peek()); + EXPECT_EQ(0U, pair[1]->Peek()); + base::SyncSocket::Handle target_handle; + // Connect the channel and listener. + ASSERT_TRUE(chan.Connect()); + listener.Init(pair[0], &chan); +#if defined(OS_WIN) + // On windows we need to duplicate the handle into the server process. + BOOL retval = DuplicateHandle(GetCurrentProcess(), pair[1]->handle(), + server_process, &target_handle, + 0, FALSE, DUPLICATE_SAME_ACCESS); + EXPECT_TRUE(retval); + // Set up a message to pass the handle to the server. + IPC::Message* msg = new MsgClassSetHandle(target_handle); +#else + target_handle = pair[1]->handle(); + // Set up a message to pass the handle to the server. + base::FileDescriptor filedesc(target_handle, false); + IPC::Message* msg = new MsgClassSetHandle(filedesc); +#endif // defined(OS_WIN) + EXPECT_TRUE(chan.Send(msg)); + // Use the current thread as the I/O thread. + MessageLoop::current()->Run(); + // Shut down. + delete pair[0]; + delete pair[1]; + EXPECT_TRUE(base::WaitForSingleProcess(server_process, 5000)); + base::CloseProcessHandle(server_process); +} diff --git a/log.h b/log.h new file mode 100644 index 0000000..90f87eb --- /dev/null +++ b/log.h @@ -0,0 +1,26 @@ +// 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. + +#ifndef BASE_LOGGING_H_ +#define BASE_LOGGING_H_ +#pragma once + +//namespace logging { +#define DCHECK(x) if (!(x)) std::cout << "Check failed" +#define CHECK(x) if (!(x)) std::cout << "Check failed" +#define NOTREACHED() std::cout << "Not reached" +#define DCHECK_EQ(x, y) if ((x) != (y)) std::cout << "Check failed" +#define DCHECK_LE(x, y) if ((x) > (y)) std::cout << "Check failed" +#define DCHECK_GE(x, y) if ((x) < (y)) std::cout << "Check failed" +#define DCHECK_NE(x, y) if ((x) == (y)) std::cout << "Check failed" +#define CHECK_NE(x, y) if ((x) == (y)) std::cout << "Check failed" +//#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +#ifndef WARN_UNUSED_RESULT +//#define WARN_UNUSED_RESULT +#endif + +//} // namespace base + +#endif // BASE_LOGGING_H_ diff --git a/packaging/chromium.changes b/packaging/chromium.changes new file mode 100644 index 0000000..771dbc8 --- /dev/null +++ b/packaging/chromium.changes @@ -0,0 +1,2 @@ +* Fri Jan 18 2013 Anas Nashif submit/trunk/20121227.051414@062f276 +- Support installation in lib64 diff --git a/packaging/chromium.spec b/packaging/chromium.spec new file mode 100755 index 0000000..cb64040 --- /dev/null +++ b/packaging/chromium.spec @@ -0,0 +1,55 @@ +Name: chromium +Summary: chromium library +Version: 1.0 +Release: 1 +VCS: external/chromium#submit/master/20130204.054301-1-gf46f96e2520bef5f41566b1752fa700ecbb2e164 +Group: TO_BE/FILLED_IN +License: BSD +Source0: %{name}-%{version}.tar.gz +BuildRequires: cmake + +%description +chromium library + +%package devel +Summary: chromium library (devel) +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} + +%description devel +chromium library (devel) + +%prep +%setup -q + +%build +MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'` +%ifarch %{ix86} +CXXFLAGS="$CXXFLAGS -D_OSP_DEBUG_ -D_OSP_X86_ -D_OSP_EMUL_" %cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER} +%else +CXXFLAGS="$CXXFLAGS -D_OSP_DEBUG_ -D_OSP_ARMEL_" %cmake . -DFULLVER=%{version} -DMAJORVER=${MAJORVER} +%endif +# Call make instruction with smp support +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +mkdir -p %{buildroot}/usr/share/license +cp %{_builddir}/%{name}-%{version}/LICENSE %{buildroot}/usr/share/license/%{name} + +%make_install + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%manifest chromium.manifest +%defattr(-,root,root,-) +/usr/share/license/%{name} +%{_libdir}/libchromium.so* + +%files devel +%defattr(-,root,root,-) +%{_includedir}/chromium/* +%{_libdir}/pkgconfig/chromium.pc