diff options
author | Kristian Monsen <kristianm@google.com> | 2011-06-09 11:47:42 +0100 |
---|---|---|
committer | Kristian Monsen <kristianm@google.com> | 2011-06-29 14:33:03 +0100 |
commit | dc0f95d653279beabeb9817299e2902918ba123e (patch) | |
tree | 32eb121cd532053a5b9cb0c390331349af8d6baa /base | |
parent | ba160cd4054d13d0cb0b1b46e61c3bed67095811 (diff) | |
download | external_chromium-dc0f95d653279beabeb9817299e2902918ba123e.zip external_chromium-dc0f95d653279beabeb9817299e2902918ba123e.tar.gz external_chromium-dc0f95d653279beabeb9817299e2902918ba123e.tar.bz2 |
Merge Chromium at r11.0.696.0: Initial merge by git
Change-Id: I273dde2843af0839dfc08b419bb443fbd449532d
Diffstat (limited to 'base')
112 files changed, 2772 insertions, 1728 deletions
diff --git a/base/PRESUBMIT.py b/base/PRESUBMIT.py new file mode 100644 index 0000000..1284659 --- /dev/null +++ b/base/PRESUBMIT.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +# 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. + +"""Chromium presubmit script for src/base. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details on the presubmit API built into gcl. +""" + +def GetPreferredTrySlaves(): + return ['win', 'linux', 'mac', 'win_sync', 'linux_sync', 'mac_sync'] diff --git a/base/allocator/allocator.gyp b/base/allocator/allocator.gyp index 87b4127..065cbf4 100644 --- a/base/allocator/allocator.gyp +++ b/base/allocator/allocator.gyp @@ -133,7 +133,6 @@ '<(tcmalloc_dir)/src/stack_trace_table.cc', '<(tcmalloc_dir)/src/stack_trace_table.h', '<(tcmalloc_dir)/src/stacktrace.cc', - '<(tcmalloc_dir)/src/stacktrace.h', '<(tcmalloc_dir)/src/stacktrace_config.h', '<(tcmalloc_dir)/src/stacktrace_generic-inl.h', '<(tcmalloc_dir)/src/stacktrace_libunwind-inl.h', @@ -177,6 +176,7 @@ '<(jemalloc_dir)/rb.h', 'allocator_shim.cc', + 'allocator_shim.h', 'generic_allocators.cc', 'win_allocator.cc', ], diff --git a/base/allocator/allocator_shim.cc b/base/allocator/allocator_shim.cc index 3935737..f11164c 100644 --- a/base/allocator/allocator_shim.cc +++ b/base/allocator/allocator_shim.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/allocator/allocator_shim.h" + #include <config.h> // When defined, different heap allocators can be used via an environment @@ -29,13 +31,24 @@ static int new_mode = 0; typedef enum { TCMALLOC, // TCMalloc is the default allocator. - JEMALLOC, // JEMalloc - WINDEFAULT, // Windows Heap - WINLFH, // Windows LFH Heap + JEMALLOC, // JEMalloc. + WINHEAP, // Windows Heap (standard Windows allocator). + WINLFH, // Windows LFH Heap. } Allocator; -// This is the default allocator. -static Allocator allocator = TCMALLOC; +// This is the default allocator. This value can be changed at startup by +// specifying environment variables shown below it. +// See SetupSubprocessAllocator() to specify a default secondary (subprocess) +// allocator. +// TODO(jar): Switch to using TCMALLOC for the renderer as well. +static Allocator allocator = WINHEAP; + +// The names of the environment variables that can optionally control the +// selection of the allocator. The primary may be used to control overall +// allocator selection, and the secondary can be used to specify an allocator +// to use in sub-processes. +static const char* primary_name = "CHROME_ALLOCATOR"; +static const char* secondary_name = "CHROME_ALLOCATOR_2"; // We include tcmalloc and the win_allocator to get as much inlining as // possible. @@ -101,7 +114,7 @@ void* malloc(size_t size) __THROW { case JEMALLOC: ptr = je_malloc(size); break; - case WINDEFAULT: + case WINHEAP: case WINLFH: ptr = win_heap_malloc(size); break; @@ -129,7 +142,7 @@ void free(void* p) __THROW { case JEMALLOC: je_free(p); return; - case WINDEFAULT: + case WINHEAP: case WINLFH: win_heap_free(p); return; @@ -153,7 +166,7 @@ void* realloc(void* ptr, size_t size) __THROW { case JEMALLOC: new_ptr = je_realloc(ptr, size); break; - case WINDEFAULT: + case WINHEAP: case WINLFH: new_ptr = win_heap_realloc(ptr, size); break; @@ -185,7 +198,7 @@ void malloc_stats(void) __THROW { case JEMALLOC: // No stats. return; - case WINDEFAULT: + case WINHEAP: case WINLFH: // No stats. return; @@ -201,7 +214,7 @@ extern "C" size_t _msize(void* p) { switch (allocator) { case JEMALLOC: return je_msize(p); - case WINDEFAULT: + case WINHEAP: case WINLFH: return win_heap_msize(p); } @@ -217,22 +230,22 @@ extern "C" intptr_t _get_heap_handle() { // The CRT heap initialization stub. extern "C" int _heap_init() { #ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING - const char* override = GetenvBeforeMain("CHROME_ALLOCATOR"); - if (override) { - if (!stricmp(override, "jemalloc")) + const char* environment_value = GetenvBeforeMain(primary_name); + if (environment_value) { + if (!stricmp(environment_value, "jemalloc")) allocator = JEMALLOC; - else if (!stricmp(override, "winheap")) - allocator = WINDEFAULT; - else if (!stricmp(override, "winlfh")) + else if (!stricmp(environment_value, "winheap")) + allocator = WINHEAP; + else if (!stricmp(environment_value, "winlfh")) allocator = WINLFH; - else if (!stricmp(override, "tcmalloc")) + else if (!stricmp(environment_value, "tcmalloc")) allocator = TCMALLOC; } switch (allocator) { case JEMALLOC: return je_malloc_init_hard() ? 0 : 1; - case WINDEFAULT: + case WINHEAP: return win_heap_init(false) ? 1 : 0; case WINLFH: return win_heap_init(true) ? 1 : 0; @@ -264,3 +277,29 @@ extern "C" void* _crtheap = reinterpret_cast<void*>(1); #include "generic_allocators.cc" } // extern C + +namespace base { +namespace allocator { + +void SetupSubprocessAllocator() { +#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING + size_t primary_length = 0; + getenv_s(&primary_length, NULL, 0, primary_name); + + size_t secondary_length = 0; + char buffer[20]; + getenv_s(&secondary_length, buffer, sizeof(buffer), secondary_name); + DCHECK_GT(sizeof(buffer), secondary_length); + buffer[sizeof(buffer) - 1] = '\0'; + + if (secondary_length || !primary_length) { + char* secondary_value = secondary_length ? buffer : "TCMALLOC"; + // Force renderer (or other subprocesses) to use secondary_value. + int ret_val = _putenv_s(primary_name, secondary_value); + CHECK_EQ(0, ret_val); + } +#endif // ENABLE_DYNAMIC_ALLOCATOR_SWITCHING +} + +} // namespace base. +} // namespace allocator. diff --git a/base/allocator/allocator_shim.h b/base/allocator/allocator_shim.h new file mode 100644 index 0000000..342710f --- /dev/null +++ b/base/allocator/allocator_shim.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. + +#ifndef BASE_ALLOCATOR_ALLOCATOR_SHIM_H_ +#define BASE_ALLOCATOR_ALLOCATOR_SHIM_H_ + +namespace base { +namespace allocator { + +// Resets the environment variable CHROME_ALLOCATOR to specify the choice to +// be used by subprocesses. Priority is given to the current value of +// CHROME_ALLOCATOR_2 (if specified), then CHROME_ALLOCATOR (if specified), and +// then a default value (typically set to TCMALLOC). +void SetupSubprocessAllocator(); + +} // namespace base. +} // namespace allocator. + +#endif // BASE_ALLOCATOR_ALLOCATOR_SHIM_H_ diff --git a/base/base.gyp b/base/base.gyp index 868569e..525d316 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -31,8 +31,8 @@ 'base', ], 'sources': [ - 'i18n/bidi_line_iterator.cc', - 'i18n/bidi_line_iterator.h', + 'i18n/bidi_line_iterator.cc', + 'i18n/bidi_line_iterator.h', 'i18n/break_iterator.cc', 'i18n/break_iterator.h', 'i18n/char_iterator.cc', @@ -69,6 +69,7 @@ 'bits_unittest.cc', 'callback_unittest.cc', 'command_line_unittest.cc', + 'cpu_unittest.cc', 'crypto/encryptor_unittest.cc', 'crypto/rsa_private_key_unittest.cc', 'crypto/rsa_private_key_nss_unittest.cc', diff --git a/base/base.gypi b/base/base.gypi index 7a5da2e..ef1d8d8 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -39,10 +39,12 @@ 'bind.h', 'bind_helpers.h', 'bind_internal.h', + 'bind_internal_win.h', 'bits.h', 'bzip2_error_handler.cc', 'callback.h', - 'callback_helpers.h', + 'callback_internal.cc', + 'callback_internal.h', 'callback_old.h', 'command_line.cc', 'command_line.h', @@ -249,8 +251,9 @@ 'task_queue.cc', 'task_queue.h', 'template_util.h', - 'threading/non_thread_safe.cc', 'threading/non_thread_safe.h', + 'threading/non_thread_safe_impl.cc', + 'threading/non_thread_safe_impl.h', 'threading/platform_thread.h', 'threading/platform_thread_mac.mm', 'threading/platform_thread_posix.cc', @@ -259,8 +262,9 @@ 'threading/simple_thread.h', 'threading/thread.cc', 'threading/thread.h', - 'threading/thread_checker.cc', 'threading/thread_checker.h', + 'threading/thread_checker_impl.cc', + 'threading/thread_checker_impl.h', 'threading/thread_collision_warner.cc', 'threading/thread_collision_warner.h', 'threading/thread_local.h', @@ -515,7 +519,6 @@ 'third_party/purify/pure_api.c', 'base_drag_source.cc', 'base_drop_target.cc', - 'cpu.cc', 'crypto/capi_util.h', 'crypto/capi_util.cc', 'event_recorder.cc', diff --git a/base/bind.h b/base/bind.h index c23af2e..cd9eb19 100644 --- a/base/bind.h +++ b/base/bind.h @@ -12,7 +12,7 @@ #pragma once #include "base/bind_internal.h" -#include "base/callback_helpers.h" +#include "base/callback_internal.h" // See base/callback.h for how to use these functions. // diff --git a/base/bind.h.pump b/base/bind.h.pump index fc7f246..62b313f 100644 --- a/base/bind.h.pump +++ b/base/bind.h.pump @@ -16,7 +16,7 @@ $var MAX_ARITY = 6 #pragma once #include "base/bind_internal.h" -#include "base/callback_helpers.h" +#include "base/callback_internal.h" // See base/callback.h for how to use these functions. // diff --git a/base/bind_internal.h b/base/bind_internal.h index 62f2050..bb5f46f 100644 --- a/base/bind_internal.h +++ b/base/bind_internal.h @@ -12,8 +12,13 @@ #pragma once #include "base/bind_helpers.h" -#include "base/callback_helpers.h" +#include "base/callback_internal.h" #include "base/template_util.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/bind_internal_win.h" +#endif namespace base { namespace internal { @@ -21,35 +26,229 @@ namespace internal { // The method by which a function is invoked is determined by 3 different // dimensions: // -// 1) The type of function (normal, method, const-method) -// 2) The arity of the function +// 1) The type of function (normal or method). +// 2) The arity of the function. // 3) The number of bound parameters. // -// The FunctionTraitsN classes unwrap the function signature type to -// specialize based on the first two dimensions. The N in FunctionTraitsN -// specifies the 3rd dimension. We could have specified the unbound parameters -// via template parameters, but this method looked cleaner. +// The templates below handle the determination of each of these dimensions. +// In brief: +// +// FunctionTraits<> -- Provides a normalied signature, and other traits. +// InvokerN<> -- Provides a DoInvoke() function that actually executes +// a calback. +// InvokerStorageN<> -- Provides storage for the bound parameters, and +// typedefs to the above. +// +// More details about the design of each class is included in a comment closer +// to their defition. + +// FunctionTraits<> +// +// The FunctionTraits<> template determines the type of function, and also +// creates a NormalizedType used to select the InvokerN classes. It turns out +// that syntactically, you only really have 2 variations when invoking a +// funciton pointer: normal, and method. One is invoked func_ptr(arg1). The +// other is invoked (*obj_->method_ptr(arg1)). +// +// However, in the type system, there are many more distinctions. In standard +// C++, there's all variations of const, and volatile on the function pointer. +// In Windows, there are additional calling conventions (eg., __stdcall, +// __fastcall, etc.). FunctionTraits<> handles categorizing each of these into +// a normalized signature. +// +// Having a NormalizedSignature signature, reduces the combinatoric +// complexity of defintions for the InvokerN<> later. Even though there are +// only 2 syntactic variations on invoking a function, without normalizing the +// signature, there would need to be one specialization of InvokerN for each +// unique (function_type, bound_arg, unbound_args) tuple in order to match all +// function signatures. +// +// By normalizing the function signature, we reduce function_type to exactly 2. + +template <typename Sig> +struct FunctionTraits; + +// Function: Arity 0. +template <typename R> +struct FunctionTraits<R(*)()> { + typedef R (*NormalizedSig)(); + typedef false_type IsMethod; +}; + +// Method: Arity 0. +template <typename R, typename T> +struct FunctionTraits<R(T::*)()> { + typedef R (T::*NormalizedSig)(); + typedef true_type IsMethod; +}; + +// Const Method: Arity 0. +template <typename R, typename T> +struct FunctionTraits<R(T::*)() const> { + typedef R (T::*NormalizedSig)(); + typedef true_type IsMethod; +}; + +// Function: Arity 1. +template <typename R, typename X1> +struct FunctionTraits<R(*)(X1)> { + typedef R (*NormalizedSig)(X1); + typedef false_type IsMethod; +}; + +// Method: Arity 1. +template <typename R, typename T, typename X1> +struct FunctionTraits<R(T::*)(X1)> { + typedef R (T::*NormalizedSig)(X1); + typedef true_type IsMethod; +}; + +// Const Method: Arity 1. +template <typename R, typename T, typename X1> +struct FunctionTraits<R(T::*)(X1) const> { + typedef R (T::*NormalizedSig)(X1); + typedef true_type IsMethod; +}; + +// Function: Arity 2. +template <typename R, typename X1, typename X2> +struct FunctionTraits<R(*)(X1, X2)> { + typedef R (*NormalizedSig)(X1, X2); + typedef false_type IsMethod; +}; + +// Method: Arity 2. +template <typename R, typename T, typename X1, typename X2> +struct FunctionTraits<R(T::*)(X1, X2)> { + typedef R (T::*NormalizedSig)(X1, X2); + typedef true_type IsMethod; +}; + +// Const Method: Arity 2. +template <typename R, typename T, typename X1, typename X2> +struct FunctionTraits<R(T::*)(X1, X2) const> { + typedef R (T::*NormalizedSig)(X1, X2); + typedef true_type IsMethod; +}; + +// Function: Arity 3. +template <typename R, typename X1, typename X2, typename X3> +struct FunctionTraits<R(*)(X1, X2, X3)> { + typedef R (*NormalizedSig)(X1, X2, X3); + typedef false_type IsMethod; +}; + +// Method: Arity 3. +template <typename R, typename T, typename X1, typename X2, typename X3> +struct FunctionTraits<R(T::*)(X1, X2, X3)> { + typedef R (T::*NormalizedSig)(X1, X2, X3); + typedef true_type IsMethod; +}; + +// Const Method: Arity 3. +template <typename R, typename T, typename X1, typename X2, typename X3> +struct FunctionTraits<R(T::*)(X1, X2, X3) const> { + typedef R (T::*NormalizedSig)(X1, X2, X3); + typedef true_type IsMethod; +}; + +// Function: Arity 4. +template <typename R, typename X1, typename X2, typename X3, typename X4> +struct FunctionTraits<R(*)(X1, X2, X3, X4)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4); + typedef false_type IsMethod; +}; + +// Method: Arity 4. +template <typename R, typename T, typename X1, typename X2, typename X3, + typename X4> +struct FunctionTraits<R(T::*)(X1, X2, X3, X4)> { + typedef R (T::*NormalizedSig)(X1, X2, X3, X4); + typedef true_type IsMethod; +}; + +// Const Method: Arity 4. +template <typename R, typename T, typename X1, typename X2, typename X3, + typename X4> +struct FunctionTraits<R(T::*)(X1, X2, X3, X4) const> { + typedef R (T::*NormalizedSig)(X1, X2, X3, X4); + typedef true_type IsMethod; +}; + +// Function: Arity 5. +template <typename R, typename X1, typename X2, typename X3, typename X4, + typename X5> +struct FunctionTraits<R(*)(X1, X2, X3, X4, X5)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4, X5); + typedef false_type IsMethod; +}; + +// Method: Arity 5. +template <typename R, typename T, typename X1, typename X2, typename X3, + typename X4, typename X5> +struct FunctionTraits<R(T::*)(X1, X2, X3, X4, X5)> { + typedef R (T::*NormalizedSig)(X1, X2, X3, X4, X5); + typedef true_type IsMethod; +}; + +// Const Method: Arity 5. +template <typename R, typename T, typename X1, typename X2, typename X3, + typename X4, typename X5> +struct FunctionTraits<R(T::*)(X1, X2, X3, X4, X5) const> { + typedef R (T::*NormalizedSig)(X1, X2, X3, X4, X5); + typedef true_type IsMethod; +}; + +// Function: Arity 6. +template <typename R, typename X1, typename X2, typename X3, typename X4, + typename X5, typename X6> +struct FunctionTraits<R(*)(X1, X2, X3, X4, X5, X6)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4, X5, X6); + typedef false_type IsMethod; +}; + +// Method: Arity 6. +template <typename R, typename T, typename X1, typename X2, typename X3, + typename X4, typename X5, typename X6> +struct FunctionTraits<R(T::*)(X1, X2, X3, X4, X5, X6)> { + typedef R (T::*NormalizedSig)(X1, X2, X3, X4, X5, X6); + typedef true_type IsMethod; +}; + +// Const Method: Arity 6. +template <typename R, typename T, typename X1, typename X2, typename X3, + typename X4, typename X5, typename X6> +struct FunctionTraits<R(T::*)(X1, X2, X3, X4, X5, X6) const> { + typedef R (T::*NormalizedSig)(X1, X2, X3, X4, X5, X6); + typedef true_type IsMethod; +}; + +// InvokerN<> +// +// The InvokerN templates contain a static DoInvoke() function that is the key +// to implementing type erasure in the Callback() classes. // -// The FunctionTraitsN contains a static DoInvoke() function that is the key to -// implementing type erasure in the Callback() classes. DoInvoke() is a static -// function with a fixed signature that is independent of StorageType; its -// first argument is a pointer to the non-templated common baseclass of -// StorageType. This lets us store pointer to DoInvoke() in a function pointer -// that has knowledge of the specific StorageType, and thus no knowledge of the -// bound function and bound parameter types. +// DoInvoke() is a static function with a fixed signature that is independent +// of StorageType; its first argument is a pointer to the non-templated common +// baseclass of StorageType. This lets us store pointer to DoInvoke() in a +// function pointer that has knowledge of the specific StorageType, and thus +// no knowledge of the bound function and bound parameter types. // // As long as we ensure that DoInvoke() is only used with pointers there were // upcasted from the correct StorageType, we can be sure that execution is // safe. +// +// The InvokerN templates are the only point that knows the number of bound +// and unbound arguments. This is intentional because it allows the other +// templates classes in the system to only have as many specializations as +// the max arity of function we wish to support. -template <typename StorageType, typename Sig> -struct FunctionTraits0; +template <typename StorageType, typename NormalizedSig> +struct Invoker0; // Function: Arity 0 -> 0. template <typename StorageType, typename R> -struct FunctionTraits0<StorageType, R(*)()> { - typedef base::false_type IsMethod; - +struct Invoker0<StorageType, R(*)()> { static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(); @@ -58,13 +257,11 @@ struct FunctionTraits0<StorageType, R(*)()> { // Function: Arity 1 -> 1. template <typename StorageType, typename R,typename X1> -struct FunctionTraits0<StorageType, R(*)(X1)> { +struct Invoker0<StorageType, R(*)(X1)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(x1); @@ -73,14 +270,12 @@ struct FunctionTraits0<StorageType, R(*)(X1)> { // Function: Arity 2 -> 2. template <typename StorageType, typename R,typename X1, typename X2> -struct FunctionTraits0<StorageType, R(*)(X1, X2)> { +struct Invoker0<StorageType, R(*)(X1, X2)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(x1, x2); @@ -90,15 +285,13 @@ struct FunctionTraits0<StorageType, R(*)(X1, X2)> { // Function: Arity 3 -> 3. template <typename StorageType, typename R,typename X1, typename X2, typename X3> -struct FunctionTraits0<StorageType, R(*)(X1, X2, X3)> { +struct Invoker0<StorageType, R(*)(X1, X2, X3)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || is_non_const_reference<X3>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, const X3& x3) { StorageType* invoker = static_cast<StorageType*>(base); @@ -109,7 +302,7 @@ struct FunctionTraits0<StorageType, R(*)(X1, X2, X3)> { // Function: Arity 4 -> 4. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4)> { +struct Invoker0<StorageType, R(*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -117,8 +310,6 @@ struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, const X3& x3, const X4& x4) { StorageType* invoker = static_cast<StorageType*>(base); @@ -129,7 +320,7 @@ struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4)> { // Function: Arity 5 -> 5. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4, X5)> { +struct Invoker0<StorageType, R(*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -138,8 +329,6 @@ struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, const X3& x3, const X4& x4, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); @@ -150,7 +339,7 @@ struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4, X5)> { // Function: Arity 6 -> 6. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5, typename X6> -struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { +struct Invoker0<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -160,8 +349,6 @@ struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { is_non_const_reference<X6>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, const X3& x3, const X4& x4, const X5& x5, const X6& x6) { StorageType* invoker = static_cast<StorageType*>(base); @@ -169,18 +356,16 @@ struct FunctionTraits0<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { } }; -template <typename StorageType, typename Sig> -struct FunctionTraits1; +template <typename StorageType, typename NormalizedSig> +struct Invoker1; // Function: Arity 1 -> 0. template <typename StorageType, typename R,typename X1> -struct FunctionTraits1<StorageType, R(*)(X1)> { +struct Invoker1<StorageType, R(*)(X1)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_)); @@ -189,36 +374,21 @@ struct FunctionTraits1<StorageType, R(*)(X1)> { // Method: Arity 0 -> 0. template <typename StorageType, typename R, typename T> -struct FunctionTraits1<StorageType, R(T::*)()> { - typedef base::true_type IsMethod; - +struct Invoker1<StorageType, R(T::*)()> { static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(); } }; -// Const Method: Arity 0 -> 0. -template <typename StorageType, typename R, typename T> -struct FunctionTraits1<StorageType, R(T::*)() const> { - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base ) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(); - } -}; - // Function: Arity 2 -> 1. template <typename StorageType, typename R,typename X1, typename X2> -struct FunctionTraits1<StorageType, R(*)(X1, X2)> { +struct Invoker1<StorageType, R(*)(X1, X2)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), x2); @@ -227,46 +397,27 @@ struct FunctionTraits1<StorageType, R(*)(X1, X2)> { // Method: Arity 1 -> 1. template <typename StorageType, typename R, typename T, typename X1> -struct FunctionTraits1<StorageType, R(T::*)(X1)> { +struct Invoker1<StorageType, R(T::*)(X1)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(x1); } }; -// Const Method: Arity 1 -> 1. -template <typename StorageType, typename R, typename T, typename X1> -struct FunctionTraits1<StorageType, R(T::*)(X1) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X1& x1) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(x1); - } -}; - // Function: Arity 3 -> 2. template <typename StorageType, typename R,typename X1, typename X2, typename X3> -struct FunctionTraits1<StorageType, R(*)(X1, X2, X3)> { +struct Invoker1<StorageType, R(*)(X1, X2, X3)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || is_non_const_reference<X3>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), x2, x3); @@ -276,41 +427,22 @@ struct FunctionTraits1<StorageType, R(*)(X1, X2, X3)> { // Method: Arity 2 -> 2. template <typename StorageType, typename R, typename T, typename X1, typename X2> -struct FunctionTraits1<StorageType, R(T::*)(X1, X2)> { +struct Invoker1<StorageType, R(T::*)(X1, X2)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(x1, x2); } }; -// Const Method: Arity 2 -> 2. -template <typename StorageType, typename R, typename T, typename X1, - typename X2> -struct FunctionTraits1<StorageType, R(T::*)(X1, X2) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(x1, x2); - } -}; - // Function: Arity 4 -> 3. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4)> { +struct Invoker1<StorageType, R(*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -318,8 +450,6 @@ struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3, const X4& x4) { StorageType* invoker = static_cast<StorageType*>(base); @@ -330,15 +460,13 @@ struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4)> { // Method: Arity 3 -> 3. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3> -struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3)> { +struct Invoker1<StorageType, R(T::*)(X1, X2, X3)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || is_non_const_reference<X3>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, const X3& x3) { StorageType* invoker = static_cast<StorageType*>(base); @@ -346,28 +474,10 @@ struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3)> { } }; -// Const Method: Arity 3 -> 3. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3> -struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, - const X3& x3) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(x1, x2, x3); - } -}; - // Function: Arity 5 -> 4. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4, X5)> { +struct Invoker1<StorageType, R(*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -376,8 +486,6 @@ struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3, const X4& x4, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); @@ -388,7 +496,7 @@ struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4, X5)> { // Method: Arity 4 -> 4. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3, X4)> { +struct Invoker1<StorageType, R(T::*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -396,8 +504,6 @@ struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, const X3& x3, const X4& x4) { StorageType* invoker = static_cast<StorageType*>(base); @@ -405,29 +511,10 @@ struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3, X4)> { } }; -// Const Method: Arity 4 -> 4. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4> -struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3, X4) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, - const X3& x3, const X4& x4) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(x1, x2, x3, x4); - } -}; - // Function: Arity 6 -> 5. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5, typename X6> -struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { +struct Invoker1<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -437,8 +524,6 @@ struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { is_non_const_reference<X6>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3, const X4& x4, const X5& x5, const X6& x6) { StorageType* invoker = static_cast<StorageType*>(base); @@ -449,7 +534,7 @@ struct FunctionTraits1<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { // Method: Arity 5 -> 5. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { +struct Invoker1<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -458,8 +543,6 @@ struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, const X3& x3, const X4& x4, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); @@ -467,39 +550,17 @@ struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { } }; -// Const Method: Arity 5 -> 5. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits1<StorageType, R(T::*)(X1, X2, X3, X4, X5) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value || - is_non_const_reference<X5>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X1& x1, const X2& x2, - const X3& x3, const X4& x4, const X5& x5) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(x1, x2, x3, x4, x5); - } -}; - -template <typename StorageType, typename Sig> -struct FunctionTraits2; +template <typename StorageType, typename NormalizedSig> +struct Invoker2; // Function: Arity 2 -> 0. template <typename StorageType, typename R,typename X1, typename X2> -struct FunctionTraits2<StorageType, R(*)(X1, X2)> { +struct Invoker2<StorageType, R(*)(X1, X2)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_)); @@ -508,46 +569,27 @@ struct FunctionTraits2<StorageType, R(*)(X1, X2)> { // Method: Arity 1 -> 0. template <typename StorageType, typename R, typename T, typename X1> -struct FunctionTraits2<StorageType, R(T::*)(X1)> { +struct Invoker2<StorageType, R(T::*)(X1)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_)); } }; -// Const Method: Arity 1 -> 0. -template <typename StorageType, typename R, typename T, typename X1> -struct FunctionTraits2<StorageType, R(T::*)(X1) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base ) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_)); - } -}; - // Function: Arity 3 -> 1. template <typename StorageType, typename R,typename X1, typename X2, typename X3> -struct FunctionTraits2<StorageType, R(*)(X1, X2, X3)> { +struct Invoker2<StorageType, R(*)(X1, X2, X3)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || is_non_const_reference<X3>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X3& x3) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), x3); @@ -557,41 +599,22 @@ struct FunctionTraits2<StorageType, R(*)(X1, X2, X3)> { // Method: Arity 2 -> 1. template <typename StorageType, typename R, typename T, typename X1, typename X2> -struct FunctionTraits2<StorageType, R(T::*)(X1, X2)> { +struct Invoker2<StorageType, R(T::*)(X1, X2)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), x2); } }; -// Const Method: Arity 2 -> 1. -template <typename StorageType, typename R, typename T, typename X1, - typename X2> -struct FunctionTraits2<StorageType, R(T::*)(X1, X2) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X2& x2) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), x2); - } -}; - // Function: Arity 4 -> 2. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4)> { +struct Invoker2<StorageType, R(*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -599,8 +622,6 @@ struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X3& x3, const X4& x4) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), x3, x4); @@ -610,42 +631,23 @@ struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4)> { // Method: Arity 3 -> 2. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3> -struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3)> { +struct Invoker2<StorageType, R(T::*)(X1, X2, X3)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || is_non_const_reference<X3>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), x2, x3); } }; -// Const Method: Arity 3 -> 2. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3> -struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), x2, x3); - } -}; - // Function: Arity 5 -> 3. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4, X5)> { +struct Invoker2<StorageType, R(*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -654,8 +656,6 @@ struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X3& x3, const X4& x4, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); @@ -666,7 +666,7 @@ struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4, X5)> { // Method: Arity 4 -> 3. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3, X4)> { +struct Invoker2<StorageType, R(T::*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -674,8 +674,6 @@ struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3, const X4& x4) { StorageType* invoker = static_cast<StorageType*>(base); @@ -684,30 +682,10 @@ struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3, X4)> { } }; -// Const Method: Arity 4 -> 3. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4> -struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3, X4) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3, - const X4& x4) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), x2, x3, - x4); - } -}; - // Function: Arity 6 -> 4. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5, typename X6> -struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { +struct Invoker2<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -717,8 +695,6 @@ struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { is_non_const_reference<X6>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X3& x3, const X4& x4, const X5& x5, const X6& x6) { StorageType* invoker = static_cast<StorageType*>(base); @@ -730,7 +706,7 @@ struct FunctionTraits2<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { // Method: Arity 5 -> 4. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { +struct Invoker2<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -739,8 +715,6 @@ struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3, const X4& x4, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); @@ -749,42 +723,19 @@ struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { } }; -// Const Method: Arity 5 -> 4. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits2<StorageType, R(T::*)(X1, X2, X3, X4, X5) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value || - is_non_const_reference<X5>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3, - const X4& x4, const X5& x5) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), x2, x3, - x4, x5); - } -}; - -template <typename StorageType, typename Sig> -struct FunctionTraits3; +template <typename StorageType, typename NormalizedSig> +struct Invoker3; // Function: Arity 3 -> 0. template <typename StorageType, typename R,typename X1, typename X2, typename X3> -struct FunctionTraits3<StorageType, R(*)(X1, X2, X3)> { +struct Invoker3<StorageType, R(*)(X1, X2, X3)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || is_non_const_reference<X3>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -795,14 +746,12 @@ struct FunctionTraits3<StorageType, R(*)(X1, X2, X3)> { // Method: Arity 2 -> 0. template <typename StorageType, typename R, typename T, typename X1, typename X2> -struct FunctionTraits3<StorageType, R(T::*)(X1, X2)> { +struct Invoker3<StorageType, R(T::*)(X1, X2)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -810,28 +759,10 @@ struct FunctionTraits3<StorageType, R(T::*)(X1, X2)> { } }; -// Const Method: Arity 2 -> 0. -template <typename StorageType, typename R, typename T, typename X1, - typename X2> -struct FunctionTraits3<StorageType, R(T::*)(X1, X2) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base ) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_)); - } -}; - // Function: Arity 4 -> 1. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4)> { +struct Invoker3<StorageType, R(*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -839,8 +770,6 @@ struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X4& x4) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -851,15 +780,13 @@ struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4)> { // Method: Arity 3 -> 1. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3> -struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3)> { +struct Invoker3<StorageType, R(T::*)(X1, X2, X3)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || is_non_const_reference<X3>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X3& x3) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -867,28 +794,10 @@ struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3)> { } }; -// Const Method: Arity 3 -> 1. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3> -struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X3& x3) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), x3); - } -}; - // Function: Arity 5 -> 2. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4, X5)> { +struct Invoker3<StorageType, R(*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -897,8 +806,6 @@ struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X4& x4, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -909,7 +816,7 @@ struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4, X5)> { // Method: Arity 4 -> 2. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3, X4)> { +struct Invoker3<StorageType, R(T::*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -917,8 +824,6 @@ struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X3& x3, const X4& x4) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -926,29 +831,10 @@ struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3, X4)> { } }; -// Const Method: Arity 4 -> 2. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4> -struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3, X4) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X3& x3, const X4& x4) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), x3, x4); - } -}; - // Function: Arity 6 -> 3. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5, typename X6> -struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { +struct Invoker3<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -958,8 +844,6 @@ struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { is_non_const_reference<X6>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X4& x4, const X5& x5, const X6& x6) { StorageType* invoker = static_cast<StorageType*>(base); @@ -971,7 +855,7 @@ struct FunctionTraits3<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { // Method: Arity 5 -> 3. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { +struct Invoker3<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -980,8 +864,6 @@ struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X3& x3, const X4& x4, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); @@ -990,34 +872,13 @@ struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { } }; -// Const Method: Arity 5 -> 3. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits3<StorageType, R(T::*)(X1, X2, X3, X4, X5) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value || - is_non_const_reference<X5>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X3& x3, const X4& x4, - const X5& x5) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), x3, x4, x5); - } -}; - -template <typename StorageType, typename Sig> -struct FunctionTraits4; +template <typename StorageType, typename NormalizedSig> +struct Invoker4; // Function: Arity 4 -> 0. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4)> { +struct Invoker4<StorageType, R(*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1025,8 +886,6 @@ struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -1037,15 +896,13 @@ struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4)> { // Method: Arity 3 -> 0. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3> -struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3)> { +struct Invoker4<StorageType, R(T::*)(X1, X2, X3)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || is_non_const_reference<X3>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -1053,28 +910,10 @@ struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3)> { } }; -// Const Method: Arity 3 -> 0. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3> -struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base ) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), Unwrap(invoker->p4_)); - } -}; - // Function: Arity 5 -> 1. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4, X5)> { +struct Invoker4<StorageType, R(*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1083,8 +922,6 @@ struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -1095,7 +932,7 @@ struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4, X5)> { // Method: Arity 4 -> 1. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3, X4)> { +struct Invoker4<StorageType, R(T::*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1103,8 +940,6 @@ struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X4& x4) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -1112,29 +947,10 @@ struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3, X4)> { } }; -// Const Method: Arity 4 -> 1. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4> -struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3, X4) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X4& x4) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), Unwrap(invoker->p4_), x4); - } -}; - // Function: Arity 6 -> 2. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5, typename X6> -struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { +struct Invoker4<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1144,8 +960,6 @@ struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { is_non_const_reference<X6>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X5& x5, const X6& x6) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -1156,7 +970,7 @@ struct FunctionTraits4<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { // Method: Arity 5 -> 2. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { +struct Invoker4<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1165,8 +979,6 @@ struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X4& x4, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -1174,33 +986,13 @@ struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { } }; -// Const Method: Arity 5 -> 2. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits4<StorageType, R(T::*)(X1, X2, X3, X4, X5) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value || - is_non_const_reference<X5>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X4& x4, const X5& x5) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), Unwrap(invoker->p4_), x4, x5); - } -}; - -template <typename StorageType, typename Sig> -struct FunctionTraits5; +template <typename StorageType, typename NormalizedSig> +struct Invoker5; // Function: Arity 5 -> 0. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits5<StorageType, R(*)(X1, X2, X3, X4, X5)> { +struct Invoker5<StorageType, R(*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1209,8 +1001,6 @@ struct FunctionTraits5<StorageType, R(*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -1221,7 +1011,7 @@ struct FunctionTraits5<StorageType, R(*)(X1, X2, X3, X4, X5)> { // Method: Arity 4 -> 0. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4> -struct FunctionTraits5<StorageType, R(T::*)(X1, X2, X3, X4)> { +struct Invoker5<StorageType, R(T::*)(X1, X2, X3, X4)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1229,8 +1019,6 @@ struct FunctionTraits5<StorageType, R(T::*)(X1, X2, X3, X4)> { is_non_const_reference<X4>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -1238,29 +1026,10 @@ struct FunctionTraits5<StorageType, R(T::*)(X1, X2, X3, X4)> { } }; -// Const Method: Arity 4 -> 0. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4> -struct FunctionTraits5<StorageType, R(T::*)(X1, X2, X3, X4) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base ) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), Unwrap(invoker->p4_), Unwrap(invoker->p5_)); - } -}; - // Function: Arity 6 -> 1. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5, typename X6> -struct FunctionTraits5<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { +struct Invoker5<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1270,8 +1039,6 @@ struct FunctionTraits5<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { is_non_const_reference<X6>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X6& x6) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -1282,7 +1049,7 @@ struct FunctionTraits5<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { // Method: Arity 5 -> 1. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits5<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { +struct Invoker5<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1291,8 +1058,6 @@ struct FunctionTraits5<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base, const X5& x5) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -1300,33 +1065,13 @@ struct FunctionTraits5<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { } }; -// Const Method: Arity 5 -> 1. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits5<StorageType, R(T::*)(X1, X2, X3, X4, X5) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value || - is_non_const_reference<X5>::value ), - do_not_bind_functions_with_nonconst_ref); - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base, const X5& x5) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), Unwrap(invoker->p4_), Unwrap(invoker->p5_), x5); - } -}; - -template <typename StorageType, typename Sig> -struct FunctionTraits6; +template <typename StorageType, typename NormalizedSig> +struct Invoker6; // Function: Arity 6 -> 0. template <typename StorageType, typename R,typename X1, typename X2, typename X3, typename X4, typename X5, typename X6> -struct FunctionTraits6<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { +struct Invoker6<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1336,8 +1081,6 @@ struct FunctionTraits6<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { is_non_const_reference<X6>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), @@ -1349,7 +1092,7 @@ struct FunctionTraits6<StorageType, R(*)(X1, X2, X3, X4, X5, X6)> { // Method: Arity 5 -> 0. template <typename StorageType, typename R, typename T, typename X1, typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits6<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { +struct Invoker6<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { COMPILE_ASSERT( !( is_non_const_reference<X1>::value || is_non_const_reference<X2>::value || @@ -1358,8 +1101,6 @@ struct FunctionTraits6<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { is_non_const_reference<X5>::value ), do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base) { StorageType* invoker = static_cast<StorageType*>(base); return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), @@ -1368,47 +1109,28 @@ struct FunctionTraits6<StorageType, R(T::*)(X1, X2, X3, X4, X5)> { } }; -// Const Method: Arity 5 -> 0. -template <typename StorageType, typename R, typename T, typename X1, - typename X2, typename X3, typename X4, typename X5> -struct FunctionTraits6<StorageType, R(T::*)(X1, X2, X3, X4, X5) const> { - COMPILE_ASSERT( - !(is_non_const_reference<X1>::value || is_non_const_reference<X2>::value - || is_non_const_reference<X3>::value || - is_non_const_reference<X4>::value || - is_non_const_reference<X5>::value ), - do_not_bind_functions_with_nonconst_ref); - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base ) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)(Unwrap(invoker->p2_), - Unwrap(invoker->p3_), Unwrap(invoker->p4_), Unwrap(invoker->p5_), - Unwrap(invoker->p6_)); - } -}; - - -// These are the actual storage classes for the invokers. +// InvokerStorageN<> +// +// These are the actual storage classes for the Invokers. // // Though these types are "classes", they are being used as structs with // all member variable public. We cannot make it a struct because it inherits // from a class which causes a compiler warning. We cannot add a "Run()" method // that forwards the unbound arguments because that would require we unwrap the -// Sig type like in FunctionTraitsN above to know the return type, and the arity +// Sig type like in InvokerN above to know the return type, and the arity // of Run(). // -// An alternate solution would be to merge FunctionTraitsN and InvokerStorageN, +// An alternate solution would be to merge InvokerN and InvokerStorageN, // but the generated code seemed harder to read. template <typename Sig> class InvokerStorage0 : public InvokerStorageBase { public: typedef InvokerStorage0 StorageType; - typedef FunctionTraits0<StorageType, Sig> FunctionTraits; - typedef typename FunctionTraits::IsMethod IsMethod; - + typedef FunctionTraits<Sig> TargetTraits; + typedef Invoker0<StorageType, typename TargetTraits::NormalizedSig> Invoker; + typedef typename TargetTraits::IsMethod IsMethod; InvokerStorage0(Sig f) @@ -1424,9 +1146,9 @@ template <typename Sig, typename P1> class InvokerStorage1 : public InvokerStorageBase { public: typedef InvokerStorage1 StorageType; - typedef FunctionTraits1<StorageType, Sig> FunctionTraits; - typedef typename FunctionTraits::IsMethod IsMethod; - + typedef FunctionTraits<Sig> TargetTraits; + typedef Invoker1<StorageType, typename TargetTraits::NormalizedSig> Invoker; + typedef typename TargetTraits::IsMethod IsMethod; // For methods, we need to be careful for parameter 1. We skip the // scoped_refptr check because the binder itself takes care of this. We also // disallow binding of an array as the method's target object. @@ -1454,9 +1176,9 @@ template <typename Sig, typename P1, typename P2> class InvokerStorage2 : public InvokerStorageBase { public: typedef InvokerStorage2 StorageType; - typedef FunctionTraits2<StorageType, Sig> FunctionTraits; - typedef typename FunctionTraits::IsMethod IsMethod; - + typedef FunctionTraits<Sig> TargetTraits; + typedef Invoker2<StorageType, typename TargetTraits::NormalizedSig> Invoker; + typedef typename TargetTraits::IsMethod IsMethod; // For methods, we need to be careful for parameter 1. We skip the // scoped_refptr check because the binder itself takes care of this. We also // disallow binding of an array as the method's target object. @@ -1488,9 +1210,9 @@ template <typename Sig, typename P1, typename P2, typename P3> class InvokerStorage3 : public InvokerStorageBase { public: typedef InvokerStorage3 StorageType; - typedef FunctionTraits3<StorageType, Sig> FunctionTraits; - typedef typename FunctionTraits::IsMethod IsMethod; - + typedef FunctionTraits<Sig> TargetTraits; + typedef Invoker3<StorageType, typename TargetTraits::NormalizedSig> Invoker; + typedef typename TargetTraits::IsMethod IsMethod; // For methods, we need to be careful for parameter 1. We skip the // scoped_refptr check because the binder itself takes care of this. We also // disallow binding of an array as the method's target object. @@ -1526,9 +1248,9 @@ template <typename Sig, typename P1, typename P2, typename P3, typename P4> class InvokerStorage4 : public InvokerStorageBase { public: typedef InvokerStorage4 StorageType; - typedef FunctionTraits4<StorageType, Sig> FunctionTraits; - typedef typename FunctionTraits::IsMethod IsMethod; - + typedef FunctionTraits<Sig> TargetTraits; + typedef Invoker4<StorageType, typename TargetTraits::NormalizedSig> Invoker; + typedef typename TargetTraits::IsMethod IsMethod; // For methods, we need to be careful for parameter 1. We skip the // scoped_refptr check because the binder itself takes care of this. We also // disallow binding of an array as the method's target object. @@ -1569,9 +1291,9 @@ template <typename Sig, typename P1, typename P2, typename P3, typename P4, class InvokerStorage5 : public InvokerStorageBase { public: typedef InvokerStorage5 StorageType; - typedef FunctionTraits5<StorageType, Sig> FunctionTraits; - typedef typename FunctionTraits::IsMethod IsMethod; - + typedef FunctionTraits<Sig> TargetTraits; + typedef Invoker5<StorageType, typename TargetTraits::NormalizedSig> Invoker; + typedef typename TargetTraits::IsMethod IsMethod; // For methods, we need to be careful for parameter 1. We skip the // scoped_refptr check because the binder itself takes care of this. We also // disallow binding of an array as the method's target object. @@ -1617,9 +1339,9 @@ template <typename Sig, typename P1, typename P2, typename P3, typename P4, class InvokerStorage6 : public InvokerStorageBase { public: typedef InvokerStorage6 StorageType; - typedef FunctionTraits6<StorageType, Sig> FunctionTraits; - typedef typename FunctionTraits::IsMethod IsMethod; - + typedef FunctionTraits<Sig> TargetTraits; + typedef Invoker6<StorageType, typename TargetTraits::NormalizedSig> Invoker; + typedef typename TargetTraits::IsMethod IsMethod; // For methods, we need to be careful for parameter 1. We skip the // scoped_refptr check because the binder itself takes care of this. We also // disallow binding of an array as the method's target object. diff --git a/base/bind_internal.h.pump b/base/bind_internal.h.pump index 132b0db..84fb2ef 100644 --- a/base/bind_internal.h.pump +++ b/base/bind_internal.h.pump @@ -16,8 +16,13 @@ $var MAX_ARITY = 6 #pragma once #include "base/bind_helpers.h" -#include "base/callback_helpers.h" +#include "base/callback_internal.h" #include "base/template_util.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/bind_internal_win.h" +#endif namespace base { namespace internal { @@ -25,32 +30,103 @@ namespace internal { // The method by which a function is invoked is determined by 3 different // dimensions: // -// 1) The type of function (normal, method, const-method) -// 2) The arity of the function +// 1) The type of function (normal or method). +// 2) The arity of the function. // 3) The number of bound parameters. // -// The FunctionTraitsN classes unwrap the function signature type to -// specialize based on the first two dimensions. The N in FunctionTraitsN -// specifies the 3rd dimension. We could have specified the unbound parameters -// via template parameters, but this method looked cleaner. +// The templates below handle the determination of each of these dimensions. +// In brief: +// +// FunctionTraits<> -- Provides a normalied signature, and other traits. +// InvokerN<> -- Provides a DoInvoke() function that actually executes +// a calback. +// InvokerStorageN<> -- Provides storage for the bound parameters, and +// typedefs to the above. +// +// More details about the design of each class is included in a comment closer +// to their defition. + +// FunctionTraits<> +// +// The FunctionTraits<> template determines the type of function, and also +// creates a NormalizedType used to select the InvokerN classes. It turns out +// that syntactically, you only really have 2 variations when invoking a +// funciton pointer: normal, and method. One is invoked func_ptr(arg1). The +// other is invoked (*obj_->method_ptr(arg1)). +// +// However, in the type system, there are many more distinctions. In standard +// C++, there's all variations of const, and volatile on the function pointer. +// In Windows, there are additional calling conventions (eg., __stdcall, +// __fastcall, etc.). FunctionTraits<> handles categorizing each of these into +// a normalized signature. // -// The FunctionTraitsN contains a static DoInvoke() function that is the key to -// implementing type erasure in the Callback() classes. DoInvoke() is a static -// function with a fixed signature that is independent of StorageType; its -// first argument is a pointer to the non-templated common baseclass of -// StorageType. This lets us store pointer to DoInvoke() in a function pointer -// that has knowledge of the specific StorageType, and thus no knowledge of the -// bound function and bound parameter types. +// Having a NormalizedSignature signature, reduces the combinatoric +// complexity of defintions for the InvokerN<> later. Even though there are +// only 2 syntactic variations on invoking a function, without normalizing the +// signature, there would need to be one specialization of InvokerN for each +// unique (function_type, bound_arg, unbound_args) tuple in order to match all +// function signatures. +// +// By normalizing the function signature, we reduce function_type to exactly 2. + +template <typename Sig> +struct FunctionTraits; + +$range ARITY 0..MAX_ARITY +$for ARITY [[ +$range ARG 1..ARITY + +// Function: Arity $(ARITY). +template <typename R[[]] +$if ARITY > 0[[, ]] $for ARG , [[typename X$(ARG)]]> +struct FunctionTraits<R(*)($for ARG , [[X$(ARG)]])> { + typedef R (*NormalizedSig)($for ARG , [[X$(ARG)]]); + typedef false_type IsMethod; +}; + +// Method: Arity $(ARITY). +template <typename R, typename T[[]] +$if ARITY > 0[[, ]] $for ARG , [[typename X$(ARG)]]> +struct FunctionTraits<R(T::*)($for ARG , [[X$(ARG)]])> { + typedef R (T::*NormalizedSig)($for ARG , [[X$(ARG)]]); + typedef true_type IsMethod; +}; + +// Const Method: Arity $(ARITY). +template <typename R, typename T[[]] +$if ARITY > 0[[, ]] $for ARG , [[typename X$(ARG)]]> +struct FunctionTraits<R(T::*)($for ARG , [[X$(ARG)]]) const> { + typedef R (T::*NormalizedSig)($for ARG , [[X$(ARG)]]); + typedef true_type IsMethod; +}; + +]] $$for ARITY + +// InvokerN<> +// +// The InvokerN templates contain a static DoInvoke() function that is the key +// to implementing type erasure in the Callback() classes. +// +// DoInvoke() is a static function with a fixed signature that is independent +// of StorageType; its first argument is a pointer to the non-templated common +// baseclass of StorageType. This lets us store pointer to DoInvoke() in a +// function pointer that has knowledge of the specific StorageType, and thus +// no knowledge of the bound function and bound parameter types. // // As long as we ensure that DoInvoke() is only used with pointers there were // upcasted from the correct StorageType, we can be sure that execution is // safe. +// +// The InvokerN templates are the only point that knows the number of bound +// and unbound arguments. This is intentional because it allows the other +// templates classes in the system to only have as many specializations as +// the max arity of function we wish to support. $range BOUND 0..MAX_ARITY $for BOUND [[ -template <typename StorageType, typename Sig> -struct FunctionTraits$(BOUND); +template <typename StorageType, typename NormalizedSig> +struct Invoker$(BOUND); $range ARITY 0..MAX_ARITY $for ARITY [[ @@ -74,7 +150,7 @@ $range M_UNBOUND_ARG (M_ARITY - UNBOUND + 1)..M_ARITY template <typename StorageType, typename R[[]] $if ARITY > 0 [[,]][[]] $for ARG , [[typename X$(ARG)]]> -struct FunctionTraits$(BOUND)<StorageType, R(*)($for ARG , [[X$(ARG)]])> { +struct Invoker$(BOUND)<StorageType, R(*)($for ARG , [[X$(ARG)]])> { $if ARITY > 0 [[ COMPILE_ASSERT( @@ -83,8 +159,6 @@ $if ARITY > 0 [[ ]] - typedef base::false_type IsMethod; - static R DoInvoke(InvokerStorageBase* base[[]] $if UNBOUND != 0 [[, ]][[]] $for UNBOUND_ARG , [[const X$(UNBOUND_ARG)& x$(UNBOUND_ARG)]]) { @@ -101,7 +175,7 @@ $if BOUND > 0 [[ // Method: Arity $(M_ARITY) -> $(UNBOUND). template <typename StorageType, typename R, typename T[[]] $if M_ARITY > 0[[, ]] $for M_ARG , [[typename X$(M_ARG)]]> -struct FunctionTraits$(BOUND)<StorageType, R(T::*)($for M_ARG , [[X$(M_ARG)]])> { +struct Invoker$(BOUND)<StorageType, R(T::*)($for M_ARG , [[X$(M_ARG)]])> { $if M_ARITY > 0 [[ COMPILE_ASSERT( @@ -110,8 +184,6 @@ $if M_ARITY > 0 [[ ]] - typedef base::true_type IsMethod; - static R DoInvoke(InvokerStorageBase* base[[]] $if UNBOUND > 0 [[, ]][[]] $for M_UNBOUND_ARG , [[const X$(M_UNBOUND_ARG)& x$(M_UNBOUND_ARG)]]) { @@ -123,31 +195,6 @@ $for M_UNBOUND_ARG , [[x$(M_UNBOUND_ARG)]]); } }; -// Const Method: Arity $(M_ARITY) -> $(UNBOUND). -template <typename StorageType, typename R, typename T[[]] -$if M_ARITY > 0[[, ]] $for M_ARG , [[typename X$(M_ARG)]]> -struct FunctionTraits$(BOUND)<StorageType, R(T::*)($for M_ARG , [[X$(M_ARG)]]) const> { -$if M_ARITY > 0 [[ - - COMPILE_ASSERT( - !($for M_ARG || [[is_non_const_reference<X$(M_ARG)>::value ]]), - do_not_bind_functions_with_nonconst_ref); - -]] - - typedef base::true_type IsMethod; - - static R DoInvoke(InvokerStorageBase* base[[]] -$if UNBOUND > 0 [[, ]] -[[]] $for M_UNBOUND_ARG , [[const X$(M_UNBOUND_ARG)& x$(M_UNBOUND_ARG)]]) { - StorageType* invoker = static_cast<StorageType*>(base); - return (Unwrap(invoker->p1_)->*invoker->f_)([[]] -$for M_BOUND_ARG , [[Unwrap(invoker->p$(M_BOUND_ARG)_)]][[]] -$if UNBOUND > 0 [[$if BOUND > 1 [[, ]]]][[]] -$for M_UNBOUND_ARG , [[x$(M_UNBOUND_ARG)]]); - } -}; - ]] $$ if BOUND ]] $$ if UNBOUND @@ -155,16 +202,18 @@ $for M_UNBOUND_ARG , [[x$(M_UNBOUND_ARG)]]); ]] $$ for BOUND -// These are the actual storage classes for the invokers. +// InvokerStorageN<> +// +// These are the actual storage classes for the Invokers. // // Though these types are "classes", they are being used as structs with // all member variable public. We cannot make it a struct because it inherits // from a class which causes a compiler warning. We cannot add a "Run()" method // that forwards the unbound arguments because that would require we unwrap the -// Sig type like in FunctionTraitsN above to know the return type, and the arity +// Sig type like in InvokerN above to know the return type, and the arity // of Run(). // -// An alternate solution would be to merge FunctionTraitsN and InvokerStorageN, +// An alternate solution would be to merge InvokerN and InvokerStorageN, // but the generated code seemed harder to read. $for BOUND [[ @@ -176,9 +225,9 @@ $for BOUND_ARG , [[typename P$(BOUND_ARG)]]> class InvokerStorage$(BOUND) : public InvokerStorageBase { public: typedef InvokerStorage$(BOUND) StorageType; - typedef FunctionTraits$(BOUND)<StorageType, Sig> FunctionTraits; - typedef typename FunctionTraits::IsMethod IsMethod; - + typedef FunctionTraits<Sig> TargetTraits; + typedef Invoker$(BOUND)<StorageType, typename TargetTraits::NormalizedSig> Invoker; + typedef typename TargetTraits::IsMethod IsMethod; $for BOUND_ARG [[ $if BOUND_ARG == 1 [[ diff --git a/base/bind_internal_win.h b/base/bind_internal_win.h new file mode 100644 index 0000000..dab8d51 --- /dev/null +++ b/base/bind_internal_win.h @@ -0,0 +1,128 @@ +// This file was GENERATED by command: +// pump.py bind_internal_win.h.pump +// DO NOT EDIT BY HAND!!! + + +// 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. + +// Specializations of FunctionTraits<> for Windows specific calling +// conventions. Please see base/bind_internal.h for more info. + +#ifndef BASE_BIND_INTERNAL_WIN_H_ +#define BASE_BIND_INTERNAL_WIN_H_ +#pragma once + +namespace base { +namespace internal { + +template <typename Sig> +struct FunctionTraits; + +// __stdcall Function: Arity 0. +template <typename R> +struct FunctionTraits<R(__stdcall *)()> { + typedef R (*NormalizedSig)(); + typedef false_type IsMethod; +}; + +// __fastcall Function: Arity 0. +template <typename R> +struct FunctionTraits<R(__fastcall *)()> { + typedef R (*NormalizedSig)(); + typedef false_type IsMethod; +}; + +// __stdcall Function: Arity 1. +template <typename R, typename X1> +struct FunctionTraits<R(__stdcall *)(X1)> { + typedef R (*NormalizedSig)(X1); + typedef false_type IsMethod; +}; + +// __fastcall Function: Arity 1. +template <typename R, typename X1> +struct FunctionTraits<R(__fastcall *)(X1)> { + typedef R (*NormalizedSig)(X1); + typedef false_type IsMethod; +}; + +// __stdcall Function: Arity 2. +template <typename R, typename X1, typename X2> +struct FunctionTraits<R(__stdcall *)(X1, X2)> { + typedef R (*NormalizedSig)(X1, X2); + typedef false_type IsMethod; +}; + +// __fastcall Function: Arity 2. +template <typename R, typename X1, typename X2> +struct FunctionTraits<R(__fastcall *)(X1, X2)> { + typedef R (*NormalizedSig)(X1, X2); + typedef false_type IsMethod; +}; + +// __stdcall Function: Arity 3. +template <typename R, typename X1, typename X2, typename X3> +struct FunctionTraits<R(__stdcall *)(X1, X2, X3)> { + typedef R (*NormalizedSig)(X1, X2, X3); + typedef false_type IsMethod; +}; + +// __fastcall Function: Arity 3. +template <typename R, typename X1, typename X2, typename X3> +struct FunctionTraits<R(__fastcall *)(X1, X2, X3)> { + typedef R (*NormalizedSig)(X1, X2, X3); + typedef false_type IsMethod; +}; + +// __stdcall Function: Arity 4. +template <typename R, typename X1, typename X2, typename X3, typename X4> +struct FunctionTraits<R(__stdcall *)(X1, X2, X3, X4)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4); + typedef false_type IsMethod; +}; + +// __fastcall Function: Arity 4. +template <typename R, typename X1, typename X2, typename X3, typename X4> +struct FunctionTraits<R(__fastcall *)(X1, X2, X3, X4)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4); + typedef false_type IsMethod; +}; + +// __stdcall Function: Arity 5. +template <typename R, typename X1, typename X2, typename X3, typename X4, + typename X5> +struct FunctionTraits<R(__stdcall *)(X1, X2, X3, X4, X5)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4, X5); + typedef false_type IsMethod; +}; + +// __fastcall Function: Arity 5. +template <typename R, typename X1, typename X2, typename X3, typename X4, + typename X5> +struct FunctionTraits<R(__fastcall *)(X1, X2, X3, X4, X5)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4, X5); + typedef false_type IsMethod; +}; + +// __stdcall Function: Arity 6. +template <typename R, typename X1, typename X2, typename X3, typename X4, + typename X5, typename X6> +struct FunctionTraits<R(__stdcall *)(X1, X2, X3, X4, X5, X6)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4, X5, X6); + typedef false_type IsMethod; +}; + +// __fastcall Function: Arity 6. +template <typename R, typename X1, typename X2, typename X3, typename X4, + typename X5, typename X6> +struct FunctionTraits<R(__fastcall *)(X1, X2, X3, X4, X5, X6)> { + typedef R (*NormalizedSig)(X1, X2, X3, X4, X5, X6); + typedef false_type IsMethod; +}; + +} // namespace internal +} // namespace base + +#endif // BASE_BIND_INTERNAL_WIN_H_ diff --git a/base/bind_internal_win.h.pump b/base/bind_internal_win.h.pump new file mode 100644 index 0000000..4d213a3 --- /dev/null +++ b/base/bind_internal_win.h.pump @@ -0,0 +1,52 @@ +$$ This is a pump file for generating file templates. Pump is a python +$$ script that is part of the Google Test suite of utilities. Description +$$ can be found here: +$$ +$$ http://code.google.com/p/googletest/wiki/PumpManual +$$ + +$var MAX_ARITY = 6 + +// 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. + +// Specializations of FunctionTraits<> for Windows specific calling +// conventions. Please see base/bind_internal.h for more info. + +#ifndef BASE_BIND_INTERNAL_WIN_H_ +#define BASE_BIND_INTERNAL_WIN_H_ +#pragma once + +namespace base { +namespace internal { + +template <typename Sig> +struct FunctionTraits; + +$range ARITY 0..MAX_ARITY +$for ARITY [[ +$range ARG 1..ARITY + +// __stdcall Function: Arity $(ARITY). +template <typename R[[]] +$if ARITY > 0[[, ]] $for ARG , [[typename X$(ARG)]]> +struct FunctionTraits<R(__stdcall *)($for ARG , [[X$(ARG)]])> { + typedef R (*NormalizedSig)($for ARG , [[X$(ARG)]]); + typedef false_type IsMethod; +}; + +// __fastcall Function: Arity $(ARITY). +template <typename R[[]] +$if ARITY > 0[[, ]] $for ARG , [[typename X$(ARG)]]> +struct FunctionTraits<R(__fastcall *)($for ARG , [[X$(ARG)]])> { + typedef R (*NormalizedSig)($for ARG , [[X$(ARG)]]); + typedef false_type IsMethod; +}; + +]] $$for ARITY + +} // namespace internal +} // namespace base + +#endif // BASE_BIND_INTERNAL_WIN_H_ diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc index 47d971a..061808b 100644 --- a/base/bind_unittest.cc +++ b/base/bind_unittest.cc @@ -212,18 +212,6 @@ class BindTest : public ::testing::Test { StrictMock<NoRef>* BindTest::static_func_mock_ptr; -// Ensure we can create unbound callbacks. We need this to be able to store -// them in class members that can be initialized later. -TEST_F(BindTest, DefaultConstruction) { - Callback<void(void)> c0; - Callback<void(int)> c1; - Callback<void(int,int)> c2; - Callback<void(int,int,int)> c3; - Callback<void(int,int,int,int)> c4; - Callback<void(int,int,int,int,int)> c5; - Callback<void(int,int,int,int,int,int)> c6; -} - // Sanity check that we can instantiate a callback for each arity. TEST_F(BindTest, ArityTest) { Callback<int(void)> c0 = Bind(&Sum, 32, 16, 8, 4, 2, 1); @@ -593,5 +581,26 @@ TEST_F(BindTest, NoCompile) { } +#if defined(OS_WIN) +int __fastcall FastCallFunc(int n) { + return n; +} + +int __stdcall StdCallFunc(int n) { + return n; +} + +// Windows specific calling convention support. +// - Can bind a __fastcall function. +// - Can bind a __stdcall function. +TEST_F(BindTest, WindowsCallingConventions) { + Callback<int(void)> fastcall_cb = Bind(&FastCallFunc, 1); + EXPECT_EQ(1, fastcall_cb.Run()); + + Callback<int(void)> stdcall_cb = Bind(&StdCallFunc, 2); + EXPECT_EQ(2, stdcall_cb.Run()); +} +#endif + } // namespace } // namespace base diff --git a/base/callback.h b/base/callback.h index 05a7182..bcc3dfd 100644 --- a/base/callback.h +++ b/base/callback.h @@ -11,7 +11,7 @@ #define BASE_CALLBACK_H_ #pragma once -#include "base/callback_helpers.h" +#include "base/callback_internal.h" #include "base/callback_old.h" // New, super-duper, unified Callback system. This will eventually replace @@ -223,11 +223,11 @@ template <typename Sig> class Callback; template <typename R> -class Callback<R(void)> { +class Callback<R(void)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -238,25 +238,25 @@ class Callback<R(void)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::Invoker::DoInvoke), + &invoker_holder.invoker_storage_) { } - R Run(void) const { - return polymorphic_invoke_(invoker_storage_.get()); - } + R Run() const { + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get()); + } }; template <typename R, typename A1> -class Callback<R(A1)> { +class Callback<R(A1)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -267,26 +267,26 @@ class Callback<R(A1)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::Invoker::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1) const { - return polymorphic_invoke_(invoker_storage_.get(), a1); - } + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get(), a1); + } }; template <typename R, typename A1, typename A2> -class Callback<R(A1, A2)> { +class Callback<R(A1, A2)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -297,29 +297,29 @@ class Callback<R(A1, A2)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::Invoker::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, const A2& a2) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2); - } + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get(), a1, + a2); + } }; template <typename R, typename A1, typename A2, typename A3> -class Callback<R(A1, A2, A3)> { +class Callback<R(A1, A2, A3)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&, const A3&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -330,32 +330,32 @@ class Callback<R(A1, A2, A3)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::Invoker::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, const A2& a2, const A3& a3) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2, - a3); - } + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get(), a1, + a2, + a3); + } }; template <typename R, typename A1, typename A2, typename A3, typename A4> -class Callback<R(A1, A2, A3, A4)> { +class Callback<R(A1, A2, A3, A4)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&, const A3&, const A4&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -366,28 +366,28 @@ class Callback<R(A1, A2, A3, A4)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::Invoker::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, const A2& a2, const A3& a3, const A4& a4) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2, - a3, - a4); - } + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; + return f(invoker_storage_.get(), a1, + a2, + a3, + a4); + } }; template <typename R, typename A1, typename A2, typename A3, typename A4, typename A5> -class Callback<R(A1, A2, A3, A4, A5)> { +class Callback<R(A1, A2, A3, A4, A5)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&, @@ -395,7 +395,7 @@ class Callback<R(A1, A2, A3, A4, A5)> { const A4&, const A5&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -406,8 +406,9 @@ class Callback<R(A1, A2, A3, A4, A5)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::Invoker::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, @@ -415,21 +416,20 @@ class Callback<R(A1, A2, A3, A4, A5)> { const A3& a3, const A4& a4, const A5& a5) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2, - a3, - a4, - a5); + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); + + return f(invoker_storage_.get(), a1, + a2, + a3, + a4, + a5); } - - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; }; template <typename R, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> -class Callback<R(A1, A2, A3, A4, A5, A6)> { +class Callback<R(A1, A2, A3, A4, A5, A6)> : public internal::CallbackBase { public: typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, const A2&, @@ -438,7 +438,7 @@ class Callback<R(A1, A2, A3, A4, A5, A6)> { const A5&, const A6&); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -449,8 +449,9 @@ class Callback<R(A1, A2, A3, A4, A5, A6)> { // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::Invoker::DoInvoke), + &invoker_holder.invoker_storage_) { } R Run(const A1& a1, @@ -459,17 +460,16 @@ class Callback<R(A1, A2, A3, A4, A5, A6)> { const A4& a4, const A5& a5, const A6& a6) const { - return polymorphic_invoke_(invoker_storage_.get(), a1, - a2, - a3, - a4, - a5, - a6); + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); + + return f(invoker_storage_.get(), a1, + a2, + a3, + a4, + a5, + a6); } - - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; }; diff --git a/base/callback.h.pump b/base/callback.h.pump index 9fc4b0b..34b0eb0 100644 --- a/base/callback.h.pump +++ b/base/callback.h.pump @@ -15,7 +15,7 @@ $var MAX_ARITY = 6 #define BASE_CALLBACK_H_ #pragma once -#include "base/callback_helpers.h" +#include "base/callback_internal.h" #include "base/callback_old.h" // New, super-duper, unified Callback system. This will eventually replace @@ -233,10 +233,10 @@ $range ARG 1..ARITY $if ARITY == 0 [[ template <typename R> -class Callback<R(void)> { +class Callback<R(void)> : public internal::CallbackBase { ]] $else [[ template <typename R, $for ARG , [[typename A$(ARG)]]> -class Callback<R($for ARG , [[A$(ARG)]])> { +class Callback<R($for ARG , [[A$(ARG)]])> : public internal::CallbackBase { ]] public: @@ -245,7 +245,7 @@ $if ARITY != 0 [[, ]] $for ARG , [[const A$(ARG)&]]); - Callback() : polymorphic_invoke_(NULL) { } + Callback() : CallbackBase(NULL, NULL) { } // We pass InvokerStorageHolder by const ref to avoid incurring an // unnecessary AddRef/Unref pair even though we will modify the object. @@ -256,27 +256,21 @@ $for ARG , // return the exact Callback<> type. See base/bind.h for details. template <typename T> Callback(const internal::InvokerStorageHolder<T>& invoker_holder) - : polymorphic_invoke_(&T::FunctionTraits::DoInvoke) { - invoker_storage_.swap(invoker_holder.invoker_storage_); + : CallbackBase( + reinterpret_cast<InvokeFuncStorage>(&T::Invoker::DoInvoke), + &invoker_holder.invoker_storage_) { } - -$if ARITY == 0 [[ - R Run(void) const { -]] $else [[ R Run($for ARG , [[const A$(ARG)& a$(ARG)]]) const { -]] + PolymorphicInvoke f = + reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_); - return polymorphic_invoke_(invoker_storage_.get()[[]] + return f(invoker_storage_.get()[[]] $if ARITY != 0 [[, ]] $for ARG , - [[a$(ARG)]]); + [[a$(ARG)]]); } - - private: - scoped_refptr<internal::InvokerStorageBase> invoker_storage_; - PolymorphicInvoke polymorphic_invoke_; }; diff --git a/base/callback_internal.cc b/base/callback_internal.cc new file mode 100644 index 0000000..d9d1e6f --- /dev/null +++ b/base/callback_internal.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/callback_internal.h" + +namespace base { +namespace internal { + +bool CallbackBase::is_null() const { + return invoker_storage_.get() == NULL; +} + +void CallbackBase::Reset() { + invoker_storage_ = NULL; + polymorphic_invoke_ = NULL; +} + +bool CallbackBase::Equals(const CallbackBase& other) const { + return invoker_storage_.get() == other.invoker_storage_.get() && + polymorphic_invoke_ == other.polymorphic_invoke_; +} + +CallbackBase::CallbackBase(InvokeFuncStorage polymorphic_invoke, + scoped_refptr<InvokerStorageBase>* invoker_storage) + : polymorphic_invoke_(polymorphic_invoke) { + if (invoker_storage) { + invoker_storage_.swap(*invoker_storage); + } +} + +CallbackBase::~CallbackBase() { +} + +} // namespace base +} // namespace internal diff --git a/base/callback_helpers.h b/base/callback_internal.h index 86b0df1..4f1d3c3 100644 --- a/base/callback_helpers.h +++ b/base/callback_internal.h @@ -5,8 +5,8 @@ // This file contains utility functions and classes that help the // implementation, and management of the Callback objects. -#ifndef BASE_CALLBACK_HELPERS_H_ -#define BASE_CALLBACK_HELPERS_H_ +#ifndef BASE_CALLBACK_INTERNAL_H_ +#define BASE_CALLBACK_INTERNAL_H_ #pragma once #include "base/ref_counted.h" @@ -49,7 +49,38 @@ InvokerStorageHolder<T> MakeInvokerStorageHolder(T* o) { return InvokerStorageHolder<T>(o); } +// Holds the Callback methods that don't require specialization to reduce +// template bloat. +class CallbackBase { + public: + // Returns true if Callback is null (doesn't refer to anything). + bool is_null() const; + + // Returns the Callback into an uninitalized state. + void Reset(); + + bool Equals(const CallbackBase& other) const; + + protected: + // In C++, it is safe to cast function pointers to function pointers of + // another type. It is not okay to use void*. We create a InvokeFuncStorage + // that that can store our function pointer, and then cast it back to + // the original type on usage. + typedef void(*InvokeFuncStorage)(void); + + CallbackBase(InvokeFuncStorage polymorphic_invoke, + scoped_refptr<InvokerStorageBase>* invoker_storage); + + // Force the destructor to be instaniated inside this translation unit so + // that our subclasses will not get inlined versions. Avoids more template + // bloat. + ~CallbackBase(); + + scoped_refptr<InvokerStorageBase> invoker_storage_; + InvokeFuncStorage polymorphic_invoke_; +}; + } // namespace internal } // namespace base -#endif // BASE_CALLBACK_HELPERS_H_ +#endif // BASE_CALLBACK_INTERNAL_H_ diff --git a/base/callback_unittest.cc b/base/callback_unittest.cc index bc15927..f327412 100644 --- a/base/callback_unittest.cc +++ b/base/callback_unittest.cc @@ -3,10 +3,12 @@ // found in the LICENSE file. #include "base/callback.h" +#include "base/callback_internal.h" #include "base/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" +namespace base { namespace { class HelperObject { @@ -19,9 +21,24 @@ class HelperObject { int next_number_; }; -} // namespace +struct FakeInvoker { + static void DoInvoke(internal::InvokerStorageBase*) { + } +}; + +// White-box testpoints to inject into a Callback<> object for checking +// comparators and emptiness APIs. +class FakeInvokerStorage1 : public internal::InvokerStorageBase { + public: + typedef FakeInvoker Invoker; +}; + +class FakeInvokerStorage2 : public internal::InvokerStorageBase { + public: + typedef FakeInvoker Invoker; +}; -TEST(Callback, OneArg) { +TEST(CallbackOld, OneArg) { HelperObject obj; scoped_ptr<Callback1<int*>::Type> callback( NewCallback(&obj, &HelperObject::GetNextNumberArg)); @@ -31,10 +48,83 @@ TEST(Callback, OneArg) { EXPECT_EQ(number, 1); } -TEST(Callback, ReturnValue) { +TEST(CallbackOld, ReturnValue) { HelperObject obj; scoped_ptr<CallbackWithReturnValue<int>::Type> callback( NewCallbackWithReturnValue(&obj, &HelperObject::GetNextNumber)); EXPECT_EQ(callback->Run(), 1); } + +class CallbackTest : public ::testing::Test { + public: + CallbackTest() + : callback_a_(MakeInvokerStorageHolder(new FakeInvokerStorage1())), + callback_b_(MakeInvokerStorageHolder(new FakeInvokerStorage2())) { + } + + virtual ~CallbackTest() { + } + + protected: + Callback<void(void)> callback_a_; + const Callback<void(void)> callback_b_; // Ensure APIs work with const. + Callback<void(void)> null_callback_; +}; + +// Ensure we can create unbound callbacks. We need this to be able to store +// them in class members that can be initialized later. +TEST_F(CallbackTest, DefaultConstruction) { + Callback<void(void)> c0; + Callback<void(int)> c1; + Callback<void(int,int)> c2; + Callback<void(int,int,int)> c3; + Callback<void(int,int,int,int)> c4; + Callback<void(int,int,int,int,int)> c5; + Callback<void(int,int,int,int,int,int)> c6; + + EXPECT_TRUE(c0.is_null()); + EXPECT_TRUE(c1.is_null()); + EXPECT_TRUE(c2.is_null()); + EXPECT_TRUE(c3.is_null()); + EXPECT_TRUE(c4.is_null()); + EXPECT_TRUE(c5.is_null()); + EXPECT_TRUE(c6.is_null()); +} + +TEST_F(CallbackTest, IsNull) { + EXPECT_TRUE(null_callback_.is_null()); + EXPECT_FALSE(callback_a_.is_null()); + EXPECT_FALSE(callback_b_.is_null()); +} + +TEST_F(CallbackTest, Equals) { + EXPECT_TRUE(callback_a_.Equals(callback_a_)); + EXPECT_FALSE(callback_a_.Equals(callback_b_)); + EXPECT_FALSE(callback_b_.Equals(callback_a_)); + + // We should compare based on instance, not type. + Callback<void(void)> callback_c( + MakeInvokerStorageHolder(new FakeInvokerStorage1())); + Callback<void(void)> callback_a2 = callback_a_; + EXPECT_TRUE(callback_a_.Equals(callback_a2)); + EXPECT_FALSE(callback_a_.Equals(callback_c)); + + // Empty, however, is always equal to empty. + Callback<void(void)> empty2; + EXPECT_TRUE(null_callback_.Equals(empty2)); +} + +TEST_F(CallbackTest, Reset) { + // Resetting should bring us back to empty. + ASSERT_FALSE(callback_a_.is_null()); + ASSERT_FALSE(callback_a_.Equals(null_callback_)); + + callback_a_.Reset(); + + EXPECT_TRUE(callback_a_.is_null()); + EXPECT_TRUE(callback_a_.Equals(null_callback_)); +} + +} // namespace +} // namespace base diff --git a/base/command_line.cc b/base/command_line.cc index 66b4437..fcb2294 100644 --- a/base/command_line.cc +++ b/base/command_line.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -27,114 +27,184 @@ CommandLine* CommandLine::current_process_commandline_ = NULL; -// Since we use a lazy match, make sure that longer versions (like L"--") -// are listed before shorter versions (like L"-") of similar prefixes. +namespace { +typedef CommandLine::StringType::value_type CharType; + +const CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); +const CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); +// Since we use a lazy match, make sure that longer versions (like "--") are +// listed before shorter versions (like "-") of similar prefixes. #if defined(OS_WIN) -const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; -const wchar_t kSwitchTerminator[] = L"--"; -const wchar_t kSwitchValueSeparator[] = L"="; +const CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; #elif defined(OS_POSIX) // Unixes don't use slash as a switch. -const char* const kSwitchPrefixes[] = {"--", "-"}; -const char kSwitchTerminator[] = "--"; -const char kSwitchValueSeparator[] = "="; +const CharType* const kSwitchPrefixes[] = {"--", "-"}; #endif #if defined(OS_WIN) -// Lowercase a string. This is used to lowercase switch names. -// Is this what we really want? It seems crazy to me. I've left it in -// for backwards compatibility on Windows. -static void Lowercase(std::string* parameter) { - transform(parameter->begin(), parameter->end(), parameter->begin(), - tolower); -} -#endif - -CommandLine::~CommandLine() { -} - -#if defined(OS_WIN) -CommandLine::CommandLine(NoProgram no_program) { +// Lowercase a string for case-insensitivity of switches. +// Is this desirable? It exists for backwards compatibility on Windows. +void Lowercase(std::string* arg) { + transform(arg->begin(), arg->end(), arg->begin(), tolower); } -void CommandLine::ParseFromString(const std::wstring& command_line) { - TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); - - if (command_line_string_.empty()) - return; +// Quote a string if necessary, such that CommandLineToArgvW() will always +// process it as a single argument. +std::wstring WindowsStyleQuote(const std::wstring& arg) { + // We follow the quoting rules of CommandLineToArgvW. + // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + if (arg.find_first_of(L" \\\"") == std::wstring::npos) { + // No quoting necessary. + return arg; + } - int num_args = 0; - wchar_t** args = NULL; + std::wstring out; + out.push_back(L'"'); + for (size_t i = 0; i < arg.size(); ++i) { + if (arg[i] == '\\') { + // Find the extent of this run of backslashes. + size_t start = i, end = start + 1; + for (; end < arg.size() && arg[end] == '\\'; ++end) + /* empty */; + size_t backslash_count = end - start; - args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); + // Backslashes are escapes only if the run is followed by a double quote. + // Since we also will end the string with a double quote, we escape for + // either a double quote or the end of the string. + if (end == arg.size() || arg[end] == '"') { + // To quote, we need to output 2x as many backslashes. + backslash_count *= 2; + } + for (size_t j = 0; j < backslash_count; ++j) + out.push_back('\\'); - // Populate program_ with the trimmed version of the first arg. - TrimWhitespace(args[0], TRIM_ALL, &program_); + // Advance i to one before the end to balance i++ in loop. + i = end - 1; + } else if (arg[i] == '"') { + out.push_back('\\'); + out.push_back('"'); + } else { + out.push_back(arg[i]); + } + } + out.push_back('"'); - bool parse_switches = true; - for (int i = 1; i < num_args; ++i) { - std::wstring arg; - TrimWhitespace(args[i], TRIM_ALL, &arg); + return out; +} +#endif - if (!parse_switches) { - args_.push_back(arg); - continue; - } +// Returns true and fills in |switch_string| and |switch_value| if +// |parameter_string| represents a switch. +bool IsSwitch(const CommandLine::StringType& parameter_string, + std::string* switch_string, + CommandLine::StringType* switch_value) { + switch_string->clear(); + switch_value->clear(); - if (arg == kSwitchTerminator) { - parse_switches = false; + for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { + CommandLine::StringType prefix(kSwitchPrefixes[i]); + if (parameter_string.find(prefix) != 0) continue; - } - std::string switch_string; - std::wstring switch_value; - if (IsSwitch(arg, &switch_string, &switch_value)) { - switches_[switch_string] = switch_value; + const size_t switch_start = prefix.length(); + const size_t equals_position = parameter_string.find( + kSwitchValueSeparator, switch_start); + CommandLine::StringType switch_native; + if (equals_position == CommandLine::StringType::npos) { + switch_native = parameter_string.substr(switch_start); } else { - args_.push_back(arg); + switch_native = parameter_string.substr( + switch_start, equals_position - switch_start); + *switch_value = parameter_string.substr(equals_position + 1); } +#if defined(OS_WIN) + *switch_string = WideToASCII(switch_native); + Lowercase(switch_string); +#else + *switch_string = switch_native; +#endif + + return true; } - if (args) - LocalFree(args); + return false; } -// static -CommandLine CommandLine::FromString(const std::wstring& command_line) { - CommandLine cmd; - cmd.ParseFromString(command_line); - return cmd; +} // namespace + +CommandLine::CommandLine(NoProgram no_program) { +#if defined(OS_POSIX) + // Push an empty argument, because we always assume argv_[0] is a program. + argv_.push_back(""); +#endif } CommandLine::CommandLine(const FilePath& program) { +#if defined(OS_WIN) if (!program.empty()) { program_ = program.value(); // TODO(evanm): proper quoting here. command_line_string_ = L'"' + program.value() + L'"'; } -} - #elif defined(OS_POSIX) -CommandLine::CommandLine(NoProgram no_program) { - // Push an empty argument, because we always assume argv_[0] is a program. - argv_.push_back(""); + argv_.push_back(program.value()); +#endif } +#if defined(OS_POSIX) CommandLine::CommandLine(int argc, const char* const* argv) { InitFromArgv(argc, argv); } -CommandLine::CommandLine(const std::vector<std::string>& argv) { +CommandLine::CommandLine(const StringVector& argv) { InitFromArgv(argv); } +#endif // OS_POSIX + +CommandLine::~CommandLine() { +} + +// static +void CommandLine::Init(int argc, const char* const* argv) { + delete current_process_commandline_; + current_process_commandline_ = new CommandLine; +#if defined(OS_WIN) + current_process_commandline_->ParseFromString(::GetCommandLineW()); +#elif defined(OS_POSIX) + current_process_commandline_->InitFromArgv(argc, argv); +#endif +} + +// static +void CommandLine::Reset() { + DCHECK(current_process_commandline_); + delete current_process_commandline_; + current_process_commandline_ = NULL; +} +// static +CommandLine* CommandLine::ForCurrentProcess() { + DCHECK(current_process_commandline_); + return current_process_commandline_; +} + +#if defined(OS_WIN) +// static +CommandLine CommandLine::FromString(const std::wstring& command_line) { + CommandLine cmd; + cmd.ParseFromString(command_line); + return cmd; +} +#endif // OS_WIN + +#if defined(OS_POSIX) void CommandLine::InitFromArgv(int argc, const char* const* argv) { for (int i = 0; i < argc; ++i) argv_.push_back(argv[i]); InitFromArgv(argv_); } -void CommandLine::InitFromArgv(const std::vector<std::string>& argv) { +void CommandLine::InitFromArgv(const StringVector& argv) { argv_ = argv; bool parse_switches = true; for (size_t i = 1; i < argv_.size(); ++i) { @@ -151,7 +221,7 @@ void CommandLine::InitFromArgv(const std::vector<std::string>& argv) { } std::string switch_string; - std::string switch_value; + StringType switch_value; if (IsSwitch(arg, &switch_string, &switch_value)) { switches_[switch_string] = switch_value; } else { @@ -159,72 +229,25 @@ void CommandLine::InitFromArgv(const std::vector<std::string>& argv) { } } } +#endif // OS_POSIX -CommandLine::CommandLine(const FilePath& program) { - argv_.push_back(program.value()); -} - -#endif - -// static -bool CommandLine::IsSwitch(const StringType& parameter_string, - std::string* switch_string, - StringType* switch_value) { - switch_string->clear(); - switch_value->clear(); - - for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { - StringType prefix(kSwitchPrefixes[i]); - if (parameter_string.find(prefix) != 0) - continue; - - const size_t switch_start = prefix.length(); - const size_t equals_position = parameter_string.find( - kSwitchValueSeparator, switch_start); - StringType switch_native; - if (equals_position == StringType::npos) { - switch_native = parameter_string.substr(switch_start); - } else { - switch_native = parameter_string.substr( - switch_start, equals_position - switch_start); - *switch_value = parameter_string.substr(equals_position + 1); - } +CommandLine::StringType CommandLine::command_line_string() const { #if defined(OS_WIN) - *switch_string = WideToASCII(switch_native); - Lowercase(switch_string); -#else - *switch_string = switch_native; + return command_line_string_; +#elif defined(OS_POSIX) + return JoinString(argv_, ' '); #endif - - return true; - } - - return false; } -// static -void CommandLine::Init(int argc, const char* const* argv) { - delete current_process_commandline_; - current_process_commandline_ = new CommandLine; +FilePath CommandLine::GetProgram() const { #if defined(OS_WIN) - current_process_commandline_->ParseFromString(::GetCommandLineW()); -#elif defined(OS_POSIX) - current_process_commandline_->InitFromArgv(argc, argv); + return FilePath(program_); +#else + DCHECK_GT(argv_.size(), 0U); + return FilePath(argv_[0]); #endif } -void CommandLine::Reset() { - DCHECK(current_process_commandline_ != NULL); - delete current_process_commandline_; - current_process_commandline_ = NULL; -} - -// static -CommandLine* CommandLine::ForCurrentProcess() { - DCHECK(current_process_commandline_); - return current_process_commandline_; -} - bool CommandLine::HasSwitch(const std::string& switch_string) const { std::string lowercased_switch(switch_string); #if defined(OS_WIN) @@ -259,8 +282,7 @@ CommandLine::StringType CommandLine::GetSwitchValueNative( Lowercase(&lowercased_switch); #endif - std::map<std::string, StringType>::const_iterator result = - switches_.find(lowercased_switch); + SwitchMap::const_iterator result = switches_.find(lowercased_switch); if (result == switches_.end()) { return CommandLine::StringType(); @@ -269,80 +291,30 @@ CommandLine::StringType CommandLine::GetSwitchValueNative( } } -FilePath CommandLine::GetProgram() const { -#if defined(OS_WIN) - return FilePath(program_); -#else - DCHECK_GT(argv_.size(), 0U); - return FilePath(argv_[0]); -#endif +size_t CommandLine::GetSwitchCount() const { + return switches_.size(); } -#if defined(OS_POSIX) -std::string CommandLine::command_line_string() const { - return JoinString(argv_, ' '); -} -#endif - -#if defined(OS_WIN) void CommandLine::AppendSwitch(const std::string& switch_string) { +#if defined(OS_WIN) command_line_string_.append(L" "); command_line_string_.append(kSwitchPrefixes[0] + ASCIIToWide(switch_string)); switches_[switch_string] = L""; +#elif defined(OS_POSIX) + argv_.push_back(kSwitchPrefixes[0] + switch_string); + switches_[switch_string] = ""; +#endif } -void CommandLine::AppendSwitchASCII(const std::string& switch_string, - const std::string& value_string) { - AppendSwitchNative(switch_string, ASCIIToWide(value_string)); -} - -// Quote a string if necessary, such that CommandLineToArgvW() will -// always process it as a single argument. -static std::wstring WindowsStyleQuote(const std::wstring& arg) { - // We follow the quoting rules of CommandLineToArgvW. - // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx - if (arg.find_first_of(L" \\\"") == std::wstring::npos) { - // No quoting necessary. - return arg; - } - - std::wstring out; - out.push_back(L'"'); - for (size_t i = 0; i < arg.size(); ++i) { - if (arg[i] == '\\') { - // Find the extent of this run of backslashes. - size_t start = i, end = start + 1; - for (; end < arg.size() && arg[end] == '\\'; ++end) - /* empty */; - size_t backslash_count = end - start; - - // Backslashes are escapes only if the run is followed by a double quote. - // Since we also will end the string with a double quote, we escape for - // either a double quote or the end of the string. - if (end == arg.size() || arg[end] == '"') { - // To quote, we need to output 2x as many backslashes. - backslash_count *= 2; - } - for (size_t j = 0; j < backslash_count; ++j) - out.push_back('\\'); - - // Advance i to one before the end to balance i++ in loop. - i = end - 1; - } else if (arg[i] == '"') { - out.push_back('\\'); - out.push_back('"'); - } else { - out.push_back(arg[i]); - } - } - out.push_back('"'); - - return out; +void CommandLine::AppendSwitchPath(const std::string& switch_string, + const FilePath& path) { + AppendSwitchNative(switch_string, path.value()); } void CommandLine::AppendSwitchNative(const std::string& switch_string, - const std::wstring& value) { - std::wstring combined_switch_string = + const CommandLine::StringType& value) { +#if defined(OS_WIN) + StringType combined_switch_string = kSwitchPrefixes[0] + ASCIIToWide(switch_string); if (!value.empty()) combined_switch_string += kSwitchValueSeparator + WindowsStyleQuote(value); @@ -351,24 +323,81 @@ void CommandLine::AppendSwitchNative(const std::string& switch_string, command_line_string_.append(combined_switch_string); switches_[switch_string] = value; +#elif defined(OS_POSIX) + StringType combined_switch_string = kSwitchPrefixes[0] + switch_string; + if (!value.empty()) + combined_switch_string += kSwitchValueSeparator + value; + argv_.push_back(combined_switch_string); + switches_[switch_string] = value; +#endif +} + +void CommandLine::AppendSwitchASCII(const std::string& switch_string, + const std::string& value_string) { +#if defined(OS_WIN) + AppendSwitchNative(switch_string, ASCIIToWide(value_string)); +#elif defined(OS_POSIX) + AppendSwitchNative(switch_string, value_string); +#endif +} + +void CommandLine::AppendSwitches(const CommandLine& other) { + SwitchMap::const_iterator i; + for (i = other.switches_.begin(); i != other.switches_.end(); ++i) + AppendSwitchNative(i->first, i->second); +} + +void CommandLine::CopySwitchesFrom(const CommandLine& source, + const char* const switches[], + size_t count) { + for (size_t i = 0; i < count; ++i) { + if (source.HasSwitch(switches[i])) { + StringType value = source.GetSwitchValueNative(switches[i]); + AppendSwitchNative(switches[i], value); + } + } } void CommandLine::AppendArg(const std::string& value) { +#if defined(OS_WIN) DCHECK(IsStringUTF8(value)); AppendArgNative(UTF8ToWide(value)); +#elif defined(OS_POSIX) + AppendArgNative(value); +#endif +} + +void CommandLine::AppendArgPath(const FilePath& path) { + AppendArgNative(path.value()); } -void CommandLine::AppendArgNative(const std::wstring& value) { +void CommandLine::AppendArgNative(const CommandLine::StringType& value) { +#if defined(OS_WIN) command_line_string_.append(L" "); command_line_string_.append(WindowsStyleQuote(value)); args_.push_back(value); +#elif defined(OS_POSIX) + DCHECK(IsStringUTF8(value)); + argv_.push_back(value); +#endif +} + +void CommandLine::AppendArgs(const CommandLine& other) { + if(other.args_.size() <= 0) + return; +#if defined(OS_WIN) + command_line_string_.append(L" --"); +#endif // OS_WIN + StringVector::const_iterator i; + for (i = other.args_.begin(); i != other.args_.end(); ++i) + AppendArgNative(*i); } void CommandLine::AppendArguments(const CommandLine& other, bool include_program) { +#if defined(OS_WIN) // Verify include_program is used correctly. - // Logic could be shorter but this is clearer. - DCHECK_EQ(include_program, !other.GetProgram().empty()); + DCHECK(!include_program || !other.GetProgram().empty()); if (include_program) program_ = other.program_; @@ -376,54 +405,7 @@ void CommandLine::AppendArguments(const CommandLine& other, command_line_string_ += L' '; command_line_string_ += other.command_line_string_; - - std::map<std::string, StringType>::const_iterator i; - for (i = other.switches_.begin(); i != other.switches_.end(); ++i) - switches_[i->first] = i->second; -} - -void CommandLine::PrependWrapper(const std::wstring& wrapper) { - if (wrapper.empty()) - return; - // The wrapper may have embedded arguments (like "gdb --args"). In this case, - // we don't pretend to do anything fancy, we just split on spaces. - std::vector<std::wstring> wrapper_and_args; - base::SplitString(wrapper, ' ', &wrapper_and_args); - program_ = wrapper_and_args[0]; - command_line_string_ = wrapper + L" " + command_line_string_; -} - #elif defined(OS_POSIX) -void CommandLine::AppendSwitch(const std::string& switch_string) { - argv_.push_back(kSwitchPrefixes[0] + switch_string); - switches_[switch_string] = ""; -} - -void CommandLine::AppendSwitchNative(const std::string& switch_string, - const std::string& value) { - std::string combined_switch_string = kSwitchPrefixes[0] + switch_string; - if (!value.empty()) - combined_switch_string += kSwitchValueSeparator + value; - argv_.push_back(combined_switch_string); - switches_[switch_string] = value; -} - -void CommandLine::AppendSwitchASCII(const std::string& switch_string, - const std::string& value_string) { - AppendSwitchNative(switch_string, value_string); -} - -void CommandLine::AppendArg(const std::string& value) { - AppendArgNative(value); -} - -void CommandLine::AppendArgNative(const std::string& value) { - DCHECK(IsStringUTF8(value)); - argv_.push_back(value); -} - -void CommandLine::AppendArguments(const CommandLine& other, - bool include_program) { // Verify include_program is used correctly. // Logic could be shorter but this is clearer. DCHECK_EQ(include_program, !other.GetProgram().empty()); @@ -435,48 +417,72 @@ void CommandLine::AppendArguments(const CommandLine& other, // arguments to our arg vector. for (size_t i = 1; i < other.argv_.size(); ++i) argv_.push_back(other.argv_[i]); +#endif - std::map<std::string, StringType>::const_iterator i; + SwitchMap::const_iterator i; for (i = other.switches_.begin(); i != other.switches_.end(); ++i) switches_[i->first] = i->second; } -void CommandLine::PrependWrapper(const std::string& wrapper) { +void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { // The wrapper may have embedded arguments (like "gdb --args"). In this case, // we don't pretend to do anything fancy, we just split on spaces. - std::vector<std::string> wrapper_and_args; + if (wrapper.empty()) + return; + StringVector wrapper_and_args; +#if defined(OS_WIN) + base::SplitString(wrapper, ' ', &wrapper_and_args); + program_ = wrapper_and_args[0]; + command_line_string_ = wrapper + L" " + command_line_string_; +#elif defined(OS_POSIX) base::SplitString(wrapper, ' ', &wrapper_and_args); argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); +#endif } -#endif +#if defined(OS_WIN) +void CommandLine::ParseFromString(const std::wstring& command_line) { + TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); -void CommandLine::AppendArgPath(const FilePath& path) { - AppendArgNative(path.value()); -} + if (command_line_string_.empty()) + return; -void CommandLine::AppendSwitchPath(const std::string& switch_string, - const FilePath& path) { - AppendSwitchNative(switch_string, path.value()); -} + int num_args = 0; + wchar_t** args = NULL; -void CommandLine::CopySwitchesFrom(const CommandLine& source, - const char* const switches[], - size_t count) { - for (size_t i = 0; i < count; ++i) { - if (source.HasSwitch(switches[i])) { - StringType value = source.GetSwitchValueNative(switches[i]); - AppendSwitchNative(switches[i], value); + args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); + + // Populate program_ with the trimmed version of the first arg. + TrimWhitespace(args[0], TRIM_ALL, &program_); + + bool parse_switches = true; + for (int i = 1; i < num_args; ++i) { + std::wstring arg; + TrimWhitespace(args[i], TRIM_ALL, &arg); + + if (!parse_switches) { + args_.push_back(arg); + continue; + } + + if (arg == kSwitchTerminator) { + parse_switches = false; + continue; + } + + std::string switch_string; + std::wstring switch_value; + if (IsSwitch(arg, &switch_string, &switch_value)) { + switches_[switch_string] = switch_value; + } else { + args_.push_back(arg); } } -} -// private -CommandLine::CommandLine() { + if (args) + LocalFree(args); } +#endif -// static -CommandLine* CommandLine::ForCurrentProcessMutable() { - DCHECK(current_process_commandline_); - return current_process_commandline_; +CommandLine::CommandLine() { } diff --git a/base/command_line.h b/base/command_line.h index b8bbba9..7ff5ab0 100644 --- a/base/command_line.h +++ b/base/command_line.h @@ -3,15 +3,13 @@ // found in the LICENSE file. // This class works with command lines: building and parsing. -// Switches can optionally have a value attached using an equals sign, -// as in "-switch=value". Arguments that aren't prefixed with a -// switch prefix are saved as extra arguments. An argument of "--" -// will terminate switch parsing, causing everything after to be -// considered as extra arguments. +// Switches can optionally have a value attached using an equals sign, as in +// "-switch=value". Arguments that aren't prefixed with a switch prefix are +// saved as extra arguments. An argument of "--" will terminate switch parsing, +// causing everything after to be considered as extra arguments. -// There is a singleton read-only CommandLine that represents the command -// line that the current process was started with. It must be initialized -// in main() (or whatever the platform's equivalent function is). +// There is a singleton read-only CommandLine that represents the command line +// that the current process was started with. It must be initialized in main(). #ifndef BASE_COMMAND_LINE_H_ #define BASE_COMMAND_LINE_H_ @@ -25,179 +23,158 @@ #include "build/build_config.h" class FilePath; -class InProcessBrowserTest; class CommandLine { public: #if defined(OS_WIN) - // The type of native command line arguments. + // The native command line string type. typedef std::wstring StringType; #elif defined(OS_POSIX) - // The type of native command line arguments. typedef std::string StringType; #endif + typedef std::vector<StringType> StringVector; // The type of map for parsed-out switch key and values. typedef std::map<std::string, StringType> SwitchMap; - // A constructor for CommandLines that are used only to carry switches and - // arguments. + // A constructor for CommandLines that only carry switches and arguments. enum NoProgram { NO_PROGRAM }; explicit CommandLine(NoProgram no_program); - // Construct a new, empty command line. - // |program| is the name of the program to run (aka argv[0]). + // Construct a new command line with |program| as argv[0]. explicit CommandLine(const FilePath& program); #if defined(OS_POSIX) CommandLine(int argc, const char* const* argv); - explicit CommandLine(const std::vector<std::string>& argv); + explicit CommandLine(const StringVector& argv); #endif ~CommandLine(); -#if defined(OS_WIN) - // Initialize by parsing the given command-line string. - // The program name is assumed to be the first item in the string. - void ParseFromString(const std::wstring& command_line); - static CommandLine FromString(const std::wstring& command_line); -#elif defined(OS_POSIX) - // Initialize from an argv vector. - void InitFromArgv(int argc, const char* const* argv); - void InitFromArgv(const std::vector<std::string>& argv); -#endif - - // Initialize the current process CommandLine singleton. On Windows, - // ignores its arguments (we instead parse GetCommandLineW() - // directly) because we don't trust the CRT's parsing of the command - // line, but it still must be called to set up the command line. + // Initialize the current process CommandLine singleton. On Windows, ignores + // its arguments (we instead parse GetCommandLineW() directly) because we + // don't trust the CRT's parsing of the command line, but it still must be + // called to set up the command line. static void Init(int argc, const char* const* argv); // Destroys the current process CommandLine singleton. This is necessary if - // you want to reset the base library to its initial state (for example in an + // you want to reset the base library to its initial state (for example, in an // outer library that needs to be able to terminate, and be re-initialized). - // If Init is called only once, e.g. in main(), calling Reset() is not - // necessary. + // If Init is called only once, as in main(), Reset() is not necessary. static void Reset(); // Get the singleton CommandLine representing the current process's - // command line. Note: returned value is mutable, but not thread safe; + // command line. Note: returned value is mutable, but not thread safe; // only mutate if you know what you're doing! static CommandLine* ForCurrentProcess(); +#if defined(OS_WIN) + static CommandLine FromString(const std::wstring& command_line); +#endif + +#if defined(OS_POSIX) + // Initialize from an argv vector. + void InitFromArgv(int argc, const char* const* argv); + void InitFromArgv(const StringVector& argv); +#endif + + // Returns the represented command line string. + // CAUTION! This should be avoided because quoting behavior is unclear. + StringType command_line_string() const; + +#if defined(OS_POSIX) + // Returns the original command line string as a vector of strings. + const StringVector& argv() const { return argv_; } +#endif + + // Returns the program part of the command line string (the first item). + FilePath GetProgram() const; + // Returns true if this command line contains the given switch. - // (Switch names are case-insensitive.) + // (Switch names are case-insensitive). bool HasSwitch(const std::string& switch_string) const; - // Returns the value associated with the given switch. If the - // switch has no value or isn't present, this method returns - // the empty string. + // Returns the value associated with the given switch. If the switch has no + // value or isn't present, this method returns the empty string. std::string GetSwitchValueASCII(const std::string& switch_string) const; FilePath GetSwitchValuePath(const std::string& switch_string) const; StringType GetSwitchValueNative(const std::string& switch_string) const; // Get the number of switches in this process. - size_t GetSwitchCount() const { return switches_.size(); } + // TODO(msw): Remove unnecessary API. + size_t GetSwitchCount() const; - // Get a copy of all switches, along with their values - const SwitchMap& GetSwitches() const { - return switches_; - } + // Get a copy of all switches, along with their values. + const SwitchMap& GetSwitches() const { return switches_; } - // Get the remaining arguments to the command. - const std::vector<StringType>& args() const { return args_; } - -#if defined(OS_WIN) - // Returns the original command line string. - const std::wstring& command_line_string() const { - return command_line_string_; - } -#elif defined(OS_POSIX) - // Returns the original command line string as a vector of strings. - const std::vector<std::string>& argv() const { - return argv_; - } - // Try to match the same result as command_line_string() would get you - // on windows. - std::string command_line_string() const; -#endif - - // Returns the program part of the command line string (the first item). - FilePath GetProgram() const; - - // Append a switch to the command line. + // Append a switch [with optional value] to the command line. + // CAUTION! Appending a switch after the "--" switch terminator is futile! void AppendSwitch(const std::string& switch_string); - - // Append a switch and value to the command line. void AppendSwitchPath(const std::string& switch_string, const FilePath& path); void AppendSwitchNative(const std::string& switch_string, const StringType& value); void AppendSwitchASCII(const std::string& switch_string, const std::string& value); + void AppendSwitches(const CommandLine& other); + + // Copy a set of switches (and any values) from another command line. + // Commonly used when launching a subprocess. + void CopySwitchesFrom(const CommandLine& source, const char* const switches[], + size_t count); + + // Get the remaining arguments to the command. + const StringVector& args() const { return args_; } - // Append an argument to the command line. - // Note on quoting: the argument will be quoted properly such that it is - // interpreted as one argument to the target command. - // AppendArg is primarily for ASCII; non-ASCII input will be - // interpreted as UTF-8. + // Append an argument to the command line. Note that the argument is quoted + // properly such that it is interpreted as one argument to the target command. + // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8. void AppendArg(const std::string& value); void AppendArgPath(const FilePath& value); void AppendArgNative(const StringType& value); + void AppendArgs(const CommandLine& other); // Append the arguments from another command line to this one. // If |include_program| is true, include |other|'s program as well. void AppendArguments(const CommandLine& other, bool include_program); - // Insert a command before the current command. Common for debuggers, - // like "valgrind" or "gdb --args". + // Insert a command before the current command. + // Common for debuggers, like "valgrind" or "gdb --args". void PrependWrapper(const StringType& wrapper); - // Copy a set of switches (and their values, if any) from another command - // line. Commonly used when launching a subprocess. - void CopySwitchesFrom(const CommandLine& source, const char* const switches[], - size_t count); +#if defined(OS_WIN) + // Initialize by parsing the given command line string. + // The program name is assumed to be the first item in the string. + void ParseFromString(const std::wstring& command_line); +#endif private: - friend class InProcessBrowserTest; - + // Disallow public default construction; a program name must be specified. CommandLine(); - // Used by InProcessBrowserTest. - static CommandLine* ForCurrentProcessMutable(); - - // Returns true and fills in |switch_string| and |switch_value| - // if |parameter_string| represents a switch. - static bool IsSwitch(const StringType& parameter_string, - std::string* switch_string, - StringType* switch_value); - - // The singleton CommandLine instance representing the current process's - // command line. + // The singleton CommandLine representing the current process's command line. static CommandLine* current_process_commandline_; // We store a platform-native version of the command line, used when building - // up a new command line to be executed. This ifdef delimits that code. - + // up a new command line to be executed. This ifdef delimits that code. #if defined(OS_WIN) - // The quoted, space-separated command-line string. - std::wstring command_line_string_; + // The quoted, space-separated command line string. + StringType command_line_string_; // The name of the program. - std::wstring program_; + StringType program_; #elif defined(OS_POSIX) // The argv array, with the program name in argv_[0]. - std::vector<std::string> argv_; + StringVector argv_; #endif - // Parsed-out values. + // Parsed-out switch keys and values. SwitchMap switches_; - // Non-switch command-line arguments. - std::vector<StringType> args_; + // Non-switch command line arguments. + StringVector args_; - // We allow copy constructors, because a common pattern is to grab a - // copy of the current process's command line and then add some - // flags to it. E.g.: + // Allow the copy constructor. A common pattern is to copy the current + // process's command line and then add some flags to it. For example: // CommandLine cl(*CommandLine::ForCurrentProcess()); // cl.AppendSwitch(...); }; diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc index 6fd6440..5ce6911 100644 --- a/base/command_line_unittest.cc +++ b/base/command_line_unittest.cc @@ -109,9 +109,9 @@ TEST(CommandLineTest, EmptyString) { EXPECT_TRUE(cl.GetProgram().empty()); #elif defined(OS_POSIX) CommandLine cl(0, NULL); - EXPECT_TRUE(cl.argv().size() == 0); + EXPECT_TRUE(cl.argv().empty()); #endif - EXPECT_EQ(0U, cl.args().size()); + EXPECT_TRUE(cl.args().empty()); } // Test methods for appending switches to a command line. diff --git a/base/cpu.cc b/base/cpu.cc index 0fef897..3232f15 100644 --- a/base/cpu.cc +++ b/base/cpu.cc @@ -1,10 +1,16 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-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/cpu.h" + +#if defined(ARCH_CPU_X86_FAMILY) +#if defined(_MSC_VER) #include <intrin.h> -#include <string> +#endif +#endif + +#include <string.h> namespace base { @@ -15,11 +21,66 @@ CPU::CPU() stepping_(0), ext_model_(0), ext_family_(0), + has_mmx_(false), + has_sse_(false), + has_sse2_(false), + has_sse3_(false), + has_ssse3_(false), + has_sse41_(false), + has_sse42_(false), cpu_vendor_("unknown") { Initialize(); } +#if defined(ARCH_CPU_X86_FAMILY) +#ifndef _MSC_VER + +#if defined(__pic__) && defined(__i386__) + +void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile ( + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type) + ); +} + +void __cpuidex(int cpu_info[4], int info_type, int info_index) { + __asm__ volatile ( + "mov %%ebx, %%edi\n" + "cpuid\n" + "xchg %%edi, %%ebx\n" + : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type), "c"(info_index) + ); +} + +#else + +void __cpuid(int cpu_info[4], int info_type) { + __asm__ volatile ( + "cpuid \n\t" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type) + ); +} + +void __cpuidex(int cpu_info[4], int info_type, int info_index) { + __asm__ volatile ( + "cpuid \n\t" + : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) + : "a"(info_type), "c"(info_index) + ); +} + +#endif +#endif // _MSC_VER +#endif // ARCH_CPU_X86_FAMILY + void CPU::Initialize() { +#if defined(ARCH_CPU_X86_FAMILY) int cpu_info[4] = {-1}; char cpu_string[0x20]; @@ -42,13 +103,23 @@ void CPU::Initialize() { if (num_ids > 0) { __cpuid(cpu_info, 1); stepping_ = cpu_info[0] & 0xf; - model_ = (cpu_info[0] >> 4) & 0xf; + model_ = ((cpu_info[0] >> 4) & 0xf) + ((cpu_info[0] >> 12) & 0xf0); family_ = (cpu_info[0] >> 8) & 0xf; type_ = (cpu_info[0] >> 12) & 0x3; ext_model_ = (cpu_info[0] >> 16) & 0xf; ext_family_ = (cpu_info[0] >> 20) & 0xff; cpu_vendor_ = cpu_string; + has_mmx_ = (cpu_info[3] & 0x00800000) != 0; + has_sse_ = (cpu_info[3] & 0x02000000) != 0; + has_sse2_ = (cpu_info[3] & 0x04000000) != 0; + has_sse3_ = (cpu_info[2] & 0x00000001) != 0; + has_ssse3_ = (cpu_info[2] & 0x00000200) != 0; + has_sse41_ = (cpu_info[2] & 0x00080000) != 0; + has_sse42_ = (cpu_info[2] & 0x00100000) != 0; } +#endif } + + } // namespace base @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2006-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. @@ -6,6 +6,8 @@ #define BASE_CPU_H_ #pragma once +#include "build/build_config.h" + #include <string> namespace base { @@ -24,6 +26,13 @@ class CPU { int type() const { return type_; } int extended_model() const { return ext_model_; } int extended_family() const { return ext_family_; } + int has_mmx() const { return has_mmx_; } + int has_sse() const { return has_sse_; } + int has_sse2() const { return has_sse2_; } + int has_sse3() const { return has_sse3_; } + int has_ssse3() const { return has_ssse3_; } + int has_sse41() const { return has_sse41_; } + int has_sse42() const { return has_sse42_; } private: // Query the processor for CPUID information. @@ -35,6 +44,13 @@ class CPU { int stepping_; // processor revision number int ext_model_; int ext_family_; + bool has_mmx_; + bool has_sse_; + bool has_sse2_; + bool has_sse3_; + bool has_ssse3_; + bool has_sse41_; + bool has_sse42_; std::string cpu_vendor_; }; diff --git a/base/cpu_unittest.cc b/base/cpu_unittest.cc new file mode 100644 index 0000000..f9351cc --- /dev/null +++ b/base/cpu_unittest.cc @@ -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. + +#include "base/cpu.h" + +#include "testing/gtest/include/gtest/gtest.h" + +// Tests whether we can run extended instructions represented by the CPU +// information. This test actually executes some extended instructions (such as +// MMX, SSE, etc.) supported by the CPU and sees we can run them without +// "undefined instruction" exceptions. That is, this test succeeds when this +// test finishes without a crash. +TEST(CPU, RunExtendedInstructions) { +#if defined(ARCH_CPU_X86_FAMILY) + // Retrieve the CPU information. + base::CPU cpu; + +#if defined(OS_WIN) + ASSERT_TRUE(cpu.has_mmx()); + + // Execute an MMX instruction. + __asm emms; + + if (cpu.has_sse()) { + // Execute an SSE instruction. + __asm xorps xmm0, xmm0; + } + + if (cpu.has_sse2()) { + // Execute an SSE 2 instruction. + __asm psrldq xmm0, 0; + } + + if (cpu.has_sse3()) { + // Execute an SSE 3 instruction. + __asm addsubpd xmm0, xmm0; + } + + if (cpu.has_ssse3()) { + // Execute a Supplimental SSE 3 instruction. + __asm psignb xmm0, xmm0; + } + + if (cpu.has_sse41()) { + // Execute an SSE 4.1 instruction. + __asm pmuldq xmm0, xmm0; + } + + if (cpu.has_sse42()) { + // Execute an SSE 4.2 instruction. + __asm crc32 eax, eax; + } +#elif defined(OS_POSIX) && defined(__x86_64__) + ASSERT_TRUE(cpu.has_mmx()); + + // Execute an MMX instruction. + __asm__ __volatile__("emms\n" : : : "mm0"); + + if (cpu.has_sse()) { + // Execute an SSE instruction. + __asm__ __volatile__("xorps %%xmm0, %%xmm0\n" : : : "xmm0"); + } + + if (cpu.has_sse2()) { + // Execute an SSE 2 instruction. + __asm__ __volatile__("psrldq $0, %%xmm0\n" : : : "xmm0"); + } + + if (cpu.has_sse3()) { + // Execute an SSE 3 instruction. + __asm__ __volatile__("addsubpd %%xmm0, %%xmm0\n" : : : "xmm0"); + } + + if (cpu.has_ssse3()) { + // Execute a Supplimental SSE 3 instruction. + __asm__ __volatile__("psignb %%xmm0, %%xmm0\n" : : : "xmm0"); + } + + if (cpu.has_sse41()) { + // Execute an SSE 4.1 instruction. + __asm__ __volatile__("pmuldq %%xmm0, %%xmm0\n" : : : "xmm0"); + } + + if (cpu.has_sse42()) { + // Execute an SSE 4.2 instruction. + __asm__ __volatile__("crc32 %%eax, %%eax\n" : : : "eax"); + } +#endif +#endif +} diff --git a/base/crypto/encryptor_mac.cc b/base/crypto/encryptor_mac.cc index d27c62e..e26c6bd 100644 --- a/base/crypto/encryptor_mac.cc +++ b/base/crypto/encryptor_mac.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -12,7 +12,9 @@ namespace base { -Encryptor::Encryptor() { +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC) { } Encryptor::~Encryptor() { diff --git a/base/crypto/encryptor_nss.cc b/base/crypto/encryptor_nss.cc index 737e8f2..3b9f7f3 100644 --- a/base/crypto/encryptor_nss.cc +++ b/base/crypto/encryptor_nss.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -13,7 +13,9 @@ namespace base { -Encryptor::Encryptor() { +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC) { EnsureNSSInit(); } @@ -83,7 +85,7 @@ bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) { } bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) { - if (ciphertext.size() == 0) + if (ciphertext.empty()) return false; ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD, diff --git a/base/crypto/encryptor_openssl.cc b/base/crypto/encryptor_openssl.cc index 7e09545..0e101a0 100644 --- a/base/crypto/encryptor_openssl.cc +++ b/base/crypto/encryptor_openssl.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -45,7 +45,8 @@ class ScopedCipherCTX { } // namespace Encryptor::Encryptor() - : key_(NULL) { + : key_(NULL), + mode_(CBC) { } Encryptor::~Encryptor() { diff --git a/base/crypto/encryptor_win.cc b/base/crypto/encryptor_win.cc index 4a137b3..1d732b5 100644 --- a/base/crypto/encryptor_win.cc +++ b/base/crypto/encryptor_win.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -28,7 +28,10 @@ DWORD GetCipherBlockSize(HCRYPTKEY key) { } // namespace -Encryptor::Encryptor() { +Encryptor::Encryptor() + : key_(NULL), + mode_(CBC), + block_size_(0) { } Encryptor::~Encryptor() { diff --git a/base/debug/debug_on_start_win.cc b/base/debug/debug_on_start_win.cc index b5c1094..6ca88dd 100644 --- a/base/debug/debug_on_start_win.cc +++ b/base/debug/debug_on_start_win.cc @@ -20,7 +20,7 @@ namespace debug { // /-/argument. // Note: command_line is non-destructively modified. bool DebugOnStart::FindArgument(wchar_t* command_line, const char* argument_c) { - wchar_t argument[50]; + wchar_t argument[50] = {}; for (int i = 0; argument_c[i]; ++i) argument[i] = argument_c[i]; diff --git a/base/event_recorder.cc b/base/event_recorder.cc index 7c3336b..1119b4f 100644 --- a/base/event_recorder.cc +++ b/base/event_recorder.cc @@ -49,7 +49,7 @@ bool EventRecorder::StartRecording(const FilePath& filename) { return false; // Open the recording file. - DCHECK(file_ == NULL); + DCHECK(!file_); file_ = file_util::OpenFile(filename, "wb+"); if (!file_) { DLOG(ERROR) << "EventRecorder could not open log file"; @@ -100,7 +100,7 @@ bool EventRecorder::StartPlayback(const FilePath& filename) { return false; // Open the recording file. - DCHECK(file_ == NULL); + DCHECK(!file_); file_ = file_util::OpenFile(filename, "rb"); if (!file_) { DLOG(ERROR) << "EventRecorder Playback could not open log file"; diff --git a/base/file_path.cc b/base/file_path.cc index 5f1375a..29ec7a8 100644 --- a/base/file_path.cc +++ b/base/file_path.cc @@ -249,9 +249,8 @@ bool FilePath::AppendRelativePath(const FilePath& child, GetComponents(&parent_components); child.GetComponents(&child_components); - if (parent_components.size() >= child_components.size()) - return false; - if (parent_components.size() == 0) + if (parent_components.empty() || + parent_components.size() >= child_components.size()) return false; std::vector<StringType>::const_iterator parent_comp = @@ -520,6 +519,12 @@ string16 FilePath::LossyDisplayName() const { return WideToUTF16(base::SysNativeMBToWide(path_)); } +std::string FilePath::MaybeAsASCII() const { + if (IsStringASCII(path_)) + return path_; + return ""; +} + // The *Hack functions are temporary while we fix the remainder of the code. // Remember to remove the #includes at the top when you remove these. @@ -535,6 +540,12 @@ string16 FilePath::LossyDisplayName() const { return path_; } +std::string FilePath::MaybeAsASCII() const { + if (IsStringASCII(path_)) + return WideToASCII(path_); + return ""; +} + // static FilePath FilePath::FromWStringHack(const std::wstring& wstring) { return FilePath(wstring); diff --git a/base/file_path.h b/base/file_path.h index 84bb350..1afa7a6 100644 --- a/base/file_path.h +++ b/base/file_path.h @@ -283,6 +283,11 @@ class FilePath { // want to stuff a string16 into some other API. string16 LossyDisplayName() const; + // Return the path as ASCII, or the empty string if the path is not ASCII. + // This should only be used for cases where the FilePath is representing a + // known-ASCII filename. + std::string MaybeAsASCII() const; + // Older Chromium code assumes that paths are always wstrings. // These functions convert wstrings to/from FilePaths, and are // useful to smooth porting that old code to the FilePath API. diff --git a/base/file_util.cc b/base/file_util.cc index c9661e7..7cf67dd 100644 --- a/base/file_util.cc +++ b/base/file_util.cc @@ -341,7 +341,7 @@ bool MemoryMappedFile::Initialize(base::PlatformFile file) { return true; } -bool MemoryMappedFile::IsValid() { +bool MemoryMappedFile::IsValid() const { return data_ != NULL; } diff --git a/base/file_util.h b/base/file_util.h index ab6906d..108c998 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -506,11 +506,17 @@ class MemoryMappedFile { // ownership of |file| and close it when done. bool Initialize(base::PlatformFile file); +#if defined(OS_WIN) + // Opens an existing file and maps it as an image section. Please refer to + // the Initialize function above for additional information. + bool InitializeAsImageSection(const FilePath& file_name); +#endif // OS_WIN + const uint8* data() const { return data_; } size_t length() const { return length_; } // Is file_ a valid file handle that points to an open, memory mapped file? - bool IsValid(); + bool IsValid() const; private: // Open the given file and pass it to MapFileToMemoryInternal(). @@ -523,10 +529,14 @@ class MemoryMappedFile { // Closes all open handles. Later we may want to make this public. void CloseHandles(); - base::PlatformFile file_; #if defined(OS_WIN) + // MapFileToMemoryInternal calls this function. It provides the ability to + // pass in flags which control the mapped section. + bool MapFileToMemoryInternalEx(int flags); + HANDLE file_mapping_; #endif + base::PlatformFile file_; uint8* data_; size_t length_; diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index 7dd4127..9a2d90a 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -633,7 +633,7 @@ FileEnumerator::FileEnumerator(const FilePath& root_path, // The Windows version of this code appends the pattern to the root_path, // potentially only matching against items in the top-most directory. // Do the same here. - if (pattern.size() == 0) + if (pattern.empty()) pattern_ = FilePath::StringType(); pending_paths_.push(root_path); } diff --git a/base/file_util_win.cc b/base/file_util_win.cc index 51bcb4e..2c26a16 100644 --- a/base/file_util_win.cc +++ b/base/file_util_win.cc @@ -606,13 +606,11 @@ bool CreateTemporaryDirInDir(const FilePath& base_dir, for (int count = 0; count < 50; ++count) { // Try create a new temporary directory with random generated name. If // the one exists, keep trying another path name until we reach some limit. - path_to_create = base_dir; - string16 new_dir_name; new_dir_name.assign(prefix); new_dir_name.append(base::IntToString16(rand() % kint16max)); - path_to_create = path_to_create.Append(new_dir_name); + path_to_create = base_dir.Append(new_dir_name); if (::CreateDirectory(path_to_create.value().c_str(), NULL)) { *new_dir = path_to_create; return true; @@ -940,7 +938,31 @@ MemoryMappedFile::MemoryMappedFile() length_(INVALID_FILE_SIZE) { } +bool MemoryMappedFile::InitializeAsImageSection(const FilePath& file_name) { + if (IsValid()) + return false; + file_ = base::CreatePlatformFile( + file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, + NULL, NULL); + + if (file_ == base::kInvalidPlatformFileValue) { + LOG(ERROR) << "Couldn't open " << file_name.value(); + return false; + } + + if (!MapFileToMemoryInternalEx(SEC_IMAGE)) { + CloseHandles(); + return false; + } + + return true; +} + bool MemoryMappedFile::MapFileToMemoryInternal() { + return MapFileToMemoryInternalEx(0); +} + +bool MemoryMappedFile::MapFileToMemoryInternalEx(int flags) { base::ThreadRestrictions::AssertIOAllowed(); if (file_ == INVALID_HANDLE_VALUE) @@ -952,7 +974,7 @@ bool MemoryMappedFile::MapFileToMemoryInternal() { // length_ value comes from GetFileSize() above. GetFileSize() returns DWORD, // therefore the cast here is safe. - file_mapping_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY, + file_mapping_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY | flags, 0, static_cast<DWORD>(length_), NULL); if (!file_mapping_) { // According to msdn, system error codes are only reserved up to 15999. diff --git a/base/file_version_info_mac.h b/base/file_version_info_mac.h index 879edb3..d8cb1a6 100644 --- a/base/file_version_info_mac.h +++ b/base/file_version_info_mac.h @@ -20,6 +20,7 @@ class NSBundle; class FileVersionInfoMac : public FileVersionInfo { public: explicit FileVersionInfoMac(NSBundle *bundle); + virtual ~FileVersionInfoMac(); // Accessors to the different version properties. // Returns an empty string if the property is not found. @@ -41,8 +42,6 @@ class FileVersionInfoMac : public FileVersionInfo { virtual bool is_official_build(); private: - - // Returns a string16 value for a property name. // Returns the empty string if the property does not exist. string16 GetString16Value(CFStringRef name); diff --git a/base/file_version_info_mac.mm b/base/file_version_info_mac.mm index f716ccc..bcfd854 100644 --- a/base/file_version_info_mac.mm +++ b/base/file_version_info_mac.mm @@ -14,6 +14,8 @@ FileVersionInfoMac::FileVersionInfoMac(NSBundle *bundle) : bundle_(bundle) { } +FileVersionInfoMac::~FileVersionInfoMac() {} + // static FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() { return CreateFileVersionInfo(base::mac::MainAppBundlePath()); diff --git a/base/i18n/bidi_line_iterator.cc b/base/i18n/bidi_line_iterator.cc index 3222a3a..5e9cd11 100644 --- a/base/i18n/bidi_line_iterator.cc +++ b/base/i18n/bidi_line_iterator.cc @@ -22,7 +22,7 @@ BiDiLineIterator::~BiDiLineIterator() { bool BiDiLineIterator::Open(const string16& text, bool right_to_left, bool url) { - DCHECK(bidi_ == NULL); + DCHECK(!bidi_); UErrorCode error = U_ZERO_ERROR; bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error); if (U_FAILURE(error)) diff --git a/base/i18n/icu_string_conversions.cc b/base/i18n/icu_string_conversions.cc index c353feb..6b46537 100644 --- a/base/i18n/icu_string_conversions.cc +++ b/base/i18n/icu_string_conversions.cc @@ -221,8 +221,9 @@ bool WideToCodepage(const std::wstring& wide, // in case each code points translates to a UTF-16 surrogate pair, // and leave room for a NUL terminator. std::vector<UChar> utf16(wide.length() * 2 + 1); - u_strFromWCS(&utf16[0], utf16.size(), &utf16_len, - wide.c_str(), wide.length(), &status); + u_strFromUTF32(&utf16[0], utf16.size(), &utf16_len, + reinterpret_cast<const UChar32*>(wide.c_str()), + wide.length(), &status); DCHECK(U_SUCCESS(status)) << "failed to convert wstring to UChar*"; return ConvertFromUTF16(converter, &utf16[0], utf16_len, on_error, encoded); diff --git a/base/id_map.h b/base/id_map.h index 9a41d74..5aea98e 100644 --- a/base/id_map.h +++ b/base/id_map.h @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// 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. @@ -11,6 +11,7 @@ #include "base/basictypes.h" #include "base/hash_tables.h" #include "base/logging.h" +#include "base/threading/non_thread_safe.h" // Ownership semantics - own pointer means the pointer is deleted in Remove() // & during destruction @@ -31,16 +32,23 @@ enum IDMapOwnershipSemantics { // This class does not have a virtual destructor, do not inherit from it when // ownership semantics are set to own because pointers will leak. template<typename T, IDMapOwnershipSemantics OS = IDMapExternalPointer> -class IDMap { +class IDMap : public base::NonThreadSafe { private: typedef int32 KeyType; typedef base::hash_map<KeyType, T*> HashTable; public: IDMap() : iteration_depth_(0), next_id_(1), check_on_null_data_(false) { + // A number of consumers of IDMap create it on one thread but always access + // it from a different, but consitent, thread post-construction. + DetachFromThread(); } ~IDMap() { + // Many IDMap's are static, and hence will be destroyed on the main thread. + // However, all the accesses may take place on another thread, such as the + // IO thread. Detaching again to clean this up. + DetachFromThread(); Releaser<OS, 0>::release_all(&data_); } @@ -49,6 +57,7 @@ class IDMap { // Adds a view with an automatically generated unique ID. See AddWithID. KeyType Add(T* data) { + DCHECK(CalledOnValidThread()); CHECK(!check_on_null_data_ || data); KeyType this_id = next_id_; DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item"; @@ -62,12 +71,14 @@ class IDMap { // this function, or allow this object to generate IDs and call Add. These // two methods may not be mixed, or duplicate IDs may be generated void AddWithID(T* data, KeyType id) { + DCHECK(CalledOnValidThread()); CHECK(!check_on_null_data_ || data); DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item"; data_[id] = data; } void Remove(KeyType id) { + DCHECK(CalledOnValidThread()); typename HashTable::iterator i = data_.find(id); if (i == data_.end()) { NOTREACHED() << "Attempting to remove an item not in the list"; @@ -83,10 +94,12 @@ class IDMap { } bool IsEmpty() const { + DCHECK(CalledOnValidThread()); return size() == 0u; } T* Lookup(KeyType id) const { + DCHECK(CalledOnValidThread()); typename HashTable::const_iterator i = data_.find(id); if (i == data_.end()) return NULL; @@ -94,6 +107,7 @@ class IDMap { } size_t size() const { + DCHECK(CalledOnValidThread()); return data_.size() - removed_ids_.size(); } @@ -105,28 +119,34 @@ class IDMap { Iterator(IDMap<T, OS>* map) : map_(map), iter_(map_->data_.begin()) { + DCHECK(map->CalledOnValidThread()); ++map_->iteration_depth_; SkipRemovedEntries(); } ~Iterator() { + DCHECK(map_->CalledOnValidThread()); if (--map_->iteration_depth_ == 0) map_->Compact(); } bool IsAtEnd() const { + DCHECK(map_->CalledOnValidThread()); return iter_ == map_->data_.end(); } KeyType GetCurrentKey() const { + DCHECK(map_->CalledOnValidThread()); return iter_->first; } ReturnType* GetCurrentValue() const { + DCHECK(map_->CalledOnValidThread()); return iter_->second; } void Advance() { + DCHECK(map_->CalledOnValidThread()); ++iter_; SkipRemovedEntries(); } diff --git a/base/lazy_instance.h b/base/lazy_instance.h index f4cfda0..a22dbf3 100644 --- a/base/lazy_instance.h +++ b/base/lazy_instance.h @@ -88,7 +88,7 @@ class LazyInstanceHelper { STATE_CREATED = 2 }; - explicit LazyInstanceHelper(LinkerInitialized x) { /* state_ is 0 */ } + explicit LazyInstanceHelper(LinkerInitialized /*unused*/) {/* state_ is 0 */} // Declaring a destructor (even if it's empty) will cause MSVC to register a // static initializer to register the empty destructor with atexit(). @@ -127,7 +127,7 @@ class LazyInstance : public LazyInstanceHelper { NeedsInstance()) { // Create the instance in the space provided by |buf_|. instance_ = Traits::New(buf_); - // Traits::Delete will be null for LeakyLazyInstannceTraits + // Traits::Delete will be null for LeakyLazyInstanceTraits void (*dtor)(void*) = Traits::Delete; CompleteInstance(this, (dtor == NULL) ? NULL : OnExit); } @@ -141,6 +141,19 @@ class LazyInstance : public LazyInstanceHelper { return instance_; } + bool operator==(Type* p) { + switch (base::subtle::NoBarrier_Load(&state_)) { + case STATE_EMPTY: + return p == NULL; + case STATE_CREATING: + return static_cast<int8*>(static_cast<void*>(p)) == buf_; + case STATE_CREATED: + return p == instance_; + default: + return false; + } + } + private: // Adapter function for use with AtExit. This should be called single // threaded, so don't use atomic operations. diff --git a/base/logging.cc b/base/logging.cc index 27bf6c2..57a2a89 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -14,7 +14,6 @@ typedef HANDLE MutexHandle; // Windows doesn't define STDERR_FILENO. Define it here. #define STDERR_FILENO 2 #elif defined(OS_MACOSX) -#include <CoreFoundation/CoreFoundation.h> #include <mach/mach.h> #include <mach/mach_time.h> #include <mach-o/dyld.h> @@ -57,10 +56,6 @@ typedef pthread_mutex_t* MutexHandle; #if defined(OS_POSIX) #include "base/safe_strerror_posix.h" #endif -#if defined(OS_MACOSX) -#include "base/mac/scoped_cftyperef.h" -#include "base/sys_string_conversions.h" -#endif namespace logging { @@ -509,12 +504,6 @@ void DisplayDebugMessageInDialog(const std::string& str) { MessageBoxW(NULL, &cmdline[0], L"Fatal error", MB_OK | MB_ICONHAND | MB_TOPMOST); } -#elif defined(OS_MACOSX) - base::mac::ScopedCFTypeRef<CFStringRef> message( - base::SysUTF8ToCFStringRef(str)); - CFUserNotificationDisplayNotice(0, kCFUserNotificationStopAlertLevel, - NULL, NULL, NULL, CFSTR("Fatal Error"), - message, NULL); #else // We intentionally don't implement a dialog on other platforms. // You can just look at stderr. diff --git a/base/logging.h b/base/logging.h index b1bc0b0..baf8eb6 100644 --- a/base/logging.h +++ b/base/logging.h @@ -382,6 +382,23 @@ const LogSeverity LOG_0 = LOG_ERROR; 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) \ @@ -539,6 +556,7 @@ DEFINE_CHECK_OP_IMPL(GT, > ) #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 @@ -555,6 +573,7 @@ DEFINE_CHECK_OP_IMPL(GT, > ) #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 @@ -589,6 +608,8 @@ enum { DEBUG_MODE = ENABLE_DLOG }; #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 diff --git a/base/mac/mac_util.h b/base/mac/mac_util.h index d75fb6e..990197d 100644 --- a/base/mac/mac_util.h +++ b/base/mac/mac_util.h @@ -110,57 +110,76 @@ void RemoveFromLoginItems(); // 'Login Item' with 'hide on startup' flag. Used to suppress opening windows. bool WasLaunchedAsHiddenLoginItem(); -#if defined(__OBJC__) +} // namespace mac +} // namespace base + +#if !defined(__OBJC__) +#define OBJC_CPP_CLASS_DECL(x) class x; +#else // __OBJC__ +#define OBJC_CPP_CLASS_DECL(x) +#endif // __OBJC__ -// Convert toll-free bridged CFTypes to NSTypes. This does not autorelease -// |cf_val|. This is useful for the case where there is a CFType in a call that -// expects an NSType and the compiler is complaining about const casting -// problems. -// The call is used like this: +// Convert toll-free bridged CFTypes to NSTypes and vice-versa. This does not +// autorelease |cf_val|. This is useful for the case where there is a CFType in +// a call that expects an NSType and the compiler is complaining about const +// casting problems. +// The calls are used like this: // NSString *foo = CFToNSCast(CFSTR("Hello")); +// CFStringRef foo2 = NSToCFCast(@"Hello"); // The macro magic below is to enforce safe casting. It could possibly have // been done using template function specialization, but template function // specialization doesn't always work intuitively, // (http://www.gotw.ca/publications/mill17.htm) so the trusty combination // of macros and function overloading is used instead. -#define CF_TO_NS_CAST(TypeCF, TypeNS) \ -inline TypeNS* CFToNSCast(TypeCF cf_val) { \ - TypeNS* ns_val = \ - const_cast<TypeNS*>(reinterpret_cast<const TypeNS*>(cf_val)); \ - DCHECK(!ns_val || [ns_val isKindOfClass:[TypeNS class]]); \ - return ns_val; \ -} +#define CF_TO_NS_CAST_DECL(TypeCF, TypeNS) \ +OBJC_CPP_CLASS_DECL(TypeNS) \ +\ +namespace base { \ +namespace mac { \ +TypeNS* CFToNSCast(TypeCF##Ref cf_val); \ +TypeCF##Ref NSToCFCast(TypeNS* ns_val); \ +} \ +} \ + +#define CF_TO_NS_MUTABLE_CAST_DECL(name) \ +CF_TO_NS_CAST_DECL(CF##name, NS##name) \ +OBJC_CPP_CLASS_DECL(NSMutable##name) \ +\ +namespace base { \ +namespace mac { \ +NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val); \ +CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val); \ +} \ +} \ // List of toll-free bridged types taken from: // http://www.cocoadev.com/index.pl?TollFreeBridged -CF_TO_NS_CAST(CFArrayRef, NSArray); -CF_TO_NS_CAST(CFMutableArrayRef, NSMutableArray); -CF_TO_NS_CAST(CFAttributedStringRef, NSAttributedString); -CF_TO_NS_CAST(CFMutableAttributedStringRef, NSMutableAttributedString); -CF_TO_NS_CAST(CFCalendarRef, NSCalendar); -CF_TO_NS_CAST(CFCharacterSetRef, NSCharacterSet); -CF_TO_NS_CAST(CFMutableCharacterSetRef, NSMutableCharacterSet); -CF_TO_NS_CAST(CFDataRef, NSData); -CF_TO_NS_CAST(CFMutableDataRef, NSMutableData); -CF_TO_NS_CAST(CFDateRef, NSDate); -CF_TO_NS_CAST(CFDictionaryRef, NSDictionary); -CF_TO_NS_CAST(CFMutableDictionaryRef, NSMutableDictionary); -CF_TO_NS_CAST(CFNumberRef, NSNumber); -CF_TO_NS_CAST(CFRunLoopTimerRef, NSTimer); -CF_TO_NS_CAST(CFSetRef, NSSet); -CF_TO_NS_CAST(CFMutableSetRef, NSMutableSet); -CF_TO_NS_CAST(CFStringRef, NSString); -CF_TO_NS_CAST(CFMutableStringRef, NSMutableString); -CF_TO_NS_CAST(CFURLRef, NSURL); -CF_TO_NS_CAST(CFTimeZoneRef, NSTimeZone); -CF_TO_NS_CAST(CFReadStreamRef, NSInputStream); -CF_TO_NS_CAST(CFWriteStreamRef, NSOutputStream); - -#endif // __OBJC__ - -} // namespace mac -} // namespace base +CF_TO_NS_MUTABLE_CAST_DECL(Array); +CF_TO_NS_MUTABLE_CAST_DECL(AttributedString); +CF_TO_NS_CAST_DECL(CFCalendar, NSCalendar); +CF_TO_NS_MUTABLE_CAST_DECL(CharacterSet); +CF_TO_NS_MUTABLE_CAST_DECL(Data); +CF_TO_NS_CAST_DECL(CFDate, NSDate); +CF_TO_NS_MUTABLE_CAST_DECL(Dictionary); +CF_TO_NS_CAST_DECL(CFError, NSError); +CF_TO_NS_CAST_DECL(CFLocale, NSLocale); +CF_TO_NS_CAST_DECL(CFNumber, NSNumber); +CF_TO_NS_CAST_DECL(CFRunLoopTimer, NSTimer); +CF_TO_NS_CAST_DECL(CFTimeZone, NSTimeZone); +CF_TO_NS_MUTABLE_CAST_DECL(Set); +CF_TO_NS_CAST_DECL(CFReadStream, NSInputStream); +CF_TO_NS_CAST_DECL(CFWriteStream, NSOutputStream); +CF_TO_NS_MUTABLE_CAST_DECL(String); +CF_TO_NS_CAST_DECL(CFURL, NSURL); + +// Stream operations for CFTypes. They can be used with NSTypes as well +// by using the NSToCFCast methods above. +// e.g. LOG(INFO) << base::mac::NSToCFCast(@"foo"); +// Operator << can not be overloaded for ObjectiveC types as the compiler +// can not distinguish between overloads for id with overloads for void*. +extern std::ostream& operator<<(std::ostream& o, const CFErrorRef err); +extern std::ostream& operator<<(std::ostream& o, const CFStringRef str); #endif // BASE_MAC_MAC_UTIL_H_ diff --git a/base/mac/mac_util.mm b/base/mac/mac_util.mm index 7f3be18..2eddeae 100644 --- a/base/mac/mac_util.mm +++ b/base/mac/mac_util.mm @@ -480,5 +480,77 @@ bool WasLaunchedAsHiddenLoginItem() { return IsHiddenLoginItem(item); } +// Definitions for the corresponding CF_TO_NS_CAST_DECL macros in mac_util.h. +#define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \ +\ +TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \ + DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ + TypeNS* ns_val = \ + const_cast<TypeNS*>(reinterpret_cast<const TypeNS*>(cf_val)); \ + return ns_val; \ +} \ +\ +TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \ + TypeCF##Ref cf_val = reinterpret_cast<TypeCF##Ref>(ns_val); \ + DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ + return cf_val; \ +} \ + +#define CF_TO_NS_MUTABLE_CAST_DEFN(name) \ +CF_TO_NS_CAST_DEFN(CF##name, NS##name) \ +\ +NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \ + DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ + NSMutable##name* ns_val = reinterpret_cast<NSMutable##name*>(cf_val); \ + return ns_val; \ +} \ +\ +CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \ + CFMutable##name##Ref cf_val = \ + reinterpret_cast<CFMutable##name##Ref>(ns_val); \ + DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ + return cf_val; \ +} \ + +CF_TO_NS_MUTABLE_CAST_DEFN(Array); +CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString); +CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar); +CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet); +CF_TO_NS_MUTABLE_CAST_DEFN(Data); +CF_TO_NS_CAST_DEFN(CFDate, NSDate); +CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary); +CF_TO_NS_CAST_DEFN(CFError, NSError); +CF_TO_NS_CAST_DEFN(CFLocale, NSLocale); +CF_TO_NS_CAST_DEFN(CFNumber, NSNumber); +CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer); +CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone); +CF_TO_NS_MUTABLE_CAST_DEFN(Set); +CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream); +CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream); +CF_TO_NS_MUTABLE_CAST_DEFN(String); +CF_TO_NS_CAST_DEFN(CFURL, NSURL); + } // namespace mac } // namespace base + +std::ostream& operator<<(std::ostream& o, const CFStringRef string) { + return o << base::SysCFStringRefToUTF8(string); +} + +std::ostream& operator<<(std::ostream& o, const CFErrorRef err) { + base::mac::ScopedCFTypeRef<CFStringRef> desc(CFErrorCopyDescription(err)); + base::mac::ScopedCFTypeRef<CFDictionaryRef> user_info( + CFErrorCopyUserInfo(err)); + CFStringRef errorDesc = NULL; + if (user_info.get()) { + errorDesc = reinterpret_cast<CFStringRef>( + CFDictionaryGetValue(user_info.get(), kCFErrorDescriptionKey)); + } + o << "Code: " << CFErrorGetCode(err) + << " Domain: " << CFErrorGetDomain(err) + << " Desc: " << desc.get(); + if(errorDesc) { + o << "(" << errorDesc << ")"; + } + return o; +} diff --git a/base/message_loop.cc b/base/message_loop.cc index 2ec872f..348ace7 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -4,6 +4,11 @@ #include "base/message_loop.h" +#if defined(OS_POSIX) && !defined(OS_MACOSX) +#include <gdk/gdk.h> +#include <gdk/gdkx.h> +#endif + #include <algorithm> #include "base/compiler_specific.h" @@ -669,7 +674,17 @@ void MessageLoopForUI::DidProcessMessage(const MSG& message) { } #endif // defined(OS_WIN) +<<<<<<< HEAD #if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(ANDROID) +======= +#if defined(USE_X11) +Display* MessageLoopForUI::GetDisplay() { + return gdk_x11_get_default_xdisplay(); +} +#endif // defined(USE_X11) + +#if !defined(OS_MACOSX) && !defined(OS_NACL) +>>>>>>> chromium.org at r11.0.696.0 void MessageLoopForUI::AddObserver(Observer* observer) { pump_ui()->AddObserver(observer); } diff --git a/base/message_loop.h b/base/message_loop.h index d238997..c1dce43 100644 --- a/base/message_loop.h +++ b/base/message_loop.h @@ -24,6 +24,7 @@ #include "base/message_pump_libevent.h" #if !defined(OS_MACOSX) #include "base/message_pump_glib.h" +typedef struct _XDisplay Display; #endif #endif #if defined(TOUCH_UI) @@ -464,9 +465,9 @@ class MessageLoop : public base::MessagePump::Delegate { scoped_refptr<base::Histogram> message_histogram_; // A null terminated list which creates an incoming_queue of tasks that are - // acquired under a mutex for processing on this instance's thread. These tasks - // have not yet been sorted out into items for our work_queue_ vs items that - // will be handled by the TimerManager. + // acquired under a mutex for processing on this instance's thread. These + // tasks have not yet been sorted out into items for our work_queue_ vs + // items that will be handled by the TimerManager. TaskQueue incoming_queue_; // Protect access to incoming_queue_. mutable base::Lock incoming_queue_lock_; @@ -508,6 +509,18 @@ class MessageLoopForUI : public MessageLoop { void DidProcessMessage(const MSG& message); #endif // defined(OS_WIN) +#if defined(USE_X11) + // Returns the Xlib Display that backs the MessagePump for this MessageLoop. + // + // This allows for raw access to the X11 server in situations where our + // abstractions do not provide enough power. + // + // Be careful how this is used. The MessagePump in general expects + // exclusive access to the Display. Calling things like XNextEvent() will + // likely break things in subtle, hard to detect, ways. + Display* GetDisplay(); +#endif // defined(OS_X11) + #if !defined(OS_MACOSX) // Please see message_pump_win/message_pump_glib for definitions of these // methods. diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc index 9576a58..2869bb8 100644 --- a/base/message_loop_unittest.cc +++ b/base/message_loop_unittest.cc @@ -768,6 +768,18 @@ class RecursiveTask : public OrderedTasks { bool is_reentrant_; }; +class RecursiveSlowTask : public RecursiveTask { + public: + RecursiveSlowTask(int depth, TaskList* order, int cookie, bool is_reentrant) + : RecursiveTask(depth, order, cookie, is_reentrant) { + } + + virtual void Run() { + RecursiveTask::Run(); + PlatformThread::Sleep(10); // milliseconds + } +}; + class QuitTask : public OrderedTasks { public: QuitTask(TaskList* order, int cookie) @@ -893,6 +905,42 @@ void RunTest_RecursiveDenial1(MessageLoop::Type message_loop_type) { EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false)); } +void RunTest_RecursiveDenial3(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed()); + TaskList order; + MessageLoop::current()->PostTask(FROM_HERE, + new RecursiveSlowTask(2, &order, 1, false)); + MessageLoop::current()->PostTask(FROM_HERE, + new RecursiveSlowTask(2, &order, 2, false)); + MessageLoop::current()->PostDelayedTask(FROM_HERE, + new OrderedTasks(&order, 3), 5); + MessageLoop::current()->PostDelayedTask(FROM_HERE, + new QuitTask(&order, 4), 5); + + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(16U, order.size()); + EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[ 5], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[ 6], TaskItem(ORDERERD, 3, true)); + EXPECT_EQ(order[ 7], TaskItem(ORDERERD, 3, false)); + EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order[10], TaskItem(QUITMESSAGELOOP, 4, true)); + EXPECT_EQ(order[11], TaskItem(QUITMESSAGELOOP, 4, false)); + EXPECT_EQ(order[12], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[13], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[14], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[15], TaskItem(RECURSIVE, 2, false)); +} + void RunTest_RecursiveSupport1(MessageLoop::Type message_loop_type) { MessageLoop loop(message_loop_type); @@ -1426,6 +1474,12 @@ TEST(MessageLoopTest, RecursiveDenial1) { RunTest_RecursiveDenial1(MessageLoop::TYPE_IO); } +TEST(MessageLoopTest, RecursiveDenial3) { + RunTest_RecursiveDenial3(MessageLoop::TYPE_DEFAULT); + RunTest_RecursiveDenial3(MessageLoop::TYPE_UI); + RunTest_RecursiveDenial3(MessageLoop::TYPE_IO); +} + TEST(MessageLoopTest, RecursiveSupport1) { RunTest_RecursiveSupport1(MessageLoop::TYPE_DEFAULT); RunTest_RecursiveSupport1(MessageLoop::TYPE_UI); diff --git a/base/message_pump.h b/base/message_pump.h index a354724..866b33a 100644 --- a/base/message_pump.h +++ b/base/message_pump.h @@ -22,7 +22,8 @@ class MessagePump : public RefCountedThreadSafe<MessagePump> { // Called from within Run in response to ScheduleWork or when the message // pump would otherwise call DoDelayedWork. Returns true to indicate that - // work was done. DoDelayedWork will not be called if DoWork returns true. + // work was done. DoDelayedWork will still be called if DoWork returns + // true, but DoIdleWork will not. virtual bool DoWork() = 0; // Called from within Run in response to ScheduleDelayedWork or when the @@ -61,7 +62,8 @@ class MessagePump : public RefCountedThreadSafe<MessagePump> { // if (should_quit_) // break; // - // did_work |= delegate_->DoDelayedWork(); + // TimeTicks next_time; + // did_work |= delegate_->DoDelayedWork(&next_time); // if (should_quit_) // break; // @@ -84,9 +86,9 @@ class MessagePump : public RefCountedThreadSafe<MessagePump> { // blocks until there is more work of any type to do. // // Notice that the run loop cycles between calling DoInternalWork, DoWork, - // and DoDelayedWork methods. This helps ensure that neither work queue - // starves the other. This is important for message pumps that are used to - // drive animations, for example. + // and DoDelayedWork methods. This helps ensure that none of these work + // queues starve the others. This is important for message pumps that are + // used to drive animations, for example. // // Notice also that after each callout to foreign code, the run loop checks // to see if it should quit. The Quit method is responsible for setting this diff --git a/base/message_pump_glib_x.cc b/base/message_pump_glib_x.cc index e27a07a..06a156e 100644 --- a/base/message_pump_glib_x.cc +++ b/base/message_pump_glib_x.cc @@ -32,6 +32,11 @@ gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams, DCHECK(window); // TODO(sad): Remove once determined if necessary. + if (GDK_WINDOW_TYPE(window) != GDK_WINDOW_TOPLEVEL && + GDK_WINDOW_TYPE(window) != GDK_WINDOW_CHILD && + GDK_WINDOW_TYPE(window) != GDK_WINDOW_DIALOG) + return true; + // TODO(sad): Do we need to set a flag on |window| to make sure we don't // select for the same GdkWindow multiple times? Does it matter? msgpump->SetupXInput2ForXWindow(GDK_WINDOW_XID(window)); @@ -44,17 +49,27 @@ gboolean GtkWidgetRealizeCallback(GSignalInvocationHint* hint, guint nparams, // signal for GTK+ widgets, so that whenever the signal triggers for any // GtkWidget, which means the GtkWidget should now have a GdkWindow, we can // setup XInput2 events for the GdkWindow. +static guint realize_signal_id = 0; +static guint realize_hook_id = 0; + void SetupGtkWidgetRealizeNotifier(base::MessagePumpGlibX* msgpump) { - guint signal_id; gpointer klass = g_type_class_ref(GTK_TYPE_WIDGET); - g_signal_parse_name("realize", GTK_TYPE_WIDGET, &signal_id, NULL, FALSE); - g_signal_add_emission_hook(signal_id, 0, GtkWidgetRealizeCallback, - static_cast<gpointer>(msgpump), NULL); + g_signal_parse_name("realize", GTK_TYPE_WIDGET, + &realize_signal_id, NULL, FALSE); + realize_hook_id = g_signal_add_emission_hook(realize_signal_id, 0, + GtkWidgetRealizeCallback, static_cast<gpointer>(msgpump), NULL); g_type_class_unref(klass); } +void RemoveGtkWidgetRealizeNotifier() { + if (realize_signal_id != 0) + g_signal_remove_emission_hook(realize_signal_id, realize_hook_id); + realize_signal_id = 0; + realize_hook_id = 0; +} + #endif // HAVE_XINPUT2 } // namespace @@ -80,6 +95,9 @@ MessagePumpGlibX::MessagePumpGlibX() : base::MessagePumpForUI(), } MessagePumpGlibX::~MessagePumpGlibX() { +#if defined(HAVE_XINPUT2) + RemoveGtkWidgetRealizeNotifier(); +#endif } #if defined(HAVE_XINPUT2) @@ -119,7 +137,7 @@ void MessagePumpGlibX::SetupXInput2ForXWindow(Window xwindow) { bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) { GdkDisplay* gdisp = gdk_display_get_default(); - if (!gdisp) + if (!gdisp || !GetDispatcher()) return MessagePumpForUI::RunOnce(context, block); Display* display = GDK_DISPLAY_XDISPLAY(gdisp); diff --git a/base/message_pump_libevent.cc b/base/message_pump_libevent.cc index 933d795..28b4bfe 100644 --- a/base/message_pump_libevent.cc +++ b/base/message_pump_libevent.cc @@ -78,7 +78,7 @@ bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() { void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e, bool is_persistent) { DCHECK(e); - DCHECK(event_ == NULL); + DCHECK(!event_); is_persistent_ = is_persistent; event_ = e; diff --git a/base/message_pump_mac.h b/base/message_pump_mac.h index c30a8ea..b903a1a 100644 --- a/base/message_pump_mac.h +++ b/base/message_pump_mac.h @@ -86,24 +86,17 @@ class MessagePumpCFRunLoopBase : public MessagePump { private: // Timer callback scheduled by ScheduleDelayedWork. This does not do any - // work, but it signals delayed_work_source_ so that delayed work can be - // performed within the appropriate priority constraints. + // work, but it signals work_source_ so that delayed work can be performed + // within the appropriate priority constraints. static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info); // Perform highest-priority work. This is associated with work_source_ - // signalled by ScheduleWork. The static method calls the instance method; - // the instance method returns true if work was done. + // signalled by ScheduleWork or RunDelayedWorkTimer. The static method calls + // the instance method; the instance method returns true if it resignalled + // work_source_ to be called again from the loop. static void RunWorkSource(void* info); bool RunWork(); - // Perform delayed-priority work. This is associated with - // delayed_work_source_ signalled by RunDelayedWorkTimer, and is responsible - // for calling ScheduleDelayedWork again if appropriate. The static method - // calls the instance method; the instance method returns true if more - // delayed work is available. - static void RunDelayedWorkSource(void* info); - bool RunDelayedWork(); - // Perform idle-priority work. This is normally called by PreWaitObserver, // but is also associated with idle_work_source_. When this function // actually does perform idle work, it will resignal that source. The @@ -162,7 +155,6 @@ class MessagePumpCFRunLoopBase : public MessagePump { // callbacks. CFRunLoopTimerRef delayed_work_timer_; CFRunLoopSourceRef work_source_; - CFRunLoopSourceRef delayed_work_source_; CFRunLoopSourceRef idle_work_source_; CFRunLoopSourceRef nesting_deferred_work_source_; CFRunLoopObserverRef pre_wait_observer_; @@ -202,7 +194,6 @@ class MessagePumpCFRunLoopBase : public MessagePump { // any call to Run on the stack. The Run method will check for delegateless // work on entry and redispatch it as needed once a delegate is available. bool delegateless_work_; - bool delegateless_delayed_work_; bool delegateless_idle_work_; DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); diff --git a/base/message_pump_mac.mm b/base/message_pump_mac.mm index 8c5461c..8feb56f 100644 --- a/base/message_pump_mac.mm +++ b/base/message_pump_mac.mm @@ -51,7 +51,6 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() run_nesting_level_(0), deepest_nesting_level_(0), delegateless_work_(false), - delegateless_delayed_work_(false), delegateless_idle_work_(false) { run_loop_ = CFRunLoopGetCurrent(); CFRetain(run_loop_); @@ -78,15 +77,9 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() &source_context); CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); - source_context.perform = RunDelayedWorkSource; - delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator - 2, // priority - &source_context); - CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); - source_context.perform = RunIdleWorkSource; idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator - 3, // priority + 2, // priority &source_context); CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); @@ -169,9 +162,6 @@ MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); CFRelease(idle_work_source_); - CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); - CFRelease(delayed_work_source_); - CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); CFRelease(work_source_); @@ -199,10 +189,6 @@ void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { CFRunLoopSourceSignal(work_source_); delegateless_work_ = false; } - if (delegateless_delayed_work_) { - CFRunLoopSourceSignal(delayed_work_source_); - delegateless_delayed_work_ = false; - } if (delegateless_idle_work_) { CFRunLoopSourceSignal(idle_work_source_); delegateless_idle_work_ = false; @@ -260,11 +246,10 @@ void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, self->delayed_work_fire_time_ = kCFTimeIntervalMax; // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. - // In order to establish the proper priority where delegate_->DoDelayedWork - // can only be called if delegate_->DoWork returns false, the timer used - // to schedule delayed work must signal a CFRunLoopSource set at a lower - // priority than the one used for delegate_->DoWork. - CFRunLoopSourceSignal(self->delayed_work_source_); + // In order to establish the proper priority in which work and delayed work + // are processed one for one, the timer used to schedule delayed work must + // signal a CFRunLoopSource used to dispatch both work and delayed work. + CFRunLoopSourceSignal(self->work_source_); } // Called from the run loop. @@ -291,58 +276,39 @@ bool MessagePumpCFRunLoopBase::RunWork() { // released promptly even in the absence of UI events. MessagePumpScopedAutoreleasePool autorelease_pool(this); - // Call DoWork once, and if something was done, arrange to come back here - // again as long as the loop is still running. + // Call DoWork and DoDelayedWork once, and if something was done, arrange to + // come back here again as long as the loop is still running. bool did_work = delegate_->DoWork(); - if (did_work) { - CFRunLoopSourceSignal(work_source_); - } - - return did_work; -} - -// Called from the run loop. -// static -void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { - MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); - self->RunDelayedWork(); -} - -// Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. -bool MessagePumpCFRunLoopBase::RunDelayedWork() { - if (!delegate_) { - // This point can be reached with a NULL delegate_ if Run is not on the - // stack but foreign code is spinning the CFRunLoop. Arrange to come back - // here when a delegate is available. - delegateless_delayed_work_ = true; - return false; - } - - // The NSApplication-based run loop only drains the autorelease pool at each - // UI event (NSEvent). The autorelease pool is not drained for each - // CFRunLoopSource target that's run. Use a local pool for any autoreleased - // objects if the app is not currently handling a UI event to ensure they're - // released promptly even in the absence of UI events. - MessagePumpScopedAutoreleasePool autorelease_pool(this); + bool resignal_work_source = did_work; TimeTicks next_time; delegate_->DoDelayedWork(&next_time); - - bool more_work = !next_time.is_null(); - if (more_work) { - TimeDelta delay = next_time - TimeTicks::Now(); - if (delay > TimeDelta()) { - // There's more delayed work to be done in the future. - ScheduleDelayedWork(next_time); - } else { - // There's more delayed work to be done, and its time is in the past. - // Arrange to come back here directly as long as the loop is still - // running. - CFRunLoopSourceSignal(delayed_work_source_); + if (!did_work) { + // Determine whether there's more delayed work, and if so, if it needs to + // be done at some point in the future or if it's already time to do it. + // Only do these checks if did_work is false. If did_work is true, this + // function, and therefore any additional delayed work, will get another + // chance to run before the loop goes to sleep. + bool more_delayed_work = !next_time.is_null(); + if (more_delayed_work) { + TimeDelta delay = next_time - TimeTicks::Now(); + if (delay > TimeDelta()) { + // There's more delayed work to be done in the future. + ScheduleDelayedWork(next_time); + } else { + // There's more delayed work to be done, and its time is in the past. + // Arrange to come back here directly as long as the loop is still + // running. + resignal_work_source = true; + } } } - return more_work; + if (resignal_work_source) { + CFRunLoopSourceSignal(work_source_); + } + + return resignal_work_source; } // Called from the run loop. @@ -398,19 +364,12 @@ bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { // Immediately try work in priority order. if (!RunWork()) { - if (!RunDelayedWork()) { - if (!RunIdleWork()) { - return false; - } - } else { - // There was no work, and delayed work was done. Arrange for the loop - // to try non-nestable idle work on a subsequent pass. - CFRunLoopSourceSignal(idle_work_source_); + if (!RunIdleWork()) { + return false; } } else { - // Work was done. Arrange for the loop to try non-nestable delayed and - // idle work on a subsequent pass. - CFRunLoopSourceSignal(delayed_work_source_); + // Work was done. Arrange for the loop to try non-nestable idle work on + // a subsequent pass. CFRunLoopSourceSignal(idle_work_source_); } @@ -672,6 +631,13 @@ MessagePumpNSApplication::MessagePumpNSApplication() void MessagePumpNSApplication::DoRun(Delegate* delegate) { bool last_running_own_loop_ = running_own_loop_; + // NSApp must be initialized by calling: + // [{some class which implements CrAppProtocol} sharedApplication] + // Most likely candidates are CrApplication or BrowserCrApplication. + // These can be initialized from C++ code by calling + // RegisterCrApp() or RegisterBrowserCrApp(). + CHECK(NSApp); + if (![NSApp isRunning]) { running_own_loop_ = false; // NSApplication manages autorelease pools itself when run this way. diff --git a/base/message_pump_win.cc b/base/message_pump_win.cc index d3f3621..6098a4a 100644 --- a/base/message_pump_win.cc +++ b/base/message_pump_win.cc @@ -470,7 +470,7 @@ void MessagePumpForIO::DoRunLoop() { void MessagePumpForIO::WaitForWork() { // We do not support nested IO message loops. This is to avoid messy // recursion problems. - DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!"; + DCHECK_EQ(1, state_->run_depth) << "Cannot nest an IO message loop!"; int timeout = GetCurrentDelay(); if (timeout < 0) // Negative value means no timers waiting. diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc index fceccde..c1416db 100644 --- a/base/metrics/field_trial.cc +++ b/base/metrics/field_trial.cc @@ -41,6 +41,7 @@ FieldTrial::FieldTrial(const std::string& name, accumulated_group_probability_(0), next_group_number_(kDefaultGroupNumber+1), group_(kNotFinalized) { + DCHECK_GT(total_probability, 0); DCHECK(!default_group_name_.empty()); FieldTrialList::Register(this); @@ -53,7 +54,7 @@ FieldTrial::FieldTrial(const std::string& name, base::Time::Exploded exploded; exploded.year = year; exploded.month = month; - exploded.day_of_week = 0; // Should be unusued. + exploded.day_of_week = 0; // Should be unused. exploded.day_of_month = day_of_month; exploded.hour = 0; exploded.minute = 0; diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h index ec3a483..8b4a606 100644 --- a/base/metrics/field_trial.h +++ b/base/metrics/field_trial.h @@ -101,6 +101,7 @@ class FieldTrial : public RefCounted<FieldTrial> { // Establish the name and probability of the next group in this trial. // Sometimes, based on construction randomization, this call may cause the // provided group to be *THE* group selected for use in this instance. + // The return value is the group number of the new group. int AppendGroup(const std::string& name, Probability group_probability); // Return the name of the FieldTrial (excluding the group name). @@ -167,7 +168,7 @@ class FieldTrial : public RefCounted<FieldTrial> { // The randomly selected probability that is used to select a group (or have // the instance not participate). It is the product of divisor_ and a random // number between [0, 1). - Probability random_; + const Probability random_; // Sum of the probabilities of all appended groups. Probability accumulated_group_probability_; diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index a308932..1d6f884 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc @@ -21,8 +21,58 @@ namespace base { +// Static table of checksums for all possible 8 bit bytes. +const uint32 Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL, +0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, +0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, +0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 0x1adad47dL, +0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, +0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, +0x4c69105eL, 0xd56041e4L, 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, +0xa50ab56bL, 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, +0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 0xc8d75180L, +0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL, 0x2802b89eL, +0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, +0xb6662d3dL, 0x76dc4190L, 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, +0x6b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL, +0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, 0x6b6b51f4L, +0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 0x1b01a57bL, 0x8208f4c1L, +0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, +0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, +0xd4bb30e2L, 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, +0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, +0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 0xc90c2086L, 0x5768b525L, +0x206f85b3L, 0xb966d409L, 0xce61e49fL, 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, +0xc7d7a8b4L, 0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, +0x9abfb3b6L, 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L, +0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, 0xe40ecf0bL, +0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, 0x8708a3d2L, 0x1e01f268L, +0x6906c2feL, 0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, +0x89d32be0L, 0x10da7a5aL, 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, +0x60b08ed5L, 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, +0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, +0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 0x4669be79L, 0xcb61b38cL, +0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, +0x5505262fL, 0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, +0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, +0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, 0x95bf4a82L, +0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, +0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, +0xf6b9265bL, 0x6fb077e1L, 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, +0x11010b5cL, 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, +0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 0x4969474dL, +0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L, 0xa9bcae53L, +0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, +0x24b4a3a6L, 0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, +0xc4614ab8L, 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, +0x2d02ef8dL, +}; + typedef Histogram::Count Count; +// static +const size_t Histogram::kBucketCount_MAX = 16384u; + scoped_refptr<Histogram> Histogram::FactoryGet(const std::string& name, Sample minimum, Sample maximum, size_t bucket_count, Flags flags) { scoped_refptr<Histogram> histogram(NULL); @@ -35,7 +85,8 @@ scoped_refptr<Histogram> Histogram::FactoryGet(const std::string& name, if (!StatisticsRecorder::FindHistogram(name, &histogram)) { histogram = new Histogram(name, minimum, maximum, bucket_count); - StatisticsRecorder::FindHistogram(name, &histogram); + histogram->InitializeBucketRange(); + StatisticsRecorder::RegisterOrDiscardDuplicate(&histogram); } DCHECK_EQ(HISTOGRAM, histogram->histogram_type()); @@ -158,7 +209,7 @@ std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, pickle.WriteInt(histogram.declared_min()); pickle.WriteInt(histogram.declared_max()); pickle.WriteSize(histogram.bucket_count()); - pickle.WriteInt(histogram.range_checksum()); + pickle.WriteUInt32(histogram.range_checksum()); pickle.WriteInt(histogram.histogram_type()); pickle.WriteInt(histogram.flags()); @@ -178,7 +229,7 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { int declared_min; int declared_max; size_t bucket_count; - int range_checksum; + uint32 range_checksum; int histogram_type; int pickle_flags; SampleSet sample; @@ -188,7 +239,7 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { !pickle.ReadInt(&iter, &declared_min) || !pickle.ReadInt(&iter, &declared_max) || !pickle.ReadSize(&iter, &bucket_count) || - !pickle.ReadInt(&iter, &range_checksum) || + !pickle.ReadUInt32(&iter, &range_checksum) || !pickle.ReadInt(&iter, &histogram_type) || !pickle.ReadInt(&iter, &pickle_flags) || !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { @@ -249,18 +300,16 @@ Histogram::Inconsistencies Histogram::FindCorruption( const SampleSet& snapshot) const { int inconsistencies = NO_INCONSISTENCIES; Sample previous_range = -1; // Bottom range is always 0. - Sample checksum = 0; int64 count = 0; for (size_t index = 0; index < bucket_count(); ++index) { count += snapshot.counts(index); int new_range = ranges(index); - checksum += new_range; if (previous_range >= new_range) inconsistencies |= BUCKET_ORDER_ERROR; previous_range = new_range; } - if (checksum != range_checksum_) + if (!HasValidRangeChecksum()) inconsistencies |= RANGE_CHECKSUM_ERROR; int64 delta64 = snapshot.redundant_count() - count; @@ -325,6 +374,10 @@ bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, (bucket_count == bucket_count_)); } +bool Histogram::HasValidRangeChecksum() const { + return CalculateRangeChecksum() == range_checksum_; +} + Histogram::Histogram(const std::string& name, Sample minimum, Sample maximum, size_t bucket_count) : histogram_name_(name), @@ -360,11 +413,6 @@ Histogram::~Histogram() { // Just to make sure most derived class did this properly... DCHECK(ValidateBucketRanges()); - DCHECK(HasValidRangeChecksum()); -} - -bool Histogram::PrintEmptyBucket(size_t index) const { - return true; } // Calculate what range of values are held in each bucket. @@ -402,6 +450,10 @@ void Histogram::InitializeBucketRange() { DCHECK_EQ(bucket_count(), bucket_index); } +bool Histogram::PrintEmptyBucket(size_t index) const { + return true; +} + size_t Histogram::BucketIndex(Sample value) const { // Use simple binary search. This is very general, but there are better // approaches if we knew that the buckets were linearly distributed. @@ -413,7 +465,7 @@ size_t Histogram::BucketIndex(Sample value) const { do { DCHECK_GE(over, under); - mid = (over + under)/2; + mid = under + (over - under)/2; if (mid == under) break; if (ranges(mid) <= value) @@ -423,7 +475,7 @@ size_t Histogram::BucketIndex(Sample value) const { } while (true); DCHECK_LE(ranges(mid), value); - DCHECK_GT(ranges(mid+1), value); + CHECK_GT(ranges(mid+1), value); return mid; } @@ -475,6 +527,14 @@ bool Histogram::ValidateBucketRanges() const { return true; } +uint32 Histogram::CalculateRangeChecksum() const { + DCHECK_EQ(ranges_.size(), bucket_count() + 1); + uint32 checksum = static_cast<uint32>(ranges_.size()); // Seed checksum. + for (size_t index = 0; index < bucket_count(); ++index) + checksum = Crc32(checksum, ranges(index)); + return checksum; +} + void Histogram::Initialize() { sample_.Resize(*this); if (declared_min_ < 1) @@ -483,26 +543,47 @@ void Histogram::Initialize() { declared_max_ = kSampleType_MAX - 1; DCHECK_LE(declared_min_, declared_max_); DCHECK_GT(bucket_count_, 1u); + CHECK_LT(bucket_count_, kBucketCount_MAX); size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; DCHECK_LE(bucket_count_, maximal_bucket_count); DCHECK_EQ(0, ranges_[0]); ranges_[bucket_count_] = kSampleType_MAX; - InitializeBucketRange(); - DCHECK(ValidateBucketRanges()); - StatisticsRecorder::Register(this); } -bool Histogram::HasValidRangeChecksum() const { - return CalculateRangeChecksum() == range_checksum_; -} - -Histogram::Sample Histogram::CalculateRangeChecksum() const { - DCHECK_EQ(ranges_.size(), bucket_count() + 1); - Sample checksum = 0; - for (size_t index = 0; index < bucket_count(); ++index) { - checksum += ranges(index); +// We generate the CRC-32 using the low order bits to select whether to XOR in +// the reversed polynomial 0xedb88320L. This is nice and simple, and allows us +// to keep the quotient in a uint32. Since we're not concerned about the nature +// of corruptions (i.e., we don't care about bit sequencing, since we are +// handling memory changes, which are more grotesque) so we don't bother to +// get the CRC correct for big-endian vs little-ending calculations. All we +// need is a nice hash, that tends to depend on all the bits of the sample, with +// very little chance of changes in one place impacting changes in another +// place. +uint32 Histogram::Crc32(uint32 sum, Histogram::Sample range) { + const bool kUseRealCrc = true; // TODO(jar): Switch to false and watch stats. + if (kUseRealCrc) { + union { + Histogram::Sample range; + unsigned char bytes[sizeof(Histogram::Sample)]; + } converter; + converter.range = range; + for (size_t i = 0; i < sizeof(converter); ++i) + sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8); + } else { + // Use hash techniques provided in ReallyFastHash, except we don't care + // about "avalanching" (which would worsten the hash, and add collisions), + // and we don't care about edge cases since we have an even number of bytes. + union { + Histogram::Sample range; + uint16 ints[sizeof(Histogram::Sample) / 2]; + } converter; + DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter)); + converter.range = range; + sum += converter.ints[0]; + sum = (sum << 16) ^ sum ^ (static_cast<uint32>(converter.ints[1]) << 11); + sum += sum >> 11; } - return checksum; + return sum; } //------------------------------------------------------------------------------ @@ -706,8 +787,11 @@ scoped_refptr<Histogram> LinearHistogram::FactoryGet(const std::string& name, maximum = kSampleType_MAX - 1; if (!StatisticsRecorder::FindHistogram(name, &histogram)) { - histogram = new LinearHistogram(name, minimum, maximum, bucket_count); - StatisticsRecorder::FindHistogram(name, &histogram); + LinearHistogram* linear_histogram = + new LinearHistogram(name, minimum, maximum, bucket_count); + linear_histogram->InitializeBucketRange(); + histogram = linear_histogram; + StatisticsRecorder::RegisterOrDiscardDuplicate(&histogram); } DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type()); @@ -742,8 +826,6 @@ LinearHistogram::LinearHistogram(const std::string& name, Sample maximum, size_t bucket_count) : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { - InitializeBucketRange(); - DCHECK(ValidateBucketRanges()); } LinearHistogram::LinearHistogram(const std::string& name, @@ -753,9 +835,6 @@ LinearHistogram::LinearHistogram(const std::string& name, : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? minimum : TimeDelta::FromMilliseconds(1), maximum, bucket_count) { - // Do a "better" (different) job at init than a base classes did... - InitializeBucketRange(); - DCHECK(ValidateBucketRanges()); } void LinearHistogram::InitializeBucketRange() { @@ -801,8 +880,10 @@ scoped_refptr<Histogram> BooleanHistogram::FactoryGet(const std::string& name, scoped_refptr<Histogram> histogram(NULL); if (!StatisticsRecorder::FindHistogram(name, &histogram)) { - histogram = new BooleanHistogram(name); - StatisticsRecorder::FindHistogram(name, &histogram); + BooleanHistogram* boolean_histogram = new BooleanHistogram(name); + boolean_histogram->InitializeBucketRange(); + histogram = boolean_histogram; + StatisticsRecorder::RegisterOrDiscardDuplicate(&histogram); } DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type()); @@ -846,8 +927,10 @@ scoped_refptr<Histogram> CustomHistogram::FactoryGet( DCHECK_LT(ranges.back(), kSampleType_MAX); if (!StatisticsRecorder::FindHistogram(name, &histogram)) { - histogram = new CustomHistogram(name, ranges); - StatisticsRecorder::FindHistogram(name, &histogram); + CustomHistogram* custom_histogram = new CustomHistogram(name, ranges); + custom_histogram->InitializedCustomBucketRange(ranges); + histogram = custom_histogram; + StatisticsRecorder::RegisterOrDiscardDuplicate(&histogram); } DCHECK_EQ(histogram->histogram_type(), CUSTOM_HISTOGRAM); @@ -867,16 +950,15 @@ CustomHistogram::CustomHistogram(const std::string& name, custom_ranges.size()) { DCHECK_GT(custom_ranges.size(), 1u); DCHECK_EQ(custom_ranges[0], 0); - ranges_vector_ = &custom_ranges; - InitializeBucketRange(); - ranges_vector_ = NULL; - DCHECK(ValidateBucketRanges()); } -void CustomHistogram::InitializeBucketRange() { - DCHECK_LE(ranges_vector_->size(), bucket_count()); - for (size_t index = 0; index < ranges_vector_->size(); ++index) - SetBucketRange(index, (*ranges_vector_)[index]); +void CustomHistogram::InitializedCustomBucketRange( + const std::vector<Sample>& custom_ranges) { + DCHECK_GT(custom_ranges.size(), 1u); + DCHECK_EQ(custom_ranges[0], 0); + DCHECK_LE(custom_ranges.size(), bucket_count()); + for (size_t index = 0; index < custom_ranges.size(); ++index) + SetBucketRange(index, custom_ranges[index]); ResetRangeChecksum(); } @@ -941,16 +1023,21 @@ bool StatisticsRecorder::IsActive() { // was passed to us, decremented it when we returned, and the instance would be // destroyed before assignment (when value was returned by new). // static -void StatisticsRecorder::Register(Histogram* histogram) { +void StatisticsRecorder::RegisterOrDiscardDuplicate( + scoped_refptr<Histogram>* histogram) { + DCHECK((*histogram)->HasValidRangeChecksum()); if (lock_ == NULL) return; base::AutoLock auto_lock(*lock_); if (!histograms_) return; - const std::string name = histogram->histogram_name(); + const std::string name = (*histogram)->histogram_name(); + HistogramMap::iterator it = histograms_->find(name); // Avoid overwriting a previous registration. - if (histograms_->end() == histograms_->find(name)) - (*histograms_)[name] = histogram; + if (histograms_->end() == it) + (*histograms_)[name] = *histogram; + else + *histogram = it->second; } // static diff --git a/base/metrics/histogram.h b/base/metrics/histogram.h index 3bb3f03..347932a 100644 --- a/base/metrics/histogram.h +++ b/base/metrics/histogram.h @@ -241,6 +241,8 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { typedef int Sample; // Used for samples (and ranges of samples). typedef int Count; // Used to count samples in a bucket. static const Sample kSampleType_MAX = INT_MAX; + // Initialize maximum number of buckets in histograms as 16,384. + static const size_t kBucketCount_MAX; typedef std::vector<Count> Counts; typedef std::vector<Sample> Ranges; @@ -409,7 +411,7 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { Sample declared_min() const { return declared_min_; } Sample declared_max() const { return declared_max_; } virtual Sample ranges(size_t i) const; - Sample range_checksum() const { return range_checksum_; } + uint32 range_checksum() const { return range_checksum_; } virtual size_t bucket_count() const; // Snapshot the current complete set of sample data. // Override with atomic/locked snapshot if needed. @@ -421,6 +423,8 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { virtual bool HasConstructorTimeDeltaArguments(TimeDelta minimum, TimeDelta maximum, size_t bucket_count); + // Return true iff the range_checksum_ matches current ranges_ vector. + bool HasValidRangeChecksum() const; protected: friend class base::RefCountedThreadSafe<Histogram>; @@ -431,14 +435,15 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { virtual ~Histogram(); + // Initialize ranges_ mapping. + void InitializeBucketRange(); + // Method to override to skip the display of the i'th bucket if it's empty. virtual bool PrintEmptyBucket(size_t index) const; //---------------------------------------------------------------------------- // Methods to override to create histogram with different bucket widths. //---------------------------------------------------------------------------- - // Initialize ranges_ mapping. - virtual void InitializeBucketRange(); // Find bucket to increment for sample value. virtual size_t BucketIndex(Sample value) const; // Get normalized size, relative to the ranges_[i]. @@ -467,18 +472,20 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { // values relate properly to the declared_min_ and declared_max_).. bool ValidateBucketRanges() const; + virtual uint32 CalculateRangeChecksum() const; + private: // Allow tests to corrupt our innards for testing purposes. FRIEND_TEST(HistogramTest, CorruptBucketBounds); FRIEND_TEST(HistogramTest, CorruptSampleCounts); + FRIEND_TEST(HistogramTest, Crc32SampleHash); + FRIEND_TEST(HistogramTest, Crc32TableTest); // Post constructor initialization. void Initialize(); - // Return true iff the range_checksum_ matches current ranges_ vector. - bool HasValidRangeChecksum() const; - - Sample CalculateRangeChecksum() const; + // Checksum function for accumulating range values into a checksum. + static uint32 Crc32(uint32 sum, Sample range); //---------------------------------------------------------------------------- // Helpers for emitting Ascii graphic. Each method appends data to output. @@ -506,6 +513,9 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { std::string* output) const; //---------------------------------------------------------------------------- + // Table for generating Crc32 values. + static const uint32 kCrcTable[256]; + //---------------------------------------------------------------------------- // Invariant values set at/near construction time // ASCII version of original name given to the constructor. All identically @@ -524,10 +534,10 @@ class Histogram : public base::RefCountedThreadSafe<Histogram> { // The dimension of ranges_ is bucket_count + 1. Ranges ranges_; - // For redundancy, we store the sum of all the sample ranges when ranges are - // generated. If ever there is ever a difference, then the histogram must + // For redundancy, we store a checksum of all the sample ranges when ranges + // are generated. If ever there is ever a difference, then the histogram must // have been corrupted. - Sample range_checksum_; + uint32 range_checksum_; // Finally, provide the state that changes with the addition of each new // sample. @@ -567,7 +577,7 @@ class LinearHistogram : public Histogram { TimeDelta maximum, size_t bucket_count); // Initialize ranges_ mapping. - virtual void InitializeBucketRange(); + void InitializeBucketRange(); virtual double GetBucketSize(Count current, size_t i) const; // If we have a description for a bucket, then return that. Otherwise @@ -623,13 +633,9 @@ class CustomHistogram : public Histogram { const std::vector<Sample>& custom_ranges); // Initialize ranges_ mapping. - virtual void InitializeBucketRange(); + void InitializedCustomBucketRange(const std::vector<Sample>& custom_ranges); virtual double GetBucketSize(Count current, size_t i) const; - private: - // Temporary pointer used during construction/initialization, and then NULLed. - const std::vector<Sample>* ranges_vector_; - DISALLOW_COPY_AND_ASSIGN(CustomHistogram); }; @@ -649,8 +655,11 @@ class StatisticsRecorder { // Find out if histograms can now be registered into our list. static bool IsActive(); - // Register, or add a new histogram to the collection of statistics. - static void Register(Histogram* histogram); + // Register, or add a new histogram to the collection of statistics. If an + // identically named histogram is already registered, then the argument + // |histogram| will be replaced by the previously registered value, discarding + // the referenced argument. + static void RegisterOrDiscardDuplicate(scoped_refptr<Histogram>* histogram); // Methods for printing histograms. Only histograms which have query as // a substring are written to output (an empty string will process all diff --git a/base/metrics/histogram_unittest.cc b/base/metrics/histogram_unittest.cc index b9c51ad..afa69a6 100644 --- a/base/metrics/histogram_unittest.cc +++ b/base/metrics/histogram_unittest.cc @@ -348,7 +348,8 @@ TEST(HistogramTest, CorruptBucketBounds) { EXPECT_EQ(0, histogram->FindCorruption(snapshot)); // No default corruption. std::swap(histogram->ranges_[1], histogram->ranges_[2]); - EXPECT_EQ(Histogram::BUCKET_ORDER_ERROR, histogram->FindCorruption(snapshot)); + EXPECT_EQ(Histogram::BUCKET_ORDER_ERROR | Histogram::RANGE_CHECKSUM_ERROR, + histogram->FindCorruption(snapshot)); std::swap(histogram->ranges_[1], histogram->ranges_[2]); EXPECT_EQ(0, histogram->FindCorruption(snapshot)); @@ -357,8 +358,30 @@ TEST(HistogramTest, CorruptBucketBounds) { EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR, histogram->FindCorruption(snapshot)); + // Show that two simple changes don't offset each other + --histogram->ranges_[4]; + EXPECT_EQ(Histogram::RANGE_CHECKSUM_ERROR, + histogram->FindCorruption(snapshot)); + // Repair histogram so that destructor won't DCHECK(). --histogram->ranges_[3]; + ++histogram->ranges_[4]; +} + +// Table was generated similarly to sample code for CRC-32 given on: +// http://www.w3.org/TR/PNG/#D-CRCAppendix. +TEST(HistogramTest, Crc32TableTest) { + for (int i = 0; i < 256; ++i) { + uint32 checksum = i; + for (int j = 0; j < 8; ++j) { + const uint32 kReversedPolynomial = 0xedb88320L; + if (checksum & 1) + checksum = kReversedPolynomial ^ (checksum >> 1); + else + checksum >>= 1; + } + EXPECT_EQ(Histogram::kCrcTable[i], checksum); + } } } // namespace base diff --git a/base/mime_util_xdg.cc b/base/mime_util_xdg.cc index 5215d01..4dd7a3e 100644 --- a/base/mime_util_xdg.cc +++ b/base/mime_util_xdg.cc @@ -525,7 +525,7 @@ FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) { EnsureUpdated(); MimeUtilConstants* constants = MimeUtilConstants::GetInstance(); std::map<std::string, IconTheme*>* icon_themes = constants->icon_themes_; - if (icon_themes->size() == 0) + if (icon_themes->empty()) InitDefaultThemes(); FilePath icon_path; diff --git a/base/pickle.cc b/base/pickle.cc index e7d5768..116d3f1 100644 --- a/base/pickle.cc +++ b/base/pickle.cc @@ -157,6 +157,20 @@ bool Pickle::ReadSize(void** iter, size_t* result) const { return true; } +bool Pickle::ReadUInt16(void** iter, uint16* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast<char*>(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) diff --git a/base/pickle.h b/base/pickle.h index ad08b7c..9d449ae 100644 --- a/base/pickle.h +++ b/base/pickle.h @@ -68,6 +68,7 @@ class Pickle { 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; @@ -97,6 +98,9 @@ class Pickle { 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)); } @@ -143,12 +147,12 @@ class Pickle { // to the Pickle constructor. template <class T> T* headerT() { - DCHECK(sizeof(T) == header_size_); + DCHECK_EQ(header_size_, sizeof(T)); return static_cast<T*>(header_); } template <class T> const T* headerT() const { - DCHECK(sizeof(T) == header_size_); + DCHECK_EQ(header_size_, sizeof(T)); return static_cast<const T*>(header_); } diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc index 39eaa1b..51330c7 100644 --- a/base/pickle_unittest.cc +++ b/base/pickle_unittest.cc @@ -19,6 +19,7 @@ const char testdata[] = "AAA\0BBB\0"; const int testdatalen = arraysize(testdata) - 1; const bool testbool1 = false; const bool testbool2 = true; +const uint16 testuint16 = 32123; // checks that the result void VerifyResult(const Pickle& pickle) { @@ -42,6 +43,10 @@ void VerifyResult(const Pickle& pickle) { EXPECT_TRUE(pickle.ReadBool(&iter, &outbool)); EXPECT_EQ(testbool2, outbool); + uint16 outuint16; + EXPECT_TRUE(pickle.ReadUInt16(&iter, &outuint16)); + EXPECT_EQ(testuint16, outuint16); + const char* outdata; int outdatalen; EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen)); @@ -66,6 +71,7 @@ TEST(PickleTest, EncodeDecode) { EXPECT_TRUE(pickle.WriteWString(testwstr)); EXPECT_TRUE(pickle.WriteBool(testbool1)); EXPECT_TRUE(pickle.WriteBool(testbool2)); + EXPECT_TRUE(pickle.WriteUInt16(testuint16)); EXPECT_TRUE(pickle.WriteData(testdata, testdatalen)); // Over allocate BeginWriteData so we can test TrimWriteData. diff --git a/base/platform_file_posix.cc b/base/platform_file_posix.cc index 901c298..4e20e25 100644 --- a/base/platform_file_posix.cc +++ b/base/platform_file_posix.cc @@ -65,7 +65,7 @@ PlatformFile CreatePlatformFile(const FilePath& name, int flags, open_flags |= O_TRUNC; } - DCHECK(O_RDONLY == 0); + COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero); int descriptor = open(name.value().c_str(), open_flags, S_IRUSR | S_IWUSR); diff --git a/base/process_util.h b/base/process_util.h index a7f8496..29b08ca 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -162,9 +162,6 @@ void CloseProcessHandle(ProcessHandle process); ProcessId GetProcId(ProcessHandle process); #if defined(OS_LINUX) -// Returns the ID for the parent of the given process. -ProcessId GetParentProcessId(ProcessHandle process); - // Returns the path to the executable of the given process. FilePath GetProcessExecutablePath(ProcessHandle process); @@ -182,6 +179,9 @@ bool AdjustOOMScore(ProcessId process, int score); #endif #if defined(OS_POSIX) +// Returns the ID for the parent of the given process. +ProcessId GetParentProcessId(ProcessHandle process); + // Close all file descriptors, except those which are a destination in the // given multimap. Only call this function in a child process where you know // that there aren't any other threads. @@ -359,7 +359,7 @@ bool KillProcessById(ProcessId process_id, int exit_code, bool wait); // will no longer be available). TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code); -// Waits for process to exit. In POSIX systems, if the process hasn't been +// Waits for process to exit. On POSIX systems, if the process hasn't been // signaled then puts the exit code in |exit_code|; otherwise it's considered // a failure. On Windows |exit_code| is always filled. Returns true on success, // and closes |handle| in any case. @@ -382,9 +382,9 @@ bool WaitForProcessesToExit(const FilePath::StringType& executable_name, const ProcessFilter* filter); // Wait for a single process to exit. Return true if it exited cleanly within -// the given time limit. -bool WaitForSingleProcess(ProcessHandle handle, - int64 wait_milliseconds); +// the given time limit. On Linux |handle| must be a child process, however +// on Mac and Windows it can be any process. +bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds); // Returns true when |wait_milliseconds| have elapsed and the process // is still running. diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm index aa0f14d..39eabac 100644 --- a/base/process_util_mac.mm +++ b/base/process_util_mac.mm @@ -124,13 +124,13 @@ bool ProcessIterator::CheckForNextProcess() { // Find out what size buffer we need. size_t data_len = 0; if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { - LOG(ERROR) << "failed to figure out the buffer size for a commandline"; + DVPLOG(1) << "failed to figure out the buffer size for a commandline"; continue; } data.resize(data_len); if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { - LOG(ERROR) << "failed to fetch a commandline"; + DVPLOG(1) << "failed to fetch a commandline"; continue; } @@ -880,4 +880,17 @@ void EnableTerminationOnOutOfMemory() { reinterpret_cast<IMP>(oom_killer_allocWithZone)); } +ProcessId GetParentProcessId(ProcessHandle process) { + struct kinfo_proc info; + size_t length = sizeof(struct kinfo_proc); + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process }; + if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) { + PLOG(ERROR) << "sysctl"; + return -1; + } + if (length == 0) + return -1; + return info.kp_eproc.e_ppid; +} + } // namespace base diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index ce19bc6..ce56625 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -32,6 +32,7 @@ #if defined(OS_MACOSX) #include <crt_externs.h> +#include <sys/event.h> #define environ (*_NSGetEnviron()) #else extern char** environ; @@ -404,7 +405,7 @@ char** AlterEnvironment(const environment_vector& changes, } // if !found, then we have a new element to add. - if (!found && j->second.size() > 0) { + if (!found && !j->second.empty()) { count++; size += j->first.size() + 1 /* '=' */ + j->second.size() + 1 /* NUL */; } @@ -455,7 +456,7 @@ char** AlterEnvironment(const environment_vector& changes, // Now handle new elements for (environment_vector::const_iterator j = changes.begin(); j != changes.end(); j++) { - if (j->second.size() == 0) + if (j->second.empty()) continue; bool found = false; @@ -501,17 +502,20 @@ bool LaunchAppImpl( scoped_array<char*> new_environ(AlterEnvironment(env_changes, environ)); pid = fork(); - if (pid < 0) + if (pid < 0) { + PLOG(ERROR) << "fork"; return false; - + } if (pid == 0) { // Child process if (start_new_process_group) { // Instead of inheriting the process group ID of the parent, the child // starts off a new process group with pgid equal to its process ID. - if (setpgid(0, 0) < 0) + if (setpgid(0, 0) < 0) { + PLOG(ERROR) << "setpgid"; return false; + } } #if defined(OS_MACOSX) RestoreDefaultExceptionHandler(); @@ -718,7 +722,79 @@ bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, return true; } +#if defined(OS_MACOSX) +// Using kqueue on Mac so that we can wait on non-child processes. +// We can't use kqueues on child processes because we need to reap +// our own children using wait. +static bool WaitForSingleNonChildProcess(ProcessHandle handle, + int64 wait_milliseconds) { + int kq = kqueue(); + if (kq == -1) { + PLOG(ERROR) << "kqueue"; + return false; + } + + struct kevent change = { 0 }; + EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); + + struct timespec spec; + struct timespec *spec_ptr; + if (wait_milliseconds != base::kNoTimeout) { + time_t sec = static_cast<time_t>(wait_milliseconds / 1000); + wait_milliseconds = wait_milliseconds - (sec * 1000); + spec.tv_sec = sec; + spec.tv_nsec = wait_milliseconds * 1000000L; + spec_ptr = &spec; + } else { + spec_ptr = NULL; + } + + while(true) { + struct kevent event = { 0 }; + int event_count = HANDLE_EINTR(kevent(kq, &change, 1, &event, 1, spec_ptr)); + if (close(kq) != 0) { + PLOG(ERROR) << "close"; + } + if (event_count < 0) { + PLOG(ERROR) << "kevent"; + return false; + } else if (event_count == 0) { + if (wait_milliseconds != base::kNoTimeout) { + // Timed out. + return false; + } + } else if ((event_count == 1) && + (handle == static_cast<pid_t>(event.ident)) && + (event.filter == EVFILT_PROC)) { + if (event.fflags == NOTE_EXIT) { + return true; + } else if (event.flags == EV_ERROR) { + LOG(ERROR) << "kevent error " << event.data; + return false; + } else { + NOTREACHED(); + return false; + } + } else { + NOTREACHED(); + return false; + } + } +} +#endif // OS_MACOSX + bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) { + ProcessHandle parent_pid = GetParentProcessId(handle); + ProcessHandle our_pid = Process::Current().handle(); + if (parent_pid != our_pid) { +#if defined(OS_MACOSX) + // On Mac we can wait on non child processes. + return WaitForSingleNonChildProcess(handle, wait_milliseconds); +#else + // Currently on Linux we can't handle non child processes. + NOTIMPLEMENTED(); +#endif // OS_MACOSX + } bool waitpid_success; int status; if (wait_milliseconds == base::kNoTimeout) diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc index 1b7368a..31e1dec 100644 --- a/base/process_util_unittest.cc +++ b/base/process_util_unittest.cc @@ -658,12 +658,12 @@ TEST_F(ProcessUtilTest, GetAppOutputRestrictedNoZombies) { } } -#if defined(OS_LINUX) TEST_F(ProcessUtilTest, GetParentProcessId) { base::ProcessId ppid = base::GetParentProcessId(base::GetCurrentProcId()); EXPECT_EQ(ppid, getppid()); } +#if defined(OS_LINUX) TEST_F(ProcessUtilTest, ParseProcStatCPU) { // /proc/self/stat for a process running "top". const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 " @@ -683,7 +683,7 @@ TEST_F(ProcessUtilTest, ParseProcStatCPU) { EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat)); } -#endif +#endif // defined(OS_LINUX) #endif // defined(OS_POSIX) diff --git a/base/ref_counted.cc b/base/ref_counted.cc index 54c1071..2d459ae 100644 --- a/base/ref_counted.cc +++ b/base/ref_counted.cc @@ -12,7 +12,7 @@ namespace base { namespace subtle { RefCountedBase::RefCountedBase() - : counter_holder_(new CounterHolder) + : ref_count_(0) #ifndef NDEBUG , in_dtor_(false) #endif @@ -20,7 +20,6 @@ RefCountedBase::RefCountedBase() } RefCountedBase::~RefCountedBase() { - delete counter_holder_; #ifndef NDEBUG DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; #endif @@ -33,7 +32,7 @@ void RefCountedBase::AddRef() const { #ifndef NDEBUG DCHECK(!in_dtor_); #endif - ++(counter_holder_->ref_count); + ++ref_count_; } bool RefCountedBase::Release() const { @@ -43,7 +42,7 @@ bool RefCountedBase::Release() const { #ifndef NDEBUG DCHECK(!in_dtor_); #endif - if (--(counter_holder_->ref_count) == 0) { + if (--ref_count_ == 0) { #ifndef NDEBUG in_dtor_ = true; #endif @@ -54,19 +53,16 @@ bool RefCountedBase::Release() const { bool RefCountedThreadSafeBase::HasOneRef() const { return AtomicRefCountIsOne( - &const_cast<RefCountedThreadSafeBase*>(this)-> - counter_holder_->ref_count); + &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_); } -RefCountedThreadSafeBase::RefCountedThreadSafeBase() - : counter_holder_(new CounterHolder) { +RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) { #ifndef NDEBUG in_dtor_ = false; #endif } RefCountedThreadSafeBase::~RefCountedThreadSafeBase() { - delete counter_holder_; #ifndef NDEBUG DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " "calling Release()"; @@ -77,15 +73,15 @@ void RefCountedThreadSafeBase::AddRef() const { #ifndef NDEBUG DCHECK(!in_dtor_); #endif - AtomicRefCountInc(&counter_holder_->ref_count); + AtomicRefCountInc(&ref_count_); } bool RefCountedThreadSafeBase::Release() const { #ifndef NDEBUG DCHECK(!in_dtor_); - DCHECK(!AtomicRefCountIsZero(&counter_holder_->ref_count)); + DCHECK(!AtomicRefCountIsZero(&ref_count_)); #endif - if (!AtomicRefCountDec(&counter_holder_->ref_count)) { + if (!AtomicRefCountDec(&ref_count_)) { #ifndef NDEBUG in_dtor_ = true; #endif diff --git a/base/ref_counted.h b/base/ref_counted.h index 6a2b996..4c3aeb8 100644 --- a/base/ref_counted.h +++ b/base/ref_counted.h @@ -17,25 +17,19 @@ class RefCountedBase { public: static bool ImplementsThreadSafeReferenceCounting() { return false; } - bool HasOneRef() const { return counter_holder_->ref_count == 1; } + bool HasOneRef() const { return ref_count_ == 1; } protected: RefCountedBase(); ~RefCountedBase(); - struct CounterHolder { - CounterHolder() : ref_count(0), weak_count(0) {} - int ref_count; - int weak_count; // Simulates weak pointer. - }; - void AddRef() const; // Returns true if the object should self-delete. bool Release() const; private: - mutable CounterHolder* counter_holder_; + mutable int ref_count_; #ifndef NDEBUG mutable bool in_dtor_; #endif @@ -61,12 +55,7 @@ class RefCountedThreadSafeBase { bool Release() const; private: - struct CounterHolder { - CounterHolder() : ref_count(0), weak_count(0) {} - AtomicRefCount ref_count; - AtomicRefCount weak_count; // Simulates weak pointer. - }; - mutable CounterHolder* counter_holder_; + mutable AtomicRefCount ref_count_; #ifndef NDEBUG mutable bool in_dtor_; #endif diff --git a/base/shared_memory_posix.cc b/base/shared_memory_posix.cc index 4793627..bd2c6e5 100644 --- a/base/shared_memory_posix.cc +++ b/base/shared_memory_posix.cc @@ -79,7 +79,8 @@ SharedMemoryHandle SharedMemory::NULLHandle() { // static void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { DCHECK(handle.fd >= 0); - close(handle.fd); + if (HANDLE_EINTR(close(handle.fd)) < 0) + PLOG(ERROR) << "close"; } bool SharedMemory::CreateAndMapAnonymous(uint32 size) { @@ -98,7 +99,7 @@ bool SharedMemory::CreateAnonymous(uint32 size) { // of mem_filename after FilePathForMemoryName(). bool SharedMemory::CreateNamed(const std::string& name, bool open_existing, uint32 size) { - DCHECK(mapped_file_ == -1); + DCHECK_EQ(-1, mapped_file_); if (size == 0) return false; // This function theoretically can block on the disk, but realistically @@ -229,7 +230,8 @@ void SharedMemory::Close() { Unmap(); if (mapped_file_ > 0) { - close(mapped_file_); + if (HANDLE_EINTR(close(mapped_file_)) < 0) + PLOG(ERROR) << "close"; mapped_file_ = -1; } } @@ -247,7 +249,7 @@ void SharedMemory::Unlock() { } bool SharedMemory::PrepareMapFile(FILE *fp) { - DCHECK(mapped_file_ == -1); + DCHECK_EQ(-1, mapped_file_); if (fp == NULL) return false; // This function theoretically can block on the disk, but realistically @@ -282,8 +284,8 @@ bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, FilePath* path) { // mem_name will be used for a filename; make sure it doesn't // contain anything which will confuse us. - DCHECK(mem_name.find('/') == std::string::npos); - DCHECK(mem_name.find('\0') == std::string::npos); + DCHECK_EQ(std::string::npos, mem_name.find('/')); + DCHECK_EQ(std::string::npos, mem_name.find('\0')); FilePath temp_dir; if (!file_util::GetShmemTempDir(&temp_dir)) diff --git a/base/shared_memory_win.cc b/base/shared_memory_win.cc index 15c61dd..526132c 100644 --- a/base/shared_memory_win.cc +++ b/base/shared_memory_win.cc @@ -80,7 +80,7 @@ bool SharedMemory::CreateAnonymous(uint32 size) { bool SharedMemory::CreateNamed(const std::string& name, bool open_existing, uint32 size) { - DCHECK(mapped_file_ == NULL); + DCHECK(!mapped_file_); if (size == 0) return false; @@ -119,7 +119,7 @@ bool SharedMemory::Delete(const std::string& name) { } bool SharedMemory::Open(const std::string& name, bool read_only) { - DCHECK(mapped_file_ == NULL); + DCHECK(!mapped_file_); name_ = ASCIIToWide(name); read_only_ = read_only; diff --git a/base/singleton.h b/base/singleton.h index 5bd5c35..0fe5e27 100644 --- a/base/singleton.h +++ b/base/singleton.h @@ -254,7 +254,7 @@ class Singleton { // Adapter function for use with AtExit(). This should be called single // threaded, so don't use atomic operations. // Calling OnExit while singleton is in use by other threads is a mistake. - static void OnExit(void* unused) { + static void OnExit(void* /*unused*/) { // AtExit should only ever be register after the singleton instance was // created. We should only ever get here with a valid instance_ pointer. Traits::Delete( diff --git a/base/string_util.cc b/base/string_util.cc index 0e0f17b..b2d9e6c 100644 --- a/base/string_util.cc +++ b/base/string_util.cc @@ -808,7 +808,8 @@ size_t Tokenize(const base::StringPiece& str, template<typename STR> static STR JoinStringT(const std::vector<STR>& parts, typename STR::value_type sep) { - if (parts.size() == 0) return STR(); + if (parts.empty()) + return STR(); STR result(parts[0]); typename std::vector<STR>::const_iterator iter = parts.begin(); diff --git a/base/sync_socket_posix.cc b/base/sync_socket_posix.cc index c2020c2..91467b1 100644 --- a/base/sync_socket_posix.cc +++ b/base/sync_socket_posix.cc @@ -63,10 +63,14 @@ bool SyncSocket::CreatePair(SyncSocket* pair[2]) { return true; cleanup: - if (handles[0] != kInvalidHandle) - (void) close(handles[0]); - if (handles[1] != kInvalidHandle) - (void) close(handles[1]); + if (handles[0] != kInvalidHandle) { + if (HANDLE_EINTR(close(handles[0])) < 0) + PLOG(ERROR) << "close"; + } + if (handles[1] != kInvalidHandle) { + if (HANDLE_EINTR(close(handles[1])) < 0) + PLOG(ERROR) << "close"; + } delete tmp_sockets[0]; delete tmp_sockets[1]; return false; @@ -76,7 +80,9 @@ bool SyncSocket::Close() { if (handle_ == kInvalidHandle) { return false; } - int retval = close(handle_); + int retval = HANDLE_EINTR(close(handle_)); + if (retval < 0) + PLOG(ERROR) << "close"; handle_ = kInvalidHandle; return (retval == 0); } diff --git a/base/synchronization/condition_variable_posix.cc b/base/synchronization/condition_variable_posix.cc index eff7053..fc68b64 100644 --- a/base/synchronization/condition_variable_posix.cc +++ b/base/synchronization/condition_variable_posix.cc @@ -20,12 +20,12 @@ ConditionVariable::ConditionVariable(Lock* user_lock) #endif { int rv = pthread_cond_init(&condition_, NULL); - DCHECK(rv == 0); + DCHECK_EQ(0, rv); } ConditionVariable::~ConditionVariable() { int rv = pthread_cond_destroy(&condition_); - DCHECK(rv == 0); + DCHECK_EQ(0, rv); } void ConditionVariable::Wait() { @@ -33,7 +33,7 @@ void ConditionVariable::Wait() { user_lock_->CheckHeldAndUnmark(); #endif int rv = pthread_cond_wait(&condition_, user_mutex_); - DCHECK(rv == 0); + DCHECK_EQ(0, rv); #if !defined(NDEBUG) user_lock_->CheckUnheldAndMark(); #endif @@ -52,7 +52,7 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) { Time::kNanosecondsPerMicrosecond; abstime.tv_sec += abstime.tv_nsec / Time::kNanosecondsPerSecond; abstime.tv_nsec %= Time::kNanosecondsPerSecond; - DCHECK(abstime.tv_sec >= now.tv_sec); // Overflow paranoia + DCHECK_GE(abstime.tv_sec, now.tv_sec); // Overflow paranoia #if !defined(NDEBUG) user_lock_->CheckHeldAndUnmark(); @@ -66,12 +66,12 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) { void ConditionVariable::Broadcast() { int rv = pthread_cond_broadcast(&condition_); - DCHECK(rv == 0); + DCHECK_EQ(0, rv); } void ConditionVariable::Signal() { int rv = pthread_cond_signal(&condition_); - DCHECK(rv == 0); + DCHECK_EQ(0, rv); } } // namespace base diff --git a/base/synchronization/waitable_event_win.cc b/base/synchronization/waitable_event_win.cc index 0fcf488..a0e39c1 100644 --- a/base/synchronization/waitable_event_win.cc +++ b/base/synchronization/waitable_event_win.cc @@ -50,7 +50,7 @@ bool WaitableEvent::Wait() { DWORD result = WaitForSingleObject(handle_, INFINITE); // It is most unexpected that this should ever fail. Help consumers learn // about it if it should ever fail. - DCHECK(result == WAIT_OBJECT_0) << "WaitForSingleObject failed"; + DCHECK_EQ(WAIT_OBJECT_0, result) << "WaitForSingleObject failed"; return result == WAIT_OBJECT_0; } diff --git a/base/task.h b/base/task.h index fc986b2..2deee56 100644 --- a/base/task.h +++ b/base/task.h @@ -423,6 +423,21 @@ inline CancelableTask* NewRunnableMethod(T* object, Method method, e, f, g)); } +template <class T, class Method, class A, class B, class C, class D, class E, + class F, class G, class H> +inline CancelableTask* NewRunnableMethod(T* object, Method method, + 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 new RunnableMethod<T, + Method, + Tuple8<A, B, C, D, E, F, G, H> >(object, + method, + MakeTuple(a, b, c, + d, e, f, + g, h)); +} + // RunnableFunction and NewRunnableFunction implementation --------------------- template <class Function, class Params> diff --git a/base/template_util.h b/base/template_util.h index 0408fc6..e48af91 100644 --- a/base/template_util.h +++ b/base/template_util.h @@ -47,8 +47,6 @@ struct NoType { YesType dummy[2]; }; -#if !defined(OS_WIN) - // 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 @@ -58,15 +56,17 @@ struct NoType { // had called it with an argument of type From. See Alexandrescu's // _Modern C++ Design_ for more details on this sort of trick. -template <typename From, typename To> struct ConvertHelper { + template <typename To> static YesType Test(To); + + template <typename To> static NoType Test(...); + + template <typename From> static From Create(); }; -#endif // !defined(OS_WIN) - // Used to determine if a type is a struct/union/class. Inspired by Boost's // is_class type_trait implementation. struct IsClassHelper { @@ -79,19 +79,18 @@ struct IsClassHelper { } // namespace internal -#if !defined(OS_WIN) - // 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 <typename From, typename To> struct is_convertible : integral_constant<bool, - sizeof(internal::ConvertHelper<From, To>::Test( - internal::ConvertHelper<From, To>::Create())) - == sizeof(internal::YesType)> { + sizeof(internal::ConvertHelper::Test<To>( + internal::ConvertHelper::Create<From>())) == + sizeof(internal::YesType)> { }; -#endif // !defined(OS_WIN) - template <typename T> struct is_class : integral_constant<bool, diff --git a/base/template_util_unittest.cc b/base/template_util_unittest.cc index 51d4d33..ea5f0ff 100644 --- a/base/template_util_unittest.cc +++ b/base/template_util_unittest.cc @@ -37,10 +37,8 @@ TEST(TemplateUtilTest, IsNonConstReference) { EXPECT_TRUE(is_non_const_reference<int&>::value); } -#if !defined(OS_WIN) -// TODO(ajwong): Why is is_convertible disabled on windows? TEST(TemplateUtilTest, IsConvertible) { - // Extra parents needed to make EXPECT_*'s parsing happy. Otherwise, + // Extra parens needed to make EXPECT_*'s parsing happy. Otherwise, // it sees the equivalent of // // EXPECT_TRUE( (is_convertible < Child), (Parent > ::value)); @@ -48,8 +46,12 @@ TEST(TemplateUtilTest, IsConvertible) { // Silly C++. EXPECT_TRUE( (is_convertible<Child, Parent>::value) ); EXPECT_FALSE( (is_convertible<Parent, Child>::value) ); + EXPECT_FALSE( (is_convertible<Parent, AStruct>::value) ); + + EXPECT_TRUE( (is_convertible<int, double>::value) ); + EXPECT_TRUE( (is_convertible<int*, void*>::value) ); + EXPECT_FALSE( (is_convertible<void*, int*>::value) ); } -#endif // !defined(OS_WIN) TEST(TemplateUtilTest, IsClass) { EXPECT_TRUE(is_class<AStruct>::value); diff --git a/base/test/test_file_util.h b/base/test/test_file_util.h index 2de74e7..e59456a 100644 --- a/base/test/test_file_util.h +++ b/base/test/test_file_util.h @@ -8,6 +8,8 @@ // File utility functions used only by tests. +#include <string> + class FilePath; namespace file_util { @@ -35,9 +37,17 @@ bool CopyRecursiveDirNoCache(const FilePath& source_dir, bool VolumeSupportsADS(const FilePath& path); // Returns true if the ZoneIdentifier is correctly set to "Internet" (3). +// Note that this function must be called from the same process as +// the one that set the zone identifier. I.e. don't use it in UI/automation +// based tests. bool HasInternetZoneIdentifier(const FilePath& full_path); #endif // defined(OS_WIN) +// In general it's not reliable to convert a FilePath to a wstring and we use +// string16 elsewhere for Unicode strings, but in tests it is frequently +// convenient to be able to compare paths to literals like L"foobar". +std::wstring FilePathAsWString(const FilePath& path); + } // namespace file_util #endif // BASE_TEST_TEST_FILE_UTIL_H_ diff --git a/base/test/test_file_util_posix.cc b/base/test/test_file_util_posix.cc index 876e882..430e52e 100644 --- a/base/test/test_file_util_posix.cc +++ b/base/test/test_file_util_posix.cc @@ -15,6 +15,7 @@ #include "base/file_path.h" #include "base/file_util.h" #include "base/string_util.h" +#include "base/utf_string_conversions.h" namespace file_util { @@ -109,4 +110,8 @@ bool EvictFileFromSystemCache(const FilePath& file) { } #endif +std::wstring FilePathAsWString(const FilePath& path) { + return UTF8ToWide(path.value()); +} + } // namespace file_util diff --git a/base/test/test_file_util_win.cc b/base/test/test_file_util_win.cc index 7ca7e84..1f20740 100644 --- a/base/test/test_file_util_win.cc +++ b/base/test/test_file_util_win.cc @@ -194,41 +194,31 @@ bool VolumeSupportsADS(const FilePath& path) { } // Return whether the ZoneIdentifier is correctly set to "Internet" (3) +// Only returns a valid result when called from same process as the +// one that (was supposed to have) set the zone identifier. bool HasInternetZoneIdentifier(const FilePath& full_path) { - std::wstring path = full_path.value() + L":Zone.Identifier"; - - // This polling and sleeping here is a very bad pattern. But due to how - // Windows file semantics work it's really hard to do it other way. We are - // reading a file written by a different process, using a different handle. - // Windows does not guarantee that we will get the same contents even after - // the other process closes the handle, flushes the buffers, etc. - for (int i = 0; i < 20; i++) { - base::PlatformThread::Sleep(1000); - - const DWORD kShare = FILE_SHARE_READ | - FILE_SHARE_WRITE | - FILE_SHARE_DELETE; - HANDLE file = CreateFile(path.c_str(), GENERIC_READ, kShare, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (file == INVALID_HANDLE_VALUE) - continue; - - char buffer[100] = {0}; - DWORD read = 0; - BOOL read_result = ::ReadFile(file, buffer, 100, &read, NULL); - CloseHandle(file); - - if (!read_result) - continue; - - const char kIdentifier[] = "[ZoneTransfer]\nZoneId=3"; - if (read != arraysize(kIdentifier)) - continue; - - if (strcmp(kIdentifier, buffer) == 0) - return true; - } - return false; + FilePath zone_path(full_path.value() + L":Zone.Identifier"); + std::string zone_path_contents; + if (!file_util::ReadFileToString(zone_path, &zone_path_contents)) + return false; + + static const char kInternetIdentifier[] = "[ZoneTransfer]\nZoneId=3"; + static const size_t kInternetIdentifierSize = + // Don't include null byte in size of identifier. + arraysize(kInternetIdentifier) - 1; + + // Our test is that the initial characters match the above, and that + // the character after the end of the string is eof, null, or newline; any + // of those three will invoke the Window Finder cautionary dialog. + return ((zone_path_contents.compare(0, kInternetIdentifierSize, + kInternetIdentifier) == 0) && + (kInternetIdentifierSize == zone_path_contents.length() || + zone_path_contents[kInternetIdentifierSize] == '\0' || + zone_path_contents[kInternetIdentifierSize] == '\n')); +} + +std::wstring FilePathAsWString(const FilePath& path) { + return path.value(); } } // namespace file_util diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc index 447a138..42b5b90 100644 --- a/base/test/test_suite.cc +++ b/base/test/test_suite.cc @@ -49,6 +49,9 @@ class MaybeTestDisabler : public testing::EmptyTestEventListener { const char TestSuite::kStrictFailureHandling[] = "strict_failure_handling"; TestSuite::TestSuite(int argc, char** argv) { +#if defined(OS_WIN) + testing::GTEST_FLAG(catch_exceptions) = false; +#endif base::EnableTerminationOnHeapCorruption(); CommandLine::Init(argc, argv); testing::InitGoogleTest(&argc, argv); diff --git a/base/threading/non_thread_safe.cc b/base/threading/non_thread_safe.cc deleted file mode 100644 index 8b41bc0..0000000 --- a/base/threading/non_thread_safe.cc +++ /dev/null @@ -1,28 +0,0 @@ -// 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/non_thread_safe.h" - -// These checks are only done in debug builds. -#ifndef NDEBUG - -#include "base/logging.h" - -namespace base { - -NonThreadSafe::~NonThreadSafe() { - DCHECK(CalledOnValidThread()); -} - -bool NonThreadSafe::CalledOnValidThread() const { - return thread_checker_.CalledOnValidThread(); -} - -void NonThreadSafe::DetachFromThread() { - thread_checker_.DetachFromThread(); -} - -} // namespace base - -#endif // NDEBUG diff --git a/base/threading/non_thread_safe.h b/base/threading/non_thread_safe.h index 868a031..bc3be3c 100644 --- a/base/threading/non_thread_safe.h +++ b/base/threading/non_thread_safe.h @@ -6,13 +6,29 @@ #define BASE_THREADING_NON_THREAD_SAFE_H_ #pragma once -#include "base/threading/thread_checker.h" +#ifndef NDEBUG +#include "base/threading/non_thread_safe_impl.h" +#endif namespace base { -// A helper class used to help verify that methods of a class are -// called from the same thread. One can inherit from this class and use -// CalledOnValidThread() to verify. +// Do nothing implementation of NonThreadSafe, for release mode. +// +// Note: You should almost always use the NonThreadSafe class to get +// the right version of the class for your build configuration. +class NonThreadSafeDoNothing { + public: + bool CalledOnValidThread() const { + return true; + } + + protected: + void DetachFromThread() {} +}; + +// NonThreadSafe is a helper class used to help verify that methods of a +// class are called from the same thread. One can inherit from this class +// and use CalledOnValidThread() to verify. // // This is intended to be used with classes that appear to be thread safe, but // aren't. For example, a service or a singleton like the preferences system. @@ -29,33 +45,10 @@ namespace base { // In Release mode, CalledOnValidThread will always return true. // #ifndef NDEBUG -class NonThreadSafe { - public: - ~NonThreadSafe(); - - bool CalledOnValidThread() const; - - protected: - // Changes the thread that is checked for in CalledOnValidThread. The next - // call to CalledOnValidThread will attach this class to a new thread. It is - // up to the NonThreadSafe derived class to decide to expose this or not. - // This may be useful when an object may be created on one thread and then - // used exclusively on another thread. - void DetachFromThread(); - - private: - ThreadChecker thread_checker_; +class NonThreadSafe : public NonThreadSafeImpl { }; #else -// Do nothing in release mode. -class NonThreadSafe { - public: - bool CalledOnValidThread() const { - return true; - } - - protected: - void DetachFromThread() {} +class NonThreadSafe : public NonThreadSafeDoNothing { }; #endif // NDEBUG diff --git a/base/threading/non_thread_safe_impl.cc b/base/threading/non_thread_safe_impl.cc new file mode 100644 index 0000000..f9c18c2 --- /dev/null +++ b/base/threading/non_thread_safe_impl.cc @@ -0,0 +1,23 @@ +// 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/non_thread_safe_impl.h" + +#include "base/logging.h" + +namespace base { + +NonThreadSafeImpl::~NonThreadSafeImpl() { + DCHECK(CalledOnValidThread()); +} + +bool NonThreadSafeImpl::CalledOnValidThread() const { + return thread_checker_.CalledOnValidThread(); +} + +void NonThreadSafeImpl::DetachFromThread() { + thread_checker_.DetachFromThread(); +} + +} // namespace base diff --git a/base/threading/non_thread_safe_impl.h b/base/threading/non_thread_safe_impl.h new file mode 100644 index 0000000..12925f1 --- /dev/null +++ b/base/threading/non_thread_safe_impl.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 BASE_THREADING_NON_THREAD_SAFE_IMPL_H_ +#define BASE_THREADING_NON_THREAD_SAFE_IMPL_H_ +#pragma once + +#include "base/threading/thread_checker_impl.h" + +namespace base { + +// Full implementation of NonThreadSafe, for debug mode or for occasional +// temporary use in release mode e.g. when you need to CHECK on a thread +// bug that only occurs in the wild. +// +// Note: You should almost always use the NonThreadSafe class to get +// the right version of the class for your build configuration. +class NonThreadSafeImpl { + public: + ~NonThreadSafeImpl(); + + bool CalledOnValidThread() const; + + protected: + // Changes the thread that is checked for in CalledOnValidThread. The next + // call to CalledOnValidThread will attach this class to a new thread. It is + // up to the NonThreadSafe derived class to decide to expose this or not. + // This may be useful when an object may be created on one thread and then + // used exclusively on another thread. + void DetachFromThread(); + + private: + ThreadCheckerImpl thread_checker_; +}; + +} // namespace base + +#endif // BASE_THREADING_NON_THREAD_SAFE_IMPL_H_ diff --git a/base/threading/non_thread_safe_unittest.cc b/base/threading/non_thread_safe_unittest.cc index 7d158cf..b79a4f4 100644 --- a/base/threading/non_thread_safe_unittest.cc +++ b/base/threading/non_thread_safe_unittest.cc @@ -9,8 +9,6 @@ #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" -#ifndef NDEBUG - namespace base { // Simple class to exersice the basics of NonThreadSafe. @@ -22,13 +20,16 @@ class NonThreadSafeClass : public NonThreadSafe { // Verifies that it was called on the same thread as the constructor. void DoStuff() { - DCHECK(CalledOnValidThread()); + CHECK(CalledOnValidThread()); } void DetachFromThread() { NonThreadSafe::DetachFromThread(); } + static void MethodOnDifferentThreadImpl(); + static void DestructorOnDifferentThreadImpl(); + private: DISALLOW_COPY_AND_ASSIGN(NonThreadSafeClass); }; @@ -94,37 +95,57 @@ TEST(NonThreadSafeTest, DetachThenDestructOnDifferentThread) { delete_on_thread.Join(); } -#if GTEST_HAS_DEATH_TEST +#if GTEST_HAS_DEATH_TEST || NDEBUG -TEST(NonThreadSafeDeathTest, MethodNotAllowedOnDifferentThread) { - ASSERT_DEBUG_DEATH({ - scoped_ptr<NonThreadSafeClass> non_thread_safe_class( - new NonThreadSafeClass); +void NonThreadSafeClass::MethodOnDifferentThreadImpl() { + scoped_ptr<NonThreadSafeClass> non_thread_safe_class( + new NonThreadSafeClass); - // Verify that DoStuff asserts when called on a different thread. - CallDoStuffOnThread call_on_thread(non_thread_safe_class.get()); + // Verify that DoStuff asserts in debug builds only when called + // on a different thread. + CallDoStuffOnThread call_on_thread(non_thread_safe_class.get()); - call_on_thread.Start(); - call_on_thread.Join(); - }, ""); + call_on_thread.Start(); + call_on_thread.Join(); } -TEST(NonThreadSafeDeathTest, DestructorNotAllowedOnDifferentThread) { +#ifndef NDEBUG +TEST(NonThreadSafeDeathTest, MethodNotAllowedOnDifferentThreadInDebug) { ASSERT_DEBUG_DEATH({ - scoped_ptr<NonThreadSafeClass> non_thread_safe_class( - new NonThreadSafeClass); + NonThreadSafeClass::MethodOnDifferentThreadImpl(); + }, ""); +} +#else +TEST(NonThreadSafeTest, MethodAllowedOnDifferentThreadInRelease) { + NonThreadSafeClass::MethodOnDifferentThreadImpl(); +} +#endif // NDEBUG - // Verify that the destructor asserts when called on a different thread. - DeleteNonThreadSafeClassOnThread delete_on_thread( - non_thread_safe_class.release()); +void NonThreadSafeClass::DestructorOnDifferentThreadImpl() { + scoped_ptr<NonThreadSafeClass> non_thread_safe_class( + new NonThreadSafeClass); - delete_on_thread.Start(); - delete_on_thread.Join(); + // Verify that the destructor asserts in debug builds only + // when called on a different thread. + DeleteNonThreadSafeClassOnThread delete_on_thread( + non_thread_safe_class.release()); + + delete_on_thread.Start(); + delete_on_thread.Join(); +} + +#ifndef NDEBUG +TEST(NonThreadSafeDeathTest, DestructorNotAllowedOnDifferentThreadInDebug) { + ASSERT_DEBUG_DEATH({ + NonThreadSafeClass::DestructorOnDifferentThreadImpl(); }, ""); } +#else +TEST(NonThreadSafeTest, DestructorAllowedOnDifferentThreadInRelease) { + NonThreadSafeClass::DestructorOnDifferentThreadImpl(); +} +#endif // NDEBUG -#endif // GTEST_HAS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST || NDEBUG } // namespace base - -#endif // NDEBUG diff --git a/base/threading/platform_thread_posix.cc b/base/threading/platform_thread_posix.cc index 8452909..103145a 100644 --- a/base/threading/platform_thread_posix.cc +++ b/base/threading/platform_thread_posix.cc @@ -189,7 +189,7 @@ void PlatformThread::SetName(const char* name) { // Mac is implemented in platform_thread_mac.mm. #else // static -void PlatformThread::SetName(const char* name) { +void PlatformThread::SetName(const char* /*name*/) { // Leave it unimplemented. // (This should be relatively simple to implement for the BSDs; I diff --git a/base/threading/thread.cc b/base/threading/thread.cc index c0fb537..7a100ca 100644 --- a/base/threading/thread.cc +++ b/base/threading/thread.cc @@ -173,7 +173,6 @@ void Thread::ThreadMain() { message_loop_ = NULL; message_loop_proxy_ = NULL; } - CleanUpAfterMessageLoopDestruction(); thread_id_ = kInvalidThreadId; } diff --git a/base/threading/thread.h b/base/threading/thread.h index 811dd80..379615d 100644 --- a/base/threading/thread.h +++ b/base/threading/thread.h @@ -25,7 +25,6 @@ namespace base { // (1) Thread::CleanUp() // (2) MessageLoop::~MessageLoop // (3.b) MessageLoop::DestructionObserver::WillDestroyCurrentMessageLoop -// (4) Thread::CleanUpAfterMessageLoopDestruction() class Thread : PlatformThread::Delegate { public: struct Options { diff --git a/base/threading/thread_checker.h b/base/threading/thread_checker.h index 712b5b5..33b6764 100644 --- a/base/threading/thread_checker.h +++ b/base/threading/thread_checker.h @@ -7,22 +7,34 @@ #pragma once #ifndef NDEBUG -#include "base/synchronization/lock.h" -#include "base/threading/platform_thread.h" -#endif // NDEBUG +#include "base/threading/thread_checker_impl.h" +#endif namespace base { +// Do nothing implementation, for use in release mode. +// +// Note: You should almost always use the ThreadChecker class to get the +// right version for your build configuration. +class ThreadCheckerDoNothing { + public: + bool CalledOnValidThread() const { + return true; + } + + void DetachFromThread() {} +}; + // Before using this class, please consider using NonThreadSafe as it // makes it much easier to determine the nature of your class. // -// A helper class used to help verify that some methods of a class are -// called from the same thread. One can inherit from this class and use -// CalledOnValidThread() to verify. +// ThreadChecker is a helper class used to help verify that some methods of a +// class are called from the same thread. One can inherit from this class and +// use CalledOnValidThread() to verify. // -// Inheriting from class indicates that one must be careful when using the class -// with multiple threads. However, it is up to the class document to indicate -// how it can be used with threads. +// Inheriting from class indicates that one must be careful when using the +// class with multiple threads. However, it is up to the class document to +// indicate how it can be used with threads. // // Example: // class MyClass : public ThreadChecker { @@ -34,37 +46,11 @@ namespace base { // } // // In Release mode, CalledOnValidThread will always return true. -// #ifndef NDEBUG -class ThreadChecker { - public: - ThreadChecker(); - ~ThreadChecker(); - - bool CalledOnValidThread() const; - - // Changes the thread that is checked for in CalledOnValidThread. This may - // be useful when an object may be created on one thread and then used - // exclusively on another thread. - void DetachFromThread(); - - private: - void EnsureThreadIdAssigned() const; - - mutable base::Lock lock_; - // This is mutable so that CalledOnValidThread can set it. - // It's guarded by |lock_|. - mutable PlatformThreadId valid_thread_id_; +class ThreadChecker : public ThreadCheckerImpl { }; #else -// Do nothing in release mode. -class ThreadChecker { - public: - bool CalledOnValidThread() const { - return true; - } - - void DetachFromThread() {} +class ThreadChecker : public ThreadCheckerDoNothing { }; #endif // NDEBUG diff --git a/base/threading/thread_checker.cc b/base/threading/thread_checker_impl.cc index 28ba400..985433e 100644 --- a/base/threading/thread_checker.cc +++ b/base/threading/thread_checker_impl.cc @@ -1,32 +1,30 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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_checker.h" - -// This code is only done in debug builds. -#ifndef NDEBUG +#include "base/threading/thread_checker_impl.h" namespace base { -ThreadChecker::ThreadChecker() : valid_thread_id_(kInvalidThreadId) { +ThreadCheckerImpl::ThreadCheckerImpl() + : valid_thread_id_(kInvalidThreadId) { EnsureThreadIdAssigned(); } -ThreadChecker::~ThreadChecker() {} +ThreadCheckerImpl::~ThreadCheckerImpl() {} -bool ThreadChecker::CalledOnValidThread() const { +bool ThreadCheckerImpl::CalledOnValidThread() const { EnsureThreadIdAssigned(); AutoLock auto_lock(lock_); return valid_thread_id_ == PlatformThread::CurrentId(); } -void ThreadChecker::DetachFromThread() { +void ThreadCheckerImpl::DetachFromThread() { AutoLock auto_lock(lock_); valid_thread_id_ = kInvalidThreadId; } -void ThreadChecker::EnsureThreadIdAssigned() const { +void ThreadCheckerImpl::EnsureThreadIdAssigned() const { AutoLock auto_lock(lock_); if (valid_thread_id_ != kInvalidThreadId) return; @@ -34,5 +32,3 @@ void ThreadChecker::EnsureThreadIdAssigned() const { } } // namespace base - -#endif // NDEBUG diff --git a/base/threading/thread_checker_impl.h b/base/threading/thread_checker_impl.h new file mode 100644 index 0000000..6d41fdc --- /dev/null +++ b/base/threading/thread_checker_impl.h @@ -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. + +#ifndef BASE_THREADING_THREAD_CHECKER_IMPL_H_ +#define BASE_THREADING_THREAD_CHECKER_IMPL_H_ +#pragma once + +#include "base/synchronization/lock.h" +#include "base/threading/platform_thread.h" + +namespace base { + +// Real implementation of ThreadChecker, for use in debug mode, or +// for temporary use in release mode (e.g. to CHECK on a threading issue +// seen only in the wild). +// +// Note: You should almost always use the ThreadChecker class to get the +// right version for your build configuration. +class ThreadCheckerImpl { + public: + ThreadCheckerImpl(); + ~ThreadCheckerImpl(); + + bool CalledOnValidThread() const; + + // Changes the thread that is checked for in CalledOnValidThread. This may + // be useful when an object may be created on one thread and then used + // exclusively on another thread. + void DetachFromThread(); + + private: + void EnsureThreadIdAssigned() const; + + mutable base::Lock lock_; + // This is mutable so that CalledOnValidThread can set it. + // It's guarded by |lock_|. + mutable PlatformThreadId valid_thread_id_; +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_CHECKER_IMPL_H_ diff --git a/base/threading/thread_checker_unittest.cc b/base/threading/thread_checker_unittest.cc index 6ce5bf1..9a22a8e 100644 --- a/base/threading/thread_checker_unittest.cc +++ b/base/threading/thread_checker_unittest.cc @@ -9,11 +9,9 @@ #include "base/threading/simple_thread.h" #include "testing/gtest/include/gtest/gtest.h" -#ifndef NDEBUG - namespace base { -// Simple class to exersice the basics of ThreadChecker. +// Simple class to exercise the basics of ThreadChecker. // Both the destructor and DoStuff should verify that they were // called on the same thread as the constructor. class ThreadCheckerClass : public ThreadChecker { @@ -22,13 +20,16 @@ class ThreadCheckerClass : public ThreadChecker { // Verifies that it was called on the same thread as the constructor. void DoStuff() { - DCHECK(CalledOnValidThread()); + CHECK(CalledOnValidThread()); } void DetachFromThread() { ThreadChecker::DetachFromThread(); } + static void MethodOnDifferentThreadImpl(); + static void DetachThenCallFromDifferentThreadImpl(); + private: DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass); }; @@ -106,41 +107,61 @@ TEST(ThreadCheckerTest, DetachFromThread) { call_on_thread.Join(); } -#if GTEST_HAS_DEATH_TEST +#if GTEST_HAS_DEATH_TEST || NDEBUG -TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThread) { - ASSERT_DEBUG_DEATH({ - scoped_ptr<ThreadCheckerClass> thread_checker_class( - new ThreadCheckerClass); +void ThreadCheckerClass::MethodOnDifferentThreadImpl() { + scoped_ptr<ThreadCheckerClass> thread_checker_class( + new ThreadCheckerClass); - // Verify that DoStuff asserts when called on a different thread. - CallDoStuffOnThread call_on_thread(thread_checker_class.get()); + // DoStuff should assert in debug builds only when called on a + // different thread. + CallDoStuffOnThread call_on_thread(thread_checker_class.get()); - call_on_thread.Start(); - call_on_thread.Join(); - }, ""); + call_on_thread.Start(); + call_on_thread.Join(); } -TEST(ThreadCheckerDeathTest, DetachFromThread) { +#ifndef NDEBUG +TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) { ASSERT_DEBUG_DEATH({ - scoped_ptr<ThreadCheckerClass> thread_checker_class( - new ThreadCheckerClass); + ThreadCheckerClass::MethodOnDifferentThreadImpl(); + }, ""); +} +#else +TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) { + ThreadCheckerClass::MethodOnDifferentThreadImpl(); +} +#endif // NDEBUG - // Verify that DoStuff doesn't assert when called on a different thread - // after a call to DetachFromThread. - thread_checker_class->DetachFromThread(); - CallDoStuffOnThread call_on_thread(thread_checker_class.get()); +void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() { + scoped_ptr<ThreadCheckerClass> thread_checker_class( + new ThreadCheckerClass); + + // DoStuff doesn't assert when called on a different thread + // after a call to DetachFromThread. + thread_checker_class->DetachFromThread(); + CallDoStuffOnThread call_on_thread(thread_checker_class.get()); - call_on_thread.Start(); - call_on_thread.Join(); + call_on_thread.Start(); + call_on_thread.Join(); - // Verify that DoStuff asserts after moving to another thread. - thread_checker_class->DoStuff(); + // DoStuff should assert in debug builds only after moving to + // another thread. + thread_checker_class->DoStuff(); +} + +#ifndef NDEBUG +TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) { + ASSERT_DEBUG_DEATH({ + ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); }, ""); } +#else +TEST(ThreadCheckerTest, DetachFromThreadInRelease) { + ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl(); +} +#endif // NDEBUG -#endif // GTEST_HAS_DEATH_TEST +#endif // GTEST_HAS_DEATH_TEST || NDEBUG } // namespace base - -#endif // NDEBUG diff --git a/base/threading/thread_local_storage_win.cc b/base/threading/thread_local_storage_win.cc index 667d1b9..c8128f1 100644 --- a/base/threading/thread_local_storage_win.cc +++ b/base/threading/thread_local_storage_win.cc @@ -47,7 +47,7 @@ void** ThreadLocalStorage::Initialize() { TlsFree(value); } } - DCHECK(TlsGetValue(tls_key_) == NULL); + DCHECK(!TlsGetValue(tls_key_)); // Create an array to store our data. void** tls_data = new void*[kThreadLocalStorageSize]; diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc index 3748fc3..c7cb76a 100644 --- a/base/threading/thread_unittest.cc +++ b/base/threading/thread_unittest.cc @@ -57,7 +57,7 @@ class SleepInsideInitThread : public Thread { enum ThreadEvent { // Thread::Init() was called. - THREAD_EVENT_INIT, + THREAD_EVENT_INIT = 0, // The MessageLoop for the thread was deleted. THREAD_EVENT_MESSAGE_LOOP_DESTROYED, @@ -65,8 +65,8 @@ enum ThreadEvent { // Thread::CleanUp() was called. THREAD_EVENT_CLEANUP, - // Thread::CleanUpAfterMessageLoopDestruction() was called. - THREAD_EVENT_CLEANUP_AFTER_LOOP, + // Keep at end of list. + THREAD_NUM_EVENTS }; typedef std::vector<ThreadEvent> EventList; @@ -93,10 +93,6 @@ class CaptureToEventList : public Thread { event_list_->push_back(THREAD_EVENT_CLEANUP); } - virtual void CleanUpAfterMessageLoopDestruction() { - event_list_->push_back(THREAD_EVENT_CLEANUP_AFTER_LOOP); - } - private: EventList* event_list_; }; @@ -230,7 +226,6 @@ TEST_F(ThreadTest, SleepInsideInit) { // (1) Thread::CleanUp() // (2) MessageLoop::~MessageLoop() // MessageLoop::DestructionObservers called. -// (3) Thread::CleanUpAfterMessageLoopDestruction TEST_F(ThreadTest, CleanUp) { EventList captured_events; CapturingDestructionObserver loop_destruction_observer(&captured_events); @@ -252,9 +247,8 @@ TEST_F(ThreadTest, CleanUp) { } // Check the order of events during shutdown. - ASSERT_EQ(4u, captured_events.size()); + ASSERT_EQ(static_cast<size_t>(THREAD_NUM_EVENTS), captured_events.size()); EXPECT_EQ(THREAD_EVENT_INIT, captured_events[0]); EXPECT_EQ(THREAD_EVENT_CLEANUP, captured_events[1]); EXPECT_EQ(THREAD_EVENT_MESSAGE_LOOP_DESTROYED, captured_events[2]); - EXPECT_EQ(THREAD_EVENT_CLEANUP_AFTER_LOOP, captured_events[3]); } diff --git a/base/tools_sanity_unittest.cc b/base/tools_sanity_unittest.cc index 779c236..e1e41a6 100644 --- a/base/tools_sanity_unittest.cc +++ b/base/tools_sanity_unittest.cc @@ -61,16 +61,8 @@ void MakeSomeErrors(char *ptr, size_t size) { ReadUninitializedValue(ptr); ReadValueOutOfArrayBoundsLeft(ptr); ReadValueOutOfArrayBoundsRight(ptr, size); - - // We execute this function only under memory checking tools - - // Valgrind on Linux and Mac, Dr. Memory on Windows. - // Currently writing values out-of-bounds makes Dr. Memory a bit crazy when - // this code is linked with /MTd, so skip these writes on Windows. - // See http://code.google.com/p/drmemory/issues/detail?id=51 -#if !defined(OS_WIN) WriteValueOutOfArrayBoundsLeft(ptr); WriteValueOutOfArrayBoundsRight(ptr, size); -#endif } } // namespace diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index a9f81b1..d10603d 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc @@ -467,7 +467,7 @@ bool ThreadData::StartTracking(bool status) { return true; } base::AutoLock lock(list_lock_); - DCHECK(status_ == UNINITIALIZED); + DCHECK_EQ(UNINITIALIZED, status_); CHECK(tls_index_.Initialize(NULL)); status_ = ACTIVE; return true; diff --git a/base/tracked_objects.h b/base/tracked_objects.h index ed629c3..ac49225 100644 --- a/base/tracked_objects.h +++ b/base/tracked_objects.h @@ -643,13 +643,10 @@ class AutoTracking { #ifndef NDEBUG if (state_ != kRunning) return; - // Don't call these in a Release build: they just waste time. - // The following should ONLY be called when in single threaded mode. It is - // unsafe to do this cleanup if other threads are still active. - // It is also very unnecessary, so I'm only doing this in debug to satisfy - // purify (if we need to!). - ThreadData::ShutdownSingleThreadedCleanup(); - state_ = kTornDownAndStopped; + // We don't do cleanup of any sort in Release build because it is a + // complete waste of time. Since Chromium doesn't join all its thread and + // guarantee we're in a single threaded mode, we don't even do cleanup in + // debug mode, as it will generate race-checker warnings. #endif } diff --git a/base/tuple.h b/base/tuple.h index 13d8722..65d0908 100644 --- a/base/tuple.h +++ b/base/tuple.h @@ -590,6 +590,13 @@ inline void DispatchToMethod(ObjT* obj, Method method, (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g); } +template<class ObjT, class Method, class A, class B, class C, class D, class E, + class F, class G, class H> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple8<A, B, C, D, E, F, G, H>& 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 <class Function> diff --git a/base/values.cc b/base/values.cc index 9c96f26..3340f9b 100644 --- a/base/values.cc +++ b/base/values.cc @@ -4,8 +4,10 @@ #include "base/values.h" +#include "base/file_path.h" #include "base/logging.h" #include "base/string_util.h" +#include "base/sys_string_conversions.h" #include "base/utf_string_conversions.h" namespace { @@ -95,6 +97,19 @@ StringValue* Value::CreateStringValue(const string16& in_value) { } // static +StringValue* Value::CreateFilePathValue(const FilePath& in_value) { +#if defined(OS_POSIX) + // Value::SetString only knows about UTF8 strings, so convert the path from + // the system native value to UTF8. + std::string path_utf8 = WideToUTF8(base::SysNativeMBToWide(in_value.value())); + return new StringValue(path_utf8); +#else + return new StringValue(in_value.value()); +#endif + +} + +// static BinaryValue* Value::CreateBinaryValue(char* buffer, size_t size) { return BinaryValue::Create(buffer, size); } @@ -119,6 +134,10 @@ bool Value::GetAsString(string16* out_value) const { return false; } +bool Value::GetAsFilePath(FilePath* out_value) const { + return false; +} + bool Value::GetAsList(ListValue** out_value) { return false; } @@ -250,6 +269,20 @@ bool StringValue::GetAsString(string16* out_value) const { return true; } +bool StringValue::GetAsFilePath(FilePath* out_value) const { + if (out_value) { + FilePath::StringType result; +#if defined(OS_POSIX) + // We store filepaths as UTF8, so convert it back to the system type. + result = base::SysWideToNativeMB(UTF8ToWide(value_)); +#elif defined(OS_WIN) + result = UTF8ToUTF16(value_); +#endif + *out_value = FilePath(result); + } + return true; +} + StringValue* StringValue::DeepCopy() const { return CreateStringValue(value_); } diff --git a/base/values.h b/base/values.h index 408ca8b..5814a9f 100644 --- a/base/values.h +++ b/base/values.h @@ -31,12 +31,13 @@ #include "base/string16.h" #include "build/build_config.h" -class Value; -class FundamentalValue; -class StringValue; class BinaryValue; class DictionaryValue; +class FilePath; +class FundamentalValue; class ListValue; +class StringValue; +class Value; typedef std::vector<Value*> ValueVector; typedef std::map<std::string, Value*> ValueMap; @@ -68,6 +69,7 @@ class Value { static FundamentalValue* CreateDoubleValue(double in_value); static StringValue* CreateStringValue(const std::string& in_value); static StringValue* CreateStringValue(const string16& in_value); + static StringValue* CreateFilePathValue(const FilePath& in_value); // This one can return NULL if the input isn't valid. If the return value // is non-null, the new object has taken ownership of the buffer pointer. @@ -92,6 +94,7 @@ class Value { virtual bool GetAsDouble(double* out_value) const; virtual bool GetAsString(std::string* out_value) const; virtual bool GetAsString(string16* out_value) const; + virtual bool GetAsFilePath(FilePath* out_value) const; virtual bool GetAsList(ListValue** out_value); // This creates a deep copy of the entire Value tree, and returns a pointer @@ -159,6 +162,7 @@ class StringValue : public Value { // Subclassed methods virtual bool GetAsString(std::string* out_value) const; virtual bool GetAsString(string16* out_value) const; + virtual bool GetAsFilePath(FilePath* out_value) const; virtual StringValue* DeepCopy() const; virtual bool Equals(const Value* other) const; diff --git a/base/win/scoped_bstr.cc b/base/win/scoped_bstr.cc index 18ffe6a..63ade0c 100644 --- a/base/win/scoped_bstr.cc +++ b/base/win/scoped_bstr.cc @@ -39,7 +39,7 @@ void ScopedBstr::Swap(ScopedBstr& bstr2) { } BSTR* ScopedBstr::Receive() { - DCHECK(bstr_ == NULL) << "BSTR leak."; + DCHECK(!bstr_) << "BSTR leak."; return &bstr_; } diff --git a/base/win/scoped_comptr.h b/base/win/scoped_comptr.h index 6375218..e508ef2 100644 --- a/base/win/scoped_comptr.h +++ b/base/win/scoped_comptr.h @@ -69,7 +69,7 @@ class ScopedComPtr : public scoped_refptr<Interface> { // Accepts an interface pointer that has already been addref-ed. void Attach(Interface* p) { - DCHECK(ptr_ == NULL); + DCHECK(!ptr_); ptr_ = p; } @@ -78,7 +78,7 @@ class ScopedComPtr : public scoped_refptr<Interface> { // The function DCHECKs on the current value being NULL. // Usage: Foo(p.Receive()); Interface** Receive() { - DCHECK(ptr_ == NULL) << "Object leak. Pointer must be NULL"; + DCHECK(!ptr_) << "Object leak. Pointer must be NULL"; return &ptr_; } @@ -114,7 +114,7 @@ class ScopedComPtr : public scoped_refptr<Interface> { // Convenience wrapper around CoCreateInstance HRESULT CreateInstance(const CLSID& clsid, IUnknown* outer = NULL, DWORD context = CLSCTX_ALL) { - DCHECK(ptr_ == NULL); + DCHECK(!ptr_); HRESULT hr = ::CoCreateInstance(clsid, outer, context, *interface_id, reinterpret_cast<void**>(&ptr_)); return hr; diff --git a/base/win/scoped_variant.cc b/base/win/scoped_variant.cc index 40ccdf2..f57ab93 100644 --- a/base/win/scoped_variant.cc +++ b/base/win/scoped_variant.cc @@ -211,7 +211,7 @@ void ScopedVariant::Set(SAFEARRAY* array) { var_.vt |= VT_ARRAY; var_.parray = array; } else { - DCHECK(array == NULL) << "Unable to determine safearray vartype"; + DCHECK(!array) << "Unable to determine safearray vartype"; var_.vt = VT_EMPTY; } } diff --git a/base/win/win_util.cc b/base/win/win_util.cc index d3d74a2..76d8c5d 100644 --- a/base/win/win_util.cc +++ b/base/win/win_util.cc @@ -137,6 +137,13 @@ bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) { return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS); } +bool ReadCommandFromAutoRun(HKEY root_key, + const string16& name, + string16* command) { + base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE); + return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS); +} + } // namespace win } // namespace base diff --git a/base/win/win_util.h b/base/win/win_util.h index 187e42d..c9bdce8 100644 --- a/base/win/win_util.h +++ b/base/win/win_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -55,7 +55,7 @@ bool IsAltPressed(); // NOTE: The EnableLUA registry flag, which is ignored on Windows XP // machines, might still exist and be set to 0 (UAC disabled), in which case // this function will return false. You should therefore check this flag only -// if the OS is Vista. +// if the OS is Vista or later. bool UserAccountControlIsEnabled(); // Sets the application id in given IPropertyStore. The function is intended @@ -72,6 +72,11 @@ bool AddCommandToAutoRun(HKEY root_key, const string16& name, // could be HKCU or HKLM or the root of any user hive. bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name); +// Reads the command specified by |name| from the AutoRun key. |root_key| +// could be HKCU or HKLM or the root of any user hive. Used for unit-tests. +bool ReadCommandFromAutoRun(HKEY root_key, + const string16& name, + string16* command); } // namespace win } // namespace base diff --git a/base/win/windows_version.cc b/base/win/windows_version.cc index a80688c..d53148d 100644 --- a/base/win/windows_version.cc +++ b/base/win/windows_version.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -68,5 +68,34 @@ void GetServicePackLevel(int* major, int* minor) { *minor = service_pack_minor; } +WindowsArchitecture GetWindowsArchitecture() { + SYSTEM_INFO system_info; + GetNativeSystemInfo(&system_info); + switch (system_info.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_INTEL: return X86_ARCHITECTURE; + case PROCESSOR_ARCHITECTURE_AMD64: return X64_ARCHITECTURE; + case PROCESSOR_ARCHITECTURE_IA64: return IA64_ARCHITECTURE; + default: return OTHER_ARCHITECTURE; + } +} + +WOW64Status GetWOW64Status() { + static WOW64Status wow64_status = + GetWOW64StatusForProcess(GetCurrentProcess()); + return wow64_status; +} + +WOW64Status GetWOW64StatusForProcess(HANDLE process_handle) { + typedef BOOL (WINAPI* IsWow64ProcessFunc)(HANDLE, PBOOL); + IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>( + GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process")); + if (!is_wow64_process) + return WOW64_DISABLED; + BOOL is_wow64 = FALSE; + if (!(*is_wow64_process)(process_handle, &is_wow64)) + return WOW64_UNKNOWN; + return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED; +} + } // namespace win } // namespace base diff --git a/base/win/windows_version.h b/base/win/windows_version.h index 7e281a6..df3cc35 100644 --- a/base/win/windows_version.h +++ b/base/win/windows_version.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -6,6 +6,8 @@ #define BASE_WIN_WINDOWS_VERSION_H_ #pragma once +typedef void* HANDLE; + namespace base { namespace win { @@ -13,10 +15,10 @@ namespace win { // "if (GetWinVersion() > WINVERSION_2000) ...". It's OK to change the values, // though. enum Version { - VERSION_PRE_2000 = 0, // Not supported - VERSION_2000 = 1, // Not supported + VERSION_PRE_2000 = 0, // Not supported + VERSION_2000 = 1, // Not supported VERSION_XP = 2, - VERSION_SERVER_2003 = 3, + VERSION_SERVER_2003 = 3, // Also includes Windows XP Professional x64 edition VERSION_VISTA = 4, VERSION_2008 = 5, VERSION_WIN7 = 6, @@ -28,6 +30,37 @@ Version GetVersion(); // Returns the major and minor version of the service pack installed. void GetServicePackLevel(int* major, int* minor); +enum WindowsArchitecture { + X86_ARCHITECTURE, + X64_ARCHITECTURE, + IA64_ARCHITECTURE, + OTHER_ARCHITECTURE, +}; + +// Returns the processor architecture this copy of Windows natively uses. +// For example, given an x64-capable processor, we have three possibilities: +// 32-bit Chrome running on 32-bit Windows: X86_ARCHITECTURE +// 32-bit Chrome running on 64-bit Windows via WOW64: X64_ARCHITECTURE +// 64-bit Chrome running on 64-bit Windows: X64_ARCHITECTURE +WindowsArchitecture GetWindowsArchitecture(); + +enum WOW64Status { + WOW64_DISABLED, + WOW64_ENABLED, + WOW64_UNKNOWN, +}; + +// Returns whether this process is running under WOW64 (the wrapper that allows +// 32-bit processes to run on 64-bit versions of Windows). This will return +// WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit Chrome +// on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g. the +// process does not have sufficient access rights to determine this. +WOW64Status GetWOW64Status(); + +// Like GetWOW64Status(), but for the supplied handle instead of the current +// process. +WOW64Status GetWOW64StatusForProcess(HANDLE process_handle); + } // namespace win } // namespace base |