diff options
Diffstat (limited to 'base')
155 files changed, 8039 insertions, 3077 deletions
diff --git a/base/allocator/allocator.gyp b/base/allocator/allocator.gyp index 64b1159..87b4127 100644 --- a/base/allocator/allocator.gyp +++ b/base/allocator/allocator.gyp @@ -248,7 +248,7 @@ ], 'msvs_settings': { # TODO(sgk): merge this with build/common.gypi settings - 'VCLibrarianTool=': { + 'VCLibrarianTool': { 'AdditionalOptions': ['/ignore:4006,4221'], 'AdditionalLibraryDirectories': ['<(DEPTH)/third_party/platformsdk_win7/files/Lib'], diff --git a/base/allocator/allocator_unittests.cc b/base/allocator/allocator_unittests.cc index b1aa9cb..d935cf9 100644 --- a/base/allocator/allocator_unittests.cc +++ b/base/allocator/allocator_unittests.cc @@ -313,9 +313,9 @@ static void TestOneNewWithoutExceptions(void* (*func)(size_t), try { void* rv = (*func)(kTooBig); EXPECT_EQ(NULL, rv); - EXPECT_EQ(false, should_throw) << "allocation should have thrown."; + EXPECT_FALSE(should_throw) << "allocation should have thrown."; } catch(...) { - EXPECT_EQ(true, should_throw) << "allocation threw unexpected exception."; + EXPECT_TRUE(should_throw) << "allocation threw unexpected exception."; } } @@ -359,7 +359,7 @@ TEST(Allocators, Malloc) { unsigned char* ptr = reinterpret_cast<unsigned char*>(malloc(size)); CheckAlignment(ptr, 2); // Should be 2 byte aligned Fill(ptr, size); - EXPECT_EQ(true, Valid(ptr, size)); + EXPECT_TRUE(Valid(ptr, size)); free(ptr); } } @@ -420,9 +420,9 @@ TEST(Allocators, Realloc2) { Fill(src, src_size); unsigned char* dst = reinterpret_cast<unsigned char*>(realloc(src, dst_size)); - EXPECT_EQ(true, Valid(dst, min(src_size, dst_size))); + EXPECT_TRUE(Valid(dst, min(src_size, dst_size))); Fill(dst, dst_size); - EXPECT_EQ(true, Valid(dst, dst_size)); + EXPECT_TRUE(Valid(dst, dst_size)); if (dst != NULL) free(dst); } } @@ -468,13 +468,13 @@ TEST(Allocators, Recalloc) { for (int dst_size = 0; dst_size >= 0; dst_size = NextSize(dst_size)) { unsigned char* src = reinterpret_cast<unsigned char*>(_recalloc(NULL, 1, src_size)); - EXPECT_EQ(true, IsZeroed(src, src_size)); + EXPECT_TRUE(IsZeroed(src, src_size)); Fill(src, src_size); unsigned char* dst = reinterpret_cast<unsigned char*>(_recalloc(src, 1, dst_size)); - EXPECT_EQ(true, Valid(dst, min(src_size, dst_size))); + EXPECT_TRUE(Valid(dst, min(src_size, dst_size))); Fill(dst, dst_size); - EXPECT_EQ(true, Valid(dst, dst_size)); + EXPECT_TRUE(Valid(dst, dst_size)); if (dst != NULL) free(dst); } diff --git a/base/at_exit.cc b/base/at_exit.cc index 9fdfd77..e6618a0 100644 --- a/base/at_exit.cc +++ b/base/at_exit.cc @@ -18,11 +18,6 @@ AtExitManager::AtExitManager() : next_manager_(NULL) { g_top_manager = this; } -AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) { - DCHECK(shadow || !g_top_manager); - g_top_manager = this; -} - AtExitManager::~AtExitManager() { if (!g_top_manager) { NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager"; @@ -64,4 +59,9 @@ void AtExitManager::ProcessCallbacksNow() { } } +AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) { + DCHECK(shadow || !g_top_manager); + g_top_manager = this; +} + } // namespace base diff --git a/base/at_exit.h b/base/at_exit.h index 35c96b9..15dcfc8 100644 --- a/base/at_exit.h +++ b/base/at_exit.h @@ -9,7 +9,7 @@ #include <stack> #include "base/basictypes.h" -#include "base/lock.h" +#include "base/synchronization/lock.h" namespace base { @@ -60,7 +60,7 @@ class AtExitManager { void* param_; }; - Lock lock_; + base::Lock lock_; std::stack<CallbackAndParam> stack_; AtExitManager* next_manager_; // Stack of managers to allow shadowing. diff --git a/base/atomicops_internals_x86_macosx.h b/base/atomicops_internals_x86_macosx.h index 5de7df3..29e58e3 100644 --- a/base/atomicops_internals_x86_macosx.h +++ b/base/atomicops_internals_x86_macosx.h @@ -110,7 +110,7 @@ inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, Atomic64 prev_value; do { if (OSAtomicCompareAndSwap64(old_value, new_value, - const_cast<Atomic64*>(ptr))) { + reinterpret_cast<volatile int64_t*>(ptr))) { return old_value; } prev_value = *ptr; @@ -124,18 +124,19 @@ inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, do { old_value = *ptr; } while (!OSAtomicCompareAndSwap64(old_value, new_value, - const_cast<Atomic64*>(ptr))); + reinterpret_cast<volatile int64_t*>(ptr))); return old_value; } inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr, Atomic64 increment) { - return OSAtomicAdd64(increment, const_cast<Atomic64*>(ptr)); + return OSAtomicAdd64(increment, reinterpret_cast<volatile int64_t*>(ptr)); } inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr, Atomic64 increment) { - return OSAtomicAdd64Barrier(increment, const_cast<Atomic64*>(ptr)); + return OSAtomicAdd64Barrier(increment, + reinterpret_cast<volatile int64_t*>(ptr)); } inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, @@ -143,8 +144,8 @@ inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, Atomic64 new_value) { Atomic64 prev_value; do { - if (OSAtomicCompareAndSwap64Barrier(old_value, new_value, - const_cast<Atomic64*>(ptr))) { + if (OSAtomicCompareAndSwap64Barrier( + old_value, new_value, reinterpret_cast<volatile int64_t*>(ptr))) { return old_value; } prev_value = *ptr; @@ -193,12 +194,11 @@ inline Atomic64 Release_Load(volatile const Atomic64 *ptr) { // MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different // on the Mac, even when they are the same size. We need to explicitly cast -// from AtomicWord to Atomic32/64 to implement the AtomicWord interface. -#ifdef __LP64__ -#define AtomicWordCastType Atomic64 -#else +// from AtomicWord to Atomic32 to implement the AtomicWord interface. +// When in 64-bit mode, AtomicWord is the same as Atomic64, so we need not +// add duplicate definitions. +#ifndef __LP64__ #define AtomicWordCastType Atomic32 -#endif inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr, AtomicWord old_value, @@ -273,6 +273,7 @@ inline AtomicWord Release_Load(volatile const AtomicWord* ptr) { } #undef AtomicWordCastType +#endif } // namespace base::subtle } // namespace base diff --git a/base/base.gyp b/base/base.gyp index 6dc450c..868569e 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -31,6 +31,8 @@ 'base', ], 'sources': [ + 'i18n/bidi_line_iterator.cc', + 'i18n/bidi_line_iterator.h', 'i18n/break_iterator.cc', 'i18n/break_iterator.h', 'i18n/char_iterator.cc', @@ -63,12 +65,14 @@ 'at_exit_unittest.cc', 'atomicops_unittest.cc', 'base64_unittest.cc', + 'bind_unittest.cc', 'bits_unittest.cc', 'callback_unittest.cc', 'command_line_unittest.cc', 'crypto/encryptor_unittest.cc', 'crypto/rsa_private_key_unittest.cc', 'crypto/rsa_private_key_nss_unittest.cc', + 'crypto/secure_hash_unittest.cc', 'crypto/signature_creator_unittest.cc', 'crypto/signature_verifier_unittest.cc', 'crypto/symmetric_key_unittest.cc', @@ -139,6 +143,7 @@ 'sys_string_conversions_unittest.cc', 'task_queue_unittest.cc', 'task_unittest.cc', + 'template_util_unittest.cc', 'threading/non_thread_safe_unittest.cc', 'threading/platform_thread_unittest.cc', 'threading/simple_thread_unittest.cc', diff --git a/base/base.gypi b/base/base.gypi index f241967..7a5da2e 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -36,9 +36,14 @@ 'base_switches.cc', 'base_switches.h', 'basictypes.h', + 'bind.h', + 'bind_helpers.h', + 'bind_internal.h', 'bits.h', 'bzip2_error_handler.cc', 'callback.h', + 'callback_helpers.h', + 'callback_old.h', 'command_line.cc', 'command_line.h', 'compiler_specific.h', @@ -52,6 +57,8 @@ 'debug/debugger_win.cc', 'debug/leak_annotations.h', 'debug/leak_tracker.h', + 'debug/profiler.cc', + 'debug/profiler.h', 'debug/stack_trace.cc', 'debug/stack_trace.h', 'debug/stack_trace_posix.cc', @@ -98,11 +105,12 @@ 'lazy_instance.h', 'linked_list.h', 'linked_ptr.h', - 'lock.h', 'logging.cc', 'logging.h', 'logging_win.cc', 'mac/cocoa_protocols.h', + 'mac/foundation_util.h', + 'mac/foundation_util.mm', 'mac/mac_util.h', 'mac/mac_util.mm', 'mac/os_crash_dumps.cc', @@ -287,8 +295,6 @@ 'utf_string_conversions.h', 'values.cc', 'values.h', - 'values_util.cc', - 'values_util.h', 'version.cc', 'version.h', 'vlog.cc', @@ -526,6 +532,7 @@ 'sources!': [ 'crypto/encryptor_nss.cc', 'crypto/rsa_private_key_nss.cc', + 'crypto/secure_hash_default.cc', 'crypto/signature_creator_nss.cc', 'crypto/signature_verifier_nss.cc', 'crypto/symmetric_key_nss.cc', @@ -545,6 +552,7 @@ 'sources!': [ 'crypto/encryptor_openssl.cc', 'crypto/rsa_private_key_openssl.cc', + 'crypto/secure_hash_openssl.cc', 'crypto/signature_creator_openssl.cc', 'crypto/signature_verifier_openssl.cc', 'crypto/symmetric_key_openssl.cc', @@ -558,6 +566,7 @@ 'sources': [ 'crypto/capi_util.cc', 'crypto/capi_util.h', + 'crypto/crypto_module_blocking_password_delegate.h', 'crypto/cssm_init.cc', 'crypto/cssm_init.h', 'crypto/encryptor.h', @@ -571,6 +580,9 @@ 'crypto/rsa_private_key_nss.cc', 'crypto/rsa_private_key_openssl.cc', 'crypto/rsa_private_key_win.cc', + 'crypto/secure_hash.h', + 'crypto/secure_hash_default.cc', + 'crypto/secure_hash_openssl.cc', 'crypto/signature_creator.h', 'crypto/signature_creator_mac.cc', 'crypto/signature_creator_nss.cc', diff --git a/base/base_paths_linux.cc b/base/base_paths_linux.cc index 48db3f8..ca8c757 100644 --- a/base/base_paths_linux.cc +++ b/base/base_paths_linux.cc @@ -83,10 +83,10 @@ bool PathProviderPosix(int key, FilePath* result) { } } // In a case of WebKit-only checkout, executable files are put into - // WebKit/out/{Debug|Release}, and we should return WebKit/WebKit/chromium - // for DIR_SOURCE_ROOT. + // <root of checkout>/out/{Debug|Release}, and we should return + // <root of checkout>/Source/WebKit/chromium for DIR_SOURCE_ROOT. if (PathService::Get(base::DIR_EXE, &path)) { - path = path.DirName().DirName().Append("WebKit/chromium"); + path = path.DirName().DirName().Append("Source/WebKit/chromium"); if (file_util::PathExists(path.Append(kThisSourceFile))) { *result = path; return true; diff --git a/base/base_switches.cc b/base/base_switches.cc index a1d688a..78f55a7 100644 --- a/base/base_switches.cc +++ b/base/base_switches.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. @@ -24,8 +24,8 @@ const char kFullMemoryCrashReport[] = "full-memory-crash-report"; // Suppresses all error dialogs when present. const char kNoErrorDialogs[] = "noerrdialogs"; -// Disable app::win::MessageBox. This is useful when running as part of -// scripts that do not have a user interface. +// Disable ui::MessageBox. This is useful when running as part of scripts that +// do not have a user interface. const char kNoMessageBox[] = "no-message-box"; // When running certain tests that spawn child processes, this switch indicates diff --git a/base/bind.h b/base/bind.h new file mode 100644 index 0000000..c23af2e --- /dev/null +++ b/base/bind.h @@ -0,0 +1,99 @@ +// This file was GENERATED by command: +// pump.py bind.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. + +#ifndef BASE_BIND_H_ +#define BASE_BIND_H_ +#pragma once + +#include "base/bind_internal.h" +#include "base/callback_helpers.h" + +// See base/callback.h for how to use these functions. +// +// IMPLEMENTATION NOTE +// Though Bind()'s result is meant to be stored in a Callback<> type, it +// cannot actually return the exact type without requiring a large amount +// of extra template specializations. The problem is that in order to +// discern the correct specialization of Callback<>, Bind would need to +// unwrap the function signature to determine the signature's arity, and +// whether or not it is a method. +// +// Each unique combination of (arity, function_type, num_prebound) where +// function_type is one of {function, method, const_method} would require +// one specialization. We eventually have to do a similar number of +// specializations anyways in the implementation (see the FunctionTraitsN, +// classes). However, it is avoidable in Bind if we return the result +// via an indirection like we do below. + +namespace base { + +template <typename Sig> +internal::InvokerStorageHolder<internal::InvokerStorage0<Sig> > +Bind(Sig f) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage0<Sig>(f)); +} + +template <typename Sig, typename P1> +internal::InvokerStorageHolder<internal::InvokerStorage1<Sig,P1> > +Bind(Sig f, const P1& p1) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage1<Sig, P1>( + f, p1)); +} + +template <typename Sig, typename P1, typename P2> +internal::InvokerStorageHolder<internal::InvokerStorage2<Sig,P1, P2> > +Bind(Sig f, const P1& p1, const P2& p2) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage2<Sig, P1, P2>( + f, p1, p2)); +} + +template <typename Sig, typename P1, typename P2, typename P3> +internal::InvokerStorageHolder<internal::InvokerStorage3<Sig,P1, P2, P3> > +Bind(Sig f, const P1& p1, const P2& p2, const P3& p3) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage3<Sig, P1, P2, P3>( + f, p1, p2, p3)); +} + +template <typename Sig, typename P1, typename P2, typename P3, typename P4> +internal::InvokerStorageHolder<internal::InvokerStorage4<Sig,P1, P2, P3, P4> > +Bind(Sig f, const P1& p1, const P2& p2, const P3& p3, const P4& p4) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage4<Sig, P1, P2, P3, P4>( + f, p1, p2, p3, p4)); +} + +template <typename Sig, typename P1, typename P2, typename P3, typename P4, + typename P5> +internal::InvokerStorageHolder<internal::InvokerStorage5<Sig,P1, P2, P3, P4, + P5> > +Bind(Sig f, const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage5<Sig, P1, P2, P3, P4, P5>( + f, p1, p2, p3, p4, p5)); +} + +template <typename Sig, typename P1, typename P2, typename P3, typename P4, + typename P5, typename P6> +internal::InvokerStorageHolder<internal::InvokerStorage6<Sig,P1, P2, P3, P4, + P5, P6> > +Bind(Sig f, const P1& p1, const P2& p2, const P3& p3, const P4& p4, + const P5& p5, const P6& p6) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage6<Sig, P1, P2, P3, P4, P5, P6>( + f, p1, p2, p3, p4, p5, p6)); +} + +} // namespace base + +#endif // BASE_BIND_H_ diff --git a/base/bind.h.pump b/base/bind.h.pump new file mode 100644 index 0000000..fc7f246 --- /dev/null +++ b/base/bind.h.pump @@ -0,0 +1,71 @@ +$$ 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. + +#ifndef BASE_BIND_H_ +#define BASE_BIND_H_ +#pragma once + +#include "base/bind_internal.h" +#include "base/callback_helpers.h" + +// See base/callback.h for how to use these functions. +// +// IMPLEMENTATION NOTE +// Though Bind()'s result is meant to be stored in a Callback<> type, it +// cannot actually return the exact type without requiring a large amount +// of extra template specializations. The problem is that in order to +// discern the correct specialization of Callback<>, Bind would need to +// unwrap the function signature to determine the signature's arity, and +// whether or not it is a method. +// +// Each unique combination of (arity, function_type, num_prebound) where +// function_type is one of {function, method, const_method} would require +// one specialization. We eventually have to do a similar number of +// specializations anyways in the implementation (see the FunctionTraitsN, +// classes). However, it is avoidable in Bind if we return the result +// via an indirection like we do below. + +namespace base { + +$range BOUND 0..MAX_ARITY +$for BOUND [[ +$range BOUND_ARG 1..BOUND + +$if BOUND == 0 [[ + +template <typename Sig> +internal::InvokerStorageHolder<internal::InvokerStorage0<Sig> > +Bind(Sig f) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage0<Sig>(f)); +} + +]] $else [[ + +template <typename Sig, $for BOUND_ARG , [[typename P$(BOUND_ARG)]]> +internal::InvokerStorageHolder<internal::InvokerStorage$(BOUND)<Sig, +$for BOUND_ARG , [[P$(BOUND_ARG)]]> > +Bind(Sig f, $for BOUND_ARG , [[const P$(BOUND_ARG)& p$(BOUND_ARG)]]) { + return internal::MakeInvokerStorageHolder( + new internal::InvokerStorage$(BOUND)<Sig, [[]] +$for BOUND_ARG , [[P$(BOUND_ARG)]]>( + f, $for BOUND_ARG , [[p$(BOUND_ARG)]])); +} + +]] + +]] $$ for BOUND + +} // namespace base + +#endif // BASE_BIND_H_ diff --git a/base/bind_helpers.h b/base/bind_helpers.h new file mode 100644 index 0000000..c1ca3d7 --- /dev/null +++ b/base/bind_helpers.h @@ -0,0 +1,287 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This defines a set of argument wrappers and related factory methods that +// can be used specify the refcounting and reference semantics of arguments +// that are bound by the Bind() function in base/bind.h. +// +// The public functions are base::Unretained() and base::ConstRef(). +// Unretained() allows Bind() to bind a non-refcounted class. +// ConstRef() allows binding a constant reference to an argument rather +// than a copy. +// +// +// EXAMPLE OF Unretained(): +// +// class Foo { +// public: +// void func() { cout << "Foo:f" << endl; +// }; +// +// // In some function somewhere. +// Foo foo; +// Callback<void(void)> foo_callback = +// Bind(&Foo::func, Unretained(&foo)); +// foo_callback.Run(); // Prints "Foo:f". +// +// Without the Unretained() wrapper on |&foo|, the above call would fail +// to compile because Foo does not support the AddRef() and Release() methods. +// +// +// EXAMPLE OF ConstRef(); +// void foo(int arg) { cout << arg << endl } +// +// int n = 1; +// Callback<void(void)> no_ref = Bind(&foo, n); +// Callback<void(void)> has_ref = Bind(&foo, ConstRef(n)); +// +// no_ref.Run(); // Prints "1" +// has_ref.Run(); // Prints "1" +// +// n = 2; +// no_ref.Run(); // Prints "1" +// has_ref.Run(); // Prints "2" +// +// Note that because ConstRef() takes a reference on |n|, |n| must outlive all +// its bound callbacks. +// + +#ifndef BASE_BIND_HELPERS_H_ +#define BASE_BIND_HELPERS_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/template_util.h" + +namespace base { +namespace internal { + +// Use the Substitution Failure Is Not An Error (SFINAE) trick to inspect T +// for the existence of AddRef() and Release() functions of the correct +// signature. +// +// http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error +// http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence +// http://stackoverflow.com/questions/4358584/sfinae-approach-comparison +// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions +// +// The last link in particular show the method used below. +// +// For SFINAE to work with inherited methods, we need to pull some extra tricks +// with multiple inheritance. In the more standard formulation, the overloads +// of Check would be: +// +// template <typename C> +// Yes NotTheCheckWeWant(Helper<&C::TargetFunc>*); +// +// template <typename C> +// No NotTheCheckWeWant(...); +// +// static const bool value = sizeof(NotTheCheckWeWant<T>(0)) == sizeof(Yes); +// +// The problem here is that template resolution will not match +// C::TargetFunc if TargetFunc does not exist directly in C. That is, if +// TargetFunc in inherited from an ancestor, &C::TargetFunc will not match, +// |value| will be false. This formulation only checks for whether or +// not TargetFunc exist directly in the class being introspected. +// +// To get around this, we play a dirty trick with multiple inheritance. +// First, We create a class BaseMixin that declares each function that we +// want to probe for. Then we create a class Base that inherits from both T +// (the class we wish to probe) and BaseMixin. Note that the function +// signature in BaseMixin does not need to match the signature of the function +// we are probing for; thus it's easiest to just use void(void). +// +// Now, if TargetFunc exists somewhere in T, then &Base::TargetFunc has an +// ambiguous resolution between BaseMixin and T. This lets us write the +// following: +// +// template <typename C> +// No GoodCheck(Helper<&C::TargetFunc>*); +// +// template <typename C> +// Yes GoodCheck(...); +// +// static const bool value = sizeof(GoodCheck<Base>(0)) == sizeof(Yes); +// +// Notice here that the variadic version of GoodCheck() returns Yes here +// instead of No like the previous one. Also notice that we calculate |value| +// by specializing GoodCheck() on Base instead of T. +// +// We've reversed the roles of the variadic, and Helper overloads. +// GoodCheck(Helper<&C::TargetFunc>*), when C = Base, fails to be a valid +// substitution if T::TargetFunc exists. Thus GoodCheck<Base>(0) will resolve +// to the variadic version if T has TargetFunc. If T::TargetFunc does not +// exist, then &C::TargetFunc is not ambiguous, and the overload resolution +// will prefer GoodCheck(Helper<&C::TargetFunc>*). +// +// This method of SFINAE will correctly probe for inherited names, but it cannot +// typecheck those names. It's still a good enough sanity check though. +// +// Works on gcc-4.2, gcc-4.4, and Visual Studio 2008. +// +// TODO(ajwong): Move to ref_counted.h or template_util.h when we've vetted +// this works well. +template <typename T> +class SupportsAddRefAndRelease { + typedef char Yes[1]; + typedef char No[2]; + + struct BaseMixin { + void AddRef(); + void Release(); + }; + + struct Base : public T, public BaseMixin { + }; + + template <void(BaseMixin::*)(void)> struct Helper {}; + + template <typename C> + static No& Check(Helper<&C::AddRef>*, Helper<&C::Release>*); + + template <typename > + static Yes& Check(...); + + public: + static const bool value = sizeof(Check<Base>(0,0)) == sizeof(Yes); +}; + + +// Helpers to assert that arguments of a recounted type are bound with a +// scoped_refptr. +template <bool IsClasstype, typename T> +struct UnsafeBindtoRefCountedArgHelper : false_type { +}; + +template <typename T> +struct UnsafeBindtoRefCountedArgHelper<true, T> + : integral_constant<bool, SupportsAddRefAndRelease<T>::value> { +}; + +template <typename T> +struct UnsafeBindtoRefCountedArg + : UnsafeBindtoRefCountedArgHelper<is_class<T>::value, T> { +}; + + +template <typename T> +class UnretainedWrapper { + public: + explicit UnretainedWrapper(T* o) : obj_(o) {} + T* get() { return obj_; } + private: + T* obj_; +}; + +template <typename T> +class ConstRefWrapper { + public: + explicit ConstRefWrapper(const T& o) : ptr_(&o) {} + const T& get() { return *ptr_; } + private: + const T* ptr_; +}; + + +// Unwrap the stored parameters for the wrappers above. +template <typename T> +T Unwrap(T o) { return o; } + +template <typename T> +T* Unwrap(UnretainedWrapper<T> unretained) { return unretained.get(); } + +template <typename T> +const T& Unwrap(ConstRefWrapper<T> const_ref) { + return const_ref.get(); +} + + +// Utility for handling different refcounting semantics in the Bind() +// function. +template <typename ref, typename T> +struct MaybeRefcount; + +template <typename T> +struct MaybeRefcount<base::false_type, T> { + static void AddRef(const T&) {} + static void Release(const T&) {} +}; + +template <typename T, size_t n> +struct MaybeRefcount<base::false_type, T[n]> { + static void AddRef(const T*) {} + static void Release(const T*) {} +}; + +template <typename T> +struct MaybeRefcount<base::true_type, UnretainedWrapper<T> > { + static void AddRef(const UnretainedWrapper<T>&) {} + static void Release(const UnretainedWrapper<T>&) {} +}; + +template <typename T> +struct MaybeRefcount<base::true_type, T*> { + static void AddRef(T* o) { o->AddRef(); } + static void Release(T* o) { o->Release(); } +}; + +template <typename T> +struct MaybeRefcount<base::true_type, const T*> { + static void AddRef(const T* o) { o->AddRef(); } + static void Release(const T* o) { o->Release(); } +}; + + +// This is a typetraits object that's used to convert an argument type into a +// type suitable for storage. In particular, it strips off references, and +// converts arrays to pointers. +// +// This array type becomes an issue because we are passing bound parameters by +// const reference. In this case, we end up passing an actual array type in the +// initializer list which C++ does not allow. This will break passing of +// C-string literals. +template <typename T> +struct BindType { + typedef T StorageType; +}; + +// This should almost be impossible to trigger unless someone manually +// specifies type of the bind parameters. However, in case they do, +// this will guard against us accidentally storing a reference parameter. +template <typename T> +struct BindType<T&> { + typedef T StorageType; +}; + +// Note that for array types, we implicitly add a const in the conversion. This +// means that it is not possible to bind array arguments to functions that take +// a non-const pointer. Trying to specialize the template based on a "const +// T[n]" does not seem to match correctly, so we are stuck with this +// restriction. +template <typename T, size_t n> +struct BindType<T[n]> { + typedef const T* StorageType; +}; + +template <typename T> +struct BindType<T[]> { + typedef const T* StorageType; +}; + +} // namespace internal + +template <typename T> +inline internal::UnretainedWrapper<T> Unretained(T* o) { + return internal::UnretainedWrapper<T>(o); +} + +template <typename T> +inline internal::ConstRefWrapper<T> ConstRef(const T& o) { + return internal::ConstRefWrapper<T>(o); +} + +} // namespace base + +#endif // BASE_BIND_HELPERS_H_ diff --git a/base/bind_internal.h b/base/bind_internal.h new file mode 100644 index 0000000..62f2050 --- /dev/null +++ b/base/bind_internal.h @@ -0,0 +1,1670 @@ +// This file was GENERATED by command: +// pump.py bind_internal.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. + +#ifndef BASE_BIND_INTERNAL_H_ +#define BASE_BIND_INTERNAL_H_ +#pragma once + +#include "base/bind_helpers.h" +#include "base/callback_helpers.h" +#include "base/template_util.h" + +namespace base { +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 +// 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 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. +// +// 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. + +template <typename StorageType, typename Sig> +struct FunctionTraits0; + +// Function: Arity 0 -> 0. +template <typename StorageType, typename R> +struct FunctionTraits0<StorageType, R(*)()> { + typedef base::false_type IsMethod; + + static R DoInvoke(InvokerStorageBase* base) { + StorageType* invoker = static_cast<StorageType*>(base); + return invoker->f_(); + } +}; + +// Function: Arity 1 -> 1. +template <typename StorageType, typename R,typename X1> +struct FunctionTraits0<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); + } +}; + +// Function: Arity 2 -> 2. +template <typename StorageType, typename R,typename X1, typename X2> +struct FunctionTraits0<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); + } +}; + +// Function: Arity 3 -> 3. +template <typename StorageType, typename R,typename X1, typename X2, + typename X3> +struct FunctionTraits0<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); + return invoker->f_(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)> { + 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::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); + return invoker->f_(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)> { + 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::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); + return invoker->f_(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)> { + 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 || + 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); + return invoker->f_(x1, x2, x3, x4, x5, x6); + } +}; + +template <typename StorageType, typename Sig> +struct FunctionTraits1; + +// Function: Arity 1 -> 0. +template <typename StorageType, typename R,typename X1> +struct FunctionTraits1<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_)); + } +}; + +// Method: Arity 0 -> 0. +template <typename StorageType, typename R, typename T> +struct FunctionTraits1<StorageType, R(T::*)()> { + typedef base::true_type IsMethod; + + 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)> { + 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); + } +}; + +// Method: Arity 1 -> 1. +template <typename StorageType, typename R, typename T, typename X1> +struct FunctionTraits1<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)> { + 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); + } +}; + +// Method: Arity 2 -> 2. +template <typename StorageType, typename R, typename T, typename X1, + typename X2> +struct FunctionTraits1<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)> { + 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::false_type IsMethod; + + static R DoInvoke(InvokerStorageBase* base, const X2& x2, const X3& x3, + const X4& x4) { + StorageType* invoker = static_cast<StorageType*>(base); + return invoker->f_(Unwrap(invoker->p1_), 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)> { + 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); + } +}; + +// 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)> { + 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::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); + return invoker->f_(Unwrap(invoker->p1_), 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)> { + 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); + } +}; + +// 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)> { + 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 || + 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); + return invoker->f_(Unwrap(invoker->p1_), 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)> { + 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); + } +}; + +// 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; + +// Function: Arity 2 -> 0. +template <typename StorageType, typename R,typename X1, typename X2> +struct FunctionTraits2<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_)); + } +}; + +// Method: Arity 1 -> 0. +template <typename StorageType, typename R, typename T, typename X1> +struct FunctionTraits2<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)> { + 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); + } +}; + +// Method: Arity 2 -> 1. +template <typename StorageType, typename R, typename T, typename X1, + typename X2> +struct FunctionTraits2<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)> { + 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::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); + } +}; + +// 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)> { + 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)> { + 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::false_type IsMethod; + + static R DoInvoke(InvokerStorageBase* base, const X3& x3, const X4& x4, + const X5& x5) { + StorageType* invoker = static_cast<StorageType*>(base); + return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), 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)> { + 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); + } +}; + +// 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)> { + 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 || + 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); + return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), 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)> { + 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); + } +}; + +// 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; + +// Function: Arity 3 -> 0. +template <typename StorageType, typename R,typename X1, typename X2, + typename X3> +struct FunctionTraits3<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_), + Unwrap(invoker->p3_)); + } +}; + +// Method: Arity 2 -> 0. +template <typename StorageType, typename R, typename T, typename X1, + typename X2> +struct FunctionTraits3<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_), + Unwrap(invoker->p3_)); + } +}; + +// 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)> { + 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::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_), + Unwrap(invoker->p3_), 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)> { + 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); + } +}; + +// 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)> { + 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::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_), + Unwrap(invoker->p3_), 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)> { + 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); + } +}; + +// 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)> { + 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 || + 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); + return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), + Unwrap(invoker->p3_), 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)> { + 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); + } +}; + +// 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; + +// 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)> { + 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::false_type IsMethod; + + static R DoInvoke(InvokerStorageBase* base) { + StorageType* invoker = static_cast<StorageType*>(base); + return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), + Unwrap(invoker->p3_), Unwrap(invoker->p4_)); + } +}; + +// 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)> { + 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_)); + } +}; + +// 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)> { + 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::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_), + Unwrap(invoker->p3_), Unwrap(invoker->p4_), 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)> { + 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); + } +}; + +// 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)> { + 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 || + 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_), + Unwrap(invoker->p3_), Unwrap(invoker->p4_), 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)> { + 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); + } +}; + +// 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; + +// 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)> { + 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::false_type IsMethod; + + static R DoInvoke(InvokerStorageBase* base) { + StorageType* invoker = static_cast<StorageType*>(base); + return invoker->f_(Unwrap(invoker->p1_), Unwrap(invoker->p2_), + Unwrap(invoker->p3_), Unwrap(invoker->p4_), Unwrap(invoker->p5_)); + } +}; + +// 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)> { + 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_)); + } +}; + +// 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)> { + 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 || + 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_), + Unwrap(invoker->p3_), Unwrap(invoker->p4_), Unwrap(invoker->p5_), 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)> { + 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); + } +}; + +// 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; + +// 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)> { + 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 || + 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_), + Unwrap(invoker->p3_), Unwrap(invoker->p4_), Unwrap(invoker->p5_), + Unwrap(invoker->p6_)); + } +}; + +// 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)> { + 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_)); + } +}; + +// 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. +// +// 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 +// of Run(). +// +// An alternate solution would be to merge FunctionTraitsN 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; + + + + InvokerStorage0(Sig f) + : f_(f) { + } + + virtual ~InvokerStorage0() { } + + Sig f_; +}; + +template <typename Sig, typename P1> +class InvokerStorage1 : public InvokerStorageBase { + public: + typedef InvokerStorage1 StorageType; + typedef FunctionTraits1<StorageType, Sig> FunctionTraits; + typedef typename FunctionTraits::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. + COMPILE_ASSERT(IsMethod::value || + !internal::UnsafeBindtoRefCountedArg<P1>::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!IsMethod::value || !is_array<P1>::value, + first_bound_argument_to_method_cannot_be_array); + + + InvokerStorage1(Sig f, const P1& p1) + : f_(f), p1_(static_cast<typename BindType<P1>::StorageType>(p1)) { + MaybeRefcount<IsMethod, P1>::AddRef(p1_); + } + + virtual ~InvokerStorage1() { + MaybeRefcount<IsMethod, P1>::Release(p1_); + } + + Sig f_; + typename BindType<P1>::StorageType p1_; +}; + +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; + + // 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. + COMPILE_ASSERT(IsMethod::value || + !internal::UnsafeBindtoRefCountedArg<P1>::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!IsMethod::value || !is_array<P1>::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P2>::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + + + InvokerStorage2(Sig f, const P1& p1, const P2& p2) + : f_(f), p1_(static_cast<typename BindType<P1>::StorageType>(p1)), + p2_(static_cast<typename BindType<P2>::StorageType>(p2)) { + MaybeRefcount<IsMethod, P1>::AddRef(p1_); + } + + virtual ~InvokerStorage2() { + MaybeRefcount<IsMethod, P1>::Release(p1_); + } + + Sig f_; + typename BindType<P1>::StorageType p1_; + typename BindType<P2>::StorageType p2_; +}; + +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; + + // 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. + COMPILE_ASSERT(IsMethod::value || + !internal::UnsafeBindtoRefCountedArg<P1>::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!IsMethod::value || !is_array<P1>::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P2>::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P3>::value, + p3_is_refcounted_type_and_needs_scoped_refptr); + + + InvokerStorage3(Sig f, const P1& p1, const P2& p2, const P3& p3) + : f_(f), p1_(static_cast<typename BindType<P1>::StorageType>(p1)), + p2_(static_cast<typename BindType<P2>::StorageType>(p2)), + p3_(static_cast<typename BindType<P3>::StorageType>(p3)) { + MaybeRefcount<IsMethod, P1>::AddRef(p1_); + } + + virtual ~InvokerStorage3() { + MaybeRefcount<IsMethod, P1>::Release(p1_); + } + + Sig f_; + typename BindType<P1>::StorageType p1_; + typename BindType<P2>::StorageType p2_; + typename BindType<P3>::StorageType p3_; +}; + +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; + + // 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. + COMPILE_ASSERT(IsMethod::value || + !internal::UnsafeBindtoRefCountedArg<P1>::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!IsMethod::value || !is_array<P1>::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P2>::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P3>::value, + p3_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P4>::value, + p4_is_refcounted_type_and_needs_scoped_refptr); + + + InvokerStorage4(Sig f, const P1& p1, const P2& p2, const P3& p3, const P4& p4) + : f_(f), p1_(static_cast<typename BindType<P1>::StorageType>(p1)), + p2_(static_cast<typename BindType<P2>::StorageType>(p2)), + p3_(static_cast<typename BindType<P3>::StorageType>(p3)), + p4_(static_cast<typename BindType<P4>::StorageType>(p4)) { + MaybeRefcount<IsMethod, P1>::AddRef(p1_); + } + + virtual ~InvokerStorage4() { + MaybeRefcount<IsMethod, P1>::Release(p1_); + } + + Sig f_; + typename BindType<P1>::StorageType p1_; + typename BindType<P2>::StorageType p2_; + typename BindType<P3>::StorageType p3_; + typename BindType<P4>::StorageType p4_; +}; + +template <typename Sig, typename P1, typename P2, typename P3, typename P4, + typename P5> +class InvokerStorage5 : public InvokerStorageBase { + public: + typedef InvokerStorage5 StorageType; + typedef FunctionTraits5<StorageType, Sig> FunctionTraits; + typedef typename FunctionTraits::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. + COMPILE_ASSERT(IsMethod::value || + !internal::UnsafeBindtoRefCountedArg<P1>::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!IsMethod::value || !is_array<P1>::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P2>::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P3>::value, + p3_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P4>::value, + p4_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P5>::value, + p5_is_refcounted_type_and_needs_scoped_refptr); + + + InvokerStorage5(Sig f, const P1& p1, const P2& p2, const P3& p3, + const P4& p4, const P5& p5) + : f_(f), p1_(static_cast<typename BindType<P1>::StorageType>(p1)), + p2_(static_cast<typename BindType<P2>::StorageType>(p2)), + p3_(static_cast<typename BindType<P3>::StorageType>(p3)), + p4_(static_cast<typename BindType<P4>::StorageType>(p4)), + p5_(static_cast<typename BindType<P5>::StorageType>(p5)) { + MaybeRefcount<IsMethod, P1>::AddRef(p1_); + } + + virtual ~InvokerStorage5() { + MaybeRefcount<IsMethod, P1>::Release(p1_); + } + + Sig f_; + typename BindType<P1>::StorageType p1_; + typename BindType<P2>::StorageType p2_; + typename BindType<P3>::StorageType p3_; + typename BindType<P4>::StorageType p4_; + typename BindType<P5>::StorageType p5_; +}; + +template <typename Sig, typename P1, typename P2, typename P3, typename P4, + typename P5, typename P6> +class InvokerStorage6 : public InvokerStorageBase { + public: + typedef InvokerStorage6 StorageType; + typedef FunctionTraits6<StorageType, Sig> FunctionTraits; + typedef typename FunctionTraits::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. + COMPILE_ASSERT(IsMethod::value || + !internal::UnsafeBindtoRefCountedArg<P1>::value, + p1_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!IsMethod::value || !is_array<P1>::value, + first_bound_argument_to_method_cannot_be_array); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P2>::value, + p2_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P3>::value, + p3_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P4>::value, + p4_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P5>::value, + p5_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P6>::value, + p6_is_refcounted_type_and_needs_scoped_refptr); + + + InvokerStorage6(Sig f, const P1& p1, const P2& p2, const P3& p3, + const P4& p4, const P5& p5, const P6& p6) + : f_(f), p1_(static_cast<typename BindType<P1>::StorageType>(p1)), + p2_(static_cast<typename BindType<P2>::StorageType>(p2)), + p3_(static_cast<typename BindType<P3>::StorageType>(p3)), + p4_(static_cast<typename BindType<P4>::StorageType>(p4)), + p5_(static_cast<typename BindType<P5>::StorageType>(p5)), + p6_(static_cast<typename BindType<P6>::StorageType>(p6)) { + MaybeRefcount<IsMethod, P1>::AddRef(p1_); + } + + virtual ~InvokerStorage6() { + MaybeRefcount<IsMethod, P1>::Release(p1_); + } + + Sig f_; + typename BindType<P1>::StorageType p1_; + typename BindType<P2>::StorageType p2_; + typename BindType<P3>::StorageType p3_; + typename BindType<P4>::StorageType p4_; + typename BindType<P5>::StorageType p5_; + typename BindType<P6>::StorageType p6_; +}; + +} // namespace internal +} // namespace base + +#endif // BASE_BIND_INTERNAL_H_ diff --git a/base/bind_internal.h.pump b/base/bind_internal.h.pump new file mode 100644 index 0000000..132b0db --- /dev/null +++ b/base/bind_internal.h.pump @@ -0,0 +1,237 @@ +$$ 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. + +#ifndef BASE_BIND_INTERNAL_H_ +#define BASE_BIND_INTERNAL_H_ +#pragma once + +#include "base/bind_helpers.h" +#include "base/callback_helpers.h" +#include "base/template_util.h" + +namespace base { +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 +// 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 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. +// +// 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. + +$range BOUND 0..MAX_ARITY +$for BOUND [[ + +template <typename StorageType, typename Sig> +struct FunctionTraits$(BOUND); + +$range ARITY 0..MAX_ARITY +$for ARITY [[ + +$var UNBOUND = ARITY - BOUND +$if UNBOUND >= 0 [[ + +$$ Variables for function traits generation. +$range ARG 1..ARITY +$range BOUND_ARG 1..BOUND +$range UNBOUND_ARG (ARITY - UNBOUND + 1)..ARITY + +$$ Variables for method traits generation. We are always short one arity since +$$ the first bound parameter is the object. +$var M_ARITY = ARITY - 1 +$range M_ARG 1..M_ARITY +$range M_BOUND_ARG 2..BOUND +$range M_UNBOUND_ARG (M_ARITY - UNBOUND + 1)..M_ARITY + +// Function: Arity $(ARITY) -> $(UNBOUND). +template <typename StorageType, typename R[[]] +$if ARITY > 0 [[,]][[]] +$for ARG , [[typename X$(ARG)]]> +struct FunctionTraits$(BOUND)<StorageType, R(*)($for ARG , [[X$(ARG)]])> { +$if ARITY > 0 [[ + + COMPILE_ASSERT( + !($for ARG || [[ is_non_const_reference<X$(ARG)>::value ]]), + do_not_bind_functions_with_nonconst_ref); + +]] + + typedef base::false_type IsMethod; + + static R DoInvoke(InvokerStorageBase* base[[]] +$if UNBOUND != 0 [[, ]][[]] +$for UNBOUND_ARG , [[const X$(UNBOUND_ARG)& x$(UNBOUND_ARG)]]) { + StorageType* invoker = static_cast<StorageType*>(base); + return invoker->f_($for BOUND_ARG , [[Unwrap(invoker->p$(BOUND_ARG)_)]][[]] +$$ Add comma if there are both boudn and unbound args. +$if UNBOUND > 0 [[$if BOUND > 0 [[, ]]]][[]] +$for UNBOUND_ARG , [[x$(UNBOUND_ARG)]]); + } +}; + +$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)]])> { +$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)]]); + } +}; + +// 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 +]] $$ for ARITY +]] $$ for BOUND + + +// 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 +// of Run(). +// +// An alternate solution would be to merge FunctionTraitsN and InvokerStorageN, +// but the generated code seemed harder to read. + +$for BOUND [[ +$range BOUND_ARG 1..BOUND + +template <typename Sig[[]] +$if BOUND > 0 [[, ]] +$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; + +$for BOUND_ARG [[ +$if BOUND_ARG == 1 [[ + + // 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. + COMPILE_ASSERT(IsMethod::value || + !internal::UnsafeBindtoRefCountedArg<P$(BOUND_ARG)>::value, + p$(BOUND_ARG)_is_refcounted_type_and_needs_scoped_refptr); + COMPILE_ASSERT(!IsMethod::value || !is_array<P$(BOUND_ARG)>::value, + first_bound_argument_to_method_cannot_be_array); +]] $else [[ + + COMPILE_ASSERT(!internal::UnsafeBindtoRefCountedArg<P$(BOUND_ARG)>::value, + p$(BOUND_ARG)_is_refcounted_type_and_needs_scoped_refptr); +]] $$ $if BOUND_ARG +]] $$ $for BOUND_ARG + + + + InvokerStorage$(BOUND)(Sig f +$if BOUND > 0 [[, ]] +$for BOUND_ARG , [[const P$(BOUND_ARG)& p$(BOUND_ARG)]]) + : f_(f)[[]] +$if BOUND == 0 [[ + { + +]] $else [[ +, $for BOUND_ARG , [[p$(BOUND_ARG)_(static_cast<typename BindType<P$(BOUND_ARG)>::StorageType>(p$(BOUND_ARG)))]] { + MaybeRefcount<IsMethod, P1>::AddRef(p1_); + +]] + } + + virtual ~InvokerStorage$(BOUND)() { +$if BOUND > 0 [[ + + MaybeRefcount<IsMethod, P1>::Release(p1_); + +]] + } + + Sig f_; + +$for BOUND_ARG [[ + typename BindType<P$(BOUND_ARG)>::StorageType p$(BOUND_ARG)_; + +]] +}; + +]] $$ for BOUND + +} // namespace internal +} // namespace base + +#endif // BASE_BIND_INTERNAL_H_ diff --git a/base/bind_unittest.cc b/base/bind_unittest.cc new file mode 100644 index 0000000..47d971a --- /dev/null +++ b/base/bind_unittest.cc @@ -0,0 +1,597 @@ +// 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/bind.h" + +#if defined(BASE_CALLBACK_H_) +// We explicitly do not want to include callback.h so people are not tempted +// to use bind.h in a headerfile for getting the Callback types. +#error "base/bind.h should avoid pulling in callback.h by default." +#endif + +#include "base/callback.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::Mock; +using ::testing::Return; +using ::testing::StrictMock; + +namespace base { +namespace { + +class NoRef { + public: + NoRef() {} + + MOCK_METHOD0(VoidMethod0, void(void)); + MOCK_CONST_METHOD0(VoidConstMethod0, void(void)); + + MOCK_METHOD0(IntMethod0, int(void)); + MOCK_CONST_METHOD0(IntConstMethod0, int(void)); + + private: + // Particularly important in this test to ensure no copies are made. + DISALLOW_COPY_AND_ASSIGN(NoRef); +}; + +class HasRef : public NoRef { + public: + HasRef() {} + + MOCK_CONST_METHOD0(AddRef, void(void)); + MOCK_CONST_METHOD0(Release, bool(void)); + + private: + // Particularly important in this test to ensure no copies are made. + DISALLOW_COPY_AND_ASSIGN(HasRef); +}; + +static const int kParentValue = 1; +static const int kChildValue = 2; + +class Parent { + public: + void AddRef(void) const {} + void Release(void) const {} + virtual void VirtualSet() { value = kParentValue; } + void NonVirtualSet() { value = kParentValue; } + int value; +}; + +class Child : public Parent { + public: + virtual void VirtualSet() { value = kChildValue; } + void NonVirtualSet() { value = kChildValue; } +}; + +class NoRefParent { + public: + virtual void VirtualSet() { value = kParentValue; } + void NonVirtualSet() { value = kParentValue; } + int value; +}; + +class NoRefChild : public NoRefParent { + virtual void VirtualSet() { value = kChildValue; } + void NonVirtualSet() { value = kChildValue; } +}; + +// Used for probing the number of copies that occur if a type must be coerced +// during argument forwarding in the Run() methods. +struct DerivedCopyCounter { + DerivedCopyCounter(int* copies, int* assigns) + : copies_(copies), assigns_(assigns) { + } + int* copies_; + int* assigns_; +}; + +// Used for probing the number of copies in an argument. +class CopyCounter { + public: + CopyCounter(int* copies, int* assigns) + : copies_(copies), assigns_(assigns) { + } + + CopyCounter(const CopyCounter& other) + : copies_(other.copies_), + assigns_(other.assigns_) { + (*copies_)++; + } + + // Probing for copies from coerscion. + CopyCounter(const DerivedCopyCounter& other) + : copies_(other.copies_), + assigns_(other.assigns_) { + (*copies_)++; + } + + const CopyCounter& operator=(const CopyCounter& rhs) { + copies_ = rhs.copies_; + assigns_ = rhs.assigns_; + + if (assigns_) { + (*assigns_)++; + } + + return *this; + } + + int copies() const { + return *copies_; + } + + int assigns() const { + return *assigns_; + } + + private: + int* copies_; + int* assigns_; +}; + +// Some test functions that we can Bind to. +template <typename T> +T PolymorphicIdentity(T t) { + return t; +} + +template <typename T> +void VoidPolymorphic1(T t) { +} + +int Identity(int n) { + return n; +} + +int ArrayGet(const int array[], int n) { + return array[n]; +} + +int Sum(int a, int b, int c, int d, int e, int f) { + return a + b + c + d + e + f; +} + +const char* CStringIdentity(const char* s) { + return s; +} + +int GetCopies(const CopyCounter& counter) { + return counter.copies(); +} + +int UnwrapNoRefParent(NoRefParent p) { + return p.value; +} + +int UnwrapNoRefParentPtr(NoRefParent* p) { + return p->value; +} + +int UnwrapNoRefParentConstRef(const NoRefParent& p) { + return p.value; +} + +// Only useful in no-compile tests. +int UnwrapNoRefParentRef(Parent& p) { + return p.value; +} + +class BindTest : public ::testing::Test { + public: + BindTest() { + const_has_ref_ptr_ = &has_ref_; + const_no_ref_ptr_ = &no_ref_; + static_func_mock_ptr = &static_func_mock_; + } + + virtual ~BindTest() { + } + + static void VoidFunc0(void) { + static_func_mock_ptr->VoidMethod0(); + } + + static int IntFunc0(void) { return static_func_mock_ptr->IntMethod0(); } + + protected: + StrictMock<NoRef> no_ref_; + StrictMock<HasRef> has_ref_; + const HasRef* const_has_ref_ptr_; + const NoRef* const_no_ref_ptr_; + StrictMock<NoRef> static_func_mock_; + + // Used by the static functions to perform expectations. + static StrictMock<NoRef>* static_func_mock_ptr; + + private: + DISALLOW_COPY_AND_ASSIGN(BindTest); +}; + +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); + EXPECT_EQ(63, c0.Run()); + + Callback<int(int)> c1 = Bind(&Sum, 32, 16, 8, 4, 2); + EXPECT_EQ(75, c1.Run(13)); + + Callback<int(int,int)> c2 = Bind(&Sum, 32, 16, 8, 4); + EXPECT_EQ(85, c2.Run(13, 12)); + + Callback<int(int,int,int)> c3 = Bind(&Sum, 32, 16, 8); + EXPECT_EQ(92, c3.Run(13, 12, 11)); + + Callback<int(int,int,int,int)> c4 = Bind(&Sum, 32, 16); + EXPECT_EQ(94, c4.Run(13, 12, 11, 10)); + + Callback<int(int,int,int,int,int)> c5 = Bind(&Sum, 32); + EXPECT_EQ(87, c5.Run(13, 12, 11, 10, 9)); + + Callback<int(int,int,int,int,int,int)> c6 = Bind(&Sum); + EXPECT_EQ(69, c6.Run(13, 12, 11, 10, 9, 14)); +} + +// Function type support. +// - Normal function. +// - Method bound to non-const object. +// - Const method bound to non-const object. +// - Const method bound to const object. +// - Derived classes can be used with pointers to non-virtual base functions. +// - Derived classes can be used with pointers to virtual base functions (and +// preserve virtual dispatch). +TEST_F(BindTest, FunctionTypeSupport) { + EXPECT_CALL(static_func_mock_, VoidMethod0()); + EXPECT_CALL(has_ref_, AddRef()).Times(3); + EXPECT_CALL(has_ref_, Release()).Times(3); + EXPECT_CALL(has_ref_, VoidMethod0()); + EXPECT_CALL(has_ref_, VoidConstMethod0()).Times(2); + + Closure normal_cb = Bind(&VoidFunc0); + Closure method_cb = Bind(&HasRef::VoidMethod0, &has_ref_); + Closure const_method_nonconst_obj_cb = Bind(&HasRef::VoidConstMethod0, + &has_ref_); + Closure const_method_const_obj_cb = Bind(&HasRef::VoidConstMethod0, + const_has_ref_ptr_); + normal_cb.Run(); + method_cb.Run(); + const_method_nonconst_obj_cb.Run(); + const_method_const_obj_cb.Run(); + + Child child; + child.value = 0; + Closure virtual_set_cb = Bind(&Parent::VirtualSet, &child); + virtual_set_cb.Run(); + EXPECT_EQ(kChildValue, child.value); + + child.value = 0; + Closure non_virtual_set_cb = Bind(&Parent::NonVirtualSet, &child); + non_virtual_set_cb.Run(); + EXPECT_EQ(kParentValue, child.value); +} + +// Return value support. +// - Function with return value. +// - Method with return value. +// - Const method with return value. +TEST_F(BindTest, ReturnValues) { + EXPECT_CALL(static_func_mock_, IntMethod0()).WillOnce(Return(1337)); + EXPECT_CALL(has_ref_, AddRef()).Times(3); + EXPECT_CALL(has_ref_, Release()).Times(3); + EXPECT_CALL(has_ref_, IntMethod0()).WillOnce(Return(31337)); + EXPECT_CALL(has_ref_, IntConstMethod0()) + .WillOnce(Return(41337)) + .WillOnce(Return(51337)); + + Callback<int(void)> normal_cb = Bind(&IntFunc0); + Callback<int(void)> method_cb = Bind(&HasRef::IntMethod0, &has_ref_); + Callback<int(void)> const_method_nonconst_obj_cb = + Bind(&HasRef::IntConstMethod0, &has_ref_); + Callback<int(void)> const_method_const_obj_cb = + Bind(&HasRef::IntConstMethod0, const_has_ref_ptr_); + EXPECT_EQ(1337, normal_cb.Run()); + EXPECT_EQ(31337, method_cb.Run()); + EXPECT_EQ(41337, const_method_nonconst_obj_cb.Run()); + EXPECT_EQ(51337, const_method_const_obj_cb.Run()); +} + +// Argument binding tests. +// - Argument binding to primitive. +// - Argument binding to primitive pointer. +// - Argument binding to a literal integer. +// - Argument binding to a literal string. +// - Argument binding with template function. +// - Argument binding to an object. +// - Argument gets type converted. +// - Pointer argument gets converted. +// - Const Reference forces conversion. +TEST_F(BindTest, ArgumentBinding) { + int n = 2; + + Callback<int(void)> bind_primitive_cb = Bind(&Identity, n); + EXPECT_EQ(n, bind_primitive_cb.Run()); + + Callback<int*(void)> bind_primitive_pointer_cb = + Bind(&PolymorphicIdentity<int*>, &n); + EXPECT_EQ(&n, bind_primitive_pointer_cb.Run()); + + Callback<int(void)> bind_int_literal_cb = Bind(&Identity, 3); + EXPECT_EQ(3, bind_int_literal_cb.Run()); + + Callback<const char*(void)> bind_string_literal_cb = + Bind(&CStringIdentity, "hi"); + EXPECT_STREQ("hi", bind_string_literal_cb.Run()); + + Callback<int(void)> bind_template_function_cb = + Bind(&PolymorphicIdentity<int>, 4); + EXPECT_EQ(4, bind_template_function_cb.Run()); + + NoRefParent p; + p.value = 5; + Callback<int(void)> bind_object_cb = Bind(&UnwrapNoRefParent, p); + EXPECT_EQ(5, bind_object_cb.Run()); + + NoRefChild c; + c.value = 6; + Callback<int(void)> bind_promotes_cb = Bind(&UnwrapNoRefParent, c); + EXPECT_EQ(6, bind_promotes_cb.Run()); + + c.value = 7; + Callback<int(void)> bind_pointer_promotes_cb = + Bind(&UnwrapNoRefParentPtr, &c); + EXPECT_EQ(7, bind_pointer_promotes_cb.Run()); + + c.value = 8; + Callback<int(void)> bind_const_reference_promotes_cb = + Bind(&UnwrapNoRefParentConstRef, c); + EXPECT_EQ(8, bind_const_reference_promotes_cb.Run()); +} + +// Functions that take reference parameters. +// - Forced reference parameter type still stores a copy. +// - Forced const reference parameter type still stores a copy. +TEST_F(BindTest, ReferenceArgumentBinding) { + int n = 1; + int& ref_n = n; + const int& const_ref_n = n; + + Callback<int(void)> ref_copies_cb = Bind(&Identity, ref_n); + EXPECT_EQ(n, ref_copies_cb.Run()); + n++; + EXPECT_EQ(n - 1, ref_copies_cb.Run()); + + Callback<int(void)> const_ref_copies_cb = Bind(&Identity, const_ref_n); + EXPECT_EQ(n, const_ref_copies_cb.Run()); + n++; + EXPECT_EQ(n - 1, const_ref_copies_cb.Run()); +} + +// Check that we can pass in arrays and have them be stored as a pointer. +// - Array of values stores a pointer. +// - Array of const values stores a pointer. +TEST_F(BindTest, ArrayArgumentBinding) { + int array[4] = {1, 1, 1, 1}; + const int (*const_array_ptr)[4] = &array; + + Callback<int(void)> array_cb = Bind(&ArrayGet, array, 1); + EXPECT_EQ(1, array_cb.Run()); + + Callback<int(void)> const_array_cb = Bind(&ArrayGet, *const_array_ptr, 1); + EXPECT_EQ(1, const_array_cb.Run()); + + array[1] = 3; + EXPECT_EQ(3, array_cb.Run()); + EXPECT_EQ(3, const_array_cb.Run()); +} + +// Verify SupportsAddRefAndRelease correctly introspects the class type for +// AddRef() and Release(). +TEST_F(BindTest, SupportsAddRefAndRelease) { + EXPECT_TRUE(internal::SupportsAddRefAndRelease<HasRef>::value); + EXPECT_FALSE(internal::SupportsAddRefAndRelease<NoRef>::value); + + // StrictMock<T> is a derived class of T. So, we use StrictMock<HasRef> and + // StrictMock<NoRef> to test that SupportsAddRefAndRelease works over + // inheritance. + EXPECT_TRUE(internal::SupportsAddRefAndRelease<StrictMock<HasRef> >::value); + EXPECT_FALSE(internal::SupportsAddRefAndRelease<StrictMock<NoRef> >::value); +} + +// Unretained() wrapper support. +// - Method bound to Unretained() non-object. +// - Const method bound to Unretained() non-const object. +// - Const method bound to Unretained() const object. +TEST_F(BindTest, Unretained) { + EXPECT_CALL(no_ref_, VoidMethod0()); + EXPECT_CALL(no_ref_, VoidConstMethod0()).Times(2); + + Callback<void(void)> method_cb = + Bind(&NoRef::VoidMethod0, Unretained(&no_ref_)); + method_cb.Run(); + + Callback<void(void)> const_method_cb = + Bind(&NoRef::VoidConstMethod0, Unretained(&no_ref_)); + const_method_cb.Run(); + + Callback<void(void)> const_method_const_ptr_cb = + Bind(&NoRef::VoidConstMethod0, Unretained(const_no_ref_ptr_)); + const_method_const_ptr_cb.Run(); +} + +// ConstRef() wrapper support. +// - Binding w/o ConstRef takes a copy. +// - Binding a ConstRef takes a reference. +// - Binding ConstRef to a function ConstRef does not copy on invoke. +TEST_F(BindTest, ConstRef) { + int n = 1; + + Callback<int(void)> copy_cb = Bind(&Identity, n); + Callback<int(void)> const_ref_cb = Bind(&Identity, ConstRef(n)); + EXPECT_EQ(n, copy_cb.Run()); + EXPECT_EQ(n, const_ref_cb.Run()); + n++; + EXPECT_EQ(n - 1, copy_cb.Run()); + EXPECT_EQ(n, const_ref_cb.Run()); + + int copies = 0; + int assigns = 0; + CopyCounter counter(&copies, &assigns); + Callback<int(void)> all_const_ref_cb = + Bind(&GetCopies, ConstRef(counter)); + EXPECT_EQ(0, all_const_ref_cb.Run()); + EXPECT_EQ(0, copies); + EXPECT_EQ(0, assigns); +} + +// Argument Copy-constructor usage for non-reference parameters. +// - Bound arguments are only copied once. +// - Forwarded arguments are only copied once. +// - Forwarded arguments with coerscions are only copied twice (once for the +// coerscion, and one for the final dispatch). +TEST_F(BindTest, ArgumentCopies) { + int copies = 0; + int assigns = 0; + + CopyCounter counter(&copies, &assigns); + + Callback<void(void)> copy_cb = + Bind(&VoidPolymorphic1<CopyCounter>, counter); + EXPECT_GE(1, copies); + EXPECT_EQ(0, assigns); + + copies = 0; + assigns = 0; + Callback<void(CopyCounter)> forward_cb = + Bind(&VoidPolymorphic1<CopyCounter>); + forward_cb.Run(counter); + EXPECT_GE(1, copies); + EXPECT_EQ(0, assigns); + + copies = 0; + assigns = 0; + DerivedCopyCounter dervied(&copies, &assigns); + Callback<void(CopyCounter)> coerce_cb = + Bind(&VoidPolymorphic1<CopyCounter>); + coerce_cb.Run(dervied); + EXPECT_GE(2, copies); + EXPECT_EQ(0, assigns); +} + +// Callback construction and assignment tests. +// - Construction from an InvokerStorageHolder should not cause ref/deref. +// - Assignment from other callback should only cause one ref +// +// TODO(ajwong): Is there actually a way to test this? + +// No-compile tests. These should not compile. If they do, we are allowing +// error-prone, or incorrect behavior in the callback system. Uncomment the +// tests to check. +TEST_F(BindTest, NoCompile) { + // - Method bound to const-object. + // + // Only const methods should be allowed to work with const objects. + // + // Callback<void(void)> method_to_const_cb = + // Bind(&HasRef::VoidMethod0, const_has_ref_ptr_); + // method_to_const_cb.Run(); + + // - Method bound to non-refcounted object. + // - Const Method bound to non-refcounted object. + // + // We require refcounts unless you have Unretained(). + // + // Callback<void(void)> no_ref_cb = + // Bind(&NoRef::VoidMethod0, &no_ref_); + // no_ref_cb.Run(); + // Callback<void(void)> no_ref_const_cb = + // Bind(&NoRef::VoidConstMethod0, &no_ref_); + // no_ref_const_cb.Run(); + + // - Unretained() used with a refcounted object. + // + // If the object supports refcounts, unretaining it in the callback is a + // memory management contract break. + // Callback<void(void)> unretained_cb = + // Bind(&HasRef::VoidConstMethod0, Unretained(&has_ref_)); + // unretained_cb.Run(); + + // - Const argument used with non-const pointer parameter of same type. + // - Const argument used with non-const pointer parameter of super type. + // + // This is just a const-correctness check. + // + // const Parent* const_parent_ptr; + // const Child* const_child_ptr; + // Callback<Parent*(void)> pointer_same_cb = + // Bind(&PolymorphicIdentity<Parent*>, const_parent_ptr); + // pointer_same_cb.Run(); + // Callback<Parent*(void)> pointer_super_cb = + // Bind(&PolymorphicIdentity<Parent*>, const_child_ptr); + // pointer_super_cb.Run(); + + // - Construction of Callback<A> from Callback<B> if A is supertype of B. + // Specific example: Callback<void(void)> a; Callback<int(void)> b; a = b; + // + // While this is technically safe, most people aren't used to it when coding + // C++ so if this is happening, it is almost certainly an error. + // + // Callback<int(void)> cb_a0 = Bind(&Identity, 1); + // Callback<void(void)> cb_b0 = cb_a0; + + // - Assignment of Callback<A> from Callback<B> if A is supertype of B. + // See explanation above. + // + // Callback<int(void)> cb_a1 = Bind(&Identity, 1); + // Callback<void(void)> cb_b1; + // cb_a1 = cb_b1; + + // - Functions with reference parameters, unsupported. + // + // First, non-const reference parameters are disallowed by the Google + // style guide. Seconds, since we are doing argument forwarding it becomes + // very tricky to avoid copies, maintain const correctness, and not + // accidentally have the function be modifying a temporary, or a copy. + // + // NoRefParent p; + // Callback<int(Parent&)> ref_arg_cb = Bind(&UnwrapNoRefParentRef); + // ref_arg_cb.Run(p); + // Callback<int(void)> ref_cb = Bind(&UnwrapNoRefParentRef, p); + // ref_cb.Run(); + + // - A method should not be bindable with an array of objects. + // + // This is likely not wanted behavior. We specifically check for it though + // because it is possible, depending on how you implement prebinding, to + // implicitly convert an array type to a pointer type. + // + // HasRef p[10]; + // Callback<void(void)> method_bound_to_array_cb = + // Bind(&HasRef::VoidConstMethod0, p); + // method_bound_to_array_cb.Run(); + + // - Refcounted types should not be bound as a raw pointer. + // HasRef for_raw_ptr; + // Callback<void(void)> ref_count_as_raw_ptr = + // Bind(&VoidPolymorphic1<HasRef*>, &for_raw_ptr); + // ASSERT_EQ(&for_raw_ptr, ref_count_as_raw_ptr.Run()); + +} + +} // namespace +} // namespace base diff --git a/base/callback.h b/base/callback.h index e5ea771..05a7182 100644 --- a/base/callback.h +++ b/base/callback.h @@ -1,4 +1,9 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// This file was GENERATED by command: +// pump.py callback.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. @@ -6,249 +11,472 @@ #define BASE_CALLBACK_H_ #pragma once -#include "base/tuple.h" -#include "base/raw_scoped_refptr_mismatch_checker.h" - -// Callback -------------------------------------------------------------------- -// -// A Callback is like a Task but with unbound parameters. It is basically an -// object-oriented function pointer. -// -// Callbacks are designed to work with Tuples. A set of helper functions and -// classes is provided to hide the Tuple details from the consumer. Client -// code will generally work with the CallbackRunner base class, which merely -// provides a Run method and is returned by the New* functions. This allows -// users to not care which type of class implements the callback, only that it -// has a certain number and type of arguments. -// -// The implementation of this is done by CallbackImpl, which inherits -// CallbackStorage to store the data. This allows the storage of the data -// (requiring the class type T) to be hidden from users, who will want to call -// this regardless of the implementor's type T. -// -// Note that callbacks currently have no facility for cancelling or abandoning -// them. We currently handle this at a higher level for cases where this is -// necessary. The pointer in a callback must remain valid until the callback -// is made. -// -// Like Task, the callback executor is responsible for deleting the callback -// pointer once the callback has executed. -// -// Example client usage: -// void Object::DoStuff(int, string); -// Callback2<int, string>::Type* callback = -// NewCallback(obj, &Object::DoStuff); -// callback->Run(5, string("hello")); -// delete callback; -// or, equivalently, using tuples directly: -// CallbackRunner<Tuple2<int, string> >* callback = -// NewCallback(obj, &Object::DoStuff); -// callback->RunWithParams(MakeTuple(5, string("hello"))); -// -// There is also a 0-args version that returns a value. Example: -// int Object::GetNextInt(); -// CallbackWithReturnValue<int>::Type* callback = -// NewCallbackWithReturnValue(obj, &Object::GetNextInt); -// int next_int = callback->Run(); -// delete callback; - -// Base for all Callbacks that handles storage of the pointers. -template <class T, typename Method> -class CallbackStorage { - public: - CallbackStorage(T* obj, Method meth) : obj_(obj), meth_(meth) { - } - - protected: - T* obj_; - Method meth_; -}; +#include "base/callback_helpers.h" +#include "base/callback_old.h" -// Interface that is exposed to the consumer, that does the actual calling -// of the method. -template <typename Params> -class CallbackRunner { +// New, super-duper, unified Callback system. This will eventually replace +// NewRunnableMethod, NewRunnableFunction, CreateFunctor, and CreateCallback +// systems currently in the Chromium code base. +// +// WHAT IS THIS: +// +// The templated Callback class is a generalized function object. Together +// with the Bind() function in bind.h, they provide a type-safe method for +// performing currying of arguments, and creating a "closure." +// +// In programing languages, a closure is a first-class function where all its +// parameters have been bound (usually via currying). Closures are well +// suited for representing, and passing around a unit of delayed execution. +// They are used in Chromium code to schedule tasks on different MessageLoops. +// +// +// MEMORY MANAGEMENT AND PASSING +// +// The Callback objects themselves should be passed by const-reference, and +// stored by copy. They internally store their state via a refcounted class +// and thus do not need to be deleted. +// +// The reason to pass via a const-reference is to avoid unnecessary +// AddRef/Release pairs to the internal state. +// +// +// EXAMPLE USAGE: +// +// /* Binding a normal function. */ +// int Return5() { return 5; } +// base::Callback<int(int)> func_cb = base::Bind(&Return5); +// LOG(INFO) << func_cb.Run(5); // Prints 5. +// +// void PrintHi() { LOG(INFO) << "hi."; } +// base::Closure void_func_cb = base::Bind(&PrintHi); +// LOG(INFO) << void_func_cb.Run(); // Prints: hi. +// +// /* Binding a class method. */ +// class Ref : public RefCountedThreadSafe<Ref> { +// public: +// int Foo() { return 3; } +// void PrintBye() { LOG(INFO) << "bye."; } +// }; +// scoped_refptr<Ref> ref = new Ref(); +// base::Callback<int(void)> ref_cb = base::Bind(&Ref::Foo, ref.get()); +// LOG(INFO) << ref_cb.Run(); // Prints out 3. +// +// base::Closure void_ref_cb = base::Bind(&Ref::PrintBye, ref.get()); +// void_ref_cb.Run(); // Prints: bye. +// +// /* Binding a class method in a non-refcounted class. +// * +// * WARNING: You must be sure the referee outlives the callback! +// * This is particularly important if you post a closure to a +// * MessageLoop because then it becomes hard to know what the +// * lifetime of the referee needs to be. +// */ +// class NoRef { +// public: +// int Foo() { return 4; } +// void PrintWhy() { LOG(INFO) << "why???"; } +// }; +// NoRef no_ref; +// base::Callback<int(void)> base::no_ref_cb = +// base::Bind(&NoRef::Foo, base::Unretained(&no_ref)); +// LOG(INFO) << ref_cb.Run(); // Prints out 4. +// +// base::Closure void_no_ref_cb = +// base::Bind(&NoRef::PrintWhy, base::Unretained(no_ref)); +// void_no_ref_cb.Run(); // Prints: why??? +// +// /* Binding a reference. */ +// int Identity(int n) { return n; } +// int value = 1; +// base::Callback<int(void)> bound_copy_cb = base::Bind(&Identity, value); +// base::Callback<int(void)> bound_ref_cb = +// base::Bind(&Identity, base::ConstRef(value)); +// LOG(INFO) << bound_copy_cb.Run(); // Prints 1. +// LOG(INFO) << bound_ref_cb.Run(); // Prints 1. +// value = 2; +// LOG(INFO) << bound_copy_cb.Run(); // Prints 1. +// LOG(INFO) << bound_ref_cb.Run(); // Prints 2. +// +// +// WHERE IS THIS DESIGN FROM: +// +// The design Callback and Bind is heavily influenced by C++'s +// tr1::function/tr1::bind, and by the "Google Callback" system used inside +// Google. +// +// +// HOW THE IMPLEMENTATION WORKS: +// +// There are three main components to the system: +// 1) The Callback classes. +// 2) The Bind() functions. +// 3) The arguments wrappers (eg., Unretained() and ConstRef()). +// +// The Callback classes represent a generic function pointer. Internally, +// it stores a refcounted piece of state that represents the target function +// and all its bound parameters. Each Callback specialization has a templated +// constructor that takes an InvokerStorageHolder<> object. In the context of +// the constructor, the static type of this InvokerStorageHolder<> object +// uniquely identifies the function it is representing, all its bound +// parameters, and a DoInvoke() that is capable of invoking the target. +// +// Callback's constructor is takes the InvokerStorageHolder<> that has the +// full static type and erases the target function type, and the bound +// parameters. It does this by storing a pointer to the specific DoInvoke() +// function, and upcasting the state of InvokerStorageHolder<> to a +// InvokerStorageBase. This is safe as long as this InvokerStorageBase pointer +// is only used with the stored DoInvoke() pointer. +// +// To create InvokerStorageHolder<> objects, we use the Bind() functions. +// These functions, along with a set of internal templates, are reponsible for +// +// - Unwrapping the function signature into return type, and parameters +// - Determining the number of parameters that are bound +// - Creating the storage for the bound parameters +// - Performing compile-time asserts to avoid error-prone behavior +// - Returning an InvokerStorageHolder<> with an DoInvoke() that has an arity +// matching the number of unbound parameters, and knows the correct +// refcounting semantics for the target object if we are binding a class +// method. +// +// The Bind functions do the above using type-inference, and template +// specializations. +// +// By default Bind() will store copies of all bound parameters, and attempt +// to refcount a target object if the function being bound is a class method. +// +// To change this behavior, we introduce a set of argument wrappers +// (eg. Unretained(), and ConstRef()). These are simple container templates +// that are passed by value, and wrap a pointer to argument. See the +// file-level comment in base/bind_helpers.h for more info. +// +// These types are passed to the Unwrap() functions, and the MaybeRefcount() +// functions respectively to modify the behavior of Bind(). The Unwrap() +// and MaybeRefcount() functions change behavior by doing partial +// specialization based on whether or not a parameter is a wrapper type. +// +// ConstRef() is similar to tr1::cref. Unretained() is specific to Chromium. +// +// +// WHY NOT TR1 FUNCTION/BIND? +// +// Direct use of tr1::function and tr1::bind was considered, but ultimately +// rejected because of the number of copy constructors invocations involved +// in the binding of arguments during construction, and the forwarding of +// arguments during invocation. These copies will no longer be an issue in +// C++0x because C++0x will support rvalue reference allowing for the compiler +// to avoid these copies. However, waiting for C++0x is not an option. +// +// Measured with valgrind on gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), the +// tr1::bind call itself will invoke a non-trivial copy constructor three times +// for each bound parameter. Also, each when passing a tr1::function, each +// bound argument will be copied again. +// +// In addition to the copies taken at binding and invocation, copying a +// tr1::function causes a copy to be made of all the bound parameters and +// state. +// +// Furthermore, in Chromium, it is desirable for the Callback to take a +// reference on a target object when representing a class method call. This +// is not supported by tr1. +// +// Lastly, tr1::function and tr1::bind has a more general and flexible API. +// This includes things like argument reordering by use of +// tr1::bind::placeholder, support for non-const reference parameters, and some +// limited amount of subtyping of the tr1::function object (eg., +// tr1::function<int(int)> is convertible to tr1::function<void(int)>). +// +// These are not features that are required in Chromium. Some of them, such as +// allowing for reference parameters, and subtyping of functions, may actually +// because a source of errors. Removing support for these features actually +// allows for a simpler implementation, and a terser Currying API. +// +// +// WHY NOT GOOGLE CALLBACKS? +// +// The Google callback system also does not support refcounting. Furthermore, +// its implementation has a number of strange edge cases with respect to type +// conversion of its arguments. In particular, the argument's constness must +// at times match exactly the function signature, or the type-inference might +// break. Given the above, writing a custom solution was easier. +// +// +// MISSING FUNCTIONALITY +// - Invoking the return of Bind. Bind(&foo).Run() does not work; +// - Binding arrays to functions that take a non-const pointer. +// Example: +// void Foo(const char* ptr); +// void Bar(char* ptr); +// Bind(&Foo, "test"); +// Bind(&Bar, "test"); // This fails because ptr is not const. + +namespace base { + +// First, we forward declare the Callback class template. This informs the +// compiler that the template only has 1 type parameter which is the function +// signature that the Callback is representing. +// +// After this, create template specializations for 0-6 parameters. Note that +// even though the template typelist grows, the specialization still +// only has one type: the function signature. +template <typename Sig> +class Callback; + +template <typename R> +class Callback<R(void)> { public: - typedef Params TupleType; - - virtual ~CallbackRunner() {} - virtual void RunWithParams(const Params& params) = 0; - - // Convenience functions so callers don't have to deal with Tuples. - inline void Run() { - RunWithParams(Tuple0()); + typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*); + + Callback() : polymorphic_invoke_(NULL) { } + + // We pass InvokerStorageHolder by const ref to avoid incurring an + // unnecessary AddRef/Unref pair even though we will modify the object. + // We cannot use a normal reference because the compiler will warn + // since this is often used on a return value, which is a temporary. + // + // Note that this constructor CANNOT be explicit, and that Bind() CANNOT + // 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_); } - template <typename Arg1> - inline void Run(const Arg1& a) { - RunWithParams(Params(a)); + R Run(void) const { + return polymorphic_invoke_(invoker_storage_.get()); } - template <typename Arg1, typename Arg2> - inline void Run(const Arg1& a, const Arg2& b) { - RunWithParams(Params(a, b)); - } + private: + scoped_refptr<internal::InvokerStorageBase> invoker_storage_; + PolymorphicInvoke polymorphic_invoke_; +}; - template <typename Arg1, typename Arg2, typename Arg3> - inline void Run(const Arg1& a, const Arg2& b, const Arg3& c) { - RunWithParams(Params(a, b, c)); +template <typename R, typename A1> +class Callback<R(A1)> { + public: + typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&); + + Callback() : polymorphic_invoke_(NULL) { } + + // We pass InvokerStorageHolder by const ref to avoid incurring an + // unnecessary AddRef/Unref pair even though we will modify the object. + // We cannot use a normal reference because the compiler will warn + // since this is often used on a return value, which is a temporary. + // + // Note that this constructor CANNOT be explicit, and that Bind() CANNOT + // 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_); } - template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> - inline void Run(const Arg1& a, const Arg2& b, const Arg3& c, const Arg4& d) { - RunWithParams(Params(a, b, c, d)); + R Run(const A1& a1) const { + return polymorphic_invoke_(invoker_storage_.get(), a1); } - template <typename Arg1, typename Arg2, typename Arg3, - typename Arg4, typename Arg5> - inline void Run(const Arg1& a, const Arg2& b, const Arg3& c, - const Arg4& d, const Arg5& e) { - RunWithParams(Params(a, b, c, d, e)); - } + private: + scoped_refptr<internal::InvokerStorageBase> invoker_storage_; + PolymorphicInvoke polymorphic_invoke_; }; -template <class T, typename Method, typename Params> -class CallbackImpl : public CallbackStorage<T, Method>, - public CallbackRunner<Params> { +template <typename R, typename A1, typename A2> +class Callback<R(A1, A2)> { public: - CallbackImpl(T* obj, Method meth) : CallbackStorage<T, Method>(obj, meth) { + typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, + const A2&); + + Callback() : polymorphic_invoke_(NULL) { } + + // We pass InvokerStorageHolder by const ref to avoid incurring an + // unnecessary AddRef/Unref pair even though we will modify the object. + // We cannot use a normal reference because the compiler will warn + // since this is often used on a return value, which is a temporary. + // + // Note that this constructor CANNOT be explicit, and that Bind() CANNOT + // 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_); } - virtual void RunWithParams(const Params& params) { - // use "this->" to force C++ to look inside our templatized base class; see - // Effective C++, 3rd Ed, item 43, p210 for details. - DispatchToMethod(this->obj_, this->meth_, params); + + R Run(const A1& a1, + const A2& a2) const { + return polymorphic_invoke_(invoker_storage_.get(), a1, + a2); } -}; -// 0-arg implementation -struct Callback0 { - typedef CallbackRunner<Tuple0> Type; + private: + scoped_refptr<internal::InvokerStorageBase> invoker_storage_; + PolymorphicInvoke polymorphic_invoke_; }; -template <class T> -typename Callback0::Type* NewCallback(T* object, void (T::*method)()) { - return new CallbackImpl<T, void (T::*)(), Tuple0 >(object, method); -} - -// 1-arg implementation -template <typename Arg1> -struct Callback1 { - typedef CallbackRunner<Tuple1<Arg1> > Type; -}; +template <typename R, typename A1, typename A2, typename A3> +class Callback<R(A1, A2, A3)> { + public: + typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, + const A2&, + const A3&); + + Callback() : polymorphic_invoke_(NULL) { } + + // We pass InvokerStorageHolder by const ref to avoid incurring an + // unnecessary AddRef/Unref pair even though we will modify the object. + // We cannot use a normal reference because the compiler will warn + // since this is often used on a return value, which is a temporary. + // + // Note that this constructor CANNOT be explicit, and that Bind() CANNOT + // 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_); + } -template <class T, typename Arg1> -typename Callback1<Arg1>::Type* NewCallback(T* object, - void (T::*method)(Arg1)) { - return new CallbackImpl<T, void (T::*)(Arg1), Tuple1<Arg1> >(object, method); -} + R Run(const A1& a1, + const A2& a2, + const A3& a3) const { + return polymorphic_invoke_(invoker_storage_.get(), a1, + a2, + a3); + } -// 2-arg implementation -template <typename Arg1, typename Arg2> -struct Callback2 { - typedef CallbackRunner<Tuple2<Arg1, Arg2> > Type; + private: + scoped_refptr<internal::InvokerStorageBase> invoker_storage_; + PolymorphicInvoke polymorphic_invoke_; }; -template <class T, typename Arg1, typename Arg2> -typename Callback2<Arg1, Arg2>::Type* NewCallback( - T* object, - void (T::*method)(Arg1, Arg2)) { - return new CallbackImpl<T, void (T::*)(Arg1, Arg2), - Tuple2<Arg1, Arg2> >(object, method); -} - -// 3-arg implementation -template <typename Arg1, typename Arg2, typename Arg3> -struct Callback3 { - typedef CallbackRunner<Tuple3<Arg1, Arg2, Arg3> > Type; -}; +template <typename R, typename A1, typename A2, typename A3, typename A4> +class Callback<R(A1, A2, A3, A4)> { + public: + typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, + const A2&, + const A3&, + const A4&); + + Callback() : polymorphic_invoke_(NULL) { } + + // We pass InvokerStorageHolder by const ref to avoid incurring an + // unnecessary AddRef/Unref pair even though we will modify the object. + // We cannot use a normal reference because the compiler will warn + // since this is often used on a return value, which is a temporary. + // + // Note that this constructor CANNOT be explicit, and that Bind() CANNOT + // 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_); + } -template <class T, typename Arg1, typename Arg2, typename Arg3> -typename Callback3<Arg1, Arg2, Arg3>::Type* NewCallback( - T* object, - void (T::*method)(Arg1, Arg2, Arg3)) { - return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3), - Tuple3<Arg1, Arg2, Arg3> >(object, method); -} - -// 4-arg implementation -template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> -struct Callback4 { - typedef CallbackRunner<Tuple4<Arg1, Arg2, Arg3, Arg4> > Type; -}; + 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); + } -template <class T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> -typename Callback4<Arg1, Arg2, Arg3, Arg4>::Type* NewCallback( - T* object, - void (T::*method)(Arg1, Arg2, Arg3, Arg4)) { - return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3, Arg4), - Tuple4<Arg1, Arg2, Arg3, Arg4> >(object, method); -} - -// 5-arg implementation -template <typename Arg1, typename Arg2, typename Arg3, - typename Arg4, typename Arg5> -struct Callback5 { - typedef CallbackRunner<Tuple5<Arg1, Arg2, Arg3, Arg4, Arg5> > Type; + private: + scoped_refptr<internal::InvokerStorageBase> invoker_storage_; + PolymorphicInvoke polymorphic_invoke_; }; -template <class T, typename Arg1, typename Arg2, - typename Arg3, typename Arg4, typename Arg5> -typename Callback5<Arg1, Arg2, Arg3, Arg4, Arg5>::Type* NewCallback( - T* object, - void (T::*method)(Arg1, Arg2, Arg3, Arg4, Arg5)) { - return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3, Arg4, Arg5), - Tuple5<Arg1, Arg2, Arg3, Arg4, Arg5> >(object, method); -} - -// An UnboundMethod is a wrapper for a method where the actual object is -// provided at Run dispatch time. -template <class T, class Method, class Params> -class UnboundMethod { +template <typename R, typename A1, typename A2, typename A3, typename A4, + typename A5> +class Callback<R(A1, A2, A3, A4, A5)> { public: - UnboundMethod(Method m, const Params& p) : m_(m), p_(p) { - COMPILE_ASSERT( - (base::internal::ParamsUseScopedRefptrCorrectly<Params>::value), - badunboundmethodparams); + typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, + const A2&, + const A3&, + const A4&, + const A5&); + + Callback() : polymorphic_invoke_(NULL) { } + + // We pass InvokerStorageHolder by const ref to avoid incurring an + // unnecessary AddRef/Unref pair even though we will modify the object. + // We cannot use a normal reference because the compiler will warn + // since this is often used on a return value, which is a temporary. + // + // Note that this constructor CANNOT be explicit, and that Bind() CANNOT + // 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_); } - void Run(T* obj) const { - DispatchToMethod(obj, m_, p_); + + R Run(const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5) const { + return polymorphic_invoke_(invoker_storage_.get(), a1, + a2, + a3, + a4, + a5); } - private: - Method m_; - Params p_; -}; -// Return value implementation with no args. -template <typename ReturnValue> -struct CallbackWithReturnValue { - class Type { - public: - virtual ~Type() {} - virtual ReturnValue Run() = 0; - }; + private: + scoped_refptr<internal::InvokerStorageBase> invoker_storage_; + PolymorphicInvoke polymorphic_invoke_; }; -template <class T, typename Method, typename ReturnValue> -class CallbackWithReturnValueImpl - : public CallbackStorage<T, Method>, - public CallbackWithReturnValue<ReturnValue>::Type { +template <typename R, typename A1, typename A2, typename A3, typename A4, + typename A5, typename A6> +class Callback<R(A1, A2, A3, A4, A5, A6)> { public: - CallbackWithReturnValueImpl(T* obj, Method meth) - : CallbackStorage<T, Method>(obj, meth) {} + typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*, const A1&, + const A2&, + const A3&, + const A4&, + const A5&, + const A6&); + + Callback() : polymorphic_invoke_(NULL) { } + + // We pass InvokerStorageHolder by const ref to avoid incurring an + // unnecessary AddRef/Unref pair even though we will modify the object. + // We cannot use a normal reference because the compiler will warn + // since this is often used on a return value, which is a temporary. + // + // Note that this constructor CANNOT be explicit, and that Bind() CANNOT + // 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_); + } - virtual ReturnValue Run() { - return (this->obj_->*(this->meth_))(); + R Run(const A1& a1, + const A2& a2, + const A3& a3, + const A4& a4, + const A5& a5, + const A6& a6) const { + return polymorphic_invoke_(invoker_storage_.get(), a1, + a2, + a3, + a4, + a5, + a6); } - protected: - virtual ~CallbackWithReturnValueImpl() {} + private: + scoped_refptr<internal::InvokerStorageBase> invoker_storage_; + PolymorphicInvoke polymorphic_invoke_; }; -template <class T, typename ReturnValue> -typename CallbackWithReturnValue<ReturnValue>::Type* -NewCallbackWithReturnValue(T* object, ReturnValue (T::*method)()) { - return new CallbackWithReturnValueImpl<T, ReturnValue (T::*)(), ReturnValue>( - object, method); -} + +// Syntactic sugar to make Callbacks<void(void)> easier to declare since it +// will be used in a lot of APIs with delayed execution. +typedef Callback<void(void)> Closure; + +} // namespace base #endif // BASE_CALLBACK_H diff --git a/base/callback.h.pump b/base/callback.h.pump new file mode 100644 index 0000000..9fc4b0b --- /dev/null +++ b/base/callback.h.pump @@ -0,0 +1,291 @@ +$$ 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. + +#ifndef BASE_CALLBACK_H_ +#define BASE_CALLBACK_H_ +#pragma once + +#include "base/callback_helpers.h" +#include "base/callback_old.h" + +// New, super-duper, unified Callback system. This will eventually replace +// NewRunnableMethod, NewRunnableFunction, CreateFunctor, and CreateCallback +// systems currently in the Chromium code base. +// +// WHAT IS THIS: +// +// The templated Callback class is a generalized function object. Together +// with the Bind() function in bind.h, they provide a type-safe method for +// performing currying of arguments, and creating a "closure." +// +// In programing languages, a closure is a first-class function where all its +// parameters have been bound (usually via currying). Closures are well +// suited for representing, and passing around a unit of delayed execution. +// They are used in Chromium code to schedule tasks on different MessageLoops. +// +// +// MEMORY MANAGEMENT AND PASSING +// +// The Callback objects themselves should be passed by const-reference, and +// stored by copy. They internally store their state via a refcounted class +// and thus do not need to be deleted. +// +// The reason to pass via a const-reference is to avoid unnecessary +// AddRef/Release pairs to the internal state. +// +// +// EXAMPLE USAGE: +// +// /* Binding a normal function. */ +// int Return5() { return 5; } +// base::Callback<int(int)> func_cb = base::Bind(&Return5); +// LOG(INFO) << func_cb.Run(5); // Prints 5. +// +// void PrintHi() { LOG(INFO) << "hi."; } +// base::Closure void_func_cb = base::Bind(&PrintHi); +// LOG(INFO) << void_func_cb.Run(); // Prints: hi. +// +// /* Binding a class method. */ +// class Ref : public RefCountedThreadSafe<Ref> { +// public: +// int Foo() { return 3; } +// void PrintBye() { LOG(INFO) << "bye."; } +// }; +// scoped_refptr<Ref> ref = new Ref(); +// base::Callback<int(void)> ref_cb = base::Bind(&Ref::Foo, ref.get()); +// LOG(INFO) << ref_cb.Run(); // Prints out 3. +// +// base::Closure void_ref_cb = base::Bind(&Ref::PrintBye, ref.get()); +// void_ref_cb.Run(); // Prints: bye. +// +// /* Binding a class method in a non-refcounted class. +// * +// * WARNING: You must be sure the referee outlives the callback! +// * This is particularly important if you post a closure to a +// * MessageLoop because then it becomes hard to know what the +// * lifetime of the referee needs to be. +// */ +// class NoRef { +// public: +// int Foo() { return 4; } +// void PrintWhy() { LOG(INFO) << "why???"; } +// }; +// NoRef no_ref; +// base::Callback<int(void)> base::no_ref_cb = +// base::Bind(&NoRef::Foo, base::Unretained(&no_ref)); +// LOG(INFO) << ref_cb.Run(); // Prints out 4. +// +// base::Closure void_no_ref_cb = +// base::Bind(&NoRef::PrintWhy, base::Unretained(no_ref)); +// void_no_ref_cb.Run(); // Prints: why??? +// +// /* Binding a reference. */ +// int Identity(int n) { return n; } +// int value = 1; +// base::Callback<int(void)> bound_copy_cb = base::Bind(&Identity, value); +// base::Callback<int(void)> bound_ref_cb = +// base::Bind(&Identity, base::ConstRef(value)); +// LOG(INFO) << bound_copy_cb.Run(); // Prints 1. +// LOG(INFO) << bound_ref_cb.Run(); // Prints 1. +// value = 2; +// LOG(INFO) << bound_copy_cb.Run(); // Prints 1. +// LOG(INFO) << bound_ref_cb.Run(); // Prints 2. +// +// +// WHERE IS THIS DESIGN FROM: +// +// The design Callback and Bind is heavily influenced by C++'s +// tr1::function/tr1::bind, and by the "Google Callback" system used inside +// Google. +// +// +// HOW THE IMPLEMENTATION WORKS: +// +// There are three main components to the system: +// 1) The Callback classes. +// 2) The Bind() functions. +// 3) The arguments wrappers (eg., Unretained() and ConstRef()). +// +// The Callback classes represent a generic function pointer. Internally, +// it stores a refcounted piece of state that represents the target function +// and all its bound parameters. Each Callback specialization has a templated +// constructor that takes an InvokerStorageHolder<> object. In the context of +// the constructor, the static type of this InvokerStorageHolder<> object +// uniquely identifies the function it is representing, all its bound +// parameters, and a DoInvoke() that is capable of invoking the target. +// +// Callback's constructor is takes the InvokerStorageHolder<> that has the +// full static type and erases the target function type, and the bound +// parameters. It does this by storing a pointer to the specific DoInvoke() +// function, and upcasting the state of InvokerStorageHolder<> to a +// InvokerStorageBase. This is safe as long as this InvokerStorageBase pointer +// is only used with the stored DoInvoke() pointer. +// +// To create InvokerStorageHolder<> objects, we use the Bind() functions. +// These functions, along with a set of internal templates, are reponsible for +// +// - Unwrapping the function signature into return type, and parameters +// - Determining the number of parameters that are bound +// - Creating the storage for the bound parameters +// - Performing compile-time asserts to avoid error-prone behavior +// - Returning an InvokerStorageHolder<> with an DoInvoke() that has an arity +// matching the number of unbound parameters, and knows the correct +// refcounting semantics for the target object if we are binding a class +// method. +// +// The Bind functions do the above using type-inference, and template +// specializations. +// +// By default Bind() will store copies of all bound parameters, and attempt +// to refcount a target object if the function being bound is a class method. +// +// To change this behavior, we introduce a set of argument wrappers +// (eg. Unretained(), and ConstRef()). These are simple container templates +// that are passed by value, and wrap a pointer to argument. See the +// file-level comment in base/bind_helpers.h for more info. +// +// These types are passed to the Unwrap() functions, and the MaybeRefcount() +// functions respectively to modify the behavior of Bind(). The Unwrap() +// and MaybeRefcount() functions change behavior by doing partial +// specialization based on whether or not a parameter is a wrapper type. +// +// ConstRef() is similar to tr1::cref. Unretained() is specific to Chromium. +// +// +// WHY NOT TR1 FUNCTION/BIND? +// +// Direct use of tr1::function and tr1::bind was considered, but ultimately +// rejected because of the number of copy constructors invocations involved +// in the binding of arguments during construction, and the forwarding of +// arguments during invocation. These copies will no longer be an issue in +// C++0x because C++0x will support rvalue reference allowing for the compiler +// to avoid these copies. However, waiting for C++0x is not an option. +// +// Measured with valgrind on gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), the +// tr1::bind call itself will invoke a non-trivial copy constructor three times +// for each bound parameter. Also, each when passing a tr1::function, each +// bound argument will be copied again. +// +// In addition to the copies taken at binding and invocation, copying a +// tr1::function causes a copy to be made of all the bound parameters and +// state. +// +// Furthermore, in Chromium, it is desirable for the Callback to take a +// reference on a target object when representing a class method call. This +// is not supported by tr1. +// +// Lastly, tr1::function and tr1::bind has a more general and flexible API. +// This includes things like argument reordering by use of +// tr1::bind::placeholder, support for non-const reference parameters, and some +// limited amount of subtyping of the tr1::function object (eg., +// tr1::function<int(int)> is convertible to tr1::function<void(int)>). +// +// These are not features that are required in Chromium. Some of them, such as +// allowing for reference parameters, and subtyping of functions, may actually +// because a source of errors. Removing support for these features actually +// allows for a simpler implementation, and a terser Currying API. +// +// +// WHY NOT GOOGLE CALLBACKS? +// +// The Google callback system also does not support refcounting. Furthermore, +// its implementation has a number of strange edge cases with respect to type +// conversion of its arguments. In particular, the argument's constness must +// at times match exactly the function signature, or the type-inference might +// break. Given the above, writing a custom solution was easier. +// +// +// MISSING FUNCTIONALITY +// - Invoking the return of Bind. Bind(&foo).Run() does not work; +// - Binding arrays to functions that take a non-const pointer. +// Example: +// void Foo(const char* ptr); +// void Bar(char* ptr); +// Bind(&Foo, "test"); +// Bind(&Bar, "test"); // This fails because ptr is not const. + +namespace base { + +// First, we forward declare the Callback class template. This informs the +// compiler that the template only has 1 type parameter which is the function +// signature that the Callback is representing. +// +// After this, create template specializations for 0-$(MAX_ARITY) parameters. Note that +// even though the template typelist grows, the specialization still +// only has one type: the function signature. +template <typename Sig> +class Callback; + + +$range ARITY 0..MAX_ARITY +$for ARITY [[ +$range ARG 1..ARITY + +$if ARITY == 0 [[ +template <typename R> +class Callback<R(void)> { +]] $else [[ +template <typename R, $for ARG , [[typename A$(ARG)]]> +class Callback<R($for ARG , [[A$(ARG)]])> { +]] + + public: + typedef R(*PolymorphicInvoke)(internal::InvokerStorageBase*[[]] +$if ARITY != 0 [[, ]] +$for ARG , + [[const A$(ARG)&]]); + + Callback() : polymorphic_invoke_(NULL) { } + + // We pass InvokerStorageHolder by const ref to avoid incurring an + // unnecessary AddRef/Unref pair even though we will modify the object. + // We cannot use a normal reference because the compiler will warn + // since this is often used on a return value, which is a temporary. + // + // Note that this constructor CANNOT be explicit, and that Bind() CANNOT + // 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_); + } + + +$if ARITY == 0 [[ + R Run(void) const { +]] $else [[ + R Run($for ARG , + [[const A$(ARG)& a$(ARG)]]) const { +]] + + return polymorphic_invoke_(invoker_storage_.get()[[]] +$if ARITY != 0 [[, ]] +$for ARG , + [[a$(ARG)]]); + } + + private: + scoped_refptr<internal::InvokerStorageBase> invoker_storage_; + PolymorphicInvoke polymorphic_invoke_; +}; + + +]] $$ for ARITY + +// Syntactic sugar to make Callbacks<void(void)> easier to declare since it +// will be used in a lot of APIs with delayed execution. +typedef Callback<void(void)> Closure; + +} // namespace base + +#endif // BASE_CALLBACK_H diff --git a/base/callback_helpers.h b/base/callback_helpers.h new file mode 100644 index 0000000..86b0df1 --- /dev/null +++ b/base/callback_helpers.h @@ -0,0 +1,55 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file 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_ +#pragma once + +#include "base/ref_counted.h" + +namespace base { +namespace internal { + +// InvokerStorageBase is used to provide an opaque handle that the Callback +// class can use to represent a function object with bound arguments. It +// behaves as an existential type that is used by a corresponding +// DoInvoke function to perform the function execution. This allows +// us to shield the Callback class from the types of the bound argument via +// "type erasure." +class InvokerStorageBase : public RefCountedThreadSafe<InvokerStorageBase> { + protected: + friend class RefCountedThreadSafe<InvokerStorageBase>; + virtual ~InvokerStorageBase() {} +}; + +// This structure exists purely to pass the returned |invoker_storage_| from +// Bind() to Callback while avoiding an extra AddRef/Release() pair. +// +// To do this, the constructor of Callback<> must take a const-ref. The +// reference must be to a const object otherwise the compiler will emit a +// warning about taking a reference to a temporary. +// +// Unfortunately, this means that the internal |invoker_storage_| field must +// be made mutable. +template <typename T> +struct InvokerStorageHolder { + explicit InvokerStorageHolder(T* invoker_storage) + : invoker_storage_(invoker_storage) { + } + + mutable scoped_refptr<InvokerStorageBase> invoker_storage_; +}; + +template <typename T> +InvokerStorageHolder<T> MakeInvokerStorageHolder(T* o) { + return InvokerStorageHolder<T>(o); +} + +} // namespace internal +} // namespace base + +#endif // BASE_CALLBACK_HELPERS_H_ diff --git a/base/callback_old.h b/base/callback_old.h new file mode 100644 index 0000000..ab3927d --- /dev/null +++ b/base/callback_old.h @@ -0,0 +1,254 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CALLBACK_OLD_H_ +#define BASE_CALLBACK_OLD_H_ +#pragma once + +#include "base/tuple.h" +#include "base/raw_scoped_refptr_mismatch_checker.h" + +// Callback -------------------------------------------------------------------- +// +// A Callback is like a Task but with unbound parameters. It is basically an +// object-oriented function pointer. +// +// Callbacks are designed to work with Tuples. A set of helper functions and +// classes is provided to hide the Tuple details from the consumer. Client +// code will generally work with the CallbackRunner base class, which merely +// provides a Run method and is returned by the New* functions. This allows +// users to not care which type of class implements the callback, only that it +// has a certain number and type of arguments. +// +// The implementation of this is done by CallbackImpl, which inherits +// CallbackStorage to store the data. This allows the storage of the data +// (requiring the class type T) to be hidden from users, who will want to call +// this regardless of the implementor's type T. +// +// Note that callbacks currently have no facility for cancelling or abandoning +// them. We currently handle this at a higher level for cases where this is +// necessary. The pointer in a callback must remain valid until the callback +// is made. +// +// Like Task, the callback executor is responsible for deleting the callback +// pointer once the callback has executed. +// +// Example client usage: +// void Object::DoStuff(int, string); +// Callback2<int, string>::Type* callback = +// NewCallback(obj, &Object::DoStuff); +// callback->Run(5, string("hello")); +// delete callback; +// or, equivalently, using tuples directly: +// CallbackRunner<Tuple2<int, string> >* callback = +// NewCallback(obj, &Object::DoStuff); +// callback->RunWithParams(MakeTuple(5, string("hello"))); +// +// There is also a 0-args version that returns a value. Example: +// int Object::GetNextInt(); +// CallbackWithReturnValue<int>::Type* callback = +// NewCallbackWithReturnValue(obj, &Object::GetNextInt); +// int next_int = callback->Run(); +// delete callback; + +// Base for all Callbacks that handles storage of the pointers. +template <class T, typename Method> +class CallbackStorage { + public: + CallbackStorage(T* obj, Method meth) : obj_(obj), meth_(meth) { + } + + protected: + T* obj_; + Method meth_; +}; + +// Interface that is exposed to the consumer, that does the actual calling +// of the method. +template <typename Params> +class CallbackRunner { + public: + typedef Params TupleType; + + virtual ~CallbackRunner() {} + virtual void RunWithParams(const Params& params) = 0; + + // Convenience functions so callers don't have to deal with Tuples. + inline void Run() { + RunWithParams(Tuple0()); + } + + template <typename Arg1> + inline void Run(const Arg1& a) { + RunWithParams(Params(a)); + } + + template <typename Arg1, typename Arg2> + inline void Run(const Arg1& a, const Arg2& b) { + RunWithParams(Params(a, b)); + } + + template <typename Arg1, typename Arg2, typename Arg3> + inline void Run(const Arg1& a, const Arg2& b, const Arg3& c) { + RunWithParams(Params(a, b, c)); + } + + template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> + inline void Run(const Arg1& a, const Arg2& b, const Arg3& c, const Arg4& d) { + RunWithParams(Params(a, b, c, d)); + } + + template <typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> + inline void Run(const Arg1& a, const Arg2& b, const Arg3& c, + const Arg4& d, const Arg5& e) { + RunWithParams(Params(a, b, c, d, e)); + } +}; + +template <class T, typename Method, typename Params> +class CallbackImpl : public CallbackStorage<T, Method>, + public CallbackRunner<Params> { + public: + CallbackImpl(T* obj, Method meth) : CallbackStorage<T, Method>(obj, meth) { + } + virtual void RunWithParams(const Params& params) { + // use "this->" to force C++ to look inside our templatized base class; see + // Effective C++, 3rd Ed, item 43, p210 for details. + DispatchToMethod(this->obj_, this->meth_, params); + } +}; + +// 0-arg implementation +struct Callback0 { + typedef CallbackRunner<Tuple0> Type; +}; + +template <class T> +typename Callback0::Type* NewCallback(T* object, void (T::*method)()) { + return new CallbackImpl<T, void (T::*)(), Tuple0 >(object, method); +} + +// 1-arg implementation +template <typename Arg1> +struct Callback1 { + typedef CallbackRunner<Tuple1<Arg1> > Type; +}; + +template <class T, typename Arg1> +typename Callback1<Arg1>::Type* NewCallback(T* object, + void (T::*method)(Arg1)) { + return new CallbackImpl<T, void (T::*)(Arg1), Tuple1<Arg1> >(object, method); +} + +// 2-arg implementation +template <typename Arg1, typename Arg2> +struct Callback2 { + typedef CallbackRunner<Tuple2<Arg1, Arg2> > Type; +}; + +template <class T, typename Arg1, typename Arg2> +typename Callback2<Arg1, Arg2>::Type* NewCallback( + T* object, + void (T::*method)(Arg1, Arg2)) { + return new CallbackImpl<T, void (T::*)(Arg1, Arg2), + Tuple2<Arg1, Arg2> >(object, method); +} + +// 3-arg implementation +template <typename Arg1, typename Arg2, typename Arg3> +struct Callback3 { + typedef CallbackRunner<Tuple3<Arg1, Arg2, Arg3> > Type; +}; + +template <class T, typename Arg1, typename Arg2, typename Arg3> +typename Callback3<Arg1, Arg2, Arg3>::Type* NewCallback( + T* object, + void (T::*method)(Arg1, Arg2, Arg3)) { + return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3), + Tuple3<Arg1, Arg2, Arg3> >(object, method); +} + +// 4-arg implementation +template <typename Arg1, typename Arg2, typename Arg3, typename Arg4> +struct Callback4 { + typedef CallbackRunner<Tuple4<Arg1, Arg2, Arg3, Arg4> > Type; +}; + +template <class T, typename Arg1, typename Arg2, typename Arg3, typename Arg4> +typename Callback4<Arg1, Arg2, Arg3, Arg4>::Type* NewCallback( + T* object, + void (T::*method)(Arg1, Arg2, Arg3, Arg4)) { + return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3, Arg4), + Tuple4<Arg1, Arg2, Arg3, Arg4> >(object, method); +} + +// 5-arg implementation +template <typename Arg1, typename Arg2, typename Arg3, + typename Arg4, typename Arg5> +struct Callback5 { + typedef CallbackRunner<Tuple5<Arg1, Arg2, Arg3, Arg4, Arg5> > Type; +}; + +template <class T, typename Arg1, typename Arg2, + typename Arg3, typename Arg4, typename Arg5> +typename Callback5<Arg1, Arg2, Arg3, Arg4, Arg5>::Type* NewCallback( + T* object, + void (T::*method)(Arg1, Arg2, Arg3, Arg4, Arg5)) { + return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3, Arg4, Arg5), + Tuple5<Arg1, Arg2, Arg3, Arg4, Arg5> >(object, method); +} + +// An UnboundMethod is a wrapper for a method where the actual object is +// provided at Run dispatch time. +template <class T, class Method, class Params> +class UnboundMethod { + public: + UnboundMethod(Method m, const Params& p) : m_(m), p_(p) { + COMPILE_ASSERT( + (base::internal::ParamsUseScopedRefptrCorrectly<Params>::value), + badunboundmethodparams); + } + void Run(T* obj) const { + DispatchToMethod(obj, m_, p_); + } + private: + Method m_; + Params p_; +}; + +// Return value implementation with no args. +template <typename ReturnValue> +struct CallbackWithReturnValue { + class Type { + public: + virtual ~Type() {} + virtual ReturnValue Run() = 0; + }; +}; + +template <class T, typename Method, typename ReturnValue> +class CallbackWithReturnValueImpl + : public CallbackStorage<T, Method>, + public CallbackWithReturnValue<ReturnValue>::Type { + public: + CallbackWithReturnValueImpl(T* obj, Method meth) + : CallbackStorage<T, Method>(obj, meth) {} + + virtual ReturnValue Run() { + return (this->obj_->*(this->meth_))(); + } + + protected: + virtual ~CallbackWithReturnValueImpl() {} +}; + +template <class T, typename ReturnValue> +typename CallbackWithReturnValue<ReturnValue>::Type* +NewCallbackWithReturnValue(T* object, ReturnValue (T::*method)()) { + return new CallbackWithReturnValueImpl<T, ReturnValue (T::*)(), ReturnValue>( + object, method); +} + +#endif // BASE_CALLBACK_OLD_H_ diff --git a/base/compiler_specific.h b/base/compiler_specific.h index 017b869..3060306 100644 --- a/base/compiler_specific.h +++ b/base/compiler_specific.h @@ -85,7 +85,7 @@ #if defined(COMPILER_MSVC) #define OVERRIDE override #elif defined(__clang__) -#define OVERRIDE __attribute__((override)) +#define OVERRIDE override #else #define OVERRIDE #endif diff --git a/base/crypto/capi_util.cc b/base/crypto/capi_util.cc index cf47a50..ef57a3c 100644 --- a/base/crypto/capi_util.cc +++ b/base/crypto/capi_util.cc @@ -5,8 +5,8 @@ #include "base/crypto/capi_util.h" #include "base/basictypes.h" -#include "base/lock.h" #include "base/singleton.h" +#include "base/synchronization/lock.h" namespace { @@ -18,7 +18,7 @@ class CAPIUtilSingleton { // Returns a lock to guard calls to CryptAcquireContext with // CRYPT_DELETEKEYSET or CRYPT_NEWKEYSET. - Lock& acquire_context_lock() { + base::Lock& acquire_context_lock() { return acquire_context_lock_; } @@ -28,7 +28,7 @@ class CAPIUtilSingleton { CAPIUtilSingleton() {} - Lock acquire_context_lock_; + base::Lock acquire_context_lock_; DISALLOW_COPY_AND_ASSIGN(CAPIUtilSingleton); }; @@ -43,7 +43,7 @@ BOOL CryptAcquireContextLocked(HCRYPTPROV* prov, DWORD prov_type, DWORD flags) { - AutoLock lock(CAPIUtilSingleton::GetInstance()->acquire_context_lock()); + base::AutoLock lock(CAPIUtilSingleton::GetInstance()->acquire_context_lock()); return CryptAcquireContext(prov, container, provider, prov_type, flags); } diff --git a/base/crypto/crypto_module_blocking_password_delegate.h b/base/crypto/crypto_module_blocking_password_delegate.h new file mode 100644 index 0000000..ae962a8 --- /dev/null +++ b/base/crypto/crypto_module_blocking_password_delegate.h @@ -0,0 +1,34 @@ +// 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_CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ +#define BASE_CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ +#pragma once + +#include <string> + +namespace base { + +// PK11_SetPasswordFunc is a global setting. An implementation of +// CryptoModuleBlockingPasswordDelegate should be passed as the user data +// argument (|wincx|) to relevant NSS functions, which the global password +// handler will call to do the actual work. +class CryptoModuleBlockingPasswordDelegate { + public: + virtual ~CryptoModuleBlockingPasswordDelegate() {} + + // Requests a password to unlock |slot_name|. The interface is + // synchronous because NSS cannot issue an asynchronous + // request. |retry| is true if this is a request for the retry + // and we previously returned the wrong password. + // The implementation should set |*cancelled| to true if the user cancelled + // instead of entering a password, otherwise it should return the password the + // user entered. + virtual std::string RequestPassword(const std::string& slot_name, bool retry, + bool* cancelled) = 0; +}; + +} + +#endif // BASE_CRYPTO_CRYPTO_MODULE_BLOCKING_PASSWORD_DELEGATE_H_ diff --git a/base/crypto/cssm_init.cc b/base/crypto/cssm_init.cc index f588f30..570dcc3 100644 --- a/base/crypto/cssm_init.cc +++ b/base/crypto/cssm_init.cc @@ -7,6 +7,7 @@ #include <Security/SecBase.h> #include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" #include "base/singleton.h" #include "base/synchronization/lock.h" #include "base/sys_string_conversions.h" @@ -20,16 +21,38 @@ namespace { +void* CSSMMalloc(CSSM_SIZE size, void* alloc_ref) { + return malloc(size); +} + +void CSSMFree(void* mem_ptr, void* alloc_ref) { + free(mem_ptr); +} + +void* CSSMRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) { + return realloc(ptr, size); +} + +void* CSSMCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) { + return calloc(num, size); +} + class CSSMInitSingleton { public: static CSSMInitSingleton* GetInstance() { - return Singleton<CSSMInitSingleton>::get(); + return Singleton<CSSMInitSingleton, + LeakySingletonTraits<CSSMInitSingleton> >::get(); } - CSSM_CSP_HANDLE csp_handle() const {return csp_handle_;} + CSSM_CSP_HANDLE csp_handle() const { return csp_handle_; } + CSSM_CL_HANDLE cl_handle() const { return cl_handle_; } + CSSM_TP_HANDLE tp_handle() const { return tp_handle_; } private: - CSSMInitSingleton() : inited_(false), loaded_(false), csp_handle_(NULL) { + CSSMInitSingleton() + : inited_(false), csp_loaded_(false), cl_loaded_(false), + tp_loaded_(false), csp_handle_(NULL), cl_handle_(NULL), + tp_handle_(NULL) { static CSSM_VERSION version = {2, 0}; // TODO(wtc): what should our caller GUID be? static const CSSM_GUID test_guid = { @@ -50,13 +73,42 @@ class CSSMInitSingleton { NOTREACHED(); return; } - loaded_ = true; + csp_loaded_ = true; + crtn = CSSM_ModuleLoad( + &gGuidAppleX509CL, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + cl_loaded_ = true; + crtn = CSSM_ModuleLoad( + &gGuidAppleX509TP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + tp_loaded_ = true; + + const CSSM_API_MEMORY_FUNCS cssmMemoryFunctions = { + CSSMMalloc, + CSSMFree, + CSSMRealloc, + CSSMCalloc, + NULL + }; - crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, - &base::kCssmMemoryFunctions, 0, + crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, &cssmMemoryFunctions, 0, CSSM_SERVICE_CSP, 0, CSSM_KEY_HIERARCHY_NONE, NULL, 0, NULL, &csp_handle_); DCHECK(crtn == CSSM_OK); + crtn = CSSM_ModuleAttach(&gGuidAppleX509CL, &version, &cssmMemoryFunctions, + 0, CSSM_SERVICE_CL, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &cl_handle_); + DCHECK(crtn == CSSM_OK); + crtn = CSSM_ModuleAttach(&gGuidAppleX509TP, &version, &cssmMemoryFunctions, + 0, CSSM_SERVICE_TP, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &tp_handle_); + DCHECK(crtn == CSSM_OK); } ~CSSMInitSingleton() { @@ -65,10 +117,26 @@ class CSSMInitSingleton { CSSM_RETURN crtn = CSSM_ModuleDetach(csp_handle_); DCHECK(crtn == CSSM_OK); } - if (loaded_) { + if (cl_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(cl_handle_); + DCHECK(crtn == CSSM_OK); + } + if (tp_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(tp_handle_); + DCHECK(crtn == CSSM_OK); + } + if (csp_loaded_) { crtn = CSSM_ModuleUnload(&gGuidAppleCSP, NULL, NULL); DCHECK(crtn == CSSM_OK); } + if (cl_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleX509CL, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } + if (tp_loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleX509TP, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } if (inited_) { crtn = CSSM_Terminate(); DCHECK(crtn == CSSM_OK); @@ -76,8 +144,12 @@ class CSSMInitSingleton { } bool inited_; // True if CSSM_Init has been called successfully. - bool loaded_; // True if CSSM_ModuleLoad has been called successfully. + bool csp_loaded_; // True if gGuidAppleCSP has been loaded + bool cl_loaded_; // True if gGuidAppleX509CL has been loaded. + bool tp_loaded_; // True if gGuidAppleX509TP has been loaded. CSSM_CSP_HANDLE csp_handle_; + CSSM_CL_HANDLE cl_handle_; + CSSM_TP_HANDLE tp_handle_; friend struct DefaultSingletonTraits<CSSMInitSingleton>; }; @@ -87,18 +159,17 @@ class CSSMInitSingleton { class SecurityServicesSingleton { public: static SecurityServicesSingleton* GetInstance() { - return Singleton<SecurityServicesSingleton>::get(); + return Singleton<SecurityServicesSingleton, + LeakySingletonTraits<SecurityServicesSingleton> >::get(); } - ~SecurityServicesSingleton() {} - base::Lock& lock() { return lock_; } private: - friend class Singleton<SecurityServicesSingleton>; friend struct DefaultSingletonTraits<SecurityServicesSingleton>; SecurityServicesSingleton() {} + ~SecurityServicesSingleton() {} base::Lock lock_; @@ -117,45 +188,44 @@ CSSM_CSP_HANDLE GetSharedCSPHandle() { return CSSMInitSingleton::GetInstance()->csp_handle(); } -void* CSSMMalloc(CSSM_SIZE size, void *alloc_ref) { - return malloc(size); +CSSM_CL_HANDLE GetSharedCLHandle() { + return CSSMInitSingleton::GetInstance()->cl_handle(); } -void CSSMFree(void* mem_ptr, void* alloc_ref) { - free(mem_ptr); +CSSM_TP_HANDLE GetSharedTPHandle() { + return CSSMInitSingleton::GetInstance()->tp_handle(); } -void* CSSMRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) { - return realloc(ptr, size); +void* CSSMMalloc(CSSM_SIZE size) { + return ::CSSMMalloc(size, NULL); } -void* CSSMCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) { - return calloc(num, size); +void CSSMFree(void* ptr) { + ::CSSMFree(ptr, NULL); } -const CSSM_API_MEMORY_FUNCS kCssmMemoryFunctions = { - CSSMMalloc, - CSSMFree, - CSSMRealloc, - CSSMCalloc, - NULL -}; - -void LogCSSMError(const char *fn_name, CSSM_RETURN err) { +void LogCSSMError(const char* fn_name, CSSM_RETURN err) { if (!err) return; - CFStringRef cfstr = SecCopyErrorMessageString(err, NULL); - if (cfstr) { - std::string err_name = SysCFStringRefToUTF8(cfstr); - CFRelease(cfstr); - LOG(ERROR) << fn_name << " returned " << err << " (" << err_name << ")"; - } else { - LOG(ERROR) << fn_name << " returned " << err; - } + base::mac::ScopedCFTypeRef<CFStringRef> cfstr( + SecCopyErrorMessageString(err, NULL)); + LOG(ERROR) << fn_name << " returned " << err + << " (" << SysCFStringRefToUTF8(cfstr) << ")"; } base::Lock& GetMacSecurityServicesLock() { return SecurityServicesSingleton::GetInstance()->lock(); } +ScopedCSSMData::ScopedCSSMData() { + memset(&data_, 0, sizeof(data_)); +} + +ScopedCSSMData::~ScopedCSSMData() { + if (data_.Data) { + CSSMFree(data_.Data); + data_.Data = NULL; + } +} + } // namespace base diff --git a/base/crypto/cssm_init.h b/base/crypto/cssm_init.h index 5644d7e..b51a3b5 100644 --- a/base/crypto/cssm_init.h +++ b/base/crypto/cssm_init.h @@ -8,7 +8,7 @@ #include <Security/cssm.h> -#include "base/scoped_ptr.h" +#include "base/basictypes.h" namespace base { @@ -22,12 +22,22 @@ void EnsureCSSMInit(); // Returns the shared CSP handle used by CSSM functions. CSSM_CSP_HANDLE GetSharedCSPHandle(); +// Returns the shared CL handle used by CSSM functions. +CSSM_CL_HANDLE GetSharedCLHandle(); + +// Returns the shared TP handle used by CSSM functions. +CSSM_TP_HANDLE GetSharedTPHandle(); + // Set of pointers to memory function wrappers that are required for CSSM extern const CSSM_API_MEMORY_FUNCS kCssmMemoryFunctions; // Utility function to log an error message including the error name. void LogCSSMError(const char *function_name, CSSM_RETURN err); +// Utility functions to allocate and release CSSM memory. +void* CSSMMalloc(CSSM_SIZE size); +void CSSMFree(void* ptr); + // The OS X certificate and key management wrappers over CSSM are not // thread-safe. In particular, code that accesses the CSSM database is // problematic. @@ -35,6 +45,25 @@ void LogCSSMError(const char *function_name, CSSM_RETURN err); // http://developer.apple.com/mac/library/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html Lock& GetMacSecurityServicesLock(); +// Wrapper class for CSSM_DATA type. This should only be used when using the +// CL/TP/CSP handles from above, since that's the only time we're guaranteed (or +// supposed to be guaranteed) that our memory management functions will be used. +// Apple's Sec* APIs manage their own memory so it shouldn't be used for those. +// The constructor initializes data_ to zero and the destructor releases the +// data properly. +class ScopedCSSMData { + public: + ScopedCSSMData(); + ~ScopedCSSMData(); + operator CSSM_DATA*() { return &data_; } + CSSM_DATA* operator ->() { return &data_; } + + private: + CSSM_DATA data_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCSSMData); +}; + } // namespace base #endif // BASE_CRYPTO_CSSM_INIT_H_ diff --git a/base/crypto/rsa_private_key.cc b/base/crypto/rsa_private_key.cc index 75da7e4..024f741 100644 --- a/base/crypto/rsa_private_key.cc +++ b/base/crypto/rsa_private_key.cc @@ -4,6 +4,7 @@ #include "base/crypto/rsa_private_key.h" +#include <algorithm> #include <list> #include "base/logging.h" @@ -48,118 +49,6 @@ const uint8 PrivateKeyInfoCodec::kRsaAlgorithmIdentifier[] = { 0x05, 0x00 }; -void PrivateKeyInfoCodec::PrependBytes(uint8* val, - int start, - int num_bytes, - std::list<uint8>* data) { - while (num_bytes > 0) { - --num_bytes; - data->push_front(val[start + num_bytes]); - } -} - -void PrivateKeyInfoCodec::PrependLength(size_t size, std::list<uint8>* data) { - // The high bit is used to indicate whether additional octets are needed to - // represent the length. - if (size < 0x80) { - data->push_front(static_cast<uint8>(size)); - } else { - uint8 num_bytes = 0; - while (size > 0) { - data->push_front(static_cast<uint8>(size & 0xFF)); - size >>= 8; - num_bytes++; - } - CHECK_LE(num_bytes, 4); - data->push_front(0x80 | num_bytes); - } -} - -void PrivateKeyInfoCodec::PrependTypeHeaderAndLength(uint8 type, - uint32 length, - std::list<uint8>* output) { - PrependLength(length, output); - output->push_front(type); -} - -void PrivateKeyInfoCodec::PrependBitString(uint8* val, - int num_bytes, - std::list<uint8>* output) { - // Start with the data. - PrependBytes(val, 0, num_bytes, output); - // Zero unused bits. - output->push_front(0); - // Add the length. - PrependLength(num_bytes + 1, output); - // Finally, add the bit string tag. - output->push_front((uint8) kBitStringTag); -} - -bool PrivateKeyInfoCodec::ReadLength(uint8** pos, uint8* end, uint32* result) { - READ_ASSERT(*pos < end); - int length = 0; - - // If the MSB is not set, the length is just the byte itself. - if (!(**pos & 0x80)) { - length = **pos; - (*pos)++; - } else { - // Otherwise, the lower 7 indicate the length of the length. - int length_of_length = **pos & 0x7F; - READ_ASSERT(length_of_length <= 4); - (*pos)++; - READ_ASSERT(*pos + length_of_length < end); - - length = 0; - for (int i = 0; i < length_of_length; ++i) { - length <<= 8; - length |= **pos; - (*pos)++; - } - } - - READ_ASSERT(*pos + length <= end); - if (result) *result = length; - return true; -} - -bool PrivateKeyInfoCodec::ReadTypeHeaderAndLength(uint8** pos, - uint8* end, - uint8 expected_tag, - uint32* length) { - READ_ASSERT(*pos < end); - READ_ASSERT(**pos == expected_tag); - (*pos)++; - - return ReadLength(pos, end, length); -} - -bool PrivateKeyInfoCodec::ReadSequence(uint8** pos, uint8* end) { - return ReadTypeHeaderAndLength(pos, end, kSequenceTag, NULL); -} - -bool PrivateKeyInfoCodec::ReadAlgorithmIdentifier(uint8** pos, uint8* end) { - READ_ASSERT(*pos + sizeof(kRsaAlgorithmIdentifier) < end); - READ_ASSERT(memcmp(*pos, kRsaAlgorithmIdentifier, - sizeof(kRsaAlgorithmIdentifier)) == 0); - (*pos) += sizeof(kRsaAlgorithmIdentifier); - return true; -} - -bool PrivateKeyInfoCodec::ReadVersion(uint8** pos, uint8* end) { - uint32 length = 0; - if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length)) - return false; - - // The version should be zero. - for (uint32 i = 0; i < length; ++i) { - READ_ASSERT(**pos == 0x00); - (*pos)++; - } - - return true; -} - PrivateKeyInfoCodec::PrivateKeyInfoCodec(bool big_endian) : big_endian_(big_endian) {} @@ -200,19 +89,12 @@ bool PrivateKeyInfoCodec::Export(std::vector<uint8>* output) { bool PrivateKeyInfoCodec::ExportPublicKeyInfo(std::vector<uint8>* output) { // Create a sequence with the modulus (n) and public exponent (e). - std::list<uint8> content; - PrependInteger(&public_exponent_[0], - static_cast<int>(public_exponent_.size()), - &content); - PrependInteger(&modulus_[0], static_cast<int>(modulus_.size()), &content); - PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); - - // Copy the sequence with n and e into a buffer. std::vector<uint8> bit_string; - for (std::list<uint8>::iterator i = content.begin(); i != content.end(); ++i) - bit_string.push_back(*i); - content.clear(); + if (!ExportPublicKey(&bit_string)) + return false; + // Add the sequence as the contents of a bit string. + std::list<uint8> content; PrependBitString(&bit_string[0], static_cast<int>(bit_string.size()), &content); @@ -231,6 +113,23 @@ bool PrivateKeyInfoCodec::ExportPublicKeyInfo(std::vector<uint8>* output) { return true; } +bool PrivateKeyInfoCodec::ExportPublicKey(std::vector<uint8>* output) { + // Create a sequence with the modulus (n) and public exponent (e). + std::list<uint8> content; + PrependInteger(&public_exponent_[0], + static_cast<int>(public_exponent_.size()), + &content); + PrependInteger(&modulus_[0], static_cast<int>(modulus_.size()), &content); + PrependTypeHeaderAndLength(kSequenceTag, content.size(), &content); + + // Copy everything into the output. + output->reserve(content.size()); + for (std::list<uint8>::iterator i = content.begin(); i != content.end(); ++i) + output->push_back(*i); + + return true; +} + bool PrivateKeyInfoCodec::Import(const std::vector<uint8>& input) { if (input.empty()) { return false; @@ -321,6 +220,36 @@ bool PrivateKeyInfoCodec::ReadInteger(uint8** pos, return ReadIntegerImpl(pos, end, out, big_endian_); } +bool PrivateKeyInfoCodec::ReadIntegerWithExpectedSize(uint8** pos, + uint8* end, + size_t expected_size, + std::vector<uint8>* out) { + std::vector<uint8> temp; + if (!ReadIntegerImpl(pos, end, &temp, true)) // Big-Endian + return false; + + int pad = expected_size - temp.size(); + int index = 0; + if (out->size() == expected_size + 1) { + READ_ASSERT(out->front() == 0x00); + pad++; + index++; + } else { + READ_ASSERT(out->size() <= expected_size); + } + + while (pad) { + out->push_back(0x00); + pad--; + } + out->insert(out->end(), temp.begin(), temp.end()); + + // Reverse output if little-endian. + if (!big_endian_) + reverse(out->begin(), out->end()); + return true; +} + bool PrivateKeyInfoCodec::ReadIntegerImpl(uint8** pos, uint8* end, std::vector<uint8>* out, @@ -346,33 +275,115 @@ bool PrivateKeyInfoCodec::ReadIntegerImpl(uint8** pos, return true; } -bool PrivateKeyInfoCodec::ReadIntegerWithExpectedSize(uint8** pos, - uint8* end, - size_t expected_size, - std::vector<uint8>* out) { - std::vector<uint8> temp; - if (!ReadIntegerImpl(pos, end, &temp, true)) // Big-Endian - return false; +void PrivateKeyInfoCodec::PrependBytes(uint8* val, + int start, + int num_bytes, + std::list<uint8>* data) { + while (num_bytes > 0) { + --num_bytes; + data->push_front(val[start + num_bytes]); + } +} - int pad = expected_size - temp.size(); - int index = 0; - if (out->size() == expected_size + 1) { - READ_ASSERT(out->front() == 0x00); - pad++; - index++; +void PrivateKeyInfoCodec::PrependLength(size_t size, std::list<uint8>* data) { + // The high bit is used to indicate whether additional octets are needed to + // represent the length. + if (size < 0x80) { + data->push_front(static_cast<uint8>(size)); } else { - READ_ASSERT(out->size() <= expected_size); + uint8 num_bytes = 0; + while (size > 0) { + data->push_front(static_cast<uint8>(size & 0xFF)); + size >>= 8; + num_bytes++; + } + CHECK_LE(num_bytes, 4); + data->push_front(0x80 | num_bytes); } +} - while (pad) { - out->push_back(0x00); - pad--; +void PrivateKeyInfoCodec::PrependTypeHeaderAndLength(uint8 type, + uint32 length, + std::list<uint8>* output) { + PrependLength(length, output); + output->push_front(type); +} + +void PrivateKeyInfoCodec::PrependBitString(uint8* val, + int num_bytes, + std::list<uint8>* output) { + // Start with the data. + PrependBytes(val, 0, num_bytes, output); + // Zero unused bits. + output->push_front(0); + // Add the length. + PrependLength(num_bytes + 1, output); + // Finally, add the bit string tag. + output->push_front((uint8) kBitStringTag); +} + +bool PrivateKeyInfoCodec::ReadLength(uint8** pos, uint8* end, uint32* result) { + READ_ASSERT(*pos < end); + int length = 0; + + // If the MSB is not set, the length is just the byte itself. + if (!(**pos & 0x80)) { + length = **pos; + (*pos)++; + } else { + // Otherwise, the lower 7 indicate the length of the length. + int length_of_length = **pos & 0x7F; + READ_ASSERT(length_of_length <= 4); + (*pos)++; + READ_ASSERT(*pos + length_of_length < end); + + length = 0; + for (int i = 0; i < length_of_length; ++i) { + length <<= 8; + length |= **pos; + (*pos)++; + } + } + + READ_ASSERT(*pos + length <= end); + if (result) *result = length; + return true; +} + +bool PrivateKeyInfoCodec::ReadTypeHeaderAndLength(uint8** pos, + uint8* end, + uint8 expected_tag, + uint32* length) { + READ_ASSERT(*pos < end); + READ_ASSERT(**pos == expected_tag); + (*pos)++; + + return ReadLength(pos, end, length); +} + +bool PrivateKeyInfoCodec::ReadSequence(uint8** pos, uint8* end) { + return ReadTypeHeaderAndLength(pos, end, kSequenceTag, NULL); +} + +bool PrivateKeyInfoCodec::ReadAlgorithmIdentifier(uint8** pos, uint8* end) { + READ_ASSERT(*pos + sizeof(kRsaAlgorithmIdentifier) < end); + READ_ASSERT(memcmp(*pos, kRsaAlgorithmIdentifier, + sizeof(kRsaAlgorithmIdentifier)) == 0); + (*pos) += sizeof(kRsaAlgorithmIdentifier); + return true; +} + +bool PrivateKeyInfoCodec::ReadVersion(uint8** pos, uint8* end) { + uint32 length = 0; + if (!ReadTypeHeaderAndLength(pos, end, kIntegerTag, &length)) + return false; + + // The version should be zero. + for (uint32 i = 0; i < length; ++i) { + READ_ASSERT(**pos == 0x00); + (*pos)++; } - out->insert(out->end(), temp.begin(), temp.end()); - // Reverse output if little-endian. - if (!big_endian_) - reverse(out->begin(), out->end()); return true; } diff --git a/base/crypto/rsa_private_key.h b/base/crypto/rsa_private_key.h index 9b8b4fd..5357adc 100644 --- a/base/crypto/rsa_private_key.h +++ b/base/crypto/rsa_private_key.h @@ -64,6 +64,10 @@ class PrivateKeyInfoCodec { // of the PublicKeyInfo structure to |output|. bool ExportPublicKeyInfo(std::vector<uint8>* output); + // Exports the contents of the integer components to the ASN.1 DER encoding + // of the RSAPublicKey structure to |output|. + bool ExportPublicKey(std::vector<uint8>* output); + // Parses the ASN.1 DER encoding of the PrivateKeyInfo structure in |input| // and populates the integer components with |big_endian_| byte-significance. // IMPORTANT NOTE: This is currently *not* security-approved for importing @@ -215,6 +219,7 @@ class RSAPrivateKey { HCRYPTKEY key() { return key_; } #elif defined(OS_MACOSX) CSSM_KEY_PTR key() { return &key_; } + CSSM_KEY_PTR public_key() { return &public_key_; } #endif // Exports the private key to a PKCS #1 PrivateKey block. @@ -257,6 +262,7 @@ class RSAPrivateKey { ScopedHCRYPTKEY key_; #elif defined(OS_MACOSX) CSSM_KEY key_; + CSSM_KEY public_key_; #endif DISALLOW_COPY_AND_ASSIGN(RSAPrivateKey); diff --git a/base/crypto/rsa_private_key_mac.cc b/base/crypto/rsa_private_key_mac.cc index e46e93e..ede8014 100644 --- a/base/crypto/rsa_private_key_mac.cc +++ b/base/crypto/rsa_private_key_mac.cc @@ -26,14 +26,12 @@ RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { return NULL; } - CSSM_KEY public_key; - memset(&public_key, 0, sizeof(CSSM_KEY)); CSSM_DATA label = { 9, const_cast<uint8*>(reinterpret_cast<const uint8*>("temp_key")) }; crtn = CSSM_GenerateKeyPair(cc_handle, CSSM_KEYUSE_VERIFY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, - &public_key, CSSM_KEYUSE_SIGN, + result->public_key(), CSSM_KEYUSE_SIGN, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, &label, NULL, result->key()); CSSM_DeleteContext(cc_handle); @@ -42,9 +40,6 @@ RSAPrivateKey* RSAPrivateKey::Create(uint16 num_bits) { return NULL; } - // Public key is not needed. - CSSM_FreeKey(GetSharedCSPHandle(), NULL, &public_key, CSSM_FALSE); - return result.release(); } @@ -106,6 +101,46 @@ RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( return NULL; } + // Extract a public key from the private key. + // Apple doesn't accept CSSM_KEYBLOB_RAW_FORMAT_X509 as a valid key + // format when attempting to generate certs, so use PKCS1 instead. + PrivateKeyInfoCodec codec(true); + std::vector<uint8> private_key_data; + private_key_data.assign(key.KeyData.Data, + key.KeyData.Data + key.KeyData.Length); + if (!codec.Import(private_key_data)) { + return NULL; + } + std::vector<uint8> public_key_data; + if (!codec.ExportPublicKey(&public_key_data)) { + return NULL; + } + + CSSM_KEY* public_key = result->public_key(); + size_t size = public_key_data.size(); + public_key->KeyData.Data = reinterpret_cast<uint8*>(CSSMMalloc(size)); + if (!public_key->KeyData.Data) { + NOTREACHED() << "CSSMMalloc failed"; + return NULL; + } + memcpy(public_key->KeyData.Data, &public_key_data.front(), size); + public_key->KeyData.Length = size; + public_key->KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; + public_key->KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; + public_key->KeyHeader.BlobType = CSSM_KEYBLOB_RAW; + public_key->KeyHeader.AlgorithmId = CSSM_ALGID_RSA; + public_key->KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; + public_key->KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; + public_key->KeyHeader.KeyUsage = CSSM_KEYUSE_ANY; + + crtn = CSSM_QueryKeySizeInBits( + base::GetSharedCSPHandle(), NULL, public_key, &key_size); + if (crtn) { + DLOG(ERROR) << "CSSM_QueryKeySizeInBits failed " << crtn; + return NULL; + } + public_key->KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; + return result.release(); } @@ -125,6 +160,7 @@ RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( RSAPrivateKey::RSAPrivateKey() { memset(&key_, 0, sizeof(key_)); + memset(&public_key_, 0, sizeof(public_key_)); EnsureCSSMInit(); } @@ -133,6 +169,9 @@ RSAPrivateKey::~RSAPrivateKey() { if (key_.KeyData.Data) { CSSM_FreeKey(GetSharedCSPHandle(), NULL, &key_, CSSM_FALSE); } + if (public_key_.KeyData.Data) { + CSSM_FreeKey(GetSharedCSPHandle(), NULL, &public_key_, CSSM_FALSE); + } } bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) { diff --git a/base/crypto/rsa_private_key_nss.cc b/base/crypto/rsa_private_key_nss.cc index 7786521..202aa1d 100644 --- a/base/crypto/rsa_private_key_nss.cc +++ b/base/crypto/rsa_private_key_nss.cc @@ -41,28 +41,11 @@ static bool ReadAttribute(SECKEYPrivateKey* key, namespace base { -// static -RSAPrivateKey* RSAPrivateKey::CreateWithParams(uint16 num_bits, - bool permanent, - bool sensitive) { - base::EnsureNSSInit(); - - scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); - - PK11SlotInfo *slot = GetDefaultNSSKeySlot(); - if (!slot) - return NULL; - - PK11RSAGenParams param; - param.keySizeInBits = num_bits; - param.pe = 65537L; - result->key_ = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, ¶m, - &result->public_key_, permanent, sensitive, NULL); - PK11_FreeSlot(slot); - if (!result->key_) - return NULL; - - return result.release(); +RSAPrivateKey::~RSAPrivateKey() { + if (key_) + SECKEY_DestroyPrivateKey(key_); + if (public_key_) + SECKEY_DestroyPublicKey(public_key_); } // static @@ -80,41 +63,6 @@ RSAPrivateKey* RSAPrivateKey::CreateSensitive(uint16 num_bits) { } // static -RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfoWithParams( - const std::vector<uint8>& input, bool permanent, bool sensitive) { - // This method currently leaks some memory. - // See http://crbug.com/34742. - ANNOTATE_SCOPED_MEMORY_LEAK; - base::EnsureNSSInit(); - - scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); - - PK11SlotInfo *slot = GetDefaultNSSKeySlot(); - if (!slot) - return NULL; - - SECItem der_private_key_info; - der_private_key_info.data = const_cast<unsigned char*>(&input.front()); - der_private_key_info.len = input.size(); - SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, - &der_private_key_info, NULL, NULL, permanent, sensitive, - KU_DIGITAL_SIGNATURE, &result->key_, NULL); - PK11_FreeSlot(slot); - if (rv != SECSuccess) { - NOTREACHED(); - return NULL; - } - - result->public_key_ = SECKEY_ConvertToPublicKey(result->key_); - if (!result->public_key_) { - NOTREACHED(); - return NULL; - } - - return result.release(); -} - -// static RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfo( const std::vector<uint8>& input) { return CreateFromPrivateKeyInfoWithParams(input, @@ -193,16 +141,6 @@ RSAPrivateKey* RSAPrivateKey::FindFromPublicKeyInfo( return result.release(); } -RSAPrivateKey::RSAPrivateKey() : key_(NULL), public_key_(NULL) { - EnsureNSSInit(); -} - -RSAPrivateKey::~RSAPrivateKey() { - if (key_) - SECKEY_DestroyPrivateKey(key_); - if (public_key_) - SECKEY_DestroyPublicKey(public_key_); -} bool RSAPrivateKey::ExportPrivateKey(std::vector<uint8>* output) { PrivateKeyInfoCodec private_key_info(true); @@ -240,4 +178,71 @@ bool RSAPrivateKey::ExportPublicKey(std::vector<uint8>* output) { return true; } +RSAPrivateKey::RSAPrivateKey() : key_(NULL), public_key_(NULL) { + EnsureNSSInit(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateWithParams(uint16 num_bits, + bool permanent, + bool sensitive) { + base::EnsureNSSInit(); + + scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); + + PK11SlotInfo *slot = GetDefaultNSSKeySlot(); + if (!slot) + return NULL; + + PK11RSAGenParams param; + param.keySizeInBits = num_bits; + param.pe = 65537L; + result->key_ = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, ¶m, + &result->public_key_, permanent, sensitive, NULL); + PK11_FreeSlot(slot); + if (!result->key_) + return NULL; + + return result.release(); +} + +// static +RSAPrivateKey* RSAPrivateKey::CreateFromPrivateKeyInfoWithParams( + const std::vector<uint8>& input, bool permanent, bool sensitive) { + // This method currently leaks some memory. + // See http://crbug.com/34742. + ANNOTATE_SCOPED_MEMORY_LEAK; + base::EnsureNSSInit(); + + scoped_ptr<RSAPrivateKey> result(new RSAPrivateKey); + + PK11SlotInfo *slot = GetDefaultNSSKeySlot(); + if (!slot) + return NULL; + + SECItem der_private_key_info; + der_private_key_info.data = const_cast<unsigned char*>(&input.front()); + der_private_key_info.len = input.size(); + // Allow the private key to be used for key unwrapping, data decryption, + // and signature generation. + const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | + KU_DIGITAL_SIGNATURE; + SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot, &der_private_key_info, NULL, NULL, permanent, sensitive, + key_usage, &result->key_, NULL); + PK11_FreeSlot(slot); + if (rv != SECSuccess) { + NOTREACHED(); + return NULL; + } + + result->public_key_ = SECKEY_ConvertToPublicKey(result->key_); + if (!result->public_key_) { + NOTREACHED(); + return NULL; + } + + return result.release(); +} + } // namespace base diff --git a/base/crypto/secure_hash.h b/base/crypto/secure_hash.h new file mode 100644 index 0000000..3759218 --- /dev/null +++ b/base/crypto/secure_hash.h @@ -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. + +#ifndef BASE_CRYPTO_SECURE_HASH_H_ +#define BASE_CRYPTO_SECURE_HASH_H_ +#pragma once + +#include "base/basictypes.h" + +namespace base { + +// A wrapper to calculate secure hashes incrementally, allowing to +// be used when the full input is not known in advance. +class SecureHash { + public: + enum Algorithm { + SHA256, + }; + virtual ~SecureHash() {} + + static SecureHash* Create(Algorithm type); + + virtual void Update(const void* input, size_t len) = 0; + virtual void Finish(void* output, size_t len) = 0; + + protected: + SecureHash() {} + + private: + DISALLOW_COPY_AND_ASSIGN(SecureHash); +}; + +} // namespace base + +#endif // BASE_CRYPTO_SECURE_HASH_H_ diff --git a/base/crypto/secure_hash_default.cc b/base/crypto/secure_hash_default.cc new file mode 100644 index 0000000..436867e --- /dev/null +++ b/base/crypto/secure_hash_default.cc @@ -0,0 +1,49 @@ +// 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/crypto/secure_hash.h" + +#include "base/logging.h" +#include "base/third_party/nss/blapi.h" +#include "base/third_party/nss/sha256.h" + +namespace base { + +namespace { + +class SecureHashSHA256NSS : public SecureHash { + public: + SecureHashSHA256NSS() { + SHA256_Begin(&ctx_); + } + + virtual ~SecureHashSHA256NSS() { + } + + virtual void Update(const void* input, size_t len) { + SHA256_Update(&ctx_, static_cast<const unsigned char*>(input), len); + } + + virtual void Finish(void* output, size_t len) { + SHA256_End(&ctx_, static_cast<unsigned char*>(output), NULL, + static_cast<unsigned int>(len)); + } + + private: + SHA256Context ctx_; +}; + +} // namespace + +SecureHash* SecureHash::Create(Algorithm algorithm) { + switch (algorithm) { + case SHA256: + return new SecureHashSHA256NSS(); + default: + NOTIMPLEMENTED(); + return NULL; + } +} + +} // namespace base diff --git a/base/crypto/secure_hash_openssl.cc b/base/crypto/secure_hash_openssl.cc new file mode 100644 index 0000000..8087279 --- /dev/null +++ b/base/crypto/secure_hash_openssl.cc @@ -0,0 +1,53 @@ +// 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/crypto/secure_hash.h" + +#include <openssl/ssl.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/openssl_util.h" + +namespace base { + +namespace { + +class SecureHashSHA256OpenSSL : public SecureHash { + public: + SecureHashSHA256OpenSSL() { + SHA256_Init(&ctx_); + } + + virtual ~SecureHashSHA256OpenSSL() { + OPENSSL_cleanse(&ctx_, sizeof(ctx_)); + } + + virtual void Update(const void* input, size_t len) { + SHA256_Update(&ctx_, static_cast<const unsigned char*>(input), len); + } + + virtual void Finish(void* output, size_t len) { + ScopedOpenSSLSafeSizeBuffer<SHA256_DIGEST_LENGTH> result( + static_cast<unsigned char*>(output), len); + SHA256_Final(result.safe_buffer(), &ctx_); + } + + private: + SHA256_CTX ctx_; +}; + +} // namespace + +SecureHash* SecureHash::Create(Algorithm algorithm) { + switch (algorithm) { + case SHA256: + return new SecureHashSHA256OpenSSL(); + default: + NOTIMPLEMENTED(); + return NULL; + } +} + +} // namespace base diff --git a/base/crypto/secure_hash_unittest.cc b/base/crypto/secure_hash_unittest.cc new file mode 100644 index 0000000..2dac928 --- /dev/null +++ b/base/crypto/secure_hash_unittest.cc @@ -0,0 +1,34 @@ +// 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/crypto/secure_hash.h" + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/sha2.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SecureHashTest, TestUpdate) { + // Example B.3 from FIPS 180-2: long message. + std::string input3(500000, 'a'); // 'a' repeated half a million times + int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c, + 0x99, 0x14, 0xfb, 0x92, + 0x81, 0xa1, 0xc7, 0xe2, + 0x84, 0xd7, 0x3e, 0x67, + 0xf1, 0x80, 0x9a, 0x48, + 0xa4, 0x97, 0x20, 0x0e, + 0x04, 0x6d, 0x39, 0xcc, + 0xc7, 0x11, 0x2c, 0xd0 }; + + uint8 output3[base::SHA256_LENGTH]; + + scoped_ptr<base::SecureHash> ctx(base::SecureHash::Create( + base::SecureHash::SHA256)); + ctx->Update(input3.data(), input3.size()); + ctx->Update(input3.data(), input3.size()); + + ctx->Finish(output3, sizeof(output3)); + for (size_t i = 0; i < base::SHA256_LENGTH; i++) + EXPECT_EQ(expected3[i], static_cast<int>(output3[i])); +} diff --git a/base/crypto/signature_creator_mac.cc b/base/crypto/signature_creator_mac.cc index d10d54c..1001c64 100644 --- a/base/crypto/signature_creator_mac.cc +++ b/base/crypto/signature_creator_mac.cc @@ -59,19 +59,15 @@ bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { } bool SignatureCreator::Final(std::vector<uint8>* signature) { - CSSM_DATA sig; - memset(&sig, 0, sizeof(CSSM_DATA)); // Allow CSSM allocate memory; - CSSM_RETURN crtn = CSSM_SignDataFinal(sig_handle_, &sig); + ScopedCSSMData sig; + CSSM_RETURN crtn = CSSM_SignDataFinal(sig_handle_, sig); if (crtn) { NOTREACHED(); return false; } - signature->assign(sig.Data, sig.Data + sig.Length); - kCssmMemoryFunctions.free_func(sig.Data, NULL); // Release data alloc'd - // by CSSM - + signature->assign(sig->Data, sig->Data + sig->Length); return true; } diff --git a/base/crypto/signature_creator_nss.cc b/base/crypto/signature_creator_nss.cc index ff1d271..4cc2c10 100644 --- a/base/crypto/signature_creator_nss.cc +++ b/base/crypto/signature_creator_nss.cc @@ -14,6 +14,13 @@ namespace base { +SignatureCreator::~SignatureCreator() { + if (sign_context_) { + SGN_DestroyContext(sign_context_, PR_TRUE); + sign_context_ = NULL; + } +} + // static SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { scoped_ptr<SignatureCreator> result(new SignatureCreator); @@ -35,17 +42,6 @@ SignatureCreator* SignatureCreator::Create(RSAPrivateKey* key) { return result.release(); } -SignatureCreator::SignatureCreator() : sign_context_(NULL) { - EnsureNSSInit(); -} - -SignatureCreator::~SignatureCreator() { - if (sign_context_) { - SGN_DestroyContext(sign_context_, PR_TRUE); - sign_context_ = NULL; - } -} - bool SignatureCreator::Update(const uint8* data_part, int data_part_len) { // TODO(wtc): Remove this const_cast when we require NSS 3.12.5. // See NSS bug https://bugzilla.mozilla.org/show_bug.cgi?id=518255 @@ -73,4 +69,8 @@ bool SignatureCreator::Final(std::vector<uint8>* signature) { return true; } +SignatureCreator::SignatureCreator() : sign_context_(NULL) { + EnsureNSSInit(); +} + } // namespace base diff --git a/base/crypto/symmetric_key_nss.cc b/base/crypto/symmetric_key_nss.cc index d291e8d..1e3551d 100644 --- a/base/crypto/symmetric_key_nss.cc +++ b/base/crypto/symmetric_key_nss.cc @@ -12,10 +12,6 @@ namespace base { -SymmetricKey::SymmetricKey(PK11SymKey* key) : key_(key) { - DCHECK(key); -} - SymmetricKey::~SymmetricKey() {} // static @@ -124,4 +120,8 @@ bool SymmetricKey::GetRawKey(std::string* raw_key) { return true; } +SymmetricKey::SymmetricKey(PK11SymKey* key) : key_(key) { + DCHECK(key); +} + } // namespace base diff --git a/base/debug/debugger_posix.cc b/base/debug/debugger_posix.cc index b865e65..2eacaf9 100644 --- a/base/debug/debugger_posix.cc +++ b/base/debug/debugger_posix.cc @@ -174,6 +174,9 @@ bool BeingDebugged() { void BreakDebugger() { DEBUG_BREAK(); +#if defined(NDEBUG) + _exit(1); +#endif } } // namespace debug diff --git a/base/debug/debugger_win.cc b/base/debug/debugger_win.cc index 3323b61..b13dbfd 100644 --- a/base/debug/debugger_win.cc +++ b/base/debug/debugger_win.cc @@ -105,6 +105,9 @@ void BreakDebugger() { if (IsDebugUISuppressed()) _exit(1); __debugbreak(); +#if defined(NDEBUG) + _exit(1); +#endif } } // namespace debug diff --git a/base/debug/profiler.cc b/base/debug/profiler.cc new file mode 100644 index 0000000..8597dac --- /dev/null +++ b/base/debug/profiler.cc @@ -0,0 +1,65 @@ +// 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/debug/profiler.h" + +#include <string> + +#include "base/process_util.h" +#include "base/string_util.h" + +#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) +#include "third_party/tcmalloc/chromium/src/google/profiler.h" +#endif + +namespace base { +namespace debug { + +#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) + +static int profile_count = 0; + +void StartProfiling(const std::string& name) { + ++profile_count; + std::string full_name(name); + std::string pid = StringPrintf("%d", GetCurrentProcId()); + std::string count = StringPrintf("%d", profile_count); + ReplaceSubstringsAfterOffset(&full_name, 0, "{pid}", pid); + ReplaceSubstringsAfterOffset(&full_name, 0, "{count}", count); + ProfilerStart(full_name.c_str()); +} + +void StopProfiling() { + ProfilerFlush(); + ProfilerStop(); +} + +void FlushProfiling() { + ProfilerFlush(); +} + +bool BeingProfiled() { + return ProfilingIsEnabledForAllThreads(); +} + +#else + +void StartProfiling(const std::string& name) { +} + +void StopProfiling() { +} + +void FlushProfiling() { +} + +bool BeingProfiled() { + return false; +} + +#endif + +} // namespace debug +} // namespace base + diff --git a/base/debug/profiler.h b/base/debug/profiler.h new file mode 100644 index 0000000..e3044d6 --- /dev/null +++ b/base/debug/profiler.h @@ -0,0 +1,35 @@ +// 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_DEBUG_PROFILER_H +#define BASE_DEBUG_PROFILER_H +#pragma once + +#include <string> + +// The Profiler functions allow usage of the underlying sampling based +// profiler. If the application has not been built with the necessary +// flags (-DENABLE_PROFILING and not -DNO_TCMALLOC) then these functions +// are noops. +namespace base { +namespace debug { + +// Start profiling with the supplied name. +// {pid} will be replaced by the process' pid and {count} will be replaced +// by the count of the profile run (starts at 1 with each process). +void StartProfiling(const std::string& name); + +// Stop profiling and write out data. +void StopProfiling(); + +// Force data to be written to file. +void FlushProfiling(); + +// Returns true if process is being profiled. +bool BeingProfiled(); + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_DEBUGGER_H diff --git a/base/debug/stack_trace_win.cc b/base/debug/stack_trace_win.cc index 6f4ad02..510d35b 100644 --- a/base/debug/stack_trace_win.cc +++ b/base/debug/stack_trace_win.cc @@ -10,9 +10,9 @@ #include <iostream> #include "base/basictypes.h" -#include "base/lock.h" #include "base/logging.h" #include "base/singleton.h" +#include "base/synchronization/lock.h" namespace base { namespace debug { @@ -59,7 +59,7 @@ class SymbolContext { void OutputTraceToStream(const void* const* trace, int count, std::ostream* os) { - AutoLock lock(lock_); + base::AutoLock lock(lock_); for (size_t i = 0; (i < count) && os->good(); ++i) { const int kMaxNameLength = 256; @@ -129,7 +129,7 @@ class SymbolContext { } DWORD init_error_; - Lock lock_; + base::Lock lock_; DISALLOW_COPY_AND_ASSIGN(SymbolContext); }; diff --git a/base/debug/trace_event.cc b/base/debug/trace_event.cc index f50422c..36a3944 100644 --- a/base/debug/trace_event.cc +++ b/base/debug/trace_event.cc @@ -28,22 +28,6 @@ static const char* kEventTypeNames[] = { static const FilePath::CharType* kLogFileName = FILE_PATH_LITERAL("trace_%d.log"); -TraceLog::TraceLog() : enabled_(false), log_file_(NULL) { - base::ProcessHandle proc = base::GetCurrentProcessHandle(); -#if !defined(OS_MACOSX) - process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc)); -#else - // The default port provider is sufficient to get data for the current - // process. - process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc, - NULL)); -#endif -} - -TraceLog::~TraceLog() { - Stop(); -} - // static TraceLog* TraceLog::GetInstance() { return Singleton<TraceLog, DefaultSingletonTraits<TraceLog> >::get(); @@ -59,61 +43,11 @@ bool TraceLog::StartTracing() { return TraceLog::GetInstance()->Start(); } -bool TraceLog::Start() { - if (enabled_) - return true; - enabled_ = OpenLogFile(); - if (enabled_) { - Log("var raw_trace_events = [\n"); - trace_start_time_ = TimeTicks::Now(); - timer_.Start(TimeDelta::FromMilliseconds(250), this, &TraceLog::Heartbeat); - } - return enabled_; -} - // static void TraceLog::StopTracing() { return TraceLog::GetInstance()->Stop(); } -void TraceLog::Stop() { - if (enabled_) { - enabled_ = false; - Log("];\n"); - CloseLogFile(); - timer_.Stop(); - } -} - -void TraceLog::Heartbeat() { - std::string cpu = StringPrintf("%.0f", process_metrics_->GetCPUUsage()); - TRACE_EVENT_INSTANT("heartbeat.cpu", 0, cpu); -} - -void TraceLog::CloseLogFile() { - if (log_file_) { - file_util::CloseFile(log_file_); - } -} - -bool TraceLog::OpenLogFile() { - FilePath::StringType pid_filename = - StringPrintf(kLogFileName, base::GetCurrentProcId()); - FilePath log_file_path; - if (!PathService::Get(base::DIR_EXE, &log_file_path)) - return false; - log_file_path = log_file_path.Append(pid_filename); - log_file_ = file_util::OpenFile(log_file_path, "a"); - if (!log_file_) { - // try the current directory - log_file_ = file_util::OpenFile(FilePath(pid_filename), "a"); - if (!log_file_) { - return false; - } - } - return true; -} - void TraceLog::Trace(const std::string& name, EventType type, const void* id, @@ -158,6 +92,72 @@ void TraceLog::Trace(const std::string& name, Log(msg); } +TraceLog::TraceLog() : enabled_(false), log_file_(NULL) { + base::ProcessHandle proc = base::GetCurrentProcessHandle(); +#if !defined(OS_MACOSX) + process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc)); +#else + // The default port provider is sufficient to get data for the current + // process. + process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc, + NULL)); +#endif +} + +TraceLog::~TraceLog() { + Stop(); +} + +bool TraceLog::OpenLogFile() { + FilePath::StringType pid_filename = + StringPrintf(kLogFileName, base::GetCurrentProcId()); + FilePath log_file_path; + if (!PathService::Get(base::DIR_EXE, &log_file_path)) + return false; + log_file_path = log_file_path.Append(pid_filename); + log_file_ = file_util::OpenFile(log_file_path, "a"); + if (!log_file_) { + // try the current directory + log_file_ = file_util::OpenFile(FilePath(pid_filename), "a"); + if (!log_file_) { + return false; + } + } + return true; +} + +void TraceLog::CloseLogFile() { + if (log_file_) { + file_util::CloseFile(log_file_); + } +} + +bool TraceLog::Start() { + if (enabled_) + return true; + enabled_ = OpenLogFile(); + if (enabled_) { + Log("var raw_trace_events = [\n"); + trace_start_time_ = TimeTicks::Now(); + timer_.Start(TimeDelta::FromMilliseconds(250), this, &TraceLog::Heartbeat); + } + return enabled_; +} + +void TraceLog::Stop() { + if (enabled_) { + enabled_ = false; + Log("];\n"); + CloseLogFile(); + timer_.Stop(); + } +} + +void TraceLog::Heartbeat() { + std::string cpu = StringPrintf("%.0f", process_metrics_->GetCPUUsage()); + TRACE_EVENT_INSTANT("heartbeat.cpu", 0, cpu); +} + void TraceLog::Log(const std::string& msg) { AutoLock lock(file_lock_); diff --git a/base/debug/trace_event.h b/base/debug/trace_event.h index 476f065..e5c2cbd 100644 --- a/base/debug/trace_event.h +++ b/base/debug/trace_event.h @@ -31,9 +31,9 @@ #include <string> -#include "base/lock.h" #include "base/scoped_ptr.h" #include "base/singleton.h" +#include "base/synchronization/lock.h" #include "base/time.h" #include "base/timer.h" @@ -135,7 +135,7 @@ class TraceLog { bool enabled_; FILE* log_file_; - Lock file_lock_; + base::Lock file_lock_; TimeTicks trace_start_time_; #ifndef ANDROID scoped_ptr<base::ProcessMetrics> process_metrics_; diff --git a/base/event_recorder.h b/base/event_recorder.h index 04fbaa1..e4d8907 100644 --- a/base/event_recorder.h +++ b/base/event_recorder.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. @@ -80,6 +80,9 @@ class EventRecorder { #endif playback_first_msg_time_(0), playback_start_time_(0) { +#if defined(OS_WIN) + memset(&playback_msg_, 0, sizeof(playback_msg_)); +#endif } ~EventRecorder(); diff --git a/base/file_path.cc b/base/file_path.cc index cddb17e..5f1375a 100644 --- a/base/file_path.cc +++ b/base/file_path.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 <algorithm> + #include "base/file_path.h" #if defined(OS_WIN) @@ -511,10 +513,14 @@ bool FilePath::ReferencesParent() const { } #if defined(OS_POSIX) - // See file_path.h for a discussion of the encoding of paths on POSIX -// platforms. These *Hack() functions are not quite correct, but they're -// only temporary while we fix the remainder of the code. +// platforms. These encoding conversion functions are not quite correct. + +string16 FilePath::LossyDisplayName() const { + return WideToUTF16(base::SysNativeMBToWide(path_)); +} + +// 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. // static @@ -525,6 +531,10 @@ std::wstring FilePath::ToWStringHack() const { return base::SysNativeMBToWide(path_); } #elif defined(OS_WIN) +string16 FilePath::LossyDisplayName() const { + return path_; +} + // static FilePath FilePath::FromWStringHack(const std::wstring& wstring) { return FilePath(wstring); diff --git a/base/file_path.h b/base/file_path.h index 01cd4a5..84bb350 100644 --- a/base/file_path.h +++ b/base/file_path.h @@ -277,6 +277,12 @@ class FilePath { // directory (i.e. has a path component that is ".." bool ReferencesParent() const; + // Return a Unicode human-readable version of this path. + // Warning: you can *not*, in general, go from a display name back to a real + // path. Only use this when displaying paths to users, not just when you + // want to stuff a string16 into some other API. + string16 LossyDisplayName() 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. @@ -290,6 +296,9 @@ class FilePath { // OS-native string format. // - Am I using well-known file names, like "config.ini"? Then use the // ASCII functions (we require paths to always be supersets of ASCII). + // - Am I displaying a string to the user in some UI? Then use the + // LossyDisplayName() function, but keep in mind that you can't + // ever use the result of that again as a path. static FilePath FromWStringHack(const std::wstring& wstring); std::wstring ToWStringHack() const; diff --git a/base/file_util.cc b/base/file_util.cc index d1a46c9..c9661e7 100644 --- a/base/file_util.cc +++ b/base/file_util.cc @@ -315,13 +315,11 @@ MemoryMappedFile::~MemoryMappedFile() { CloseHandles(); } -bool MemoryMappedFile::Initialize(base::PlatformFile file) { +bool MemoryMappedFile::Initialize(const FilePath& file_name) { if (IsValid()) return false; - file_ = file; - - if (!MapFileToMemoryInternal()) { + if (!MapFileToMemory(file_name)) { CloseHandles(); return false; } @@ -329,11 +327,13 @@ bool MemoryMappedFile::Initialize(base::PlatformFile file) { return true; } -bool MemoryMappedFile::Initialize(const FilePath& file_name) { +bool MemoryMappedFile::Initialize(base::PlatformFile file) { if (IsValid()) return false; - if (!MapFileToMemory(file_name)) { + file_ = file; + + if (!MapFileToMemoryInternal()) { CloseHandles(); return false; } @@ -341,6 +341,10 @@ bool MemoryMappedFile::Initialize(const FilePath& file_name) { return true; } +bool MemoryMappedFile::IsValid() { + return data_ != NULL; +} + bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) { file_ = base::CreatePlatformFile( file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, @@ -354,10 +358,6 @@ bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) { return MapFileToMemoryInternal(); } -bool MemoryMappedFile::IsValid() { - return data_ != NULL; -} - // Deprecated functions ---------------------------------------------------- #if defined(OS_WIN) diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc index 475918e..7dd4127 100644 --- a/base/file_util_posix.cc +++ b/base/file_util_posix.cc @@ -641,26 +641,6 @@ FileEnumerator::FileEnumerator(const FilePath& root_path, FileEnumerator::~FileEnumerator() { } -void FileEnumerator::GetFindInfo(FindInfo* info) { - DCHECK(info); - - if (current_directory_entry_ >= directory_entries_.size()) - return; - - DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_]; - memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat)); - info->filename.assign(cur_entry->filename.value()); -} - -bool FileEnumerator::IsDirectory(const FindInfo& info) { - return S_ISDIR(info.stat.st_mode); -} - -// static -FilePath FileEnumerator::GetFilename(const FindInfo& find_info) { - return FilePath(find_info.filename); -} - FilePath FileEnumerator::Next() { ++current_directory_entry_; @@ -702,6 +682,26 @@ FilePath FileEnumerator::Next() { ].filename); } +void FileEnumerator::GetFindInfo(FindInfo* info) { + DCHECK(info); + + if (current_directory_entry_ >= directory_entries_.size()) + return; + + DirectoryEntryInfo* cur_entry = &directory_entries_[current_directory_entry_]; + memcpy(&(info->stat), &(cur_entry->stat), sizeof(info->stat)); + info->filename.assign(cur_entry->filename.value()); +} + +bool FileEnumerator::IsDirectory(const FindInfo& info) { + return S_ISDIR(info.stat.st_mode); +} + +// static +FilePath FileEnumerator::GetFilename(const FindInfo& find_info) { + return FilePath(find_info.filename); +} + bool FileEnumerator::ReadDirectory(std::vector<DirectoryEntryInfo>* entries, const FilePath& source, bool show_links) { base::ThreadRestrictions::AssertIOAllowed(); diff --git a/base/file_util_proxy.cc b/base/file_util_proxy.cc index bd08909..d357e98 100644 --- a/base/file_util_proxy.cc +++ b/base/file_util_proxy.cc @@ -525,12 +525,11 @@ class RelayRead : public MessageLoopRelay { public: RelayRead(base::PlatformFile file, int64 offset, - char* buffer, int bytes_to_read, - base::FileUtilProxy::ReadWriteCallback* callback) + base::FileUtilProxy::ReadCallback* callback) : file_(file), offset_(offset), - buffer_(buffer), + buffer_(new char[bytes_to_read]), bytes_to_read_(bytes_to_read), callback_(callback), bytes_read_(0) { @@ -538,7 +537,7 @@ class RelayRead : public MessageLoopRelay { protected: virtual void RunWork() { - bytes_read_ = base::ReadPlatformFile(file_, offset_, buffer_, + bytes_read_ = base::ReadPlatformFile(file_, offset_, buffer_.get(), bytes_to_read_); if (bytes_read_ < 0) set_error_code(base::PLATFORM_FILE_ERROR_FAILED); @@ -546,7 +545,7 @@ class RelayRead : public MessageLoopRelay { virtual void RunCallback() { if (callback_) { - callback_->Run(error_code(), bytes_read_); + callback_->Run(error_code(), buffer_.get(), bytes_read_); delete callback_; } } @@ -554,9 +553,9 @@ class RelayRead : public MessageLoopRelay { private: base::PlatformFile file_; int64 offset_; - char* buffer_; + scoped_array<char> buffer_; int bytes_to_read_; - base::FileUtilProxy::ReadWriteCallback* callback_; + base::FileUtilProxy::ReadCallback* callback_; int bytes_read_; }; @@ -566,17 +565,18 @@ class RelayWrite : public MessageLoopRelay { int64 offset, const char* buffer, int bytes_to_write, - base::FileUtilProxy::ReadWriteCallback* callback) + base::FileUtilProxy::WriteCallback* callback) : file_(file), offset_(offset), - buffer_(buffer), + buffer_(new char[bytes_to_write]), bytes_to_write_(bytes_to_write), callback_(callback) { + memcpy(buffer_.get(), buffer, bytes_to_write); } protected: virtual void RunWork() { - bytes_written_ = base::WritePlatformFile(file_, offset_, buffer_, + bytes_written_ = base::WritePlatformFile(file_, offset_, buffer_.get(), bytes_to_write_); if (bytes_written_ < 0) set_error_code(base::PLATFORM_FILE_ERROR_FAILED); @@ -592,9 +592,9 @@ class RelayWrite : public MessageLoopRelay { private: base::PlatformFile file_; int64 offset_; - const char* buffer_; + scoped_array<char> buffer_; int bytes_to_write_; - base::FileUtilProxy::ReadWriteCallback* callback_; + base::FileUtilProxy::WriteCallback* callback_; int bytes_written_; }; @@ -747,17 +747,6 @@ bool FileUtilProxy::CreateTemporary( } // static -bool FileUtilProxy::CreateDirectory( - scoped_refptr<MessageLoopProxy> message_loop_proxy, - const FilePath& file_path, - bool exclusive, - bool recursive, - StatusCallback* callback) { - return Start(FROM_HERE, message_loop_proxy, new RelayCreateDirectory( - file_path, exclusive, recursive, callback)); -} - -// static bool FileUtilProxy::Close(scoped_refptr<MessageLoopProxy> message_loop_proxy, base::PlatformFile file_handle, StatusCallback* callback) { @@ -774,13 +763,43 @@ bool FileUtilProxy::EnsureFileExists( message_loop_proxy, file_path, callback)); } +// Retrieves the information about a file. It is invalid to pass NULL for the +// callback. +bool FileUtilProxy::GetFileInfo( + scoped_refptr<MessageLoopProxy> message_loop_proxy, + const FilePath& file_path, + GetFileInfoCallback* callback) { + return Start(FROM_HERE, message_loop_proxy, new RelayGetFileInfo( + file_path, callback)); +} + // static -bool FileUtilProxy::Delete(scoped_refptr<MessageLoopProxy> message_loop_proxy, - const FilePath& file_path, - bool recursive, - StatusCallback* callback) { +bool FileUtilProxy::GetFileInfoFromPlatformFile( + scoped_refptr<MessageLoopProxy> message_loop_proxy, + PlatformFile file, + GetFileInfoCallback* callback) { return Start(FROM_HERE, message_loop_proxy, - new RelayDelete(file_path, recursive, callback)); + new RelayGetFileInfoFromPlatformFile(file, callback)); +} + +// static +bool FileUtilProxy::ReadDirectory( + scoped_refptr<MessageLoopProxy> message_loop_proxy, + const FilePath& file_path, + ReadDirectoryCallback* callback) { + return Start(FROM_HERE, message_loop_proxy, new RelayReadDirectory( + file_path, callback)); +} + +// static +bool FileUtilProxy::CreateDirectory( + scoped_refptr<MessageLoopProxy> message_loop_proxy, + const FilePath& file_path, + bool exclusive, + bool recursive, + StatusCallback* callback) { + return Start(FROM_HERE, message_loop_proxy, new RelayCreateDirectory( + file_path, exclusive, recursive, callback)); } // static @@ -802,22 +821,12 @@ bool FileUtilProxy::Move(scoped_refptr<MessageLoopProxy> message_loop_proxy, } // static -bool FileUtilProxy::ReadDirectory( - scoped_refptr<MessageLoopProxy> message_loop_proxy, - const FilePath& file_path, - ReadDirectoryCallback* callback) { - return Start(FROM_HERE, message_loop_proxy, new RelayReadDirectory( - file_path, callback)); -} - -// Retrieves the information about a file. It is invalid to pass NULL for the -// callback. -bool FileUtilProxy::GetFileInfo( - scoped_refptr<MessageLoopProxy> message_loop_proxy, - const FilePath& file_path, - GetFileInfoCallback* callback) { - return Start(FROM_HERE, message_loop_proxy, new RelayGetFileInfo( - file_path, callback)); +bool FileUtilProxy::Delete(scoped_refptr<MessageLoopProxy> message_loop_proxy, + const FilePath& file_path, + bool recursive, + StatusCallback* callback) { + return Start(FROM_HERE, message_loop_proxy, + new RelayDelete(file_path, recursive, callback)); } // static @@ -830,24 +839,14 @@ bool FileUtilProxy::RecursiveDelete( } // static -bool FileUtilProxy::GetFileInfoFromPlatformFile( - scoped_refptr<MessageLoopProxy> message_loop_proxy, - PlatformFile file, - GetFileInfoCallback* callback) { - return Start(FROM_HERE, message_loop_proxy, - new RelayGetFileInfoFromPlatformFile(file, callback)); -} - -// static bool FileUtilProxy::Read( scoped_refptr<MessageLoopProxy> message_loop_proxy, PlatformFile file, int64 offset, - char* buffer, int bytes_to_read, - ReadWriteCallback* callback) { + ReadCallback* callback) { return Start(FROM_HERE, message_loop_proxy, - new RelayRead(file, offset, buffer, bytes_to_read, callback)); + new RelayRead(file, offset, bytes_to_read, callback)); } // static @@ -857,7 +856,7 @@ bool FileUtilProxy::Write( int64 offset, const char* buffer, int bytes_to_write, - ReadWriteCallback* callback) { + WriteCallback* callback) { return Start(FROM_HERE, message_loop_proxy, new RelayWrite(file, offset, buffer, bytes_to_write, callback)); } diff --git a/base/file_util_proxy.h b/base/file_util_proxy.h index 4181a26..f2368cc 100644 --- a/base/file_util_proxy.h +++ b/base/file_util_proxy.h @@ -46,8 +46,11 @@ class FileUtilProxy { >::Type GetFileInfoCallback; typedef Callback2<PlatformFileError /* error code */, const std::vector<Entry>&>::Type ReadDirectoryCallback; + typedef Callback3<PlatformFileError /* error code */, + const char* /* data */, + int /* bytes read/written */>::Type ReadCallback; typedef Callback2<PlatformFileError /* error code */, - int /* bytes read/written */>::Type ReadWriteCallback; + int /* bytes written */>::Type WriteCallback; // Creates or opens a file with the given flags. It is invalid to pass NULL // for the callback. @@ -100,6 +103,15 @@ class FileUtilProxy { const FilePath& file_path, ReadDirectoryCallback* callback); + // Creates directory at given path. It's an error to create + // if |exclusive| is true and dir already exists. + static bool CreateDirectory( + scoped_refptr<MessageLoopProxy> message_loop_proxy, + const FilePath& file_path, + bool exclusive, + bool recursive, + StatusCallback* callback); + // Copies a file or a directory from |src_file_path| to |dest_file_path| // Error cases: // If destination file doesn't exist or destination's parent @@ -113,13 +125,12 @@ class FileUtilProxy { const FilePath& dest_file_path, StatusCallback* callback); - // Creates directory at given path. It's an error to create - // if |exclusive| is true and dir already exists. - static bool CreateDirectory( + // Moves a file or a directory from src_file_path to dest_file_path. + // Error cases are similar to Copy method's error cases. + static bool Move( scoped_refptr<MessageLoopProxy> message_loop_proxy, - const FilePath& file_path, - bool exclusive, - bool recursive, + const FilePath& src_file_path, + const FilePath& dest_file_path, StatusCallback* callback); // Deletes a file or a directory. @@ -129,14 +140,6 @@ class FileUtilProxy { bool recursive, StatusCallback* callback); - // Moves a file or a directory from src_file_path to dest_file_path. - // Error cases are similar to Copy method's error cases. - static bool Move( - scoped_refptr<MessageLoopProxy> message_loop_proxy, - const FilePath& src_file_path, - const FilePath& dest_file_path, - StatusCallback* callback); - // Deletes a directory and all of its contents. static bool RecursiveDelete( scoped_refptr<MessageLoopProxy> message_loop_proxy, @@ -149,9 +152,8 @@ class FileUtilProxy { scoped_refptr<MessageLoopProxy> message_loop_proxy, PlatformFile file, int64 offset, - char* buffer, int bytes_to_read, - ReadWriteCallback* callback); + ReadCallback* callback); // Writes to a file. If |offset| is greater than the length of the file, // |false| is returned. On success, the file pointer is moved to position @@ -162,7 +164,7 @@ class FileUtilProxy { int64 offset, const char* buffer, int bytes_to_write, - ReadWriteCallback* callback); + WriteCallback* callback); // Touches a file. The callback can be NULL. static bool Touch( diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc index 6ea94e4..ea29df5 100644 --- a/base/file_util_unittest.cc +++ b/base/file_util_unittest.cc @@ -163,7 +163,7 @@ class FindResultCollector { void CreateTextFile(const FilePath& filename, const std::wstring& contents) { std::ofstream file; - file.open(WideToUTF8(filename.ToWStringHack()).c_str()); + file.open(filename.value().c_str()); ASSERT_TRUE(file.is_open()); file << contents; file.close(); @@ -173,7 +173,7 @@ void CreateTextFile(const FilePath& filename, std::wstring ReadTextFile(const FilePath& filename) { wchar_t contents[64]; std::wifstream file; - file.open(WideToUTF8(filename.ToWStringHack()).c_str()); + file.open(filename.value().c_str()); EXPECT_TRUE(file.is_open()); file.getline(contents, 64); file.close(); diff --git a/base/file_util_win.cc b/base/file_util_win.cc index 7476b53..51bcb4e 100644 --- a/base/file_util_win.cc +++ b/base/file_util_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. @@ -667,9 +667,10 @@ bool CreateDirectory(const FilePath& full_path) { if (!::CreateDirectory(full_path_str, NULL)) { DWORD error_code = ::GetLastError(); if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path)) { - // This error code doesn't indicate whether we were racing with someone - // creating the same directory, or a file with the same path, therefore - // we check. + // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we + // were racing with someone creating the same directory, or a file + // with the same path. If DirectoryExists() returns true, we lost the + // race to create the same directory. return true; } else { LOG(WARNING) << "Failed to create directory " << full_path_str diff --git a/base/global_descriptors_posix.cc b/base/global_descriptors_posix.cc index 2fe953c..65e7955 100644 --- a/base/global_descriptors_posix.cc +++ b/base/global_descriptors_posix.cc @@ -11,10 +11,6 @@ namespace base { -GlobalDescriptors::GlobalDescriptors() {} - -GlobalDescriptors::~GlobalDescriptors() {} - // static GlobalDescriptors* GlobalDescriptors::GetInstance() { typedef Singleton<base::GlobalDescriptors, @@ -23,6 +19,14 @@ GlobalDescriptors* GlobalDescriptors::GetInstance() { return GlobalDescriptorsSingleton::get(); } +int GlobalDescriptors::Get(Key key) const { + const int ret = MaybeGet(key); + + if (ret == -1) + LOG(FATAL) << "Unknown global descriptor: " << key; + return ret; +} + int GlobalDescriptors::MaybeGet(Key key) const { for (Mapping::const_iterator i = descriptors_.begin(); i != descriptors_.end(); ++i) { @@ -35,14 +39,6 @@ int GlobalDescriptors::MaybeGet(Key key) const { return kBaseDescriptor + key; } -int GlobalDescriptors::Get(Key key) const { - const int ret = MaybeGet(key); - - if (ret == -1) - LOG(FATAL) << "Unknown global descriptor: " << key; - return ret; -} - void GlobalDescriptors::Set(Key key, int fd) { for (Mapping::iterator i = descriptors_.begin(); i != descriptors_.end(); ++i) { @@ -55,4 +51,12 @@ void GlobalDescriptors::Set(Key key, int fd) { descriptors_.push_back(std::make_pair(key, fd)); } +void GlobalDescriptors::Reset(const Mapping& mapping) { + descriptors_ = mapping; +} + +GlobalDescriptors::GlobalDescriptors() {} + +GlobalDescriptors::~GlobalDescriptors() {} + } // namespace base diff --git a/base/global_descriptors_posix.h b/base/global_descriptors_posix.h index 0cb5b4f..060bf0a 100644 --- a/base/global_descriptors_posix.h +++ b/base/global_descriptors_posix.h @@ -55,9 +55,7 @@ class GlobalDescriptors { // Set the descriptor for the given key. void Set(Key key, int fd); - void Reset(const Mapping& mapping) { - descriptors_ = mapping; - } + void Reset(const Mapping& mapping); private: friend struct DefaultSingletonTraits<GlobalDescriptors>; diff --git a/base/hmac_nss.cc b/base/hmac_nss.cc index 2ca4f67..af0b3eb 100644 --- a/base/hmac_nss.cc +++ b/base/hmac_nss.cc @@ -36,6 +36,9 @@ HMAC::HMAC(HashAlgorithm hash_alg) } } +HMAC::~HMAC() { +} + bool HMAC::Init(const unsigned char *key, int key_length) { base::EnsureNSSInit(); @@ -70,9 +73,6 @@ bool HMAC::Init(const unsigned char *key, int key_length) { return true; } -HMAC::~HMAC() { -} - bool HMAC::Sign(const std::string& data, unsigned char* digest, int digest_length) { diff --git a/base/i18n/bidi_line_iterator.cc b/base/i18n/bidi_line_iterator.cc new file mode 100644 index 0000000..3222a3a --- /dev/null +++ b/base/i18n/bidi_line_iterator.cc @@ -0,0 +1,60 @@ +// 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/i18n/bidi_line_iterator.h" + +#include "base/logging.h" + +namespace base { +namespace i18n { + +BiDiLineIterator::BiDiLineIterator() : bidi_(NULL) { +} + +BiDiLineIterator::~BiDiLineIterator() { + if (bidi_) { + ubidi_close(bidi_); + bidi_ = NULL; + } +} + +bool BiDiLineIterator::Open(const string16& text, + bool right_to_left, + bool url) { + DCHECK(bidi_ == NULL); + UErrorCode error = U_ZERO_ERROR; + bidi_ = ubidi_openSized(static_cast<int>(text.length()), 0, &error); + if (U_FAILURE(error)) + return false; + if (right_to_left && url) + ubidi_setReorderingMode(bidi_, UBIDI_REORDER_RUNS_ONLY); + ubidi_setPara(bidi_, text.data(), static_cast<int>(text.length()), + right_to_left ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR, + NULL, &error); + return U_SUCCESS(error) ? true : false; +} + +int BiDiLineIterator::CountRuns() { + DCHECK(bidi_ != NULL); + UErrorCode error = U_ZERO_ERROR; + const int runs = ubidi_countRuns(bidi_, &error); + return U_SUCCESS(error) ? runs : 0; +} + +UBiDiDirection BiDiLineIterator::GetVisualRun(int index, + int* start, + int* length) { + DCHECK(bidi_ != NULL); + return ubidi_getVisualRun(bidi_, index, start, length); +} + +void BiDiLineIterator::GetLogicalRun(int start, + int* end, + UBiDiLevel* level) { + DCHECK(bidi_ != NULL); + ubidi_getLogicalRun(bidi_, start, end, level); +} + +} // namespace i18n +} // namespace base diff --git a/base/i18n/bidi_line_iterator.h b/base/i18n/bidi_line_iterator.h new file mode 100644 index 0000000..5fff6a3 --- /dev/null +++ b/base/i18n/bidi_line_iterator.h @@ -0,0 +1,47 @@ +// 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_I18N_BIDI_LINE_ITERATOR_H_ +#define BASE_I18N_BIDI_LINE_ITERATOR_H_ +#pragma once + +#include "unicode/ubidi.h" + +#include "base/basictypes.h" +#include "base/string16.h" + +namespace base { +namespace i18n { + +// A simple wrapper class for the bidirectional iterator of ICU. +// This class uses the bidirectional iterator of ICU to split a line of +// bidirectional texts into visual runs in its display order. +class BiDiLineIterator { + public: + BiDiLineIterator(); + ~BiDiLineIterator(); + + // Initializes the bidirectional iterator with the specified text. Returns + // whether initialization succeeded. + bool Open(const string16& text, bool right_to_left, bool url); + + // Returns the number of visual runs in the text, or zero on error. + int CountRuns(); + + // Gets the logical offset, length, and direction of the specified visual run. + UBiDiDirection GetVisualRun(int index, int* start, int* length); + + // Given a start position, figure out where the run ends (and the BiDiLevel). + void GetLogicalRun(int start, int* end, UBiDiLevel* level); + + private: + UBiDi* bidi_; + + DISALLOW_COPY_AND_ASSIGN(BiDiLineIterator); +}; + +} // namespace i18n +} // namespace base + +#endif // BASE_I18N_BIDI_LINE_ITERATOR_H_ diff --git a/base/i18n/char_iterator.cc b/base/i18n/char_iterator.cc index a6cf944..ce4d513 100644 --- a/base/i18n/char_iterator.cc +++ b/base/i18n/char_iterator.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. @@ -8,6 +8,7 @@ #include "unicode/utf16.h" namespace base { +namespace i18n { UTF8CharIterator::UTF8CharIterator(const std::string* str) : str_(reinterpret_cast<const uint8_t*>(str->data())), @@ -20,6 +21,9 @@ UTF8CharIterator::UTF8CharIterator(const std::string* str) U8_NEXT(str_, next_pos_, len_, char_); } +UTF8CharIterator::~UTF8CharIterator() { +} + bool UTF8CharIterator::Advance() { if (array_pos_ >= len_) return false; @@ -54,6 +58,9 @@ UTF16CharIterator::UTF16CharIterator(const char16* str, size_t str_len) ReadChar(); } +UTF16CharIterator::~UTF16CharIterator() { +} + bool UTF16CharIterator::Advance() { if (array_pos_ >= len_) return false; @@ -71,4 +78,5 @@ void UTF16CharIterator::ReadChar() { U16_NEXT(str_, next_pos_, len_, char_); } +} // namespace i18n } // namespace base diff --git a/base/i18n/char_iterator.h b/base/i18n/char_iterator.h index f45b04b..bd7cc9f 100644 --- a/base/i18n/char_iterator.h +++ b/base/i18n/char_iterator.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. @@ -25,12 +25,13 @@ typedef unsigned char uint8_t; #endif namespace base { +namespace i18n { class UTF8CharIterator { public: // Requires |str| to live as long as the UTF8CharIterator does. UTF8CharIterator(const std::string* str); - ~UTF8CharIterator() {} + ~UTF8CharIterator(); // Return the starting array index of the current character within the // string. @@ -77,7 +78,7 @@ class UTF16CharIterator { // Requires |str| to live as long as the UTF16CharIterator does. UTF16CharIterator(const string16* str); UTF16CharIterator(const char16* str, size_t str_len); - ~UTF16CharIterator() {} + ~UTF16CharIterator(); // Return the starting array index of the current character within the // string. @@ -123,6 +124,7 @@ class UTF16CharIterator { DISALLOW_COPY_AND_ASSIGN(UTF16CharIterator); }; +} // namespace i18n } // namespace base #endif // BASE_I18N_CHAR_ITERATOR_H_ diff --git a/base/i18n/char_iterator_unittest.cc b/base/i18n/char_iterator_unittest.cc index 4fe7ebb..6d1294e 100644 --- a/base/i18n/char_iterator_unittest.cc +++ b/base/i18n/char_iterator_unittest.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. @@ -7,16 +7,19 @@ #include "base/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" +namespace base { +namespace i18n { + TEST(CharIteratorsTest, TestUTF8) { std::string empty(""); - base::UTF8CharIterator empty_iter(&empty); + UTF8CharIterator empty_iter(&empty); ASSERT_TRUE(empty_iter.end()); ASSERT_EQ(0, empty_iter.array_pos()); ASSERT_EQ(0, empty_iter.char_pos()); ASSERT_FALSE(empty_iter.Advance()); std::string str("s\303\273r"); // [u with circumflex] - base::UTF8CharIterator iter(&str); + UTF8CharIterator iter(&str); ASSERT_FALSE(iter.end()); ASSERT_EQ(0, iter.array_pos()); ASSERT_EQ(0, iter.char_pos()); @@ -47,7 +50,7 @@ TEST(CharIteratorsTest, TestUTF8) { TEST(CharIteratorsTest, TestUTF16) { string16 empty = UTF8ToUTF16(""); - base::UTF16CharIterator empty_iter(&empty); + UTF16CharIterator empty_iter(&empty); ASSERT_TRUE(empty_iter.end()); ASSERT_EQ(0, empty_iter.array_pos()); ASSERT_EQ(0, empty_iter.char_pos()); @@ -59,7 +62,7 @@ TEST(CharIteratorsTest, TestUTF16) { // math double-struck A - 4 bytes in UTF8, 2 codewords in UTF16 // z string16 str = UTF8ToUTF16("x\303\273\360\235\224\270z"); - base::UTF16CharIterator iter(&str); + UTF16CharIterator iter(&str); ASSERT_FALSE(iter.end()); ASSERT_EQ(0, iter.array_pos()); ASSERT_EQ(0, iter.char_pos()); @@ -93,3 +96,6 @@ TEST(CharIteratorsTest, TestUTF16) { ASSERT_FALSE(iter.Advance()); } + +} // namespace i18n +} // namespace base diff --git a/base/i18n/file_util_icu.cc b/base/i18n/file_util_icu.cc index 34eefac..ba69da0 100644 --- a/base/i18n/file_util_icu.cc +++ b/base/i18n/file_util_icu.cc @@ -90,7 +90,7 @@ class LocaleAwareComparator { int Compare(const string16& a, const string16& b) { // We are not sure if Collator::compare is thread-safe. // Use an AutoLock just in case. - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); UErrorCode error_code = U_ZERO_ERROR; UCollationResult result = collator_->compare( @@ -120,7 +120,7 @@ class LocaleAwareComparator { } scoped_ptr<icu::Collator> collator_; - Lock lock_; + base::Lock lock_; friend struct DefaultSingletonTraits<LocaleAwareComparator>; DISALLOW_COPY_AND_ASSIGN(LocaleAwareComparator); diff --git a/base/i18n/icu_util.cc b/base/i18n/icu_util.cc index d378a25..eb7a688 100644 --- a/base/i18n/icu_util.cc +++ b/base/i18n/icu_util.cc @@ -21,6 +21,10 @@ #include "unicode/putil.h" #include "unicode/udata.h" +#if defined(OS_MACOSX) +#include "base/mac/foundation_util.h" +#endif + #define ICU_UTIL_DATA_FILE 0 #define ICU_UTIL_DATA_SHARED 1 #define ICU_UTIL_DATA_STATIC 2 @@ -35,10 +39,14 @@ #endif // ICU_UTIL_DATA_IMPL -#if defined(OS_WIN) +#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE +#define ICU_UTIL_DATA_FILE_NAME "icudt" U_ICU_VERSION_SHORT "l.dat" +#elif ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED #define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat" +#if defined(OS_WIN) #define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt" U_ICU_VERSION_SHORT ".dll" #endif +#endif namespace icu_util { @@ -78,6 +86,7 @@ bool Initialize() { // Mac/Linux bundle the ICU data in. return true; #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) +#if !defined(OS_MACOSX) // For now, expect the data file to be alongside the executable. // This is sufficient while we work on unit tests, but will eventually // likely live in a data directory. @@ -90,6 +99,32 @@ bool Initialize() { UErrorCode err = U_ZERO_ERROR; udata_setFileAccess(UDATA_ONLY_PACKAGES, &err); return err == U_ZERO_ERROR; +#else + // If the ICU data directory is set, ICU won't actually load the data until + // it is needed. This can fail if the process is sandboxed at that time. + // Instead, Mac maps the file in and hands off the data so the sandbox won't + // cause any problems. + + // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever + // be released. + static file_util::MemoryMappedFile mapped_file; + if (!mapped_file.IsValid()) { + // Assume it is in the MainBundle's Resources directory. + FilePath data_path = + base::mac::PathForMainAppBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME)); + if (data_path.empty()) { + LOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle"; + return false; + } + if (!mapped_file.Initialize(data_path)) { + LOG(ERROR) << "Couldn't mmap " << data_path.value(); + return false; + } + } + UErrorCode err = U_ZERO_ERROR; + udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err); + return err == U_ZERO_ERROR; +#endif // OS check #endif } diff --git a/base/json/json_reader.cc b/base/json/json_reader.cc index 391f58b..c8fe78d 100644 --- a/base/json/json_reader.cc +++ b/base/json/json_reader.cc @@ -81,6 +81,11 @@ const char* JSONReader::kUnsupportedEncoding = const char* JSONReader::kUnquotedDictionaryKey = "Dictionary keys must be quoted."; +JSONReader::JSONReader() + : start_pos_(NULL), json_pos_(NULL), stack_depth_(0), + allow_trailing_comma_(false), + error_code_(JSON_NO_ERROR), error_line_(0), error_col_(0) {} + /* static */ Value* JSONReader::Read(const std::string& json, bool allow_trailing_comma) { @@ -106,16 +111,6 @@ Value* JSONReader::ReadAndReturnError(const std::string& json, } /* static */ -std::string JSONReader::FormatErrorMessage(int line, int column, - const std::string& description) { - if (line || column) { - return StringPrintf("Line: %i, column: %i, %s", - line, column, description.c_str()); - } - return description; -} - -/* static */ std::string JSONReader::ErrorCodeToString(JsonParseError error_code) { switch (error_code) { case JSON_NO_ERROR: @@ -147,11 +142,6 @@ std::string JSONReader::GetErrorMessage() const { ErrorCodeToString(error_code_)); } -JSONReader::JSONReader() - : start_pos_(NULL), json_pos_(NULL), stack_depth_(0), - allow_trailing_comma_(false), - error_code_(JSON_NO_ERROR), error_line_(0), error_col_(0) {} - Value* JSONReader::JsonToValue(const std::string& json, bool check_root, bool allow_trailing_comma) { // The input must be in UTF-8. @@ -195,6 +185,16 @@ Value* JSONReader::JsonToValue(const std::string& json, bool check_root, return NULL; } +/* static */ +std::string JSONReader::FormatErrorMessage(int line, int column, + const std::string& description) { + if (line || column) { + return StringPrintf("Line: %i, column: %i, %s", + line, column, description.c_str()); + } + return description; +} + Value* JSONReader::BuildValue(bool is_root) { ++stack_depth_; if (stack_depth_ > kStackLimit) { @@ -396,7 +396,7 @@ Value* JSONReader::DecodeNumber(const Token& token) { double num_double; if (StringToDouble(WideToUTF8(num_string), &num_double) && base::IsFinite(num_double)) - return Value::CreateRealValue(num_double); + return Value::CreateDoubleValue(num_double); return NULL; } @@ -580,16 +580,6 @@ JSONReader::Token JSONReader::ParseToken() { return token; } -bool JSONReader::NextStringMatch(const std::wstring& str) { - for (size_t i = 0; i < str.length(); ++i) { - if ('\0' == *json_pos_) - return false; - if (*(json_pos_ + i) != str[i]) - return false; - } - return true; -} - void JSONReader::EatWhitespaceAndComments() { while ('\0' != *json_pos_) { switch (*json_pos_) { @@ -645,6 +635,16 @@ bool JSONReader::EatComment() { return true; } +bool JSONReader::NextStringMatch(const std::wstring& str) { + for (size_t i = 0; i < str.length(); ++i) { + if ('\0' == *json_pos_) + return false; + if (*(json_pos_ + i) != str[i]) + return false; + } + return true; +} + void JSONReader::SetErrorCode(JsonParseError error, const wchar_t* error_pos) { int line_number = 1; diff --git a/base/json/json_reader.h b/base/json/json_reader.h index 77c4e74..a6f0686 100644 --- a/base/json/json_reader.h +++ b/base/json/json_reader.h @@ -146,14 +146,12 @@ class JSONReader { bool allow_trailing_comma); private: - static std::string FormatErrorMessage(int line, int column, - const std::string& description); - - DISALLOW_COPY_AND_ASSIGN(JSONReader); - FRIEND_TEST(JSONReaderTest, Reading); FRIEND_TEST(JSONReaderTest, ErrorMessages); + static std::string FormatErrorMessage(int line, int column, + const std::string& description); + // Recursively build Value. Returns NULL if we don't have a valid JSON // string. If |is_root| is true, we verify that the root element is either // an object or an array. @@ -213,6 +211,8 @@ class JSONReader { JsonParseError error_code_; int error_line_; int error_col_; + + DISALLOW_COPY_AND_ASSIGN(JSONReader); }; } // namespace base diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc index c00c976..db0ab63 100644 --- a/base/json/json_reader_unittest.cc +++ b/base/json/json_reader_unittest.cc @@ -75,60 +75,60 @@ TEST(JSONReaderTest, Reading) { // storage as doubles root.reset(JSONReader().JsonToValue("2147483648", false, false)); ASSERT_TRUE(root.get()); - double real_val; - ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); - real_val = 0.0; - ASSERT_TRUE(root->GetAsReal(&real_val)); - ASSERT_DOUBLE_EQ(2147483648.0, real_val); + double double_val; + ASSERT_TRUE(root->IsType(Value::TYPE_DOUBLE)); + double_val = 0.0; + ASSERT_TRUE(root->GetAsDouble(&double_val)); + ASSERT_DOUBLE_EQ(2147483648.0, double_val); root.reset(JSONReader().JsonToValue("-2147483649", false, false)); ASSERT_TRUE(root.get()); - ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); - real_val = 0.0; - ASSERT_TRUE(root->GetAsReal(&real_val)); - ASSERT_DOUBLE_EQ(-2147483649.0, real_val); + ASSERT_TRUE(root->IsType(Value::TYPE_DOUBLE)); + double_val = 0.0; + ASSERT_TRUE(root->GetAsDouble(&double_val)); + ASSERT_DOUBLE_EQ(-2147483649.0, double_val); // Parse a double root.reset(JSONReader().JsonToValue("43.1", false, false)); ASSERT_TRUE(root.get()); - ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); - real_val = 0.0; - ASSERT_TRUE(root->GetAsReal(&real_val)); - ASSERT_DOUBLE_EQ(43.1, real_val); + ASSERT_TRUE(root->IsType(Value::TYPE_DOUBLE)); + double_val = 0.0; + ASSERT_TRUE(root->GetAsDouble(&double_val)); + ASSERT_DOUBLE_EQ(43.1, double_val); root.reset(JSONReader().JsonToValue("4.3e-1", false, false)); ASSERT_TRUE(root.get()); - ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); - real_val = 0.0; - ASSERT_TRUE(root->GetAsReal(&real_val)); - ASSERT_DOUBLE_EQ(.43, real_val); + ASSERT_TRUE(root->IsType(Value::TYPE_DOUBLE)); + double_val = 0.0; + ASSERT_TRUE(root->GetAsDouble(&double_val)); + ASSERT_DOUBLE_EQ(.43, double_val); root.reset(JSONReader().JsonToValue("2.1e0", false, false)); ASSERT_TRUE(root.get()); - ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); - real_val = 0.0; - ASSERT_TRUE(root->GetAsReal(&real_val)); - ASSERT_DOUBLE_EQ(2.1, real_val); + ASSERT_TRUE(root->IsType(Value::TYPE_DOUBLE)); + double_val = 0.0; + ASSERT_TRUE(root->GetAsDouble(&double_val)); + ASSERT_DOUBLE_EQ(2.1, double_val); root.reset(JSONReader().JsonToValue("2.1e+0001", false, false)); ASSERT_TRUE(root.get()); - ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); - real_val = 0.0; - ASSERT_TRUE(root->GetAsReal(&real_val)); - ASSERT_DOUBLE_EQ(21.0, real_val); + ASSERT_TRUE(root->IsType(Value::TYPE_DOUBLE)); + double_val = 0.0; + ASSERT_TRUE(root->GetAsDouble(&double_val)); + ASSERT_DOUBLE_EQ(21.0, double_val); root.reset(JSONReader().JsonToValue("0.01", false, false)); ASSERT_TRUE(root.get()); - ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); - real_val = 0.0; - ASSERT_TRUE(root->GetAsReal(&real_val)); - ASSERT_DOUBLE_EQ(0.01, real_val); + ASSERT_TRUE(root->IsType(Value::TYPE_DOUBLE)); + double_val = 0.0; + ASSERT_TRUE(root->GetAsDouble(&double_val)); + ASSERT_DOUBLE_EQ(0.01, double_val); root.reset(JSONReader().JsonToValue("1.00", false, false)); ASSERT_TRUE(root.get()); - ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); - real_val = 0.0; - ASSERT_TRUE(root->GetAsReal(&real_val)); - ASSERT_DOUBLE_EQ(1.0, real_val); + ASSERT_TRUE(root->IsType(Value::TYPE_DOUBLE)); + double_val = 0.0; + ASSERT_TRUE(root->GetAsDouble(&double_val)); + ASSERT_DOUBLE_EQ(1.0, double_val); // Fractional parts must have a digit before and after the decimal point. root.reset(JSONReader().JsonToValue("1.", false, false)); @@ -303,9 +303,9 @@ TEST(JSONReaderTest, Reading) { ASSERT_TRUE(root.get()); ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); DictionaryValue* dict_val = static_cast<DictionaryValue*>(root.get()); - real_val = 0.0; - ASSERT_TRUE(dict_val->GetReal("number", &real_val)); - ASSERT_DOUBLE_EQ(9.87654321, real_val); + double_val = 0.0; + ASSERT_TRUE(dict_val->GetDouble("number", &double_val)); + ASSERT_DOUBLE_EQ(9.87654321, double_val); Value* null_val = NULL; ASSERT_TRUE(dict_val->Get("null", &null_val)); ASSERT_TRUE(null_val->IsType(Value::TYPE_NULL)); diff --git a/base/json/json_writer.cc b/base/json/json_writer.cc index dbf43ec..fa8f239 100644 --- a/base/json/json_writer.cc +++ b/base/json/json_writer.cc @@ -75,10 +75,10 @@ void JSONWriter::BuildJSONString(const Value* const node, break; } - case Value::TYPE_REAL: + case Value::TYPE_DOUBLE: { double value; - bool result = node->GetAsReal(&value); + bool result = node->GetAsDouble(&value); DCHECK(result); std::string real = DoubleToString(value); // Ensure that the number has a .0 if there's no decimal or 'e'. This diff --git a/base/json/json_writer_unittest.cc b/base/json/json_writer_unittest.cc index 937d083..6d7714b 100644 --- a/base/json/json_writer_unittest.cc +++ b/base/json/json_writer_unittest.cc @@ -29,19 +29,19 @@ TEST(JSONWriterTest, Writing) { delete root; // Test Real values should always have a decimal or an 'e'. - root = Value::CreateRealValue(1.0); + root = Value::CreateDoubleValue(1.0); JSONWriter::Write(root, false, &output_js); ASSERT_EQ("1.0", output_js); delete root; // Test Real values in the the range (-1, 1) must have leading zeros - root = Value::CreateRealValue(0.2); + root = Value::CreateDoubleValue(0.2); JSONWriter::Write(root, false, &output_js); ASSERT_EQ("0.2", output_js); delete root; // Test Real values in the the range (-1, 1) must have leading zeros - root = Value::CreateRealValue(-0.8); + root = Value::CreateDoubleValue(-0.8); JSONWriter::Write(root, false, &output_js); ASSERT_EQ("-0.8", output_js); delete root; diff --git a/base/linux_util.cc b/base/linux_util.cc index e1f7275..4e7cc5c 100644 --- a/base/linux_util.cc +++ b/base/linux_util.cc @@ -17,12 +17,12 @@ #include "base/command_line.h" #include "base/file_util.h" -#include "base/lock.h" #include "base/path_service.h" #include "base/process_util.h" #include "base/singleton.h" #include "base/scoped_ptr.h" #include "base/string_util.h" +#include "base/synchronization/lock.h" namespace { @@ -51,7 +51,7 @@ class LinuxDistroHelper { // we automatically move to STATE_CHECK_STARTED so nobody else will // do the check. LinuxDistroState State() { - AutoLock scoped_lock(lock_); + base::AutoLock scoped_lock(lock_); if (STATE_DID_NOT_CHECK == state_) { state_ = STATE_CHECK_STARTED; return STATE_DID_NOT_CHECK; @@ -61,13 +61,13 @@ class LinuxDistroHelper { // Indicate the check finished, move to STATE_CHECK_FINISHED. void CheckFinished() { - AutoLock scoped_lock(lock_); + base::AutoLock scoped_lock(lock_); DCHECK(state_ == STATE_CHECK_STARTED); state_ = STATE_CHECK_FINISHED; } private: - Lock lock_; + base::Lock lock_; LinuxDistroState state_; }; #endif // if defined(OS_LINUX) diff --git a/base/lock.h b/base/lock.h deleted file mode 100644 index 7c90d86..0000000 --- a/base/lock.h +++ /dev/null @@ -1,18 +0,0 @@ -// 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_LOCK_H_ -#define BASE_LOCK_H_ -#pragma once - -// This is a temporary forwarding file so not every user of lock needs to -// be updated at once. -// TODO(brettw) remove this and fix everybody up to using the new location. -#include "base/synchronization/lock.h" - -using base::AutoLock; -using base::AutoUnlock; -using base::Lock; - -#endif // BASE_LOCK_H_ diff --git a/base/logging.cc b/base/logging.cc index a736590..c38a2e0 100644 --- a/base/logging.cc +++ b/base/logging.cc @@ -64,7 +64,7 @@ typedef pthread_mutex_t* MutexHandle; namespace logging { -bool g_enable_dcheck = false; +DcheckState g_dcheck_state = DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS; VlogInfo* g_vlog_info = NULL; const char* const log_severity_names[LOG_NUM_SEVERITIES] = { @@ -353,15 +353,19 @@ bool InitializeLogFileHandle() { bool BaseInitLoggingImpl(const PathChar* new_log_file, LoggingDestination logging_dest, LogLockingState lock_log, +<<<<<<< HEAD OldFileDeletionState delete_old) { #ifdef ANDROID // ifdef is here because we don't support parsing command line parameters g_enable_dcheck = false; g_vlog_info = NULL; #else +======= + OldFileDeletionState delete_old, + DcheckState dcheck_state) { +>>>>>>> chromium.org at r11.0.672.0 CommandLine* command_line = CommandLine::ForCurrentProcess(); - g_enable_dcheck = - command_line->HasSwitch(switches::kEnableDCHECK); + g_dcheck_state = dcheck_state; delete g_vlog_info; g_vlog_info = NULL; // Don't bother initializing g_vlog_info unless we use one of the @@ -536,71 +540,29 @@ LogMessage::LogMessage(const char* file, int line, LogSeverity severity, Init(file, line); } -LogMessage::LogMessage(const char* file, int line, const CheckOpString& result) - : severity_(LOG_FATAL), file_(file), line_(line) { +LogMessage::LogMessage(const char* file, int line) + : severity_(LOG_INFO), file_(file), line_(line) { Init(file, line); - stream_ << "Check failed: " << (*result.str_); } -LogMessage::LogMessage(const char* file, int line, LogSeverity severity, - const CheckOpString& result) +LogMessage::LogMessage(const char* file, int line, LogSeverity severity) : severity_(severity), file_(file), line_(line) { Init(file, line); - stream_ << "Check failed: " << (*result.str_); } -LogMessage::LogMessage(const char* file, int line) - : severity_(LOG_INFO), file_(file), line_(line) { +LogMessage::LogMessage(const char* file, int line, std::string* result) + : severity_(LOG_FATAL), file_(file), line_(line) { Init(file, line); + stream_ << "Check failed: " << *result; + delete result; } -LogMessage::LogMessage(const char* file, int line, LogSeverity severity) +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + std::string* result) : severity_(severity), file_(file), line_(line) { Init(file, line); -} - -// writes the common header info to the stream -void LogMessage::Init(const char* file, int line) { - base::StringPiece filename(file); - size_t last_slash_pos = filename.find_last_of("\\/"); - if (last_slash_pos != base::StringPiece::npos) - filename.remove_prefix(last_slash_pos + 1); - - // TODO(darin): It might be nice if the columns were fixed width. - - stream_ << '['; - if (log_process_id) - stream_ << CurrentProcessId() << ':'; - if (log_thread_id) - stream_ << CurrentThreadId() << ':'; - if (log_timestamp) { - time_t t = time(NULL); - struct tm local_time = {0}; -#if _MSC_VER >= 1400 - localtime_s(&local_time, &t); -#else - localtime_r(&t, &local_time); -#endif - struct tm* tm_time = &local_time; - stream_ << std::setfill('0') - << std::setw(2) << 1 + tm_time->tm_mon - << std::setw(2) << tm_time->tm_mday - << '/' - << std::setw(2) << tm_time->tm_hour - << std::setw(2) << tm_time->tm_min - << std::setw(2) << tm_time->tm_sec - << ':'; - } - if (log_tickcount) - stream_ << TickCount() << ':'; - if (severity_ >= 0) - stream_ << log_severity_names[severity_]; - else - stream_ << "VERBOSE" << -severity_; - - stream_ << ":" << filename << "(" << line << ")] "; - - message_start_ = stream_.tellp(); + stream_ << "Check failed: " << *result; + delete result; } LogMessage::~LogMessage() { @@ -696,6 +658,50 @@ LogMessage::~LogMessage() { } } +// writes the common header info to the stream +void LogMessage::Init(const char* file, int line) { + base::StringPiece filename(file); + size_t last_slash_pos = filename.find_last_of("\\/"); + if (last_slash_pos != base::StringPiece::npos) + filename.remove_prefix(last_slash_pos + 1); + + // TODO(darin): It might be nice if the columns were fixed width. + + stream_ << '['; + if (log_process_id) + stream_ << CurrentProcessId() << ':'; + if (log_thread_id) + stream_ << CurrentThreadId() << ':'; + if (log_timestamp) { + time_t t = time(NULL); + struct tm local_time = {0}; +#if _MSC_VER >= 1400 + localtime_s(&local_time, &t); +#else + localtime_r(&t, &local_time); +#endif + struct tm* tm_time = &local_time; + stream_ << std::setfill('0') + << std::setw(2) << 1 + tm_time->tm_mon + << std::setw(2) << tm_time->tm_mday + << '/' + << std::setw(2) << tm_time->tm_hour + << std::setw(2) << tm_time->tm_min + << std::setw(2) << tm_time->tm_sec + << ':'; + } + if (log_tickcount) + stream_ << TickCount() << ':'; + if (severity_ >= 0) + stream_ << log_severity_names[severity_]; + else + stream_ << "VERBOSE" << -severity_; + + stream_ << ":" << filename << "(" << line << ")] "; + + message_start_ = stream_.tellp(); +} + #if defined(OS_WIN) // This has already been defined in the header, but defining it again as DWORD // ensures that the type used in the header is equivalent to DWORD. If not, diff --git a/base/logging.h b/base/logging.h index a097568..b1bc0b0 100644 --- a/base/logging.h +++ b/base/logging.h @@ -165,6 +165,11 @@ enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE }; // Defaults to APPEND_TO_OLD_LOG_FILE. enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE }; +enum DcheckState { + DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS, + ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS +}; + // TODO(avi): do we want to do a unification of character types here? #if defined(OS_WIN) typedef wchar_t PathChar; @@ -188,7 +193,8 @@ typedef char PathChar; bool BaseInitLoggingImpl(const PathChar* log_file, LoggingDestination logging_dest, LogLockingState lock_log, - OldFileDeletionState delete_old); + OldFileDeletionState delete_old, + DcheckState dcheck_state); // Sets the log file name and other global logging state. Calling this function // is recommended, and is normally done at the beginning of application init. @@ -203,8 +209,10 @@ bool BaseInitLoggingImpl(const PathChar* log_file, inline bool InitLogging(const PathChar* log_file, LoggingDestination logging_dest, LogLockingState lock_log, - OldFileDeletionState delete_old) { - return BaseInitLoggingImpl(log_file, logging_dest, lock_log, delete_old); + OldFileDeletionState delete_old, + DcheckState dcheck_state) { + return BaseInitLoggingImpl(log_file, logging_dest, lock_log, + delete_old, dcheck_state); } // Sets the log level. Anything at or above this level will be written to the @@ -428,19 +436,10 @@ const LogSeverity LOG_0 = LOG_ERROR; LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \ << "Check failed: " #condition ". " -// A container for a string pointer which can be evaluated to a bool - -// true iff the pointer is NULL. -struct CheckOpString { - CheckOpString(std::string* str) : str_(str) { } - // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), - // so there's no point in cleaning up str_. - operator bool() const { return str_ != NULL; } - std::string* str_; -}; - // Build the error message string. This is separate from the "Impl" // function template because it is not performance critical and so can -// be out of line, while the "Impl" code should be inline. +// be out of line, while the "Impl" code should be inline. Caller +// takes ownership of the returned string. template<class t1, class t2> std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { std::ostringstream ss; @@ -471,7 +470,7 @@ extern template std::string* MakeCheckOpString<std::string, std::string>( // TODO(akalin): Rewrite this so that constructs like if (...) // CHECK_EQ(...) else { ... } work properly. #define CHECK_OP(name, op, val1, val2) \ - if (logging::CheckOpString _result = \ + if (std::string* _result = \ logging::Check##name##Impl((val1), (val2), \ #val1 " " #op " " #val2)) \ logging::LogMessage(__FILE__, __LINE__, _result).stream() @@ -600,10 +599,11 @@ enum { DEBUG_MODE = ENABLE_DLOG }; COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName , ##__VA_ARGS__) #define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_ERROR_REPORT const LogSeverity LOG_DCHECK = LOG_ERROR_REPORT; -// This is set to true in InitLogging when we want to enable the -// DCHECKs in release. -extern bool g_enable_dcheck; -#define DCHECK_IS_ON() (::logging::g_enable_dcheck && LOG_IS_ON(DCHECK)) +extern DcheckState g_dcheck_state; +#define DCHECK_IS_ON() \ + ((::logging::g_dcheck_state == \ + ::logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS) && \ + LOG_IS_ON(DCHECK)) #else // defined(NDEBUG) @@ -646,7 +646,7 @@ const LogSeverity LOG_DCHECK = LOG_INFO; // Don't use this macro directly in your code, use DCHECK_EQ et al below. #define DCHECK_OP(name, op, val1, val2) \ if (DCHECK_IS_ON()) \ - if (logging::CheckOpString _result = \ + if (std::string* _result = \ logging::Check##name##Impl((val1), (val2), \ #val1 " " #op " " #val2)) \ logging::LogMessage( \ @@ -714,14 +714,15 @@ class LogMessage { // saves a couple of bytes per call site. LogMessage(const char* file, int line, LogSeverity severity); - // A special constructor used for check failures. + // A special constructor used for check failures. Takes ownership + // of the given string. // Implied severity = LOG_FATAL - LogMessage(const char* file, int line, const CheckOpString& result); + LogMessage(const char* file, int line, std::string* result); // A special constructor used for check failures, with the option to - // specify severity. + // specify severity. Takes ownership of the given string. LogMessage(const char* file, int line, LogSeverity severity, - const CheckOpString& result); + std::string* result); ~LogMessage(); @@ -910,4 +911,13 @@ inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) { } while(0) #endif +namespace base { + +class StringPiece; + +// allow StringPiece to be logged (needed for unit testing). +extern std::ostream& operator<<(std::ostream& o, const StringPiece& piece); + +} // namespace base + #endif // BASE_LOGGING_H_ diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc index 4b7fdbc..bb6e3d1 100644 --- a/base/logging_unittest.cc +++ b/base/logging_unittest.cc @@ -197,7 +197,7 @@ TEST_F(LoggingTest, DcheckStreamsAreLazy) { #if !defined(LOGGING_IS_OFFICIAL_BUILD) && defined(NDEBUG) // Unofficial release build. - logging::g_enable_dcheck = false; + g_dcheck_state = DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS; DCHECK(mock_log_source.Log()) << mock_log_source.Log(); DPCHECK(mock_log_source.Log()) << mock_log_source.Log(); DCHECK_EQ(0, 0) << mock_log_source.Log(); @@ -213,13 +213,13 @@ TEST_F(LoggingTest, Dcheck) { EXPECT_FALSE(DLOG_IS_ON(DCHECK)); #elif defined(NDEBUG) // Unofficial release build. - logging::g_enable_dcheck = true; - logging::SetLogReportHandler(&LogSink); + g_dcheck_state = ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS; + SetLogReportHandler(&LogSink); EXPECT_TRUE(DCHECK_IS_ON()); EXPECT_FALSE(DLOG_IS_ON(DCHECK)); #else // Unofficial debug build. - logging::SetLogAssertHandler(&LogSink); + SetLogAssertHandler(&LogSink); EXPECT_TRUE(DCHECK_IS_ON()); EXPECT_TRUE(DLOG_IS_ON(DCHECK)); #endif // defined(LOGGING_IS_OFFICIAL_BUILD) diff --git a/base/mac/foundation_util.h b/base/mac/foundation_util.h new file mode 100644 index 0000000..a7c525a --- /dev/null +++ b/base/mac/foundation_util.h @@ -0,0 +1,107 @@ +// 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_MAC_FOUNDATION_UTIL_H_ +#define BASE_MAC_FOUNDATION_UTIL_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/logging.h" + +#if defined(__OBJC__) +#import <Foundation/Foundation.h> +@class NSBundle; +#else // __OBJC__ +class NSBundle; +#endif // __OBJC__ + +class FilePath; + +// Adapted from NSPathUtilities.h and NSObjCRuntime.h. +#if __LP64__ || NS_BUILD_32_LIKE_64 +typedef unsigned long NSSearchPathDirectory; +typedef unsigned long NSSearchPathDomainMask; +#else +typedef unsigned int NSSearchPathDirectory; +typedef unsigned int NSSearchPathDomainMask; +#endif + +namespace base { +namespace mac { + +// Returns true if the application is running from a bundle +bool AmIBundled(); +void SetOverrideAmIBundled(bool value); + +// Returns true if this process is marked as a "Background only process". +bool IsBackgroundOnlyProcess(); + +// Returns the main bundle or the override, used for code that needs +// to fetch resources from bundles, but work within a unittest where we +// aren't a bundle. +NSBundle* MainAppBundle(); +FilePath MainAppBundlePath(); + +// Returns the path to a resource within the MainAppBundle. +FilePath PathForMainAppBundleResource(CFStringRef resourceName); + +// Set the bundle that MainAppBundle will return, overriding the default value +// (Restore the default by calling SetOverrideAppBundle(nil)). +void SetOverrideAppBundle(NSBundle* bundle); +void SetOverrideAppBundlePath(const FilePath& file_path); + +// Returns the creator code associated with the CFBundleRef at bundle. +OSType CreatorCodeForCFBundleRef(CFBundleRef bundle); + +// Returns the creator code associated with this application, by calling +// CreatorCodeForCFBundleRef for the application's main bundle. If this +// information cannot be determined, returns kUnknownType ('????'). This +// does not respect the override app bundle because it's based on CFBundle +// instead of NSBundle, and because callers probably don't want the override +// app bundle's creator code anyway. +OSType CreatorCodeForApplication(); + +// Searches for directories for the given key in only the given |domain_mask|. +// If found, fills result (which must always be non-NULL) with the +// first found directory and returns true. Otherwise, returns false. +bool GetSearchPathDirectory(NSSearchPathDirectory directory, + NSSearchPathDomainMask domain_mask, + FilePath* result); + +// Searches for directories for the given key in only the local domain. +// If found, fills result (which must always be non-NULL) with the +// first found directory and returns true. Otherwise, returns false. +bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result); + +// Searches for directories for the given key in only the user domain. +// If found, fills result (which must always be non-NULL) with the +// first found directory and returns true. Otherwise, returns false. +bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result); + +// Returns the ~/Library directory. +FilePath GetUserLibraryPath(); + +// Takes a path to an (executable) binary and tries to provide the path to an +// application bundle containing it. It takes the outermost bundle that it can +// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). +// |exec_name| - path to the binary +// returns - path to the application bundle, or empty on error +FilePath GetAppBundlePath(const FilePath& exec_name); + +// Utility function to pull out a value from a dictionary, check its type, and +// return it. Returns NULL if the key is not present or of the wrong type. +CFTypeRef GetValueFromDictionary(CFDictionaryRef dict, + CFStringRef key, + CFTypeID expected_type); + +// Retain/release calls for memory management in C++. +void NSObjectRetain(void* obj); +void NSObjectRelease(void* obj); + +} // namespace mac +} // namespace base + +#endif // BASE_MAC_FOUNDATION_UTIL_H_ diff --git a/base/mac/foundation_util.mm b/base/mac/foundation_util.mm new file mode 100644 index 0000000..151d82f --- /dev/null +++ b/base/mac/foundation_util.mm @@ -0,0 +1,236 @@ +// 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/mac/foundation_util.h" + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" +#include "base/sys_string_conversions.h" + +namespace base { +namespace mac { + +static bool g_override_am_i_bundled = false; +static bool g_override_am_i_bundled_value = false; + +// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled +static bool UncachedAmIBundled() { + if (g_override_am_i_bundled) + return g_override_am_i_bundled_value; + + ProcessSerialNumber psn = {0, kCurrentProcess}; + + FSRef fsref; + OSStatus pbErr; + if ((pbErr = GetProcessBundleLocation(&psn, &fsref)) != noErr) { + LOG(ERROR) << "GetProcessBundleLocation failed: error " << pbErr; + return false; + } + + FSCatalogInfo info; + OSErr fsErr; + if ((fsErr = FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info, + NULL, NULL, NULL)) != noErr) { + LOG(ERROR) << "FSGetCatalogInfo failed: error " << fsErr; + return false; + } + + return info.nodeFlags & kFSNodeIsDirectoryMask; +} + +bool AmIBundled() { + // If the return value is not cached, this function will return different + // values depending on when it's called. This confuses some client code, see + // http://crbug.com/63183 . + static bool result = UncachedAmIBundled(); + DCHECK_EQ(result, UncachedAmIBundled()) + << "The return value of AmIBundled() changed. This will confuse tests. " + << "Call SetAmIBundled() override manually if your test binary " + << "delay-loads the framework."; + return result; +} + +void SetOverrideAmIBundled(bool value) { + g_override_am_i_bundled = true; + g_override_am_i_bundled_value = value; +} + +bool IsBackgroundOnlyProcess() { + // This function really does want to examine NSBundle's idea of the main + // bundle dictionary, and not the overriden MainAppBundle. It needs to look + // at the actual running .app's Info.plist to access its LSUIElement + // property. + NSDictionary* info_dictionary = [[NSBundle mainBundle] infoDictionary]; + return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO; +} + +// No threading worries since NSBundle isn't thread safe. +static NSBundle* g_override_app_bundle = nil; + +NSBundle* MainAppBundle() { + if (g_override_app_bundle) + return g_override_app_bundle; + return [NSBundle mainBundle]; +} + +FilePath MainAppBundlePath() { + NSBundle* bundle = MainAppBundle(); + return FilePath([[bundle bundlePath] fileSystemRepresentation]); +} + +FilePath PathForMainAppBundleResource(CFStringRef resourceName) { + NSBundle* bundle = MainAppBundle(); + NSString* resourcePath = [bundle pathForResource:(NSString*)resourceName + ofType:nil]; + if (!resourcePath) + return FilePath(); + return FilePath([resourcePath fileSystemRepresentation]); +} + +void SetOverrideAppBundle(NSBundle* bundle) { + if (bundle != g_override_app_bundle) { + [g_override_app_bundle release]; + g_override_app_bundle = [bundle retain]; + } +} + +void SetOverrideAppBundlePath(const FilePath& file_path) { + NSString* path = base::SysUTF8ToNSString(file_path.value()); + NSBundle* bundle = [NSBundle bundleWithPath:path]; + CHECK(bundle) << "Failed to load the bundle at " << file_path.value(); + + SetOverrideAppBundle(bundle); +} + +OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) { + OSType creator = kUnknownType; + CFBundleGetPackageInfo(bundle, NULL, &creator); + return creator; +} + +OSType CreatorCodeForApplication() { + CFBundleRef bundle = CFBundleGetMainBundle(); + if (!bundle) + return kUnknownType; + + return CreatorCodeForCFBundleRef(bundle); +} + +bool GetSearchPathDirectory(NSSearchPathDirectory directory, + NSSearchPathDomainMask domain_mask, + FilePath* result) { + DCHECK(result); + NSArray* dirs = + NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES); + if ([dirs count] < 1) { + return false; + } + NSString* path = [dirs objectAtIndex:0]; + *result = FilePath([path fileSystemRepresentation]); + return true; +} + +bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) { + return GetSearchPathDirectory(directory, NSLocalDomainMask, result); +} + +bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { + return GetSearchPathDirectory(directory, NSUserDomainMask, result); +} + +FilePath GetUserLibraryPath() { + FilePath user_library_path; + if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) { + LOG(WARNING) << "Could not get user library path"; + } + return user_library_path; +} + +// Takes a path to an (executable) binary and tries to provide the path to an +// application bundle containing it. It takes the outermost bundle that it can +// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). +// |exec_name| - path to the binary +// returns - path to the application bundle, or empty on error +FilePath GetAppBundlePath(const FilePath& exec_name) { + const char kExt[] = ".app"; + const size_t kExtLength = arraysize(kExt) - 1; + + // Split the path into components. + std::vector<std::string> components; + exec_name.GetComponents(&components); + + // It's an error if we don't get any components. + if (!components.size()) + return FilePath(); + + // Don't prepend '/' to the first component. + std::vector<std::string>::const_iterator it = components.begin(); + std::string bundle_name = *it; + DCHECK(it->length() > 0); + // If the first component ends in ".app", we're already done. + if (it->length() > kExtLength && + !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) + return FilePath(bundle_name); + + // The first component may be "/" or "//", etc. Only append '/' if it doesn't + // already end in '/'. + if (bundle_name[bundle_name.length() - 1] != '/') + bundle_name += '/'; + + // Go through the remaining components. + for (++it; it != components.end(); ++it) { + DCHECK(it->length() > 0); + + bundle_name += *it; + + // If the current component ends in ".app", we're done. + if (it->length() > kExtLength && + !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) + return FilePath(bundle_name); + + // Separate this component from the next one. + bundle_name += '/'; + } + + return FilePath(); +} + +CFTypeRef GetValueFromDictionary(CFDictionaryRef dict, + CFStringRef key, + CFTypeID expected_type) { + CFTypeRef value = CFDictionaryGetValue(dict, key); + if (!value) + return value; + + if (CFGetTypeID(value) != expected_type) { + ScopedCFTypeRef<CFStringRef> expected_type_ref( + CFCopyTypeIDDescription(expected_type)); + ScopedCFTypeRef<CFStringRef> actual_type_ref( + CFCopyTypeIDDescription(CFGetTypeID(value))); + LOG(WARNING) << "Expected value for key " + << base::SysCFStringRefToUTF8(key) + << " to be " + << base::SysCFStringRefToUTF8(expected_type_ref) + << " but it was " + << base::SysCFStringRefToUTF8(actual_type_ref) + << " instead"; + return NULL; + } + + return value; +} + +void NSObjectRetain(void* obj) { + id<NSObject> nsobj = static_cast<id<NSObject> >(obj); + [nsobj retain]; +} + +void NSObjectRelease(void* obj) { + id<NSObject> nsobj = static_cast<id<NSObject> >(obj); + [nsobj release]; +} + +} // namespace mac +} // namespace base diff --git a/base/mac/mac_util.h b/base/mac/mac_util.h index 7e5dddb..d75fb6e 100644 --- a/base/mac/mac_util.h +++ b/base/mac/mac_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. @@ -8,32 +8,20 @@ #include <Carbon/Carbon.h> #include <string> -#include <vector> #include "base/logging.h" +// TODO(rohitrao): Clean up sites that include mac_util.h and remove this line. +#include "base/mac/foundation_util.h" + #if defined(__OBJC__) #import <Foundation/Foundation.h> - -@class NSBundle; -@class NSWindow; #else // __OBJC__ -class NSBundle; class NSImage; -class NSWindow; #endif // __OBJC__ class FilePath; -// Adapted from NSPathUtilities.h and NSObjCRuntime.h. -#if __LP64__ || NS_BUILD_32_LIKE_64 -typedef unsigned long NSSearchPathDirectory; -typedef unsigned long NSSearchPathDomainMask; -#else -typedef unsigned int NSSearchPathDirectory; -typedef unsigned int NSSearchPathDomainMask; -#endif - namespace base { namespace mac { @@ -53,55 +41,6 @@ enum FullScreenMode { std::string PathFromFSRef(const FSRef& ref); bool FSRefFromPath(const std::string& path, FSRef* ref); -// Returns true if the application is running from a bundle -bool AmIBundled(); -void SetOverrideAmIBundled(bool value); - -// Returns true if this process is marked as a "Background only process". -bool IsBackgroundOnlyProcess(); - -// Returns the main bundle or the override, used for code that needs -// to fetch resources from bundles, but work within a unittest where we -// aren't a bundle. -NSBundle* MainAppBundle(); -FilePath MainAppBundlePath(); - -// Set the bundle that MainAppBundle will return, overriding the default value -// (Restore the default by calling SetOverrideAppBundle(nil)). -void SetOverrideAppBundle(NSBundle* bundle); -void SetOverrideAppBundlePath(const FilePath& file_path); - -// Returns the creator code associated with the CFBundleRef at bundle. -OSType CreatorCodeForCFBundleRef(CFBundleRef bundle); - -// Returns the creator code associated with this application, by calling -// CreatorCodeForCFBundleRef for the application's main bundle. If this -// information cannot be determined, returns kUnknownType ('????'). This -// does not respect the override app bundle because it's based on CFBundle -// instead of NSBundle, and because callers probably don't want the override -// app bundle's creator code anyway. -OSType CreatorCodeForApplication(); - -// Searches for directories for the given key in only the given |domain_mask|. -// If found, fills result (which must always be non-NULL) with the -// first found directory and returns true. Otherwise, returns false. -bool GetSearchPathDirectory(NSSearchPathDirectory directory, - NSSearchPathDomainMask domain_mask, - FilePath* result); - -// Searches for directories for the given key in only the user domain. -// If found, fills result (which must always be non-NULL) with the -// first found directory and returns true. Otherwise, returns false. -bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result); - -// Searches for directories for the given key in only the local domain. -// If found, fills result (which must always be non-NULL) with the -// first found directory and returns true. Otherwise, returns false. -bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result); - -// Returns the ~/Library directory. -FilePath GetUserLibraryPath(); - // Returns an sRGB color space. The return value is a static value; do not // release it! CGColorSpaceRef GetSRGBColorSpace(); @@ -136,29 +75,11 @@ void SetCursorVisibility(bool visible); bool ShouldWindowsMiniaturizeOnDoubleClick(); // Activates the process with the given PID. -void ActivateProcess(pid_t); - -// Pulls a snapshot of the entire browser into png_representation. -void GrabWindowSnapshot(NSWindow* window, - std::vector<unsigned char>* png_representation, - int* width, int* height); - -// Takes a path to an (executable) binary and tries to provide the path to an -// application bundle containing it. It takes the outermost bundle that it can -// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). -// |exec_name| - path to the binary -// returns - path to the application bundle, or empty on error -FilePath GetAppBundlePath(const FilePath& exec_name); +void ActivateProcess(pid_t pid); // Set the Time Machine exclusion property for the given file. bool SetFileBackupExclusion(const FilePath& file_path, bool exclude); -// Utility function to pull out a value from a dictionary, check its type, and -// return it. Returns NULL if the key is not present or of the wrong type. -CFTypeRef GetValueFromDictionary(CFDictionaryRef dict, - CFStringRef key, - CFTypeID expected_type); - // Sets the process name as displayed in Activity Monitor to process_name. void SetProcessName(CFStringRef process_name); @@ -189,10 +110,6 @@ void RemoveFromLoginItems(); // 'Login Item' with 'hide on startup' flag. Used to suppress opening windows. bool WasLaunchedAsHiddenLoginItem(); -// Retain/release calls for memory management in C++. -void NSObjectRetain(void* obj); -void NSObjectRelease(void* obj); - #if defined(__OBJC__) // Convert toll-free bridged CFTypes to NSTypes. This does not autorelease diff --git a/base/mac/mac_util.mm b/base/mac/mac_util.mm index 21213ba..7f3be18 100644 --- a/base/mac/mac_util.mm +++ b/base/mac/mac_util.mm @@ -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. @@ -9,7 +9,6 @@ #include "base/file_path.h" #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" -#include "base/message_loop.h" #include "base/scoped_nsobject.h" #include "base/sys_string_conversions.h" @@ -141,133 +140,6 @@ bool FSRefFromPath(const std::string& path, FSRef* ref) { return status == noErr; } -static bool g_override_am_i_bundled = false; -static bool g_override_am_i_bundled_value = false; - -// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled -static bool UncachedAmIBundled() { - if (g_override_am_i_bundled) - return g_override_am_i_bundled_value; - - ProcessSerialNumber psn = {0, kCurrentProcess}; - - FSRef fsref; - OSStatus pbErr; - if ((pbErr = GetProcessBundleLocation(&psn, &fsref)) != noErr) { - LOG(ERROR) << "GetProcessBundleLocation failed: error " << pbErr; - return false; - } - - FSCatalogInfo info; - OSErr fsErr; - if ((fsErr = FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info, - NULL, NULL, NULL)) != noErr) { - LOG(ERROR) << "FSGetCatalogInfo failed: error " << fsErr; - return false; - } - - return info.nodeFlags & kFSNodeIsDirectoryMask; -} - -bool AmIBundled() { - // If the return value is not cached, this function will return different - // values depending on when it's called. This confuses some client code, see - // http://crbug.com/63183 . - static bool result = UncachedAmIBundled(); - DCHECK_EQ(result, UncachedAmIBundled()) - << "The return value of AmIBundled() changed. This will confuse tests. " - << "Call SetAmIBundled() override manually if your test binary " - << "delay-loads the framework."; - return result; -} - -void SetOverrideAmIBundled(bool value) { - g_override_am_i_bundled = true; - g_override_am_i_bundled_value = value; -} - -bool IsBackgroundOnlyProcess() { - // This function really does want to examine NSBundle's idea of the main - // bundle dictionary, and not the overriden MainAppBundle. It needs to look - // at the actual running .app's Info.plist to access its LSUIElement - // property. - NSDictionary* info_dictionary = [[NSBundle mainBundle] infoDictionary]; - return [[info_dictionary objectForKey:@"LSUIElement"] boolValue] != NO; -} - -// No threading worries since NSBundle isn't thread safe. -static NSBundle* g_override_app_bundle = nil; - -NSBundle* MainAppBundle() { - if (g_override_app_bundle) - return g_override_app_bundle; - return [NSBundle mainBundle]; -} - -FilePath MainAppBundlePath() { - NSBundle* bundle = MainAppBundle(); - return FilePath([[bundle bundlePath] fileSystemRepresentation]); -} - -void SetOverrideAppBundle(NSBundle* bundle) { - if (bundle != g_override_app_bundle) { - [g_override_app_bundle release]; - g_override_app_bundle = [bundle retain]; - } -} - -void SetOverrideAppBundlePath(const FilePath& file_path) { - NSString* path = base::SysUTF8ToNSString(file_path.value()); - NSBundle* bundle = [NSBundle bundleWithPath:path]; - CHECK(bundle) << "Failed to load the bundle at " << file_path.value(); - - SetOverrideAppBundle(bundle); -} - -OSType CreatorCodeForCFBundleRef(CFBundleRef bundle) { - OSType creator = kUnknownType; - CFBundleGetPackageInfo(bundle, NULL, &creator); - return creator; -} - -OSType CreatorCodeForApplication() { - CFBundleRef bundle = CFBundleGetMainBundle(); - if (!bundle) - return kUnknownType; - - return CreatorCodeForCFBundleRef(bundle); -} - -bool GetSearchPathDirectory(NSSearchPathDirectory directory, - NSSearchPathDomainMask domain_mask, - FilePath* result) { - DCHECK(result); - NSArray* dirs = - NSSearchPathForDirectoriesInDomains(directory, domain_mask, YES); - if ([dirs count] < 1) { - return false; - } - NSString* path = [dirs objectAtIndex:0]; - *result = FilePath([path fileSystemRepresentation]); - return true; -} - -bool GetLocalDirectory(NSSearchPathDirectory directory, FilePath* result) { - return GetSearchPathDirectory(directory, NSLocalDomainMask, result); -} - -bool GetUserDirectory(NSSearchPathDirectory directory, FilePath* result) { - return GetSearchPathDirectory(directory, NSUserDomainMask, result); -} - -FilePath GetUserLibraryPath() { - FilePath user_library_path; - if (!GetUserDirectory(NSLibraryDirectory, &user_library_path)) { - LOG(WARNING) << "Could not get user library path"; - } - return user_library_path; -} - CGColorSpaceRef GetSRGBColorSpace() { // Leaked. That's OK, it's scoped to the lifetime of the application. static CGColorSpaceRef g_color_space_sRGB = @@ -356,26 +228,6 @@ bool ShouldWindowsMiniaturizeOnDoubleClick() { [NSWindow performSelector:@selector(_shouldMiniaturizeOnDoubleClick)]; } -void GrabWindowSnapshot(NSWindow* window, - std::vector<unsigned char>* png_representation, - int* width, int* height) { - // Make sure to grab the "window frame" view so we get current tab + - // tabstrip. - NSView* view = [[window contentView] superview]; - NSBitmapImageRep* rep = - [view bitmapImageRepForCachingDisplayInRect:[view bounds]]; - [view cacheDisplayInRect:[view bounds] toBitmapImageRep:rep]; - NSData* data = [rep representationUsingType:NSPNGFileType properties:nil]; - const unsigned char* buf = static_cast<const unsigned char*>([data bytes]); - NSUInteger length = [data length]; - if (buf != NULL && length > 0){ - *width = static_cast<int>([rep pixelsWide]); - *height = static_cast<int>([rep pixelsHigh]); - png_representation->assign(buf, buf + length); - DCHECK(png_representation->size() > 0); - } -} - void ActivateProcess(pid_t pid) { ProcessSerialNumber process; OSStatus status = GetProcessForPID(pid, &process); @@ -386,55 +238,6 @@ void ActivateProcess(pid_t pid) { } } -// Takes a path to an (executable) binary and tries to provide the path to an -// application bundle containing it. It takes the outermost bundle that it can -// find (so for "/Foo/Bar.app/.../Baz.app/..." it produces "/Foo/Bar.app"). -// |exec_name| - path to the binary -// returns - path to the application bundle, or empty on error -FilePath GetAppBundlePath(const FilePath& exec_name) { - const char kExt[] = ".app"; - const size_t kExtLength = arraysize(kExt) - 1; - - // Split the path into components. - std::vector<std::string> components; - exec_name.GetComponents(&components); - - // It's an error if we don't get any components. - if (!components.size()) - return FilePath(); - - // Don't prepend '/' to the first component. - std::vector<std::string>::const_iterator it = components.begin(); - std::string bundle_name = *it; - DCHECK(it->length() > 0); - // If the first component ends in ".app", we're already done. - if (it->length() > kExtLength && - !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) - return FilePath(bundle_name); - - // The first component may be "/" or "//", etc. Only append '/' if it doesn't - // already end in '/'. - if (bundle_name[bundle_name.length() - 1] != '/') - bundle_name += '/'; - - // Go through the remaining components. - for (++it; it != components.end(); ++it) { - DCHECK(it->length() > 0); - - bundle_name += *it; - - // If the current component ends in ".app", we're done. - if (it->length() > kExtLength && - !it->compare(it->length() - kExtLength, kExtLength, kExt, kExtLength)) - return FilePath(bundle_name); - - // Separate this component from the next one. - bundle_name += '/'; - } - - return FilePath(); -} - bool SetFileBackupExclusion(const FilePath& file_path, bool exclude) { NSString* filePath = [NSString stringWithUTF8String:file_path.value().c_str()]; @@ -478,31 +281,6 @@ bool SetFileBackupExclusion(const FilePath& file_path, bool exclude) { return success; } -CFTypeRef GetValueFromDictionary(CFDictionaryRef dict, - CFStringRef key, - CFTypeID expected_type) { - CFTypeRef value = CFDictionaryGetValue(dict, key); - if (!value) - return value; - - if (CFGetTypeID(value) != expected_type) { - ScopedCFTypeRef<CFStringRef> expected_type_ref( - CFCopyTypeIDDescription(expected_type)); - ScopedCFTypeRef<CFStringRef> actual_type_ref( - CFCopyTypeIDDescription(CFGetTypeID(value))); - LOG(WARNING) << "Expected value for key " - << base::SysCFStringRefToUTF8(key) - << " to be " - << base::SysCFStringRefToUTF8(expected_type_ref) - << " but it was " - << base::SysCFStringRefToUTF8(actual_type_ref) - << " instead"; - return NULL; - } - - return value; -} - void SetProcessName(CFStringRef process_name) { if (!process_name || CFStringGetLength(process_name) == 0) { NOTREACHED() << "SetProcessName given bad name."; @@ -702,15 +480,5 @@ bool WasLaunchedAsHiddenLoginItem() { return IsHiddenLoginItem(item); } -void NSObjectRetain(void* obj) { - id<NSObject> nsobj = static_cast<id<NSObject> >(obj); - [nsobj retain]; -} - -void NSObjectRelease(void* obj) { - id<NSObject> nsobj = static_cast<id<NSObject> >(obj); - [nsobj release]; -} - } // namespace mac } // namespace base diff --git a/base/mac/mac_util_unittest.mm b/base/mac/mac_util_unittest.mm index 47ecebf..bae0019 100644 --- a/base/mac/mac_util_unittest.mm +++ b/base/mac/mac_util_unittest.mm @@ -1,18 +1,15 @@ -// 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. #import <Cocoa/Cocoa.h> -#include <vector> #include "base/mac/mac_util.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/mac/scoped_cftyperef.h" -#include "base/test/mock_chrome_application_mac.h" #include "base/scoped_nsobject.h" -#include "base/scoped_ptr.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -53,37 +50,6 @@ TEST_F(MacUtilTest, TestLibraryPath) { EXPECT_FALSE(library_dir.value().empty()); } -TEST_F(MacUtilTest, TestGrabWindowSnapshot) { - // Launch a test window so we can take a snapshot. - [MockCrApp sharedApplication]; - NSRect frame = NSMakeRect(0, 0, 400, 400); - scoped_nsobject<NSWindow> window( - [[NSWindow alloc] initWithContentRect:frame - styleMask:NSBorderlessWindowMask - backing:NSBackingStoreBuffered - defer:NO]); - [window setBackgroundColor:[NSColor whiteColor]]; - [window makeKeyAndOrderFront:NSApp]; - - scoped_ptr<std::vector<unsigned char> > png_representation( - new std::vector<unsigned char>); - int width, height; - GrabWindowSnapshot(window, png_representation.get(), - &width, &height); - - // Copy png back into NSData object so we can make sure we grabbed a png. - scoped_nsobject<NSData> image_data( - [[NSData alloc] initWithBytes:&(*png_representation)[0] - length:png_representation->size()]); - NSBitmapImageRep* rep = [NSBitmapImageRep imageRepWithData:image_data.get()]; - EXPECT_TRUE([rep isKindOfClass:[NSBitmapImageRep class]]); - EXPECT_TRUE(CGImageGetWidth([rep CGImage]) == 400); - NSColor* color = [rep colorAtX:200 y:200]; - CGFloat red = 0, green = 0, blue = 0, alpha = 0; - [color getRed:&red green:&green blue:&blue alpha:&alpha]; - EXPECT_GE(red + green + blue, 3.0); -} - TEST_F(MacUtilTest, TestGetAppBundlePath) { FilePath out; diff --git a/base/message_loop.cc b/base/message_loop.cc index 7e35d9f..2ec872f 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -289,6 +289,12 @@ void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) { task_observers_.RemoveObserver(task_observer); } +void MessageLoop::AssertIdle() const { + // We only check |incoming_queue_|, since we don't want to lock |work_queue_|. + base::AutoLock lock(incoming_queue_lock_); + DCHECK(incoming_queue_.empty()); +} + //------------------------------------------------------------------------------ // Runs the loop in two different SEH modes: @@ -399,7 +405,7 @@ void MessageLoop::ReloadWorkQueue() { // Acquire all we can from the inter-thread queue with one lock acquisition. { - AutoLock lock(incoming_queue_lock_); + base::AutoLock lock(incoming_queue_lock_); if (incoming_queue_.empty()) return; incoming_queue_.Swap(&work_queue_); // Constant time @@ -500,7 +506,7 @@ void MessageLoop::PostTask_Helper( scoped_refptr<base::MessagePump> pump; { - AutoLock locked(incoming_queue_lock_); + base::AutoLock locked(incoming_queue_lock_); bool was_empty = incoming_queue_.empty(); incoming_queue_.push(pending_task); diff --git a/base/message_loop.h b/base/message_loop.h index d5093a9..d238997 100644 --- a/base/message_loop.h +++ b/base/message_loop.h @@ -10,10 +10,10 @@ #include <string> #include "base/basictypes.h" -#include "base/lock.h" #include "base/message_pump.h" #include "base/observer_list.h" #include "base/ref_counted.h" +#include "base/synchronization/lock.h" #include "base/task.h" #if defined(OS_WIN) @@ -315,6 +315,9 @@ class MessageLoop : public base::MessagePump::Delegate { // for at least 1s. static const int kHighResolutionTimerModeLeaseTimeMs = 1000; + // Asserts that the MessageLoop is "idle". + void AssertIdle() const; + //---------------------------------------------------------------------------- protected: struct RunState { @@ -461,12 +464,12 @@ 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 - // aquired under a mutex for processing on this instance's thread. These tasks + // 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_. - Lock incoming_queue_lock_; + mutable base::Lock incoming_queue_lock_; RunState* state_; diff --git a/base/message_loop_proxy_impl.cc b/base/message_loop_proxy_impl.cc index 3b01fd6..b47c934 100644 --- a/base/message_loop_proxy_impl.cc +++ b/base/message_loop_proxy_impl.cc @@ -7,11 +7,6 @@ namespace base { -MessageLoopProxyImpl::MessageLoopProxyImpl() - : target_message_loop_(MessageLoop::current()) { - target_message_loop_->AddDestructionObserver(this); -} - MessageLoopProxyImpl::~MessageLoopProxyImpl() { AutoLock lock(message_loop_lock_); // If the target message loop still exists, the d'tor WILL execute on the @@ -56,25 +51,10 @@ bool MessageLoopProxyImpl::BelongsToCurrentThread() { (MessageLoop::current() == target_message_loop_)); } -bool MessageLoopProxyImpl::PostTaskHelper( - const tracked_objects::Location& from_here, Task* task, int64 delay_ms, - bool nestable) { - bool ret = false; - { - AutoLock lock(message_loop_lock_); - if (target_message_loop_) { - if (nestable) { - target_message_loop_->PostDelayedTask(from_here, task, delay_ms); - } else { - target_message_loop_->PostNonNestableDelayedTask(from_here, task, - delay_ms); - } - ret = true; - } - } - if (!ret) - delete task; - return ret; +// MessageLoop::DestructionObserver implementation +void MessageLoopProxyImpl::WillDestroyCurrentMessageLoop() { + AutoLock lock(message_loop_lock_); + target_message_loop_ = NULL; } void MessageLoopProxyImpl::OnDestruct() const { @@ -96,10 +76,30 @@ void MessageLoopProxyImpl::OnDestruct() const { delete this; } -// MessageLoop::DestructionObserver implementation -void MessageLoopProxyImpl::WillDestroyCurrentMessageLoop() { - AutoLock lock(message_loop_lock_); - target_message_loop_ = NULL; +MessageLoopProxyImpl::MessageLoopProxyImpl() + : target_message_loop_(MessageLoop::current()) { + target_message_loop_->AddDestructionObserver(this); +} + +bool MessageLoopProxyImpl::PostTaskHelper( + const tracked_objects::Location& from_here, Task* task, int64 delay_ms, + bool nestable) { + bool ret = false; + { + AutoLock lock(message_loop_lock_); + if (target_message_loop_) { + if (nestable) { + target_message_loop_->PostDelayedTask(from_here, task, delay_ms); + } else { + target_message_loop_->PostNonNestableDelayedTask(from_here, task, + delay_ms); + } + ret = true; + } + } + if (!ret) + delete task; + return ret; } scoped_refptr<MessageLoopProxy> diff --git a/base/message_loop_proxy_impl.h b/base/message_loop_proxy_impl.h index 44ab2ea..03e0271 100644 --- a/base/message_loop_proxy_impl.h +++ b/base/message_loop_proxy_impl.h @@ -6,9 +6,9 @@ #define BASE_MESSAGE_LOOP_PROXY_IMPL_H_ #pragma once -#include "base/lock.h" #include "base/message_loop.h" #include "base/message_loop_proxy.h" +#include "base/synchronization/lock.h" namespace base { @@ -50,7 +50,7 @@ class MessageLoopProxyImpl : public MessageLoopProxy, friend class MessageLoopProxy; // The lock that protects access to target_message_loop_. - mutable Lock message_loop_lock_; + mutable base::Lock message_loop_lock_; MessageLoop* target_message_loop_; DISALLOW_COPY_AND_ASSIGN(MessageLoopProxyImpl); diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc index c471e38..9576a58 100644 --- a/base/message_loop_unittest.cc +++ b/base/message_loop_unittest.cc @@ -1555,18 +1555,18 @@ TEST(MessageLoopTest, HighResolutionTimer) { const int kFastTimerMs = 5; const int kSlowTimerMs = 100; - EXPECT_EQ(false, loop.high_resolution_timers_enabled()); + EXPECT_FALSE(loop.high_resolution_timers_enabled()); // Post a fast task to enable the high resolution timers. loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kFastTimerMs); loop.Run(); - EXPECT_EQ(true, loop.high_resolution_timers_enabled()); + EXPECT_TRUE(loop.high_resolution_timers_enabled()); // Post a slow task and verify high resolution timers // are still enabled. loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kSlowTimerMs); loop.Run(); - EXPECT_EQ(true, loop.high_resolution_timers_enabled()); + EXPECT_TRUE(loop.high_resolution_timers_enabled()); // Wait for a while so that high-resolution mode elapses. Sleep(MessageLoop::kHighResolutionTimerModeLeaseTimeMs); @@ -1574,7 +1574,7 @@ TEST(MessageLoopTest, HighResolutionTimer) { // Post a slow task to disable the high resolution timers. loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kSlowTimerMs); loop.Run(); - EXPECT_EQ(false, loop.high_resolution_timers_enabled()); + EXPECT_FALSE(loop.high_resolution_timers_enabled()); } #endif // defined(OS_WIN) diff --git a/base/message_pump_glib.cc b/base/message_pump_glib.cc index b9dcc46..721fedb 100644 --- a/base/message_pump_glib.cc +++ b/base/message_pump_glib.cc @@ -303,16 +303,15 @@ void MessagePumpForUI::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } -MessagePumpForUI::Dispatcher* MessagePumpForUI::GetDispatcher() { - return state_ ? state_->dispatcher : NULL; -} - -void MessagePumpForUI::WillProcessEvent(GdkEvent* event) { - FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event)); -} - -void MessagePumpForUI::DidProcessEvent(GdkEvent* event) { - FOR_EACH_OBSERVER(Observer, observers_, DidProcessEvent(event)); +void MessagePumpForUI::DispatchEvents(GdkEvent* event) { + WillProcessEvent(event); + if (state_ && state_->dispatcher) { // state_ may be null during tests. + if (!state_->dispatcher->Dispatch(event)) + state_->should_quit = true; + } else { + gtk_main_do_event(event); + } + DidProcessEvent(event); } void MessagePumpForUI::Run(Delegate* delegate) { @@ -344,15 +343,16 @@ void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { ScheduleWork(); } -void MessagePumpForUI::DispatchEvents(GdkEvent* event) { - WillProcessEvent(event); - if (state_ && state_->dispatcher) { // state_ may be null during tests. - if (!state_->dispatcher->Dispatch(event)) - state_->should_quit = true; - } else { - gtk_main_do_event(event); - } - DidProcessEvent(event); +MessagePumpForUI::Dispatcher* MessagePumpForUI::GetDispatcher() { + return state_ ? state_->dispatcher : NULL; +} + +void MessagePumpForUI::WillProcessEvent(GdkEvent* event) { + FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event)); +} + +void MessagePumpForUI::DidProcessEvent(GdkEvent* event) { + FOR_EACH_OBSERVER(Observer, observers_, DidProcessEvent(event)); } // static diff --git a/base/message_pump_glib_unittest.cc b/base/message_pump_glib_unittest.cc index 9c9f288..fc604b9 100644 --- a/base/message_pump_glib_unittest.cc +++ b/base/message_pump_glib_unittest.cc @@ -45,7 +45,6 @@ class EventInjector { bool HandleCheck() { if (events_.empty()) return false; - Event event = events_[0]; return events_[0].time <= base::Time::NowFromSystemTime(); } diff --git a/base/message_pump_glib_x.cc b/base/message_pump_glib_x.cc index 775e940..e27a07a 100644 --- a/base/message_pump_glib_x.cc +++ b/base/message_pump_glib_x.cc @@ -82,6 +82,41 @@ MessagePumpGlibX::MessagePumpGlibX() : base::MessagePumpForUI(), MessagePumpGlibX::~MessagePumpGlibX() { } +#if defined(HAVE_XINPUT2) +void MessagePumpGlibX::SetupXInput2ForXWindow(Window xwindow) { + Display* xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); + + // Setup mask for mouse events. + unsigned char mask[(XI_LASTEVENT + 7)/8]; + memset(mask, 0, sizeof(mask)); + + XISetMask(mask, XI_ButtonPress); + XISetMask(mask, XI_ButtonRelease); + XISetMask(mask, XI_Motion); + + // It is necessary to select only for the master devices. XInput2 provides + // enough information to the event callback to decide which slave device + // triggered the event, thus decide whether the 'pointer event' is a 'mouse + // event' or a 'touch event'. So it is not necessary to select for the slave + // devices here. + XIEventMask evmasks[masters_.size()]; + int count = 0; + for (std::set<int>::const_iterator iter = masters_.begin(); + iter != masters_.end(); + ++iter, ++count) { + evmasks[count].deviceid = *iter; + evmasks[count].mask_len = sizeof(mask); + evmasks[count].mask = mask; + } + + XISelectEvents(xdisplay, xwindow, evmasks, masters_.size()); + + // TODO(sad): Setup masks for keyboard events. + + XFlush(xdisplay); +} +#endif // HAVE_XINPUT2 + bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) { GdkDisplay* gdisp = gdk_display_get_default(); if (!gdisp) @@ -110,7 +145,7 @@ bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) { MessagePumpGlibXDispatcher::DispatchStatus status = static_cast<MessagePumpGlibXDispatcher*> - (GetDispatcher())->Dispatch(&xev); + (GetDispatcher())->DispatchX(&xev); if (status == MessagePumpGlibXDispatcher::EVENT_QUIT) { should_quit = true; @@ -166,6 +201,25 @@ bool MessagePumpGlibX::RunOnce(GMainContext* context, bool block) { return retvalue; } +void MessagePumpGlibX::EventDispatcherX(GdkEvent* event, gpointer data) { + MessagePumpGlibX* pump_x = reinterpret_cast<MessagePumpGlibX*>(data); + + if (!pump_x->gdksource_) { + pump_x->gdksource_ = g_main_current_source(); + pump_x->gdkdispatcher_ = pump_x->gdksource_->source_funcs->dispatch; + } else if (!pump_x->IsDispatchingEvent()) { + if (event->type != GDK_NOTHING && + pump_x->capture_gdk_events_[event->type]) { + // TODO(sad): An X event is caught by the GDK handler. Put it back in the + // X queue so that we catch it in the next iteration. When done, the + // following DLOG statement will be removed. + DLOG(WARNING) << "GDK received an event it shouldn't have"; + } + } + + pump_x->DispatchEvents(event); +} + void MessagePumpGlibX::InitializeEventsToCapture(void) { // TODO(sad): Decide which events we want to capture and update the tables // accordingly. @@ -237,59 +291,6 @@ void MessagePumpGlibX::InitializeXInput2(void) { // put it off for a later time. // Note: It is not necessary to listen for XI_DeviceChanged events. } - -void MessagePumpGlibX::SetupXInput2ForXWindow(Window xwindow) { - Display* xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); - - // Setup mask for mouse events. - unsigned char mask[(XI_LASTEVENT + 7)/8]; - memset(mask, 0, sizeof(mask)); - - XISetMask(mask, XI_ButtonPress); - XISetMask(mask, XI_ButtonRelease); - XISetMask(mask, XI_Motion); - - // It is necessary to select only for the master devices. XInput2 provides - // enough information to the event callback to decide which slave device - // triggered the event, thus decide whether the 'pointer event' is a 'mouse - // event' or a 'touch event'. So it is not necessary to select for the slave - // devices here. - XIEventMask evmasks[masters_.size()]; - int count = 0; - for (std::set<int>::const_iterator iter = masters_.begin(); - iter != masters_.end(); - ++iter, ++count) { - evmasks[count].deviceid = *iter; - evmasks[count].mask_len = sizeof(mask); - evmasks[count].mask = mask; - } - - XISelectEvents(xdisplay, xwindow, evmasks, masters_.size()); - - // TODO(sad): Setup masks for keyboard events. - - XFlush(xdisplay); -} - #endif // HAVE_XINPUT2 -void MessagePumpGlibX::EventDispatcherX(GdkEvent* event, gpointer data) { - MessagePumpGlibX* pump_x = reinterpret_cast<MessagePumpGlibX*>(data); - - if (!pump_x->gdksource_) { - pump_x->gdksource_ = g_main_current_source(); - pump_x->gdkdispatcher_ = pump_x->gdksource_->source_funcs->dispatch; - } else if (!pump_x->IsDispatchingEvent()) { - if (event->type != GDK_NOTHING && - pump_x->capture_gdk_events_[event->type]) { - // TODO(sad): An X event is caught by the GDK handler. Put it back in the - // X queue so that we catch it in the next iteration. When done, the - // following DLOG statement will be removed. - DLOG(WARNING) << "GDK received an event it shouldn't have"; - } - } - - pump_x->DispatchEvents(event); -} - } // namespace base diff --git a/base/message_pump_glib_x_dispatch.h b/base/message_pump_glib_x_dispatch.h index 4a97372..4c44cab 100644 --- a/base/message_pump_glib_x_dispatch.h +++ b/base/message_pump_glib_x_dispatch.h @@ -28,7 +28,7 @@ class MessagePumpGlibXDispatcher : public MessagePumpForUI::Dispatcher { // Dispatches the event. EVENT_IGNORED is returned if the event was ignored // (i.e. not processed). EVENT_PROCESSED is returned if the event was // processed. The nested loop exits immediately if EVENT_QUIT is returned. - virtual DispatchStatus Dispatch(XEvent* xevent) = 0; + virtual DispatchStatus DispatchX(XEvent* xevent) = 0; }; } // namespace base diff --git a/base/message_pump_libevent.cc b/base/message_pump_libevent.cc index 1410f79..933d795 100644 --- a/base/message_pump_libevent.cc +++ b/base/message_pump_libevent.cc @@ -62,6 +62,19 @@ MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() { } } +bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() { + event* e = ReleaseEvent(); + if (e == NULL) + return true; + + // event_del() is a no-op if the event isn't active. + int rv = event_del(e); + delete e; + pump_ = NULL; + watcher_ = NULL; + return (rv == 0); +} + void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e, bool is_persistent) { DCHECK(e); @@ -77,19 +90,6 @@ event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() { return e; } -bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() { - event* e = ReleaseEvent(); - if (e == NULL) - return true; - - // event_del() is a no-op if the event isn't active. - int rv = event_del(e); - delete e; - pump_ = NULL; - watcher_ = NULL; - return (rv == 0); -} - void MessagePumpLibevent::FileDescriptorWatcher::OnFileCanReadWithoutBlocking( int fd, MessagePumpLibevent* pump) { pump->WillProcessIOEvent(); @@ -104,20 +104,6 @@ void MessagePumpLibevent::FileDescriptorWatcher::OnFileCanWriteWithoutBlocking( pump->DidProcessIOEvent(); } -// Called if a byte is received on the wakeup pipe. -void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) { - base::MessagePumpLibevent* that = - static_cast<base::MessagePumpLibevent*>(context); - DCHECK(that->wakeup_pipe_out_ == socket); - - // Remove and discard the wakeup byte. - char buf; - int nread = HANDLE_EINTR(read(socket, &buf, 1)); - DCHECK_EQ(nread, 1); - // Tell libevent to break out of inner loop. - event_base_loopbreak(that->event_base_); -} - MessagePumpLibevent::MessagePumpLibevent() : keep_running_(true), in_run_(false), @@ -128,33 +114,6 @@ MessagePumpLibevent::MessagePumpLibevent() NOTREACHED(); } -bool MessagePumpLibevent::Init() { - int fds[2]; - if (pipe(fds)) { - DLOG(ERROR) << "pipe() failed, errno: " << errno; - return false; - } - if (SetNonBlocking(fds[0])) { - DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno; - return false; - } - if (SetNonBlocking(fds[1])) { - DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno; - return false; - } - wakeup_pipe_out_ = fds[0]; - wakeup_pipe_in_ = fds[1]; - - wakeup_event_ = new event; - event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST, - OnWakeup, this); - event_base_set(event_base_, wakeup_event_); - - if (event_add(wakeup_event_, 0)) - return false; - return true; -} - MessagePumpLibevent::~MessagePumpLibevent() { DCHECK(wakeup_event_); DCHECK(event_base_); @@ -234,19 +193,12 @@ bool MessagePumpLibevent::WatchFileDescriptor(int fd, return true; } -void MessagePumpLibevent::OnLibeventNotification(int fd, short flags, - void* context) { - FileDescriptorWatcher* controller = - static_cast<FileDescriptorWatcher*>(context); - - MessagePumpLibevent* pump = controller->pump(); +void MessagePumpLibevent::AddIOObserver(IOObserver *obs) { + io_observers_.AddObserver(obs); +} - if (flags & EV_WRITE) { - controller->OnFileCanWriteWithoutBlocking(fd, pump); - } - if (flags & EV_READ) { - controller->OnFileCanReadWithoutBlocking(fd, pump); - } +void MessagePumpLibevent::RemoveIOObserver(IOObserver *obs) { + io_observers_.RemoveObserver(obs); } // Tell libevent to break out of inner loop. @@ -334,14 +286,6 @@ void MessagePumpLibevent::ScheduleDelayedWork( delayed_work_time_ = delayed_work_time; } -void MessagePumpLibevent::AddIOObserver(IOObserver *obs) { - io_observers_.AddObserver(obs); -} - -void MessagePumpLibevent::RemoveIOObserver(IOObserver *obs) { - io_observers_.RemoveObserver(obs); -} - void MessagePumpLibevent::WillProcessIOEvent() { FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent()); } @@ -350,4 +294,62 @@ void MessagePumpLibevent::DidProcessIOEvent() { FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent()); } +bool MessagePumpLibevent::Init() { + int fds[2]; + if (pipe(fds)) { + DLOG(ERROR) << "pipe() failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[0])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[1])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno; + return false; + } + wakeup_pipe_out_ = fds[0]; + wakeup_pipe_in_ = fds[1]; + + wakeup_event_ = new event; + event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST, + OnWakeup, this); + event_base_set(event_base_, wakeup_event_); + + if (event_add(wakeup_event_, 0)) + return false; + return true; +} + +// static +void MessagePumpLibevent::OnLibeventNotification(int fd, short flags, + void* context) { + FileDescriptorWatcher* controller = + static_cast<FileDescriptorWatcher*>(context); + + MessagePumpLibevent* pump = controller->pump(); + + if (flags & EV_WRITE) { + controller->OnFileCanWriteWithoutBlocking(fd, pump); + } + if (flags & EV_READ) { + controller->OnFileCanReadWithoutBlocking(fd, pump); + } +} + +// Called if a byte is received on the wakeup pipe. +// static +void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) { + base::MessagePumpLibevent* that = + static_cast<base::MessagePumpLibevent*>(context); + DCHECK(that->wakeup_pipe_out_ == socket); + + // Remove and discard the wakeup byte. + char buf; + int nread = HANDLE_EINTR(read(socket, &buf, 1)); + DCHECK_EQ(nread, 1); + // Tell libevent to break out of inner loop. + event_base_loopbreak(that->event_base_); +} + } // namespace base diff --git a/base/message_pump_win.cc b/base/message_pump_win.cc index 0df888a..d3f3621 100644 --- a/base/message_pump_win.cc +++ b/base/message_pump_win.cc @@ -421,7 +421,7 @@ void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, IOHandler* handler) { ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler); HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1); - DCHECK(port == port_.Get()); + DPCHECK(port); } //----------------------------------------------------------------------------- diff --git a/base/metrics/field_trial.cc b/base/metrics/field_trial.cc index d29ed2d..fceccde 100644 --- a/base/metrics/field_trial.cc +++ b/base/metrics/field_trial.cc @@ -7,14 +7,15 @@ #include "base/logging.h" #include "base/rand_util.h" #include "base/stringprintf.h" +#include "base/utf_string_conversions.h" namespace base { // static -const int FieldTrial::kNotParticipating = -1; +const int FieldTrial::kNotFinalized = -1; // static -const int FieldTrial::kAllRemainingProbability = -2; +const int FieldTrial::kDefaultGroupNumber = 0; // static bool FieldTrial::enable_benchmarking_ = false; @@ -28,30 +29,53 @@ static const char kHistogramFieldTrialSeparator('_'); // FieldTrial methods and members. FieldTrial::FieldTrial(const std::string& name, - const Probability total_probability) + const Probability total_probability, + const std::string& default_group_name, + const int year, + const int month, + const int day_of_month) : name_(name), divisor_(total_probability), + default_group_name_(default_group_name), random_(static_cast<Probability>(divisor_ * base::RandDouble())), accumulated_group_probability_(0), - next_group_number_(0), - group_(kNotParticipating) { + next_group_number_(kDefaultGroupNumber+1), + group_(kNotFinalized) { + DCHECK(!default_group_name_.empty()); FieldTrialList::Register(this); + + DCHECK_GT(year, 1970); + DCHECK_GT(month, 0); + DCHECK_LT(month, 13); + DCHECK_GT(day_of_month, 0); + DCHECK_LT(day_of_month, 32); + + base::Time::Exploded exploded; + exploded.year = year; + exploded.month = month; + exploded.day_of_week = 0; // Should be unusued. + exploded.day_of_month = day_of_month; + exploded.hour = 0; + exploded.minute = 0; + exploded.second = 0; + exploded.millisecond = 0; + + base::Time expiration_time = Time::FromLocalExploded(exploded); + disable_field_trial_ = (GetBuildTime() > expiration_time) ? true : false; } int FieldTrial::AppendGroup(const std::string& name, Probability group_probability) { DCHECK(group_probability <= divisor_); - DCHECK(group_probability >=0 || - group_probability == kAllRemainingProbability); - if (group_probability == kAllRemainingProbability) { - accumulated_group_probability_ = divisor_; - } else { - if (enable_benchmarking_) - group_probability = 0; - accumulated_group_probability_ += group_probability; - } + DCHECK_GE(group_probability, 0); + + if (enable_benchmarking_ || disable_field_trial_) + group_probability = 0; + + accumulated_group_probability_ += group_probability; + DCHECK(accumulated_group_probability_ <= divisor_); - if (group_ == kNotParticipating && accumulated_group_probability_ > random_) { + if (group_ == kNotFinalized && accumulated_group_probability_ > random_) { // This is the group that crossed the random line, so we do the assignment. group_ = next_group_number_; if (name.empty()) @@ -62,6 +86,20 @@ int FieldTrial::AppendGroup(const std::string& name, return next_group_number_++; } +int FieldTrial::group() { + if (group_ == kNotFinalized) { + accumulated_group_probability_ = divisor_; + group_ = kDefaultGroupNumber; + group_name_ = default_group_name_; + } + return group_; +} + +std::string FieldTrial::group_name() { + group(); // call group() to make group assignment was done. + return group_name_; +} + // static std::string FieldTrial::MakeName(const std::string& name_prefix, const std::string& trial_name) { @@ -78,6 +116,16 @@ void FieldTrial::EnableBenchmarking() { FieldTrial::~FieldTrial() {} +// static +Time FieldTrial::GetBuildTime() { + Time integral_build_time; + const char* kDateTime = __DATE__ " " __TIME__; + bool result = Time::FromString(ASCIIToWide(kDateTime).c_str(), + &integral_build_time); + DCHECK(result); + return integral_build_time; +} + //------------------------------------------------------------------------------ // FieldTrialList methods and members. @@ -117,11 +165,19 @@ void FieldTrialList::Register(FieldTrial* trial) { } // static +FieldTrial* FieldTrialList::Find(const std::string& name) { + if (!global_) + return NULL; + AutoLock auto_lock(global_->lock_); + return global_->PreLockedFind(name); +} + +// static int FieldTrialList::FindValue(const std::string& name) { FieldTrial* field_trial = Find(name); if (field_trial) return field_trial->group(); - return FieldTrial::kNotParticipating; + return FieldTrial::kNotFinalized; } // static @@ -133,21 +189,6 @@ std::string FieldTrialList::FindFullName(const std::string& name) { } // static -FieldTrial* FieldTrialList::Find(const std::string& name) { - if (!global_) - return NULL; - AutoLock auto_lock(global_->lock_); - return global_->PreLockedFind(name); -} - -FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { - RegistrationList::iterator it = registered_.find(name); - if (registered_.end() == it) - return NULL; - return it->second; -} - -// static void FieldTrialList::StatesToString(std::string* output) { if (!global_) return; @@ -156,9 +197,11 @@ void FieldTrialList::StatesToString(std::string* output) { for (RegistrationList::iterator it = global_->registered_.begin(); it != global_->registered_.end(); ++it) { const std::string name = it->first; - const std::string group_name = it->second->group_name(); + std::string group_name = it->second->group_name_internal(); if (group_name.empty()) - continue; // No definitive winner in this trial. + // No definitive winner in this trial, use default_group_name as the + // group_name. + group_name = it->second->default_group_name(); DCHECK_EQ(name.find(kPersistentStringSeparator), std::string::npos); DCHECK_EQ(group_name.find(kPersistentStringSeparator), std::string::npos); output->append(name); @@ -169,34 +212,43 @@ void FieldTrialList::StatesToString(std::string* output) { } // static -bool FieldTrialList::StringAugmentsState(const std::string& prior_state) { +bool FieldTrialList::CreateTrialsInChildProcess( + const std::string& parent_trials) { DCHECK(global_); - if (prior_state.empty() || !global_) + if (parent_trials.empty() || !global_) return true; + Time::Exploded exploded; + Time two_years_from_now = + Time::NowFromSystemTime() + TimeDelta::FromDays(730); + two_years_from_now.LocalExplode(&exploded); + const int kTwoYearsFromNow = exploded.year; + size_t next_item = 0; - while (next_item < prior_state.length()) { - size_t name_end = prior_state.find(kPersistentStringSeparator, next_item); - if (name_end == prior_state.npos || next_item == name_end) + while (next_item < parent_trials.length()) { + size_t name_end = parent_trials.find(kPersistentStringSeparator, next_item); + if (name_end == parent_trials.npos || next_item == name_end) return false; - size_t group_name_end = prior_state.find(kPersistentStringSeparator, - name_end + 1); - if (group_name_end == prior_state.npos || name_end + 1 == group_name_end) + size_t group_name_end = parent_trials.find(kPersistentStringSeparator, + name_end + 1); + if (group_name_end == parent_trials.npos || name_end + 1 == group_name_end) return false; - std::string name(prior_state, next_item, name_end - next_item); - std::string group_name(prior_state, name_end + 1, + std::string name(parent_trials, next_item, name_end - next_item); + std::string group_name(parent_trials, name_end + 1, group_name_end - name_end - 1); next_item = group_name_end + 1; FieldTrial *field_trial(FieldTrialList::Find(name)); if (field_trial) { // In single process mode, we may have already created the field trial. - if (field_trial->group_name() != group_name) + if ((field_trial->group_name_internal() != group_name) && + (field_trial->default_group_name() != group_name)) return false; continue; } const int kTotalProbability = 100; - field_trial = new FieldTrial(name, kTotalProbability); + field_trial = new FieldTrial(name, kTotalProbability, group_name, + kTwoYearsFromNow, 1, 1); field_trial->AppendGroup(group_name, kTotalProbability); } return true; @@ -210,4 +262,11 @@ size_t FieldTrialList::GetFieldTrialCount() { return global_->registered_.size(); } +FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { + RegistrationList::iterator it = registered_.find(name); + if (registered_.end() == it) + return NULL; + return it->second; +} + } // namespace base diff --git a/base/metrics/field_trial.h b/base/metrics/field_trial.h index 8902077..ec3a483 100644 --- a/base/metrics/field_trial.h +++ b/base/metrics/field_trial.h @@ -66,36 +66,40 @@ #include <map> #include <string> -#include "base/lock.h" +#include "base/gtest_prod_util.h" #include "base/ref_counted.h" +#include "base/synchronization/lock.h" #include "base/time.h" namespace base { +class FieldTrialList; + class FieldTrial : public RefCounted<FieldTrial> { public: typedef int Probability; // Probability type for being selected in a trial. // A return value to indicate that a given instance has not yet had a group // assignment (and hence is not yet participating in the trial). - static const int kNotParticipating; + static const int kNotFinalized; - // Provide an easy way to assign all remaining probability to a group. Note - // that this will force an instance to participate, and make it illegal to - // attempt to probabalistically add any other groups to the trial. When doing - // A/B tests with timings, it is often best to define all groups, so that - // histograms will get unique names via the MakeName() methods. - static const Probability kAllRemainingProbability; + // This is the group number of the 'default' group. This provides an easy way + // to assign all the remaining probability to a group ('default'). + static const int kDefaultGroupNumber; // The name is used to register the instance with the FieldTrialList class, // and can be used to find the trial (only one trial can be present for each // name). // Group probabilities that are later supplied must sum to less than or equal - // to the total_probability. - FieldTrial(const std::string& name, Probability total_probability); + // to the total_probability. Arguments year, month and day_of_month specify + // the expiration time. If the build time is after the expiration time then + // the field trial reverts to the 'default' group. + FieldTrial(const std::string& name, Probability total_probability, + const std::string& default_group_name, const int year, + const int month, const int day_of_month); // Establish the name and probability of the next group in this trial. - // Sometimes, based on construction randomization, this call may causes the + // Sometimes, based on construction randomization, this call may cause the // provided group to be *THE* group selected for use in this instance. int AppendGroup(const std::string& name, Probability group_probability); @@ -103,14 +107,18 @@ class FieldTrial : public RefCounted<FieldTrial> { std::string name() const { return name_; } // Return the randomly selected group number that was assigned. - // Return kNotParticipating if the instance is not participating in the - // experiment. - int group() const { return group_; } + // Return kDefaultGroupNumber if the instance is in the 'default' group. + // Note that this will force an instance to participate, and make it illegal + // to attempt to probabalistically add any other groups to the trial. + int group(); // If the field trial is not in an experiment, this returns the empty string. // if the group's name is empty, a name of "_" concatenated with the group // number is used as the group name. - std::string group_name() const { return group_name_; } + std::string group_name(); + + // Return the default group name of the FieldTrial. + std::string default_group_name() const { return default_group_name_; } // Helper function for the most common use: as an argument to specifiy the // name of a HISTOGRAM. Use the original histogram name as the name_prefix. @@ -121,17 +129,40 @@ class FieldTrial : public RefCounted<FieldTrial> { static void EnableBenchmarking(); private: + // Allow tests to access our innards for testing purposes. + FRIEND_TEST(FieldTrialTest, Registration); + FRIEND_TEST(FieldTrialTest, AbsoluteProbabilities); + FRIEND_TEST(FieldTrialTest, RemainingProbability); + FRIEND_TEST(FieldTrialTest, FiftyFiftyProbability); + FRIEND_TEST(FieldTrialTest, MiddleProbabilities); + FRIEND_TEST(FieldTrialTest, OneWinner); + FRIEND_TEST(FieldTrialTest, DisableProbability); + FRIEND_TEST(FieldTrialTest, Save); + FRIEND_TEST(FieldTrialTest, DuplicateRestore); + FRIEND_TEST(FieldTrialTest, MakeName); + + friend class base::FieldTrialList; + friend class RefCounted<FieldTrial>; virtual ~FieldTrial(); + // Returns the group_name. A winner need not have been chosen. + std::string group_name_internal() const { return group_name_; } + + // Get build time. + static Time GetBuildTime(); + // The name of the field trial, as can be found via the FieldTrialList. // This is empty of the trial is not in the experiment. const std::string name_; // The maximum sum of all probabilities supplied, which corresponds to 100%. // This is the scaling factor used to adjust supplied probabilities. - Probability divisor_; + const Probability divisor_; + + // The name of the default group. + const std::string default_group_name_; // 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 @@ -144,15 +175,19 @@ class FieldTrial : public RefCounted<FieldTrial> { int next_group_number_; // The pseudo-randomly assigned group number. - // This is kNotParticipating if no group has been assigned. + // This is kNotFinalized if no group has been assigned. int group_; - // A textual name for the randomly selected group, including the Trial name. - // If this Trial is not a member of an group, this string is empty. + // A textual name for the randomly selected group. If this Trial is not a + // member of an group, this string is empty. std::string group_name_; + // When disable_field_trial_ is true, field trial reverts to the 'default' + // group. + bool disable_field_trial_; + // When benchmarking is enabled, field trials all revert to the 'default' - // bucket. + // group. static bool enable_benchmarking_; DISALLOW_COPY_AND_ASSIGN(FieldTrial); @@ -199,7 +234,7 @@ class FieldTrialList { // is commonly used in a sub-process, to carry randomly selected state in a // parent process into this sub-process. // Currently only the group_name_ and name_ are restored. - static bool StringAugmentsState(const std::string& prior_state); + static bool CreateTrialsInChildProcess(const std::string& prior_trials); // The time of construction of the global map is recorded in a static variable // and is commonly used by experiments to identify the time since the start @@ -236,7 +271,7 @@ class FieldTrialList { TimeTicks application_start_time_; // Lock for access to registered_. - Lock lock_; + base::Lock lock_; RegistrationList registered_; DISALLOW_COPY_AND_ASSIGN(FieldTrialList); diff --git a/base/metrics/field_trial_unittest.cc b/base/metrics/field_trial_unittest.cc index a8138bb..0af6d60 100644 --- a/base/metrics/field_trial_unittest.cc +++ b/base/metrics/field_trial_unittest.cc @@ -13,7 +13,23 @@ namespace base { class FieldTrialTest : public testing::Test { public: - FieldTrialTest() : trial_list_() { } + FieldTrialTest() : trial_list_() { + Time now = Time::NowFromSystemTime(); + TimeDelta oneYear = TimeDelta::FromDays(365); + Time::Exploded exploded; + + Time next_year_time = now + oneYear; + next_year_time.LocalExplode(&exploded); + next_year_ = exploded.year; + + Time last_year_time = now - oneYear; + last_year_time.LocalExplode(&exploded); + last_year_ = exploded.year; + } + + protected: + int next_year_; + int last_year_; private: FieldTrialList trial_list_; @@ -27,20 +43,22 @@ TEST_F(FieldTrialTest, Registration) { EXPECT_FALSE(FieldTrialList::Find(name1)); EXPECT_FALSE(FieldTrialList::Find(name2)); - FieldTrial* trial1 = new FieldTrial(name1, 10); - EXPECT_EQ(FieldTrial::kNotParticipating, trial1->group()); + FieldTrial* trial1 = + new FieldTrial(name1, 10, "default name 1 test", next_year_, 12, 31); + EXPECT_EQ(FieldTrial::kNotFinalized, trial1->group_); EXPECT_EQ(name1, trial1->name()); - EXPECT_EQ("", trial1->group_name()); + EXPECT_EQ("", trial1->group_name_internal()); trial1->AppendGroup("", 7); EXPECT_EQ(trial1, FieldTrialList::Find(name1)); EXPECT_FALSE(FieldTrialList::Find(name2)); - FieldTrial* trial2 = new FieldTrial(name2, 10); - EXPECT_EQ(FieldTrial::kNotParticipating, trial2->group()); + FieldTrial* trial2 = + new FieldTrial(name2, 10, "default name 2 test", next_year_, 12, 31); + EXPECT_EQ(FieldTrial::kNotFinalized, trial2->group_); EXPECT_EQ(name2, trial2->name()); - EXPECT_EQ("", trial2->group_name()); + EXPECT_EQ("", trial2->group_name_internal()); trial2->AppendGroup("a first group", 7); @@ -51,20 +69,28 @@ TEST_F(FieldTrialTest, Registration) { TEST_F(FieldTrialTest, AbsoluteProbabilities) { char always_true[] = " always true"; + char default_always_true[] = " default always true"; char always_false[] = " always false"; + char default_always_false[] = " default always false"; for (int i = 1; i < 250; ++i) { // Try lots of names, by changing the first character of the name. always_true[0] = i; + default_always_true[0] = i; always_false[0] = i; + default_always_false[0] = i; - FieldTrial* trial_true = new FieldTrial(always_true, 10); + FieldTrial* trial_true = + new FieldTrial( + always_true, 10, default_always_true, next_year_, 12, 31); const std::string winner = "TheWinner"; int winner_group = trial_true->AppendGroup(winner, 10); EXPECT_EQ(winner_group, trial_true->group()); EXPECT_EQ(winner, trial_true->group_name()); - FieldTrial* trial_false = new FieldTrial(always_false, 10); + FieldTrial* trial_false = + new FieldTrial( + always_false, 10, default_always_false, next_year_, 12, 31); int loser_group = trial_false->AppendGroup("ALoser", 0); EXPECT_NE(loser_group, trial_false->group()); @@ -79,12 +105,13 @@ TEST_F(FieldTrialTest, RemainingProbability) { int counter = 0; do { std::string name = StringPrintf("trial%d", ++counter); - trial = new FieldTrial(name, 10); + trial = new FieldTrial(name, 10, winner, next_year_, 12, 31); trial->AppendGroup(loser, 5); // 50% chance of not being chosen. - } while (trial->group() != FieldTrial::kNotParticipating); + // If a group is not assigned, group_ will be kNotFinalized. + } while (trial->group_ != FieldTrial::kNotFinalized); - // Now add a winner with all remaining probability. - trial->AppendGroup(winner, FieldTrial::kAllRemainingProbability); + // And that 'default' group (winner) should always win. + EXPECT_EQ(FieldTrial::kDefaultGroupNumber, trial->group()); // And that winner should ALWAYS win. EXPECT_EQ(winner, trial->group_name()); @@ -100,14 +127,18 @@ TEST_F(FieldTrialTest, FiftyFiftyProbability) { int counter = 0; do { std::string name = base::StringPrintf("FiftyFifty%d", ++counter); - scoped_refptr<FieldTrial> trial(new FieldTrial(name, 2)); + std::string default_group_name = base::StringPrintf("Default FiftyFifty%d", + ++counter); + scoped_refptr<FieldTrial> trial( + new FieldTrial(name, 2, default_group_name, next_year_, 12, 31)); trial->AppendGroup("first", 1); // 50% chance of being chosen. - if (trial->group() != FieldTrial::kNotParticipating) { + // If group_ is kNotFinalized, then a group assignement hasn't been done. + if (trial->group_ != FieldTrial::kNotFinalized) { first_winner = true; continue; } trial->AppendGroup("second", 1); // Always chosen at this point. - EXPECT_NE(FieldTrial::kNotParticipating, trial->group()); + EXPECT_NE(FieldTrial::kNotFinalized, trial->group()); second_winner = true; } while ((!second_winner || !first_winner) && counter < 100); EXPECT_TRUE(second_winner); @@ -116,11 +147,14 @@ TEST_F(FieldTrialTest, FiftyFiftyProbability) { TEST_F(FieldTrialTest, MiddleProbabilities) { char name[] = " same name"; + char default_group_name[] = " default same name"; bool false_event_seen = false; bool true_event_seen = false; for (int i = 1; i < 250; ++i) { name[0] = i; - FieldTrial* trial = new FieldTrial(name, 10); + default_group_name[0] = i; + FieldTrial* trial = + new FieldTrial(name, 10, default_group_name, next_year_, 12, 31); int might_win = trial->AppendGroup("MightWin", 5); if (trial->group() == might_win) { @@ -139,16 +173,21 @@ TEST_F(FieldTrialTest, MiddleProbabilities) { TEST_F(FieldTrialTest, OneWinner) { char name[] = "Some name"; + char default_group_name[] = "Default some name"; int group_count(10); - FieldTrial* trial = new FieldTrial(name, group_count); + FieldTrial* trial = + new FieldTrial( + name, group_count, default_group_name, next_year_, 12, 31); int winner_index(-2); std::string winner_name; for (int i = 1; i <= group_count; ++i) { int might_win = trial->AppendGroup("", 1); - if (trial->group() == might_win) { + // Because we keep appending groups, we want to see if the last group that + // was added has been assigned or not. + if (trial->group_ == might_win) { EXPECT_EQ(-2, winner_index); winner_index = might_win; StringAppendF(&winner_name, "%d", might_win); @@ -160,14 +199,34 @@ TEST_F(FieldTrialTest, OneWinner) { EXPECT_EQ(trial->group_name(), winner_name); } +TEST_F(FieldTrialTest, DisableProbability) { + const std::string default_group_name = "Default group"; + const std::string loser = "Loser"; + const std::string name = "Trial"; + + // Create a field trail that has expired. + scoped_refptr<FieldTrial> trial; + trial = new FieldTrial( + name, 1000000000, default_group_name, last_year_, 1, 1); + trial->AppendGroup(loser, 999999999); // 99.9999999% chance of being chosen. + + // Because trial has expired, we should always be in the default group. + EXPECT_EQ(FieldTrial::kDefaultGroupNumber, trial->group()); + + // And that default_group_name should ALWAYS win. + EXPECT_EQ(default_group_name, trial->group_name()); +} + TEST_F(FieldTrialTest, Save) { std::string save_string; - FieldTrial* trial = new FieldTrial("Some name", 10); + FieldTrial* trial = + new FieldTrial( + "Some name", 10, "Default some name", next_year_, 12, 31); // There is no winner yet, so no textual group name is associated with trial. - EXPECT_EQ("", trial->group_name()); + EXPECT_EQ("", trial->group_name_internal()); FieldTrialList::StatesToString(&save_string); - EXPECT_EQ("", save_string); + EXPECT_EQ("Some name/Default some name/", save_string); save_string.clear(); // Create a winning group. @@ -177,7 +236,8 @@ TEST_F(FieldTrialTest, Save) { save_string.clear(); // Create a second trial and winning group. - FieldTrial* trial2 = new FieldTrial("xxx", 10); + FieldTrial* trial2 = + new FieldTrial("xxx", 10, "Default xxx", next_year_, 12, 31); trial2->AppendGroup("yyyy", 10); FieldTrialList::StatesToString(&save_string); @@ -189,7 +249,7 @@ TEST_F(FieldTrialTest, Restore) { EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL); EXPECT_TRUE(FieldTrialList::Find("xxx") == NULL); - FieldTrialList::StringAugmentsState("Some_name/Winner/xxx/yyyy/"); + FieldTrialList::CreateTrialsInChildProcess("Some_name/Winner/xxx/yyyy/"); FieldTrial* trial = FieldTrialList::Find("Some_name"); ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); @@ -203,29 +263,34 @@ TEST_F(FieldTrialTest, Restore) { } TEST_F(FieldTrialTest, BogusRestore) { - EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingSlash")); - EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingGroupName/")); - EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingFinalSlash/gname")); - EXPECT_FALSE(FieldTrialList::StringAugmentsState("/noname, only group/")); + EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess("MissingSlash")); + EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess("MissingGroupName/")); + EXPECT_FALSE( + FieldTrialList::CreateTrialsInChildProcess("MissingFinalSlash/gname")); + EXPECT_FALSE( + FieldTrialList::CreateTrialsInChildProcess("/noname, only group/")); } TEST_F(FieldTrialTest, DuplicateRestore) { - FieldTrial* trial = new FieldTrial("Some name", 10); + FieldTrial* trial = + new FieldTrial( + "Some name", 10, "Default some name", next_year_, 12, 31); trial->AppendGroup("Winner", 10); std::string save_string; FieldTrialList::StatesToString(&save_string); EXPECT_EQ("Some name/Winner/", save_string); // It is OK if we redundantly specify a winner. - EXPECT_TRUE(FieldTrialList::StringAugmentsState(save_string)); + EXPECT_TRUE(FieldTrialList::CreateTrialsInChildProcess(save_string)); // But it is an error to try to change to a different winner. - EXPECT_FALSE(FieldTrialList::StringAugmentsState("Some name/Loser/")); + EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess("Some name/Loser/")); } TEST_F(FieldTrialTest, MakeName) { - FieldTrial* trial = new FieldTrial("Field Trial", 10); - trial->AppendGroup("Winner", 10); + FieldTrial* trial = + new FieldTrial("Field Trial", 10, "Winner", next_year_, 12, 31); + trial->group(); EXPECT_EQ("Histogram_Winner", FieldTrial::MakeName("Histogram", "Field Trial")); } diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index 1526cd8..a308932 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc @@ -53,48 +53,6 @@ scoped_refptr<Histogram> Histogram::FactoryTimeGet(const std::string& name, bucket_count, flags); } -Histogram::Histogram(const std::string& name, Sample minimum, - Sample maximum, size_t bucket_count) - : histogram_name_(name), - declared_min_(minimum), - declared_max_(maximum), - bucket_count_(bucket_count), - flags_(kNoFlags), - ranges_(bucket_count + 1, 0), - range_checksum_(0), - sample_() { - Initialize(); -} - -Histogram::Histogram(const std::string& name, TimeDelta minimum, - TimeDelta maximum, size_t bucket_count) - : histogram_name_(name), - declared_min_(static_cast<int> (minimum.InMilliseconds())), - declared_max_(static_cast<int> (maximum.InMilliseconds())), - bucket_count_(bucket_count), - flags_(kNoFlags), - ranges_(bucket_count + 1, 0), - range_checksum_(0), - sample_() { - Initialize(); -} - -Histogram::~Histogram() { - if (StatisticsRecorder::dump_on_exit()) { - std::string output; - WriteAscii(true, "\n", &output); - LOG(INFO) << output; - } - - // Just to make sure most derived class did this properly... - DCHECK(ValidateBucketRanges()); - DCHECK(HasValidRangeChecksum()); -} - -bool Histogram::PrintEmptyBucket(size_t index) const { - return true; -} - void Histogram::Add(int value) { if (value > kSampleType_MAX - 1) value = kSampleType_MAX - 1; @@ -190,31 +148,223 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline, DCHECK_EQ(sample_count, past); } -bool Histogram::ValidateBucketRanges() const { - // Standard assertions that all bucket ranges should satisfy. - DCHECK_EQ(bucket_count_ + 1, ranges_.size()); - DCHECK_EQ(0, ranges_[0]); - DCHECK_EQ(declared_min(), ranges_[1]); - DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]); - DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]); +// static +std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, + const SampleSet& snapshot) { + DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); + + Pickle pickle; + pickle.WriteString(histogram.histogram_name()); + pickle.WriteInt(histogram.declared_min()); + pickle.WriteInt(histogram.declared_max()); + pickle.WriteSize(histogram.bucket_count()); + pickle.WriteInt(histogram.range_checksum()); + pickle.WriteInt(histogram.histogram_type()); + pickle.WriteInt(histogram.flags()); + + snapshot.Serialize(&pickle); + return std::string(static_cast<const char*>(pickle.data()), pickle.size()); +} + +// static +bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { + if (histogram_info.empty()) { + return false; + } + + Pickle pickle(histogram_info.data(), + static_cast<int>(histogram_info.size())); + std::string histogram_name; + int declared_min; + int declared_max; + size_t bucket_count; + int range_checksum; + int histogram_type; + int pickle_flags; + SampleSet sample; + + void* iter = NULL; + if (!pickle.ReadString(&iter, &histogram_name) || + !pickle.ReadInt(&iter, &declared_min) || + !pickle.ReadInt(&iter, &declared_max) || + !pickle.ReadSize(&iter, &bucket_count) || + !pickle.ReadInt(&iter, &range_checksum) || + !pickle.ReadInt(&iter, &histogram_type) || + !pickle.ReadInt(&iter, &pickle_flags) || + !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { + LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; + return false; + } + DCHECK(pickle_flags & kIPCSerializationSourceFlag); + // Since these fields may have come from an untrusted renderer, do additional + // checks above and beyond those in Histogram::Initialize() + if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || + INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) { + LOG(ERROR) << "Values error decoding Histogram: " << histogram_name; + return false; + } + + Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag); + + DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type); + + scoped_refptr<Histogram> render_histogram(NULL); + + if (histogram_type == HISTOGRAM) { + render_histogram = Histogram::FactoryGet( + histogram_name, declared_min, declared_max, bucket_count, flags); + } else if (histogram_type == LINEAR_HISTOGRAM) { + render_histogram = LinearHistogram::FactoryGet( + histogram_name, declared_min, declared_max, bucket_count, flags); + } else if (histogram_type == BOOLEAN_HISTOGRAM) { + render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); + } else { + LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " + << histogram_type; + return false; + } + + DCHECK_EQ(render_histogram->declared_min(), declared_min); + DCHECK_EQ(render_histogram->declared_max(), declared_max); + DCHECK_EQ(render_histogram->bucket_count(), bucket_count); + DCHECK_EQ(render_histogram->range_checksum(), range_checksum); + DCHECK_EQ(render_histogram->histogram_type(), histogram_type); + + if (render_histogram->flags() & kIPCSerializationSourceFlag) { + DVLOG(1) << "Single process mode, histogram observed and not copied: " + << histogram_name; + } else { + DCHECK_EQ(flags & render_histogram->flags(), flags); + render_histogram->AddSampleSet(sample); + } + return true; } -void Histogram::Initialize() { - sample_.Resize(*this); - if (declared_min_ < 1) - declared_min_ = 1; - if (declared_max_ > kSampleType_MAX - 1) - declared_max_ = kSampleType_MAX - 1; - DCHECK_LE(declared_min_, declared_max_); - DCHECK_GT(bucket_count_, 1u); - 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(); +//------------------------------------------------------------------------------ +// Methods for the validating a sample and a related histogram. +//------------------------------------------------------------------------------ + +Histogram::Inconsistencies Histogram::FindCorruption( + const SampleSet& snapshot) const { + int inconsistencies = NO_INCONSISTENCIES; + Sample previous_range = -1; // Bottom range is always 0. + 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_) + inconsistencies |= RANGE_CHECKSUM_ERROR; + + int64 delta64 = snapshot.redundant_count() - count; + if (delta64 != 0) { + int delta = static_cast<int>(delta64); + if (delta != delta64) + delta = INT_MAX; // Flag all giant errors as INT_MAX. + // Since snapshots of histograms are taken asynchronously relative to + // sampling (and snapped from different threads), it is pretty likely that + // we'll catch a redundant count that doesn't match the sample count. We + // allow for a certain amount of slop before flagging this as an + // inconsistency. Even with an inconsistency, we'll snapshot it again (for + // UMA in about a half hour, so we'll eventually get the data, if it was + // not the result of a corruption. If histograms show that 1 is "too tight" + // then we may try to use 2 or 3 for this slop value. + const int kCommonRaceBasedCountMismatch = 1; + if (delta > 0) { + UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta); + if (delta > kCommonRaceBasedCountMismatch) + inconsistencies |= COUNT_HIGH_ERROR; + } else { + DCHECK_GT(0, delta); + UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta); + if (-delta > kCommonRaceBasedCountMismatch) + inconsistencies |= COUNT_LOW_ERROR; + } + } + return static_cast<Inconsistencies>(inconsistencies); +} + +Histogram::ClassType Histogram::histogram_type() const { + return HISTOGRAM; +} + +Histogram::Sample Histogram::ranges(size_t i) const { + return ranges_[i]; +} + +size_t Histogram::bucket_count() const { + return bucket_count_; +} + +// Do a safe atomic snapshot of sample data. +// This implementation assumes we are on a safe single thread. +void Histogram::SnapshotSample(SampleSet* sample) const { + // Note locking not done in this version!!! + *sample = sample_; +} + +bool Histogram::HasConstructorArguments(Sample minimum, + Sample maximum, + size_t bucket_count) { + return ((minimum == declared_min_) && (maximum == declared_max_) && + (bucket_count == bucket_count_)); +} + +bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count) { + return ((minimum.InMilliseconds() == declared_min_) && + (maximum.InMilliseconds() == declared_max_) && + (bucket_count == bucket_count_)); +} + +Histogram::Histogram(const std::string& name, Sample minimum, + Sample maximum, size_t bucket_count) + : histogram_name_(name), + declared_min_(minimum), + declared_max_(maximum), + bucket_count_(bucket_count), + flags_(kNoFlags), + ranges_(bucket_count + 1, 0), + range_checksum_(0), + sample_() { + Initialize(); +} + +Histogram::Histogram(const std::string& name, TimeDelta minimum, + TimeDelta maximum, size_t bucket_count) + : histogram_name_(name), + declared_min_(static_cast<int> (minimum.InMilliseconds())), + declared_max_(static_cast<int> (maximum.InMilliseconds())), + bucket_count_(bucket_count), + flags_(kNoFlags), + ranges_(bucket_count + 1, 0), + range_checksum_(0), + sample_() { + Initialize(); +} + +Histogram::~Histogram() { + if (StatisticsRecorder::dump_on_exit()) { + std::string output; + WriteAscii(true, "\n", &output); + LOG(INFO) << output; + } + + // Just to make sure most derived class did this properly... DCHECK(ValidateBucketRanges()); - StatisticsRecorder::Register(this); + DCHECK(HasValidRangeChecksum()); +} + +bool Histogram::PrintEmptyBucket(size_t index) const { + return true; } // Calculate what range of values are held in each bucket. @@ -295,60 +445,64 @@ void Histogram::ResetRangeChecksum() { range_checksum_ = CalculateRangeChecksum(); } -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); - } - return checksum; +const std::string Histogram::GetAsciiBucketRange(size_t i) const { + std::string result; + if (kHexRangePrintingFlag & flags_) + StringAppendF(&result, "%#x", ranges(i)); + else + StringAppendF(&result, "%d", ranges(i)); + return result; } -//------------------------------------------------------------------------------ -// The following two methods can be overridden to provide a thread safe -// version of this class. The cost of locking is low... but an error in each -// of these methods has minimal impact. For now, I'll leave this unlocked, -// and I don't believe I can loose more than a count or two. -// The vectors are NOT reallocated, so there is no risk of them moving around. - // Update histogram data with new sample. void Histogram::Accumulate(Sample value, Count count, size_t index) { // Note locking not done in this version!!! sample_.Accumulate(value, count, index); } -// Do a safe atomic snapshot of sample data. -// This implementation assumes we are on a safe single thread. -void Histogram::SnapshotSample(SampleSet* sample) const { - // Note locking not done in this version!!! - *sample = sample_; +void Histogram::SetBucketRange(size_t i, Sample value) { + DCHECK_GT(bucket_count_, i); + ranges_[i] = value; } -bool Histogram::HasConstructorArguments(Sample minimum, - Sample maximum, - size_t bucket_count) { - return ((minimum == declared_min_) && (maximum == declared_max_) && - (bucket_count == bucket_count_)); +bool Histogram::ValidateBucketRanges() const { + // Standard assertions that all bucket ranges should satisfy. + DCHECK_EQ(bucket_count_ + 1, ranges_.size()); + DCHECK_EQ(0, ranges_[0]); + DCHECK_EQ(declared_min(), ranges_[1]); + DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]); + DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]); + return true; } -bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, - TimeDelta maximum, - size_t bucket_count) { - return ((minimum.InMilliseconds() == declared_min_) && - (maximum.InMilliseconds() == declared_max_) && - (bucket_count == bucket_count_)); +void Histogram::Initialize() { + sample_.Resize(*this); + if (declared_min_ < 1) + declared_min_ = 1; + if (declared_max_ > kSampleType_MAX - 1) + declared_max_ = kSampleType_MAX - 1; + DCHECK_LE(declared_min_, declared_max_); + DCHECK_GT(bucket_count_, 1u); + 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); } -//------------------------------------------------------------------------------ -// Accessor methods +bool Histogram::HasValidRangeChecksum() const { + return CalculateRangeChecksum() == range_checksum_; +} -void Histogram::SetBucketRange(size_t i, Sample value) { - DCHECK_GT(bucket_count_, i); - ranges_[i] = value; +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); + } + return checksum; } //------------------------------------------------------------------------------ @@ -400,15 +554,6 @@ void Histogram::WriteAsciiBucketContext(const int64 past, } } -const std::string Histogram::GetAsciiBucketRange(size_t i) const { - std::string result; - if (kHexRangePrintingFlag & flags_) - StringAppendF(&result, "%#x", ranges(i)); - else - StringAppendF(&result, "%d", ranges(i)); - return result; -} - void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, std::string* output) const { StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); @@ -428,161 +573,6 @@ void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, output->append(" "); } -// static -std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, - const SampleSet& snapshot) { - DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); - - Pickle pickle; - pickle.WriteString(histogram.histogram_name()); - pickle.WriteInt(histogram.declared_min()); - pickle.WriteInt(histogram.declared_max()); - pickle.WriteSize(histogram.bucket_count()); - pickle.WriteInt(histogram.range_checksum()); - pickle.WriteInt(histogram.histogram_type()); - pickle.WriteInt(histogram.flags()); - - snapshot.Serialize(&pickle); - return std::string(static_cast<const char*>(pickle.data()), pickle.size()); -} - -// static -bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { - if (histogram_info.empty()) { - return false; - } - - Pickle pickle(histogram_info.data(), - static_cast<int>(histogram_info.size())); - std::string histogram_name; - int declared_min; - int declared_max; - size_t bucket_count; - int range_checksum; - int histogram_type; - int pickle_flags; - SampleSet sample; - - void* iter = NULL; - if (!pickle.ReadString(&iter, &histogram_name) || - !pickle.ReadInt(&iter, &declared_min) || - !pickle.ReadInt(&iter, &declared_max) || - !pickle.ReadSize(&iter, &bucket_count) || - !pickle.ReadInt(&iter, &range_checksum) || - !pickle.ReadInt(&iter, &histogram_type) || - !pickle.ReadInt(&iter, &pickle_flags) || - !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { - LOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; - return false; - } - DCHECK(pickle_flags & kIPCSerializationSourceFlag); - // Since these fields may have come from an untrusted renderer, do additional - // checks above and beyond those in Histogram::Initialize() - if (declared_max <= 0 || declared_min <= 0 || declared_max < declared_min || - INT_MAX / sizeof(Count) <= bucket_count || bucket_count < 2) { - LOG(ERROR) << "Values error decoding Histogram: " << histogram_name; - return false; - } - - Flags flags = static_cast<Flags>(pickle_flags & ~kIPCSerializationSourceFlag); - - DCHECK_NE(NOT_VALID_IN_RENDERER, histogram_type); - - scoped_refptr<Histogram> render_histogram(NULL); - - if (histogram_type == HISTOGRAM) { - render_histogram = Histogram::FactoryGet( - histogram_name, declared_min, declared_max, bucket_count, flags); - } else if (histogram_type == LINEAR_HISTOGRAM) { - render_histogram = LinearHistogram::FactoryGet( - histogram_name, declared_min, declared_max, bucket_count, flags); - } else if (histogram_type == BOOLEAN_HISTOGRAM) { - render_histogram = BooleanHistogram::FactoryGet(histogram_name, flags); - } else { - LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " - << histogram_type; - return false; - } - - DCHECK_EQ(render_histogram->declared_min(), declared_min); - DCHECK_EQ(render_histogram->declared_max(), declared_max); - DCHECK_EQ(render_histogram->bucket_count(), bucket_count); - DCHECK_EQ(render_histogram->range_checksum(), range_checksum); - DCHECK_EQ(render_histogram->histogram_type(), histogram_type); - - if (render_histogram->flags() & kIPCSerializationSourceFlag) { - DVLOG(1) << "Single process mode, histogram observed and not copied: " - << histogram_name; - } else { - DCHECK_EQ(flags & render_histogram->flags(), flags); - render_histogram->AddSampleSet(sample); - } - - return true; -} - -//------------------------------------------------------------------------------ -// Methods for the validating a sample and a related histogram. -//------------------------------------------------------------------------------ - -Histogram::Inconsistencies Histogram::FindCorruption( - const SampleSet& snapshot) const { - int inconsistencies = NO_INCONSISTENCIES; - Sample previous_range = -1; // Bottom range is always 0. - 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_) - inconsistencies |= RANGE_CHECKSUM_ERROR; - - int64 delta64 = snapshot.redundant_count() - count; - if (delta64 != 0) { - int delta = static_cast<int>(delta64); - if (delta != delta64) - delta = INT_MAX; // Flag all giant errors as INT_MAX. - // Since snapshots of histograms are taken asynchronously relative to - // sampling (and snapped from different threads), it is pretty likely that - // we'll catch a redundant count that doesn't match the sample count. We - // allow for a certain amount of slop before flagging this as an - // inconsistency. Even with an inconsistency, we'll snapshot it again (for - // UMA in about a half hour, so we'll eventually get the data, if it was - // not the result of a corruption. If histograms show that 1 is "too tight" - // then we may try to use 2 or 3 for this slop value. - const int kCommonRaceBasedCountMismatch = 1; - if (delta > 0) { - UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta); - if (delta > kCommonRaceBasedCountMismatch) - inconsistencies |= COUNT_HIGH_ERROR; - } else { - DCHECK_GT(0, delta); - UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountLow", -delta); - if (-delta > kCommonRaceBasedCountMismatch) - inconsistencies |= COUNT_LOW_ERROR; - } - } - return static_cast<Inconsistencies>(inconsistencies); -} - -Histogram::ClassType Histogram::histogram_type() const { - return HISTOGRAM; -} - -Histogram::Sample Histogram::ranges(size_t i) const { - return ranges_[i]; -} - -size_t Histogram::bucket_count() const { - return bucket_count_; -} - //------------------------------------------------------------------------------ // Methods for the Histogram::SampleSet class //------------------------------------------------------------------------------ @@ -700,6 +690,9 @@ bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { // buckets. //------------------------------------------------------------------------------ +LinearHistogram::~LinearHistogram() { +} + scoped_refptr<Histogram> LinearHistogram::FactoryGet(const std::string& name, Sample minimum, Sample maximum, @@ -733,7 +726,15 @@ scoped_refptr<Histogram> LinearHistogram::FactoryTimeGet( bucket_count, flags); } -LinearHistogram::~LinearHistogram() { +Histogram::ClassType LinearHistogram::histogram_type() const { + return LINEAR_HISTOGRAM; +} + +void LinearHistogram::SetRangeDescriptions( + const DescriptionPair descriptions[]) { + for (int i =0; descriptions[i].description; ++i) { + bucket_description_[descriptions[i].sample] = descriptions[i].description; + } } LinearHistogram::LinearHistogram(const std::string& name, @@ -757,30 +758,6 @@ LinearHistogram::LinearHistogram(const std::string& name, DCHECK(ValidateBucketRanges()); } -Histogram::ClassType LinearHistogram::histogram_type() const { - return LINEAR_HISTOGRAM; -} - -void LinearHistogram::SetRangeDescriptions( - const DescriptionPair descriptions[]) { - for (int i =0; descriptions[i].description; ++i) { - bucket_description_[descriptions[i].sample] = descriptions[i].description; - } -} - -const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { - int range = ranges(i); - BucketDescriptionMap::const_iterator it = bucket_description_.find(range); - if (it == bucket_description_.end()) - return Histogram::GetAsciiBucketRange(i); - return it->second; -} - -bool LinearHistogram::PrintEmptyBucket(size_t index) const { - return bucket_description_.find(ranges(index)) == bucket_description_.end(); -} - - void LinearHistogram::InitializeBucketRange() { DCHECK_GT(declared_min(), 0); // 0 is the underflow bucket here. double min = declared_min(); @@ -802,6 +779,19 @@ double LinearHistogram::GetBucketSize(Count current, size_t i) const { return current/denominator; } +const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { + int range = ranges(i); + BucketDescriptionMap::const_iterator it = bucket_description_.find(range); + if (it == bucket_description_.end()) + return Histogram::GetAsciiBucketRange(i); + return it->second; +} + +bool LinearHistogram::PrintEmptyBucket(size_t index) const { + return bucket_description_.find(ranges(index)) == bucket_description_.end(); +} + + //------------------------------------------------------------------------------ // This section provides implementation for BooleanHistogram. //------------------------------------------------------------------------------ diff --git a/base/metrics/stats_table.cc b/base/metrics/stats_table.cc index 757c08e..0e8d016 100644 --- a/base/metrics/stats_table.cc +++ b/base/metrics/stats_table.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. @@ -136,7 +136,14 @@ class StatsTable::Private { private: // Constructor is private because you should use New() instead. - Private() {} + Private() + : table_header_(NULL), + thread_names_table_(NULL), + thread_tid_table_(NULL), + thread_pid_table_(NULL), + counter_names_table_(NULL), + data_table_(NULL) { + } // Initializes the table on first access. Sets header values // appropriately and zeroes all counters. @@ -282,6 +289,13 @@ StatsTable::~StatsTable() { global_table_ = NULL; } +int StatsTable::GetSlot() const { + TLSData* data = GetTLSData(); + if (!data) + return 0; + return data->slot; +} + int StatsTable::RegisterThread(const std::string& name) { #ifdef ANDROID return 0; @@ -319,15 +333,121 @@ int StatsTable::RegisterThread(const std::string& name) { #endif } -StatsTable::TLSData* StatsTable::GetTLSData() const { - TLSData* data = - static_cast<TLSData*>(tls_index_.Get()); - if (!data) +int StatsTable::CountThreadsRegistered() const { + if (!impl_) + return 0; + + // Loop through the shared memory and count the threads that are active. + // We intentionally do not lock the table during the operation. + int count = 0; + for (int index = 1; index <= impl_->max_threads(); index++) { + char* name = impl_->thread_name(index); + if (*name != '\0') + count++; + } + return count; +} + +int StatsTable::FindCounter(const std::string& name) { + // Note: the API returns counters numbered from 1..N, although + // internally, the array is 0..N-1. This is so that we can return + // zero as "not found". + if (!impl_) + return 0; + + // Create a scope for our auto-lock. + { + AutoLock scoped_lock(counters_lock_); + + // Attempt to find the counter. + CountersMap::const_iterator iter; + iter = counters_.find(name); + if (iter != counters_.end()) + return iter->second; + } + + // Counter does not exist, so add it. + return AddCounter(name); +} + +int* StatsTable::GetLocation(int counter_id, int slot_id) const { + if (!impl_) + return NULL; + if (slot_id > impl_->max_threads()) return NULL; - DCHECK(data->slot); - DCHECK_EQ(data->table, this); - return data; + int* row = impl_->row(counter_id); + return &(row[slot_id-1]); +} + +const char* StatsTable::GetRowName(int index) const { + if (!impl_) + return NULL; + + return impl_->counter_name(index); +} + +int StatsTable::GetRowValue(int index) const { + return GetRowValue(index, 0); +} + +int StatsTable::GetRowValue(int index, int pid) const { + if (!impl_) + return 0; + + int rv = 0; + int* row = impl_->row(index); + for (int slot_id = 0; slot_id < impl_->max_threads(); slot_id++) { + if (pid == 0 || *impl_->thread_pid(slot_id) == pid) + rv += row[slot_id]; + } + return rv; +} + +int StatsTable::GetCounterValue(const std::string& name) { + return GetCounterValue(name, 0); +} + +int StatsTable::GetCounterValue(const std::string& name, int pid) { + if (!impl_) + return 0; + + int row = FindCounter(name); + if (!row) + return 0; + return GetRowValue(row, pid); +} + +int StatsTable::GetMaxCounters() const { + if (!impl_) + return 0; + return impl_->max_counters(); +} + +int StatsTable::GetMaxThreads() const { + if (!impl_) + return 0; + return impl_->max_threads(); +} + +int* StatsTable::FindLocation(const char* name) { + // Get the static StatsTable + StatsTable *table = StatsTable::current(); + if (!table) + return NULL; + + // Get the slot for this thread. Try to register + // it if none exists. + int slot = table->GetSlot(); + if (!slot && !(slot = table->RegisterThread(""))) + return NULL; + + // Find the counter id for the counter. + std::string str_name(name); + int counter = table->FindCounter(str_name); + + // Now we can find the location in the table. + return table->GetLocation(counter, slot); } void StatsTable::UnregisterThread() { @@ -359,28 +479,6 @@ void StatsTable::SlotReturnFunction(void* data) { } } -int StatsTable::CountThreadsRegistered() const { - if (!impl_) - return 0; - - // Loop through the shared memory and count the threads that are active. - // We intentionally do not lock the table during the operation. - int count = 0; - for (int index = 1; index <= impl_->max_threads(); index++) { - char* name = impl_->thread_name(index); - if (*name != '\0') - count++; - } - return count; -} - -int StatsTable::GetSlot() const { - TLSData* data = GetTLSData(); - if (!data) - return 0; - return data->slot; -} - int StatsTable::FindEmptyThread() const { // Note: the API returns slots numbered from 1..N, although // internally, the array is 0..N-1. This is so that we can return @@ -426,28 +524,6 @@ int StatsTable::FindCounterOrEmptyRow(const std::string& name) const { return free_slot; } -int StatsTable::FindCounter(const std::string& name) { - // Note: the API returns counters numbered from 1..N, although - // internally, the array is 0..N-1. This is so that we can return - // zero as "not found". - if (!impl_) - return 0; - - // Create a scope for our auto-lock. - { - AutoLock scoped_lock(counters_lock_); - - // Attempt to find the counter. - CountersMap::const_iterator iter; - iter = counters_.find(name); - if (iter != counters_.end()) - return iter->second; - } - - // Counter does not exist, so add it. - return AddCounter(name); -} - int StatsTable::AddCounter(const std::string& name) { #ifdef ANDROID return 0; @@ -483,84 +559,15 @@ int StatsTable::AddCounter(const std::string& name) { #endif } -int* StatsTable::GetLocation(int counter_id, int slot_id) const { - if (!impl_) - return NULL; - if (slot_id > impl_->max_threads()) - return NULL; - - int* row = impl_->row(counter_id); - return &(row[slot_id-1]); -} - -const char* StatsTable::GetRowName(int index) const { - if (!impl_) - return NULL; - - return impl_->counter_name(index); -} - -int StatsTable::GetRowValue(int index, int pid) const { - if (!impl_) - return 0; - - int rv = 0; - int* row = impl_->row(index); - for (int slot_id = 0; slot_id < impl_->max_threads(); slot_id++) { - if (pid == 0 || *impl_->thread_pid(slot_id) == pid) - rv += row[slot_id]; - } - return rv; -} - -int StatsTable::GetRowValue(int index) const { - return GetRowValue(index, 0); -} - -int StatsTable::GetCounterValue(const std::string& name, int pid) { - if (!impl_) - return 0; - - int row = FindCounter(name); - if (!row) - return 0; - return GetRowValue(row, pid); -} - -int StatsTable::GetCounterValue(const std::string& name) { - return GetCounterValue(name, 0); -} - -int StatsTable::GetMaxCounters() const { - if (!impl_) - return 0; - return impl_->max_counters(); -} - -int StatsTable::GetMaxThreads() const { - if (!impl_) - return 0; - return impl_->max_threads(); -} - -int* StatsTable::FindLocation(const char* name) { - // Get the static StatsTable - StatsTable *table = StatsTable::current(); - if (!table) +StatsTable::TLSData* StatsTable::GetTLSData() const { + TLSData* data = + static_cast<TLSData*>(tls_index_.Get()); + if (!data) return NULL; - // Get the slot for this thread. Try to register - // it if none exists. - int slot = table->GetSlot(); - if (!slot && !(slot = table->RegisterThread(""))) - return NULL; - - // Find the counter id for the counter. - std::string str_name(name); - int counter = table->FindCounter(str_name); - - // Now we can find the location in the table. - return table->GetLocation(counter, slot); + DCHECK(data->slot); + DCHECK_EQ(data->table, this); + return data; } } // namespace base diff --git a/base/metrics/stats_table.h b/base/metrics/stats_table.h index 32b22eb..94f7463 100644 --- a/base/metrics/stats_table.h +++ b/base/metrics/stats_table.h @@ -25,7 +25,7 @@ #include "base/basictypes.h" #include "base/hash_tables.h" -#include "base/lock.h" +#include "base/synchronization/lock.h" #include "base/threading/thread_local_storage.h" namespace base { @@ -122,7 +122,7 @@ class StatsTable { // The maximum length (in characters) of a Counter's name including // null terminator, as stored in the shared memory. - static const int kMaxCounterNameLength = 32; + static const int kMaxCounterNameLength = 64; // Convenience function to lookup a counter location for a // counter by name for the calling thread. Will register @@ -175,7 +175,7 @@ class StatsTable { Private* impl_; // The counters_lock_ protects the counters_ hash table. - Lock counters_lock_; + base::Lock counters_lock_; // The counters_ hash map is an in-memory hash of the counters. // It is used for quick lookup of counters, but is cannot be used diff --git a/base/mime_util_xdg.cc b/base/mime_util_xdg.cc index 8be1d0d..5215d01 100644 --- a/base/mime_util_xdg.cc +++ b/base/mime_util_xdg.cc @@ -21,6 +21,7 @@ #include "base/string_split.h" #include "base/string_util.h" #include "base/third_party/xdg_mime/xdgmime.h" +#include "base/threading/thread_restrictions.h" namespace { @@ -155,6 +156,7 @@ class IconTheme { IconTheme::IconTheme(const std::string& name) : index_theme_loaded_(false), info_array_(NULL) { + base::ThreadRestrictions::AssertIOAllowed(); // Iterate on all icon directories to find directories of the specified // theme and load the first encountered index.theme. std::map<FilePath, int>::iterator iter; @@ -550,10 +552,12 @@ MimeUtilConstants::~MimeUtilConstants() { namespace mime_util { std::string GetFileMimeType(const FilePath& filepath) { + base::ThreadRestrictions::AssertIOAllowed(); return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str()); } std::string GetDataMimeType(const std::string& data) { + base::ThreadRestrictions::AssertIOAllowed(); return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL); } @@ -576,6 +580,7 @@ void DetectGtkTheme() { } FilePath GetMimeIcon(const std::string& mime_type, size_t size) { + base::ThreadRestrictions::AssertIOAllowed(); std::vector<std::string> icon_names; std::string icon_name; FilePath icon_file; diff --git a/base/nss_util.cc b/base/nss_util.cc index b411422..fe78fe0 100644 --- a/base/nss_util.cc +++ b/base/nss_util.cc @@ -29,9 +29,10 @@ // use NSS for crypto or certificate verification, and we don't use the NSS // certificate and key databases. #if defined(USE_NSS) +#include "base/crypto/crypto_module_blocking_password_delegate.h" #include "base/environment.h" -#include "base/lock.h" #include "base/scoped_ptr.h" +#include "base/synchronization/lock.h" #endif // defined(USE_NSS) namespace base { @@ -69,6 +70,26 @@ FilePath GetInitialConfigDirectory() { #endif // defined(OS_CHROMEOS) } +// This callback for NSS forwards all requests to a caller-specified +// CryptoModuleBlockingPasswordDelegate object. +char* PKCS11PasswordFunc(PK11SlotInfo* slot, PRBool retry, void* arg) { + base::CryptoModuleBlockingPasswordDelegate* delegate = + reinterpret_cast<base::CryptoModuleBlockingPasswordDelegate*>(arg); + if (delegate) { + bool cancelled = false; + std::string password = delegate->RequestPassword(PK11_GetTokenName(slot), + retry != PR_FALSE, + &cancelled); + if (cancelled) + return NULL; + char* result = PORT_Strdup(password.c_str()); + password.replace(0, password.size(), password.size(), 0); + return result; + } + DLOG(ERROR) << "PK11 password requested with NULL arg"; + return NULL; +} + // NSS creates a local cache of the sqlite database if it detects that the // filesystem the database is on is much slower than the local disk. The // detection doesn't work with the latest versions of sqlite, such as 3.6.22 @@ -78,6 +99,9 @@ FilePath GetInitialConfigDirectory() { // // TODO(wtc): port this function to other USE_NSS platforms. It is defined // only for OS_LINUX simply because the statfs structure is OS-specific. +// +// Because this function sets an environment variable it must be run before we +// go multi-threaded. void UseLocalCacheOfNSSDatabaseIfNFS(const FilePath& database_dir) { #if defined(OS_LINUX) struct statfs buf; @@ -141,6 +165,9 @@ class NSSInitSingleton { #if defined(OS_CHROMEOS) void OpenPersistentNSSDB() { if (!chromeos_user_logged_in_) { + // GetDefaultConfigDirectory causes us to do blocking IO on UI thread. + // Temporarily allow it until we fix http://crbug.com.70119 + ThreadRestrictions::ScopedAllowIO allow_io; chromeos_user_logged_in_ = true; real_db_slot_ = OpenUserDB(GetDefaultConfigDirectory(), "Real NSS database"); @@ -219,6 +246,9 @@ class NSSInitSingleton { #else FilePath database_dir = GetInitialConfigDirectory(); if (!database_dir.empty()) { + // This duplicates the work which should have been done in + // EarlySetupForNSSInit. However, this function is idempotent so there's + // no harm done. UseLocalCacheOfNSSDatabaseIfNFS(database_dir); // Initialize with a persistent database (likely, ~/.pki/nssdb). @@ -247,6 +277,8 @@ class NSSInitSingleton { } } + PK11_SetPasswordFunc(PKCS11PasswordFunc); + // If we haven't initialized the password for the NSS databases, // initialize an empty-string password so that we don't need to // log in. @@ -321,6 +353,14 @@ LazyInstance<NSSInitSingleton, LeakyLazyInstanceTraits<NSSInitSingleton> > } // namespace +#if defined(USE_NSS) +void EarlySetupForNSSInit() { + FilePath database_dir = GetInitialConfigDirectory(); + if (!database_dir.empty()) + UseLocalCacheOfNSSDatabaseIfNFS(database_dir); +} +#endif + void EnsureNSPRInit() { g_nspr_singleton.Get(); } diff --git a/base/nss_util.h b/base/nss_util.h index 2b0139e..10bbdfb 100644 --- a/base/nss_util.h +++ b/base/nss_util.h @@ -20,6 +20,13 @@ namespace base { class Lock; class Time; +#if defined(USE_NSS) +// EarlySetupForNSSInit performs lightweight setup which must occur before the +// process goes multithreaded. This does not initialise NSS. For test, see +// EnsureNSSInit. +void EarlySetupForNSSInit(); +#endif + // Initialize NRPR if it isn't already initialized. This function is // thread-safe, and NSPR will only ever be initialized once. NSPR will be // properly shut down on program exit. diff --git a/base/observer_list_threadsafe.h b/base/observer_list_threadsafe.h index d034e6c..47a662e 100644 --- a/base/observer_list_threadsafe.h +++ b/base/observer_list_threadsafe.h @@ -49,9 +49,32 @@ // will notify its regular ObserverList. // /////////////////////////////////////////////////////////////////////////////// + +// Forward declaration for ObserverListThreadSafeTraits. +template <class ObserverType> +class ObserverListThreadSafe; + +// This class is used to work around VS2005 not accepting: +// +// friend class +// base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> >; +// +// Instead of friending the class, we could friend the actual function +// which calls delete. However, this ends up being +// RefCountedThreadSafe::DeleteInternal(), which is private. So we +// define our own templated traits class so we can friend it. +template <class T> +struct ObserverListThreadSafeTraits { + static void Destruct(const ObserverListThreadSafe<T>* x) { + delete x; + } +}; + template <class ObserverType> class ObserverListThreadSafe - : public base::RefCountedThreadSafe<ObserverListThreadSafe<ObserverType> > { + : public base::RefCountedThreadSafe< + ObserverListThreadSafe<ObserverType>, + ObserverListThreadSafeTraits<ObserverType> > { public: typedef typename ObserverList<ObserverType>::NotificationType NotificationType; @@ -60,13 +83,6 @@ class ObserverListThreadSafe : type_(ObserverListBase<ObserverType>::NOTIFY_ALL) {} explicit ObserverListThreadSafe(NotificationType type) : type_(type) {} - ~ObserverListThreadSafe() { - typename ObserversListMap::const_iterator it; - for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) - delete (*it).second; - observer_lists_.clear(); - } - // Add an observer to the list. void AddObserver(ObserverType* obs) { ObserverList<ObserverType>* list = NULL; @@ -77,7 +93,7 @@ class ObserverListThreadSafe if (!loop) return; // Some unittests may access this without a message loop. { - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); if (observer_lists_.find(loop) == observer_lists_.end()) observer_lists_[loop] = new ObserverList<ObserverType>(type_); list = observer_lists_[loop]; @@ -96,7 +112,7 @@ class ObserverListThreadSafe if (!loop) return; // On shutdown, it is possible that current() is already null. { - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); list = observer_lists_[loop]; if (!list) { NOTREACHED() << "RemoveObserver called on for unknown thread"; @@ -137,9 +153,19 @@ class ObserverListThreadSafe // TODO(mbelshe): Add more wrappers for Notify() with more arguments. private: + // See comment above ObserverListThreadSafeTraits' definition. + friend struct ObserverListThreadSafeTraits<ObserverType>; + + ~ObserverListThreadSafe() { + typename ObserversListMap::const_iterator it; + for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) + delete (*it).second; + observer_lists_.clear(); + } + template <class Method, class Params> void Notify(const UnboundMethod<ObserverType, Method, Params>& method) { - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); typename ObserversListMap::iterator it; for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) { MessageLoop* loop = (*it).first; @@ -161,7 +187,7 @@ class ObserverListThreadSafe // Check that this list still needs notifications. { - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); typename ObserversListMap::iterator it = observer_lists_.find(MessageLoop::current()); @@ -183,7 +209,7 @@ class ObserverListThreadSafe // If there are no more observers on the list, we can now delete it. if (list->size() == 0) { { - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); // Remove |list| if it's not already removed. // This can happen if multiple observers got removed in a notification. // See http://crbug.com/55725. @@ -199,7 +225,7 @@ class ObserverListThreadSafe typedef std::map<MessageLoop*, ObserverList<ObserverType>*> ObserversListMap; // These are marked mutable to facilitate having NotifyAll be const. - Lock list_lock_; // Protects the observer_lists_. + base::Lock list_lock_; // Protects the observer_lists_. ObserversListMap observer_lists_; const NotificationType type_; diff --git a/base/openssl_util.cc b/base/openssl_util.cc index bc174fa..931485a 100644 --- a/base/openssl_util.cc +++ b/base/openssl_util.cc @@ -7,11 +7,11 @@ #include <openssl/err.h> #include <openssl/ssl.h> -#include "base/lock.h" #include "base/logging.h" #include "base/scoped_vector.h" #include "base/singleton.h" #include "base/string_piece.h" +#include "base/synchronization/lock.h" namespace base { @@ -46,7 +46,7 @@ class OpenSSLInitSingleton { int num_locks = CRYPTO_num_locks(); locks_.reserve(num_locks); for (int i = 0; i < num_locks; ++i) - locks_.push_back(new Lock()); + locks_.push_back(new base::Lock()); CRYPTO_set_locking_callback(LockingCallback); CRYPTO_set_id_callback(CurrentThreadId); } @@ -70,7 +70,7 @@ class OpenSSLInitSingleton { } // These locks are used and managed by OpenSSL via LockingCallback(). - ScopedVector<Lock> locks_; + ScopedVector<base::Lock> locks_; DISALLOW_COPY_AND_ASSIGN(OpenSSLInitSingleton); }; diff --git a/base/path_service.cc b/base/path_service.cc index 56ce5fa..117feb5 100644 --- a/base/path_service.cc +++ b/base/path_service.cc @@ -14,8 +14,8 @@ #include "base/file_util.h" #include "base/hash_tables.h" #include "base/lazy_instance.h" -#include "base/lock.h" #include "base/logging.h" +#include "base/synchronization/lock.h" namespace base { bool PathProvider(int key, FilePath* result); @@ -92,9 +92,9 @@ static Provider base_provider_posix = { struct PathData { - Lock lock; - PathMap cache; // Cache mappings from path key to path value. - PathMap overrides; // Track path overrides. + base::Lock lock; + PathMap cache; // Cache mappings from path key to path value. + PathMap overrides; // Track path overrides. Provider* providers; // Linked list of path service providers. PathData() { @@ -130,7 +130,7 @@ static PathData* GetPathData() { // static bool PathService::GetFromCache(int key, FilePath* result) { PathData* path_data = GetPathData(); - AutoLock scoped_lock(path_data->lock); + base::AutoLock scoped_lock(path_data->lock); // check for a cached version PathMap::const_iterator it = path_data->cache.find(key); @@ -144,7 +144,7 @@ bool PathService::GetFromCache(int key, FilePath* result) { // static bool PathService::GetFromOverrides(int key, FilePath* result) { PathData* path_data = GetPathData(); - AutoLock scoped_lock(path_data->lock); + base::AutoLock scoped_lock(path_data->lock); // check for an overriden version. PathMap::const_iterator it = path_data->overrides.find(key); @@ -158,7 +158,7 @@ bool PathService::GetFromOverrides(int key, FilePath* result) { // static void PathService::AddToCache(int key, const FilePath& path) { PathData* path_data = GetPathData(); - AutoLock scoped_lock(path_data->lock); + base::AutoLock scoped_lock(path_data->lock); // Save the computed path in our cache. path_data->cache[key] = path; } @@ -225,7 +225,7 @@ bool PathService::Override(int key, const FilePath& path) { if (!file_util::AbsolutePath(&file_path)) return false; - AutoLock scoped_lock(path_data->lock); + base::AutoLock scoped_lock(path_data->lock); // Clear the cache now. Some of its entries could have depended // on the value we are overriding, and are now out of sync with reality. @@ -243,7 +243,7 @@ void PathService::RegisterProvider(ProviderFunc func, int key_start, DCHECK(path_data); DCHECK(key_end > key_start); - AutoLock scoped_lock(path_data->lock); + base::AutoLock scoped_lock(path_data->lock); Provider* p; diff --git a/base/pickle.cc b/base/pickle.cc index a05df28..e7d5768 100644 --- a/base/pickle.cc +++ b/base/pickle.cc @@ -406,6 +406,9 @@ const char* Pickle::FindNext(size_t header_size, DCHECK(header_size == AlignInt(header_size, sizeof(uint32))); DCHECK(header_size <= static_cast<size_t>(kPayloadUnit)); + if (static_cast<size_t>(end - start) < sizeof(Header)) + return NULL; + const Header* hdr = reinterpret_cast<const Header*>(start); const char* payload_base = start + header_size; const char* payload_end = payload_base + hdr->payload_size; diff --git a/base/pickle.h b/base/pickle.h index bbe5d34..ad08b7c 100644 --- a/base/pickle.h +++ b/base/pickle.h @@ -55,8 +55,7 @@ class Pickle { Pickle& operator=(const Pickle& other); // Returns the size of the Pickle's data. - int size() const { return static_cast<int>(header_size_ + - header_->payload_size); } + size_t size() const { return header_size_ + header_->payload_size; } // Returns the data for this Pickle. const void* data() const { return header_; } @@ -236,6 +235,7 @@ class Pickle { FRIEND_TEST_ALL_PREFIXES(PickleTest, Resize); FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNext); + FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNextWithIncompleteHeader); FRIEND_TEST_ALL_PREFIXES(PickleTest, IteratorHasRoom); }; diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc index fdc0664..39eaa1b 100644 --- a/base/pickle_unittest.cc +++ b/base/pickle_unittest.cc @@ -171,6 +171,17 @@ TEST(PickleTest, FindNext) { EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end + 1)); } +TEST(PickleTest, FindNextWithIncompleteHeader) { + size_t header_size = sizeof(Pickle::Header); + scoped_array<char> buffer(new char[header_size - 1]); + memset(buffer.get(), 0x1, header_size - 1); + + const char* start = buffer.get(); + const char* end = start + header_size - 1; + + EXPECT_TRUE(NULL == Pickle::FindNext(header_size, start, end)); +} + TEST(PickleTest, IteratorHasRoom) { Pickle pickle; EXPECT_TRUE(pickle.WriteInt(1)); diff --git a/base/pr_time_unittest.cc b/base/pr_time_unittest.cc index 6dda299..646eb16 100644 --- a/base/pr_time_unittest.cc +++ b/base/pr_time_unittest.cc @@ -136,7 +136,7 @@ TEST_F(PRTimeTest, ParseTimeTest9) { TEST_F(PRTimeTest, ParseTimeTest10) { Time parsed_time; bool result = Time::FromString(L"15/10/07 12:45", &parsed_time); - EXPECT_EQ(true, result); + EXPECT_TRUE(result); time_t computed_time = parsed_time.ToTimeT(); time_t time_to_compare = comparison_time_local_ / @@ -149,7 +149,7 @@ TEST_F(PRTimeTest, ParseTimeTest11) { Time parsed_time; bool result = Time::FromString(L"Mon, 15 Oct 2007 19:45:00 GMT", &parsed_time); - EXPECT_EQ(true, result); + EXPECT_TRUE(result); time_t computed_time = parsed_time.ToTimeT(); time_t time_to_compare = comparison_time_pdt / Time::kMicrosecondsPerSecond; @@ -161,11 +161,11 @@ TEST_F(PRTimeTest, ParseTimeTestEpoch0) { Time parsed_time; // time_t == epoch == 0 - EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 01:00:00 +0100 1970", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Thu Jan 01 01:00:00 +0100 1970", + &parsed_time)); EXPECT_EQ(0, parsed_time.ToTimeT()); - EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:00:00 GMT 1970", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Thu Jan 01 00:00:00 GMT 1970", + &parsed_time)); EXPECT_EQ(0, parsed_time.ToTimeT()); } @@ -173,11 +173,11 @@ TEST_F(PRTimeTest, ParseTimeTestEpoch1) { Time parsed_time; // time_t == 1 second after epoch == 1 - EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 01:00:01 +0100 1970", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Thu Jan 01 01:00:01 +0100 1970", + &parsed_time)); EXPECT_EQ(1, parsed_time.ToTimeT()); - EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:00:01 GMT 1970", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Thu Jan 01 00:00:01 GMT 1970", + &parsed_time)); EXPECT_EQ(1, parsed_time.ToTimeT()); } @@ -185,11 +185,11 @@ TEST_F(PRTimeTest, ParseTimeTestEpoch2) { Time parsed_time; // time_t == 2 seconds after epoch == 2 - EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 01:00:02 +0100 1970", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Thu Jan 01 01:00:02 +0100 1970", + &parsed_time)); EXPECT_EQ(2, parsed_time.ToTimeT()); - EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:00:02 GMT 1970", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Thu Jan 01 00:00:02 GMT 1970", + &parsed_time)); EXPECT_EQ(2, parsed_time.ToTimeT()); } @@ -197,11 +197,11 @@ TEST_F(PRTimeTest, ParseTimeTestEpochNeg1) { Time parsed_time; // time_t == 1 second before epoch == -1 - EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:59:59 +0100 1970", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Thu Jan 01 00:59:59 +0100 1970", + &parsed_time)); EXPECT_EQ(-1, parsed_time.ToTimeT()); - EXPECT_EQ(true, Time::FromString(L"Wed Dec 31 23:59:59 GMT 1969", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Wed Dec 31 23:59:59 GMT 1969", + &parsed_time)); EXPECT_EQ(-1, parsed_time.ToTimeT()); } @@ -211,8 +211,8 @@ TEST_F(PRTimeTest, ParseTimeTestEpochNeg1) { TEST_F(PRTimeTest, ParseTimeTestEpochNotNeg1) { Time parsed_time; - EXPECT_EQ(true, Time::FromString(L"Wed Dec 31 23:59:59 GMT 2100", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Wed Dec 31 23:59:59 GMT 2100", + &parsed_time)); EXPECT_NE(-1, parsed_time.ToTimeT()); } @@ -220,11 +220,11 @@ TEST_F(PRTimeTest, ParseTimeTestEpochNeg2) { Time parsed_time; // time_t == 2 seconds before epoch == -2 - EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:59:58 +0100 1970", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Thu Jan 01 00:59:58 +0100 1970", + &parsed_time)); EXPECT_EQ(-2, parsed_time.ToTimeT()); - EXPECT_EQ(true, Time::FromString(L"Wed Dec 31 23:59:58 GMT 1969", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Wed Dec 31 23:59:58 GMT 1969", + &parsed_time)); EXPECT_EQ(-2, parsed_time.ToTimeT()); } @@ -232,14 +232,14 @@ TEST_F(PRTimeTest, ParseTimeTestEpoch1960) { Time parsed_time; // time_t before Epoch, in 1960 - EXPECT_EQ(true, Time::FromString(L"Wed Jun 29 19:40:01 +0100 1960", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Wed Jun 29 19:40:01 +0100 1960", + &parsed_time)); EXPECT_EQ(-299999999, parsed_time.ToTimeT()); - EXPECT_EQ(true, Time::FromString(L"Wed Jun 29 18:40:01 GMT 1960", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Wed Jun 29 18:40:01 GMT 1960", + &parsed_time)); EXPECT_EQ(-299999999, parsed_time.ToTimeT()); - EXPECT_EQ(true, Time::FromString(L"Wed Jun 29 17:40:01 GMT 1960", - &parsed_time)); + EXPECT_TRUE(Time::FromString(L"Wed Jun 29 17:40:01 GMT 1960", + &parsed_time)); EXPECT_EQ(-300003599, parsed_time.ToTimeT()); } diff --git a/base/process_linux.cc b/base/process_linux.cc index 21ed933..14c2424 100644 --- a/base/process_linux.cc +++ b/base/process_linux.cc @@ -11,7 +11,14 @@ namespace base { +#if defined(OS_CHROMEOS) +// We are more aggressive in our lowering of background process priority +// for chromeos as we have much more control over other processes running +// on the machine. +const int kPriorityAdjustment = 19; +#else const int kPriorityAdjustment = 5; +#endif bool Process::IsProcessBackgrounded() const { DCHECK(process_); diff --git a/base/process_posix.cc b/base/process_posix.cc index ee70e5a..6e65ebf 100644 --- a/base/process_posix.cc +++ b/base/process_posix.cc @@ -13,6 +13,22 @@ namespace base { +// static +Process Process::Current() { + return Process(GetCurrentProcessHandle()); +} + +ProcessId Process::pid() const { + if (process_ == 0) + return 0; + + return GetProcId(process_); +} + +bool Process::is_current() const { + return process_ == GetCurrentProcessHandle(); +} + void Process::Close() { process_ = 0; // if the process wasn't terminated (so we waited) or the state @@ -43,22 +59,6 @@ bool Process::SetProcessBackgrounded(bool value) { } #endif -ProcessId Process::pid() const { - if (process_ == 0) - return 0; - - return GetProcId(process_); -} - -bool Process::is_current() const { - return process_ == GetCurrentProcessHandle(); -} - -// static -Process Process::Current() { - return Process(GetCurrentProcessHandle()); -} - int Process::GetPriority() const { DCHECK(process_); return getpriority(PRIO_PROCESS, process_); diff --git a/base/process_util.cc b/base/process_util.cc index 7b2935d..462dcbf 100644 --- a/base/process_util.cc +++ b/base/process_util.cc @@ -44,10 +44,6 @@ const ProcessEntry* ProcessIterator::NextProcessEntry() { return NULL; } -bool ProcessIterator::IncludeEntry() { - return !filter_ || filter_->Includes(entry_); -} - ProcessIterator::ProcessEntries ProcessIterator::Snapshot() { ProcessEntries found; while (const ProcessEntry* process_entry = NextProcessEntry()) { @@ -56,6 +52,10 @@ ProcessIterator::ProcessEntries ProcessIterator::Snapshot() { return found; } +bool ProcessIterator::IncludeEntry() { + return !filter_ || filter_->Includes(entry_); +} + NamedProcessIterator::NamedProcessIterator( const FilePath::StringType& executable_name, const ProcessFilter* filter) : ProcessIterator(filter), diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc index dcaeeb4..4cd27f6 100644 --- a/base/process_util_linux.cc +++ b/base/process_util_linux.cc @@ -68,6 +68,39 @@ bool GetProcCmdline(pid_t pid, std::vector<std::string>* proc_cmd_line_args) { return true; } +// Get the total CPU of a single process. Return value is number of jiffies +// on success or -1 on error. +int GetProcessCPU(pid_t pid) { + // Synchronously reading files in /proc is safe. + base::ThreadRestrictions::ScopedAllowIO allow_io; + + // Use /proc/<pid>/task to find all threads and parse their /stat file. + FilePath path = FilePath(StringPrintf("/proc/%d/task/", pid)); + + DIR* dir = opendir(path.value().c_str()); + if (!dir) { + PLOG(ERROR) << "opendir(" << path.value() << ")"; + return -1; + } + + int total_cpu = 0; + while (struct dirent* ent = readdir(dir)) { + if (ent->d_name[0] == '.') + continue; + + FilePath stat_path = path.AppendASCII(ent->d_name).AppendASCII("stat"); + std::string stat; + if (file_util::ReadFileToString(stat_path, &stat)) { + int cpu = base::ParseProcStatCPU(stat); + if (cpu > 0) + total_cpu += cpu; + } + } + closedir(dir); + + return total_cpu; +} + } // namespace namespace base { @@ -167,18 +200,18 @@ bool ProcessIterator::CheckForNextProcess() { std::string pid_string(slot->d_name); int pid; if (StringToInt(pid_string, &pid) && !GetProcCmdline(pid, &cmd_line_args)) - return false; + continue; // Read the process's status. char buf[NAME_MAX + 12]; sprintf(buf, "/proc/%s/stat", slot->d_name); FILE *fp = fopen(buf, "r"); if (!fp) - return false; + continue; const char* result = fgets(buf, sizeof(buf), fp); fclose(fp); if (!result) - return false; + continue; // Parse the status. It is formatted like this: // %d (%s) %c %d %d ... @@ -188,7 +221,7 @@ bool ProcessIterator::CheckForNextProcess() { openparen = strchr(buf, '('); closeparen = strrchr(buf, ')'); if (!openparen || !closeparen) - return false; + continue; char runstate = closeparen[2]; // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? @@ -226,14 +259,6 @@ bool NamedProcessIterator::IncludeEntry() { } -ProcessMetrics::ProcessMetrics(ProcessHandle process) - : process_(process), - last_time_(0), - last_system_time_(0), - last_cpu_(0) { - processor_count_ = base::SysInfo::NumberOfProcessors(); -} - // static ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { return new ProcessMetrics(process); @@ -399,6 +424,49 @@ bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { return true; } +double ProcessMetrics::GetCPUUsage() { + // This queries the /proc-specific scaling factor which is + // conceptually the system hertz. To dump this value on another + // system, try + // od -t dL /proc/self/auxv + // and look for the number after 17 in the output; mine is + // 0000040 17 100 3 134512692 + // which means the answer is 100. + // It may be the case that this value is always 100. + static const int kHertz = sysconf(_SC_CLK_TCK); + + struct timeval now; + int retval = gettimeofday(&now, NULL); + if (retval) + return 0; + int64 time = TimeValToMicroseconds(now); + + if (last_time_ == 0) { + // First call, just set the last values. + last_time_ = time; + last_cpu_ = GetProcessCPU(process_); + return 0; + } + + int64 time_delta = time - last_time_; + DCHECK_NE(time_delta, 0); + if (time_delta == 0) + return 0; + + int cpu = GetProcessCPU(process_); + + // We have the number of jiffies in the time period. Convert to percentage. + // Note this means we will go *over* 100 in the case where multiple threads + // are together adding to more than one CPU's worth. + int percentage = 100 * (cpu - last_cpu_) / + (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF()); + + last_time_ = time; + last_cpu_ = cpu; + + return percentage; +} + // To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING // in your kernel configuration. bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { @@ -446,6 +514,14 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { return true; } +ProcessMetrics::ProcessMetrics(ProcessHandle process) + : process_(process), + last_time_(0), + last_system_time_(0), + last_cpu_(0) { + processor_count_ = base::SysInfo::NumberOfProcessors(); +} + // Exposed for testing. int ParseProcStatCPU(const std::string& input) { @@ -469,82 +545,6 @@ int ParseProcStatCPU(const std::string& input) { return fields11 + fields12; } -// Get the total CPU of a single process. Return value is number of jiffies -// on success or -1 on error. -static int GetProcessCPU(pid_t pid) { - // Synchronously reading files in /proc is safe. - base::ThreadRestrictions::ScopedAllowIO allow_io; - - // Use /proc/<pid>/task to find all threads and parse their /stat file. - FilePath path = FilePath(StringPrintf("/proc/%d/task/", pid)); - - DIR* dir = opendir(path.value().c_str()); - if (!dir) { - PLOG(ERROR) << "opendir(" << path.value() << ")"; - return -1; - } - - int total_cpu = 0; - while (struct dirent* ent = readdir(dir)) { - if (ent->d_name[0] == '.') - continue; - - FilePath stat_path = path.AppendASCII(ent->d_name).AppendASCII("stat"); - std::string stat; - if (file_util::ReadFileToString(stat_path, &stat)) { - int cpu = ParseProcStatCPU(stat); - if (cpu > 0) - total_cpu += cpu; - } - } - closedir(dir); - - return total_cpu; -} - -double ProcessMetrics::GetCPUUsage() { - // This queries the /proc-specific scaling factor which is - // conceptually the system hertz. To dump this value on another - // system, try - // od -t dL /proc/self/auxv - // and look for the number after 17 in the output; mine is - // 0000040 17 100 3 134512692 - // which means the answer is 100. - // It may be the case that this value is always 100. - static const int kHertz = sysconf(_SC_CLK_TCK); - - struct timeval now; - int retval = gettimeofday(&now, NULL); - if (retval) - return 0; - int64 time = TimeValToMicroseconds(now); - - if (last_time_ == 0) { - // First call, just set the last values. - last_time_ = time; - last_cpu_ = GetProcessCPU(process_); - return 0; - } - - int64 time_delta = time - last_time_; - DCHECK_NE(time_delta, 0); - if (time_delta == 0) - return 0; - - int cpu = GetProcessCPU(process_); - - // We have the number of jiffies in the time period. Convert to percentage. - // Note this means we will go *over* 100 in the case where multiple threads - // are together adding to more than one CPU's worth. - int percentage = 100 * (cpu - last_cpu_) / - (kHertz * TimeDelta::FromMicroseconds(time_delta).InSecondsF()); - - last_time_ = time; - last_cpu_ = cpu; - - return percentage; -} - namespace { // The format of /proc/meminfo is: diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc index 672e396..1b7368a 100644 --- a/base/process_util_unittest.cc +++ b/base/process_util_unittest.cc @@ -485,12 +485,15 @@ std::string TestLaunchApp(const base::environment_vector& env_changes) { fds_to_remap.push_back(std::make_pair(fds[1], 1)); EXPECT_TRUE(base::LaunchApp(args, env_changes, fds_to_remap, - true /* wait for exit */, &handle)); - PCHECK(close(fds[1]) == 0); + true /* wait for exit */, &handle)); + PCHECK(HANDLE_EINTR(close(fds[1])) == 0); char buf[512]; const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf))); PCHECK(n > 0); + + PCHECK(HANDLE_EINTR(close(fds[0])) == 0); + return std::string(buf, n); } diff --git a/base/ref_counted.cc b/base/ref_counted.cc index 2f795ea..54c1071 100644 --- a/base/ref_counted.cc +++ b/base/ref_counted.cc @@ -12,7 +12,7 @@ namespace base { namespace subtle { RefCountedBase::RefCountedBase() - : ref_count_(0) + : counter_holder_(new CounterHolder) #ifndef NDEBUG , in_dtor_(false) #endif @@ -20,6 +20,7 @@ RefCountedBase::RefCountedBase() } RefCountedBase::~RefCountedBase() { + delete counter_holder_; #ifndef NDEBUG DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; #endif @@ -32,7 +33,7 @@ void RefCountedBase::AddRef() const { #ifndef NDEBUG DCHECK(!in_dtor_); #endif - ++ref_count_; + ++(counter_holder_->ref_count); } bool RefCountedBase::Release() const { @@ -42,7 +43,7 @@ bool RefCountedBase::Release() const { #ifndef NDEBUG DCHECK(!in_dtor_); #endif - if (--ref_count_ == 0) { + if (--(counter_holder_->ref_count) == 0) { #ifndef NDEBUG in_dtor_ = true; #endif @@ -51,13 +52,21 @@ bool RefCountedBase::Release() const { return false; } -RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) { +bool RefCountedThreadSafeBase::HasOneRef() const { + return AtomicRefCountIsOne( + &const_cast<RefCountedThreadSafeBase*>(this)-> + counter_holder_->ref_count); +} + +RefCountedThreadSafeBase::RefCountedThreadSafeBase() + : counter_holder_(new CounterHolder) { #ifndef NDEBUG in_dtor_ = false; #endif } RefCountedThreadSafeBase::~RefCountedThreadSafeBase() { + delete counter_holder_; #ifndef NDEBUG DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " "calling Release()"; @@ -68,15 +77,15 @@ void RefCountedThreadSafeBase::AddRef() const { #ifndef NDEBUG DCHECK(!in_dtor_); #endif - AtomicRefCountInc(&ref_count_); + AtomicRefCountInc(&counter_holder_->ref_count); } bool RefCountedThreadSafeBase::Release() const { #ifndef NDEBUG DCHECK(!in_dtor_); - DCHECK(!AtomicRefCountIsZero(&ref_count_)); + DCHECK(!AtomicRefCountIsZero(&counter_holder_->ref_count)); #endif - if (!AtomicRefCountDec(&ref_count_)) { + if (!AtomicRefCountDec(&counter_holder_->ref_count)) { #ifndef NDEBUG in_dtor_ = true; #endif @@ -85,11 +94,6 @@ bool RefCountedThreadSafeBase::Release() const { return false; } -bool RefCountedThreadSafeBase::HasOneRef() const { - return AtomicRefCountIsOne( - &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_); -} - } // namespace subtle } // namespace base diff --git a/base/ref_counted.h b/base/ref_counted.h index 4c3aeb8..6a2b996 100644 --- a/base/ref_counted.h +++ b/base/ref_counted.h @@ -17,19 +17,25 @@ class RefCountedBase { public: static bool ImplementsThreadSafeReferenceCounting() { return false; } - bool HasOneRef() const { return ref_count_ == 1; } + bool HasOneRef() const { return counter_holder_->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 int ref_count_; + mutable CounterHolder* counter_holder_; #ifndef NDEBUG mutable bool in_dtor_; #endif @@ -55,7 +61,12 @@ class RefCountedThreadSafeBase { bool Release() const; private: - mutable AtomicRefCount ref_count_; + struct CounterHolder { + CounterHolder() : ref_count(0), weak_count(0) {} + AtomicRefCount ref_count; + AtomicRefCount weak_count; // Simulates weak pointer. + }; + mutable CounterHolder* counter_holder_; #ifndef NDEBUG mutable bool in_dtor_; #endif diff --git a/base/scoped_temp_dir.h b/base/scoped_temp_dir.h index f44bcca..4286d28 100644 --- a/base/scoped_temp_dir.h +++ b/base/scoped_temp_dir.h @@ -28,17 +28,17 @@ class ScopedTempDir { // Creates a unique directory in TempPath, and takes ownership of it. // See file_util::CreateNewTemporaryDirectory. - bool CreateUniqueTempDir(); + bool CreateUniqueTempDir() WARN_UNUSED_RESULT; // Creates a unique directory under a given path, and takes ownership of it. - bool CreateUniqueTempDirUnderPath(const FilePath& path); + bool CreateUniqueTempDirUnderPath(const FilePath& path) WARN_UNUSED_RESULT; // Takes ownership of directory at |path|, creating it if necessary. // Don't call multiple times unless Take() has been called first. - bool Set(const FilePath& path); + bool Set(const FilePath& path) WARN_UNUSED_RESULT; // Deletes the temporary directory wrapped by this object. - bool Delete(); + bool Delete() WARN_UNUSED_RESULT; // Caller takes ownership of the temporary directory so it won't be destroyed // when this object goes out of scope. diff --git a/base/scoped_temp_dir_unittest.cc b/base/scoped_temp_dir_unittest.cc index 039a1ed..135c2fd 100644 --- a/base/scoped_temp_dir_unittest.cc +++ b/base/scoped_temp_dir_unittest.cc @@ -23,7 +23,7 @@ TEST(ScopedTempDir, FullPath) { { ScopedTempDir dir; - dir.Set(test_path); + EXPECT_TRUE(dir.Set(test_path)); // Now the dir doesn't exist, so ensure that it gets created. EXPECT_TRUE(file_util::DirectoryExists(test_path)); // When we call Release(), it shouldn't get destroyed when leaving scope. @@ -36,7 +36,7 @@ TEST(ScopedTempDir, FullPath) { // Clean up. { ScopedTempDir dir; - dir.Set(test_path); + EXPECT_TRUE(dir.Set(test_path)); } EXPECT_FALSE(file_util::DirectoryExists(test_path)); } @@ -83,7 +83,7 @@ TEST(ScopedTempDir, MultipleInvocations) { EXPECT_TRUE(dir.CreateUniqueTempDir()); EXPECT_FALSE(dir.CreateUniqueTempDir()); ScopedTempDir other_dir; - other_dir.Set(dir.Take()); + EXPECT_TRUE(other_dir.Set(dir.Take())); EXPECT_TRUE(dir.CreateUniqueTempDir()); EXPECT_FALSE(dir.CreateUniqueTempDir()); EXPECT_FALSE(other_dir.CreateUniqueTempDir()); diff --git a/base/sha1_portable.cc b/base/sha1_portable.cc index d0f3c1c..cc05a5c 100644 --- a/base/sha1_portable.cc +++ b/base/sha1_portable.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. @@ -99,6 +99,11 @@ static inline void swapends(uint32* t) { const int SecureHashAlgorithm::kDigestSizeBytes = 20; void SecureHashAlgorithm::Init() { + A = 0; + B = 0; + C = 0; + D = 0; + E = 0; cursor = 0; l = 0; H[0] = 0x67452301; diff --git a/base/shared_memory_posix.cc b/base/shared_memory_posix.cc index e83b982..5ac7597 100644 --- a/base/shared_memory_posix.cc +++ b/base/shared_memory_posix.cc @@ -196,24 +196,52 @@ bool SharedMemory::Open(const std::string& name, bool read_only) { return PrepareMapFile(fp); } -// For the given shmem named |mem_name|, return a filename to mmap() -// (and possibly create). Modifies |filename|. Return false on -// error, or true of we are happy. -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); +bool SharedMemory::Map(uint32 bytes) { + if (mapped_file_ == -1) + return false; - FilePath temp_dir; - if (!file_util::GetShmemTempDir(&temp_dir)) + memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), + MAP_SHARED, mapped_file_, 0); + + if (memory_) + mapped_size_ = bytes; + + bool mmap_succeeded = (memory_ != (void*)-1); + DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno; + return mmap_succeeded; +} + +bool SharedMemory::Unmap() { + if (memory_ == NULL) return false; - *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name); + munmap(memory_, mapped_size_); + memory_ = NULL; + mapped_size_ = 0; return true; } +SharedMemoryHandle SharedMemory::handle() const { + return FileDescriptor(mapped_file_, false); +} + +void SharedMemory::Close() { + Unmap(); + + if (mapped_file_ > 0) { + close(mapped_file_); + mapped_file_ = -1; + } +} + +void SharedMemory::Lock() { + LockOrUnlockCommon(F_LOCK); +} + +void SharedMemory::Unlock() { + LockOrUnlockCommon(F_ULOCK); +} + bool SharedMemory::PrepareMapFile(FILE *fp) { DCHECK(mapped_file_ == -1); if (fp == NULL) return false; @@ -243,55 +271,24 @@ bool SharedMemory::PrepareMapFile(FILE *fp) { return true; } -bool SharedMemory::Map(uint32 bytes) { - if (mapped_file_ == -1) - return false; - - memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), - MAP_SHARED, mapped_file_, 0); - - if (memory_) - mapped_size_ = bytes; - - bool mmap_succeeded = (memory_ != (void*)-1); - DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno; - return mmap_succeeded; -} +// For the given shmem named |mem_name|, return a filename to mmap() +// (and possibly create). Modifies |filename|. Return false on +// error, or true of we are happy. +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); -bool SharedMemory::Unmap() { - if (memory_ == NULL) + FilePath temp_dir; + if (!file_util::GetShmemTempDir(&temp_dir)) return false; - munmap(memory_, mapped_size_); - memory_ = NULL; - mapped_size_ = 0; - return true; -} - -bool SharedMemory::ShareToProcessCommon(ProcessHandle process, - SharedMemoryHandle *new_handle, - bool close_self) { - const int new_fd = dup(mapped_file_); - DCHECK(new_fd >= 0); - new_handle->fd = new_fd; - new_handle->auto_close = true; - - if (close_self) - Close(); - + *path = temp_dir.AppendASCII("com.google.chrome.shmem." + mem_name); return true; } - -void SharedMemory::Close() { - Unmap(); - - if (mapped_file_ > 0) { - close(mapped_file_); - mapped_file_ = -1; - } -} - void SharedMemory::LockOrUnlockCommon(int function) { DCHECK(mapped_file_ >= 0); #if !defined(ANDROID) @@ -313,6 +310,7 @@ void SharedMemory::LockOrUnlockCommon(int function) { #endif } +<<<<<<< HEAD void SharedMemory::Lock() { #if !defined(ANDROID) LockOrUnlockCommon(F_LOCK); @@ -324,9 +322,20 @@ void SharedMemory::Unlock() { LockOrUnlockCommon(F_ULOCK); #endif } +======= +bool SharedMemory::ShareToProcessCommon(ProcessHandle process, + SharedMemoryHandle *new_handle, + bool close_self) { + const int new_fd = dup(mapped_file_); + DCHECK(new_fd >= 0); + new_handle->fd = new_fd; + new_handle->auto_close = true; -SharedMemoryHandle SharedMemory::handle() const { - return FileDescriptor(mapped_file_, false); + if (close_self) + Close(); +>>>>>>> chromium.org at r11.0.672.0 + + return true; } } // namespace base diff --git a/base/string_piece.h b/base/string_piece.h index 80c6cab..64326e1 100644 --- a/base/string_piece.h +++ b/base/string_piece.h @@ -19,8 +19,6 @@ #define BASE_STRING_PIECE_H_ #pragma once -#include <algorithm> -#include <iosfwd> #include <string> #include "base/basictypes.h" @@ -93,7 +91,8 @@ class StringPiece { } int compare(const StringPiece& x) const { - int r = wordmemcmp(ptr_, x.ptr_, std::min(length_, x.length_)); + int r = wordmemcmp( + ptr_, x.ptr_, (length_ < x.length_ ? length_ : x.length_)); if (r == 0) { if (length_ < x.length_) r = -1; else if (length_ > x.length_) r = +1; @@ -171,8 +170,8 @@ inline bool operator!=(const StringPiece& x, const StringPiece& y) { } inline bool operator<(const StringPiece& x, const StringPiece& y) { - const int r = StringPiece::wordmemcmp(x.data(), y.data(), - std::min(x.size(), y.size())); + const int r = StringPiece::wordmemcmp( + x.data(), y.data(), (x.size() < y.size() ? x.size() : y.size())); return ((r < 0) || ((r == 0) && (x.size() < y.size()))); } @@ -188,9 +187,6 @@ inline bool operator>=(const StringPiece& x, const StringPiece& y) { return !(x < y); } -// allow StringPiece to be logged (needed for unit testing). -extern std::ostream& operator<<(std::ostream& o, const StringPiece& piece); - } // namespace base #endif // BASE_STRING_PIECE_H_ diff --git a/base/synchronization/condition_variable.h b/base/synchronization/condition_variable.h index 3acd0ac..db75a49 100644 --- a/base/synchronization/condition_variable.h +++ b/base/synchronization/condition_variable.h @@ -75,7 +75,7 @@ #endif #include "base/basictypes.h" -#include "base/lock.h" +#include "base/synchronization/lock.h" namespace base { @@ -156,10 +156,10 @@ class ConditionVariable { RunState run_state_; // Private critical section for access to member data. - Lock internal_lock_; + base::Lock internal_lock_; // Lock that is acquired before calling Wait(). - Lock& user_lock_; + base::Lock& user_lock_; // Events that threads are blocked on. Event waiting_list_; @@ -176,7 +176,7 @@ class ConditionVariable { pthread_cond_t condition_; pthread_mutex_t* user_mutex_; #if !defined(NDEBUG) - Lock* user_lock_; // Needed to adjust shadow lock state on wait. + base::Lock* user_lock_; // Needed to adjust shadow lock state on wait. #endif #endif diff --git a/base/synchronization/condition_variable_unittest.cc b/base/synchronization/condition_variable_unittest.cc index 8cfe4fe..cf18320 100644 --- a/base/synchronization/condition_variable_unittest.cc +++ b/base/synchronization/condition_variable_unittest.cc @@ -8,11 +8,11 @@ #include <algorithm> #include <vector> -#include "base/synchronization/condition_variable.h" -#include "base/lock.h" #include "base/logging.h" #include "base/scoped_ptr.h" #include "base/spin_wait.h" +#include "base/synchronization/condition_variable.h" +#include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_collision_warner.h" #include "base/time.h" @@ -198,7 +198,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { Time start_time; // Used to time task processing. { - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); while (!queue.EveryIdWasAllocated()) queue.all_threads_have_ids()->Wait(); } @@ -209,7 +209,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { { // Since we have no tasks yet, all threads should be waiting by now. - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -232,7 +232,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { { // Wait until all 10 work tasks have at least been assigned. - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); while (queue.task_count()) queue.no_more_tasks()->Wait(); // The last of the tasks *might* still be running, but... all but one should @@ -252,7 +252,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { { // Check that all work was done by one thread id. - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -278,7 +278,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { { // Wait until all work tasks have at least been assigned. - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); while (queue.task_count()) queue.no_more_tasks()->Wait(); @@ -301,7 +301,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { queue.SpinUntilAllThreadsAreWaiting(); { - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -322,7 +322,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { queue.SpinUntilAllThreadsAreWaiting(); { - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -343,7 +343,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { queue.SpinUntilAllThreadsAreWaiting(); // Should take about 60 ms. { - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -362,7 +362,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { queue.SpinUntilAllThreadsAreWaiting(); // Should take about 60 ms. { - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -381,11 +381,11 @@ TEST_F(ConditionVariableTest, LargeFastTaskTest) { WorkQueue queue(kThreadCount); // Start the threads. Lock private_lock; // Used locally for master to wait. - AutoLock private_held_lock(private_lock); + base::AutoLock private_held_lock(private_lock); ConditionVariable private_cv(&private_lock); { - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); while (!queue.EveryIdWasAllocated()) queue.all_threads_have_ids()->Wait(); } @@ -395,7 +395,7 @@ TEST_F(ConditionVariableTest, LargeFastTaskTest) { { // Since we have no tasks, all threads should be waiting by now. - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -412,7 +412,7 @@ TEST_F(ConditionVariableTest, LargeFastTaskTest) { queue.work_is_available()->Broadcast(); // Start up all threads. // Wait until we've handed out all tasks. { - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); while (queue.task_count() != 0) queue.no_more_tasks()->Wait(); } @@ -423,7 +423,7 @@ TEST_F(ConditionVariableTest, LargeFastTaskTest) { { // With Broadcast(), every thread should have participated. // but with racing.. they may not all have done equal numbers of tasks. - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -440,7 +440,7 @@ TEST_F(ConditionVariableTest, LargeFastTaskTest) { // Wait until we've handed out all tasks { - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); while (queue.task_count() != 0) queue.no_more_tasks()->Wait(); } @@ -451,7 +451,7 @@ TEST_F(ConditionVariableTest, LargeFastTaskTest) { { // With Signal(), every thread should have participated. // but with racing.. they may not all have done four tasks. - AutoLock auto_lock(*queue.lock()); + base::AutoLock auto_lock(*queue.lock()); EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments()); EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks()); EXPECT_EQ(0, queue.task_count()); @@ -500,7 +500,7 @@ WorkQueue::WorkQueue(int thread_count) WorkQueue::~WorkQueue() { { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); SetShutdown(); } work_is_available_.Broadcast(); // Tell them all to terminate. @@ -558,7 +558,7 @@ bool WorkQueue::shutdown() const { // lock already acquired. bool WorkQueue::ThreadSafeCheckShutdown(int thread_count) { bool all_shutdown; - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); { // Declare in scope so DFAKE is guranteed to be destroyed before AutoLock. DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); @@ -657,7 +657,7 @@ void WorkQueue::SetShutdown() { void WorkQueue::SpinUntilAllThreadsAreWaiting() { while (true) { { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); if (waiting_thread_count_ == thread_count_) break; } @@ -668,7 +668,7 @@ void WorkQueue::SpinUntilAllThreadsAreWaiting() { void WorkQueue::SpinUntilTaskCountLessThan(int task_count) { while (true) { { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); if (task_count_ < task_count) break; } @@ -698,7 +698,7 @@ void WorkQueue::SpinUntilTaskCountLessThan(int task_count) { void WorkQueue::ThreadMain() { int thread_id; { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); thread_id = GetThreadId(); if (EveryIdWasAllocated()) all_threads_have_ids()->Signal(); // Tell creator we're ready. @@ -709,7 +709,7 @@ void WorkQueue::ThreadMain() { TimeDelta work_time; bool could_use_help; { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); while (0 == task_count() && !shutdown()) { ++waiting_thread_count_; work_is_available()->Wait(); @@ -732,13 +732,13 @@ void WorkQueue::ThreadMain() { if (work_time > TimeDelta::FromMilliseconds(0)) { // We could just sleep(), but we'll instead further exercise the // condition variable class, and do a timed wait. - AutoLock auto_lock(private_lock); + base::AutoLock auto_lock(private_lock); ConditionVariable private_cv(&private_lock); private_cv.TimedWait(work_time); // Unsynchronized waiting. } { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); // Send notification that we completed our "work." WorkIsCompleted(thread_id); } diff --git a/base/synchronization/waitable_event.h b/base/synchronization/waitable_event.h index 01b5987..9f357d1 100644 --- a/base/synchronization/waitable_event.h +++ b/base/synchronization/waitable_event.h @@ -15,8 +15,8 @@ #if defined(OS_POSIX) #include <list> #include <utility> -#include "base/lock.h" #include "base/ref_counted.h" +#include "base/synchronization/lock.h" #endif namespace base { @@ -149,7 +149,7 @@ class WaitableEvent { bool Dequeue(Waiter* waiter, void* tag); - Lock lock_; + base::Lock lock_; const bool manual_reset_; bool signaled_; std::list<Waiter*> waiters_; diff --git a/base/synchronization/waitable_event_posix.cc b/base/synchronization/waitable_event_posix.cc index 9cbc03a..ae03ead 100644 --- a/base/synchronization/waitable_event_posix.cc +++ b/base/synchronization/waitable_event_posix.cc @@ -149,6 +149,10 @@ class SyncWaiter : public WaitableEvent::Waiter { base::ConditionVariable cv_; }; +bool WaitableEvent::Wait() { + return TimedWait(TimeDelta::FromSeconds(-1)); +} + bool WaitableEvent::TimedWait(const TimeDelta& max_time) { const Time end_time(Time::Now() + max_time); const bool finite_time = max_time.ToInternalValue() >= 0; @@ -204,13 +208,6 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) { } } -bool WaitableEvent::Wait() { - return TimedWait(TimeDelta::FromSeconds(-1)); -} - -// ----------------------------------------------------------------------------- - - // ----------------------------------------------------------------------------- // Synchronous waiting on multiple objects. diff --git a/base/sys_info_win.cc b/base/sys_info_win.cc index fa3ab43..83e099c 100644 --- a/base/sys_info_win.cc +++ b/base/sys_info_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. @@ -63,8 +63,8 @@ std::string SysInfo::OperatingSystemVersion() { } // TODO: Implement OperatingSystemVersionComplete, which would include -// patchlevel/service pack number. See chrome/browser/views/bug_report_view.cc, -// BugReportView::SetOSVersion. +// patchlevel/service pack number. +// See chrome/browser/ui/views/bug_report_view.cc, BugReportView::SetOSVersion. // static std::string SysInfo::CPUArchitecture() { diff --git a/base/template_util.h b/base/template_util.h index 27bdb73..0408fc6 100644 --- a/base/template_util.h +++ b/base/template_util.h @@ -6,6 +6,8 @@ #define BASE_TEMPLATE_UTIL_H_ #pragma once +#include <cstddef> // For size_t. + #include "build/build_config.h" namespace base { @@ -27,14 +29,22 @@ typedef integral_constant<bool, false> false_type; template <class T> struct is_pointer : false_type {}; template <class T> struct is_pointer<T*> : true_type {}; +template<class> struct is_array : public false_type {}; +template<class T, size_t n> struct is_array<T[n]> : public true_type {}; +template<class T> struct is_array<T[]> : public true_type {}; + +template <class T> struct is_non_const_reference : false_type {}; +template <class T> struct is_non_const_reference<T&> : true_type {}; +template <class T> struct is_non_const_reference<const T&> : false_type {}; + namespace internal { -// Types small_ and big_ are guaranteed such that sizeof(small_) < -// sizeof(big_) -typedef char small_; +// Types YesType and NoType are guaranteed such that sizeof(YesType) < +// sizeof(NoType). +typedef char YesType; -struct big_ { - small_ dummy[2]; +struct NoType { + YesType dummy[2]; }; #if !defined(OS_WIN) @@ -50,13 +60,23 @@ struct big_ { template <typename From, typename To> struct ConvertHelper { - static small_ Test(To); - static big_ Test(...); + static YesType Test(To); + static NoType Test(...); 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 { + template <typename C> + static YesType Test(void(C::*)(void)); + + template <typename C> + static NoType Test(...); +}; + } // namespace internal #if !defined(OS_WIN) @@ -67,11 +87,18 @@ struct is_convertible : integral_constant<bool, sizeof(internal::ConvertHelper<From, To>::Test( internal::ConvertHelper<From, To>::Create())) - == sizeof(internal::small_)> { + == sizeof(internal::YesType)> { }; #endif // !defined(OS_WIN) +template <typename T> +struct is_class + : integral_constant<bool, + sizeof(internal::IsClassHelper::Test<T>(0)) == + sizeof(internal::YesType)> { +}; + } // namespace base #endif // BASE_TEMPLATE_UTIL_H_ diff --git a/base/template_util_unittest.cc b/base/template_util_unittest.cc new file mode 100644 index 0000000..51d4d33 --- /dev/null +++ b/base/template_util_unittest.cc @@ -0,0 +1,66 @@ +// 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/template_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace { + +struct AStruct {}; +class AClass {}; +enum AnEnum {}; + +class Parent {}; +class Child : public Parent {}; + +TEST(TemplateUtilTest, IsPointer) { + EXPECT_FALSE(is_pointer<int>::value); + EXPECT_FALSE(is_pointer<int&>::value); + EXPECT_TRUE(is_pointer<int*>::value); + EXPECT_TRUE(is_pointer<const int*>::value); +} + +TEST(TemplateUtilTest, IsArray) { + EXPECT_FALSE(is_array<int>::value); + EXPECT_FALSE(is_array<int*>::value); + EXPECT_FALSE(is_array<int(*)[3]>::value); + EXPECT_TRUE(is_array<int[]>::value); + EXPECT_TRUE(is_array<const int[]>::value); + EXPECT_TRUE(is_array<int[3]>::value); +} + +TEST(TemplateUtilTest, IsNonConstReference) { + EXPECT_FALSE(is_non_const_reference<int>::value); + EXPECT_FALSE(is_non_const_reference<const int&>::value); + 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, + // it sees the equivalent of + // + // EXPECT_TRUE( (is_convertible < Child), (Parent > ::value)); + // + // Silly C++. + EXPECT_TRUE( (is_convertible<Child, Parent>::value) ); + EXPECT_FALSE( (is_convertible<Parent, Child>::value) ); +} +#endif // !defined(OS_WIN) + +TEST(TemplateUtilTest, IsClass) { + EXPECT_TRUE(is_class<AStruct>::value); + EXPECT_TRUE(is_class<AClass>::value); + + EXPECT_FALSE(is_class<AnEnum>::value); + EXPECT_FALSE(is_class<int>::value); + EXPECT_FALSE(is_class<char*>::value); + EXPECT_FALSE(is_class<int&>::value); + EXPECT_FALSE(is_class<char[3]>::value); +} + +} // namespace +} // namespace base diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc index aa23f04..447a138 100644 --- a/base/test/test_suite.cc +++ b/base/test/test_suite.cc @@ -24,6 +24,10 @@ #include "testing/gtest/include/gtest/gtest.h" #include "testing/multiprocess_func_list.h" +#if defined(OS_MACOSX) +#include "base/test/mock_chrome_application_mac.h" +#endif + #if defined(TOOLKIT_USES_GTK) #include <gtk/gtk.h> #endif @@ -171,14 +175,21 @@ void TestSuite::SuppressErrorDialogs() { } void TestSuite::Initialize() { +#if defined(OS_MACOSX) + // Some of the app unit tests spin runloops. + mock_cr_app::RegisterMockCrApp(); +#endif + // Initialize logging. FilePath exe; PathService::Get(base::FILE_EXE, &exe); FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log")); - logging::InitLogging(log_filename.value().c_str(), - logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG, - logging::LOCK_LOG_FILE, - logging::DELETE_OLD_LOG_FILE); + logging::InitLogging( + log_filename.value().c_str(), + logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE, + logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); // We want process and thread IDs because we may have multiple processes. // Note: temporarily enabled timestamps in an effort to catch bug 6361. logging::SetLogItems(true, true, true, true); diff --git a/base/test/test_timeouts.cc b/base/test/test_timeouts.cc index 016456e..20cd8a9 100644 --- a/base/test/test_timeouts.cc +++ b/base/test/test_timeouts.cc @@ -44,7 +44,7 @@ bool TestTimeouts::initialized_ = false; // static int TestTimeouts::tiny_timeout_ms_ = 100; int TestTimeouts::action_timeout_ms_ = 2000; -int TestTimeouts::action_max_timeout_ms_ = 15000; +int TestTimeouts::action_max_timeout_ms_ = 20000; int TestTimeouts::large_test_timeout_ms_ = 3 * 60 * 1000; int TestTimeouts::huge_test_timeout_ms_ = 10 * 60 * 1000; diff --git a/base/third_party/dmg_fp/dtoa_wrapper.cc b/base/third_party/dmg_fp/dtoa_wrapper.cc index fbbaf80..e34b8a6 100644 --- a/base/third_party/dmg_fp/dtoa_wrapper.cc +++ b/base/third_party/dmg_fp/dtoa_wrapper.cc @@ -4,12 +4,12 @@ // // The purpose of this file is to supply the macro definintions necessary // to make third_party/dmg_fp/dtoa.cc threadsafe. -#include "base/lock.h" +#include "base/synchronization/lock.h" #include "base/logging.h" // We need two locks because they're sometimes grabbed at the same time. // A single lock would lead to an attempted recursive grab. -static Lock dtoa_locks[2]; +static base::Lock dtoa_locks[2]; /* * This define and the code below is to trigger thread-safe behavior diff --git a/base/third_party/dynamic_annotations/dynamic_annotations.c b/base/third_party/dynamic_annotations/dynamic_annotations.c index 4bfc33b..31e9a2b 100644 --- a/base/third_party/dynamic_annotations/dynamic_annotations.c +++ b/base/third_party/dynamic_annotations/dynamic_annotations.c @@ -50,77 +50,86 @@ #if DYNAMIC_ANNOTATIONS_ENABLED == 1 -void AnnotateRWLockCreate(const char *file, int line, - const volatile void *lock){} -void AnnotateRWLockDestroy(const char *file, int line, - const volatile void *lock){} -void AnnotateRWLockAcquired(const char *file, int line, - const volatile void *lock, long is_w){} -void AnnotateRWLockReleased(const char *file, int line, - const volatile void *lock, long is_w){} -void AnnotateBarrierInit(const char *file, int line, - const volatile void *barrier, long count, - long reinitialization_allowed) {} -void AnnotateBarrierWaitBefore(const char *file, int line, - const volatile void *barrier) {} -void AnnotateBarrierWaitAfter(const char *file, int line, - const volatile void *barrier) {} -void AnnotateBarrierDestroy(const char *file, int line, - const volatile void *barrier) {} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)( + const char *file, int line, const volatile void *lock){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)( + const char *file, int line, const volatile void *lock){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)( + const char *file, int line, const volatile void *lock, long is_w){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)( + const char *file, int line, const volatile void *lock, long is_w){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)( + const char *file, int line, const volatile void *barrier, long count, + long reinitialization_allowed) {} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)( + const char *file, int line, const volatile void *barrier) {} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)( + const char *file, int line, const volatile void *barrier) {} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)( + const char *file, int line, const volatile void *barrier) {} -void AnnotateCondVarWait(const char *file, int line, - const volatile void *cv, - const volatile void *lock){} -void AnnotateCondVarSignal(const char *file, int line, - const volatile void *cv){} -void AnnotateCondVarSignalAll(const char *file, int line, - const volatile void *cv){} -void AnnotatePublishMemoryRange(const char *file, int line, - const volatile void *address, - long size){} -void AnnotateUnpublishMemoryRange(const char *file, int line, - const volatile void *address, - long size){} -void AnnotatePCQCreate(const char *file, int line, - const volatile void *pcq){} -void AnnotatePCQDestroy(const char *file, int line, - const volatile void *pcq){} -void AnnotatePCQPut(const char *file, int line, - const volatile void *pcq){} -void AnnotatePCQGet(const char *file, int line, - const volatile void *pcq){} -void AnnotateNewMemory(const char *file, int line, - const volatile void *mem, - long size){} -void AnnotateExpectRace(const char *file, int line, - const volatile void *mem, - const char *description){} -void AnnotateBenignRace(const char *file, int line, - const volatile void *mem, - const char *description){} -void AnnotateBenignRaceSized(const char *file, int line, - const volatile void *mem, - long size, - const char *description) {} -void AnnotateMutexIsUsedAsCondVar(const char *file, int line, - const volatile void *mu){} -void AnnotateTraceMemory(const char *file, int line, - const volatile void *arg){} -void AnnotateThreadName(const char *file, int line, - const char *name){} -void AnnotateIgnoreReadsBegin(const char *file, int line){} -void AnnotateIgnoreReadsEnd(const char *file, int line){} -void AnnotateIgnoreWritesBegin(const char *file, int line){} -void AnnotateIgnoreWritesEnd(const char *file, int line){} -void AnnotateIgnoreSyncBegin(const char *file, int line){} -void AnnotateIgnoreSyncEnd(const char *file, int line){} -void AnnotateEnableRaceDetection(const char *file, int line, int enable){} -void AnnotateNoOp(const char *file, int line, - const volatile void *arg){} -void AnnotateFlushState(const char *file, int line){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)( + const char *file, int line, const volatile void *cv, + const volatile void *lock){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)( + const char *file, int line, const volatile void *cv){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)( + const char *file, int line, const volatile void *cv){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)( + const char *file, int line, const volatile void *address, long size){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)( + const char *file, int line, const volatile void *address, long size){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)( + const char *file, int line, const volatile void *pcq){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)( + const char *file, int line, const volatile void *pcq){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)( + const char *file, int line, const volatile void *pcq){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)( + const char *file, int line, const volatile void *pcq){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)( + const char *file, int line, const volatile void *mem, long size){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)( + const char *file, int line, const volatile void *mem, + const char *description){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)( + const char *file, int line){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRace)( + const char *file, int line, const volatile void *mem, + const char *description){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)( + const char *file, int line, const volatile void *mem, long size, + const char *description){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)( + const char *file, int line, const volatile void *mu){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)( + const char *file, int line, const volatile void *mu){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)( + const char *file, int line, const volatile void *arg){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)( + const char *file, int line, const char *name){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)( + const char *file, int line){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)( + const char *file, int line){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)( + const char *file, int line){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)( + const char *file, int line){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)( + const char *file, int line){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)( + const char *file, int line){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)( + const char *file, int line, int enable){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)( + const char *file, int line, const volatile void *arg){} +void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)( + const char *file, int line){} #endif /* DYNAMIC_ANNOTATIONS_ENABLED == 1 */ +#if DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 static int GetRunningOnValgrind(void) { #ifdef RUNNING_ON_VALGRIND if (RUNNING_ON_VALGRIND) return 1; @@ -157,3 +166,5 @@ int RunningOnValgrind(void) { running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind(); return local_running_on_valgrind; } + +#endif /* DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 */ diff --git a/base/third_party/dynamic_annotations/dynamic_annotations.h b/base/third_party/dynamic_annotations/dynamic_annotations.h index 373f2ac..263b8ef 100644 --- a/base/third_party/dynamic_annotations/dynamic_annotations.h +++ b/base/third_party/dynamic_annotations/dynamic_annotations.h @@ -53,6 +53,33 @@ #ifndef __DYNAMIC_ANNOTATIONS_H__ #define __DYNAMIC_ANNOTATIONS_H__ +#ifndef DYNAMIC_ANNOTATIONS_PREFIX +# define DYNAMIC_ANNOTATIONS_PREFIX +#endif + +#ifndef DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND +# define DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND 1 +#endif + +#ifdef DYNAMIC_ANNOTATIONS_WANT_ATTRIBUTE_WEAK +# ifdef __GNUC__ +# define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK __attribute__((weak)) +# else +/* TODO(glider): for Windows support we may want to change this macro in order + to prepend __declspec(selectany) to the annotations' declarations. */ +# error weak annotations are not supported for your compiler +# endif +#else +# define DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK +#endif + +/* The following preprocessor magic prepends the value of + DYNAMIC_ANNOTATIONS_PREFIX to annotation function names. */ +#define DYNAMIC_ANNOTATIONS_GLUE0(A, B) A##B +#define DYNAMIC_ANNOTATIONS_GLUE(A, B) DYNAMIC_ANNOTATIONS_GLUE0(A, B) +#define DYNAMIC_ANNOTATIONS_NAME(name) \ + DYNAMIC_ANNOTATIONS_GLUE(DYNAMIC_ANNOTATIONS_PREFIX, name) + #ifndef DYNAMIC_ANNOTATIONS_ENABLED # define DYNAMIC_ANNOTATIONS_ENABLED 0 #endif @@ -100,40 +127,36 @@ /* Report that wait on the condition variable at address "cv" has succeeded and the lock at address "lock" is held. */ #define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) \ - AnnotateCondVarWait(__FILE__, __LINE__, cv, lock) + DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(__FILE__, __LINE__, cv, lock) /* Report that wait on the condition variable at "cv" has succeeded. Variant w/o lock. */ #define ANNOTATE_CONDVAR_WAIT(cv) \ - AnnotateCondVarWait(__FILE__, __LINE__, cv, NULL) + DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)(__FILE__, __LINE__, cv, NULL) /* Report that we are about to signal on the condition variable at address "cv". */ #define ANNOTATE_CONDVAR_SIGNAL(cv) \ - AnnotateCondVarSignal(__FILE__, __LINE__, cv) + DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)(__FILE__, __LINE__, cv) /* Report that we are about to signal_all on the condition variable at address "cv". */ #define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) \ - AnnotateCondVarSignalAll(__FILE__, __LINE__, cv) + DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)(__FILE__, __LINE__, cv) /* Annotations for user-defined synchronization mechanisms. */ #define ANNOTATE_HAPPENS_BEFORE(obj) ANNOTATE_CONDVAR_SIGNAL(obj) #define ANNOTATE_HAPPENS_AFTER(obj) ANNOTATE_CONDVAR_WAIT(obj) - /* Report that the bytes in the range [pointer, pointer+size) are about - to be published safely. The race checker will create a happens-before - arc from the call ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) to - subsequent accesses to this memory. - Note: this annotation may not work properly if the race detector uses - sampling, i.e. does not observe all memory accesses. - */ + /* DEPRECATED. Don't use it. */ #define ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \ - AnnotatePublishMemoryRange(__FILE__, __LINE__, pointer, size) + DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)(__FILE__, __LINE__, \ + pointer, size) /* DEPRECATED. Don't use it. */ #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size) \ - AnnotateUnpublishMemoryRange(__FILE__, __LINE__, pointer, size) + DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)(__FILE__, __LINE__, \ + pointer, size) /* DEPRECATED. Don't use it. */ #define ANNOTATE_SWAP_MEMORY_RANGE(pointer, size) \ @@ -150,11 +173,19 @@ happens-before detectors this is a no-op. For more details see http://code.google.com/p/data-race-test/wiki/PureHappensBeforeVsHybrid . */ #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \ - AnnotateMutexIsUsedAsCondVar(__FILE__, __LINE__, mu) + DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(__FILE__, __LINE__, \ + mu) + + /* Opposite to ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. + Instruct the tool to NOT create h-b arcs between Unlock and Lock, even in + pure happens-before mode. For a hybrid mode this is a no-op. */ + #define ANNOTATE_NOT_HAPPENS_BEFORE_MUTEX(mu) \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)(__FILE__, __LINE__, mu) /* Deprecated. Use ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. */ #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) \ - AnnotateMutexIsUsedAsCondVar(__FILE__, __LINE__, mu) + DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)(__FILE__, __LINE__, \ + mu) /* ------------------------------------------------------------- Annotations useful when defining memory allocators, or when memory that @@ -165,7 +196,8 @@ is about to be reused, or when a the locking discipline for a variable changes. */ #define ANNOTATE_NEW_MEMORY(address, size) \ - AnnotateNewMemory(__FILE__, __LINE__, address, size) + DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)(__FILE__, __LINE__, address, \ + size) /* ------------------------------------------------------------- Annotations useful when defining FIFO queues that transfer data between @@ -176,21 +208,21 @@ should be used only for FIFO queues. For non-FIFO queues use ANNOTATE_HAPPENS_BEFORE (for put) and ANNOTATE_HAPPENS_AFTER (for get). */ #define ANNOTATE_PCQ_CREATE(pcq) \ - AnnotatePCQCreate(__FILE__, __LINE__, pcq) + DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)(__FILE__, __LINE__, pcq) /* Report that the queue at address "pcq" is about to be destroyed. */ #define ANNOTATE_PCQ_DESTROY(pcq) \ - AnnotatePCQDestroy(__FILE__, __LINE__, pcq) + DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)(__FILE__, __LINE__, pcq) /* Report that we are about to put an element into a FIFO queue at address "pcq". */ #define ANNOTATE_PCQ_PUT(pcq) \ - AnnotatePCQPut(__FILE__, __LINE__, pcq) + DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)(__FILE__, __LINE__, pcq) /* Report that we've just got an element from a FIFO queue at address "pcq". */ #define ANNOTATE_PCQ_GET(pcq) \ - AnnotatePCQGet(__FILE__, __LINE__, pcq) + DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)(__FILE__, __LINE__, pcq) /* ------------------------------------------------------------- Annotations that suppress errors. It is usually better to express the @@ -202,13 +234,14 @@ point where "pointer" has been allocated, preferably close to the point where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC. */ #define ANNOTATE_BENIGN_RACE(pointer, description) \ - AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, \ - sizeof(*(pointer)), description) + DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(__FILE__, __LINE__, \ + pointer, sizeof(*(pointer)), description) /* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to the memory range [address, address+size). */ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \ - AnnotateBenignRaceSized(__FILE__, __LINE__, address, size, description) + DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)(__FILE__, __LINE__, \ + address, size, description) /* Request the analysis tool to ignore all reads in the current thread until ANNOTATE_IGNORE_READS_END is called. @@ -216,19 +249,19 @@ other reads and all writes. See also ANNOTATE_UNPROTECTED_READ. */ #define ANNOTATE_IGNORE_READS_BEGIN() \ - AnnotateIgnoreReadsBegin(__FILE__, __LINE__) + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)(__FILE__, __LINE__) /* Stop ignoring reads. */ #define ANNOTATE_IGNORE_READS_END() \ - AnnotateIgnoreReadsEnd(__FILE__, __LINE__) + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)(__FILE__, __LINE__) /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes. */ #define ANNOTATE_IGNORE_WRITES_BEGIN() \ - AnnotateIgnoreWritesBegin(__FILE__, __LINE__) + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)(__FILE__, __LINE__) /* Stop ignoring writes. */ #define ANNOTATE_IGNORE_WRITES_END() \ - AnnotateIgnoreWritesEnd(__FILE__, __LINE__) + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)(__FILE__, __LINE__) /* Start ignoring all memory accesses (reads and writes). */ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ @@ -247,29 +280,30 @@ /* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore synchronization events: RWLOCK* and CONDVAR*. */ #define ANNOTATE_IGNORE_SYNC_BEGIN() \ - AnnotateIgnoreSyncBegin(__FILE__, __LINE__) + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)(__FILE__, __LINE__) /* Stop ignoring sync events. */ #define ANNOTATE_IGNORE_SYNC_END() \ - AnnotateIgnoreSyncEnd(__FILE__, __LINE__) + DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)(__FILE__, __LINE__) /* Enable (enable!=0) or disable (enable==0) race detection for all threads. This annotation could be useful if you want to skip expensive race analysis during some period of program execution, e.g. during initialization. */ #define ANNOTATE_ENABLE_RACE_DETECTION(enable) \ - AnnotateEnableRaceDetection(__FILE__, __LINE__, enable) + DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)(__FILE__, __LINE__, \ + enable) /* ------------------------------------------------------------- Annotations useful for debugging. */ /* Request to trace every access to "address". */ #define ANNOTATE_TRACE_MEMORY(address) \ - AnnotateTraceMemory(__FILE__, __LINE__, address) + DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)(__FILE__, __LINE__, address) /* Report the current thread name to a race detector. */ #define ANNOTATE_THREAD_NAME(name) \ - AnnotateThreadName(__FILE__, __LINE__, name) + DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)(__FILE__, __LINE__, name) /* ------------------------------------------------------------- Annotations useful when implementing locks. They are not @@ -278,20 +312,22 @@ /* Report that a lock has been created at address "lock". */ #define ANNOTATE_RWLOCK_CREATE(lock) \ - AnnotateRWLockCreate(__FILE__, __LINE__, lock) + DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)(__FILE__, __LINE__, lock) /* Report that the lock at address "lock" is about to be destroyed. */ #define ANNOTATE_RWLOCK_DESTROY(lock) \ - AnnotateRWLockDestroy(__FILE__, __LINE__, lock) + DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)(__FILE__, __LINE__, lock) /* Report that the lock at address "lock" has been acquired. is_w=1 for writer lock, is_w=0 for reader lock. */ #define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \ - AnnotateRWLockAcquired(__FILE__, __LINE__, lock, is_w) + DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)(__FILE__, __LINE__, lock, \ + is_w) /* Report that the lock at address "lock" is about to be released. */ #define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \ - AnnotateRWLockReleased(__FILE__, __LINE__, lock, is_w) + DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)(__FILE__, __LINE__, lock, \ + is_w) /* ------------------------------------------------------------- Annotations useful when implementing barriers. They are not @@ -302,20 +338,23 @@ If 'reinitialization_allowed' is true, initialization is allowed to happen multiple times w/o calling barrier_destroy() */ #define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \ - AnnotateBarrierInit(__FILE__, __LINE__, barrier, count, \ - reinitialization_allowed) + DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)(__FILE__, __LINE__, barrier, \ + count, reinitialization_allowed) /* Report that we are about to enter barrier_wait("barrier"). */ #define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \ - AnnotateBarrierWaitBefore(__FILE__, __LINE__, barrier) + DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)(__FILE__, __LINE__, \ + barrier) /* Report that we just exited barrier_wait("barrier"). */ #define ANNOTATE_BARRIER_WAIT_AFTER(barrier) \ - AnnotateBarrierWaitAfter(__FILE__, __LINE__, barrier) + DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)(__FILE__, __LINE__, \ + barrier) /* Report that the "barrier" has been destroyed. */ #define ANNOTATE_BARRIER_DESTROY(barrier) \ - AnnotateBarrierDestroy(__FILE__, __LINE__, barrier) + DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)(__FILE__, __LINE__, \ + barrier) /* ------------------------------------------------------------- Annotations useful for testing race detectors. */ @@ -323,16 +362,20 @@ /* Report that we expect a race on the variable at "address". Use only in unit tests for a race detector. */ #define ANNOTATE_EXPECT_RACE(address, description) \ - AnnotateExpectRace(__FILE__, __LINE__, address, description) + DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)(__FILE__, __LINE__, address, \ + description) + + #define ANNOTATE_FLUSH_EXPECTED_RACES() \ + DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)(__FILE__, __LINE__) /* A no-op. Insert where you like to test the interceptors. */ #define ANNOTATE_NO_OP(arg) \ - AnnotateNoOp(__FILE__, __LINE__, arg) + DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)(__FILE__, __LINE__, arg) /* Force the race detector to flush its state. The actual effect depends on * the implementation of the detector. */ #define ANNOTATE_FLUSH_STATE() \ - AnnotateFlushState(__FILE__, __LINE__) + DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)(__FILE__, __LINE__) #else /* DYNAMIC_ANNOTATIONS_ENABLED == 0 */ @@ -360,6 +403,7 @@ #define ANNOTATE_PCQ_GET(pcq) /* empty */ #define ANNOTATE_NEW_MEMORY(address, size) /* empty */ #define ANNOTATE_EXPECT_RACE(address, description) /* empty */ + #define ANNOTATE_FLUSH_EXPECTED_RACES(address, description) /* empty */ #define ANNOTATE_BENIGN_RACE(address, description) /* empty */ #define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) /* empty */ #define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) /* empty */ @@ -384,72 +428,106 @@ #ifdef __cplusplus extern "C" { #endif -void AnnotateRWLockCreate(const char *file, int line, - const volatile void *lock); -void AnnotateRWLockDestroy(const char *file, int line, - const volatile void *lock); -void AnnotateRWLockAcquired(const char *file, int line, - const volatile void *lock, long is_w); -void AnnotateRWLockReleased(const char *file, int line, - const volatile void *lock, long is_w); -void AnnotateBarrierInit(const char *file, int line, - const volatile void *barrier, long count, - long reinitialization_allowed); -void AnnotateBarrierWaitBefore(const char *file, int line, - const volatile void *barrier); -void AnnotateBarrierWaitAfter(const char *file, int line, - const volatile void *barrier); -void AnnotateBarrierDestroy(const char *file, int line, - const volatile void *barrier); -void AnnotateCondVarWait(const char *file, int line, - const volatile void *cv, - const volatile void *lock); -void AnnotateCondVarSignal(const char *file, int line, - const volatile void *cv); -void AnnotateCondVarSignalAll(const char *file, int line, - const volatile void *cv); -void AnnotatePublishMemoryRange(const char *file, int line, - const volatile void *address, - long size); -void AnnotateUnpublishMemoryRange(const char *file, int line, - const volatile void *address, - long size); -void AnnotatePCQCreate(const char *file, int line, - const volatile void *pcq); -void AnnotatePCQDestroy(const char *file, int line, - const volatile void *pcq); -void AnnotatePCQPut(const char *file, int line, - const volatile void *pcq); -void AnnotatePCQGet(const char *file, int line, - const volatile void *pcq); -void AnnotateNewMemory(const char *file, int line, - const volatile void *address, - long size); -void AnnotateExpectRace(const char *file, int line, - const volatile void *address, - const char *description); -void AnnotateBenignRace(const char *file, int line, - const volatile void *address, - const char *description); -void AnnotateBenignRaceSized(const char *file, int line, - const volatile void *address, - long size, - const char *description); -void AnnotateMutexIsUsedAsCondVar(const char *file, int line, - const volatile void *mu); -void AnnotateTraceMemory(const char *file, int line, - const volatile void *arg); -void AnnotateThreadName(const char *file, int line, - const char *name); -void AnnotateIgnoreReadsBegin(const char *file, int line); -void AnnotateIgnoreReadsEnd(const char *file, int line); -void AnnotateIgnoreWritesBegin(const char *file, int line); -void AnnotateIgnoreWritesEnd(const char *file, int line); -void AnnotateEnableRaceDetection(const char *file, int line, int enable); -void AnnotateNoOp(const char *file, int line, - const volatile void *arg); -void AnnotateFlushState(const char *file, int line); + +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockCreate)( + const char *file, int line, + const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockDestroy)( + const char *file, int line, + const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockAcquired)( + const char *file, int line, + const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateRWLockReleased)( + const char *file, int line, + const volatile void *lock, long is_w) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierInit)( + const char *file, int line, const volatile void *barrier, long count, + long reinitialization_allowed) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitBefore)( + const char *file, int line, + const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierWaitAfter)( + const char *file, int line, + const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBarrierDestroy)( + const char *file, int line, + const volatile void *barrier) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarWait)( + const char *file, int line, const volatile void *cv, + const volatile void *lock) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignal)( + const char *file, int line, + const volatile void *cv) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateCondVarSignalAll)( + const char *file, int line, + const volatile void *cv) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePublishMemoryRange)( + const char *file, int line, + const volatile void *address, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateUnpublishMemoryRange)( + const char *file, int line, + const volatile void *address, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQCreate)( + const char *file, int line, + const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQDestroy)( + const char *file, int line, + const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQPut)( + const char *file, int line, + const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotatePCQGet)( + const char *file, int line, + const volatile void *pcq) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateNewMemory)( + const char *file, int line, + const volatile void *mem, long size) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateExpectRace)( + const char *file, int line, const volatile void *mem, + const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushExpectedRaces)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRace)( + const char *file, int line, const volatile void *mem, + const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateBenignRaceSized)( + const char *file, int line, const volatile void *mem, long size, + const char *description) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsUsedAsCondVar)( + const char *file, int line, + const volatile void *mu) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateMutexIsNotPHB)( + const char *file, int line, + const volatile void *mu) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateTraceMemory)( + const char *file, int line, + const volatile void *arg) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateThreadName)( + const char *file, int line, + const char *name) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsBegin)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreReadsEnd)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesBegin)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreWritesEnd)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncBegin)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateIgnoreSyncEnd)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateEnableRaceDetection)( + const char *file, int line, int enable) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateNoOp)( + const char *file, int line, + const volatile void *arg) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; +void DYNAMIC_ANNOTATIONS_NAME(AnnotateFlushState)( + const char *file, int line) DYNAMIC_ANNOTATIONS_ATTRIBUTE_WEAK; + +#if DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 /* Return non-zero value if running under valgrind. If "valgrind.h" is included into dynamic_annotations.c, @@ -466,6 +544,7 @@ void AnnotateFlushState(const char *file, int line); change its return value. */ int RunningOnValgrind(void); +#endif /* DYNAMIC_ANNOTATIONS_PROVIDE_RUNNING_ON_VALGRIND == 1 */ #ifdef __cplusplus } diff --git a/base/threading/simple_thread.cc b/base/threading/simple_thread.cc index 2b030f6..4441477 100644 --- a/base/threading/simple_thread.cc +++ b/base/threading/simple_thread.cc @@ -10,6 +10,22 @@ namespace base { +SimpleThread::SimpleThread(const std::string& name_prefix) + : name_prefix_(name_prefix), name_(name_prefix), + thread_(), event_(true, false), tid_(0), joined_(false) { +} + +SimpleThread::SimpleThread(const std::string& name_prefix, + const Options& options) + : name_prefix_(name_prefix), name_(name_prefix), options_(options), + thread_(), event_(true, false), tid_(0), joined_(false) { +} + +SimpleThread::~SimpleThread() { + DCHECK(HasBeenStarted()) << "SimpleThread was never started."; + DCHECK(HasBeenJoined()) << "SimpleThread destroyed without being Join()ed."; +} + void SimpleThread::Start() { DCHECK(!HasBeenStarted()) << "Tried to Start a thread multiple times."; bool success = PlatformThread::Create(options_.stack_size(), this, &thread_); @@ -37,22 +53,6 @@ void SimpleThread::ThreadMain() { Run(); } -SimpleThread::SimpleThread(const std::string& name_prefix) - : name_prefix_(name_prefix), name_(name_prefix), - thread_(), event_(true, false), tid_(0), joined_(false) { -} - -SimpleThread::SimpleThread(const std::string& name_prefix, - const Options& options) - : name_prefix_(name_prefix), name_(name_prefix), options_(options), - thread_(), event_(true, false), tid_(0), joined_(false) { -} - -SimpleThread::~SimpleThread() { - DCHECK(HasBeenStarted()) << "SimpleThread was never started."; - DCHECK(HasBeenJoined()) << "SimpleThread destroyed without being Join()ed."; -} - DelegateSimpleThread::DelegateSimpleThread(Delegate* delegate, const std::string& name_prefix) : SimpleThread(name_prefix), diff --git a/base/threading/simple_thread.h b/base/threading/simple_thread.h index f55bd62..1631336 100644 --- a/base/threading/simple_thread.h +++ b/base/threading/simple_thread.h @@ -46,8 +46,8 @@ #include <vector> #include "base/basictypes.h" -#include "base/lock.h" #include "base/threading/platform_thread.h" +#include "base/synchronization/lock.h" #include "base/synchronization/waitable_event.h" namespace base { @@ -173,7 +173,7 @@ class DelegateSimpleThreadPool : public DelegateSimpleThread::Delegate { int num_threads_; std::vector<DelegateSimpleThread*> threads_; std::queue<Delegate*> delegates_; - Lock lock_; // Locks delegates_ + base::Lock lock_; // Locks delegates_ WaitableEvent dry_; // Not signaled when there is no work to do. }; diff --git a/base/threading/thread.cc b/base/threading/thread.cc index 09f8847..c0fb537 100644 --- a/base/threading/thread.cc +++ b/base/threading/thread.cc @@ -11,6 +11,17 @@ namespace base { +namespace { + +// We use this thread-local variable to record whether or not a thread exited +// because its Stop method was called. This allows us to catch cases where +// MessageLoop::Quit() is called directly, which is unexpected when using a +// Thread to setup and run a MessageLoop. +base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool( + base::LINKER_INITIALIZED); + +} // namespace + // This task is used to trigger the message loop to exit. class ThreadQuitTask : public Task { public: @@ -48,29 +59,6 @@ Thread::~Thread() { Stop(); } -namespace { - -// We use this thread-local variable to record whether or not a thread exited -// because its Stop method was called. This allows us to catch cases where -// MessageLoop::Quit() is called directly, which is unexpected when using a -// Thread to setup and run a MessageLoop. -base::LazyInstance<base::ThreadLocalBoolean> lazy_tls_bool( - base::LINKER_INITIALIZED); - -} // namespace - -void Thread::SetThreadWasQuitProperly(bool flag) { - lazy_tls_bool.Pointer()->Set(flag); -} - -bool Thread::GetThreadWasQuitProperly() { - bool quit_properly = true; -#ifndef NDEBUG - quit_properly = lazy_tls_bool.Pointer()->Get(); -#endif - return quit_properly; -} - bool Thread::Start() { return StartWithOptions(Options()); } @@ -140,6 +128,18 @@ void Thread::Run(MessageLoop* message_loop) { message_loop->Run(); } +void Thread::SetThreadWasQuitProperly(bool flag) { + lazy_tls_bool.Pointer()->Set(flag); +} + +bool Thread::GetThreadWasQuitProperly() { + bool quit_properly = true; +#ifndef NDEBUG + quit_properly = lazy_tls_bool.Pointer()->Get(); +#endif + return quit_properly; +} + void Thread::ThreadMain() { { // The message loop for this thread. diff --git a/base/threading/thread_checker.h b/base/threading/thread_checker.h index c0010fb..712b5b5 100644 --- a/base/threading/thread_checker.h +++ b/base/threading/thread_checker.h @@ -7,7 +7,7 @@ #pragma once #ifndef NDEBUG -#include "base/lock.h" +#include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" #endif // NDEBUG @@ -51,7 +51,7 @@ class ThreadChecker { private: void EnsureThreadIdAssigned() const; - mutable Lock lock_; + mutable base::Lock lock_; // This is mutable so that CalledOnValidThread can set it. // It's guarded by |lock_|. mutable PlatformThreadId valid_thread_id_; diff --git a/base/threading/thread_collision_warner_unittest.cc b/base/threading/thread_collision_warner_unittest.cc index 68987c3..a3d3b66 100644 --- a/base/threading/thread_collision_warner_unittest.cc +++ b/base/threading/thread_collision_warner_unittest.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "base/compiler_specific.h" -#include "base/lock.h" #include "base/scoped_ptr.h" +#include "base/synchronization/lock.h" #include "base/threading/platform_thread.h" #include "base/threading/simple_thread.h" #include "base/threading/thread_collision_warner.h" @@ -266,30 +266,30 @@ TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) { // a lock. class QueueUser : public base::DelegateSimpleThread::Delegate { public: - QueueUser(NonThreadSafeQueue& queue, Lock& lock) + QueueUser(NonThreadSafeQueue& queue, base::Lock& lock) : queue_(queue), lock_(lock) {} virtual void Run() { { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); queue_.push(0); } { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); queue_.pop(); } } private: NonThreadSafeQueue& queue_; - Lock& lock_; + base::Lock& lock_; }; AssertReporter* local_reporter = new AssertReporter(); NonThreadSafeQueue queue(local_reporter); - Lock lock; + base::Lock lock; QueueUser queue_user_a(queue, lock); QueueUser queue_user_b(queue, lock); @@ -340,34 +340,34 @@ TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) { // a lock. class QueueUser : public base::DelegateSimpleThread::Delegate { public: - QueueUser(NonThreadSafeQueue& queue, Lock& lock) + QueueUser(NonThreadSafeQueue& queue, base::Lock& lock) : queue_(queue), lock_(lock) {} virtual void Run() { { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); queue_.push(0); } { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); queue_.bar(); } { - AutoLock auto_lock(lock_); + base::AutoLock auto_lock(lock_); queue_.pop(); } } private: NonThreadSafeQueue& queue_; - Lock& lock_; + base::Lock& lock_; }; AssertReporter* local_reporter = new AssertReporter(); NonThreadSafeQueue queue(local_reporter); - Lock lock; + base::Lock lock; QueueUser queue_user_a(queue, lock); QueueUser queue_user_b(queue, lock); diff --git a/base/threading/thread_local_storage_posix.cc b/base/threading/thread_local_storage_posix.cc index 81b7332..3d0e187 100644 --- a/base/threading/thread_local_storage_posix.cc +++ b/base/threading/thread_local_storage_posix.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. @@ -9,7 +9,8 @@ namespace base { ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) - : initialized_(false) { + : initialized_(false), + key_(0) { Initialize(destructor); } diff --git a/base/threading/thread_local_storage_win.cc b/base/threading/thread_local_storage_win.cc index 2967a27..667d1b9 100644 --- a/base/threading/thread_local_storage_win.cc +++ b/base/threading/thread_local_storage_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. @@ -57,7 +57,8 @@ void** ThreadLocalStorage::Initialize() { } ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) - : initialized_(false) { + : initialized_(false), + slot_(0) { Initialize(destructor); } diff --git a/base/threading/worker_pool_posix_unittest.cc b/base/threading/worker_pool_posix_unittest.cc index 332c55e..c984ee3 100644 --- a/base/threading/worker_pool_posix_unittest.cc +++ b/base/threading/worker_pool_posix_unittest.cc @@ -6,8 +6,8 @@ #include <set> -#include "base/lock.h" #include "base/synchronization/condition_variable.h" +#include "base/synchronization/lock.h" #include "base/task.h" #include "base/threading/platform_thread.h" #include "base/synchronization/waitable_event.h" @@ -61,12 +61,12 @@ class IncrementingTask : public Task { virtual void Run() { AddSelfToUniqueThreadSet(); - AutoLock locked(*counter_lock_); + base::AutoLock locked(*counter_lock_); (*counter_)++; } void AddSelfToUniqueThreadSet() { - AutoLock locked(*unique_threads_lock_); + base::AutoLock locked(*unique_threads_lock_); unique_threads_->insert(PlatformThread::CurrentId()); } @@ -100,7 +100,7 @@ class BlockingIncrementingTask : public Task { virtual void Run() { { - AutoLock num_waiting_to_start_locked(*num_waiting_to_start_lock_); + base::AutoLock num_waiting_to_start_locked(*num_waiting_to_start_lock_); (*num_waiting_to_start_)++; } num_waiting_to_start_cv_->Signal(); @@ -138,14 +138,14 @@ class PosixDynamicThreadPoolTest : public testing::Test { } void WaitForTasksToStart(int num_tasks) { - AutoLock num_waiting_to_start_locked(num_waiting_to_start_lock_); + base::AutoLock num_waiting_to_start_locked(num_waiting_to_start_lock_); while (num_waiting_to_start_ < num_tasks) { num_waiting_to_start_cv_.Wait(); } } void WaitForIdleThreads(int num_idle_threads) { - AutoLock pool_locked(*peer_.lock()); + base::AutoLock pool_locked(*peer_.lock()); while (peer_.num_idle_threads() < num_idle_threads) { peer_.num_idle_threads_cv()->Wait(); } @@ -249,7 +249,7 @@ TEST_F(PosixDynamicThreadPoolTest, Complex) { // Wake up all idle threads so they can exit. { - AutoLock locked(*peer_.lock()); + base::AutoLock locked(*peer_.lock()); while (peer_.num_idle_threads() > 0) { peer_.tasks_available_cv()->Signal(); peer_.num_idle_threads_cv()->Wait(); diff --git a/base/time.h b/base/time.h index e1fbf96..ed4e772 100644 --- a/base/time.h +++ b/base/time.h @@ -411,10 +411,6 @@ class Time { int64 us_; }; -inline Time TimeDelta::operator+(Time t) const { - return Time(t.us_ + delta_); -} - // Inline the TimeDelta factory methods, for fast TimeDelta construction. // static @@ -447,6 +443,10 @@ inline TimeDelta TimeDelta::FromMicroseconds(int64 us) { return TimeDelta(us); } +inline Time TimeDelta::operator+(Time t) const { + return Time(t.us_ + delta_); +} + // TimeTicks ------------------------------------------------------------------ class TimeTicks { diff --git a/base/time_posix.cc b/base/time_posix.cc index 9646c87..62e6e49 100644 --- a/base/time_posix.cc +++ b/base/time_posix.cc @@ -14,6 +14,19 @@ namespace base { +struct timespec TimeDelta::ToTimeSpec() const { + int64 microseconds = InMicroseconds(); + time_t seconds = 0; + if (microseconds >= Time::kMicrosecondsPerSecond) { + seconds = InSeconds(); + microseconds -= seconds * Time::kMicrosecondsPerSecond; + } + struct timespec result = + {seconds, + microseconds * Time::kNanosecondsPerMicrosecond}; + return result; +} + #if !defined(OS_MACOSX) // The Time routines in this file use standard POSIX routines, or almost- // standard routines in the case of timegm. We need to use a Mach-specific @@ -60,6 +73,30 @@ Time Time::NowFromSystemTime() { return Now(); } +void Time::Explode(bool is_local, Exploded* exploded) const { + // Time stores times with microsecond resolution, but Exploded only carries + // millisecond resolution, so begin by being lossy. Adjust from Windows + // epoch (1601) to Unix epoch (1970); + int64 milliseconds = (us_ - kWindowsEpochDeltaMicroseconds) / + kMicrosecondsPerMillisecond; + time_t seconds = milliseconds / kMillisecondsPerSecond; + + struct tm timestruct; + if (is_local) + localtime_r(&seconds, ×truct); + else + gmtime_r(&seconds, ×truct); + + exploded->year = timestruct.tm_year + 1900; + exploded->month = timestruct.tm_mon + 1; + exploded->day_of_week = timestruct.tm_wday; + exploded->day_of_month = timestruct.tm_mday; + exploded->hour = timestruct.tm_hour; + exploded->minute = timestruct.tm_min; + exploded->second = timestruct.tm_sec; + exploded->millisecond = milliseconds % kMillisecondsPerSecond; +} + // static Time Time::FromExploded(bool is_local, const Exploded& exploded) { struct tm timestruct; @@ -119,30 +156,6 @@ Time Time::FromExploded(bool is_local, const Exploded& exploded) { kWindowsEpochDeltaMicroseconds); } -void Time::Explode(bool is_local, Exploded* exploded) const { - // Time stores times with microsecond resolution, but Exploded only carries - // millisecond resolution, so begin by being lossy. Adjust from Windows - // epoch (1601) to Unix epoch (1970); - int64 milliseconds = (us_ - kWindowsEpochDeltaMicroseconds) / - kMicrosecondsPerMillisecond; - time_t seconds = milliseconds / kMillisecondsPerSecond; - - struct tm timestruct; - if (is_local) - localtime_r(&seconds, ×truct); - else - gmtime_r(&seconds, ×truct); - - exploded->year = timestruct.tm_year + 1900; - exploded->month = timestruct.tm_mon + 1; - exploded->day_of_week = timestruct.tm_wday; - exploded->day_of_month = timestruct.tm_mday; - exploded->hour = timestruct.tm_hour; - exploded->minute = timestruct.tm_min; - exploded->second = timestruct.tm_sec; - exploded->millisecond = milliseconds % kMillisecondsPerSecond; -} - // TimeTicks ------------------------------------------------------------------ // FreeBSD 6 has CLOCK_MONOLITHIC but defines _POSIX_MONOTONIC_CLOCK to -1. #if (defined(OS_POSIX) && \ @@ -177,19 +190,6 @@ TimeTicks TimeTicks::HighResNow() { #endif // !OS_MACOSX -struct timespec TimeDelta::ToTimeSpec() const { - int64 microseconds = InMicroseconds(); - time_t seconds = 0; - if (microseconds >= Time::kMicrosecondsPerSecond) { - seconds = InSeconds(); - microseconds -= seconds * Time::kMicrosecondsPerSecond; - } - struct timespec result = - {seconds, - microseconds * Time::kNanosecondsPerMicrosecond}; - return result; -} - struct timeval Time::ToTimeVal() const { struct timeval result; int64 us = us_ - kTimeTToMicrosecondsOffset; diff --git a/base/time_win.cc b/base/time_win.cc index ca3aef1..601211c 100644 --- a/base/time_win.cc +++ b/base/time_win.cc @@ -41,10 +41,10 @@ #include <mmsystem.h> #include "base/basictypes.h" -#include "base/lock.h" #include "base/logging.h" #include "base/cpu.h" #include "base/singleton.h" +#include "base/synchronization/lock.h" using base::Time; using base::TimeDelta; @@ -262,7 +262,7 @@ DWORD last_seen_now = 0; // easy to use a Singleton without even knowing it, and that may lead to many // gotchas). Its impact on startup time should be negligible due to low-level // nature of time code. -Lock rollover_lock; +base::Lock rollover_lock; // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic // because it returns the number of milliseconds since Windows has started, @@ -270,7 +270,7 @@ Lock rollover_lock; // rollover ourselves, which works if TimeTicks::Now() is called at least every // 49 days. TimeDelta RolloverProtectedNow() { - AutoLock locked(rollover_lock); + base::AutoLock locked(rollover_lock); // We should hold the lock while calling tick_function to make sure that // we keep last_seen_now stay correctly in sync. DWORD now = tick_function(); @@ -409,4 +409,4 @@ int64 TimeTicks::GetQPCDriftMicroseconds() { // static bool TimeTicks::IsHighResClockWorking() { return HighResNowSingleton::GetInstance()->IsUsingHighResClock(); -}
\ No newline at end of file +} diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc index 3646000..a9f81b1 100644 --- a/base/tracked_objects.cc +++ b/base/tracked_objects.cc @@ -85,7 +85,7 @@ Births::Births(const Location& location) // static ThreadData* ThreadData::first_ = NULL; // static -Lock ThreadData::list_lock_; +base::Lock ThreadData::list_lock_; // static ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; @@ -111,7 +111,7 @@ ThreadData* ThreadData::current() { bool too_late_to_create = false; { registry = new ThreadData; - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); // Use lock to insure we have most recent status. if (!IsActive()) { too_late_to_create = true; @@ -285,7 +285,7 @@ Births* ThreadData::TallyABirth(const Location& location) { Births* tracker = new Births(location); // Lock since the map may get relocated now, and other threads sometimes // snapshot it (but they lock before copying it). - AutoLock lock(lock_); + base::AutoLock lock(lock_); birth_map_[location] = tracker; return tracker; } @@ -305,13 +305,13 @@ void ThreadData::TallyADeath(const Births& lifetimes, return; } - AutoLock lock(lock_); // Lock since the map may get relocated now. + base::AutoLock lock(lock_); // Lock since the map may get relocated now. death_map_[&lifetimes].RecordDeath(duration); } // static ThreadData* ThreadData::first() { - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); return first_; } @@ -323,7 +323,7 @@ const std::string ThreadData::ThreadName() const { // This may be called from another thread. void ThreadData::SnapshotBirthMap(BirthMap *output) const { - AutoLock lock(lock_); + base::AutoLock lock(lock_); for (BirthMap::const_iterator it = birth_map_.begin(); it != birth_map_.end(); ++it) (*output)[it->first] = it->second; @@ -331,7 +331,7 @@ void ThreadData::SnapshotBirthMap(BirthMap *output) const { // This may be called from another thread. void ThreadData::SnapshotDeathMap(DeathMap *output) const { - AutoLock lock(lock_); + base::AutoLock lock(lock_); for (DeathMap::const_iterator it = death_map_.begin(); it != death_map_.end(); ++it) (*output)[it->first] = it->second; @@ -348,7 +348,7 @@ void ThreadData::ResetAllThreadData() { } void ThreadData::Reset() { - AutoLock lock(lock_); + base::AutoLock lock(lock_); for (DeathMap::iterator it = death_map_.begin(); it != death_map_.end(); ++it) it->second.Clear(); @@ -372,7 +372,7 @@ class ThreadData::ThreadSafeDownCounter { private: size_t remaining_count_; - Lock lock_; // protect access to remaining_count_. + base::Lock lock_; // protect access to remaining_count_. }; ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count) @@ -382,7 +382,7 @@ ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count) bool ThreadData::ThreadSafeDownCounter::LastCaller() { { - AutoLock lock(lock_); + base::AutoLock lock(lock_); if (--remaining_count_) return false; } // Release lock, so we can delete everything in this instance. @@ -461,12 +461,12 @@ bool ThreadData::StartTracking(bool status) { #endif if (!status) { - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); status_ = SHUTDOWN; return true; } - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); DCHECK(status_ == UNINITIALIZED); CHECK(tls_index_.Initialize(NULL)); status_ = ACTIVE; @@ -504,7 +504,7 @@ void ThreadData::ShutdownSingleThreadedCleanup() { return; ThreadData* thread_data_list; { - AutoLock lock(list_lock_); + base::AutoLock lock(list_lock_); thread_data_list = first_; first_ = NULL; } @@ -614,7 +614,7 @@ void DataCollector::Append(const ThreadData& thread_data) { thread_data.SnapshotDeathMap(&death_map); // Use our lock to protect our accumulation activity. - AutoLock lock(accumulation_lock_); + base::AutoLock lock(accumulation_lock_); DCHECK(count_of_contributing_threads_); @@ -743,11 +743,6 @@ void Comparator::Clear() { selector_ = NIL; } -void Comparator::Sort(DataCollector::Collection* collection) const { - std::sort(collection->begin(), collection->end(), *this); -} - - bool Comparator::operator()(const Snapshot& left, const Snapshot& right) const { switch (selector_) { @@ -816,6 +811,10 @@ bool Comparator::operator()(const Snapshot& left, return false; } +void Comparator::Sort(DataCollector::Collection* collection) const { + std::sort(collection->begin(), collection->end(), *this); +} + bool Comparator::Equivalent(const Snapshot& left, const Snapshot& right) const { switch (selector_) { diff --git a/base/tracked_objects.h b/base/tracked_objects.h index 07731ff..ed629c3 100644 --- a/base/tracked_objects.h +++ b/base/tracked_objects.h @@ -10,7 +10,7 @@ #include <string> #include <vector> -#include "base/lock.h" +#include "base/synchronization/lock.h" #include "base/tracked.h" #include "base/threading/thread_local_storage.h" @@ -322,7 +322,7 @@ class DataCollector { // seen a death count. BirthCount global_birth_count_; - Lock accumulation_lock_; // Protects access during accumulation phase. + base::Lock accumulation_lock_; // Protects access during accumulation phase. DISALLOW_COPY_AND_ASSIGN(DataCollector); }; @@ -577,7 +577,7 @@ class ThreadData { // Link to the most recently created instance (starts a null terminated list). static ThreadData* first_; // Protection for access to first_. - static Lock list_lock_; + static base::Lock list_lock_; // We set status_ to SHUTDOWN when we shut down the tracking service. This // setting is redundantly established by all participating threads so that we @@ -613,7 +613,7 @@ class ThreadData { // thread, or reading from another thread. For reading from this thread we // don't need a lock, as there is no potential for a conflict since the // writing is only done from this thread. - mutable Lock lock_; + mutable base::Lock lock_; DISALLOW_COPY_AND_ASSIGN(ThreadData); }; diff --git a/base/tuple.h b/base/tuple.h index 6b0d336..13d8722 100644 --- a/base/tuple.h +++ b/base/tuple.h @@ -548,13 +548,6 @@ inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) { template <class ObjT, class Method, class A> inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) { - -#if defined(OS_CHROMEOS) - // To troubleshoot crosbug.com/7327. - CHECK(obj); - CHECK(&arg); - CHECK(method); -#endif (obj->*method)(arg.a); } diff --git a/base/utf_string_conversions.cc b/base/utf_string_conversions.cc index 41a70db..7b73696 100644 --- a/base/utf_string_conversions.cc +++ b/base/utf_string_conversions.cc @@ -133,7 +133,7 @@ bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) { return ConvertUnicode(src, src_len, output); } -string16 UTF8ToUTF16(const std::string& utf8) { +string16 UTF8ToUTF16(const base::StringPiece& utf8) { string16 ret; // Ignore the success flag of this call, it will do the best it can for // invalid input, which is what we want here. @@ -161,7 +161,7 @@ bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) { return UTF8ToWide(src, src_len, output); } -string16 UTF8ToUTF16(const std::string& utf8) { +string16 UTF8ToUTF16(const base::StringPiece& utf8) { return UTF8ToWide(utf8); } @@ -175,22 +175,12 @@ std::string UTF16ToUTF8(const string16& utf16) { #endif -std::wstring ASCIIToWide(const char* ascii) { - DCHECK(IsStringASCII(ascii)) << ascii; - return std::wstring(ascii, &ascii[strlen(ascii)]); -} - -std::wstring ASCIIToWide(const std::string& ascii) { +std::wstring ASCIIToWide(const base::StringPiece& ascii) { DCHECK(IsStringASCII(ascii)) << ascii; return std::wstring(ascii.begin(), ascii.end()); } -string16 ASCIIToUTF16(const char* ascii) { - DCHECK(IsStringASCII(ascii)) << ascii; - return string16(ascii, &ascii[strlen(ascii)]); -} - -string16 ASCIIToUTF16(const std::string& ascii) { +string16 ASCIIToUTF16(const base::StringPiece& ascii) { DCHECK(IsStringASCII(ascii)) << ascii; return string16(ascii.begin(), ascii.end()); } diff --git a/base/utf_string_conversions.h b/base/utf_string_conversions.h index 6c49b41..4aa4d41 100644 --- a/base/utf_string_conversions.h +++ b/base/utf_string_conversions.h @@ -9,10 +9,7 @@ #include <string> #include "base/string16.h" - -namespace base { -class StringPiece; -} +#include "base/string_piece.h" // These convert between UTF-8, -16, and -32 strings. They are potentially slow, // so avoid unnecessary conversions. The low-level versions return a boolean @@ -31,7 +28,7 @@ bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output); std::wstring UTF16ToWide(const string16& utf16); bool UTF8ToUTF16(const char* src, size_t src_len, string16* output); -string16 UTF8ToUTF16(const std::string& utf8); +string16 UTF8ToUTF16(const base::StringPiece& utf8); bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output); std::string UTF16ToUTF8(const string16& utf16); @@ -50,16 +47,7 @@ std::string UTF16ToUTF8(const string16& utf16); // These convert an ASCII string, typically a hardcoded constant, to a // UTF16/Wide string. -// -// Note that this doesn't use StringPiece because it's very common to need -// ASCIIToUTF16("foo"), and either we have to include it in this file, or -// forward declare it and force all callers to include string_piece.h. Given -// that string_piece brings in complicated stuff like <algorithm>, it's -// easier to just duplicate these very simple definitions for the two calling -// cases we actually use. -std::wstring ASCIIToWide(const char* ascii); -std::wstring ASCIIToWide(const std::string& ascii); -string16 ASCIIToUTF16(const char* ascii); -string16 ASCIIToUTF16(const std::string& ascii); +std::wstring ASCIIToWide(const base::StringPiece& ascii); +string16 ASCIIToUTF16(const base::StringPiece& ascii); #endif // BASE_UTF_STRING_CONVERSIONS_H_ diff --git a/base/values.cc b/base/values.cc index 3522569..9c96f26 100644 --- a/base/values.cc +++ b/base/values.cc @@ -70,27 +70,27 @@ Value* Value::CreateNullValue() { } // static -Value* Value::CreateBooleanValue(bool in_value) { +FundamentalValue* Value::CreateBooleanValue(bool in_value) { return new FundamentalValue(in_value); } // static -Value* Value::CreateIntegerValue(int in_value) { +FundamentalValue* Value::CreateIntegerValue(int in_value) { return new FundamentalValue(in_value); } // static -Value* Value::CreateRealValue(double in_value) { +FundamentalValue* Value::CreateDoubleValue(double in_value) { return new FundamentalValue(in_value); } // static -Value* Value::CreateStringValue(const std::string& in_value) { +StringValue* Value::CreateStringValue(const std::string& in_value) { return new StringValue(in_value); } // static -Value* Value::CreateStringValue(const string16& in_value) { +StringValue* Value::CreateStringValue(const string16& in_value) { return new StringValue(in_value); } @@ -107,7 +107,7 @@ bool Value::GetAsInteger(int* out_value) const { return false; } -bool Value::GetAsReal(double* out_value) const { +bool Value::GetAsDouble(double* out_value) const { return false; } @@ -158,7 +158,7 @@ FundamentalValue::FundamentalValue(int in_value) } FundamentalValue::FundamentalValue(double in_value) - : Value(TYPE_REAL), real_value_(in_value) { + : Value(TYPE_DOUBLE), double_value_(in_value) { } FundamentalValue::~FundamentalValue() { @@ -176,13 +176,13 @@ bool FundamentalValue::GetAsInteger(int* out_value) const { return (IsType(TYPE_INTEGER)); } -bool FundamentalValue::GetAsReal(double* out_value) const { - if (out_value && IsType(TYPE_REAL)) - *out_value = real_value_; - return (IsType(TYPE_REAL)); +bool FundamentalValue::GetAsDouble(double* out_value) const { + if (out_value && IsType(TYPE_DOUBLE)) + *out_value = double_value_; + return (IsType(TYPE_DOUBLE)); } -Value* FundamentalValue::DeepCopy() const { +FundamentalValue* FundamentalValue::DeepCopy() const { switch (GetType()) { case TYPE_BOOLEAN: return CreateBooleanValue(boolean_value_); @@ -190,8 +190,8 @@ Value* FundamentalValue::DeepCopy() const { case TYPE_INTEGER: return CreateIntegerValue(integer_value_); - case TYPE_REAL: - return CreateRealValue(real_value_); + case TYPE_DOUBLE: + return CreateDoubleValue(double_value_); default: NOTREACHED(); @@ -212,9 +212,9 @@ bool FundamentalValue::Equals(const Value* other) const { int lhs, rhs; return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs; } - case TYPE_REAL: { + case TYPE_DOUBLE: { double lhs, rhs; - return GetAsReal(&lhs) && other->GetAsReal(&rhs) && lhs == rhs; + return GetAsDouble(&lhs) && other->GetAsDouble(&rhs) && lhs == rhs; } default: NOTREACHED(); @@ -250,7 +250,7 @@ bool StringValue::GetAsString(string16* out_value) const { return true; } -Value* StringValue::DeepCopy() const { +StringValue* StringValue::DeepCopy() const { return CreateStringValue(value_); } @@ -288,7 +288,7 @@ BinaryValue* BinaryValue::CreateWithCopiedBuffer(const char* buffer, return new BinaryValue(buffer_copy, size); } -Value* BinaryValue::DeepCopy() const { +BinaryValue* BinaryValue::DeepCopy() const { return CreateWithCopiedBuffer(buffer_, size_); } @@ -367,8 +367,8 @@ void DictionaryValue::SetInteger(const std::string& path, int in_value) { Set(path, CreateIntegerValue(in_value)); } -void DictionaryValue::SetReal(const std::string& path, double in_value) { - Set(path, CreateRealValue(in_value)); +void DictionaryValue::SetDouble(const std::string& path, double in_value) { + Set(path, CreateDoubleValue(in_value)); } void DictionaryValue::SetString(const std::string& path, @@ -430,13 +430,13 @@ bool DictionaryValue::GetInteger(const std::string& path, return value->GetAsInteger(out_value); } -bool DictionaryValue::GetReal(const std::string& path, - double* out_value) const { +bool DictionaryValue::GetDouble(const std::string& path, + double* out_value) const { Value* value; if (!Get(path, &value)) return false; - return value->GetAsReal(out_value); + return value->GetAsDouble(out_value); } bool DictionaryValue::GetString(const std::string& path, @@ -533,13 +533,13 @@ bool DictionaryValue::GetIntegerWithoutPathExpansion(const std::string& key, return value->GetAsInteger(out_value); } -bool DictionaryValue::GetRealWithoutPathExpansion(const std::string& key, - double* out_value) const { +bool DictionaryValue::GetDoubleWithoutPathExpansion(const std::string& key, + double* out_value) const { Value* value; if (!GetWithoutPathExpansion(key, &value)) return false; - return value->GetAsReal(out_value); + return value->GetAsDouble(out_value); } bool DictionaryValue::GetStringWithoutPathExpansion( @@ -646,7 +646,7 @@ void DictionaryValue::MergeDictionary(const DictionaryValue* dictionary) { } } -Value* DictionaryValue::DeepCopy() const { +DictionaryValue* DictionaryValue::DeepCopy() const { DictionaryValue* result = new DictionaryValue; for (ValueMap::const_iterator current_entry(dictionary_.begin()); @@ -742,12 +742,12 @@ bool ListValue::GetInteger(size_t index, int* out_value) const { return value->GetAsInteger(out_value); } -bool ListValue::GetReal(size_t index, double* out_value) const { +bool ListValue::GetDouble(size_t index, double* out_value) const { Value* value; if (!Get(index, &value)) return false; - return value->GetAsReal(out_value); + return value->GetAsDouble(out_value); } bool ListValue::GetString(size_t index, std::string* out_value) const { @@ -862,7 +862,7 @@ bool ListValue::GetAsList(ListValue** out_value) { return true; } -Value* ListValue::DeepCopy() const { +ListValue* ListValue::DeepCopy() const { ListValue* result = new ListValue; for (ValueVector::const_iterator i(list_.begin()); i != list_.end(); ++i) diff --git a/base/values.h b/base/values.h index d69a685..408ca8b 100644 --- a/base/values.h +++ b/base/values.h @@ -50,7 +50,7 @@ class Value { TYPE_NULL = 0, TYPE_BOOLEAN, TYPE_INTEGER, - TYPE_REAL, + TYPE_DOUBLE, TYPE_STRING, TYPE_BINARY, TYPE_DICTIONARY, @@ -63,11 +63,11 @@ class Value { // kinds of values without thinking about which class implements them. // These can always be expected to return a valid Value*. static Value* CreateNullValue(); - static Value* CreateBooleanValue(bool in_value); - static Value* CreateIntegerValue(int in_value); - static Value* CreateRealValue(double in_value); - static Value* CreateStringValue(const std::string& in_value); - static Value* CreateStringValue(const string16& in_value); + static FundamentalValue* CreateBooleanValue(bool in_value); + static FundamentalValue* CreateIntegerValue(int in_value); + static FundamentalValue* CreateDoubleValue(double in_value); + static StringValue* CreateStringValue(const std::string& in_value); + static StringValue* CreateStringValue(const string16& in_value); // 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. @@ -89,13 +89,16 @@ class Value { // returned; otherwise, false is returned and |out_value| is unchanged. virtual bool GetAsBoolean(bool* out_value) const; virtual bool GetAsInteger(int* out_value) const; - virtual bool GetAsReal(double* out_value) const; + virtual bool GetAsDouble(double* out_value) const; virtual bool GetAsString(std::string* out_value) const; virtual bool GetAsString(string16* out_value) const; virtual bool GetAsList(ListValue** out_value); // This creates a deep copy of the entire Value tree, and returns a pointer // to the copy. The caller gets ownership of the copy, of course. + // + // Subclasses return their own type directly in their overrides; + // this works because C++ supports covariant return types. virtual Value* DeepCopy() const; // Compares if two Value objects have equal contents. @@ -129,15 +132,15 @@ class FundamentalValue : public Value { // Subclassed methods virtual bool GetAsBoolean(bool* out_value) const; virtual bool GetAsInteger(int* out_value) const; - virtual bool GetAsReal(double* out_value) const; - virtual Value* DeepCopy() const; + virtual bool GetAsDouble(double* out_value) const; + virtual FundamentalValue* DeepCopy() const; virtual bool Equals(const Value* other) const; private: union { bool boolean_value_; int integer_value_; - double real_value_; + double double_value_; }; DISALLOW_COPY_AND_ASSIGN(FundamentalValue); @@ -156,7 +159,7 @@ class StringValue : public Value { // Subclassed methods virtual bool GetAsString(std::string* out_value) const; virtual bool GetAsString(string16* out_value) const; - virtual Value* DeepCopy() const; + virtual StringValue* DeepCopy() const; virtual bool Equals(const Value* other) const; private: @@ -185,7 +188,7 @@ class BinaryValue: public Value { const char* GetBuffer() const { return buffer_; } // Overridden from Value: - virtual Value* DeepCopy() const; + virtual BinaryValue* DeepCopy() const; virtual bool Equals(const Value* other) const; private: @@ -234,7 +237,7 @@ class DictionaryValue : public Value { // value at that path, even if it has a different type. void SetBoolean(const std::string& path, bool in_value); void SetInteger(const std::string& path, int in_value); - void SetReal(const std::string& path, double in_value); + void SetDouble(const std::string& path, double in_value); void SetString(const std::string& path, const std::string& in_value); void SetString(const std::string& path, const string16& in_value); @@ -256,7 +259,7 @@ class DictionaryValue : public Value { // the end of the path can be returned in the form specified. bool GetBoolean(const std::string& path, bool* out_value) const; bool GetInteger(const std::string& path, int* out_value) const; - bool GetReal(const std::string& path, double* out_value) const; + bool GetDouble(const std::string& path, double* out_value) const; bool GetString(const std::string& path, std::string* out_value) const; bool GetString(const std::string& path, string16* out_value) const; bool GetStringASCII(const std::string& path, std::string* out_value) const; @@ -271,7 +274,7 @@ class DictionaryValue : public Value { Value** out_value) const; bool GetIntegerWithoutPathExpansion(const std::string& key, int* out_value) const; - bool GetRealWithoutPathExpansion(const std::string& key, + bool GetDoubleWithoutPathExpansion(const std::string& key, double* out_value) const; bool GetStringWithoutPathExpansion(const std::string& key, std::string* out_value) const; @@ -330,7 +333,7 @@ class DictionaryValue : public Value { key_iterator end_keys() const { return key_iterator(dictionary_.end()); } // Overridden from Value: - virtual Value* DeepCopy() const; + virtual DictionaryValue* DeepCopy() const; virtual bool Equals(const Value* other) const; private: @@ -374,7 +377,7 @@ class ListValue : public Value { // in the specified form. bool GetBoolean(size_t index, bool* out_value) const; bool GetInteger(size_t index, int* out_value) const; - bool GetReal(size_t index, double* out_value) const; + bool GetDouble(size_t index, double* out_value) const; bool GetString(size_t index, std::string* out_value) const; bool GetString(size_t index, string16* out_value) const; bool GetBinary(size_t index, BinaryValue** out_value) const; @@ -417,7 +420,7 @@ class ListValue : public Value { // Overridden from Value: virtual bool GetAsList(ListValue** out_value); - virtual Value* DeepCopy() const; + virtual ListValue* DeepCopy() const; virtual bool Equals(const Value* other) const; private: diff --git a/base/values_unittest.cc b/base/values_unittest.cc index adcd07e..5f901b5 100644 --- a/base/values_unittest.cc +++ b/base/values_unittest.cc @@ -60,7 +60,7 @@ TEST_F(ValuesTest, List) { scoped_ptr<ListValue> mixed_list(new ListValue()); mixed_list->Set(0, Value::CreateBooleanValue(true)); mixed_list->Set(1, Value::CreateIntegerValue(42)); - mixed_list->Set(2, Value::CreateRealValue(88.8)); + mixed_list->Set(2, Value::CreateDoubleValue(88.8)); mixed_list->Set(3, Value::CreateStringValue("foo")); ASSERT_EQ(4u, mixed_list->GetSize()); @@ -74,7 +74,7 @@ TEST_F(ValuesTest, List) { ASSERT_FALSE(mixed_list->GetInteger(0, &int_value)); ASSERT_EQ(0, int_value); - ASSERT_FALSE(mixed_list->GetReal(1, &double_value)); + ASSERT_FALSE(mixed_list->GetDouble(1, &double_value)); ASSERT_EQ(0.0, double_value); ASSERT_FALSE(mixed_list->GetString(2, &string_value)); ASSERT_EQ("", string_value); @@ -85,7 +85,7 @@ TEST_F(ValuesTest, List) { ASSERT_TRUE(bool_value); ASSERT_TRUE(mixed_list->GetInteger(1, &int_value)); ASSERT_EQ(42, int_value); - ASSERT_TRUE(mixed_list->GetReal(2, &double_value)); + ASSERT_TRUE(mixed_list->GetDouble(2, &double_value)); ASSERT_EQ(88.8, double_value); ASSERT_TRUE(mixed_list->GetString(3, &string_value)); ASSERT_EQ("foo", string_value); @@ -323,15 +323,16 @@ TEST_F(ValuesTest, DeepCopy) { DictionaryValue original_dict; Value* original_null = Value::CreateNullValue(); original_dict.Set("null", original_null); - Value* original_bool = Value::CreateBooleanValue(true); + FundamentalValue* original_bool = Value::CreateBooleanValue(true); original_dict.Set("bool", original_bool); - Value* original_int = Value::CreateIntegerValue(42); + FundamentalValue* original_int = Value::CreateIntegerValue(42); original_dict.Set("int", original_int); - Value* original_real = Value::CreateRealValue(3.14); - original_dict.Set("real", original_real); - Value* original_string = Value::CreateStringValue("hello"); + FundamentalValue* original_double = Value::CreateDoubleValue(3.14); + original_dict.Set("double", original_double); + StringValue* original_string = Value::CreateStringValue("hello"); original_dict.Set("string", original_string); - Value* original_string16 = Value::CreateStringValue(ASCIIToUTF16("hello16")); + StringValue* original_string16 = + Value::CreateStringValue(ASCIIToUTF16("hello16")); original_dict.Set("string16", original_string16); char* original_buffer = new char[42]; @@ -340,14 +341,13 @@ TEST_F(ValuesTest, DeepCopy) { original_dict.Set("binary", original_binary); ListValue* original_list = new ListValue(); - Value* original_list_element_0 = Value::CreateIntegerValue(0); + FundamentalValue* original_list_element_0 = Value::CreateIntegerValue(0); original_list->Append(original_list_element_0); - Value* original_list_element_1 = Value::CreateIntegerValue(1); + FundamentalValue* original_list_element_1 = Value::CreateIntegerValue(1); original_list->Append(original_list_element_1); original_dict.Set("list", original_list); - scoped_ptr<DictionaryValue> copy_dict( - static_cast<DictionaryValue*>(original_dict.DeepCopy())); + scoped_ptr<DictionaryValue> copy_dict(original_dict.DeepCopy()); ASSERT_TRUE(copy_dict.get()); ASSERT_NE(copy_dict.get(), &original_dict); @@ -375,14 +375,14 @@ TEST_F(ValuesTest, DeepCopy) { ASSERT_TRUE(copy_int->GetAsInteger(©_int_value)); ASSERT_EQ(42, copy_int_value); - Value* copy_real = NULL; - ASSERT_TRUE(copy_dict->Get("real", ©_real)); - ASSERT_TRUE(copy_real); - ASSERT_NE(copy_real, original_real); - ASSERT_TRUE(copy_real->IsType(Value::TYPE_REAL)); - double copy_real_value = 0; - ASSERT_TRUE(copy_real->GetAsReal(©_real_value)); - ASSERT_EQ(3.14, copy_real_value); + Value* copy_double = NULL; + ASSERT_TRUE(copy_dict->Get("double", ©_double)); + ASSERT_TRUE(copy_double); + ASSERT_NE(copy_double, original_double); + ASSERT_TRUE(copy_double->IsType(Value::TYPE_DOUBLE)); + double copy_double_value = 0; + ASSERT_TRUE(copy_double->GetAsDouble(©_double_value)); + ASSERT_EQ(3.14, copy_double_value); Value* copy_string = NULL; ASSERT_TRUE(copy_dict->Get("string", ©_string)); @@ -459,13 +459,13 @@ TEST_F(ValuesTest, Equals) { DictionaryValue dv; dv.SetBoolean("a", false); dv.SetInteger("b", 2); - dv.SetReal("c", 2.5); + dv.SetDouble("c", 2.5); dv.SetString("d1", "string"); dv.SetString("d2", ASCIIToUTF16("http://google.com")); dv.Set("e", Value::CreateNullValue()); scoped_ptr<DictionaryValue> copy; - copy.reset(static_cast<DictionaryValue*>(dv.DeepCopy())); + copy.reset(dv.DeepCopy()); EXPECT_TRUE(dv.Equals(copy.get())); ListValue* list = new ListValue; @@ -481,7 +481,7 @@ TEST_F(ValuesTest, Equals) { EXPECT_FALSE(dv.Equals(copy.get())); // Check if Equals detects differences in only the keys. - copy.reset(static_cast<DictionaryValue*>(dv.DeepCopy())); + copy.reset(dv.DeepCopy()); EXPECT_TRUE(dv.Equals(copy.get())); copy->Remove("a", NULL); copy->SetBoolean("aa", false); @@ -511,6 +511,62 @@ TEST_F(ValuesTest, StaticEquals) { EXPECT_FALSE(Value::Equals(NULL, null1.get())); } +TEST_F(ValuesTest, DeepCopyCovariantReturnTypes) { + DictionaryValue original_dict; + Value* original_null = Value::CreateNullValue(); + original_dict.Set("null", original_null); + FundamentalValue* original_bool = Value::CreateBooleanValue(true); + original_dict.Set("bool", original_bool); + FundamentalValue* original_int = Value::CreateIntegerValue(42); + original_dict.Set("int", original_int); + FundamentalValue* original_double = Value::CreateDoubleValue(3.14); + original_dict.Set("double", original_double); + StringValue* original_string = Value::CreateStringValue("hello"); + original_dict.Set("string", original_string); + StringValue* original_string16 = + Value::CreateStringValue(ASCIIToUTF16("hello16")); + original_dict.Set("string16", original_string16); + + char* original_buffer = new char[42]; + memset(original_buffer, '!', 42); + BinaryValue* original_binary = Value::CreateBinaryValue(original_buffer, 42); + original_dict.Set("binary", original_binary); + + ListValue* original_list = new ListValue(); + FundamentalValue* original_list_element_0 = Value::CreateIntegerValue(0); + original_list->Append(original_list_element_0); + FundamentalValue* original_list_element_1 = Value::CreateIntegerValue(1); + original_list->Append(original_list_element_1); + original_dict.Set("list", original_list); + + Value* original_dict_value = &original_dict; + Value* original_bool_value = original_bool; + Value* original_int_value = original_int; + Value* original_double_value = original_double; + Value* original_string_value = original_string; + Value* original_string16_value = original_string16; + Value* original_binary_value = original_binary; + Value* original_list_value = original_list; + + scoped_ptr<Value> copy_dict_value(original_dict_value->DeepCopy()); + scoped_ptr<Value> copy_bool_value(original_bool_value->DeepCopy()); + scoped_ptr<Value> copy_int_value(original_int_value->DeepCopy()); + scoped_ptr<Value> copy_double_value(original_double_value->DeepCopy()); + scoped_ptr<Value> copy_string_value(original_string_value->DeepCopy()); + scoped_ptr<Value> copy_string16_value(original_string16_value->DeepCopy()); + scoped_ptr<Value> copy_binary_value(original_binary_value->DeepCopy()); + scoped_ptr<Value> copy_list_value(original_list_value->DeepCopy()); + + EXPECT_TRUE(original_dict_value->Equals(copy_dict_value.get())); + EXPECT_TRUE(original_bool_value->Equals(copy_bool_value.get())); + EXPECT_TRUE(original_int_value->Equals(copy_int_value.get())); + EXPECT_TRUE(original_double_value->Equals(copy_double_value.get())); + EXPECT_TRUE(original_string_value->Equals(copy_string_value.get())); + EXPECT_TRUE(original_string16_value->Equals(copy_string16_value.get())); + EXPECT_TRUE(original_binary_value->Equals(copy_binary_value.get())); + EXPECT_TRUE(original_list_value->Equals(copy_list_value.get())); +} + TEST_F(ValuesTest, RemoveEmptyChildren) { scoped_ptr<DictionaryValue> root(new DictionaryValue); // Remove empty lists and dictionaries. diff --git a/base/values_util.cc b/base/values_util.cc deleted file mode 100644 index fbc616b..0000000 --- a/base/values_util.cc +++ /dev/null @@ -1,18 +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/values_util.h" - -RefCountedList::RefCountedList(ListValue* list) { - list_ = list; -} - -RefCountedList::~RefCountedList() { - if (list_) - delete list_; -} - -ListValue* RefCountedList::Get() { - return list_; -} diff --git a/base/values_util.h b/base/values_util.h deleted file mode 100644 index 1626bb5..0000000 --- a/base/values_util.h +++ /dev/null @@ -1,26 +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. - -#ifndef BASE_VALUES_UTIL_H_ -#define BASE_VALUES_UTIL_H_ -#pragma once - -#include "base/values.h" -#include "base/ref_counted.h" - -class RefCountedList : public base::RefCountedThreadSafe<RefCountedList> { - public: - // Takes ownership of |list|. - explicit RefCountedList(ListValue* list); - virtual ~RefCountedList(); - - virtual ListValue* Get(); - - private: - ListValue* list_; - - DISALLOW_COPY_AND_ASSIGN(RefCountedList); -}; - -#endif // BASE_VALUES_UTIL_H_ diff --git a/base/vlog.cc b/base/vlog.cc index 8903f39..41bf2a5 100644 --- a/base/vlog.cc +++ b/base/vlog.cc @@ -13,6 +13,18 @@ namespace logging { const int VlogInfo::kDefaultVlogLevel = 0; +struct VlogInfo::VmodulePattern { + enum MatchTarget { MATCH_MODULE, MATCH_FILE }; + + explicit VmodulePattern(const std::string& pattern); + + VmodulePattern(); + + std::string pattern; + int vlog_level; + MatchTarget match_target; +}; + VlogInfo::VmodulePattern::VmodulePattern(const std::string& pattern) : pattern(pattern), vlog_level(VlogInfo::kDefaultVlogLevel), @@ -64,15 +76,6 @@ VlogInfo::VlogInfo(const std::string& v_switch, VlogInfo::~VlogInfo() {} -void VlogInfo::SetMaxVlogLevel(int level) { - // Log severity is the negative verbosity. - *min_log_level_ = -level; -} - -int VlogInfo::GetMaxVlogLevel() const { - return -*min_log_level_; -} - namespace { // Given a path, returns the basename with the extension chopped off @@ -109,6 +112,15 @@ int VlogInfo::GetVlogLevel(const base::StringPiece& file) const { return GetMaxVlogLevel(); } +void VlogInfo::SetMaxVlogLevel(int level) { + // Log severity is the negative verbosity. + *min_log_level_ = -level; +} + +int VlogInfo::GetMaxVlogLevel() const { + return -*min_log_level_; +} + bool MatchVlogPattern(const base::StringPiece& string, const base::StringPiece& vlog_pattern) { base::StringPiece p(vlog_pattern); diff --git a/base/vlog.h b/base/vlog.h index 529afd5..54e777f 100644 --- a/base/vlog.h +++ b/base/vlog.h @@ -18,6 +18,8 @@ namespace logging { // A helper class containing all the settings for vlogging. class VlogInfo { public: + static const int kDefaultVlogLevel; + // |v_switch| gives the default maximal active V-logging level; 0 is // the default. Normally positive values are used for V-logging // levels. @@ -45,26 +47,13 @@ class VlogInfo { // __FILE__). int GetVlogLevel(const base::StringPiece& file) const; - static const int kDefaultVlogLevel; - private: void SetMaxVlogLevel(int level); int GetMaxVlogLevel() const; // VmodulePattern holds all the information for each pattern parsed // from |vmodule_switch|. - struct VmodulePattern { - enum MatchTarget { MATCH_MODULE, MATCH_FILE }; - - explicit VmodulePattern(const std::string& pattern); - - VmodulePattern(); - - std::string pattern; - int vlog_level; - MatchTarget match_target; - }; - + struct VmodulePattern; std::vector<VmodulePattern> vmodule_levels_; int* min_log_level_; diff --git a/base/weak_ptr.cc b/base/weak_ptr.cc index 6473b4a..86c89c1 100644 --- a/base/weak_ptr.cc +++ b/base/weak_ptr.cc @@ -61,10 +61,10 @@ void WeakReferenceOwner::Invalidate() { WeakPtrBase::WeakPtrBase() { } -WeakPtrBase::WeakPtrBase(const WeakReference& ref) : ref_(ref) { +WeakPtrBase::~WeakPtrBase() { } -WeakPtrBase::~WeakPtrBase() { +WeakPtrBase::WeakPtrBase(const WeakReference& ref) : ref_(ref) { } } // namespace internal diff --git a/base/win/registry.cc b/base/win/registry.cc index dbb8d7a..d105a4c 100644 --- a/base/win/registry.cc +++ b/base/win/registry.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. @@ -14,136 +14,7 @@ namespace base { namespace win { -RegistryValueIterator::RegistryValueIterator(HKEY root_key, - const wchar_t* folder_key) { - base::ThreadRestrictions::AssertIOAllowed(); - - LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); - if (result != ERROR_SUCCESS) { - key_ = NULL; - } else { - DWORD count = 0; - result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, - NULL, NULL, NULL, NULL); - - if (result != ERROR_SUCCESS) { - ::RegCloseKey(key_); - key_ = NULL; - } else { - index_ = count - 1; - } - } - - Read(); -} - -RegistryValueIterator::~RegistryValueIterator() { - base::ThreadRestrictions::AssertIOAllowed(); - if (key_) - ::RegCloseKey(key_); -} - -bool RegistryValueIterator::Valid() const { - return key_ != NULL && index_ >= 0; -} - -void RegistryValueIterator::operator++() { - --index_; - Read(); -} - -bool RegistryValueIterator::Read() { - base::ThreadRestrictions::AssertIOAllowed(); - if (Valid()) { - DWORD ncount = arraysize(name_); - value_size_ = sizeof(value_); - LRESULT r = ::RegEnumValue(key_, index_, name_, &ncount, NULL, &type_, - reinterpret_cast<BYTE*>(value_), &value_size_); - if (ERROR_SUCCESS == r) - return true; - } - - name_[0] = '\0'; - value_[0] = '\0'; - value_size_ = 0; - return false; -} - -DWORD RegistryValueIterator::ValueCount() const { - base::ThreadRestrictions::AssertIOAllowed(); - DWORD count = 0; - HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, - &count, NULL, NULL, NULL, NULL); - - if (result != ERROR_SUCCESS) - return 0; - - return count; -} - -RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, - const wchar_t* folder_key) { - base::ThreadRestrictions::AssertIOAllowed(); - LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); - if (result != ERROR_SUCCESS) { - key_ = NULL; - } else { - DWORD count = 0; - HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, - NULL, NULL, NULL, NULL, NULL); - - if (result != ERROR_SUCCESS) { - ::RegCloseKey(key_); - key_ = NULL; - } else { - index_ = count - 1; - } - } - - Read(); -} - -RegistryKeyIterator::~RegistryKeyIterator() { - base::ThreadRestrictions::AssertIOAllowed(); - if (key_) - ::RegCloseKey(key_); -} - -bool RegistryKeyIterator::Valid() const { - return key_ != NULL && index_ >= 0; -} - -void RegistryKeyIterator::operator++() { - --index_; - Read(); -} - -bool RegistryKeyIterator::Read() { - base::ThreadRestrictions::AssertIOAllowed(); - if (Valid()) { - DWORD ncount = arraysize(name_); - FILETIME written; - LRESULT r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL, - NULL, &written); - if (ERROR_SUCCESS == r) - return true; - } - - name_[0] = '\0'; - return false; -} - -DWORD RegistryKeyIterator::SubkeyCount() const { - base::ThreadRestrictions::AssertIOAllowed(); - DWORD count = 0; - HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, - NULL, NULL, NULL, NULL, NULL); - - if (result != ERROR_SUCCESS) - return 0; - - return count; -} +// RegKey ---------------------------------------------------------------------- RegKey::RegKey() : key_(NULL), @@ -168,57 +39,33 @@ RegKey::~RegKey() { Close(); } -void RegKey::Close() { - base::ThreadRestrictions::AssertIOAllowed(); - StopWatching(); - if (key_) { - ::RegCloseKey(key_); - key_ = NULL; - } -} - -bool RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { +LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { DWORD disposition_value; return CreateWithDisposition(rootkey, subkey, &disposition_value, access); } -bool RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, +LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, DWORD* disposition, REGSAM access) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(rootkey && subkey && access && disposition); Close(); - LONG result = RegCreateKeyEx(rootkey, - subkey, - 0, - NULL, - REG_OPTION_NON_VOLATILE, - access, - NULL, - &key_, + LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL, + REG_OPTION_NON_VOLATILE, access, NULL, &key_, disposition); - if (result != ERROR_SUCCESS) { - key_ = NULL; - return false; - } - - return true; + return result; } -bool RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { +LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(rootkey && subkey && access); Close(); LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_); - if (result != ERROR_SUCCESS) { - key_ = NULL; - return false; - } - return true; + return result; } -bool RegKey::CreateKey(const wchar_t* name, REGSAM access) { +LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(name && access); @@ -228,10 +75,10 @@ bool RegKey::CreateKey(const wchar_t* name, REGSAM access) { Close(); key_ = subkey; - return (result == ERROR_SUCCESS); + return result; } -bool RegKey::OpenKey(const wchar_t* name, REGSAM access) { +LONG RegKey::OpenKey(const wchar_t* name, REGSAM access) { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(name && access); @@ -241,56 +88,76 @@ bool RegKey::OpenKey(const wchar_t* name, REGSAM access) { Close(); key_ = subkey; - return (result == ERROR_SUCCESS); + return result; +} + +void RegKey::Close() { + base::ThreadRestrictions::AssertIOAllowed(); + StopWatching(); + if (key_) { + ::RegCloseKey(key_); + key_ = NULL; + } } DWORD RegKey::ValueCount() const { base::ThreadRestrictions::AssertIOAllowed(); DWORD count = 0; - HRESULT result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, - NULL, &count, NULL, NULL, NULL, NULL); + LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, + NULL, NULL, NULL, NULL); return (result != ERROR_SUCCESS) ? 0 : count; } -bool RegKey::ReadName(int index, std::wstring* name) const { +LONG RegKey::ReadName(int index, std::wstring* name) const { base::ThreadRestrictions::AssertIOAllowed(); wchar_t buf[256]; DWORD bufsize = arraysize(buf); - LRESULT r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, - NULL, NULL); - if (r != ERROR_SUCCESS) - return false; - if (name) + LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL); + if (r == ERROR_SUCCESS) *name = buf; - return true; + + return r; +} + +LONG RegKey::DeleteKey(const wchar_t* name) { + base::ThreadRestrictions::AssertIOAllowed(); + DCHECK(key_); + DCHECK(name); + LONG result = SHDeleteKey(key_, name); + return result; +} + +LONG RegKey::DeleteValue(const wchar_t* value_name) { + base::ThreadRestrictions::AssertIOAllowed(); + DCHECK(key_); + DCHECK(value_name); + LONG result = RegDeleteValue(key_, value_name); + return result; } -bool RegKey::ValueExists(const wchar_t* name) { +bool RegKey::ValueExists(const wchar_t* name) const { base::ThreadRestrictions::AssertIOAllowed(); - if (!key_) - return false; - HRESULT result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL); - return (result == ERROR_SUCCESS); + LONG result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL); + return result == ERROR_SUCCESS; } -bool RegKey::ReadValue(const wchar_t* name, void* data, - DWORD* dsize, DWORD* dtype) const { +LONG RegKey::ReadValue(const wchar_t* name, void* data, DWORD* dsize, + DWORD* dtype) const { base::ThreadRestrictions::AssertIOAllowed(); - if (!key_) - return false; - HRESULT result = RegQueryValueEx(key_, name, 0, dtype, - reinterpret_cast<LPBYTE>(data), dsize); - return (result == ERROR_SUCCESS); + LONG result = RegQueryValueEx(key_, name, 0, dtype, + reinterpret_cast<LPBYTE>(data), dsize); + return result; } -bool RegKey::ReadValue(const wchar_t* name, std::wstring* value) const { +LONG RegKey::ReadValue(const wchar_t* name, std::wstring* value) const { base::ThreadRestrictions::AssertIOAllowed(); DCHECK(value); const size_t kMaxStringLength = 1024; // This is after expansion. // Use the one of the other forms of ReadValue if 1024 is too small for you. wchar_t raw_value[kMaxStringLength]; DWORD type = REG_SZ, size = sizeof(raw_value); - if (ReadValue(name, raw_value, &size, &type)) { + LONG result = ReadValue(name, raw_value, &size, &type); + if (result == ERROR_SUCCESS) { if (type == REG_SZ) { *value = raw_value; } else if (type == REG_EXPAND_SZ) { @@ -299,80 +166,76 @@ bool RegKey::ReadValue(const wchar_t* name, std::wstring* value) const { // Success: returns the number of wchar_t's copied // Fail: buffer too small, returns the size required // Fail: other, returns 0 - if (size == 0 || size > kMaxStringLength) - return false; - *value = expanded; + if (size == 0 || size > kMaxStringLength) { + result = ERROR_MORE_DATA; + } else { + *value = expanded; + } } else { // Not a string. Oops. - return false; + result = ERROR_CANTREAD; } - return true; } - return false; + return result; } -bool RegKey::ReadValueDW(const wchar_t* name, DWORD* value) const { +LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* value) const { DCHECK(value); DWORD type = REG_DWORD; DWORD size = sizeof(DWORD); - DWORD result = 0; - if (ReadValue(name, &result, &size, &type) && - (type == REG_DWORD || type == REG_BINARY) && - size == sizeof(DWORD)) { - *value = result; - return true; + DWORD local_value = 0; + LONG result = ReadValue(name, &local_value, &size, &type); + if (result == ERROR_SUCCESS) { + if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD)) { + *value = local_value; + } else { + result = ERROR_CANTREAD; + } } - return false; + return result; } -bool RegKey::WriteValue(const wchar_t* name, const void * data, +LONG RegKey::ReadInt64(const wchar_t* name, int64* value) const { + DCHECK(value); + DWORD type = REG_QWORD; + int64 local_value = 0; + DWORD size = sizeof(local_value); + LONG result = ReadValue(name, &local_value, &size, &type); + if (result == ERROR_SUCCESS) { + if ((type == REG_QWORD || type == REG_BINARY) && + size == sizeof(local_value)) { + *value = local_value; + } else { + result = ERROR_CANTREAD; + } + } + + return result; +} + +LONG RegKey::WriteValue(const wchar_t* name, const void * data, DWORD dsize, DWORD dtype) { base::ThreadRestrictions::AssertIOAllowed(); - DCHECK(data); - - if (!key_) - return false; + DCHECK(data || !dsize); - HRESULT result = RegSetValueEx( - key_, - name, - 0, - dtype, - reinterpret_cast<LPBYTE>(const_cast<void*>(data)), - dsize); - return (result == ERROR_SUCCESS); + LONG result = RegSetValueEx(key_, name, 0, dtype, + reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize); + return result; } -bool RegKey::WriteValue(const wchar_t * name, const wchar_t* value) { +LONG RegKey::WriteValue(const wchar_t * name, const wchar_t* value) { return WriteValue(name, value, static_cast<DWORD>(sizeof(*value) * (wcslen(value) + 1)), REG_SZ); } -bool RegKey::WriteValue(const wchar_t* name, DWORD value) { - return WriteValue(name, &value, - static_cast<DWORD>(sizeof(value)), REG_DWORD); -} - -bool RegKey::DeleteKey(const wchar_t* name) { - base::ThreadRestrictions::AssertIOAllowed(); - if (!key_) - return false; - LSTATUS ret = SHDeleteKey(key_, name); - if (ERROR_SUCCESS != ret) - SetLastError(ret); - return ERROR_SUCCESS == ret; -} - -bool RegKey::DeleteValue(const wchar_t* value_name) { - base::ThreadRestrictions::AssertIOAllowed(); - DCHECK(value_name); - HRESULT result = RegDeleteValue(key_, value_name); - return (result == ERROR_SUCCESS); +LONG RegKey::WriteValue(const wchar_t* name, DWORD value) { + return WriteValue(name, &value, static_cast<DWORD>(sizeof(value)), REG_DWORD); } -bool RegKey::StartWatching() { +LONG RegKey::StartWatching() { + DCHECK(key_); if (!watch_event_) watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -382,33 +245,165 @@ bool RegKey::StartWatching() { REG_NOTIFY_CHANGE_SECURITY; // Watch the registry key for a change of value. - HRESULT result = RegNotifyChangeKeyValue(key_, TRUE, filter, - watch_event_, TRUE); - if (SUCCEEDED(result)) { - return true; - } else { + LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE); + if (result != ERROR_SUCCESS) { CloseHandle(watch_event_); watch_event_ = 0; - return false; } + + return result; } -bool RegKey::StopWatching() { +bool RegKey::HasChanged() { if (watch_event_) { - CloseHandle(watch_event_); - watch_event_ = 0; - return true; + if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) { + StartWatching(); + return true; + } } return false; } -bool RegKey::HasChanged() { +LONG RegKey::StopWatching() { + LONG result = ERROR_INVALID_HANDLE; if (watch_event_) { - if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) { - StartWatching(); + CloseHandle(watch_event_); + watch_event_ = 0; + result = ERROR_SUCCESS; + } + return result; +} + +// RegistryValueIterator ------------------------------------------------------ + +RegistryValueIterator::RegistryValueIterator(HKEY root_key, + const wchar_t* folder_key) { + base::ThreadRestrictions::AssertIOAllowed(); + + LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); + if (result != ERROR_SUCCESS) { + key_ = NULL; + } else { + DWORD count = 0; + result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, + NULL, NULL, NULL, NULL); + + if (result != ERROR_SUCCESS) { + ::RegCloseKey(key_); + key_ = NULL; + } else { + index_ = count - 1; + } + } + + Read(); +} + +RegistryValueIterator::~RegistryValueIterator() { + base::ThreadRestrictions::AssertIOAllowed(); + if (key_) + ::RegCloseKey(key_); +} + +DWORD RegistryValueIterator::ValueCount() const { + base::ThreadRestrictions::AssertIOAllowed(); + DWORD count = 0; + LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, + &count, NULL, NULL, NULL, NULL); + if (result != ERROR_SUCCESS) + return 0; + + return count; +} + +bool RegistryValueIterator::Valid() const { + return key_ != NULL && index_ >= 0; +} + +void RegistryValueIterator::operator++() { + --index_; + Read(); +} + +bool RegistryValueIterator::Read() { + base::ThreadRestrictions::AssertIOAllowed(); + if (Valid()) { + DWORD ncount = arraysize(name_); + value_size_ = sizeof(value_); + LONG r = ::RegEnumValue(key_, index_, name_, &ncount, NULL, &type_, + reinterpret_cast<BYTE*>(value_), &value_size_); + if (ERROR_SUCCESS == r) return true; + } + + name_[0] = '\0'; + value_[0] = '\0'; + value_size_ = 0; + return false; +} + +// RegistryKeyIterator -------------------------------------------------------- + +RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, + const wchar_t* folder_key) { + base::ThreadRestrictions::AssertIOAllowed(); + LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); + if (result != ERROR_SUCCESS) { + key_ = NULL; + } else { + DWORD count = 0; + LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, + NULL, NULL, NULL, NULL, NULL); + + if (result != ERROR_SUCCESS) { + ::RegCloseKey(key_); + key_ = NULL; + } else { + index_ = count - 1; } } + + Read(); +} + +RegistryKeyIterator::~RegistryKeyIterator() { + base::ThreadRestrictions::AssertIOAllowed(); + if (key_) + ::RegCloseKey(key_); +} + +DWORD RegistryKeyIterator::SubkeyCount() const { + base::ThreadRestrictions::AssertIOAllowed(); + DWORD count = 0; + LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, + NULL, NULL, NULL, NULL, NULL); + if (result != ERROR_SUCCESS) + return 0; + + return count; +} + +bool RegistryKeyIterator::Valid() const { + return key_ != NULL && index_ >= 0; +} + +void RegistryKeyIterator::operator++() { + --index_; + Read(); +} + +bool RegistryKeyIterator::Read() { + base::ThreadRestrictions::AssertIOAllowed(); + if (Valid()) { + DWORD ncount = arraysize(name_); + FILETIME written; + LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL, + NULL, &written); + if (ERROR_SUCCESS == r) + return true; + } + + name_[0] = '\0'; return false; } diff --git a/base/win/registry.h b/base/win/registry.h index 96a2bb3..cd5421d 100644 --- a/base/win/registry.h +++ b/base/win/registry.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. @@ -17,57 +17,62 @@ namespace win { // Utility class to read, write and manipulate the Windows Registry. // Registry vocabulary primer: a "key" is like a folder, in which there // are "values", which are <name, data> pairs, with an associated data type. +// +// Note: +// ReadValue family of functions guarantee that the return arguments +// are not touched in case of failure. class RegKey { public: RegKey(); RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access); ~RegKey(); - bool Create(HKEY rootkey, const wchar_t* subkey, REGSAM access); + LONG Create(HKEY rootkey, const wchar_t* subkey, REGSAM access); - bool CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, + LONG CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, DWORD* disposition, REGSAM access); - bool Open(HKEY rootkey, const wchar_t* subkey, REGSAM access); + LONG Open(HKEY rootkey, const wchar_t* subkey, REGSAM access); // Creates a subkey or open it if it already exists. - bool CreateKey(const wchar_t* name, REGSAM access); + LONG CreateKey(const wchar_t* name, REGSAM access); // Opens a subkey - bool OpenKey(const wchar_t* name, REGSAM access); + LONG OpenKey(const wchar_t* name, REGSAM access); void Close(); DWORD ValueCount() const; // Determine the nth value's name. - bool ReadName(int index, std::wstring* name) const; + LONG ReadName(int index, std::wstring* name) const; // True while the key is valid. bool Valid() const { return key_ != NULL; } // Kill a key and everything that live below it; please be careful when using // it. - bool DeleteKey(const wchar_t* name); + LONG DeleteKey(const wchar_t* name); // Deletes a single value within the key. - bool DeleteValue(const wchar_t* name); + LONG DeleteValue(const wchar_t* name); - bool ValueExists(const wchar_t* name); + bool ValueExists(const wchar_t* name) const; - bool ReadValue(const wchar_t* name, void* data, DWORD* dsize, + LONG ReadValue(const wchar_t* name, void* data, DWORD* dsize, DWORD* dtype) const; - bool ReadValue(const wchar_t* name, std::wstring* value) const; - bool ReadValueDW(const wchar_t* name, DWORD* value) const; + LONG ReadValue(const wchar_t* name, std::wstring* value) const; + LONG ReadValueDW(const wchar_t* name, DWORD* value) const; + LONG ReadInt64(const wchar_t* name, int64* value) const; - bool WriteValue(const wchar_t* name, const void* data, DWORD dsize, + LONG WriteValue(const wchar_t* name, const void* data, DWORD dsize, DWORD dtype); - bool WriteValue(const wchar_t* name, const wchar_t* value); - bool WriteValue(const wchar_t* name, DWORD value); + LONG WriteValue(const wchar_t* name, const wchar_t* value); + LONG WriteValue(const wchar_t* name, DWORD value); // Starts watching the key to see if any of its values have changed. // The key must have been opened with the KEY_NOTIFY access privilege. - bool StartWatching(); + LONG StartWatching(); // If StartWatching hasn't been called, always returns false. // Otherwise, returns true if anything under the key has changed. @@ -76,7 +81,7 @@ class RegKey { // Will automatically be called by destructor if not manually called // beforehand. Returns true if it was watching, false otherwise. - bool StopWatching(); + LONG StopWatching(); inline bool IsWatching() const { return watch_event_ != 0; } HANDLE watch_event() const { return watch_event_; } diff --git a/base/win/registry_unittest.cc b/base/win/registry_unittest.cc index 524612a..a7961e9 100644 --- a/base/win/registry_unittest.cc +++ b/base/win/registry_unittest.cc @@ -21,14 +21,14 @@ class RegistryTest : public testing::Test { // Create a temporary key. RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); key.DeleteKey(kRootKey); - ASSERT_FALSE(key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); - ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ)); + ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ)); } virtual void TearDown() { // Clean up the temporary key. RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE); - ASSERT_TRUE(key.DeleteKey(kRootKey)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); } private: @@ -40,22 +40,59 @@ TEST_F(RegistryTest, ValueTest) { std::wstring foo_key(kRootKey); foo_key += L"\\Foo"; - ASSERT_TRUE(key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), + KEY_READ)); { - ASSERT_TRUE(key.Open(HKEY_CURRENT_USER, foo_key.c_str(), - KEY_READ | KEY_SET_VALUE)); - - const wchar_t* kName = L"Bar"; - const wchar_t* kValue = L"bar"; - EXPECT_TRUE(key.WriteValue(kName, kValue)); - EXPECT_TRUE(key.ValueExists(kName)); - std::wstring out_value; - EXPECT_TRUE(key.ReadValue(kName, &out_value)); - EXPECT_NE(out_value, L""); - EXPECT_STREQ(out_value.c_str(), kValue); - EXPECT_EQ(1U, key.ValueCount()); - EXPECT_TRUE(key.DeleteValue(kName)); + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(), + KEY_READ | KEY_SET_VALUE)); + ASSERT_TRUE(key.Valid()); + + const wchar_t* kStringValueName = L"StringValue"; + const wchar_t* kDWORDValueName = L"DWORDValue"; + const wchar_t* kInt64ValueName = L"Int64Value"; + const wchar_t* kStringData = L"string data"; + const DWORD kDWORDData = 0xdeadbabe; + const int64 kInt64Data = 0xdeadbabedeadbabeLL; + + // Test value creation + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kStringValueName, kStringData)); + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kDWORDValueName, kDWORDData)); + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kInt64ValueName, &kInt64Data, + sizeof(kInt64Data), REG_QWORD)); + EXPECT_EQ(3U, key.ValueCount()); + EXPECT_TRUE(key.ValueExists(kStringValueName)); + EXPECT_TRUE(key.ValueExists(kDWORDValueName)); + EXPECT_TRUE(key.ValueExists(kInt64ValueName)); + + // Test Read + std::wstring string_value; + DWORD dword_value = 0; + int64 int64_value = 0; + ASSERT_EQ(ERROR_SUCCESS, key.ReadValue(kStringValueName, &string_value)); + ASSERT_EQ(ERROR_SUCCESS, key.ReadValueDW(kDWORDValueName, &dword_value)); + ASSERT_EQ(ERROR_SUCCESS, key.ReadInt64(kInt64ValueName, &int64_value)); + EXPECT_STREQ(kStringData, string_value.c_str()); + EXPECT_EQ(kDWORDData, dword_value); + EXPECT_EQ(kInt64Data, int64_value); + + // Make sure out args are not touched if ReadValue fails + const wchar_t* kNonExistent = L"NonExistent"; + ASSERT_NE(ERROR_SUCCESS, key.ReadValue(kNonExistent, &string_value)); + ASSERT_NE(ERROR_SUCCESS, key.ReadValueDW(kNonExistent, &dword_value)); + ASSERT_NE(ERROR_SUCCESS, key.ReadInt64(kNonExistent, &int64_value)); + EXPECT_STREQ(kStringData, string_value.c_str()); + EXPECT_EQ(kDWORDData, dword_value); + EXPECT_EQ(kInt64Data, int64_value); + + // Test delete + ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kStringValueName)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kDWORDValueName)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kInt64ValueName)); + EXPECT_EQ(0U, key.ValueCount()); + EXPECT_FALSE(key.ValueExists(kStringValueName)); + EXPECT_FALSE(key.ValueExists(kDWORDValueName)); + EXPECT_FALSE(key.ValueExists(kInt64ValueName)); } } diff --git a/base/win/win_util.cc b/base/win/win_util.cc index 87905ea..d3d74a2 100644 --- a/base/win/win_util.cc +++ b/base/win/win_util.cc @@ -93,7 +93,7 @@ bool UserAccountControlIsEnabled() { L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System", KEY_READ); DWORD uac_enabled; - if (!key.ReadValueDW(L"EnableLUA", &uac_enabled)) + if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS) return true; // Users can set the EnableLUA value to something arbitrary, like 2, which // Vista will treat as UAC enabled, so we make sure it is not set to 0. @@ -128,12 +128,13 @@ static const char16 kAutoRunKeyPath[] = bool AddCommandToAutoRun(HKEY root_key, const string16& name, const string16& command) { base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE); - return autorun_key.WriteValue(name.c_str(), command.c_str()); + return (autorun_key.WriteValue(name.c_str(), command.c_str()) == + ERROR_SUCCESS); } bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) { base::win::RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE); - return autorun_key.DeleteValue(name.c_str()); + return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS); } } // namespace win |