diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /ipc/chromium/src/base | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ipc/chromium/src/base')
113 files changed, 20897 insertions, 0 deletions
diff --git a/ipc/chromium/src/base/at_exit.cc b/ipc/chromium/src/base/at_exit.cc new file mode 100644 index 0000000000..057d8b2678 --- /dev/null +++ b/ipc/chromium/src/base/at_exit.cc @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "base/logging.h" + +namespace base { + +// Keep a stack of registered AtExitManagers. We always operate on the most +// recent, and we should never have more than one outside of testing, when we +// use the shadow version of the constructor. We don't protect this for +// thread-safe access, since it will only be modified in testing. +static AtExitManager* g_top_manager = NULL; + +AtExitManager::AtExitManager() : lock_("AtExitManager"), next_manager_(NULL) { + DCHECK(!g_top_manager); + g_top_manager = this; +} + +AtExitManager::AtExitManager(bool shadow) + : lock_("AtExitManager"), next_manager_(g_top_manager) { + DCHECK(shadow || !g_top_manager); + g_top_manager = this; +} + +AtExitManager::~AtExitManager() { + if (!g_top_manager) { + NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager"; + return; + } + DCHECK(g_top_manager == this); + + ProcessCallbacksNow(); + g_top_manager = next_manager_; +} + +// static +void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { + if (!g_top_manager) { + NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; + return; + } + + DCHECK(func); + + mozilla::MutexAutoLock lock(g_top_manager->lock_); + g_top_manager->stack_.push(CallbackAndParam(func, param)); +} + +// static +void AtExitManager::ProcessCallbacksNow() { + if (!g_top_manager) { + NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager"; + return; + } + + mozilla::MutexAutoLock lock(g_top_manager->lock_); + + while (!g_top_manager->stack_.empty()) { + CallbackAndParam callback_and_param = g_top_manager->stack_.top(); + g_top_manager->stack_.pop(); + + callback_and_param.func_(callback_and_param.param_); + } +} + +// static +bool AtExitManager::AlreadyRegistered() { return !!g_top_manager; } + +} // namespace base diff --git a/ipc/chromium/src/base/at_exit.h b/ipc/chromium/src/base/at_exit.h new file mode 100644 index 0000000000..268d9e0fbe --- /dev/null +++ b/ipc/chromium/src/base/at_exit.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_AT_EXIT_H_ +#define BASE_AT_EXIT_H_ + +#include <stack> + +#include "base/basictypes.h" + +#include "mozilla/Mutex.h" + +namespace base { + +// This class provides a facility similar to the CRT atexit(), except that +// we control when the callbacks are executed. Under Windows for a DLL they +// happen at a really bad time and under the loader lock. This facility is +// mostly used by base::Singleton. +// +// The usage is simple. Early in the main() or WinMain() scope create an +// AtExitManager object on the stack: +// int main(...) { +// base::AtExitManager exit_manager; +// +// } +// When the exit_manager object goes out of scope, all the registered +// callbacks and singleton destructors will be called. + +class AtExitManager { + protected: + // This constructor will allow this instance of AtExitManager to be created + // even if one already exists. This should only be used for testing! + // AtExitManagers are kept on a global stack, and it will be removed during + // destruction. This allows you to shadow another AtExitManager. + explicit AtExitManager(bool shadow); + + public: + typedef void (*AtExitCallbackType)(void*); + + AtExitManager(); + + // The dtor calls all the registered callbacks. Do not try to register more + // callbacks after this point. + ~AtExitManager(); + + // Registers the specified function to be called at exit. The prototype of + // the callback function is void func(). + static void RegisterCallback(AtExitCallbackType func, void* param); + + // Calls the functions registered with RegisterCallback in LIFO order. It + // is possible to register new callbacks after calling this function. + static void ProcessCallbacksNow(); + + static bool AlreadyRegistered(); + + private: + struct CallbackAndParam { + CallbackAndParam(AtExitCallbackType func, void* param) + : func_(func), param_(param) {} + AtExitCallbackType func_; + void* param_; + }; + + mozilla::Mutex lock_; + std::stack<CallbackAndParam> stack_ MOZ_GUARDED_BY(lock_); + AtExitManager* next_manager_; // Stack of managers to allow shadowing. + + DISALLOW_COPY_AND_ASSIGN(AtExitManager); +}; + +} // namespace base + +#endif // BASE_AT_EXIT_H_ diff --git a/ipc/chromium/src/base/basictypes.h b/ipc/chromium/src/base/basictypes.h new file mode 100644 index 0000000000..78dcd8964e --- /dev/null +++ b/ipc/chromium/src/base/basictypes.h @@ -0,0 +1,262 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASICTYPES_H_ +#define BASE_BASICTYPES_H_ + +#include <limits.h> // So we can set the bounds of our types +#include <stddef.h> // For size_t +#include <string.h> // for memcpy + +#include "base/port.h" // Types that only need exist on certain systems + +#include "mozilla/Assertions.h" +#include "mozilla/IntegerPrintfMacros.h" + +// A type to represent a Unicode code-point value. As of Unicode 4.0, +// such values require up to 21 bits. +// (For type-checking on pointers, make this explicitly signed, +// and it should always be the signed version of whatever int32_t is.) +typedef signed int char32; + +const uint8_t kuint8max = ((uint8_t)0xFF); +const uint16_t kuint16max = ((uint16_t)0xFFFF); +const uint32_t kuint32max = ((uint32_t)0xFFFFFFFF); +const uint64_t kuint64max = ((uint64_t)GG_LONGLONG(0xFFFFFFFFFFFFFFFF)); +const int8_t kint8min = ((int8_t)0x80); +const int8_t kint8max = ((int8_t)0x7F); +const int16_t kint16min = ((int16_t)0x8000); +const int16_t kint16max = ((int16_t)0x7FFF); +const int32_t kint32min = ((int32_t)0x80000000); +const int32_t kint32max = ((int32_t)0x7FFFFFFF); +const int64_t kint64min = ((int64_t)GG_LONGLONG(0x8000000000000000)); +const int64_t kint64max = ((int64_t)GG_LONGLONG(0x7FFFFFFFFFFFFFFF)); + +// Platform- and hardware-dependent printf specifiers +#if defined(XP_UNIX) +# define PRId64L "I64d" +# define PRIu64L "I64u" +# define PRIx64L "I64x" +#else +# define PRId64L L"I64d" +# define PRIu64L L"I64u" +# define PRIx64L L"I64x" +#endif + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#undef DISALLOW_COPY_AND_ASSIGN +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// An older, deprecated, politically incorrect name for the above. +#undef DISALLOW_EVIL_CONSTRUCTORS +#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#undef DISALLOW_IMPLICIT_CONSTRUCTORS +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +// +// One caveat is that arraysize() doesn't accept any array of an +// anonymous type or a type defined inside a function. In these rare +// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is +// due to a limitation in C++'s template system. The limitation might +// eventually be removed, but it hasn't happened yet. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +template <typename T, size_t N> +char (&ArraySizeHelper(T (&array)[N]))[N]; + +// That gcc wants both of these prototypes seems mysterious. VC, for +// its part, can't decide which to use (another mystery). Matching of +// template overloads: the final frontier. +#ifndef _MSC_VER +template <typename T, size_t N> +char (&ArraySizeHelper(const T (&array)[N]))[N]; +#endif + +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + +// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize, +// but can be used on anonymous types or types defined inside +// functions. It's less safe than arraysize as it accepts some +// (although not all) pointers. Therefore, you should use arraysize +// whenever possible. +// +// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type +// size_t. +// +// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error +// +// "warning: division by zero in ..." +// +// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer. +// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays. +// +// The following comments are on the implementation details, and can +// be ignored by the users. +// +// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in +// the array) and sizeof(*(arr)) (the # of bytes in one array +// element). If the former is divisible by the latter, perhaps arr is +// indeed an array, in which case the division result is the # of +// elements in the array. Otherwise, arr cannot possibly be an array, +// and we generate a compiler error to prevent the code from +// compiling. +// +// Since the size of bool is implementation-defined, we need to cast +// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final +// result has type size_t. +// +// This macro is not perfect as it wrongfully accepts certain +// pointers, namely where the pointer size is divisible by the pointee +// size. Since all our code has to go through a 32-bit compiler, +// where a pointer is 4 bytes, this means all pointers to a type whose +// size is 3 or greater than 4 will be (righteously) rejected. + +#define ARRAYSIZE_UNSAFE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertable to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast<ToType>(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template <typename To, typename From> +inline To implicit_cast(From const& f) { + return f; +} + +// The COMPILE_ASSERT macro (below) creates an otherwise-unused typedef. This +// triggers compiler warnings with gcc 4.8 and higher, so mark the typedef +// as permissibly-unused to disable the warnings. +#if defined(__GNUC__) +# define COMPILE_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +# define COMPILE_ASSERT_UNUSED_ATTRIBUTE /* nothing */ +#endif + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +// Avoid multiple definitions for webrtc +#if !defined(COMPILE_ASSERT) +template <bool> +struct CompileAssert {}; + +# define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> \ + msg[bool(expr) ? 1 : -1] COMPILE_ASSERT_UNUSED_ATTRIBUTE +#endif + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert<bool(expr)> +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// MetatagId refers to metatag-id that we assign to +// each metatag <name, value> pair.. +typedef uint32_t MetatagId; + +// Argument type used in interfaces that can optionally take ownership +// of a passed in argument. If TAKE_OWNERSHIP is passed, the called +// object takes ownership of the argument. Otherwise it does not. +enum Ownership { DO_NOT_TAKE_OWNERSHIP, TAKE_OWNERSHIP }; + +// The following enum should be used only as a constructor argument to indicate +// that the variable has static storage class, and that the constructor should +// do nothing to its state. It indicates to the reader that it is legal to +// declare a static instance of the class, provided the constructor is given +// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a +// static variable that has a constructor or a destructor because invocation +// order is undefined. However, IF the type can be initialized by filling with +// zeroes (which the loader does for static variables), AND the destructor also +// does nothing to the storage, AND there are no virtual methods, then a +// constructor declared as +// explicit MyClass(base::LinkerInitialized x) {} +// and invoked as +// static MyClass my_variable_name(base::LINKER_INITIALIZED); +namespace base { +enum LinkerInitialized { LINKER_INITIALIZED }; +} // namespace base + +#endif // BASE_BASICTYPES_H_ diff --git a/ipc/chromium/src/base/chrome_application_mac.h b/ipc/chromium/src/base/chrome_application_mac.h new file mode 100644 index 0000000000..8c5bb197f7 --- /dev/null +++ b/ipc/chromium/src/base/chrome_application_mac.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CHROME_APPLICATION_MAC_H_ +#define BASE_CHROME_APPLICATION_MAC_H_ + +#import <AppKit/AppKit.h> + +#include "base/basictypes.h" +#include "base/scoped_nsobject.h" + +// Event hooks must implement this protocol. +@protocol CrApplicationEventHookProtocol +- (void)hookForEvent:(NSEvent*)theEvent; +@end + +@interface CrApplication : NSApplication { + @private + BOOL handlingSendEvent_; + // Array of objects implementing the CrApplicationEventHookProtocol + scoped_nsobject<NSMutableArray> eventHooks_; +} +@property(readonly, getter=isHandlingSendEvent, nonatomic) + BOOL handlingSendEvent; + +// Add or remove an event hook to be called for every sendEvent: +// that the application receives. These handlers are called before +// the normal [NSApplication sendEvent:] call is made. + +// This is not a good alternative to a nested event loop. It should +// be used only when normal event logic and notification breaks down +// (e.g. when clicking outside a canBecomeKey:NO window to "switch +// context" out of it). +- (void)addEventHook:(id<CrApplicationEventHookProtocol>)hook; +- (void)removeEventHook:(id<CrApplicationEventHookProtocol>)hook; + ++ (NSApplication*)sharedApplication; +@end + +namespace chrome_application_mac { + +// Controls the state of |handlingSendEvent_| in the event loop so that it is +// reset properly. +class ScopedSendingEvent { + public: + ScopedSendingEvent(); + ~ScopedSendingEvent(); + + private: + CrApplication* app_; + BOOL handling_; + DISALLOW_COPY_AND_ASSIGN(ScopedSendingEvent); +}; + +} // namespace chrome_application_mac + +#endif // BASE_CHROME_APPLICATION_MAC_H_ diff --git a/ipc/chromium/src/base/chrome_application_mac.mm b/ipc/chromium/src/base/chrome_application_mac.mm new file mode 100644 index 0000000000..405a02a0e7 --- /dev/null +++ b/ipc/chromium/src/base/chrome_application_mac.mm @@ -0,0 +1,68 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "chrome_application_mac.h" + +#include "base/logging.h" + +@interface CrApplication () +@property(readwrite, getter=isHandlingSendEvent, nonatomic) + BOOL handlingSendEvent; +@end + +@implementation CrApplication +@synthesize handlingSendEvent = handlingSendEvent_; + +// Initialize NSApplication using the custom subclass. Check whether NSApp +// was already initialized using another class, because that would break +// some things. ++ (NSApplication*)sharedApplication { + NSApplication* app = [super sharedApplication]; + if (![NSApp isKindOfClass:self]) { + CHROMIUM_LOG(ERROR) << "NSApp should be of type " + << [[self className] UTF8String] << ", not " + << [[NSApp className] UTF8String]; + DCHECK(false) << "NSApp is of wrong type"; + } + return app; +} + +- (id)init { + if ((self = [super init])) { + eventHooks_.reset([[NSMutableArray alloc] init]); + } + return self; +} + +- (void)sendEvent:(NSEvent*)event { + chrome_application_mac::ScopedSendingEvent sendingEventScoper; + for (id<CrApplicationEventHookProtocol> handler in eventHooks_.get()) { + [handler hookForEvent:event]; + } + [super sendEvent:event]; +} + +- (void)addEventHook:(id<CrApplicationEventHookProtocol>)handler { + [eventHooks_ addObject:handler]; +} + +- (void)removeEventHook:(id<CrApplicationEventHookProtocol>)handler { + [eventHooks_ removeObject:handler]; +} + +@end + +namespace chrome_application_mac { + +ScopedSendingEvent::ScopedSendingEvent() + : app_(static_cast<CrApplication*>([CrApplication sharedApplication])), + handling_([app_ isHandlingSendEvent]) { + [app_ setHandlingSendEvent:YES]; +} + +ScopedSendingEvent::~ScopedSendingEvent() { + [app_ setHandlingSendEvent:handling_]; +} + +} // namespace chrome_application_mac diff --git a/ipc/chromium/src/base/command_line.cc b/ipc/chromium/src/base/command_line.cc new file mode 100644 index 0000000000..b3daa6f104 --- /dev/null +++ b/ipc/chromium/src/base/command_line.cc @@ -0,0 +1,389 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/command_line.h" + +#if defined(XP_WIN) +# include <windows.h> +# include <shellapi.h> +# include "mozilla/DynamicallyLinkedFunctionPtr.h" +#endif + +#include <algorithm> + +#include "base/logging.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" + +CommandLine* CommandLine::current_process_commandline_ = NULL; + +// Since we use a lazy match, make sure that longer versions (like L"--") +// are listed before shorter versions (like L"-") of similar prefixes. +#if defined(XP_WIN) +const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; +const wchar_t kSwitchTerminator[] = L"--"; +const wchar_t kSwitchValueSeparator[] = L"="; +#else +// Unixes don't use slash as a switch. +const char* const kSwitchPrefixes[] = {"--", "-"}; +const char kSwitchTerminator[] = "--"; +const char kSwitchValueSeparator[] = "="; +#endif + +#if defined(XP_WIN) +// Lowercase a string. This is used to lowercase switch names. +// Is this what we really want? It seems crazy to me. I've left it in +// for backwards compatibility on Windows. +static void Lowercase(std::wstring* parameter) { + transform(parameter->begin(), parameter->end(), parameter->begin(), tolower); +} +#endif + +#if defined(XP_WIN) +void CommandLine::ParseFromString(const std::wstring& command_line) { + TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); + + if (command_line_string_.empty()) return; + + int num_args = 0; + wchar_t** args = NULL; + // When calling CommandLineToArgvW, use the API set if available. + // Doing so will bypass loading shell32.dll on Win8+. + mozilla::DynamicallyLinkedFunctionPtr<decltype(&::CommandLineToArgvW)> + pCommandLineToArgvW(L"api-ms-win-downlevel-shell32-l1-1-0.dll", + "CommandLineToArgvW"); + if (pCommandLineToArgvW) { + args = pCommandLineToArgvW(command_line_string_.c_str(), &num_args); + } else { + args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); + } + + // Populate program_ with the trimmed version of the first arg. + TrimWhitespace(args[0], TRIM_ALL, &program_); + + bool parse_switches = true; + for (int i = 1; i < num_args; ++i) { + std::wstring arg; + TrimWhitespace(args[i], TRIM_ALL, &arg); + + if (!parse_switches) { + loose_values_.push_back(arg); + continue; + } + + if (arg == kSwitchTerminator) { + parse_switches = false; + continue; + } + + std::string switch_string; + std::wstring switch_value; + if (IsSwitch(arg, &switch_string, &switch_value)) { + switches_[switch_string] = switch_value; + } else { + loose_values_.push_back(arg); + } + } + + if (args) LocalFree(args); +} +CommandLine::CommandLine(const std::wstring& program) { + if (!program.empty()) { + program_ = program; + command_line_string_ = L'"' + program + L'"'; + } +} +#else +CommandLine::CommandLine(int argc, const char* const* argv) { + for (int i = 0; i < argc; ++i) argv_.push_back(argv[i]); + InitFromArgv(); +} +CommandLine::CommandLine(const std::vector<std::string>& argv) { + argv_ = argv; + InitFromArgv(); +} + +void CommandLine::InitFromArgv() { + bool parse_switches = true; + for (size_t i = 1; i < argv_.size(); ++i) { + const std::string& arg = argv_[i]; + + if (!parse_switches) { + loose_values_.push_back(arg); + continue; + } + + if (arg == kSwitchTerminator) { + parse_switches = false; + continue; + } + + std::string switch_string; + std::string switch_value; + if (IsSwitch(arg, &switch_string, &switch_value)) { + switches_[switch_string] = switch_value; + } else { + loose_values_.push_back(arg); + } + } +} + +CommandLine::CommandLine(const std::wstring& program) { + argv_.push_back(WideToASCII(program)); +} +#endif + +// static +bool CommandLine::IsSwitch(const StringType& parameter_string, + std::string* switch_string, + StringType* switch_value) { + switch_string->clear(); + switch_value->clear(); + + for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { + StringType prefix(kSwitchPrefixes[i]); + if (parameter_string.find(prefix) != 0) continue; + + const size_t switch_start = prefix.length(); + const size_t equals_position = + parameter_string.find(kSwitchValueSeparator, switch_start); + StringType switch_native; + if (equals_position == StringType::npos) { + switch_native = parameter_string.substr(switch_start); + } else { + switch_native = + parameter_string.substr(switch_start, equals_position - switch_start); + *switch_value = parameter_string.substr(equals_position + 1); + } +#if defined(XP_WIN) + Lowercase(&switch_native); + *switch_string = WideToASCII(switch_native); +#else + *switch_string = switch_native; +#endif + + return true; + } + + return false; +} + +// static +void CommandLine::Init(int argc, const char* const* argv) { + DCHECK(current_process_commandline_ == NULL); +#if defined(XP_WIN) + current_process_commandline_ = new CommandLine; + current_process_commandline_->ParseFromString(::GetCommandLineW()); +#else + current_process_commandline_ = new CommandLine(argc, argv); +#endif +} + +void CommandLine::Terminate() { + DCHECK(current_process_commandline_ != NULL); + delete current_process_commandline_; + current_process_commandline_ = NULL; +} + +bool CommandLine::HasSwitch(const std::wstring& switch_string) const { + std::wstring lowercased_switch(switch_string); +#if defined(XP_WIN) + Lowercase(&lowercased_switch); +#endif + return switches_.find(WideToASCII(lowercased_switch)) != switches_.end(); +} + +std::wstring CommandLine::GetSwitchValue( + const std::wstring& switch_string) const { + std::wstring lowercased_switch(switch_string); +#if defined(XP_WIN) + Lowercase(&lowercased_switch); +#endif + + std::map<std::string, StringType>::const_iterator result = + switches_.find(WideToASCII(lowercased_switch)); + + if (result == switches_.end()) { + return L""; + } else { +#if defined(XP_WIN) + return result->second; +#else + return ASCIIToWide(result->second); +#endif + } +} + +#if defined(XP_WIN) +std::vector<std::wstring> CommandLine::GetLooseValues() const { + return loose_values_; +} +std::wstring CommandLine::program() const { return program_; } +#else +std::vector<std::wstring> CommandLine::GetLooseValues() const { + std::vector<std::wstring> values; + for (size_t i = 0; i < loose_values_.size(); ++i) + values.push_back(ASCIIToWide(loose_values_[i])); + return values; +} +std::wstring CommandLine::program() const { + DCHECK(argv_.size() > 0); + return ASCIIToWide(argv_[0]); +} +#endif + +// static +std::wstring CommandLine::PrefixedSwitchString( + const std::wstring& switch_string) { + return StringPrintf(L"%ls%ls", kSwitchPrefixes[0], switch_string.c_str()); +} + +// static +std::wstring CommandLine::PrefixedSwitchStringWithValue( + const std::wstring& switch_string, const std::wstring& value_string) { + if (value_string.empty()) { + return PrefixedSwitchString(switch_string); + } + + return StringPrintf(L"%ls%ls%ls%ls", kSwitchPrefixes[0], + switch_string.c_str(), kSwitchValueSeparator, + value_string.c_str()); +} + +#if defined(XP_WIN) +void CommandLine::AppendSwitch(const std::wstring& switch_string) { + std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string); + command_line_string_.append(L" "); + command_line_string_.append(prefixed_switch_string); + switches_[WideToASCII(switch_string)] = L""; +} + +// Quote a string if necessary, such that CommandLineToArgvW() will +// always process it as a single argument. +static std::wstring WindowsStyleQuote(const std::wstring& arg) { + // We follow the quoting rules of CommandLineToArgvW. + // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + if (arg.find_first_of(L" \\\"\t") == std::wstring::npos) { + // No quoting necessary. + return arg; + } + + std::wstring out; + out.push_back(L'"'); + for (size_t i = 0; i < arg.size(); ++i) { + if (arg[i] == '\\') { + // Find the extent of this run of backslashes. + size_t start = i, end = start + 1; + for (; end < arg.size() && arg[end] == '\\'; ++end) /* empty */ + ; + size_t backslash_count = end - start; + + // Backslashes are escapes only if the run is followed by a double quote. + // Since we also will end the string with a double quote, we escape for + // either a double quote or the end of the string. + if (end == arg.size() || arg[end] == '"') { + // To quote, we need to output 2x as many backslashes. + backslash_count *= 2; + } + for (size_t j = 0; j < backslash_count; ++j) out.push_back('\\'); + + // Advance i to one before the end to balance i++ in loop. + i = end - 1; + } else if (arg[i] == '"') { + out.push_back('\\'); + out.push_back('"'); + } else { + out.push_back(arg[i]); + } + } + out.push_back('"'); + + return out; +} + +void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string) { + std::wstring quoted_value_string = WindowsStyleQuote(value_string); + std::wstring combined_switch_string = + PrefixedSwitchStringWithValue(switch_string, quoted_value_string); + + command_line_string_.append(L" "); + command_line_string_.append(combined_switch_string); + + switches_[WideToASCII(switch_string)] = value_string; +} + +void CommandLine::AppendLooseValue(const std::wstring& value) { + command_line_string_.append(L" "); + command_line_string_.append(WindowsStyleQuote(value)); +} + +void CommandLine::AppendArguments(const CommandLine& other, + bool include_program) { + // Verify include_program is used correctly. + // Logic could be shorter but this is clearer. + DCHECK(include_program ? !other.program().empty() : other.program().empty()); + command_line_string_ += L" " + other.command_line_string_; + std::map<std::string, StringType>::const_iterator i; + for (i = other.switches_.begin(); i != other.switches_.end(); ++i) + switches_[i->first] = i->second; +} + +void CommandLine::PrependWrapper(const std::wstring& wrapper) { + // The wrapper may have embedded arguments (like "gdb --args"). In this case, + // we don't pretend to do anything fancy, we just split on spaces. + std::vector<std::wstring> wrapper_and_args; + SplitString(wrapper, ' ', &wrapper_and_args); + program_ = wrapper_and_args[0]; + command_line_string_ = wrapper + L" " + command_line_string_; +} + +#else +void CommandLine::AppendSwitch(const std::wstring& switch_string) { + std::string ascii_switch = WideToASCII(switch_string); + argv_.push_back(kSwitchPrefixes[0] + ascii_switch); + switches_[ascii_switch] = ""; +} + +void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string) { + std::string ascii_switch = WideToASCII(switch_string); + std::string ascii_value = WideToASCII(value_string); + + argv_.push_back(kSwitchPrefixes[0] + ascii_switch + kSwitchValueSeparator + + ascii_value); + switches_[ascii_switch] = ascii_value; +} + +void CommandLine::AppendLooseValue(const std::wstring& value) { + argv_.push_back(WideToASCII(value)); +} + +void CommandLine::AppendArguments(const CommandLine& other, + bool include_program) { + // Verify include_program is used correctly. + // Logic could be shorter but this is clearer. + DCHECK(include_program ? !other.program().empty() : other.program().empty()); + + size_t first_arg = include_program ? 0 : 1; + for (size_t i = first_arg; i < other.argv_.size(); ++i) + argv_.push_back(other.argv_[i]); + std::map<std::string, StringType>::const_iterator i; + for (i = other.switches_.begin(); i != other.switches_.end(); ++i) + switches_[i->first] = i->second; +} + +void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) { + // The wrapper may have embedded arguments (like "gdb --args"). In this case, + // we don't pretend to do anything fancy, we just split on spaces. + const std::string wrapper = WideToASCII(wrapper_wide); + std::vector<std::string> wrapper_and_args; + SplitString(wrapper, ' ', &wrapper_and_args); + argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); +} + +#endif diff --git a/ipc/chromium/src/base/command_line.h b/ipc/chromium/src/base/command_line.h new file mode 100644 index 0000000000..1aff5eae07 --- /dev/null +++ b/ipc/chromium/src/base/command_line.h @@ -0,0 +1,188 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This class works with command lines: building and parsing. +// Switches can optionally have a value attached using an equals sign, +// as in "-switch=value". Arguments that aren't prefixed with a +// switch prefix are considered "loose parameters". Switch names are +// case-insensitive. An argument of "--" will terminate switch +// parsing, causing everything after to be considered as loose +// parameters. + +// There is a singleton read-only CommandLine that represents the command +// line that the current process was started with. It must be initialized +// in main() (or whatever the platform's equivalent function is). + +#ifndef BASE_COMMAND_LINE_H_ +#define BASE_COMMAND_LINE_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" + +class InProcessBrowserTest; + +class CommandLine { + public: +#if defined(XP_WIN) + // Creates a parsed version of the given command-line string. + // The program name is assumed to be the first item in the string. + void ParseFromString(const std::wstring& command_line); +#else + // Initialize from an argv vector (or directly from main()'s argv). + CommandLine(int argc, const char* const* argv); + explicit CommandLine(const std::vector<std::string>& argv); +#endif + + // Construct a new, empty command line. + // |program| is the name of the program to run (aka argv[0]). + // TODO(port): should be a FilePath. + explicit CommandLine(const std::wstring& program); + + // Initialize the current process CommandLine singleton. On Windows, + // ignores its arguments (we instead parse GetCommandLineW() + // directly) because we don't trust the CRT's parsing of the command + // line, but it still must be called to set up the command line. + static void Init(int argc, const char* const* argv); + + // Destroys the current process CommandLine singleton. This is necessary if + // you want to reset the base library to its initial state (for example in an + // outer library that needs to be able to terminate, and be re-initialized). + // If Init is called only once, e.g. in main(), calling Terminate() is not + // necessary. + static void Terminate(); + + // Get the singleton CommandLine representing the current process's + // command line. + static const CommandLine* ForCurrentProcess() { + DCHECK(current_process_commandline_); + return current_process_commandline_; + } + + static bool IsInitialized() { return !!current_process_commandline_; } + + // Returns true if this command line contains the given switch. + // (Switch names are case-insensitive.) + bool HasSwitch(const std::wstring& switch_string) const; + + // Returns the value associated with the given switch. If the + // switch has no value or isn't present, this method returns + // the empty string. + std::wstring GetSwitchValue(const std::wstring& switch_string) const; + + // Get the remaining arguments to the command. + // WARNING: this is incorrect on POSIX; we must do string conversions. + std::vector<std::wstring> GetLooseValues() const; + +#if defined(XP_WIN) + // Returns the original command line string. + const std::wstring& command_line_string() const { + return command_line_string_; + } +#else + // Returns the original command line string as a vector of strings. + const std::vector<std::string>& argv() const { return argv_; } +#endif + + // Returns the program part of the command line string (the first item). + std::wstring program() const; + + // Return a copy of the string prefixed with a switch prefix. + // Used internally. + static std::wstring PrefixedSwitchString(const std::wstring& switch_string); + + // Return a copy of the string prefixed with a switch prefix, + // and appended with the given value. Used internally. + static std::wstring PrefixedSwitchStringWithValue( + const std::wstring& switch_string, const std::wstring& value_string); + + // Appends the given switch string (preceded by a space and a switch + // prefix) to the given string. + void AppendSwitch(const std::wstring& switch_string); + + // Appends the given switch string (preceded by a space and a switch + // prefix) to the given string, with the given value attached. + void AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string); + + // Append a loose value to the command line. + void AppendLooseValue(const std::wstring& value); + +#if defined(XP_WIN) + void AppendLooseValue(const wchar_t* value) { + AppendLooseValue(std::wstring(value)); + } +#endif + + // Append the arguments from another command line to this one. + // If |include_program| is true, include |other|'s program as well. + void AppendArguments(const CommandLine& other, bool include_program); + + // On POSIX systems it's common to run processes via a wrapper (like + // "valgrind" or "gdb --args"). + void PrependWrapper(const std::wstring& wrapper); + + private: + friend class InProcessBrowserTest; + + CommandLine() {} + + // Used by InProcessBrowserTest. + static CommandLine* ForCurrentProcessMutable() { + DCHECK(current_process_commandline_); + return current_process_commandline_; + } + + // The singleton CommandLine instance representing the current process's + // command line. + static CommandLine* current_process_commandline_; + + // We store a platform-native version of the command line, used when building + // up a new command line to be executed. This ifdef delimits that code. + +#if defined(XP_WIN) + // The quoted, space-separated command-line string. + std::wstring command_line_string_; + + // The name of the program. + std::wstring program_; + + // The type of native command line arguments. + typedef std::wstring StringType; + +#else + // The argv array, with the program name in argv_[0]. + std::vector<std::string> argv_; + + // The type of native command line arguments. + typedef std::string StringType; + + // Shared by the two POSIX constructor forms. Initalize from argv_. + void InitFromArgv(); +#endif + + // Returns true and fills in |switch_string| and |switch_value| + // if |parameter_string| represents a switch. + static bool IsSwitch(const StringType& parameter_string, + std::string* switch_string, StringType* switch_value); + + // Parsed-out values. + std::map<std::string, StringType> switches_; + + // Non-switch command-line arguments. + std::vector<StringType> loose_values_; + + // We allow copy constructors, because a common pattern is to grab a + // copy of the current process's command line and then add some + // flags to it. E.g.: + // CommandLine cl(*CommandLine::ForCurrentProcess()); + // cl.AppendSwitch(...); +}; + +#endif // BASE_COMMAND_LINE_H_ diff --git a/ipc/chromium/src/base/compiler_specific.h b/ipc/chromium/src/base/compiler_specific.h new file mode 100644 index 0000000000..9cf749d9f1 --- /dev/null +++ b/ipc/chromium/src/base/compiler_specific.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_COMPILER_SPECIFIC_H_ +#define BASE_COMPILER_SPECIFIC_H_ + +#if defined(_MSC_VER) + +// Macros for suppressing and disabling warnings on MSVC. +// +// Warning numbers are enumerated at: +// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx +// +// The warning pragma: +// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx +// +// Using __pragma instead of #pragma inside macros: +// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx + +// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and +// for the next line of the source file. +# define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress : n)) + +// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled. +// The warning remains disabled until popped by MSVC_POP_WARNING. +# define MSVC_PUSH_DISABLE_WARNING(n) \ + __pragma(warning(push)) __pragma(warning(disable : n)) + +// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level +// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all +// warnings. +# define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n)) + +// Pop effects of innermost MSVC_PUSH_* macro. +# define MSVC_POP_WARNING() __pragma(warning(pop)) + +# define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off)) +# define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on)) + +// Allows |this| to be passed as an argument in constructor initializer lists. +// This uses push/pop instead of the seemingly simpler suppress feature to avoid +// having the warning be disabled for more than just |code|. +// +// Example usage: +// Foo::Foo() : x(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(y(this)), z(3) {} +// +// Compiler warning C4355: 'this': used in base member initializer list: +// http://msdn.microsoft.com/en-us/library/3c594ae3(VS.80).aspx +# define ALLOW_THIS_IN_INITIALIZER_LIST(code) \ + MSVC_PUSH_DISABLE_WARNING(4355) \ + code MSVC_POP_WARNING() + +#else // Not MSVC + +# define MSVC_SUPPRESS_WARNING(n) +# define MSVC_PUSH_DISABLE_WARNING(n) +# define MSVC_PUSH_WARNING_LEVEL(n) +# define MSVC_POP_WARNING() +# define MSVC_DISABLE_OPTIMIZE() +# define MSVC_ENABLE_OPTIMIZE() +# define ALLOW_THIS_IN_INITIALIZER_LIST(code) code + +#endif // _MSC_VER + +// Annotate a function indicating the caller must examine the return value. +// Use like: +// int foo() WARN_UNUSED_RESULT; +// To explicitly ignore a result, see |ignore_result()| in base/macros.h. +#undef WARN_UNUSED_RESULT +#if defined(__GNUC__) || defined(__clang__) +# define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +# define WARN_UNUSED_RESULT +#endif + +#endif // BASE_COMPILER_SPECIFIC_H_ diff --git a/ipc/chromium/src/base/condition_variable.h b/ipc/chromium/src/base/condition_variable.h new file mode 100644 index 0000000000..0b82824ce6 --- /dev/null +++ b/ipc/chromium/src/base/condition_variable.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ConditionVariable wraps pthreads condition variable synchronization or, on +// Windows, simulates it. This functionality is very helpful for having +// several threads wait for an event, as is common with a managed thread pool. +// The meaning of such an event in the (worker) thread pool scenario is that +// additional tasks are now available for processing. It is used in Chrome in +// the DNS prefetching system to notify worker threads that a queue now has +// items (tasks) which need to be tended to. A related use would have a pool +// manager waiting on a ConditionVariable, waiting for a thread in the pool to +// announce (signal) that there is now more room in a (bounded size) +// communications queue for the manager to deposit tasks, or, as a second +// example, that the queue of tasks is completely empty and all workers are +// waiting. +// +// USAGE NOTE 1: spurious signal events are possible with this and +// most implementations of condition variables. As a result, be +// *sure* to retest your condition before proceeding. The following +// is a good example of doing this correctly: +// +// while (!work_to_be_done()) Wait(...); +// +// In contrast do NOT do the following: +// +// if (!work_to_be_done()) Wait(...); // Don't do this. +// +// Especially avoid the above if you are relying on some other thread only +// issuing a signal up *if* there is work-to-do. There can/will +// be spurious signals. Recheck state on waiting thread before +// assuming the signal was intentional. Caveat caller ;-). +// +// USAGE NOTE 2: Broadcast() frees up all waiting threads at once, +// which leads to contention for the locks they all held when they +// called Wait(). This results in POOR performance. A much better +// approach to getting a lot of threads out of Wait() is to have each +// thread (upon exiting Wait()) call Signal() to free up another +// Wait'ing thread. Look at condition_variable_unittest.cc for +// both examples. +// +// Broadcast() can be used nicely during teardown, as it gets the job +// done, and leaves no sleeping threads... and performance is less +// critical at that point. +// +// The semantics of Broadcast() are carefully crafted so that *all* +// threads that were waiting when the request was made will indeed +// get signaled. Some implementations mess up, and don't signal them +// all, while others allow the wait to be effectively turned off (for +// a while while waiting threads come around). This implementation +// appears correct, as it will not "lose" any signals, and will guarantee +// that all threads get signaled by Broadcast(). +// +// This implementation offers support for "performance" in its selection of +// which thread to revive. Performance, in direct contrast with "fairness," +// assures that the thread that most recently began to Wait() is selected by +// Signal to revive. Fairness would (if publicly supported) assure that the +// thread that has Wait()ed the longest is selected. The default policy +// may improve performance, as the selected thread may have a greater chance of +// having some of its stack data in various CPU caches. +// +// For a discussion of the many very subtle implementation details, see the FAQ +// at the end of condition_variable_win.cc. + +#ifndef BASE_CONDITION_VARIABLE_H_ +#define BASE_CONDITION_VARIABLE_H_ + +#include "base/basictypes.h" +#include "base/lock.h" + +#if defined(XP_UNIX) +# include <pthread.h> +#endif + +#if defined(XP_WIN) +# include <windows.h> +#endif + +namespace base { +class TimeDelta; +} + +class ConditionVariable { + public: + // Construct a cv for use with ONLY one user lock. + explicit ConditionVariable(Lock* user_lock); + + ~ConditionVariable(); + + // Wait() releases the caller's critical section atomically as it starts to + // sleep, and the reacquires it when it is signaled. The wait functions are + // susceptible to spurious wakeups. (See usage note 1 for more details.) + void Wait(); + void TimedWait(const base::TimeDelta& max_time); + + // Broadcast() revives all waiting threads. (See usage note 2 for more + // details.) + void Broadcast(); + // Signal() revives one waiting thread. + void Signal(); + + private: +#if defined(XP_WIN) + CONDITION_VARIABLE cv_; + SRWLOCK* const srwlock_; +#else + pthread_cond_t condition_; + pthread_mutex_t* user_mutex_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ConditionVariable); +}; + +#endif // BASE_CONDITION_VARIABLE_H_ diff --git a/ipc/chromium/src/base/condition_variable_posix.cc b/ipc/chromium/src/base/condition_variable_posix.cc new file mode 100644 index 0000000000..be01d3af95 --- /dev/null +++ b/ipc/chromium/src/base/condition_variable_posix.cc @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/condition_variable.h" + +#include <errno.h> +#include <stdint.h> +#include <sys/time.h> + +#include "base/lock.h" +#include "base/logging.h" +#include "base/time.h" + +ConditionVariable::ConditionVariable(Lock* user_lock) + : user_mutex_(user_lock->lock_.native_handle()) { + int rv = 0; + // http://crbug.com/293736 + // On older Android platform versions, monotonic clock based absolute + // deadlines are supported through the non-standard + // pthread_cond_timedwait_monotonic_np. Newer platform versions have + // pthread_condattr_setclock. + // Mac can use relative time deadlines. +#if !defined(XP_DARWIN) && \ + !(defined(ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)) + pthread_condattr_t attrs; + rv = pthread_condattr_init(&attrs); + DCHECK_EQ(0, rv); + pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC); + rv = pthread_cond_init(&condition_, &attrs); + pthread_condattr_destroy(&attrs); +#else + rv = pthread_cond_init(&condition_, NULL); +#endif + DCHECK_EQ(0, rv); +} + +ConditionVariable::~ConditionVariable() { +#if defined(XP_DARWIN) + // This hack is necessary to avoid a fatal pthreads subsystem bug in the + // Darwin kernel. http://crbug.com/517681. + { + Lock lock; + AutoLock l(lock); + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1; + pthread_cond_timedwait_relative_np(&condition_, lock.lock_.native_handle(), + &ts); + } +#endif + + int rv = pthread_cond_destroy(&condition_); + DCHECK_EQ(0, rv); +} + +void ConditionVariable::Wait() { + int rv = pthread_cond_wait(&condition_, user_mutex_); + DCHECK_EQ(0, rv); +} + +void ConditionVariable::TimedWait(const base::TimeDelta& max_time) { + int64_t usecs = max_time.InMicroseconds(); + struct timespec relative_time; + relative_time.tv_sec = usecs / base::Time::kMicrosecondsPerSecond; + relative_time.tv_nsec = (usecs % base::Time::kMicrosecondsPerSecond) * + base::Time::kNanosecondsPerMicrosecond; + +#if defined(XP_DARWIN) + int rv = pthread_cond_timedwait_relative_np(&condition_, user_mutex_, + &relative_time); +#else + // The timeout argument to pthread_cond_timedwait is in absolute time. + struct timespec absolute_time; + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + absolute_time.tv_sec = now.tv_sec; + absolute_time.tv_nsec = now.tv_nsec; + + absolute_time.tv_sec += relative_time.tv_sec; + absolute_time.tv_nsec += relative_time.tv_nsec; + absolute_time.tv_sec += + absolute_time.tv_nsec / base::Time::kNanosecondsPerSecond; + absolute_time.tv_nsec %= base::Time::kNanosecondsPerSecond; + DCHECK_GE(absolute_time.tv_sec, now.tv_sec); // Overflow paranoia + +# if defined(ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC) + int rv = pthread_cond_timedwait_monotonic_np(&condition_, user_mutex_, + &absolute_time); +# else + int rv = pthread_cond_timedwait(&condition_, user_mutex_, &absolute_time); +# endif // ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC +#endif // XP_DARWIN + + // On failure, we only expect the CV to timeout. Any other error value means + // that we've unexpectedly woken up. + DCHECK(rv == 0 || rv == ETIMEDOUT); +} + +void ConditionVariable::Broadcast() { + int rv = pthread_cond_broadcast(&condition_); + DCHECK_EQ(0, rv); +} + +void ConditionVariable::Signal() { + int rv = pthread_cond_signal(&condition_); + DCHECK_EQ(0, rv); +} diff --git a/ipc/chromium/src/base/condition_variable_win.cc b/ipc/chromium/src/base/condition_variable_win.cc new file mode 100644 index 0000000000..35d79ba2ec --- /dev/null +++ b/ipc/chromium/src/base/condition_variable_win.cc @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 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/condition_variable.h" + +#include "base/lock.h" +#include "base/logging.h" +#include "base/time.h" + +ConditionVariable::ConditionVariable(Lock* user_lock) + : srwlock_(user_lock->lock_.native_handle()) { + DCHECK(user_lock); + InitializeConditionVariable(&cv_); +} + +ConditionVariable::~ConditionVariable() = default; + +void ConditionVariable::Wait() { + TimedWait(base::TimeDelta::FromMilliseconds(INFINITE)); +} + +void ConditionVariable::TimedWait(const base::TimeDelta& max_time) { + DWORD timeout = static_cast<DWORD>(max_time.InMilliseconds()); + + if (!SleepConditionVariableSRW(&cv_, srwlock_, timeout, 0)) { + // On failure, we only expect the CV to timeout. Any other error value means + // that we've unexpectedly woken up. + // Note that WAIT_TIMEOUT != ERROR_TIMEOUT. WAIT_TIMEOUT is used with the + // WaitFor* family of functions as a direct return value. ERROR_TIMEOUT is + // used with GetLastError(). + DCHECK_EQ(static_cast<DWORD>(ERROR_TIMEOUT), GetLastError()); + } +} + +void ConditionVariable::Broadcast() { WakeAllConditionVariable(&cv_); } + +void ConditionVariable::Signal() { WakeConditionVariable(&cv_); } diff --git a/ipc/chromium/src/base/dir_reader_bsd.h b/ipc/chromium/src/base/dir_reader_bsd.h new file mode 100644 index 0000000000..851f46a006 --- /dev/null +++ b/ipc/chromium/src/base/dir_reader_bsd.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// derived from dir_reader_linux.h + +#ifndef BASE_DIR_READER_BSD_H_ +#define BASE_DIR_READER_BSD_H_ +#pragma once + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/eintr_wrapper.h" + +// See the comments in dir_reader_posix.h about this. + +namespace base { + +class DirReaderBSD { + public: + explicit DirReaderBSD(const char* directory_path) +#ifdef O_DIRECTORY + : fd_(open(directory_path, O_RDONLY | O_DIRECTORY)), +#else + : fd_(open(directory_path, O_RDONLY)), +#endif + offset_(0), + size_(0) { + memset(buf_, 0, sizeof(buf_)); + } + + ~DirReaderBSD() { + if (fd_ >= 0) { + if (IGNORE_EINTR(close(fd_))) + DLOG(ERROR) << "Failed to close directory handle"; + } + } + + bool IsValid() const { return fd_ >= 0; } + + // Move to the next entry returning false if the iteration is complete. + bool Next() { + if (size_) { + struct dirent* dirent = reinterpret_cast<struct dirent*>(&buf_[offset_]); +#ifdef __DragonFly__ + offset_ += _DIRENT_DIRSIZ(dirent); +#else + offset_ += dirent->d_reclen; +#endif + } + + if (offset_ != size_) return true; + + const int r = getdents(fd_, buf_, sizeof(buf_)); + if (r == 0) return false; + if (r == -1) { + DLOG(ERROR) << "getdents returned an error: " << errno; + return false; + } + size_ = r; + offset_ = 0; + return true; + } + + const char* name() const { + if (!size_) return NULL; + + const struct dirent* dirent = + reinterpret_cast<const struct dirent*>(&buf_[offset_]); + return dirent->d_name; + } + + int fd() const { return fd_; } + + static bool IsFallback() { return false; } + + private: + const int fd_; + char buf_[512]; + size_t offset_, size_; + + DISALLOW_COPY_AND_ASSIGN(DirReaderBSD); +}; + +} // namespace base + +#endif // BASE_DIR_READER_BSD_H_ diff --git a/ipc/chromium/src/base/dir_reader_fallback.h b/ipc/chromium/src/base/dir_reader_fallback.h new file mode 100644 index 0000000000..5fbf658d49 --- /dev/null +++ b/ipc/chromium/src/base/dir_reader_fallback.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_DIR_READER_FALLBACK_H_ +#define BASE_DIR_READER_FALLBACK_H_ +#pragma once + +namespace base { + +class DirReaderFallback { + public: + // Open a directory. If |IsValid| is true, then |Next| can be called to start + // the iteration at the beginning of the directory. + explicit DirReaderFallback(const char* directory_path) {} + // After construction, IsValid returns true iff the directory was + // successfully opened. + bool IsValid() const { return false; } + // Move to the next entry returning false if the iteration is complete. + bool Next() { return false; } + // Return the name of the current directory entry. + const char* name() { return 0; } + // Return the file descriptor which is being used. + int fd() const { return -1; } + // Returns true if this is a no-op fallback class (for testing). + static bool IsFallback() { return true; } +}; + +} // namespace base + +#endif // BASE_DIR_READER_FALLBACK_H_ diff --git a/ipc/chromium/src/base/dir_reader_linux.h b/ipc/chromium/src/base/dir_reader_linux.h new file mode 100644 index 0000000000..a70ff3bb64 --- /dev/null +++ b/ipc/chromium/src/base/dir_reader_linux.h @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_DIR_READER_LINUX_H_ +#define BASE_DIR_READER_LINUX_H_ +#pragma once + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include "base/logging.h" +#include "base/eintr_wrapper.h" + +// See the comments in dir_reader_posix.h about this. + +namespace base { + +struct linux_dirent { + uint64_t d_ino; + int64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[0]; +}; + +class DirReaderLinux { + public: + explicit DirReaderLinux(const char* directory_path) + : fd_(open(directory_path, O_RDONLY | O_DIRECTORY)), + offset_(0), + size_(0) { + memset(buf_, 0, sizeof(buf_)); + } + + ~DirReaderLinux() { + if (fd_ >= 0) { + if (IGNORE_EINTR(close(fd_))) + DLOG(ERROR) << "Failed to close directory handle"; + } + } + + bool IsValid() const { return fd_ >= 0; } + + // Move to the next entry returning false if the iteration is complete. + bool Next() { + if (size_) { + linux_dirent* dirent = reinterpret_cast<linux_dirent*>(&buf_[offset_]); + offset_ += dirent->d_reclen; + } + + if (offset_ != size_) return true; + + const int r = syscall(__NR_getdents64, fd_, buf_, sizeof(buf_)); + if (r == 0) return false; + if (r == -1) { + DLOG(ERROR) << "getdents64 returned an error: " << errno; + return false; + } + size_ = r; + offset_ = 0; + return true; + } + + const char* name() const { + if (!size_) return NULL; + + const linux_dirent* dirent = + reinterpret_cast<const linux_dirent*>(&buf_[offset_]); + return dirent->d_name; + } + + int fd() const { return fd_; } + + static bool IsFallback() { return false; } + + private: + const int fd_; + union { + linux_dirent dirent_; + unsigned char buf_[512]; + }; + size_t offset_, size_; + + DISALLOW_COPY_AND_ASSIGN(DirReaderLinux); +}; + +} // namespace base + +#endif // BASE_DIR_READER_LINUX_H_ diff --git a/ipc/chromium/src/base/dir_reader_posix.h b/ipc/chromium/src/base/dir_reader_posix.h new file mode 100644 index 0000000000..2a2aae1140 --- /dev/null +++ b/ipc/chromium/src/base/dir_reader_posix.h @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_DIR_READER_POSIX_H_ +#define BASE_DIR_READER_POSIX_H_ +#pragma once + +// This header provides a class, DirReaderPosix, which allows one to open and +// read from directories without allocating memory. For the interface, see +// the generic fallback in dir_reader_fallback.h. + +// Mac note: OS X has getdirentries, but it only works if we restrict Chrome to +// 32-bit inodes. There is a getdirentries64 syscall in 10.6, but it's not +// wrapped and the direct syscall interface is unstable. Using an unstable API +// seems worse than falling back to enumerating all file descriptors so we will +// probably never implement this on the Mac. + +#if defined(XP_LINUX) +# include "base/dir_reader_linux.h" +#elif (defined(__DragonFly__) || defined(XP_FREEBSD) || defined(XP_NETBSD) || \ + defined(XP_OPENBSD)) && \ + !defined(__GLIBC__) +# include "base/dir_reader_bsd.h" +#else +# include "base/dir_reader_fallback.h" +#endif + +namespace base { + +#if defined(XP_LINUX) +typedef DirReaderLinux DirReaderPosix; +#elif (defined(__DragonFly__) || defined(XP_FREEBSD) || defined(XP_NETBSD) || \ + defined(XP_OPENBSD)) && \ + !defined(__GLIBC__) +typedef DirReaderBSD DirReaderPosix; +#else +typedef DirReaderFallback DirReaderPosix; +#endif + +} // namespace base + +#endif // BASE_DIR_READER_POSIX_H_ diff --git a/ipc/chromium/src/base/eintr_wrapper.h b/ipc/chromium/src/base/eintr_wrapper.h new file mode 100644 index 0000000000..8592efa8ce --- /dev/null +++ b/ipc/chromium/src/base/eintr_wrapper.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This provides a wrapper around system calls which may be interrupted by a +// signal and return EINTR. See man 7 signal. +// +// On Windows, this wrapper macro does nothing because there are no +// signals. +// +// Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return +// value of close is significant. See http://crbug.com/269623. + +#ifndef BASE_EINTR_WRAPPER_H_ +#define BASE_EINTR_WRAPPER_H_ + +#if defined(XP_UNIX) + +# include <errno.h> + +# define HANDLE_EINTR(x) \ + ({ \ + decltype(x) eintr_wrapper_result; \ + do { \ + eintr_wrapper_result = (x); \ + } while (eintr_wrapper_result == -1 && errno == EINTR); \ + eintr_wrapper_result; \ + }) + +# define IGNORE_EINTR(x) \ + ({ \ + decltype(x) eintr_wrapper_result; \ + do { \ + eintr_wrapper_result = (x); \ + if (eintr_wrapper_result == -1 && errno == EINTR) { \ + eintr_wrapper_result = 0; \ + } \ + } while (0); \ + eintr_wrapper_result; \ + }) + +# define HANDLE_RV_EINTR(x) \ + ({ \ + decltype(x) eintr_wrapper_result; \ + do { \ + eintr_wrapper_result = (x); \ + } while (eintr_wrapper_result == EINTR); \ + eintr_wrapper_result; \ + }) + +#else + +# define HANDLE_EINTR(x) (x) +# define IGNORE_EINTR(x) (x) +# define HANDLE_RV_EINTR(x) (x) + +#endif // XP_UNIX + +#endif // !BASE_EINTR_WRAPPER_H_ diff --git a/ipc/chromium/src/base/file_path.cc b/ipc/chromium/src/base/file_path.cc new file mode 100644 index 0000000000..6796ad45d0 --- /dev/null +++ b/ipc/chromium/src/base/file_path.cc @@ -0,0 +1,313 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <fstream> + +#include "base/file_path.h" +#include "base/logging.h" + +// These includes are just for the *Hack functions, and should be removed +// when those functions are removed. +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" + +#if defined(FILE_PATH_USES_WIN_SEPARATORS) +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); +#else // FILE_PATH_USES_WIN_SEPARATORS +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); +#endif // FILE_PATH_USES_WIN_SEPARATORS + +const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); +const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); + +const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); + +namespace { + +// If this FilePath contains a drive letter specification, returns the +// position of the last character of the drive letter specification, +// otherwise returns npos. This can only be true on Windows, when a pathname +// begins with a letter followed by a colon. On other platforms, this always +// returns npos. +FilePath::StringType::size_type FindDriveLetter( + const FilePath::StringType& path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + // This is dependent on an ASCII-based character set, but that's a + // reasonable assumption. iswalpha can be too inclusive here. + if (path.length() >= 2 && path[1] == L':' && + ((path[0] >= L'A' && path[0] <= L'Z') || + (path[0] >= L'a' && path[0] <= L'z'))) { + return 1; + } +#endif // FILE_PATH_USES_DRIVE_LETTERS + return FilePath::StringType::npos; +} + +bool IsPathAbsolute(const FilePath::StringType& path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + FilePath::StringType::size_type letter = FindDriveLetter(path); + if (letter != FilePath::StringType::npos) { + // Look for a separator right after the drive specification. + return path.length() > letter + 1 && + FilePath::IsSeparator(path[letter + 1]); + } + // Look for a pair of leading separators. + return path.length() > 1 && FilePath::IsSeparator(path[0]) && + FilePath::IsSeparator(path[1]); +#else // FILE_PATH_USES_DRIVE_LETTERS + // Look for a separator in the first position. + return path.length() > 0 && FilePath::IsSeparator(path[0]); +#endif // FILE_PATH_USES_DRIVE_LETTERS +} + +} // namespace + +bool FilePath::IsSeparator(CharType character) { + for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) { + if (character == kSeparators[i]) { + return true; + } + } + + return false; +} + +// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't +// guaranteed to not modify their input strings, and in fact are implemented +// differently in this regard on different platforms. Don't use them, but +// adhere to their behavior. +FilePath FilePath::DirName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, always needs to remain in the output. If there + // is no drive letter, as will always be the case on platforms which do not + // support drive letters, letter will be npos, or -1, so the comparisons and + // resizes below using letter will still be valid. + StringType::size_type letter = FindDriveLetter(new_path.path_); + + StringType::size_type last_separator = new_path.path_.find_last_of( + kSeparators, StringType::npos, arraysize(kSeparators) - 1); + if (last_separator == StringType::npos) { + // path_ is in the current directory. + new_path.path_.resize(letter + 1); + } else if (last_separator == letter + 1) { + // path_ is in the root directory. + new_path.path_.resize(letter + 2); + } else if (last_separator == letter + 2 && + IsSeparator(new_path.path_[letter + 1])) { + // path_ is in "//" (possibly with a drive letter); leave the double + // separator intact indicating alternate root. + new_path.path_.resize(letter + 3); + } else if (last_separator != 0) { + // path_ is somewhere else, trim the basename. + new_path.path_.resize(last_separator); + } + + new_path.StripTrailingSeparatorsInternal(); + if (!new_path.path_.length()) new_path.path_ = kCurrentDirectory; + + return new_path; +} + +FilePath FilePath::BaseName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, is always stripped. + StringType::size_type letter = FindDriveLetter(new_path.path_); + if (letter != StringType::npos) { + new_path.path_.erase(0, letter + 1); + } + + // Keep everything after the final separator, but if the pathname is only + // one character and it's a separator, leave it alone. + StringType::size_type last_separator = new_path.path_.find_last_of( + kSeparators, StringType::npos, arraysize(kSeparators) - 1); + if (last_separator != StringType::npos && + last_separator < new_path.path_.length() - 1) { + new_path.path_.erase(0, last_separator + 1); + } + + return new_path; +} + +FilePath::StringType FilePath::Extension() const { + // BaseName() calls StripTrailingSeparators, so cases like /foo.baz/// work. + StringType base = BaseName().value(); + + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) + return StringType(); + + const StringType::size_type last_dot = base.rfind(kExtensionSeparator); + if (last_dot == StringType::npos) return StringType(); + return StringType(base, last_dot); +} + +FilePath FilePath::RemoveExtension() const { + StringType ext = Extension(); + // It's important to check Extension() since that verifies that the + // kExtensionSeparator actually appeared in the last path component. + if (ext.empty()) return FilePath(path_); + // Since Extension() verified that the extension is in fact in the last path + // component, this substr will effectively strip trailing separators. + const StringType::size_type last_dot = path_.rfind(kExtensionSeparator); + return FilePath(path_.substr(0, last_dot)); +} + +FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const { + if (suffix.empty()) return FilePath(path_); + + if (path_.empty()) return FilePath(); + + StringType base = BaseName().value(); + if (base.empty()) return FilePath(); + if (*(base.end() - 1) == kExtensionSeparator) { + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) { + return FilePath(); + } + } + + StringType ext = Extension(); + StringType ret = RemoveExtension().value(); + ret.append(suffix); + ret.append(ext); + return FilePath(ret); +} + +FilePath FilePath::ReplaceExtension(const StringType& extension) const { + if (path_.empty()) return FilePath(); + + StringType base = BaseName().value(); + if (base.empty()) return FilePath(); + if (*(base.end() - 1) == kExtensionSeparator) { + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) { + return FilePath(); + } + } + + FilePath no_ext = RemoveExtension(); + // If the new extension is "" or ".", then just remove the current extension. + if (extension.empty() || extension == StringType(1, kExtensionSeparator)) + return no_ext; + + StringType str = no_ext.value(); + if (extension[0] != kExtensionSeparator) str.append(1, kExtensionSeparator); + str.append(extension); + return FilePath(str); +} + +FilePath FilePath::Append(const StringType& component) const { + DCHECK(!IsPathAbsolute(component)); + if (path_.compare(kCurrentDirectory) == 0) { + // Append normally doesn't do any normalization, but as a special case, + // when appending to kCurrentDirectory, just return a new path for the + // component argument. Appending component to kCurrentDirectory would + // serve no purpose other than needlessly lengthening the path, and + // it's likely in practice to wind up with FilePath objects containing + // only kCurrentDirectory when calling DirName on a single relative path + // component. + return FilePath(component); + } + + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // Don't append a separator if the path is empty (indicating the current + // directory) or if the path component is empty (indicating nothing to + // append). + if (component.length() > 0 && new_path.path_.length() > 0) { + // Don't append a separator if the path still ends with a trailing + // separator after stripping (indicating the root directory). + if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { + // Don't append a separator if the path is just a drive letter. + if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { + new_path.path_.append(1, kSeparators[0]); + } + } + } + + new_path.path_.append(component); + return new_path; +} + +FilePath FilePath::Append(const FilePath& component) const { + return Append(component.value()); +} + +FilePath FilePath::AppendASCII(const std::string& component) const { + DCHECK(IsStringASCII(component)); +#if defined(XP_WIN) + return Append(ASCIIToWide(component)); +#else + return Append(component); +#endif +} + +bool FilePath::IsAbsolute() const { return IsPathAbsolute(path_); } + +#if defined(XP_UNIX) +// See file_path.h for a discussion of the encoding of paths on POSIX +// platforms. These *Hack() functions are not quite correct, but they're +// only temporary while we fix the remainder of the code. +// Remember to remove the #includes at the top when you remove these. + +// static +FilePath FilePath::FromWStringHack(const std::wstring& wstring) { + return FilePath(base::SysWideToNativeMB(wstring)); +} +std::wstring FilePath::ToWStringHack() const { + return base::SysNativeMBToWide(path_); +} +#else +// static +FilePath FilePath::FromWStringHack(const std::wstring& wstring) { + return FilePath(wstring); +} +std::wstring FilePath::ToWStringHack() const { return path_; } +#endif + +void FilePath::OpenInputStream(std::ifstream& stream) const { + stream.open( +#ifndef __MINGW32__ + path_.c_str(), +#else + base::SysWideToNativeMB(path_).c_str(), +#endif + std::ios::in | std::ios::binary); +} + +FilePath FilePath::StripTrailingSeparators() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + return new_path; +} + +void FilePath::StripTrailingSeparatorsInternal() { + // If there is no drive letter, start will be 1, which will prevent stripping + // the leading separator if there is only one separator. If there is a drive + // letter, start will be set appropriately to prevent stripping the first + // separator following the drive letter, if a separator immediately follows + // the drive letter. + StringType::size_type start = FindDriveLetter(path_) + 2; + + StringType::size_type last_stripped = StringType::npos; + for (StringType::size_type pos = path_.length(); + pos > start && IsSeparator(path_[pos - 1]); --pos) { + // If the string only has two separators and they're at the beginning, + // don't strip them, unless the string began with more than two separators. + if (pos != start + 1 || last_stripped == start + 2 || + !IsSeparator(path_[start - 1])) { + path_.resize(pos - 1); + last_stripped = pos; + } + } +} diff --git a/ipc/chromium/src/base/file_path.h b/ipc/chromium/src/base/file_path.h new file mode 100644 index 0000000000..3551fba6a9 --- /dev/null +++ b/ipc/chromium/src/base/file_path.h @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FilePath is a container for pathnames stored in a platform's native string +// type, providing containers for manipulation in according with the +// platform's conventions for pathnames. It supports the following path +// types: +// +// POSIX Windows +// --------------- ---------------------------------- +// Fundamental type char[] wchar_t[] +// Encoding unspecified* UTF-16 +// Separator / \, tolerant of / +// Drive letters no case-insensitive A-Z followed by : +// Alternate root // (surprise!) \\, for UNC paths +// +// * The encoding need not be specified on POSIX systems, although some +// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. +// Linux does not specify an encoding, but in practice, the locale's +// character set may be used. +// +// FilePath objects are intended to be used anywhere paths are. An +// application may pass FilePath objects around internally, masking the +// underlying differences between systems, only differing in implementation +// where interfacing directly with the system. For example, a single +// OpenFile(const FilePath &) function may be made available, allowing all +// callers to operate without regard to the underlying implementation. On +// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might +// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This +// allows each platform to pass pathnames around without requiring conversions +// between encodings, which has an impact on performance, but more imporantly, +// has an impact on correctness on platforms that do not have well-defined +// encodings for pathnames. +// +// Several methods are available to perform common operations on a FilePath +// object, such as determining the parent directory (DirName), isolating the +// final path component (BaseName), and appending a relative pathname string +// to an existing FilePath object (Append). These methods are highly +// recommended over attempting to split and concatenate strings directly. +// These methods are based purely on string manipulation and knowledge of +// platform-specific pathname conventions, and do not consult the filesystem +// at all, making them safe to use without fear of blocking on I/O operations. +// These methods do not function as mutators but instead return distinct +// instances of FilePath objects, and are therefore safe to use on const +// objects. The objects themselves are safe to share between threads. +// +// To aid in initialization of FilePath objects from string literals, a +// FILE_PATH_LITERAL macro is provided, which accounts for the difference +// between char[]-based pathnames on POSIX systems and wchar_t[]-based +// pathnames on Windows. +// +// Because a FilePath object should not be instantiated at the global scope, +// instead, use a FilePath::CharType[] and initialize it with +// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the +// character array. Example: +// +// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); +// | +// | void Function() { +// | FilePath log_file_path(kLogFileName); +// | [...] +// | } + +#ifndef BASE_FILE_PATH_H_ +#define BASE_FILE_PATH_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" + +// Windows-style drive letter support and pathname separator characters can be +// enabled and disabled independently, to aid testing. These #defines are +// here so that the same setting can be used in both the implementation and +// in the unit test. +#if defined(XP_WIN) +# define FILE_PATH_USES_DRIVE_LETTERS +# define FILE_PATH_USES_WIN_SEPARATORS +#endif // XP_WIN + +// An abstraction to isolate users from the differences between native +// pathnames on different platforms. +class FilePath { + public: +#if defined(XP_UNIX) + // On most platforms, native pathnames are char arrays, and the encoding + // may or may not be specified. On Mac OS X, native pathnames are encoded + // in UTF-8. + typedef std::string StringType; +#else + // On Windows, for Unicode-aware applications, native pathnames are wchar_t + // arrays encoded in UTF-16. + typedef std::wstring StringType; +#endif + + typedef StringType::value_type CharType; + + // Null-terminated array of separators used to separate components in + // hierarchical paths. Each character in this array is a valid separator, + // but kSeparators[0] is treated as the canonical separator and will be used + // when composing pathnames. + static const CharType kSeparators[]; + + // A special path component meaning "this directory." + static const CharType kCurrentDirectory[]; + + // A special path component meaning "the parent directory." + static const CharType kParentDirectory[]; + + // The character used to identify a file extension. + static const CharType kExtensionSeparator; + + FilePath() {} + FilePath(const FilePath& that) : path_(that.path_) {} + explicit FilePath(const StringType& path) : path_(path) {} + +#if defined(XP_WIN) + explicit FilePath(const wchar_t* path) : path_(path) {} +#endif + + FilePath& operator=(const FilePath& that) { + path_ = that.path_; + return *this; + } + + bool operator==(const FilePath& that) const { return path_ == that.path_; } + + bool operator!=(const FilePath& that) const { return path_ != that.path_; } + + // Required for some STL containers and operations + bool operator<(const FilePath& that) const { return path_ < that.path_; } + + const StringType& value() const { return path_; } + + bool empty() const { return path_.empty(); } + + // Returns true if |character| is in kSeparators. + static bool IsSeparator(CharType character); + + // Returns a FilePath corresponding to the directory containing the path + // named by this object, stripping away the file component. If this object + // only contains one component, returns a FilePath identifying + // kCurrentDirectory. If this object already refers to the root directory, + // returns a FilePath identifying the root directory. + FilePath DirName() const; + + // Returns a FilePath corresponding to the last path component of this + // object, either a file or a directory. If this object already refers to + // the root directory, returns a FilePath identifying the root directory; + // this is the only situation in which BaseName will return an absolute path. + FilePath BaseName() const; + + // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if + // the file has no extension. If non-empty, Extension() will always start + // with precisely one ".". The following code should always work regardless + // of the value of path. + // new_path = path.RemoveExtension().value().append(path.Extension()); + // ASSERT(new_path == path.value()); + // NOTE: this is different from the original file_util implementation which + // returned the extension without a leading "." ("jpg" instead of ".jpg") + StringType Extension() const; + + // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" + // NOTE: this is slightly different from the similar file_util implementation + // which returned simply 'jojo'. + FilePath RemoveExtension() const; + + // Inserts |suffix| after the file name portion of |path| but before the + // extension. Returns "" if BaseName() == "." or "..". + // Examples: + // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg" + // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg" + // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)" + // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)" + FilePath InsertBeforeExtension(const StringType& suffix) const; + + // Replaces the extension of |file_name| with |extension|. If |file_name| + // does not have an extension, them |extension| is added. If |extension| is + // empty, then the extension is removed from |file_name|. + // Returns "" if BaseName() == "." or "..". + FilePath ReplaceExtension(const StringType& extension) const; + + // Returns a FilePath by appending a separator and the supplied path + // component to this object's path. Append takes care to avoid adding + // excessive separators if this object's path already ends with a separator. + // If this object's path is kCurrentDirectory, a new FilePath corresponding + // only to |component| is returned. |component| must be a relative path; + // it is an error to pass an absolute path. + FilePath Append(const StringType& component) const WARN_UNUSED_RESULT; + FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT; + + // Although Windows StringType is std::wstring, since the encoding it uses for + // paths is well defined, it can handle ASCII path components as well. + // Mac uses UTF8, and since ASCII is a subset of that, it works there as well. + // On Linux, although it can use any 8-bit encoding for paths, we assume that + // ASCII is a valid subset, regardless of the encoding, since many operating + // system paths will always be ASCII. + FilePath AppendASCII(const std::string& component) const WARN_UNUSED_RESULT; + + // Returns true if this FilePath contains an absolute path. On Windows, an + // absolute path begins with either a drive letter specification followed by + // a separator character, or with two separator characters. On POSIX + // platforms, an absolute path begins with a separator character. + bool IsAbsolute() const; + + // Returns a copy of this FilePath that does not end with a trailing + // separator. + FilePath StripTrailingSeparators() const; + + // Calls open on given ifstream instance + void OpenInputStream(std::ifstream& stream) const; + + // Older Chromium code assumes that paths are always wstrings. + // This function converts a wstring to a FilePath, and is useful to smooth + // porting that old code to the FilePath API. + // It has "Hack" in its name so people feel bad about using it. + // TODO(port): remove these functions. + static FilePath FromWStringHack(const std::wstring& wstring); + + // Older Chromium code assumes that paths are always wstrings. + // This function produces a wstring from a FilePath, and is useful to smooth + // porting that old code to the FilePath API. + // It has "Hack" in its name so people feel bad about using it. + // TODO(port): remove these functions. + std::wstring ToWStringHack() const; + + private: + // Remove trailing separators from this object. If the path is absolute, it + // will never be stripped any more than to refer to the absolute root + // directory, so "////" will become "/", not "". A leading pair of + // separators is never stripped, to support alternate roots. This is used to + // support UNC paths on Windows. + void StripTrailingSeparatorsInternal(); + + StringType path_; +}; + +// Macros for string literal initialization of FilePath::CharType[]. +#if defined(XP_UNIX) +# define FILE_PATH_LITERAL(x) x +#else +# define FILE_PATH_LITERAL(x) L##x +#endif + +#endif // BASE_FILE_PATH_H_ diff --git a/ipc/chromium/src/base/histogram.cc b/ipc/chromium/src/base/histogram.cc new file mode 100644 index 0000000000..1ddb788b41 --- /dev/null +++ b/ipc/chromium/src/base/histogram.cc @@ -0,0 +1,638 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Histogram is an object that aggregates statistics, and can summarize them in +// various forms, including ASCII graphical, HTML, and numerically (as a +// vector of numbers corresponding to each of the aggregating buckets). +// See header file for details and examples. + +#include "base/histogram.h" + +#include <math.h> + +#include <algorithm> +#include <string> + +#include "base/logging.h" +#include "base/pickle.h" +#include "base/string_util.h" +#include "base/logging.h" + +namespace base { + +#define CHECK_GT DCHECK_GT +#define CHECK_LT DCHECK_LT + +// Static table of checksums for all possible 8 bit bytes. +const uint32_t Histogram::kCrcTable[256] = { + 0x0, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x76dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x9b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x1db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x6b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0xf00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x86d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x3b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x4db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0xd6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0xa00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x26d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x5005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0xcb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0xbdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL, +}; + +typedef Histogram::Count Count; + +// static +const size_t Histogram::kBucketCount_MAX = 16384u; + +Histogram* Histogram::FactoryGet(Sample minimum, Sample maximum, + size_t bucket_count, Flags flags, + const int* buckets) { + DCHECK(buckets); + Histogram* histogram(NULL); + + // Defensive code. + if (minimum < 1) minimum = 1; + if (maximum > kSampleType_MAX - 1) maximum = kSampleType_MAX - 1; + + histogram = new Histogram(minimum, maximum, bucket_count); + histogram->InitializeBucketRangeFromData(buckets); + histogram->SetFlags(flags); + + DCHECK_EQ(HISTOGRAM, histogram->histogram_type()); + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + return histogram; +} + +void Histogram::Add(int value) { + if (value > kSampleType_MAX - 1) value = kSampleType_MAX - 1; + if (value < 0) value = 0; + size_t index = BucketIndex(value); + DCHECK_GE(value, ranges(index)); + DCHECK_LT(value, ranges(index + 1)); + Accumulate(value, 1, index); +} + +void Histogram::Subtract(int value) { + if (value > kSampleType_MAX - 1) value = kSampleType_MAX - 1; + if (value < 0) value = 0; + size_t index = BucketIndex(value); + DCHECK_GE(value, ranges(index)); + DCHECK_LT(value, ranges(index + 1)); + Accumulate(value, -1, index); +} + +void Histogram::AddBoolean(bool value) { DCHECK(false); } + +void Histogram::AddSampleSet(const SampleSet& sample) { sample_.Add(sample); } + +void Histogram::Clear() { sample_.Clear(); } + +void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) { + DCHECK(false); +} + +//------------------------------------------------------------------------------ +// Methods for the validating a sample and a related histogram. +//------------------------------------------------------------------------------ + +Histogram::Inconsistencies Histogram::FindCorruption( + const SampleSet& snapshot) const { + int inconsistencies = NO_INCONSISTENCIES; + Sample previous_range = -1; // Bottom range is always 0. + int64_t count = 0; + for (size_t index = 0; index < bucket_count(); ++index) { + count += snapshot.counts(index); + int new_range = ranges(index); + if (previous_range >= new_range) inconsistencies |= BUCKET_ORDER_ERROR; + previous_range = new_range; + } + + if (!HasValidRangeChecksum()) inconsistencies |= RANGE_CHECKSUM_ERROR; + + int64_t delta64 = snapshot.redundant_count() - count; + if (delta64 != 0) { + int delta = static_cast<int>(delta64); + if (delta != delta64) delta = INT_MAX; // Flag all giant errors as INT_MAX. + // Since snapshots of histograms are taken asynchronously relative to + // sampling (and snapped from different threads), it is pretty likely that + // we'll catch a redundant count that doesn't match the sample count. We + // allow for a certain amount of slop before flagging this as an + // inconsistency. Even with an inconsistency, we'll snapshot it again (for + // UMA in about a half hour, so we'll eventually get the data, if it was + // not the result of a corruption. If histograms show that 1 is "too tight" + // then we may try to use 2 or 3 for this slop value. + const int kCommonRaceBasedCountMismatch = 1; + if (delta > 0) { + if (delta > kCommonRaceBasedCountMismatch) + inconsistencies |= COUNT_HIGH_ERROR; + } else { + DCHECK_GT(0, 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_; } + +Histogram::SampleSet Histogram::SnapshotSample() const { + return sample_.Clone(); +} + +bool Histogram::HasConstructorArguments(Sample minimum, Sample maximum, + size_t bucket_count) { + return ((minimum == declared_min_) && (maximum == declared_max_) && + (bucket_count == bucket_count_)); +} + +bool Histogram::HasConstructorTimeDeltaArguments(TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count) { + return ((minimum.InMilliseconds() == declared_min_) && + (maximum.InMilliseconds() == declared_max_) && + (bucket_count == bucket_count_)); +} + +bool Histogram::HasValidRangeChecksum() const { + return CalculateRangeChecksum() == range_checksum_; +} + +size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) { + size_t n = 0; + n += aMallocSizeOf(this); + n += sample_.SizeOfExcludingThis(aMallocSizeOf); + return n; +} + +size_t Histogram::SampleSet::SizeOfExcludingThis( + mozilla::MallocSizeOf aMallocSizeOf) { + return counts_.ShallowSizeOfExcludingThis(aMallocSizeOf); +} + +Histogram::Histogram(Sample minimum, Sample maximum, size_t bucket_count) + : declared_min_(minimum), + declared_max_(maximum), + bucket_count_(bucket_count), + flags_(kNoFlags), + range_checksum_(0) { + Initialize(); +} + +Histogram::Histogram(TimeDelta minimum, TimeDelta maximum, size_t bucket_count) + : declared_min_(static_cast<int>(minimum.InMilliseconds())), + declared_max_(static_cast<int>(maximum.InMilliseconds())), + bucket_count_(bucket_count), + flags_(kNoFlags), + range_checksum_(0) { + Initialize(); +} + +Histogram::~Histogram() { + // Just to make sure most derived class did this properly... + DCHECK(ValidateBucketRanges()); +} + +void Histogram::InitializeBucketRangeFromData(const int* buckets) { + ranges_ = buckets; + ResetRangeChecksum(); + DCHECK(ValidateBucketRanges()); +} + +bool Histogram::PrintEmptyBucket(size_t index) const { return true; } + +size_t Histogram::BucketIndex(Sample value) const { + // Use simple binary search. This is very general, but there are better + // approaches if we knew that the buckets were linearly distributed. + DCHECK_LE(ranges(0), value); + DCHECK_GT(ranges(bucket_count()), value); + size_t under = 0; + size_t over = bucket_count(); + size_t mid; + + do { + DCHECK_GE(over, under); + mid = under + (over - under) / 2; + if (mid == under) break; + if (ranges(mid) <= value) + under = mid; + else + over = mid; + } while (true); + + DCHECK_LE(ranges(mid), value); + CHECK_GT(ranges(mid + 1), value); + return mid; +} + +// Use the actual bucket widths (like a linear histogram) until the widths get +// over some transition value, and then use that transition width. Exponentials +// get so big so fast (and we don't expect to see a lot of entries in the large +// buckets), so we need this to make it possible to see what is going on and +// not have 0-graphical-height buckets. +double Histogram::GetBucketSize(Count current, size_t i) const { + DCHECK_GT(ranges(i + 1), ranges(i)); + static const double kTransitionWidth = 5; + double denominator = ranges(i + 1) - ranges(i); + if (denominator > kTransitionWidth) + denominator = kTransitionWidth; // Stop trying to normalize. + return current / denominator; +} + +void Histogram::ResetRangeChecksum() { + range_checksum_ = CalculateRangeChecksum(); +} + +const std::string Histogram::GetAsciiBucketRange(size_t i) const { + std::string result; + if (kHexRangePrintingFlag & flags_) + StringAppendF(&result, "%#x", ranges(i)); + else + StringAppendF(&result, "%d", ranges(i)); + return result; +} + +// Update histogram data with new sample. +void Histogram::Accumulate(Sample value, Count count, size_t index) { + sample_.Accumulate(value, count, index); +} + +bool Histogram::ValidateBucketRanges() const { + // Standard assertions that all bucket ranges should satisfy. + DCHECK_EQ(0, ranges_[bucket_count_ + 1]); + DCHECK_EQ(0, ranges_[0]); + DCHECK_EQ(declared_min(), ranges_[1]); + DCHECK_EQ(declared_max(), ranges_[bucket_count_ - 1]); + DCHECK_EQ(kSampleType_MAX, ranges_[bucket_count_]); + return true; +} + +uint32_t Histogram::CalculateRangeChecksum() const { + DCHECK_EQ(0, ranges_[bucket_count_ + 1]); + uint32_t checksum = + static_cast<uint32_t>(bucket_count_ + 1); // Seed checksum. + for (size_t index = 0; index < bucket_count(); ++index) + checksum = Crc32(checksum, ranges(index)); + return checksum; +} + +void Histogram::Initialize() { + sample_.Resize(*this); + if (declared_min_ < 1) declared_min_ = 1; + if (declared_max_ > kSampleType_MAX - 1) declared_max_ = kSampleType_MAX - 1; + DCHECK_LE(declared_min_, declared_max_); + DCHECK_GT(bucket_count_, 1u); + CHECK_LT(bucket_count_, kBucketCount_MAX); + size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; + DCHECK_LE(bucket_count_, maximal_bucket_count); +} + +// We generate the CRC-32 using the low order bits to select whether to XOR in +// the reversed polynomial 0xedb88320L. This is nice and simple, and allows us +// to keep the quotient in a uint32_t. Since we're not concerned about the +// nature of corruptions (i.e., we don't care about bit sequencing, since we are +// handling memory changes, which are more grotesque) so we don't bother to +// get the CRC correct for big-endian vs little-ending calculations. All we +// need is a nice hash, that tends to depend on all the bits of the sample, with +// very little chance of changes in one place impacting changes in another +// place. +uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) { + const bool kUseRealCrc = true; // TODO(jar): Switch to false and watch stats. + if (kUseRealCrc) { + union { + Histogram::Sample range; + unsigned char bytes[sizeof(Histogram::Sample)]; + } converter; + converter.range = range; + for (size_t i = 0; i < sizeof(converter); ++i) + sum = kCrcTable[(sum & 0xff) ^ converter.bytes[i]] ^ (sum >> 8); + } else { + // Use hash techniques provided in ReallyFastHash, except we don't care + // about "avalanching" (which would worsten the hash, and add collisions), + // and we don't care about edge cases since we have an even number of bytes. + union { + Histogram::Sample range; + uint16_t ints[sizeof(Histogram::Sample) / 2]; + } converter; + DCHECK_EQ(sizeof(Histogram::Sample), sizeof(converter)); + converter.range = range; + sum += converter.ints[0]; + sum = (sum << 16) ^ sum ^ (static_cast<uint32_t>(converter.ints[1]) << 11); + sum += sum >> 11; + } + return sum; +} + +//------------------------------------------------------------------------------ +// Private methods + +double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { + double max = 0; + for (size_t i = 0; i < bucket_count(); ++i) { + double current_size = GetBucketSize(snapshot.counts(i), i); + if (current_size > max) max = current_size; + } + return max; +} + +//------------------------------------------------------------------------------ +// Methods for the Histogram::SampleSet class +//------------------------------------------------------------------------------ + +Histogram::SampleSet::SampleSet() : sum_(0), redundant_count_(0) {} + +Histogram::SampleSet::~SampleSet() = default; + +void Histogram::SampleSet::Resize(const Histogram& histogram) { + size_t oldSize = counts_.Length(); + counts_.SetLength(histogram.bucket_count()); + + for (size_t i = oldSize; i < histogram.bucket_count(); ++i) counts_[i] = 0; +} + +void Histogram::SampleSet::Accumulate(Sample value, Count count, size_t index) { + DCHECK(count == 1 || count == -1); + counts_[index] += count; + redundant_count_ += count; + sum_ += static_cast<int64_t>(count) * value; + DCHECK_GE(counts_[index], 0); + DCHECK_GE(sum_, 0); + DCHECK_GE(redundant_count_, 0); +} + +Count Histogram::SampleSet::TotalCount() const { + Count total = 0; + for (Counts::const_iterator it = counts_.begin(); it != counts_.end(); ++it) { + total += *it; + } + return total; +} + +void Histogram::SampleSet::Add(const SampleSet& other) { + DCHECK_EQ(counts_.Length(), other.counts_.Length()); + sum_ += other.sum_; + redundant_count_ += other.redundant_count_; + for (size_t index = 0; index < counts_.Length(); ++index) + counts_[index] += other.counts_[index]; +} + +//------------------------------------------------------------------------------ +// LinearHistogram: This histogram uses a traditional set of evenly spaced +// buckets. +//------------------------------------------------------------------------------ + +LinearHistogram::~LinearHistogram() = default; + +Histogram* LinearHistogram::FactoryGet(Sample minimum, Sample maximum, + size_t bucket_count, Flags flags, + const int* buckets) { + Histogram* histogram(NULL); + + if (minimum < 1) minimum = 1; + if (maximum > kSampleType_MAX - 1) maximum = kSampleType_MAX - 1; + + LinearHistogram* linear_histogram = + new LinearHistogram(minimum, maximum, bucket_count); + linear_histogram->InitializeBucketRangeFromData(buckets); + linear_histogram->SetFlags(flags); + histogram = linear_histogram; + + DCHECK_EQ(LINEAR_HISTOGRAM, histogram->histogram_type()); + DCHECK(histogram->HasConstructorArguments(minimum, maximum, bucket_count)); + return histogram; +} + +Histogram::ClassType LinearHistogram::histogram_type() const { + return LINEAR_HISTOGRAM; +} + +void LinearHistogram::Accumulate(Sample value, Count count, size_t index) { + sample_.Accumulate(value, count, index); +} + +void LinearHistogram::SetRangeDescriptions( + const DescriptionPair descriptions[]) { + for (int i = 0; descriptions[i].description; ++i) { + bucket_description_[descriptions[i].sample] = descriptions[i].description; + } +} + +LinearHistogram::LinearHistogram(Sample minimum, Sample maximum, + size_t bucket_count) + : Histogram(minimum >= 1 ? minimum : 1, maximum, bucket_count) {} + +LinearHistogram::LinearHistogram(TimeDelta minimum, TimeDelta maximum, + size_t bucket_count) + : Histogram(minimum >= TimeDelta::FromMilliseconds(1) + ? minimum + : TimeDelta::FromMilliseconds(1), + maximum, bucket_count) {} + +double LinearHistogram::GetBucketSize(Count current, size_t i) const { + DCHECK_GT(ranges(i + 1), ranges(i)); + // Adjacent buckets with different widths would have "surprisingly" many (few) + // samples in a histogram if we didn't normalize this way. + double denominator = ranges(i + 1) - ranges(i); + return current / denominator; +} + +const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { + int range = ranges(i); + BucketDescriptionMap::const_iterator it = bucket_description_.find(range); + if (it == bucket_description_.end()) return Histogram::GetAsciiBucketRange(i); + return it->second; +} + +bool LinearHistogram::PrintEmptyBucket(size_t index) const { + return bucket_description_.find(ranges(index)) == bucket_description_.end(); +} + +//------------------------------------------------------------------------------ +// This section provides implementation for BooleanHistogram. +//------------------------------------------------------------------------------ + +Histogram* BooleanHistogram::FactoryGet(Flags flags, const int* buckets) { + Histogram* histogram(NULL); + + BooleanHistogram* tentative_histogram = new BooleanHistogram(); + tentative_histogram->InitializeBucketRangeFromData(buckets); + tentative_histogram->SetFlags(flags); + histogram = tentative_histogram; + + DCHECK_EQ(BOOLEAN_HISTOGRAM, histogram->histogram_type()); + return histogram; +} + +Histogram::ClassType BooleanHistogram::histogram_type() const { + return BOOLEAN_HISTOGRAM; +} + +void BooleanHistogram::AddBoolean(bool value) { Add(value ? 1 : 0); } + +BooleanHistogram::BooleanHistogram() : LinearHistogram(1, 2, 3) {} + +void BooleanHistogram::Accumulate(Sample value, Count count, size_t index) { + // Callers will have computed index based on the non-booleanified value. + // So we need to adjust the index manually. + LinearHistogram::Accumulate(!!value, count, value ? 1 : 0); +} + +//------------------------------------------------------------------------------ +// FlagHistogram: +//------------------------------------------------------------------------------ + +Histogram* FlagHistogram::FactoryGet(Flags flags, const int* buckets) { + Histogram* h(nullptr); + + FlagHistogram* fh = new FlagHistogram(); + fh->InitializeBucketRangeFromData(buckets); + fh->SetFlags(flags); + size_t zero_index = fh->BucketIndex(0); + fh->LinearHistogram::Accumulate(0, 1, zero_index); + h = fh; + + return h; +} + +FlagHistogram::FlagHistogram() : mSwitched(false) {} + +Histogram::ClassType FlagHistogram::histogram_type() const { + return FLAG_HISTOGRAM; +} + +void FlagHistogram::Accumulate(Sample value, Count count, size_t index) { + if (mSwitched) { + return; + } + + mSwitched = true; + DCHECK_EQ(value, 1); + LinearHistogram::Accumulate(value, 1, index); + size_t zero_index = BucketIndex(0); + LinearHistogram::Accumulate(0, -1, zero_index); +} + +void FlagHistogram::AddSampleSet(const SampleSet& sample) { + DCHECK_EQ(bucket_count(), sample.size()); + // We can't be sure the SampleSet provided came from another FlagHistogram, + // so we take the following steps: + // - If our flag has already been set do nothing. + // - Set our flag if the following hold: + // - The sum of the counts in the provided SampleSet is 1. + // - The bucket index for that single value is the same as the index + // where we + // would place our set flag. + // - Otherwise, take no action. + + if (mSwitched) { + return; + } + + if (sample.sum() != 1) { + return; + } + + size_t one_index = BucketIndex(1); + if (sample.counts(one_index) == 1) { + Accumulate(1, 1, one_index); + } +} + +void FlagHistogram::Clear() { + Histogram::Clear(); + + mSwitched = false; + size_t zero_index = BucketIndex(0); + LinearHistogram::Accumulate(0, 1, zero_index); +} + +//------------------------------------------------------------------------------ +// CountHistogram: +//------------------------------------------------------------------------------ + +Histogram* CountHistogram::FactoryGet(Flags flags, const int* buckets) { + Histogram* h(nullptr); + + CountHistogram* fh = new CountHistogram(); + fh->InitializeBucketRangeFromData(buckets); + fh->SetFlags(flags); + h = fh; + + return h; +} + +CountHistogram::CountHistogram() : LinearHistogram(1, 2, 3) {} + +Histogram::ClassType CountHistogram::histogram_type() const { + return COUNT_HISTOGRAM; +} + +void CountHistogram::Accumulate(Sample value, Count count, size_t index) { + size_t zero_index = BucketIndex(0); + LinearHistogram::Accumulate(value, 1, zero_index); +} + +void CountHistogram::AddSampleSet(const SampleSet& sample) { + DCHECK_EQ(bucket_count(), sample.size()); + // We can't be sure the SampleSet provided came from another CountHistogram, + // so we at least check that the unused buckets are empty. + + const size_t indices[] = {BucketIndex(0), BucketIndex(1), BucketIndex(2)}; + + if (sample.counts(indices[1]) != 0 || sample.counts(indices[2]) != 0) { + return; + } + + if (sample.counts(indices[0]) != 0) { + Histogram::AddSampleSet(sample); + } +} + +} // namespace base diff --git a/ipc/chromium/src/base/histogram.h b/ipc/chromium/src/base/histogram.h new file mode 100644 index 0000000000..f9ea21c8df --- /dev/null +++ b/ipc/chromium/src/base/histogram.h @@ -0,0 +1,432 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Histogram is an object that aggregates statistics, and can summarize them in +// various forms, including ASCII graphical, HTML, and numerically (as a +// vector of numbers corresponding to each of the aggregating buckets). + +// It supports calls to accumulate either time intervals (which are processed +// as integral number of milliseconds), or arbitrary integral units. + +// The default layout of buckets is exponential. For example, buckets might +// contain (sequentially) the count of values in the following intervals: +// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity) +// That bucket allocation would actually result from construction of a histogram +// for values between 1 and 64, with 8 buckets, such as: +// Histogram count(L"some name", 1, 64, 8); +// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity) +// are not counted by the constructor in the user supplied "bucket_count" +// argument. +// The above example has an exponential ratio of 2 (doubling the bucket width +// in each consecutive bucket. The Histogram class automatically calculates +// the smallest ratio that it can use to construct the number of buckets +// selected in the constructor. An another example, if you had 50 buckets, +// and millisecond time values from 1 to 10000, then the ratio between +// consecutive bucket widths will be approximately somewhere around the 50th +// root of 10000. This approach provides very fine grain (narrow) buckets +// at the low end of the histogram scale, but allows the histogram to cover a +// gigantic range with the addition of very few buckets. + +// Histograms use a pattern involving a function static variable, that is a +// pointer to a histogram. This static is explicitly initialized on any thread +// that detects a uninitialized (NULL) pointer. The potentially racy +// initialization is not a problem as it is always set to point to the same +// value (i.e., the FactoryGet always returns the same value). FactoryGet +// is also completely thread safe, which results in a completely thread safe, +// and relatively fast, set of counters. To avoid races at shutdown, the static +// pointer is NOT deleted, and we leak the histograms at process termination. + +#ifndef BASE_METRICS_HISTOGRAM_H_ +#define BASE_METRICS_HISTOGRAM_H_ +#pragma once + +#include "mozilla/Atomics.h" +#include "mozilla/MemoryReporting.h" + +#include <map> +#include <string> + +#include "base/time.h" + +#include "nsTArray.h" + +namespace base { + +//------------------------------------------------------------------------------ + +class BooleanHistogram; +class CustomHistogram; +class Histogram; +class LinearHistogram; + +class Histogram { + public: + typedef int Sample; // Used for samples (and ranges of samples). + typedef int Count; // Used to count samples in a bucket. + static const Sample kSampleType_MAX = INT_MAX; + // Initialize maximum number of buckets in histograms as 16,384. + static const size_t kBucketCount_MAX; + + typedef nsTArray<Count> Counts; + typedef const Sample* Ranges; + + // These enums are used to facilitate deserialization of renderer histograms + // into the browser. + enum ClassType { + HISTOGRAM, + LINEAR_HISTOGRAM, + BOOLEAN_HISTOGRAM, + FLAG_HISTOGRAM, + COUNT_HISTOGRAM, + CUSTOM_HISTOGRAM, + NOT_VALID_IN_RENDERER + }; + + enum BucketLayout { EXPONENTIAL, LINEAR, CUSTOM }; + + enum Flags { + kNoFlags = 0, + kUmaTargetedHistogramFlag = 0x1, // Histogram should be UMA uploaded. + + kHexRangePrintingFlag = 0x8000 // Fancy bucket-naming supported. + }; + + enum Inconsistencies { + NO_INCONSISTENCIES = 0x0, + RANGE_CHECKSUM_ERROR = 0x1, + BUCKET_ORDER_ERROR = 0x2, + COUNT_HIGH_ERROR = 0x4, + COUNT_LOW_ERROR = 0x8, + + NEVER_EXCEEDED_VALUE = 0x10 + }; + + struct DescriptionPair { + Sample sample; + const char* description; // Null means end of a list of pairs. + }; + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); + + bool is_empty() const { + return this->sample_.counts(0) == 0 && this->sample_.sum() == 0; + } + + //---------------------------------------------------------------------------- + // Statistic values, developed over the life of the histogram. + + class SampleSet { + public: + explicit SampleSet(); + ~SampleSet(); + SampleSet(SampleSet&&) = default; + + // None of the methods in this class are thread-safe. Callers + // must deal with locking themselves. + + // Adjust size of counts_ for use with given histogram. + void Resize(const Histogram& histogram); + + // Accessor for histogram to make routine additions. + void Accumulate(Sample value, Count count, size_t index); + + // Arithmetic manipulation of corresponding elements of the set. + void Add(const SampleSet& other); + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf); + + Count counts(size_t i) const { return counts_[i]; } + Count TotalCount() const; + int64_t sum() const { return sum_; } + int64_t redundant_count() const { return redundant_count_; } + size_t size() const { return counts_.Length(); } + SampleSet Clone() const { + SampleSet result; + result.counts_ = counts_.Clone(); + result.sum_ = sum_; + result.redundant_count_ = redundant_count_; + return result; + } + void Clear() { + sum_ = 0; + redundant_count_ = 0; + for (int& i : counts_) { + i = 0; + } + } + + protected: + // Actual histogram data is stored in buckets, showing the count of values + // that fit into each bucket. + Counts counts_; + + // Save simple stats locally. Note that this MIGHT get done in base class + // without shared memory at some point. + int64_t sum_; // sum of samples. + + // To help identify memory corruption, we reduntantly save the number of + // samples we've accumulated into all of our buckets. We can compare this + // count to the sum of the counts in all buckets, and detect problems. Note + // that due to races in histogram accumulation (if a histogram is indeed + // updated on several threads simultaneously), the tallies might mismatch, + // and also the snapshotting code may asynchronously get a mismatch (though + // generally either race based mismatch cause is VERY rare). + int64_t redundant_count_; + }; + + //---------------------------------------------------------------------------- + // minimum should start from 1. 0 is invalid as a minimum. 0 is an implicit + // default underflow bucket. + static Histogram* FactoryGet(Sample minimum, Sample maximum, + size_t bucket_count, Flags flags, + const int* buckets); + + virtual ~Histogram(); + + void Add(int value); + void Subtract(int value); + + // This method is an interface, used only by BooleanHistogram. + virtual void AddBoolean(bool value); + + // Accept a TimeDelta to increment. + void AddTime(TimeDelta time) { Add(static_cast<int>(time.InMilliseconds())); } + + virtual void AddSampleSet(const SampleSet& sample); + + virtual void Clear(); + + // This method is an interface, used only by LinearHistogram. + virtual void SetRangeDescriptions(const DescriptionPair descriptions[]); + + // Support generic flagging of Histograms. + // 0x1 Currently used to mark this histogram to be recorded by UMA.. + // 0x8000 means print ranges in hex. + void SetFlags(Flags flags) { flags_ = static_cast<Flags>(flags_ | flags); } + void ClearFlags(Flags flags) { flags_ = static_cast<Flags>(flags_ & ~flags); } + int flags() const { return flags_; } + + // Check to see if bucket ranges, counts and tallies in the snapshot are + // consistent with the bucket ranges and checksums in our histogram. This can + // produce a false-alarm if a race occurred in the reading of the data during + // a SnapShot process, but should otherwise be false at all times (unless we + // have memory over-writes, or DRAM failures). + virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const; + + //---------------------------------------------------------------------------- + // Accessors for factory constuction, serialization and testing. + //---------------------------------------------------------------------------- + virtual ClassType histogram_type() const; + Sample declared_min() const { return declared_min_; } + Sample declared_max() const { return declared_max_; } + virtual Sample ranges(size_t i) const; + uint32_t range_checksum() const { return range_checksum_; } + virtual size_t bucket_count() const; + + virtual SampleSet SnapshotSample() const; + + virtual bool HasConstructorArguments(Sample minimum, Sample maximum, + size_t bucket_count); + + virtual bool HasConstructorTimeDeltaArguments(TimeDelta minimum, + TimeDelta maximum, + size_t bucket_count); + // Return true iff the range_checksum_ matches current ranges_ vector. + bool HasValidRangeChecksum() const; + + protected: + Histogram(Sample minimum, Sample maximum, size_t bucket_count); + Histogram(TimeDelta minimum, TimeDelta maximum, size_t bucket_count); + + // Initialize ranges_ mapping from raw data. + void InitializeBucketRangeFromData(const int* buckets); + + // Method to override to skip the display of the i'th bucket if it's empty. + virtual bool PrintEmptyBucket(size_t index) const; + + //---------------------------------------------------------------------------- + // Methods to override to create histogram with different bucket widths. + //---------------------------------------------------------------------------- + // Find bucket to increment for sample value. + virtual size_t BucketIndex(Sample value) const; + // Get normalized size, relative to the ranges_[i]. + virtual double GetBucketSize(Count current, size_t i) const; + + // Recalculate range_checksum_. + void ResetRangeChecksum(); + + // Return a string description of what goes in a given bucket. + // Most commonly this is the numeric value, but in derived classes it may + // be a name (or string description) given to the bucket. + virtual const std::string GetAsciiBucketRange(size_t it) const; + + //---------------------------------------------------------------------------- + // Methods to override to create thread safe histogram. + //---------------------------------------------------------------------------- + // Update all our internal data, including histogram + virtual void Accumulate(Sample value, Count count, size_t index); + + // Validate that ranges_ was created sensibly (top and bottom range + // values relate properly to the declared_min_ and declared_max_).. + bool ValidateBucketRanges() const; + + virtual uint32_t CalculateRangeChecksum() const; + + // Finally, provide the state that changes with the addition of each new + // sample. + SampleSet sample_; + + private: + // Post constructor initialization. + void Initialize(); + + // Checksum function for accumulating range values into a checksum. + static uint32_t Crc32(uint32_t sum, Sample range); + + //---------------------------------------------------------------------------- + // Helpers for emitting Ascii graphic. Each method appends data to output. + + // Find out how large the (graphically) the largest bucket will appear to be. + double GetPeakBucketSize(const SampleSet& snapshot) const; + + //---------------------------------------------------------------------------- + // Table for generating Crc32 values. + static const uint32_t kCrcTable[256]; + //---------------------------------------------------------------------------- + // Invariant values set at/near construction time + + Sample declared_min_; // Less than this goes into counts_[0] + Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1]. + size_t bucket_count_; // Dimension of counts_[]. + + // Flag the histogram for recording by UMA via metric_services.h. + Flags flags_; + + // For each index, show the least value that can be stored in the + // corresponding bucket. We also append one extra element in this array, + // containing kSampleType_MAX, to make calculations easy. + // The dimension of ranges_ is bucket_count + 1. + Ranges ranges_; + + // For redundancy, we store a checksum of all the sample ranges when ranges + // are generated. If ever there is ever a difference, then the histogram must + // have been corrupted. + uint32_t range_checksum_; + + DISALLOW_COPY_AND_ASSIGN(Histogram); +}; + +//------------------------------------------------------------------------------ + +// LinearHistogram is a more traditional histogram, with evenly spaced +// buckets. +class LinearHistogram : public Histogram { + public: + virtual ~LinearHistogram(); + + /* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit + default underflow bucket. */ + static Histogram* FactoryGet(Sample minimum, Sample maximum, + size_t bucket_count, Flags flags, + const int* buckets); + + // Overridden from Histogram: + virtual ClassType histogram_type() const override; + + virtual void Accumulate(Sample value, Count count, size_t index) override; + + // Store a list of number/text values for use in rendering the histogram. + // The last element in the array has a null in its "description" slot. + virtual void SetRangeDescriptions( + const DescriptionPair descriptions[]) override; + + protected: + LinearHistogram(Sample minimum, Sample maximum, size_t bucket_count); + + LinearHistogram(TimeDelta minimum, TimeDelta maximum, size_t bucket_count); + + virtual double GetBucketSize(Count current, size_t i) const override; + + // If we have a description for a bucket, then return that. Otherwise + // let parent class provide a (numeric) description. + virtual const std::string GetAsciiBucketRange(size_t i) const override; + + // Skip printing of name for numeric range if we have a name (and if this is + // an empty bucket). + virtual bool PrintEmptyBucket(size_t index) const override; + + private: + // For some ranges, we store a printable description of a bucket range. + // If there is no desciption, then GetAsciiBucketRange() uses parent class + // to provide a description. + typedef std::map<Sample, std::string> BucketDescriptionMap; + BucketDescriptionMap bucket_description_; + + DISALLOW_COPY_AND_ASSIGN(LinearHistogram); +}; + +//------------------------------------------------------------------------------ + +// BooleanHistogram is a histogram for booleans. +class BooleanHistogram : public LinearHistogram { + public: + static Histogram* FactoryGet(Flags flags, const int* buckets); + + virtual ClassType histogram_type() const override; + + virtual void AddBoolean(bool value) override; + + virtual void Accumulate(Sample value, Count count, size_t index) override; + + protected: + explicit BooleanHistogram(); + + DISALLOW_COPY_AND_ASSIGN(BooleanHistogram); +}; + +//------------------------------------------------------------------------------ + +// FlagHistogram is like boolean histogram, but only allows a single off/on +// value. +class FlagHistogram : public BooleanHistogram { + public: + static Histogram* FactoryGet(Flags flags, const int* buckets); + + virtual ClassType histogram_type() const override; + + virtual void Accumulate(Sample value, Count count, size_t index) override; + + virtual void AddSampleSet(const SampleSet& sample) override; + + virtual void Clear() override; + + private: + explicit FlagHistogram(); + bool mSwitched; + + DISALLOW_COPY_AND_ASSIGN(FlagHistogram); +}; + +// CountHistogram only allows a single monotic counter value. +class CountHistogram : public LinearHistogram { + public: + static Histogram* FactoryGet(Flags flags, const int* buckets); + + virtual ClassType histogram_type() const override; + + virtual void Accumulate(Sample value, Count count, size_t index) override; + + virtual void AddSampleSet(const SampleSet& sample) override; + + private: + explicit CountHistogram(); + + DISALLOW_COPY_AND_ASSIGN(CountHistogram); +}; + +} // namespace base + +#endif // BASE_METRICS_HISTOGRAM_H_ diff --git a/ipc/chromium/src/base/linked_ptr.h b/ipc/chromium/src/base/linked_ptr.h new file mode 100644 index 0000000000..7b428312b8 --- /dev/null +++ b/ipc/chromium/src/base/linked_ptr.h @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is released, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Thread Safety: +// A linked_ptr is NOT thread safe. Copying a linked_ptr object is +// effectively a read-write operation. +// +// Alternative: to linked_ptr is shared_ptr, which +// - is also two pointers in size (8 bytes for 32 bit addresses) +// - is thread safe for copying and deletion +// - supports weak_ptrs + +#ifndef BASE_LINKED_PTR_H_ +#define BASE_LINKED_PTR_H_ + +#include "base/logging.h" // for CHECK macros + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { next_ = this; } + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) { + next_ = ptr->next_; + ptr->next_ = this; + } + + // Leave whatever circle we're part of. Returns true iff we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() { + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template <typename T> +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template <typename U> + linked_ptr(linked_ptr<U> const& ptr) { + copy(&ptr); + } + linked_ptr(linked_ptr const& ptr) { + DCHECK_NE(&ptr, this); + copy(&ptr); + } + + // Assignment releases the old value and acquires the new. + template <typename U> + linked_ptr& operator=(linked_ptr<U> const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { + depart(); + capture(ptr); + } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + // Release ownership of the pointed object and returns it. + // Sole ownership by this linked_ptr object is required. + T* release() { + bool last = link_.depart(); + CHECK(last); + T* v = value_; + value_ = NULL; + return v; + } + + bool operator==(const T* p) const { return value_ == p; } + bool operator!=(const T* p) const { return value_ != p; } + template <typename U> + bool operator==(linked_ptr<U> const& ptr) const { + return value_ == ptr.get(); + } + template <typename U> + bool operator!=(linked_ptr<U> const& ptr) const { + return value_ != ptr.get(); + } + + private: + template <typename U> + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template <typename U> + void copy(linked_ptr<U> const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template <typename T> +inline bool operator==(T* ptr, const linked_ptr<T>& x) { + return ptr == x.get(); +} + +template <typename T> +inline bool operator!=(T* ptr, const linked_ptr<T>& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr<T> +// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation +// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg)) +template <typename T> +linked_ptr<T> make_linked_ptr(T* ptr) { + return linked_ptr<T>(ptr); +} + +#endif // BASE_LINKED_PTR_H_ diff --git a/ipc/chromium/src/base/linux_memfd_defs.h b/ipc/chromium/src/base/linux_memfd_defs.h new file mode 100644 index 0000000000..f5b0de1de8 --- /dev/null +++ b/ipc/chromium/src/base/linux_memfd_defs.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BASE_LINUX_MEMFD_DEFS_H +#define BASE_LINUX_MEMFD_DEFS_H + +#include <sys/syscall.h> + +// glibc before 2.27 didn't have a memfd_create wrapper, and if the +// build system is old enough then it won't have the syscall number +// and various related constants either. + +#if defined(__x86_64__) +# define MEMFD_CREATE_NR 319 +#elif defined(__i386__) +# define MEMFD_CREATE_NR 356 +#elif defined(__aarch64__) +# define MEMFD_CREATE_NR 279 +#elif defined(__arm__) +# define MEMFD_CREATE_NR 385 +#elif defined(__powerpc__) +# define MEMFD_CREATE_NR 360 +#elif defined(__s390__) +# define MEMFD_CREATE_NR 350 +#elif defined(__mips__) +# include <sgidefs.h> +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define MEMFD_CREATE_NR 4354 +# elif _MIPS_SIM == _MIPS_SIM_ABI64 +# define MEMFD_CREATE_NR 5314 +# elif _MIPS_SIM == _MIPS_SIM_NABI32 +# define MEMFD_CREATE_NR 6318 +# endif // mips subarch +#endif // arch + +#ifdef MEMFD_CREATE_NR +# ifdef SYS_memfd_create +static_assert(MEMFD_CREATE_NR == SYS_memfd_create, + "MEMFD_CREATE_NR should match the actual SYS_memfd_create value"); +# else // defined here but not in system headers +# define SYS_memfd_create MEMFD_CREATE_NR +# endif +#endif + +#ifndef MFD_CLOEXEC +# define MFD_CLOEXEC 0x0001U +# define MFD_ALLOW_SEALING 0x0002U +#endif + +#ifndef F_ADD_SEALS +# ifndef F_LINUX_SPECIFIC_BASE +# define F_LINUX_SPECIFIC_BASE 1024 +# endif +# define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +# define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) +# define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ +# define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ +# define F_SEAL_GROW 0x0004 /* prevent file from growing */ +# define F_SEAL_WRITE 0x0008 /* prevent writes */ +#endif + +#ifndef F_SEAL_FUTURE_WRITE +# define F_SEAL_FUTURE_WRITE 0x0010 +#endif + +#endif // BASE_LINUX_MEMFD_DEFS_H diff --git a/ipc/chromium/src/base/lock.h b/ipc/chromium/src/base/lock.h new file mode 100644 index 0000000000..085e70e725 --- /dev/null +++ b/ipc/chromium/src/base/lock.h @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LOCK_H_ +#define BASE_LOCK_H_ + +#include "base/basictypes.h" +#include "base/lock_impl.h" +#include "base/platform_thread.h" + +// A convenient wrapper for an OS specific critical section. +class Lock { + public: + // Optimized wrapper implementation + Lock() = default; + ~Lock() = default; + void Acquire() { lock_.Lock(); } + void Release() { lock_.Unlock(); } + + // If the lock is not held, take it and return true. If the lock is already + // held by another thread, immediately return false. This must not be called + // by a thread already holding the lock (what happens is undefined and an + // assertion may fail). + bool Try() { return lock_.Try(); } + + // Null implementation if not debug. + void AssertAcquired() const {} + + // Whether Lock mitigates priority inversion when used from different thread + // priorities. + static bool HandlesMultipleThreadPriorities() { +#if defined(XP_UNIX) + // POSIX mitigates priority inversion by setting the priority of a thread + // holding a Lock to the maximum priority of any other thread waiting on it. + return base::internal::LockImpl::PriorityInheritanceAvailable(); +#else + // Windows mitigates priority inversion by randomly boosting the priority of + // ready threads. + // https://msdn.microsoft.com/library/windows/desktop/ms684831.aspx + return true; +#endif + } + + // Both Windows and POSIX implementations of ConditionVariable need to be + // able to see our lock and tweak our debugging counters, as they release and + // acquire locks inside of their condition variable APIs. + friend class ConditionVariable; + + private: + // Platform specific underlying lock implementation. + ::base::internal::LockImpl lock_; + + DISALLOW_COPY_AND_ASSIGN(Lock); +}; + +// A helper class that acquires the given Lock while the AutoLock is in scope. +class AutoLock { + public: + struct AlreadyAcquired {}; + + explicit AutoLock(Lock& lock) : lock_(lock) { lock_.Acquire(); } + + AutoLock(Lock& lock, const AlreadyAcquired&) : lock_(lock) { + lock_.AssertAcquired(); + } + + ~AutoLock() { + lock_.AssertAcquired(); + lock_.Release(); + } + + private: + Lock& lock_; + DISALLOW_COPY_AND_ASSIGN(AutoLock); +}; + +// AutoUnlock is a helper that will Release() the |lock| argument in the +// constructor, and re-Acquire() it in the destructor. +class AutoUnlock { + public: + explicit AutoUnlock(Lock& lock) : lock_(lock) { + // We require our caller to have the lock. + lock_.AssertAcquired(); + lock_.Release(); + } + + ~AutoUnlock() { lock_.Acquire(); } + + private: + Lock& lock_; + DISALLOW_COPY_AND_ASSIGN(AutoUnlock); +}; + +#endif // BASE_LOCK_H_ diff --git a/ipc/chromium/src/base/lock_impl.h b/ipc/chromium/src/base/lock_impl.h new file mode 100644 index 0000000000..95588bcca2 --- /dev/null +++ b/ipc/chromium/src/base/lock_impl.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 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_IMPL_H_ +#define BASE_LOCK_IMPL_H_ + +#include "base/basictypes.h" + +#if defined(XP_WIN) +# include <windows.h> +#else +# include <pthread.h> +#endif + +namespace base { +namespace internal { + +// This class implements the underlying platform-specific spin-lock mechanism +// used for the Lock class. Most users should not use LockImpl directly, but +// should instead use Lock. +class LockImpl { + public: +#if defined(XP_WIN) + using NativeHandle = SRWLOCK; +#else + using NativeHandle = pthread_mutex_t; +#endif + + LockImpl(); + ~LockImpl(); + + // If the lock is not held, take it and return true. If the lock is already + // held by something else, immediately return false. + bool Try(); + + // Take the lock, blocking until it is available if necessary. + void Lock(); + + // Release the lock. This must only be called by the lock's holder: after + // a successful call to Try, or a call to Lock. + void Unlock(); + + // Return the native underlying lock. + // TODO(awalker): refactor lock and condition variables so that this is + // unnecessary. + NativeHandle* native_handle() { return &native_handle_; } + +#if defined(XP_UNIX) + // Whether this lock will attempt to use priority inheritance. + static bool PriorityInheritanceAvailable(); +#endif + + private: + NativeHandle native_handle_; + + DISALLOW_COPY_AND_ASSIGN(LockImpl); +}; + +} // namespace internal +} // namespace base + +#endif // BASE_LOCK_IMPL_H_ diff --git a/ipc/chromium/src/base/lock_impl_posix.cc b/ipc/chromium/src/base/lock_impl_posix.cc new file mode 100644 index 0000000000..a4a2ce87b6 --- /dev/null +++ b/ipc/chromium/src/base/lock_impl_posix.cc @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/lock_impl.h" + +#include <errno.h> +#include <string.h> + +#include "base/logging.h" +#include "base/lock.h" + +namespace base { +namespace internal { + +// Determines which platforms can consider using priority inheritance locks. Use +// this define for platform code that may not compile if priority inheritance +// locks aren't available. For this platform code, +// PRIORITY_INHERITANCE_LOCKS_POSSIBLE() is a necessary but insufficient check. +// Lock::PriorityInheritanceAvailable still must be checked as the code may +// compile but the underlying platform still may not correctly support priority +// inheritance locks. +#if defined(ANDROID) +# define PRIORITY_INHERITANCE_LOCKS_POSSIBLE() 0 +#else +# define PRIORITY_INHERITANCE_LOCKS_POSSIBLE() 1 +#endif + +LockImpl::LockImpl() { + pthread_mutexattr_t mta; + int rv = pthread_mutexattr_init(&mta); + DCHECK_EQ(rv, 0) << ". " << strerror(rv); +#if PRIORITY_INHERITANCE_LOCKS_POSSIBLE() + if (PriorityInheritanceAvailable()) { + rv = pthread_mutexattr_setprotocol(&mta, PTHREAD_PRIO_INHERIT); + DCHECK_EQ(rv, 0) << ". " << strerror(rv); + } +#endif +#ifndef NDEBUG + // In debug, setup attributes for lock error checking. + rv = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK); + DCHECK_EQ(rv, 0) << ". " << strerror(rv); +#endif + rv = pthread_mutex_init(&native_handle_, &mta); + DCHECK_EQ(rv, 0) << ". " << strerror(rv); + rv = pthread_mutexattr_destroy(&mta); + DCHECK_EQ(rv, 0) << ". " << strerror(rv); +} + +LockImpl::~LockImpl() { + int rv = pthread_mutex_destroy(&native_handle_); + DCHECK_EQ(rv, 0) << ". " << strerror(rv); +} + +bool LockImpl::Try() { + int rv = pthread_mutex_trylock(&native_handle_); + DCHECK(rv == 0 || rv == EBUSY) << ". " << strerror(rv); + return rv == 0; +} + +void LockImpl::Lock() { + int rv = pthread_mutex_lock(&native_handle_); + DCHECK_EQ(rv, 0) << ". " << strerror(rv); +} + +void LockImpl::Unlock() { + int rv = pthread_mutex_unlock(&native_handle_); + DCHECK_EQ(rv, 0) << ". " << strerror(rv); +} + +// static +bool LockImpl::PriorityInheritanceAvailable() { +#if PRIORITY_INHERITANCE_LOCKS_POSSIBLE() && defined(XP_DARWIN) + return true; +#else + // Security concerns prevent the use of priority inheritance mutexes on Linux. + // * CVE-2010-0622 - wake_futex_pi unlocks incorrect, possible DoS. + // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0622 + // * CVE-2012-6647 - Linux < 3.5.1, futex_wait_requeue_pi possible DoS. + // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2012-6647 + // * CVE-2014-3153 - Linux <= 3.14.5, futex_requeue, privilege escalation. + // https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3153 + // + // If the above were all addressed, we still need a runtime check to deal with + // the bug below. + // * glibc Bug 14652: https://sourceware.org/bugzilla/show_bug.cgi?id=14652 + // Fixed in glibc 2.17. + // Priority inheritance mutexes may deadlock with condition variables + // during recacquisition of the mutex after the condition variable is + // signalled. + return false; +#endif +} + +} // namespace internal +} // namespace base diff --git a/ipc/chromium/src/base/lock_impl_win.cc b/ipc/chromium/src/base/lock_impl_win.cc new file mode 100644 index 0000000000..fc16b3a464 --- /dev/null +++ b/ipc/chromium/src/base/lock_impl_win.cc @@ -0,0 +1,23 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/lock_impl.h" + +namespace base { +namespace internal { + +LockImpl::LockImpl() : native_handle_(SRWLOCK_INIT) {} + +LockImpl::~LockImpl() = default; + +bool LockImpl::Try() { return !!::TryAcquireSRWLockExclusive(&native_handle_); } + +void LockImpl::Lock() { ::AcquireSRWLockExclusive(&native_handle_); } + +void LockImpl::Unlock() { ::ReleaseSRWLockExclusive(&native_handle_); } + +} // namespace internal +} // namespace base diff --git a/ipc/chromium/src/base/logging.cc b/ipc/chromium/src/base/logging.cc new file mode 100644 index 0000000000..217ae80844 --- /dev/null +++ b/ipc/chromium/src/base/logging.cc @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" + +#include <utility> + +#include "base/string_util.h" +#include "nsXPCOM.h" + +namespace mozilla { + +Logger::~Logger() { + LogLevel prlevel = LogLevel::Debug; + int xpcomlevel = -1; + + switch (mSeverity) { + case LOG_INFO: + prlevel = LogLevel::Debug; + xpcomlevel = -1; + break; + + case LOG_WARNING: + prlevel = LogLevel::Warning; + xpcomlevel = NS_DEBUG_WARNING; + break; + + case LOG_ERROR: + prlevel = LogLevel::Error; + xpcomlevel = NS_DEBUG_WARNING; + break; + + case LOG_ERROR_REPORT: + prlevel = LogLevel::Error; + xpcomlevel = NS_DEBUG_ASSERTION; + break; + + case LOG_FATAL: + prlevel = LogLevel::Error; + xpcomlevel = NS_DEBUG_ABORT; + break; + } + + MOZ_LOG(gChromiumPRLog, prlevel, + ("%s:%i: %s", mFile, mLine, mMsg ? mMsg.get() : "<no message>")); + if (xpcomlevel != -1) + NS_DebugBreak(xpcomlevel, mMsg.get(), NULL, mFile, mLine); +} + +void Logger::printf(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + mMsg = mozilla::VsmprintfAppend(std::move(mMsg), fmt, args); + va_end(args); +} + +LazyLogModule Logger::gChromiumPRLog("chromium"); + +mozilla::Logger& operator<<(mozilla::Logger& log, const char* s) { + log.printf("%s", s); + return log; +} + +mozilla::Logger& operator<<(mozilla::Logger& log, const std::string& s) { + log.printf("%s", s.c_str()); + return log; +} + +mozilla::Logger& operator<<(mozilla::Logger& log, int i) { + log.printf("%i", i); + return log; +} + +mozilla::Logger& operator<<(mozilla::Logger& log, const std::wstring& s) { + log.printf("%s", WideToASCII(s).c_str()); + return log; +} + +mozilla::Logger& operator<<(mozilla::Logger& log, void* p) { + log.printf("%p", p); + return log; +} + +} // namespace mozilla diff --git a/ipc/chromium/src/base/logging.h b/ipc/chromium/src/base/logging.h new file mode 100644 index 0000000000..4313f131b3 --- /dev/null +++ b/ipc/chromium/src/base/logging.h @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LOGGING_H_ +#define BASE_LOGGING_H_ + +#include <string> +#include <cstring> + +#include "base/basictypes.h" +#include "mozilla/Attributes.h" +#include "mozilla/Logging.h" +#include "mozilla/Printf.h" + +#ifdef NO_CHROMIUM_LOGGING +# include <sstream> +#endif + +// Replace the Chromium logging code with NSPR-based logging code and +// some C++ wrappers to emulate std::ostream + +namespace mozilla { + +enum LogSeverity { + LOG_INFO, + LOG_WARNING, + LOG_ERROR, + LOG_ERROR_REPORT, + LOG_FATAL, + LOG_0 = LOG_ERROR +}; + +class Logger { + public: + Logger(LogSeverity severity, const char* file, int line) + : mSeverity(severity), mFile(file), mLine(line) {} + + ~Logger(); + + // not private so that the operator<< overloads can get to it + void printf(const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3); + + private: + static mozilla::LazyLogModule gChromiumPRLog; + // static PRLogModuleInfo* GetLog(); + + LogSeverity mSeverity; + const char* mFile; + int mLine; + SmprintfPointer mMsg; + + DISALLOW_EVIL_CONSTRUCTORS(Logger); +}; + +class LogWrapper { + public: + LogWrapper(LogSeverity severity, const char* file, int line) + : log(severity, file, line) {} + + operator Logger&() const { return log; } + + private: + mutable Logger log; + + DISALLOW_EVIL_CONSTRUCTORS(LogWrapper); +}; + +struct EmptyLog {}; + +mozilla::Logger& operator<<(mozilla::Logger& log, const char* s); +mozilla::Logger& operator<<(mozilla::Logger& log, const std::string& s); +mozilla::Logger& operator<<(mozilla::Logger& log, int i); +mozilla::Logger& operator<<(mozilla::Logger& log, const std::wstring& s); +mozilla::Logger& operator<<(mozilla::Logger& log, void* p); + +template <class T> +const mozilla::EmptyLog& operator<<(const mozilla::EmptyLog& log, const T&) { + return log; +} + +} // namespace mozilla + +#ifdef NO_CHROMIUM_LOGGING +# define CHROMIUM_LOG(info) std::stringstream() +# define LOG_IF(info, condition) \ + if (!(condition)) std::stringstream() +#else +# define CHROMIUM_LOG(info) \ + mozilla::LogWrapper(mozilla::LOG_##info, __FILE__, __LINE__) +# define LOG_IF(info, condition) \ + if (!(condition)) \ + mozilla::LogWrapper(mozilla::LOG_##info, __FILE__, __LINE__) +#endif + +#ifdef DEBUG +# define DLOG(info) CHROMIUM_LOG(info) +# define DLOG_IF(info, condition) LOG_IF(info, condition) +# define DCHECK(condition) CHECK(condition) +#else +# define DLOG(info) mozilla::EmptyLog() +# define DLOG_IF(info, condition) mozilla::EmptyLog() +# define DCHECK(condition) \ + while (false && (condition)) mozilla::EmptyLog() +#endif + +#define DVLOG(level) DLOG(INFO) + +#undef LOG_ASSERT +#define LOG_ASSERT(cond) CHECK(0) +#define DLOG_ASSERT(cond) DCHECK(0) + +#define NOTREACHED() CHROMIUM_LOG(ERROR) +#define NOTIMPLEMENTED() CHROMIUM_LOG(ERROR) + +#undef CHECK +#ifdef FUZZING +# define CHECK(condition) LOG_IF(WARNING, condition) +#else +# define CHECK(condition) LOG_IF(FATAL, condition) +#endif + +#define DCHECK_EQ(v1, v2) DCHECK((v1) == (v2)) +#define DCHECK_NE(v1, v2) DCHECK((v1) != (v2)) +#define DCHECK_LE(v1, v2) DCHECK((v1) <= (v2)) +#define DCHECK_LT(v1, v2) DCHECK((v1) < (v2)) +#define DCHECK_GE(v1, v2) DCHECK((v1) >= (v2)) +#define DCHECK_GT(v1, v2) DCHECK((v1) > (v2)) + +#endif // BASE_LOGGING_H_ diff --git a/ipc/chromium/src/base/mac_util.h b/ipc/chromium/src/base/mac_util.h new file mode 100644 index 0000000000..6af2bd7032 --- /dev/null +++ b/ipc/chromium/src/base/mac_util.h @@ -0,0 +1,17 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_UTIL_H_ +#define BASE_MAC_UTIL_H_ + +namespace mac_util { + +// Returns true if the application is running from a bundle +bool AmIBundled(); + +} // namespace mac_util + +#endif // BASE_MAC_UTIL_H_ diff --git a/ipc/chromium/src/base/mac_util.mm b/ipc/chromium/src/base/mac_util.mm new file mode 100644 index 0000000000..dde8d9a273 --- /dev/null +++ b/ipc/chromium/src/base/mac_util.mm @@ -0,0 +1,33 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac_util.h" + +#include <Carbon/Carbon.h> +#import <Cocoa/Cocoa.h> + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/scoped_cftyperef.h" +#include "base/sys_string_conversions.h" + +namespace mac_util { + +// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled +bool AmIBundled() { + ProcessSerialNumber psn = {0, kCurrentProcess}; + + FSRef fsref; + if (GetProcessBundleLocation(&psn, &fsref) != noErr) return false; + + FSCatalogInfo info; + if (FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info, NULL, NULL, NULL) != + noErr) { + return false; + } + + return info.nodeFlags & kFSNodeIsDirectoryMask; +} + +} // namespace mac_util diff --git a/ipc/chromium/src/base/message_loop.cc b/ipc/chromium/src/base/message_loop.cc new file mode 100644 index 0000000000..c82d555ee5 --- /dev/null +++ b/ipc/chromium/src/base/message_loop.cc @@ -0,0 +1,725 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop.h" + +#include <algorithm> + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/message_pump_default.h" +#include "base/string_util.h" +#include "base/thread_local.h" +#include "mozilla/Atomics.h" +#include "mozilla/Mutex.h" +#include "mozilla/ProfilerRunnable.h" +#include "nsIEventTarget.h" +#include "nsITargetShutdownTask.h" +#include "nsThreadUtils.h" + +#if defined(XP_DARWIN) +# include "base/message_pump_mac.h" +#endif +#if defined(XP_UNIX) +# include "base/message_pump_libevent.h" +#endif +#if defined(XP_LINUX) || defined(__DragonFly__) || defined(XP_FREEBSD) || \ + defined(XP_NETBSD) || defined(XP_OPENBSD) +# if defined(MOZ_WIDGET_GTK) +# include "base/message_pump_glib.h" +# endif +#endif +#ifdef ANDROID +# include "base/message_pump_android.h" +#endif +#include "nsISerialEventTarget.h" + +#include "mozilla/ipc/MessagePump.h" +#include "nsThreadUtils.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + +using mozilla::Runnable; + +static base::ThreadLocalPointer<MessageLoop>& get_tls_ptr() { + static base::ThreadLocalPointer<MessageLoop> tls_ptr; + return tls_ptr; +} + +//------------------------------------------------------------------------------ + +#if defined(XP_WIN) + +// Upon a SEH exception in this thread, it restores the original unhandled +// exception filter. +static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) { + ::SetUnhandledExceptionFilter(old_filter); + return EXCEPTION_CONTINUE_SEARCH; +} + +// Retrieves a pointer to the current unhandled exception filter. There +// is no standalone getter method. +static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() { + LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL; + top_filter = ::SetUnhandledExceptionFilter(0); + ::SetUnhandledExceptionFilter(top_filter); + return top_filter; +} + +#endif // defined(XP_WIN) + +//------------------------------------------------------------------------------ + +class MessageLoop::EventTarget : public nsISerialEventTarget, + public nsITargetShutdownTask, + public MessageLoop::DestructionObserver { + public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIEVENTTARGET_FULL + + void TargetShutdown() override { + nsTArray<nsCOMPtr<nsITargetShutdownTask>> shutdownTasks; + { + mozilla::MutexAutoLock lock(mMutex); + if (mShutdownTasksRun) { + return; + } + mShutdownTasksRun = true; + shutdownTasks = std::move(mShutdownTasks); + mShutdownTasks.Clear(); + } + + for (auto& task : shutdownTasks) { + task->TargetShutdown(); + } + } + + explicit EventTarget(MessageLoop* aLoop) + : mMutex("MessageLoop::EventTarget"), mLoop(aLoop) { + aLoop->AddDestructionObserver(this); + } + + private: + virtual ~EventTarget() { + if (mLoop) { + mLoop->RemoveDestructionObserver(this); + } + MOZ_ASSERT(mShutdownTasks.IsEmpty()); + } + + void WillDestroyCurrentMessageLoop() override { + { + mozilla::MutexAutoLock lock(mMutex); + // The MessageLoop is being destroyed and we are called from its + // destructor There's no real need to remove ourselves from the + // destruction observer list. But it makes things look tidier. + mLoop->RemoveDestructionObserver(this); + mLoop = nullptr; + } + + TargetShutdown(); + } + + mozilla::Mutex mMutex; + bool mShutdownTasksRun MOZ_GUARDED_BY(mMutex) = false; + nsTArray<nsCOMPtr<nsITargetShutdownTask>> mShutdownTasks + MOZ_GUARDED_BY(mMutex); + MessageLoop* mLoop MOZ_GUARDED_BY(mMutex); +}; + +NS_IMPL_ISUPPORTS(MessageLoop::EventTarget, nsIEventTarget, + nsISerialEventTarget) + +NS_IMETHODIMP_(bool) +MessageLoop::EventTarget::IsOnCurrentThreadInfallible() { + mozilla::MutexAutoLock lock(mMutex); + return mLoop == MessageLoop::current(); +} + +NS_IMETHODIMP +MessageLoop::EventTarget::IsOnCurrentThread(bool* aResult) { + *aResult = IsOnCurrentThreadInfallible(); + return NS_OK; +} + +NS_IMETHODIMP +MessageLoop::EventTarget::DispatchFromScript(nsIRunnable* aEvent, + uint32_t aFlags) { + nsCOMPtr<nsIRunnable> event(aEvent); + return Dispatch(event.forget(), aFlags); +} + +NS_IMETHODIMP +MessageLoop::EventTarget::Dispatch(already_AddRefed<nsIRunnable> aEvent, + uint32_t aFlags) { + mozilla::MutexAutoLock lock(mMutex); + if (!mLoop) { + return NS_ERROR_NOT_INITIALIZED; + } + + if (aFlags != NS_DISPATCH_NORMAL) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + mLoop->PostTask(std::move(aEvent)); + return NS_OK; +} + +NS_IMETHODIMP +MessageLoop::EventTarget::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, + uint32_t aDelayMs) { + mozilla::MutexAutoLock lock(mMutex); + if (!mLoop) { + return NS_ERROR_NOT_INITIALIZED; + } + + mLoop->PostDelayedTask(std::move(aEvent), aDelayMs); + return NS_OK; +} + +NS_IMETHODIMP +MessageLoop::EventTarget::RegisterShutdownTask(nsITargetShutdownTask* aTask) { + mozilla::MutexAutoLock lock(mMutex); + if (!mLoop || mShutdownTasksRun) { + return NS_ERROR_UNEXPECTED; + } + MOZ_ASSERT(!mShutdownTasks.Contains(aTask)); + mShutdownTasks.AppendElement(aTask); + return NS_OK; +} + +NS_IMETHODIMP +MessageLoop::EventTarget::UnregisterShutdownTask(nsITargetShutdownTask* aTask) { + mozilla::MutexAutoLock lock(mMutex); + if (!mLoop || mShutdownTasksRun) { + return NS_ERROR_UNEXPECTED; + } + return mShutdownTasks.RemoveElement(aTask) ? NS_OK : NS_ERROR_UNEXPECTED; +} + +//------------------------------------------------------------------------------ + +// static +MessageLoop* MessageLoop::current() { return get_tls_ptr().Get(); } + +// static +void MessageLoop::set_current(MessageLoop* loop) { get_tls_ptr().Set(loop); } + +static mozilla::Atomic<int32_t> message_loop_id_seq(0); + +MessageLoop::MessageLoop(Type type, nsISerialEventTarget* aEventTarget) + : type_(type), + id_(++message_loop_id_seq), + nestable_tasks_allowed_(true), + exception_restoration_(false), + incoming_queue_lock_("MessageLoop Incoming Queue Lock"), + state_(NULL), + run_depth_base_(1), + shutting_down_(false), +#ifdef XP_WIN + os_modal_loop_(false), +#endif // XP_WIN + transient_hang_timeout_(0), + permanent_hang_timeout_(0), + next_sequence_num_(0) { + DCHECK(!current()) << "should only have one message loop per thread"; + get_tls_ptr().Set(this); + + // Must initialize after current() is initialized. + mEventTarget = new EventTarget(this); + + switch (type_) { + case TYPE_MOZILLA_PARENT: + MOZ_RELEASE_ASSERT(!aEventTarget); + pump_ = new mozilla::ipc::MessagePump(aEventTarget); + return; + case TYPE_MOZILLA_CHILD: + MOZ_RELEASE_ASSERT(!aEventTarget); + pump_ = new mozilla::ipc::MessagePumpForChildProcess(); + // There is a MessageLoop Run call from XRE_InitChildProcess + // and another one from MessagePumpForChildProcess. The one + // from MessagePumpForChildProcess becomes the base, so we need + // to set run_depth_base_ to 2 or we'll never be able to process + // Idle tasks. + run_depth_base_ = 2; + return; + case TYPE_MOZILLA_NONMAINTHREAD: + pump_ = new mozilla::ipc::MessagePumpForNonMainThreads(aEventTarget); + return; +#if defined(XP_WIN) || defined(XP_DARWIN) + case TYPE_MOZILLA_NONMAINUITHREAD: + pump_ = new mozilla::ipc::MessagePumpForNonMainUIThreads(aEventTarget); + return; +#elif defined(MOZ_WIDGET_ANDROID) + case TYPE_MOZILLA_ANDROID_UI: + MOZ_RELEASE_ASSERT(aEventTarget); + pump_ = new mozilla::ipc::MessagePumpForAndroidUI(aEventTarget); + return; +#endif // defined(MOZ_WIDGET_ANDROID) + default: + // Create one of Chromium's standard MessageLoop types below. + break; + } + +#if defined(XP_WIN) + // TODO(rvargas): Get rid of the OS guards. + if (type_ == TYPE_DEFAULT) { + pump_ = new base::MessagePumpDefault(); + } else if (type_ == TYPE_IO) { + pump_ = new base::MessagePumpForIO(); + } else { + DCHECK(type_ == TYPE_UI); + pump_ = new base::MessagePumpForUI(); + } +#else + if (type_ == TYPE_UI) { +# if defined(XP_DARWIN) + pump_ = base::MessagePumpMac::Create(); +# elif defined(XP_LINUX) || defined(__DragonFly__) || defined(XP_FREEBSD) || \ + defined(XP_NETBSD) || defined(XP_OPENBSD) + pump_ = new base::MessagePumpForUI(); +# endif // XP_LINUX + } else if (type_ == TYPE_IO) { + pump_ = new base::MessagePumpLibevent(); + } else { + pump_ = new base::MessagePumpDefault(); + } +#endif + + // We want GetCurrentSerialEventTarget() to return the real nsThread if it + // will be used to dispatch tasks. However, under all other cases; we'll want + // it to return this MessageLoop's EventTarget. + if (nsISerialEventTarget* thread = pump_->GetXPCOMThread()) { + MOZ_ALWAYS_SUCCEEDS(thread->RegisterShutdownTask(mEventTarget)); + } else { + mozilla::SerialEventTargetGuard::Set(mEventTarget); + } +} + +MessageLoop::~MessageLoop() { + DCHECK(this == current()); + + // Let interested parties have one last shot at accessing this. + FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_, + WillDestroyCurrentMessageLoop()); + + DCHECK(!state_); + + // Clean up any unprocessed tasks, but take care: deleting a task could + // result in the addition of more tasks (e.g., via DeleteSoon). We set a + // limit on the number of times we will allow a deleted task to generate more + // tasks. Normally, we should only pass through this loop once or twice. If + // we end up hitting the loop limit, then it is probably due to one task that + // is being stubborn. Inspect the queues to see who is left. + bool did_work; + for (int i = 0; i < 100; ++i) { + DeletePendingTasks(); + ReloadWorkQueue(); + // If we end up with empty queues, then break out of the loop. + did_work = DeletePendingTasks(); + if (!did_work) break; + } + DCHECK(!did_work); + + // OK, now make it so that no one can find us. + get_tls_ptr().Set(NULL); +} + +void MessageLoop::AddDestructionObserver(DestructionObserver* obs) { + DCHECK(this == current()); + destruction_observers_.AddObserver(obs); +} + +void MessageLoop::RemoveDestructionObserver(DestructionObserver* obs) { + DCHECK(this == current()); + destruction_observers_.RemoveObserver(obs); +} + +void MessageLoop::Run() { + AutoRunState save_state(this); + RunHandler(); +} + +// Runs the loop in two different SEH modes: +// enable_SEH_restoration_ = false : any unhandled exception goes to the last +// one that calls SetUnhandledExceptionFilter(). +// enable_SEH_restoration_ = true : any unhandled exception goes to the filter +// that was existed before the loop was run. +void MessageLoop::RunHandler() { +#if defined(XP_WIN) + if (exception_restoration_) { + LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter(); + MOZ_SEH_TRY { RunInternal(); } + MOZ_SEH_EXCEPT(SEHFilter(current_filter)) {} + return; + } +#endif + + RunInternal(); +} + +//------------------------------------------------------------------------------ + +void MessageLoop::RunInternal() { + DCHECK(this == current()); + pump_->Run(this); +} + +//------------------------------------------------------------------------------ +// Wrapper functions for use in above message loop framework. + +bool MessageLoop::ProcessNextDelayedNonNestableTask() { + if (state_->run_depth > run_depth_base_) return false; + + if (deferred_non_nestable_work_queue_.empty()) return false; + + nsCOMPtr<nsIRunnable> task = + std::move(deferred_non_nestable_work_queue_.front().task); + deferred_non_nestable_work_queue_.pop(); + + RunTask(task.forget()); + return true; +} + +//------------------------------------------------------------------------------ + +void MessageLoop::Quit() { + DCHECK(current() == this); + if (state_) { + state_->quit_received = true; + } else { + NOTREACHED() << "Must be inside Run to call Quit"; + } +} + +void MessageLoop::PostTask(already_AddRefed<nsIRunnable> task) { + PostTask_Helper(std::move(task), 0); +} + +void MessageLoop::PostDelayedTask(already_AddRefed<nsIRunnable> task, + int delay_ms) { + PostTask_Helper(std::move(task), delay_ms); +} + +void MessageLoop::PostIdleTask(already_AddRefed<nsIRunnable> task) { + DCHECK(current() == this); + MOZ_ASSERT(NS_IsMainThread()); + + PendingTask pending_task(std::move(task), false); + mozilla::LogRunnable::LogDispatch(pending_task.task.get()); + deferred_non_nestable_work_queue_.push(std::move(pending_task)); +} + +// Possibly called on a background thread! +void MessageLoop::PostTask_Helper(already_AddRefed<nsIRunnable> task, + int delay_ms) { + if (nsISerialEventTarget* target = pump_->GetXPCOMThread()) { + nsresult rv; + if (delay_ms) { + rv = target->DelayedDispatch(std::move(task), delay_ms); + } else { + rv = target->Dispatch(std::move(task), 0); + } + MOZ_ALWAYS_SUCCEEDS(rv); + return; + } + + // Tasks should only be queued before or during the Run loop, not after. + MOZ_ASSERT(!shutting_down_); + + PendingTask pending_task(std::move(task), true); + + if (delay_ms > 0) { + pending_task.delayed_run_time = + TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms); + } else { + DCHECK(delay_ms == 0) << "delay should not be negative"; + } + + // Warning: Don't try to short-circuit, and handle this thread's tasks more + // directly, as it could starve handling of foreign threads. Put every task + // into this queue. + + RefPtr<base::MessagePump> pump; + { + mozilla::MutexAutoLock locked(incoming_queue_lock_); + mozilla::LogRunnable::LogDispatch(pending_task.task.get()); + incoming_queue_.push(std::move(pending_task)); + pump = pump_; + } + // Since the incoming_queue_ may contain a task that destroys this message + // loop, we cannot exit incoming_queue_lock_ until we are done with |this|. + // We use a stack-based reference to the message pump so that we can call + // ScheduleWork outside of incoming_queue_lock_. + + pump->ScheduleWork(); +} + +void MessageLoop::SetNestableTasksAllowed(bool allowed) { + if (nestable_tasks_allowed_ != allowed) { + nestable_tasks_allowed_ = allowed; + if (!nestable_tasks_allowed_) return; + // Start the native pump if we are not already pumping. + pump_->ScheduleWorkForNestedLoop(); + } +} + +void MessageLoop::ScheduleWork() { + // Start the native pump if we are not already pumping. + pump_->ScheduleWork(); +} + +bool MessageLoop::NestableTasksAllowed() const { + return nestable_tasks_allowed_; +} + +//------------------------------------------------------------------------------ + +void MessageLoop::RunTask(already_AddRefed<nsIRunnable> aTask) { + DCHECK(nestable_tasks_allowed_); + // Execute the task and assume the worst: It is probably not reentrant. + nestable_tasks_allowed_ = false; + + nsCOMPtr<nsIRunnable> task = aTask; + + { + mozilla::LogRunnable::Run log(task.get()); + AUTO_PROFILE_FOLLOWING_RUNNABLE(task); + task->Run(); + task = nullptr; + } + + nestable_tasks_allowed_ = true; +} + +bool MessageLoop::DeferOrRunPendingTask(PendingTask&& pending_task) { + if (pending_task.nestable || state_->run_depth <= run_depth_base_) { + RunTask(pending_task.task.forget()); + // Show that we ran a task (Note: a new one might arrive as a + // consequence!). + return true; + } + + // We couldn't run the task now because we're in a nested message loop + // and the task isn't nestable. + mozilla::LogRunnable::LogDispatch(pending_task.task.get()); + deferred_non_nestable_work_queue_.push(std::move(pending_task)); + return false; +} + +void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) { + // Move to the delayed work queue. Initialize the sequence number + // before inserting into the delayed_work_queue_. The sequence number + // is used to faciliate FIFO sorting when two tasks have the same + // delayed_run_time value. + PendingTask new_pending_task(pending_task); + new_pending_task.sequence_num = next_sequence_num_++; + mozilla::LogRunnable::LogDispatch(new_pending_task.task.get()); + delayed_work_queue_.push(std::move(new_pending_task)); +} + +void MessageLoop::ReloadWorkQueue() { + // We can improve performance of our loading tasks from incoming_queue_ to + // work_queue_ by waiting until the last minute (work_queue_ is empty) to + // load. That reduces the number of locks-per-task significantly when our + // queues get large. + if (!work_queue_.empty()) + return; // Wait till we *really* need to lock and load. + + // Acquire all we can from the inter-thread queue with one lock acquisition. + { + mozilla::MutexAutoLock lock(incoming_queue_lock_); + if (incoming_queue_.empty()) return; + std::swap(incoming_queue_, work_queue_); + DCHECK(incoming_queue_.empty()); + } +} + +bool MessageLoop::DeletePendingTasks() { + MOZ_ASSERT(work_queue_.empty()); + bool did_work = !deferred_non_nestable_work_queue_.empty(); + while (!deferred_non_nestable_work_queue_.empty()) { + deferred_non_nestable_work_queue_.pop(); + } + did_work |= !delayed_work_queue_.empty(); + while (!delayed_work_queue_.empty()) { + delayed_work_queue_.pop(); + } + return did_work; +} + +bool MessageLoop::DoWork() { + if (!nestable_tasks_allowed_) { + // Task can't be executed right now. + return false; + } + + for (;;) { + ReloadWorkQueue(); + if (work_queue_.empty()) break; + + // Execute oldest task. + do { + PendingTask pending_task = std::move(work_queue_.front()); + work_queue_.pop(); + if (!pending_task.delayed_run_time.is_null()) { + // NB: Don't move, because we use this later! + AddToDelayedWorkQueue(pending_task); + // If we changed the topmost task, then it is time to re-schedule. + if (delayed_work_queue_.top().task == pending_task.task) + pump_->ScheduleDelayedWork(pending_task.delayed_run_time); + } else { + if (DeferOrRunPendingTask(std::move(pending_task))) return true; + } + } while (!work_queue_.empty()); + } + + // Nothing happened. + return false; +} + +bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) { + if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) { + *next_delayed_work_time = TimeTicks(); + return false; + } + + if (delayed_work_queue_.top().delayed_run_time > TimeTicks::Now()) { + *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; + return false; + } + + PendingTask pending_task = delayed_work_queue_.top(); + delayed_work_queue_.pop(); + + if (!delayed_work_queue_.empty()) + *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; + + return DeferOrRunPendingTask(std::move(pending_task)); +} + +bool MessageLoop::DoIdleWork() { + if (ProcessNextDelayedNonNestableTask()) return true; + + if (state_->quit_received) pump_->Quit(); + + return false; +} + +//------------------------------------------------------------------------------ +// MessageLoop::AutoRunState + +MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) { + // Top-level Run should only get called once. + MOZ_ASSERT(!loop_->shutting_down_); + + // Make the loop reference us. + previous_state_ = loop_->state_; + if (previous_state_) { + run_depth = previous_state_->run_depth + 1; + } else { + run_depth = 1; + } + loop_->state_ = this; + + // Initialize the other fields: + quit_received = false; +#if defined(XP_WIN) + dispatcher = NULL; +#endif +} + +MessageLoop::AutoRunState::~AutoRunState() { + loop_->state_ = previous_state_; + + // If exiting a top-level Run, then we're shutting down. + loop_->shutting_down_ = !previous_state_; +} + +//------------------------------------------------------------------------------ +// MessageLoop::PendingTask + +bool MessageLoop::PendingTask::operator<(const PendingTask& other) const { + // Since the top of a priority queue is defined as the "greatest" element, we + // need to invert the comparison here. We want the smaller time to be at the + // top of the heap. + + if (delayed_run_time < other.delayed_run_time) return false; + + if (delayed_run_time > other.delayed_run_time) return true; + + // If the times happen to match, then we use the sequence number to decide. + // Compare the difference to support integer roll-over. + return (sequence_num - other.sequence_num) > 0; +} + +//------------------------------------------------------------------------------ +// MessageLoop::SerialEventTarget + +nsISerialEventTarget* MessageLoop::SerialEventTarget() { return mEventTarget; } + +//------------------------------------------------------------------------------ +// MessageLoopForUI + +#if defined(XP_WIN) + +void MessageLoopForUI::Run(Dispatcher* dispatcher) { + AutoRunState save_state(this); + state_->dispatcher = dispatcher; + RunHandler(); +} + +void MessageLoopForUI::AddObserver(Observer* observer) { + pump_win()->AddObserver(observer); +} + +void MessageLoopForUI::RemoveObserver(Observer* observer) { + pump_win()->RemoveObserver(observer); +} + +void MessageLoopForUI::WillProcessMessage(const MSG& message) { + pump_win()->WillProcessMessage(message); +} +void MessageLoopForUI::DidProcessMessage(const MSG& message) { + pump_win()->DidProcessMessage(message); +} +void MessageLoopForUI::PumpOutPendingPaintMessages() { + pump_ui()->PumpOutPendingPaintMessages(); +} + +#endif // defined(XP_WIN) + +//------------------------------------------------------------------------------ +// MessageLoopForIO + +#if defined(XP_WIN) + +void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) { + pump_io()->RegisterIOHandler(file, handler); +} + +bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { + return pump_io()->WaitForIOCompletion(timeout, filter); +} + +#else + +bool MessageLoopForIO::WatchFileDescriptor(int fd, bool persistent, Mode mode, + FileDescriptorWatcher* controller, + Watcher* delegate) { + return pump_libevent()->WatchFileDescriptor( + fd, persistent, static_cast<base::MessagePumpLibevent::Mode>(mode), + controller, delegate); +} + +bool MessageLoopForIO::CatchSignal(int sig, SignalEvent* sigevent, + SignalWatcher* delegate) { + return pump_libevent()->CatchSignal(sig, sigevent, delegate); +} + +#endif diff --git a/ipc/chromium/src/base/message_loop.h b/ipc/chromium/src/base/message_loop.h new file mode 100644 index 0000000000..ee3f118ac3 --- /dev/null +++ b/ipc/chromium/src/base/message_loop.h @@ -0,0 +1,559 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_H_ +#define BASE_MESSAGE_LOOP_H_ + +#include <deque> +#include <queue> +#include <string> +#include <vector> +#include <map> + +#include "base/message_pump.h" +#include "base/observer_list.h" + +#include "mozilla/Mutex.h" + +#if defined(XP_WIN) +// We need this to declare base::MessagePumpWin::Dispatcher, which we should +// really just eliminate. +# include "base/message_pump_win.h" +#else +# include "base/message_pump_libevent.h" +#endif + +#include "nsCOMPtr.h" +#include "nsIRunnable.h" +#include "nsThreadUtils.h" + +class nsISerialEventTarget; + +namespace mozilla { +namespace ipc { + +class DoWorkRunnable; + +} /* namespace ipc */ +} /* namespace mozilla */ + +// A MessageLoop is used to process events for a particular thread. There is +// at most one MessageLoop instance per thread. +// +// Events include at a minimum Task instances submitted to PostTask or those +// managed by TimerManager. Depending on the type of message pump used by the +// MessageLoop other events such as UI messages may be processed. On Windows +// APC calls (as time permits) and signals sent to a registered set of HANDLEs +// may also be processed. +// +// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called +// on the thread where the MessageLoop's Run method executes. +// +// NOTE: MessageLoop has task reentrancy protection. This means that if a +// task is being processed, a second task cannot start until the first task is +// finished. Reentrancy can happen when processing a task, and an inner +// message pump is created. That inner pump then processes native messages +// which could implicitly start an inner task. Inner message pumps are created +// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions +// (DoDragDrop), printer functions (StartDoc) and *many* others. +// +// Sample workaround when inner task processing is needed: +// bool old_state = MessageLoop::current()->NestableTasksAllowed(); +// MessageLoop::current()->SetNestableTasksAllowed(true); +// HRESULT hr = DoDragDrop(...); // Implicitly runs a modal message loop here. +// MessageLoop::current()->SetNestableTasksAllowed(old_state); +// // Process hr (the result returned by DoDragDrop(). +// +// Please be SURE your task is reentrant (nestable) and all global variables +// are stable and accessible before calling SetNestableTasksAllowed(true). +// +class MessageLoop : public base::MessagePump::Delegate { + friend class mozilla::ipc::DoWorkRunnable; + + public: + // A DestructionObserver is notified when the current MessageLoop is being + // destroyed. These obsevers are notified prior to MessageLoop::current() + // being changed to return NULL. This gives interested parties the chance to + // do final cleanup that depends on the MessageLoop. + // + // NOTE: Any tasks posted to the MessageLoop during this notification will + // not be run. Instead, they will be deleted. + // + class DestructionObserver { + public: + virtual ~DestructionObserver() {} + virtual void WillDestroyCurrentMessageLoop() = 0; + }; + + // Add a DestructionObserver, which will start receiving notifications + // immediately. + void AddDestructionObserver(DestructionObserver* destruction_observer); + + // Remove a DestructionObserver. It is safe to call this method while a + // DestructionObserver is receiving a notification callback. + void RemoveDestructionObserver(DestructionObserver* destruction_observer); + + // The "PostTask" family of methods call the task's Run method asynchronously + // from within a message loop at some point in the future. + // + // With the PostTask variant, tasks are invoked in FIFO order, inter-mixed + // with normal UI or IO event processing. With the PostDelayedTask variant, + // tasks are called after at least approximately 'delay_ms' have elapsed. + // + // The NonNestable variants work similarly except that they promise never to + // dispatch the task from a nested invocation of MessageLoop::Run. Instead, + // such tasks get deferred until the top-most MessageLoop::Run is executing. + // + // The MessageLoop takes ownership of the Task, and deletes it after it has + // been Run(). + // + // New tasks should not be posted after the invocation of a MessageLoop's + // Run method. Otherwise, they may fail to actually run. Callers should check + // if the MessageLoop is processing tasks if necessary by calling + // IsAcceptingTasks(). + // + // NOTE: These methods may be called on any thread. The Task will be invoked + // on the thread that executes MessageLoop::Run(). + + bool IsAcceptingTasks() const { return !shutting_down_; } + + void PostTask(already_AddRefed<nsIRunnable> task); + + void PostDelayedTask(already_AddRefed<nsIRunnable> task, int delay_ms); + + // PostIdleTask is not thread safe and should be called on this thread + void PostIdleTask(already_AddRefed<nsIRunnable> task); + + // Run the message loop. + void Run(); + + // Signals the Run method to return after it is done processing all pending + // messages. This method may only be called on the same thread that called + // Run, and Run must still be on the call stack. + // + // Use QuitTask if you need to Quit another thread's MessageLoop, but note + // that doing so is fairly dangerous if the target thread makes nested calls + // to MessageLoop::Run. The problem being that you won't know which nested + // run loop you are quiting, so be careful! + // + void Quit(); + + // Invokes Quit on the current MessageLoop when run. Useful to schedule an + // arbitrary MessageLoop to Quit. + class QuitTask : public mozilla::Runnable { + public: + QuitTask() : mozilla::Runnable("QuitTask") {} + NS_IMETHOD Run() override { + MessageLoop::current()->Quit(); + return NS_OK; + } + }; + + // Return an XPCOM-compatible event target for this thread. + nsISerialEventTarget* SerialEventTarget(); + + // A MessageLoop has a particular type, which indicates the set of + // asynchronous events it may process in addition to tasks and timers. + // + // TYPE_DEFAULT + // This type of ML only supports tasks and timers. + // + // TYPE_UI + // This type of ML also supports native UI events (e.g., Windows messages). + // See also MessageLoopForUI. + // + // TYPE_IO + // This type of ML also supports asynchronous IO. See also + // MessageLoopForIO. + // + // TYPE_MOZILLA_CHILD + // This type of ML is used in Mozilla child processes which initialize + // XPCOM and use the gecko event loop. + // + // TYPE_MOZILLA_PARENT + // This type of ML is used in Mozilla parent processes which initialize + // XPCOM and use the gecko event loop. + // + // TYPE_MOZILLA_NONMAINTHREAD + // This type of ML is used in Mozilla parent processes which initialize + // XPCOM and use the nsThread event loop. + // + // TYPE_MOZILLA_NONMAINUITHREAD + // This type of ML is used in Mozilla processes which initialize XPCOM + // and use TYPE_UI loop logic. + // + enum Type { + TYPE_DEFAULT, + TYPE_UI, + TYPE_IO, + TYPE_MOZILLA_CHILD, + TYPE_MOZILLA_PARENT, + TYPE_MOZILLA_NONMAINTHREAD, + TYPE_MOZILLA_NONMAINUITHREAD, + TYPE_MOZILLA_ANDROID_UI + }; + + // Normally, it is not necessary to instantiate a MessageLoop. Instead, it + // is typical to make use of the current thread's MessageLoop instance. + explicit MessageLoop(Type type = TYPE_DEFAULT, + nsISerialEventTarget* aEventTarget = nullptr); + ~MessageLoop(); + + // Returns the type passed to the constructor. + Type type() const { return type_; } + + // Unique, non-repeating ID for this message loop. + int32_t id() const { return id_; } + + // Optional call to connect the thread name with this loop. + void set_thread_name(const std::string& aThreadName) { + DCHECK(thread_name_.empty()) << "Should not rename this thread!"; + thread_name_ = aThreadName; + } + const std::string& thread_name() const { return thread_name_; } + + // Returns the MessageLoop object for the current thread, or null if none. + static MessageLoop* current(); + + static void set_current(MessageLoop* loop); + + // Enables or disables the recursive task processing. This happens in the case + // of recursive message loops. Some unwanted message loop may occurs when + // using common controls or printer functions. By default, recursive task + // processing is disabled. + // + // The specific case where tasks get queued is: + // - The thread is running a message loop. + // - It receives a task #1 and execute it. + // - The task #1 implicitly start a message loop, like a MessageBox in the + // unit test. This can also be StartDoc or GetSaveFileName. + // - The thread receives a task #2 before or while in this second message + // loop. + // - With NestableTasksAllowed set to true, the task #2 will run right away. + // Otherwise, it will get executed right after task #1 completes at "thread + // message loop level". + void SetNestableTasksAllowed(bool allowed); + void ScheduleWork(); + bool NestableTasksAllowed() const; + + // Enables or disables the restoration during an exception of the unhandled + // exception filter that was active when Run() was called. This can happen + // if some third party code call SetUnhandledExceptionFilter() and never + // restores the previous filter. + void set_exception_restoration(bool restore) { + exception_restoration_ = restore; + } + +#if defined(XP_WIN) + void set_os_modal_loop(bool os_modal_loop) { os_modal_loop_ = os_modal_loop; } + + bool& os_modal_loop() { return os_modal_loop_; } +#endif // XP_WIN + + // Set the timeouts for background hang monitoring. + // A value of 0 indicates there is no timeout. + void set_hang_timeouts(uint32_t transient_timeout_ms, + uint32_t permanent_timeout_ms) { + transient_hang_timeout_ = transient_timeout_ms; + permanent_hang_timeout_ = permanent_timeout_ms; + } + uint32_t transient_hang_timeout() const { return transient_hang_timeout_; } + uint32_t permanent_hang_timeout() const { return permanent_hang_timeout_; } + + //---------------------------------------------------------------------------- + protected: + struct RunState { + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // Used to record that Quit() was called, or that we should quit the pump + // once it becomes idle. + bool quit_received; + +#if defined(XP_WIN) + base::MessagePumpWin::Dispatcher* dispatcher; +#endif + }; + + class AutoRunState : RunState { + public: + explicit AutoRunState(MessageLoop* loop); + ~AutoRunState(); + + private: + MessageLoop* loop_; + RunState* previous_state_; + }; + + // This structure is copied around by value. + struct PendingTask { + nsCOMPtr<nsIRunnable> task; // The task to run. + base::TimeTicks delayed_run_time; // The time when the task should be run. + int sequence_num; // Secondary sort key for run time. + bool nestable; // OK to dispatch from a nested loop. + + PendingTask(already_AddRefed<nsIRunnable> aTask, bool aNestable) + : task(aTask), sequence_num(0), nestable(aNestable) {} + + PendingTask(PendingTask&& aOther) + : task(std::move(aOther.task)), + delayed_run_time(aOther.delayed_run_time), + sequence_num(aOther.sequence_num), + nestable(aOther.nestable) {} + + // std::priority_queue<T>::top is dumb, so we have to have this. + PendingTask(const PendingTask& aOther) + : task(aOther.task), + delayed_run_time(aOther.delayed_run_time), + sequence_num(aOther.sequence_num), + nestable(aOther.nestable) {} + PendingTask& operator=(const PendingTask& aOther) { + task = aOther.task; + delayed_run_time = aOther.delayed_run_time; + sequence_num = aOther.sequence_num; + nestable = aOther.nestable; + return *this; + } + + // Used to support sorting. + bool operator<(const PendingTask& other) const; + }; + + typedef std::queue<PendingTask> TaskQueue; + typedef std::priority_queue<PendingTask> DelayedTaskQueue; + +#if defined(XP_WIN) + base::MessagePumpWin* pump_win() { + return static_cast<base::MessagePumpWin*>(pump_.get()); + } +#else + base::MessagePumpLibevent* pump_libevent() { + return static_cast<base::MessagePumpLibevent*>(pump_.get()); + } +#endif + + // A function to encapsulate all the exception handling capability in the + // stacks around the running of a main message loop. It will run the message + // loop in a SEH try block or not depending on the set_SEH_restoration() + // flag. + void RunHandler(); + + // A surrounding stack frame around the running of the message loop that + // supports all saving and restoring of state, as is needed for any/all (ugly) + // recursive calls. + void RunInternal(); + + // Called to process any delayed non-nestable tasks. + bool ProcessNextDelayedNonNestableTask(); + + //---------------------------------------------------------------------------- + // Run a work_queue_ task or new_task, and delete it (if it was processed by + // PostTask). If there are queued tasks, the oldest one is executed and + // new_task is queued. new_task is optional and can be NULL. In this NULL + // case, the method will run one pending task (if any exist). Returns true if + // it executes a task. Queued tasks accumulate only when there is a + // non-nestable task currently processing, in which case the new_task is + // appended to the list work_queue_. Such re-entrancy generally happens when + // an unrequested message pump (typical of a native dialog) is executing in + // the context of a task. + bool QueueOrRunTask(already_AddRefed<nsIRunnable> new_task); + + // Runs the specified task and deletes it. + void RunTask(already_AddRefed<nsIRunnable> task); + + // Calls RunTask or queues the pending_task on the deferred task list if it + // cannot be run right now. Returns true if the task was run. + bool DeferOrRunPendingTask(PendingTask&& pending_task); + + // Adds the pending task to delayed_work_queue_. + void AddToDelayedWorkQueue(const PendingTask& pending_task); + + // Load tasks from the incoming_queue_ into work_queue_ if the latter is + // empty. The former requires a lock to access, while the latter is directly + // accessible on this thread. + void ReloadWorkQueue(); + + // Delete tasks that haven't run yet without running them. Used in the + // destructor to make sure all the task's destructors get called. Returns + // true if some work was done. + bool DeletePendingTasks(); + + // Post a task to our incomming queue. + void PostTask_Helper(already_AddRefed<nsIRunnable> task, int delay_ms); + + // base::MessagePump::Delegate methods: + virtual bool DoWork() override; + virtual bool DoDelayedWork(base::TimeTicks* next_delayed_work_time) override; + virtual bool DoIdleWork() override; + + Type type_; + int32_t id_; + + // A list of tasks that need to be processed by this instance. Note that + // this queue is only accessed (push/pop) by our current thread. + TaskQueue work_queue_; + + // Contains delayed tasks, sorted by their 'delayed_run_time' property. + DelayedTaskQueue delayed_work_queue_; + + // A queue of non-nestable tasks that we had to defer because when it came + // time to execute them we were in a nested message loop. They will execute + // once we're out of nested message loops. + TaskQueue deferred_non_nestable_work_queue_; + + RefPtr<base::MessagePump> pump_; + + base::ObserverList<DestructionObserver> destruction_observers_; + + // A recursion block that prevents accidentally running additonal tasks when + // insider a (accidentally induced?) nested message pump. + bool nestable_tasks_allowed_; + + bool exception_restoration_; + + std::string thread_name_; + + // A null terminated list which creates an incoming_queue of tasks that are + // aquired under a mutex for processing on this instance's thread. These tasks + // have not yet been sorted out into items for our work_queue_ vs items that + // will be handled by the TimerManager. + TaskQueue incoming_queue_ MOZ_GUARDED_BY(incoming_queue_lock_); + // Protect access to incoming_queue_. + mozilla::Mutex incoming_queue_lock_; + + RunState* state_; + int run_depth_base_; + bool shutting_down_; + +#if defined(XP_WIN) + // Should be set to true before calling Windows APIs like TrackPopupMenu, etc + // which enter a modal message loop. + bool os_modal_loop_; +#endif + + // Timeout values for hang monitoring + uint32_t transient_hang_timeout_; + uint32_t permanent_hang_timeout_; + + // The next sequence number to use for delayed tasks. + int next_sequence_num_; + + class EventTarget; + RefPtr<EventTarget> mEventTarget; + + DISALLOW_COPY_AND_ASSIGN(MessageLoop); +}; + +//----------------------------------------------------------------------------- +// MessageLoopForUI extends MessageLoop with methods that are particular to a +// MessageLoop instantiated with TYPE_UI. +// +// This class is typically used like so: +// MessageLoopForUI::current()->...call some method... +// +class MessageLoopForUI : public MessageLoop { + public: + explicit MessageLoopForUI(Type aType = TYPE_UI) : MessageLoop(aType) {} + + // Returns the MessageLoopForUI of the current thread. + static MessageLoopForUI* current() { + MessageLoop* loop = MessageLoop::current(); + if (!loop) return NULL; + Type type = loop->type(); + DCHECK(type == MessageLoop::TYPE_UI || + type == MessageLoop::TYPE_MOZILLA_PARENT || + type == MessageLoop::TYPE_MOZILLA_CHILD); + return static_cast<MessageLoopForUI*>(loop); + } + +#if defined(XP_WIN) + typedef base::MessagePumpWin::Dispatcher Dispatcher; + typedef base::MessagePumpWin::Observer Observer; + + // Please see MessagePumpWin for definitions of these methods. + void Run(Dispatcher* dispatcher); + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + void WillProcessMessage(const MSG& message); + void DidProcessMessage(const MSG& message); + void PumpOutPendingPaintMessages(); + + protected: + // TODO(rvargas): Make this platform independent. + base::MessagePumpForUI* pump_ui() { + return static_cast<base::MessagePumpForUI*>(pump_.get()); + } +#endif // defined(XP_WIN) +}; + +// Do not add any member variables to MessageLoopForUI! This is important b/c +// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra +// data that you need should be stored on the MessageLoop's pump_ instance. +COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI), + MessageLoopForUI_should_not_have_extra_member_variables); + +//----------------------------------------------------------------------------- +// MessageLoopForIO extends MessageLoop with methods that are particular to a +// MessageLoop instantiated with TYPE_IO. +// +// This class is typically used like so: +// MessageLoopForIO::current()->...call some method... +// +class MessageLoopForIO : public MessageLoop { + public: + MessageLoopForIO() : MessageLoop(TYPE_IO) {} + + // Returns the MessageLoopForIO of the current thread. + static MessageLoopForIO* current() { + MessageLoop* loop = MessageLoop::current(); + DCHECK_EQ(MessageLoop::TYPE_IO, loop->type()); + return static_cast<MessageLoopForIO*>(loop); + } + +#if defined(XP_WIN) + typedef base::MessagePumpForIO::IOHandler IOHandler; + typedef base::MessagePumpForIO::IOContext IOContext; + + // Please see MessagePumpWin for definitions of these methods. + void RegisterIOHandler(HANDLE file_handle, IOHandler* handler); + bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); + + protected: + // TODO(rvargas): Make this platform independent. + base::MessagePumpForIO* pump_io() { + return static_cast<base::MessagePumpForIO*>(pump_.get()); + } + +#else + typedef base::MessagePumpLibevent::Watcher Watcher; + typedef base::MessagePumpLibevent::FileDescriptorWatcher + FileDescriptorWatcher; + + enum Mode { + WATCH_READ = base::MessagePumpLibevent::WATCH_READ, + WATCH_WRITE = base::MessagePumpLibevent::WATCH_WRITE, + WATCH_READ_WRITE = base::MessagePumpLibevent::WATCH_READ_WRITE + }; + + // Please see MessagePumpLibevent for definition. + bool WatchFileDescriptor(int fd, bool persistent, Mode mode, + FileDescriptorWatcher* controller, + Watcher* delegate); + + typedef base::MessagePumpLibevent::SignalEvent SignalEvent; + typedef base::MessagePumpLibevent::SignalWatcher SignalWatcher; + bool CatchSignal(int sig, SignalEvent* sigevent, SignalWatcher* delegate); + +#endif +}; + +// Do not add any member variables to MessageLoopForIO! This is important b/c +// MessageLoopForIO is often allocated via MessageLoop(TYPE_IO). Any extra +// data that you need should be stored on the MessageLoop's pump_ instance. +COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForIO), + MessageLoopForIO_should_not_have_extra_member_variables); + +#endif // BASE_MESSAGE_LOOP_H_ diff --git a/ipc/chromium/src/base/message_pump.h b/ipc/chromium/src/base/message_pump.h new file mode 100644 index 0000000000..884c016d20 --- /dev/null +++ b/ipc/chromium/src/base/message_pump.h @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_H_ +#define BASE_MESSAGE_PUMP_H_ + +#include "nsISupportsImpl.h" + +class nsISerialEventTarget; + +namespace base { + +class TimeTicks; + +class MessagePump { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessagePump) + + // Please see the comments above the Run method for an illustration of how + // these delegate methods are used. + class Delegate { + public: + virtual ~Delegate() {} + + // Called from within Run in response to ScheduleWork or when the message + // pump would otherwise call DoDelayedWork. Returns true to indicate that + // work was done. DoDelayedWork will not be called if DoWork returns true. + virtual bool DoWork() = 0; + + // Called from within Run in response to ScheduleDelayedWork or when the + // message pump would otherwise sleep waiting for more work. Returns true + // to indicate that delayed work was done. DoIdleWork will not be called + // if DoDelayedWork returns true. Upon return |next_delayed_work_time| + // indicates the time when DoDelayedWork should be called again. If + // |next_delayed_work_time| is null (per Time::is_null), then the queue of + // future delayed work (timer events) is currently empty, and no additional + // calls to this function need to be scheduled. + virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0; + + // Called from within Run just before the message pump goes to sleep. + // Returns true to indicate that idle work was done. + virtual bool DoIdleWork() = 0; + }; + + // The Run method is called to enter the message pump's run loop. + // + // Within the method, the message pump is responsible for processing native + // messages as well as for giving cycles to the delegate periodically. The + // message pump should take care to mix delegate callbacks with native + // message processing so neither type of event starves the other of cycles. + // + // The anatomy of a typical run loop: + // + // for (;;) { + // bool did_work = DoInternalWork(); + // if (should_quit_) + // break; + // + // did_work |= delegate_->DoWork(); + // if (should_quit_) + // break; + // + // did_work |= delegate_->DoDelayedWork(); + // if (should_quit_) + // break; + // + // if (did_work) + // continue; + // + // did_work = delegate_->DoIdleWork(); + // if (should_quit_) + // break; + // + // if (did_work) + // continue; + // + // WaitForWork(); + // } + // + // Here, DoInternalWork is some private method of the message pump that is + // responsible for dispatching the next UI message or notifying the next IO + // completion (for example). WaitForWork is a private method that simply + // blocks until there is more work of any type to do. + // + // Notice that the run loop cycles between calling DoInternalWork, DoWork, + // and DoDelayedWork methods. This helps ensure that neither work queue + // starves the other. This is important for message pumps that are used to + // drive animations, for example. + // + // Notice also that after each callout to foreign code, the run loop checks + // to see if it should quit. The Quit method is responsible for setting this + // flag. No further work is done once the quit flag is set. + // + // NOTE: Care must be taken to handle Run being called again from within any + // of the callouts to foreign code. Native message pumps may also need to + // deal with other native message pumps being run outside their control + // (e.g., the MessageBox API on Windows pumps UI messages!). To be specific, + // the callouts (DoWork and DoDelayedWork) MUST still be provided even in + // nested sub-loops that are "seemingly" outside the control of this message + // pump. DoWork in particular must never be starved for time slices unless + // it returns false (meaning it has run out of things to do). + // + virtual void Run(Delegate* delegate) = 0; + + // Quit immediately from the most recently entered run loop. This method may + // only be used on the thread that called Run. + virtual void Quit() = 0; + + // Schedule a DoWork callback to happen reasonably soon. Does nothing if a + // DoWork callback is already scheduled. This method may be called from any + // thread. Once this call is made, DoWork should not be "starved" at least + // until it returns a value of false. + virtual void ScheduleWork() = 0; + + // This method may only called from the thread that called Run. + // + // Ensure that DoWork will be called if a nested loop is entered. + // If a MessagePump can already guarantee that DoWork will be called + // "reasonably soon", this method can be a no-op to avoid expensive + // atomic tests and/or syscalls required for ScheduleWork(). + virtual void ScheduleWorkForNestedLoop() { ScheduleWork(); }; + + // Schedule a DoDelayedWork callback to happen at the specified time, + // cancelling any pending DoDelayedWork callback. This method may only be + // used on the thread that called Run. + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0; + + // If returned, just use the nsThread. + virtual nsISerialEventTarget* GetXPCOMThread() { return nullptr; } + + protected: + virtual ~MessagePump(){}; +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_H_ diff --git a/ipc/chromium/src/base/message_pump_android.cc b/ipc/chromium/src/base/message_pump_android.cc new file mode 100644 index 0000000000..6d35534e5c --- /dev/null +++ b/ipc/chromium/src/base/message_pump_android.cc @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_android.h" + +#include <fcntl.h> +#include <math.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace mozilla { +bool ProcessNextEvent(); +void NotifyEvent(); +} // namespace mozilla + +namespace base { + +MessagePumpForUI::MessagePumpForUI() : state_(NULL) {} + +MessagePumpForUI::~MessagePumpForUI() {} + +void MessagePumpForUI::Run(Delegate* delegate) { + RunState state; + state.delegate = delegate; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunAllPending() works correctly. + state.more_work_is_plausible = true; + + RunState* previous_state = state_; + state_ = &state; + + // We run our own loop instead of using g_main_loop_quit in one of the + // callbacks. This is so we only quit our own loops, and we don't quit + // nested loops run by others. TODO(deanm): Is this what we want? + + while (!state_->should_quit) { + mozilla::ProcessNextEvent(); + if (work_scheduled) { + work_scheduled = false; + HandleDispatch(); + } + } + + state_ = previous_state; +} + +void MessagePumpForUI::HandleDispatch() { + // We should only ever have a single message on the wakeup pipe, since we + // are only signaled when the queue went from empty to non-empty. The qApp + // poll will tell us whether there was data, so this read shouldn't block. + if (state_->should_quit) return; + + state_->more_work_is_plausible = false; + + if (state_->delegate->DoWork()) state_->more_work_is_plausible = true; + + if (state_->should_quit) return; + + if (state_->delegate->DoDelayedWork(&delayed_work_time_)) + state_->more_work_is_plausible = true; + if (state_->should_quit) return; + + // Don't do idle work if we think there are more important things + // that we could be doing. + if (state_->more_work_is_plausible) return; + + if (state_->delegate->DoIdleWork()) state_->more_work_is_plausible = true; + if (state_->should_quit) return; +} + +void MessagePumpForUI::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpForUI::ScheduleWork() { + // This can be called on any thread, so we don't want to touch any state + // variables as we would then need locks all over. This ensures that if + // we are sleeping in a poll that we will wake up. + work_scheduled = true; + mozilla::NotifyEvent(); +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // We need to wake up the loop in case the poll timeout needs to be + // adjusted. This will cause us to try to do work, but that's ok. + delayed_work_time_ = delayed_work_time; + ScheduleWork(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_android.h b/ipc/chromium/src/base/message_pump_android.h new file mode 100644 index 0000000000..8dd3fdac86 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_android.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_ANDROID_H_ +#define BASE_MESSAGE_PUMP_ANDROID_H_ + +#include "base/message_pump.h" +#include "base/time.h" + +namespace base { + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// Android +class MessagePumpForUI : public MessagePump { + public: + MessagePumpForUI(); + ~MessagePumpForUI(); + + virtual void Run(Delegate* delegate); + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. + // HandleDispatch is called after the poll has completed. + void HandleDispatch(); + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState { + Delegate* delegate; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // Used internally for controlling whether we want a message pump + // iteration to be blocking or not. + bool more_work_is_plausible; + }; + + RunState* state_; + + // This is the time when we need to do delayed work. + TimeTicks delayed_work_time_; + + bool work_scheduled; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_ANDROID_H_ diff --git a/ipc/chromium/src/base/message_pump_default.cc b/ipc/chromium/src/base/message_pump_default.cc new file mode 100644 index 0000000000..9db5789f1a --- /dev/null +++ b/ipc/chromium/src/base/message_pump_default.cc @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_default.h" + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_nsautorelease_pool.h" + +#include "mozilla/BackgroundHangMonitor.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/ProfilerThreadSleep.h" + +namespace base { + +MessagePumpDefault::MessagePumpDefault() + : keep_running_(true), event_(false, false) {} + +void MessagePumpDefault::Run(Delegate* delegate) { + AUTO_PROFILER_LABEL("MessagePumpDefault::Run", OTHER); + + DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + + const MessageLoop* const loop = MessageLoop::current(); + mozilla::BackgroundHangMonitor hangMonitor(loop->thread_name().c_str(), + loop->transient_hang_timeout(), + loop->permanent_hang_timeout()); + + for (;;) { + ScopedNSAutoreleasePool autorelease_pool; + + hangMonitor.NotifyActivity(); + bool did_work = delegate->DoWork(); + if (!keep_running_) break; + + hangMonitor.NotifyActivity(); + did_work |= delegate->DoDelayedWork(&delayed_work_time_); + if (!keep_running_) break; + + if (did_work) continue; + + hangMonitor.NotifyActivity(); + did_work = delegate->DoIdleWork(); + if (!keep_running_) break; + + if (did_work) continue; + + if (delayed_work_time_.is_null()) { + hangMonitor.NotifyWait(); + AUTO_PROFILER_LABEL("MessagePumpDefault::Run:Wait", IDLE); + { + AUTO_PROFILER_THREAD_SLEEP; + event_.Wait(); + } + } else { + TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); + if (delay > TimeDelta()) { + hangMonitor.NotifyWait(); + AUTO_PROFILER_LABEL("MessagePumpDefault::Run:Wait", IDLE); + { + AUTO_PROFILER_THREAD_SLEEP; + event_.TimedWait(delay); + } + } else { + // It looks like delayed_work_time_ indicates a time in the past, so we + // need to call DoDelayedWork now. + delayed_work_time_ = TimeTicks(); + } + } + // Since event_ is auto-reset, we don't need to do anything special here + // other than service each delegate method. + } + + keep_running_ = true; +} + +void MessagePumpDefault::Quit() { keep_running_ = false; } + +void MessagePumpDefault::ScheduleWork() { + // Since this can be called on any thread, we need to ensure that our Run + // loop wakes up. + event_.Signal(); +} + +void MessagePumpDefault::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_default.h b/ipc/chromium/src/base/message_pump_default.h new file mode 100644 index 0000000000..99abaf6580 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_default.h @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_DEFAULT_H_ +#define BASE_MESSAGE_PUMP_DEFAULT_H_ + +#include "base/message_pump.h" +#include "base/time.h" +#include "base/waitable_event.h" + +namespace base { + +class MessagePumpDefault : public MessagePump { + public: + MessagePumpDefault(); + ~MessagePumpDefault() {} + + // MessagePump methods: + virtual void Run(Delegate* delegate) override; + virtual void Quit() override; + virtual void ScheduleWork() override; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; + + protected: + // This flag is set to false when Run should return. + bool keep_running_; + + // Used to sleep until there is more work to do. + WaitableEvent event_; + + // The time at which we should call DoDelayedWork. + TimeTicks delayed_work_time_; + + private: + DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_DEFAULT_H_ diff --git a/ipc/chromium/src/base/message_pump_glib.cc b/ipc/chromium/src/base/message_pump_glib.cc new file mode 100644 index 0000000000..7f3dc0f965 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_glib.cc @@ -0,0 +1,291 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_glib.h" + +#include <unistd.h> +#include <math.h> + +#include <gtk/gtk.h> +#include <glib.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace { + +// Return a timeout suitable for the glib loop, -1 to block forever, +// 0 to return right away, or a timeout in milliseconds from now. +int GetTimeIntervalMilliseconds(const base::TimeTicks& from) { + if (from.is_null()) return -1; + + // Be careful here. TimeDelta has a precision of microseconds, but we want a + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or + // 6? It should be 6 to avoid executing delayed work too early. + int delay = + static_cast<int>(ceil((from - base::TimeTicks::Now()).InMillisecondsF())); + + // If this value is negative, then we need to run delayed work soon. + return delay < 0 ? 0 : delay; +} + +// A brief refresher on GLib: +// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize. +// On each iteration of the GLib pump, it calls each source's Prepare function. +// This function should return TRUE if it wants GLib to call its Dispatch, and +// FALSE otherwise. It can also set a timeout in this case for the next time +// Prepare should be called again (it may be called sooner). +// After the Prepare calls, GLib does a poll to check for events from the +// system. File descriptors can be attached to the sources. The poll may block +// if none of the Prepare calls returned TRUE. It will block indefinitely, or +// by the minimum time returned by a source in Prepare. +// After the poll, GLib calls Check for each source that returned FALSE +// from Prepare. The return value of Check has the same meaning as for Prepare, +// making Check a second chance to tell GLib we are ready for Dispatch. +// Finally, GLib calls Dispatch for each source that is ready. If Dispatch +// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive +// (i.e., you can call Run from them), but Prepare and Check cannot. +// Finalize is called when the source is destroyed. +// NOTE: It is common for subsytems to want to process pending events while +// doing intensive work, for example the flash plugin. They usually use the +// following pattern (recommended by the GTK docs): +// while (gtk_events_pending()) { +// gtk_main_iteration(); +// } +// +// gtk_events_pending just calls g_main_context_pending, which does the +// following: +// - Call prepare on all the sources. +// - Do the poll with a timeout of 0 (not blocking). +// - Call check on all the sources. +// - *Does not* call dispatch on the sources. +// - Return true if any of prepare() or check() returned true. +// +// gtk_main_iteration just calls g_main_context_iteration, which does the whole +// thing, respecting the timeout for the poll (and block, although it is +// expected not to if gtk_events_pending returned true), and call dispatch. +// +// Thus it is important to only return true from prepare or check if we +// actually have events or work to do. We also need to make sure we keep +// internal state consistent so that if prepare/check return true when called +// from gtk_events_pending, they will still return true when called right +// after, from gtk_main_iteration. +// +// For the GLib pump we try to follow the Windows UI pump model: +// - Whenever we receive a wakeup event or the timer for delayed work expires, +// we run DoWork and/or DoDelayedWork. That part will also run in the other +// event pumps. +// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main +// loop, around event handling. + +struct WorkSource : public GSource { + base::MessagePumpForUI* pump; +}; + +gboolean WorkSourcePrepare(GSource* source, gint* timeout_ms) { + *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare(); + // We always return FALSE, so that our timeout is honored. If we were + // to return TRUE, the timeout would be considered to be 0 and the poll + // would never block. Once the poll is finished, Check will be called. + return FALSE; +} + +gboolean WorkSourceCheck(GSource* source) { + // Only return TRUE if Dispatch should be called. + return static_cast<WorkSource*>(source)->pump->HandleCheck(); +} + +gboolean WorkSourceDispatch(GSource* source, GSourceFunc unused_func, + gpointer unused_data) { + static_cast<WorkSource*>(source)->pump->HandleDispatch(); + // Always return TRUE so our source stays registered. + return TRUE; +} + +// I wish these could be const, but g_source_new wants non-const. +GSourceFuncs WorkSourceFuncs = {WorkSourcePrepare, WorkSourceCheck, + WorkSourceDispatch, NULL}; + +} // namespace + +namespace base { + +MessagePumpForUI::MessagePumpForUI() + : state_(NULL), + context_(g_main_context_default()), + wakeup_gpollfd_(new GPollFD), + pipe_full_(false) { + // Create our wakeup pipe, which is used to flag when work was scheduled. + int fds[2]; + CHECK(pipe(fds) == 0); + wakeup_pipe_read_ = fds[0]; + wakeup_pipe_write_ = fds[1]; + wakeup_gpollfd_->fd = wakeup_pipe_read_; + wakeup_gpollfd_->events = G_IO_IN; + + work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); + static_cast<WorkSource*>(work_source_)->pump = this; + g_source_add_poll(work_source_, wakeup_gpollfd_.get()); + // Use a low priority so that we let other events in the queue go first. + g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); + // This is needed to allow Run calls inside Dispatch. + g_source_set_can_recurse(work_source_, TRUE); + g_source_attach(work_source_, context_); +} + +MessagePumpForUI::~MessagePumpForUI() { + gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event), this, + NULL); + g_source_destroy(work_source_); + g_source_unref(work_source_); + close(wakeup_pipe_read_); + close(wakeup_pipe_write_); +} + +void MessagePumpForUI::Run(Delegate* delegate) { +#ifndef NDEBUG + // Make sure we only run this on one thread. GTK only has one message pump + // so we can only have one UI loop per process. + static PlatformThreadId thread_id = PlatformThread::CurrentId(); + DCHECK(thread_id == PlatformThread::CurrentId()) + << "Running MessagePumpForUI on two different threads; " + "this is unsupported by GLib!"; +#endif + + RunState state; + state.delegate = delegate; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + state.has_work = false; + + RunState* previous_state = state_; + state_ = &state; + + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunAllPending() works correctly. + bool more_work_is_plausible = true; + + // We run our own loop instead of using g_main_loop_quit in one of the + // callbacks. This is so we only quit our own loops, and we don't quit + // nested loops run by others. TODO(deanm): Is this what we want? + for (;;) { + // Don't block if we think we have more work to do. + bool block = !more_work_is_plausible; + + // g_main_context_iteration returns true if events have been dispatched. + more_work_is_plausible = g_main_context_iteration(context_, block); + if (state_->should_quit) break; + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) break; + + if (more_work_is_plausible) continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) break; + } + + state_ = previous_state; +} + +// Return the timeout we want passed to poll. +int MessagePumpForUI::HandlePrepare() { + // We know we have work, but we haven't called HandleDispatch yet. Don't let + // the pump block so that we can do some processing. + if (state_ && // state_ may be null during tests. + state_->has_work) + return 0; + + // We don't think we have work to do, but make sure not to block + // longer than the next time we need to run delayed work. + return GetTimeIntervalMilliseconds(delayed_work_time_); +} + +bool MessagePumpForUI::HandleCheck() { + if (!state_) // state_ may be null during tests. + return false; + + // We should only ever have a single message on the wakeup pipe since we only + // write to the pipe when pipe_full_ is false. The glib poll will tell us + // whether there was data, so this read shouldn't block. + if (wakeup_gpollfd_->revents & G_IO_IN) { + pipe_full_ = false; + + char msg; + if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') { + NOTREACHED() << "Error reading from the wakeup pipe."; + } + // Since we ate the message, we need to record that we have more work, + // because HandleCheck() may be called without HandleDispatch being called + // afterwards. + state_->has_work = true; + } + + if (state_->has_work) return true; + + if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) { + // The timer has expired. That condition will stay true until we process + // that delayed work, so we don't need to record this differently. + return true; + } + + return false; +} + +void MessagePumpForUI::HandleDispatch() { + state_->has_work = false; + if (state_->delegate->DoWork()) { + // NOTE: on Windows at this point we would call ScheduleWork (see + // MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here, + // instead of posting a message on the wakeup pipe, we can avoid the + // syscalls and just signal that we have more work. + state_->has_work = true; + } + + if (state_->should_quit) return; + + state_->delegate->DoDelayedWork(&delayed_work_time_); +} + +void MessagePumpForUI::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpForUI::ScheduleWork() { + bool was_full = pipe_full_.exchange(true); + if (was_full) { + return; + } + + // This can be called on any thread, so we don't want to touch any state + // variables as we would then need locks all over. This ensures that if + // we are sleeping in a poll that we will wake up. + char msg = '!'; + if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) { + NOTREACHED() << "Could not write to the UI message loop wakeup pipe!"; + } +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // We need to wake up the loop in case the poll timeout needs to be + // adjusted. This will cause us to try to do work, but that's ok. + delayed_work_time_ = delayed_work_time; + ScheduleWork(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_glib.h b/ipc/chromium/src/base/message_pump_glib.h new file mode 100644 index 0000000000..c7888b25b9 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_glib.h @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_GLIB_H_ +#define BASE_MESSAGE_PUMP_GLIB_H_ + +#include "base/message_pump.h" +#include "base/observer_list.h" +#include "base/time.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Atomics.h" + +typedef struct _GMainContext GMainContext; +typedef struct _GPollFD GPollFD; +typedef struct _GSource GSource; + +namespace base { + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// XP_LINUX platforms using GLib. +class MessagePumpForUI : public MessagePump { + public: + MessagePumpForUI(); + virtual ~MessagePumpForUI(); + + void Run(Delegate* delegate) override; + void Quit() override; + void ScheduleWork() override; + void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. HandlePrepare + // is called during the prepare step of glib, and returns a timeout that + // will be passed to the poll. HandleCheck is called after the poll + // has completed, and returns whether or not HandleDispatch should be called. + // HandleDispatch is called if HandleCheck returned true. + int HandlePrepare(); + bool HandleCheck(); + void HandleDispatch(); + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState { + Delegate* delegate; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // This keeps the state of whether the pump got signaled that there was new + // work to be done. Since we eat the message on the wake up pipe as soon as + // we get it, we keep that state here to stay consistent. + bool has_work; + }; + + RunState* state_; + + // This is a GLib structure that we can add event sources to. We use the + // default GLib context, which is the one to which all GTK events are + // dispatched. + GMainContext* context_; + + // This is the time when we need to do delayed work. + TimeTicks delayed_work_time_; + + // The work source. It is shared by all calls to Run and destroyed when + // the message pump is destroyed. + GSource* work_source_; + + // We use a wakeup pipe to make sure we'll get out of the glib polling phase + // when another thread has scheduled us to do some work. There is a glib + // mechanism g_main_context_wakeup, but this won't guarantee that our event's + // Dispatch() will be called. + int wakeup_pipe_read_; + int wakeup_pipe_write_; + // Use an autoptr to avoid needing the definition of GPollFD in the header. + mozilla::UniquePtr<GPollFD> wakeup_gpollfd_; + + mozilla::Atomic<bool> pipe_full_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_GLIB_H_ diff --git a/ipc/chromium/src/base/message_pump_libevent.cc b/ipc/chromium/src/base/message_pump_libevent.cc new file mode 100644 index 0000000000..004001fddd --- /dev/null +++ b/ipc/chromium/src/base/message_pump_libevent.cc @@ -0,0 +1,410 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_libevent.h" + +#include <errno.h> +#include <fcntl.h> +#if defined(ANDROID) || defined(XP_UNIX) +# include <unistd.h> +#endif + +#include "eintr_wrapper.h" +#include "base/logging.h" +#include "base/scoped_nsautorelease_pool.h" +#include "base/time.h" +#include "event.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/ProfilerThreadSleep.h" +#include "mozilla/UniquePtr.h" + +// This macro checks that the _EVENT_SIZEOF_* constants defined in +// ipc/chromiume/src/third_party/<platform>/event2/event-config.h are correct. +#if defined(_EVENT_SIZEOF_SHORT) +# define CHECK_EVENT_SIZEOF(TYPE, type) \ + static_assert(_EVENT_SIZEOF_##TYPE == sizeof(type), \ + "bad _EVENT_SIZEOF_" #TYPE); +#elif defined(EVENT__SIZEOF_SHORT) +# define CHECK_EVENT_SIZEOF(TYPE, type) \ + static_assert(EVENT__SIZEOF_##TYPE == sizeof(type), \ + "bad EVENT__SIZEOF_" #TYPE); +#else +# error Cannot find libevent type sizes +#endif + +CHECK_EVENT_SIZEOF(LONG, long); +CHECK_EVENT_SIZEOF(LONG_LONG, long long); +CHECK_EVENT_SIZEOF(OFF_T, ev_off_t); +CHECK_EVENT_SIZEOF(PTHREAD_T, pthread_t); +CHECK_EVENT_SIZEOF(SHORT, short); +CHECK_EVENT_SIZEOF(SIZE_T, size_t); +CHECK_EVENT_SIZEOF(TIME_T, time_t); +CHECK_EVENT_SIZEOF(VOID_P, void*); + +// Lifecycle of struct event +// Libevent uses two main data structures: +// struct event_base (of which there is one per message pump), and +// struct event (of which there is roughly one per socket). +// The socket's struct event is created in +// MessagePumpLibevent::WatchFileDescriptor(), +// is owned by the FileDescriptorWatcher, and is destroyed in +// StopWatchingFileDescriptor(). +// It is moved into and out of lists in struct event_base by +// the libevent functions event_add() and event_del(). +// +// TODO(dkegel): +// At the moment bad things happen if a FileDescriptorWatcher +// is active after its MessagePumpLibevent has been destroyed. +// See MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop +// Not clear yet whether that situation occurs in practice, +// but if it does, we need to fix it. + +namespace base { + +// Return 0 on success +// Too small a function to bother putting in a library? +static int SetNonBlocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher() + : is_persistent_(false), event_(NULL) {} + +MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() { + if (event_) { + StopWatchingFileDescriptor(); + } +} + +void MessagePumpLibevent::FileDescriptorWatcher::Init(event* e, + bool is_persistent) { + DCHECK(e); + DCHECK(event_ == NULL); + + is_persistent_ = is_persistent; + event_ = e; +} + +event* MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() { + struct event* e = event_; + event_ = NULL; + return e; +} + +bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() { + event* e = ReleaseEvent(); + if (e == NULL) return true; + + // event_del() is a no-op if the event isn't active. + int rv = event_del(e); + delete e; + return (rv == 0); +} + +bool MessagePumpLibevent::awake_ = false; + +// Called if a byte is received on the wakeup pipe. +void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) { + if (!awake_) { + profiler_thread_wake(); + awake_ = true; + } + + AUTO_PROFILER_LABEL("MessagePumpLibevent::OnWakeup", OTHER); + + base::MessagePumpLibevent* that = + static_cast<base::MessagePumpLibevent*>(context); + DCHECK(that->wakeup_pipe_out_ == socket); + + // Remove and discard the wakeup byte. + char buf; + int nread = HANDLE_EINTR(read(socket, &buf, 1)); + DCHECK_EQ(nread, 1); + // Tell libevent to break out of inner loop. + event_base_loopbreak(that->event_base_); +} + +MessagePumpLibevent::MessagePumpLibevent() + : keep_running_(true), + in_run_(false), + event_base_(event_base_new()), + wakeup_pipe_in_(-1), + wakeup_pipe_out_(-1) { + if (!Init()) NOTREACHED(); +} + +bool MessagePumpLibevent::Init() { + int fds[2]; + if (pipe(fds)) { + DLOG(ERROR) << "pipe() failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[0])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[1])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno; + return false; + } + wakeup_pipe_out_ = fds[0]; + wakeup_pipe_in_ = fds[1]; + + wakeup_event_ = new event; + event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST, OnWakeup, + this); + event_base_set(event_base_, wakeup_event_); + + if (event_add(wakeup_event_, 0)) return false; + return true; +} + +MessagePumpLibevent::~MessagePumpLibevent() { + DCHECK(wakeup_event_); + DCHECK(event_base_); + event_del(wakeup_event_); + delete wakeup_event_; + if (wakeup_pipe_in_ >= 0) close(wakeup_pipe_in_); + if (wakeup_pipe_out_ >= 0) close(wakeup_pipe_out_); + event_base_free(event_base_); +} + +bool MessagePumpLibevent::WatchFileDescriptor(int fd, bool persistent, + Mode mode, + FileDescriptorWatcher* controller, + Watcher* delegate) { + DCHECK(fd > 0); + DCHECK(controller); + DCHECK(delegate); + DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); + + int event_mask = persistent ? EV_PERSIST : 0; + if ((mode & WATCH_READ) != 0) { + event_mask |= EV_READ; + } + if ((mode & WATCH_WRITE) != 0) { + event_mask |= EV_WRITE; + } + + // |should_delete_event| is true if we're modifying an event that's currently + // active in |controller|. + // If we're modifying an existing event and there's an error then we need to + // tell libevent to clean it up via event_delete() before returning. + bool should_delete_event = true; + mozilla::UniquePtr<event> evt(controller->ReleaseEvent()); + if (evt.get() == NULL) { + should_delete_event = false; + // Ownership is transferred to the controller. + evt = mozilla::MakeUnique<event>(); + } else { + // It's illegal to use this function to listen on 2 separate fds with the + // same |controller|. + if (EVENT_FD(evt.get()) != fd) { + NOTREACHED() << "FDs don't match" << EVENT_FD(evt.get()) << "!=" << fd; + return false; + } + + // Make sure we don't pick up any funky internal libevent masks. + int old_interest_mask = + evt.get()->ev_events & (EV_READ | EV_WRITE | EV_PERSIST); + + // Combine old/new event masks. + event_mask |= old_interest_mask; + + // Must disarm the event before we can reuse it. + event_del(evt.get()); + } + + // Set current interest mask and message pump for this event. + event_set(evt.get(), fd, event_mask, OnLibeventNotification, delegate); + + // Tell libevent which message pump this socket will belong to when we add it. + if (event_base_set(event_base_, evt.get()) != 0) { + if (should_delete_event) { + event_del(evt.get()); + } + return false; + } + + // Add this socket to the list of monitored sockets. + if (event_add(evt.get(), NULL) != 0) { + if (should_delete_event) { + event_del(evt.get()); + } + return false; + } + + // Transfer ownership of evt to controller. + controller->Init(evt.release(), persistent); + return true; +} + +void MessagePumpLibevent::OnLibeventNotification(int fd, short flags, + void* context) { + if (!awake_) { + profiler_thread_wake(); + awake_ = true; + } + AUTO_PROFILER_LABEL("MessagePumpLibevent::OnLibeventNotification", OTHER); + + Watcher* watcher = static_cast<Watcher*>(context); + + if (flags & EV_WRITE) { + watcher->OnFileCanWriteWithoutBlocking(fd); + } + if (flags & EV_READ) { + watcher->OnFileCanReadWithoutBlocking(fd); + } +} + +MessagePumpLibevent::SignalEvent::SignalEvent() : event_(NULL) {} + +MessagePumpLibevent::SignalEvent::~SignalEvent() { + if (event_) { + StopCatching(); + } +} + +void MessagePumpLibevent::SignalEvent::Init(event* e) { + DCHECK(e); + DCHECK(event_ == NULL); + event_ = e; +} + +bool MessagePumpLibevent::SignalEvent::StopCatching() { + // XXX/cjones: this code could be shared with + // FileDescriptorWatcher. ironic that libevent is "more" + // object-oriented than this C++ + event* e = ReleaseEvent(); + if (e == NULL) return true; + + // event_del() is a no-op if the event isn't active. + int rv = event_del(e); + delete e; + return (rv == 0); +} + +event* MessagePumpLibevent::SignalEvent::ReleaseEvent() { + event* e = event_; + event_ = NULL; + return e; +} + +bool MessagePumpLibevent::CatchSignal(int sig, SignalEvent* sigevent, + SignalWatcher* delegate) { + DCHECK(sig > 0); + DCHECK(sigevent); + DCHECK(delegate); + // TODO if we want to support re-using SignalEvents, this code needs + // to jump through the same hoops as WatchFileDescriptor(). Not + // needed at present + DCHECK(NULL == sigevent->event_); + + mozilla::UniquePtr<event> evt = mozilla::MakeUnique<event>(); + signal_set(evt.get(), sig, OnLibeventSignalNotification, delegate); + + if (event_base_set(event_base_, evt.get())) return false; + + if (signal_add(evt.get(), NULL)) return false; + + // Transfer ownership of evt to controller. + sigevent->Init(evt.release()); + return true; +} + +void MessagePumpLibevent::OnLibeventSignalNotification(int sig, short flags, + void* context) { + if (!awake_) { + profiler_thread_wake(); + awake_ = true; + } + AUTO_PROFILER_LABEL("MessagePumpLibevent::OnLibeventSignalNotification", + OTHER); + + DCHECK(sig > 0); + DCHECK(EV_SIGNAL == flags); + DCHECK(context); + reinterpret_cast<SignalWatcher*>(context)->OnSignal(sig); +} + +// Reentrant! +void MessagePumpLibevent::Run(Delegate* delegate) { + DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + + bool old_in_run = in_run_; + in_run_ = true; + + for (;;) { + ScopedNSAutoreleasePool autorelease_pool; + + bool did_work = delegate->DoWork(); + if (!keep_running_) break; + + did_work |= delegate->DoDelayedWork(&delayed_work_time_); + if (!keep_running_) break; + + if (did_work) continue; + + did_work = delegate->DoIdleWork(); + if (!keep_running_) break; + + if (did_work) continue; + + // EVLOOP_ONCE tells libevent to only block once, + // but to service all pending events when it wakes up. + AUTO_PROFILER_LABEL("MessagePumpLibevent::Run::Wait", IDLE); + if (delayed_work_time_.is_null()) { + profiler_thread_sleep(); + awake_ = false; + event_base_loop(event_base_, EVLOOP_ONCE); + } else { + TimeDelta delay = delayed_work_time_ - TimeTicks::Now(); + if (delay > TimeDelta()) { + struct timeval poll_tv; + poll_tv.tv_sec = delay.InSeconds(); + poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond; + event_base_loopexit(event_base_, &poll_tv); + profiler_thread_sleep(); + awake_ = false; + event_base_loop(event_base_, EVLOOP_ONCE); + } else { + // It looks like delayed_work_time_ indicates a time in the past, so we + // need to call DoDelayedWork now. + delayed_work_time_ = TimeTicks(); + } + } + } + + keep_running_ = true; + in_run_ = old_in_run; +} + +void MessagePumpLibevent::Quit() { + DCHECK(in_run_); + // Tell both libevent and Run that they should break out of their loops. + keep_running_ = false; + ScheduleWork(); +} + +void MessagePumpLibevent::ScheduleWork() { + // Tell libevent (in a threadsafe way) that it should break out of its loop. + char buf = 0; + int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1)); + DCHECK(nwrite == 1 || errno == EAGAIN) + << "[nwrite:" << nwrite << "] [errno:" << errno << "]"; +} + +void MessagePumpLibevent::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_libevent.h b/ipc/chromium/src/base/message_pump_libevent.h new file mode 100644 index 0000000000..b1a7d474b3 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_libevent.h @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_LIBEVENT_H_ +#define BASE_MESSAGE_PUMP_LIBEVENT_H_ + +#include "base/message_pump.h" +#include "base/time.h" + +// Declare structs we need from libevent.h rather than including it +struct event_base; +struct event; + +namespace base { + +// Class to monitor sockets and issue callbacks when sockets are ready for I/O +// TODO(dkegel): add support for background file IO somehow +class MessagePumpLibevent : public MessagePump { + public: + // Object returned by WatchFileDescriptor to manage further watching. + class FileDescriptorWatcher { + public: + FileDescriptorWatcher(); + ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor. + + // NOTE: These methods aren't called StartWatching()/StopWatching() to + // avoid confusion with the win32 ObjectWatcher class. + + // Stop watching the FD, always safe to call. No-op if there's nothing + // to do. + bool StopWatchingFileDescriptor(); + + private: + // Called by MessagePumpLibevent, ownership of |e| is transferred to this + // object. + void Init(event* e, bool is_persistent); + + // Used by MessagePumpLibevent to take ownership of event_. + event* ReleaseEvent(); + friend class MessagePumpLibevent; + + private: + bool is_persistent_; // false if this event is one-shot. + event* event_; + DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher); + }; + + // Used with WatchFileDescptor to asynchronously monitor the I/O readiness of + // a File Descriptor. + class Watcher { + public: + virtual ~Watcher() {} + // Called from MessageLoop::Run when an FD can be read from/written to + // without blocking + virtual void OnFileCanReadWithoutBlocking(int fd) = 0; + virtual void OnFileCanWriteWithoutBlocking(int fd) = 0; + }; + + MessagePumpLibevent(); + + enum Mode { + WATCH_READ = 1 << 0, + WATCH_WRITE = 1 << 1, + WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE + }; + + // Have the current thread's message loop watch for a a situation in which + // reading/writing to the FD can be performed without Blocking. + // Callers must provide a preallocated FileDescriptorWatcher object which + // can later be used to manage the Lifetime of this event. + // If a FileDescriptorWatcher is passed in which is already attached to + // an event, then the effect is cumulative i.e. after the call |controller| + // will watch both the previous event and the new one. + // If an error occurs while calling this method in a cumulative fashion, the + // event previously attached to |controller| is aborted. + // Returns true on success. + // TODO(dkegel): switch to edge-triggered readiness notification + bool WatchFileDescriptor(int fd, bool persistent, Mode mode, + FileDescriptorWatcher* controller, + Watcher* delegate); + + // This is analagous to FileDescriptorWatcher above, which really is + // just a wrapper around libevent's |struct event|. This class acts + // as a sort of "scoped event watcher" in that it guarantees that + // when this class is out of scope, the signal-event it wraps is + // removed from libevent's guts. + // + // XXX/cjones: this isn't my favorite API, but preserving it in + // order to match code above + class SignalEvent { + friend class MessagePumpLibevent; + + public: + SignalEvent(); + ~SignalEvent(); // implicitly calls StopCatching() + + // Have libevent forget this event. + bool StopCatching(); + + private: + void Init(event* e); + event* ReleaseEvent(); + + event* event_; + + DISALLOW_COPY_AND_ASSIGN(SignalEvent); + }; + + class SignalWatcher { + public: + virtual ~SignalWatcher() {} + // Called from MessageLoop::Run when |sig| has been delivered to + // this process + virtual void OnSignal(int sig) = 0; + }; + + // Have the current thread's message loop catch the signal |sig|. + // Multiple watchers can catch the same signal; they're all notified + // upon its delivery. Callers must provide a preallocated + // SignalEvent object which can be used to manage the lifetime of + // this event. Returns true on success. + bool CatchSignal(int sig, SignalEvent* sigevent, SignalWatcher* delegate); + + // MessagePump methods: + virtual void Run(Delegate* delegate) override; + virtual void Quit() override; + virtual void ScheduleWork() override; + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override; + + protected: + virtual ~MessagePumpLibevent(); + + private: + // Risky part of constructor. Returns true on success. + bool Init(); + + // This flag is set to false when Run should return. + bool keep_running_; + + // This flag is set when inside Run. + bool in_run_; + + // The time at which we should call DoDelayedWork. + TimeTicks delayed_work_time_; + + // Libevent dispatcher. Watches all sockets registered with it, and sends + // readiness callbacks when a socket is ready for I/O. + event_base* event_base_; + + // Called by libevent to tell us a registered FD can be read/written to. + static void OnLibeventNotification(int fd, short flags, void* context); + + // Called by libevent upon receiving a signal + static void OnLibeventSignalNotification(int sig, short flags, void* context); + + // Unix pipe used to implement ScheduleWork() + // ... callback; called by libevent inside Run() when pipe is ready to read + static void OnWakeup(int socket, short flags, void* context); + // ... write end; ScheduleWork() writes a single byte to it + int wakeup_pipe_in_; + // ... read end; OnWakeup reads it and then breaks Run() out of its sleep + int wakeup_pipe_out_; + // ... libevent wrapper for read end + event* wakeup_event_; + + // Set to false when calling event_base_loop and to true when either of the + // OnWakeup, OnLibeventNotification or OnLibeventSignalNotification callbacks + // is called. + // Used to ensure our calls to profiler_thread_sleep and profiler_thread_wake + // are paired correctly. + static bool awake_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_LIBEVENT_H_ diff --git a/ipc/chromium/src/base/message_pump_mac.h b/ipc/chromium/src/base/message_pump_mac.h new file mode 100644 index 0000000000..13902bbf3e --- /dev/null +++ b/ipc/chromium/src/base/message_pump_mac.h @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The basis for all native run loops on the Mac is the CFRunLoop. It can be +// used directly, it can be used as the driving force behind the similar +// Foundation NSRunLoop, and it can be used to implement higher-level event +// loops such as the NSApplication event loop. +// +// This file introduces a basic CFRunLoop-based implementation of the +// MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all +// of the machinery necessary to dispatch events to a delegate, but does not +// implement the specific run loop. Concrete subclasses must provide their +// own DoRun and Quit implementations. +// +// A concrete subclass that just runs a CFRunLoop loop is provided in +// MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop +// is provided. +// +// For the application's event loop, an implementation based on AppKit's +// NSApplication event system is provided in MessagePumpNSApplication. +// +// Typically, MessagePumpNSApplication only makes sense on a Cocoa +// application's main thread. If a CFRunLoop-based message pump is needed on +// any other thread, one of the other concrete subclasses is preferrable. +// MessagePumpMac::Create is defined, which returns a new NSApplication-based +// or NSRunLoop-based MessagePump subclass depending on which thread it is +// called on. + +#ifndef BASE_MESSAGE_PUMP_MAC_H_ +#define BASE_MESSAGE_PUMP_MAC_H_ + +#include "base/message_pump.h" + +#include "base/basictypes.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <IOKit/IOKitLib.h> + +#if defined(__OBJC__) +@class NSAutoreleasePool; +#else // defined(__OBJC__) +class NSAutoreleasePool; +#endif // defined(__OBJC__) + +namespace base { + +class TimeTicks; + +class MessagePumpCFRunLoopBase : public MessagePump { + // Needs access to CreateAutoreleasePool. + friend class MessagePumpScopedAutoreleasePool; + + public: + MessagePumpCFRunLoopBase(); + virtual ~MessagePumpCFRunLoopBase(); + + // Subclasses should implement the work they need to do in MessagePump::Run + // in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly. + // This arrangement is used because MessagePumpCFRunLoopBase needs to set + // up and tear down things before and after the "meat" of DoRun. + virtual void Run(Delegate* delegate); + virtual void DoRun(Delegate* delegate) = 0; + + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + protected: + // Accessors for private data members to be used by subclasses. + CFRunLoopRef run_loop() const { return run_loop_; } + int nesting_level() const { return nesting_level_; } + int run_nesting_level() const { return run_nesting_level_; } + + // Return an autorelease pool to wrap around any work being performed. + // In some cases, CreateAutoreleasePool may return nil intentionally to + // preventing an autorelease pool from being created, allowing any + // objects autoreleased by work to fall into the current autorelease pool. + virtual NSAutoreleasePool* CreateAutoreleasePool(); + + private: + // Timer callback scheduled by ScheduleDelayedWork. This does not do any + // work, but it signals delayed_work_source_ so that delayed work can be + // performed within the appropriate priority constraints. + static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info); + + // Perform highest-priority work. This is associated with work_source_ + // signalled by ScheduleWork. The static method calls the instance method; + // the instance method returns true if work was done. + static void RunWorkSource(void* info); + bool RunWork(); + + // Perform delayed-priority work. This is associated with + // delayed_work_source_ signalled by RunDelayedWorkTimer, and is responsible + // for calling ScheduleDelayedWork again if appropriate. The static method + // calls the instance method; the instance method returns true if more + // delayed work is available. + static void RunDelayedWorkSource(void* info); + bool RunDelayedWork(); + + // Perform idle-priority work. This is normally called by PreWaitObserver, + // but is also associated with idle_work_source_. When this function + // actually does perform idle work, it will resignal that source. The + // static method calls the instance method; the instance method returns + // true if idle work was done. + static void RunIdleWorkSource(void* info); + bool RunIdleWork(); + + // Perform work that may have been deferred because it was not runnable + // within a nested run loop. This is associated with + // nesting_deferred_work_source_ and is signalled by + // MaybeScheduleNestingDeferredWork when returning from a nested loop, + // so that an outer loop will be able to perform the necessary tasks if it + // permits nestable tasks. + static void RunNestingDeferredWorkSource(void* info); + bool RunNestingDeferredWork(); + + // Schedules possible nesting-deferred work to be processed before the run + // loop goes to sleep, exits, or begins processing sources at the top of its + // loop. If this function detects that a nested loop had run since the + // previous attempt to schedule nesting-deferred work, it will schedule a + // call to RunNestingDeferredWorkSource. + void MaybeScheduleNestingDeferredWork(); + + // Observer callback responsible for performing idle-priority work, before + // the run loop goes to sleep. Associated with idle_work_observer_. + static void PreWaitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Observer callback called before the run loop processes any sources. + // Associated with pre_source_observer_. + static void PreSourceObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Observer callback called when the run loop starts and stops, at the + // beginning and end of calls to CFRunLoopRun. This is used to maintain + // nesting_level_. Associated with enter_exit_observer_. + static void EnterExitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Called by EnterExitObserver after performing maintenance on nesting_level_. + // This allows subclasses an opportunity to perform additional processing on + // the basis of run loops starting and stopping. + virtual void EnterExitRunLoop(CFRunLoopActivity activity); + + // IOKit power state change notification callback, called when the system + // enters and leaves the sleep state. + static void PowerStateNotification(void* info, io_service_t service, + uint32_t message_type, + void* message_argument); + + // The thread's run loop. + CFRunLoopRef run_loop_; + + // The timer, sources, and observers are described above alongside their + // callbacks. + CFRunLoopTimerRef delayed_work_timer_; + CFRunLoopSourceRef work_source_; + CFRunLoopSourceRef delayed_work_source_; + CFRunLoopSourceRef idle_work_source_; + CFRunLoopSourceRef nesting_deferred_work_source_; + CFRunLoopObserverRef pre_wait_observer_; + CFRunLoopObserverRef pre_source_observer_; + CFRunLoopObserverRef enter_exit_observer_; + + // Objects used for power state notification. See PowerStateNotification. + io_connect_t root_power_domain_; + IONotificationPortRef power_notification_port_; + io_object_t power_notification_object_; + + // (weak) Delegate passed as an argument to the innermost Run call. + Delegate* delegate_; + + // The time that delayed_work_timer_ is scheduled to fire. This is tracked + // independently of CFRunLoopTimerGetNextFireDate(delayed_work_timer_) + // to be able to reset the timer properly after waking from system sleep. + // See PowerStateNotification. + CFAbsoluteTime delayed_work_fire_time_; + + // The recursion depth of the currently-executing CFRunLoopRun loop on the + // run loop's thread. 0 if no run loops are running inside of whatever scope + // the object was created in. + int nesting_level_; + + // The recursion depth (calculated in the same way as nesting_level_) of the + // innermost executing CFRunLoopRun loop started by a call to Run. + int run_nesting_level_; + + // The deepest (numerically highest) recursion depth encountered since the + // most recent attempt to run nesting-deferred work. + int deepest_nesting_level_; + + // "Delegateless" work flags are set when work is ready to be performed but + // must wait until a delegate is available to process it. This can happen + // when a MessagePumpCFRunLoopBase is instantiated and work arrives without + // any call to Run on the stack. The Run method will check for delegateless + // work on entry and redispatch it as needed once a delegate is available. + bool delegateless_work_; + bool delegateless_delayed_work_; + bool delegateless_idle_work_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); +}; + +class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { + public: + MessagePumpCFRunLoop(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + private: + virtual void EnterExitRunLoop(CFRunLoopActivity activity); + + // True if Quit is called to stop the innermost MessagePump + // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_) + // is running inside the MessagePump's innermost Run call. + bool quit_pending_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop); +}; + +class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { + public: + MessagePumpNSRunLoop(); + virtual ~MessagePumpNSRunLoop(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + private: + // A source that doesn't do anything but provide something signalable + // attached to the run loop. This source will be signalled when Quit + // is called, to cause the loop to wake up so that it can stop. + CFRunLoopSourceRef quit_source_; + + // False after Quit is called. + bool keep_running_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop); +}; + +class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { + public: + MessagePumpNSApplication(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + protected: + // Returns nil if NSApp is currently in the middle of calling -sendEvent. + virtual NSAutoreleasePool* CreateAutoreleasePool(); + + private: + // False after Quit is called. + bool keep_running_; + + // True if DoRun is managing its own run loop as opposed to letting + // -[NSApplication run] handle it. The outermost run loop in the application + // is managed by -[NSApplication run], inner run loops are handled by a loop + // in DoRun. + bool running_own_loop_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication); +}; + +class MessagePumpMac { + public: + // Returns a new instance of MessagePumpNSApplication if called on the main + // thread. Otherwise, returns a new instance of MessagePumpNSRunLoop. + static MessagePump* Create(); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_MAC_H_ diff --git a/ipc/chromium/src/base/message_pump_mac.mm b/ipc/chromium/src/base/message_pump_mac.mm new file mode 100644 index 0000000000..1382e2a0dc --- /dev/null +++ b/ipc/chromium/src/base/message_pump_mac.mm @@ -0,0 +1,735 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_mac.h" + +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> +#include <IOKit/IOMessage.h> +#include <IOKit/pwr_mgt/IOPMLib.h> + +#include <limits> + +#import "base/chrome_application_mac.h" +#include "base/logging.h" +#include "base/time.h" + +namespace { + +void NoOp(void* info) {} + +const CFTimeInterval kCFTimeIntervalMax = + std::numeric_limits<CFTimeInterval>::max(); + +} // namespace + +namespace base { + +// A scoper for autorelease pools created from message pump run loops. +// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare +// case where an autorelease pool needs to be passed in. +class MessagePumpScopedAutoreleasePool { + public: + explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) + : pool_(pump->CreateAutoreleasePool()) {} + ~MessagePumpScopedAutoreleasePool() { [pool_ drain]; } + + private: + NSAutoreleasePool* pool_; + DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool); +}; + +// Must be called on the run loop thread. +MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() + : delegate_(NULL), + delayed_work_fire_time_(kCFTimeIntervalMax), + nesting_level_(0), + run_nesting_level_(0), + deepest_nesting_level_(0), + delegateless_work_(false), + delegateless_delayed_work_(false), + delegateless_idle_work_(false) { + run_loop_ = CFRunLoopGetCurrent(); + CFRetain(run_loop_); + + // Set a repeating timer with a preposterous firing time and interval. The + // timer will effectively never fire as-is. The firing time will be adjusted + // as needed when ScheduleDelayedWork is called. + CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); + timer_context.info = this; + delayed_work_timer_ = + CFRunLoopTimerCreate(NULL, // allocator + kCFTimeIntervalMax, // fire time + kCFTimeIntervalMax, // interval + 0, // flags + 0, // priority + RunDelayedWorkTimer, &timer_context); + CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); + source_context.info = this; + source_context.perform = RunWorkSource; + work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 1, // priority + &source_context); + CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunDelayedWorkSource; + delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 2, // priority + &source_context); + CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunIdleWorkSource; + idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 3, // priority + &source_context); + CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunNestingDeferredWorkSource; + nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_, + kCFRunLoopCommonModes); + + CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); + observer_context.info = this; + pre_wait_observer_ = + CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopBeforeWaiting, + true, // repeat + 0, // priority + PreWaitObserver, &observer_context); + CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); + + pre_source_observer_ = + CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopBeforeSources, + true, // repeat + 0, // priority + PreSourceObserver, &observer_context); + CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes); + + enter_exit_observer_ = + CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopEntry | kCFRunLoopExit, + true, // repeat + 0, // priority + EnterExitObserver, &observer_context); + CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); + + root_power_domain_ = IORegisterForSystemPower(this, &power_notification_port_, + PowerStateNotification, + &power_notification_object_); + if (root_power_domain_ != MACH_PORT_NULL) { + CFRunLoopAddSource( + run_loop_, IONotificationPortGetRunLoopSource(power_notification_port_), + kCFRunLoopCommonModes); + } +} + +// Ideally called on the run loop thread. If other run loops were running +// lower on the run loop thread's stack when this object was created, the +// same number of run loops must be running when this object is destroyed. +MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { + if (root_power_domain_ != MACH_PORT_NULL) { + CFRunLoopRemoveSource( + run_loop_, IONotificationPortGetRunLoopSource(power_notification_port_), + kCFRunLoopCommonModes); + IODeregisterForSystemPower(&power_notification_object_); + IOServiceClose(root_power_domain_); + IONotificationPortDestroy(power_notification_port_); + } + + CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, + kCFRunLoopCommonModes); + CFRelease(enter_exit_observer_); + + CFRunLoopRemoveObserver(run_loop_, pre_source_observer_, + kCFRunLoopCommonModes); + CFRelease(pre_source_observer_); + + CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes); + CFRelease(pre_wait_observer_); + + CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_, + kCFRunLoopCommonModes); + CFRelease(nesting_deferred_work_source_); + + CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes); + CFRelease(idle_work_source_); + + CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); + CFRelease(delayed_work_source_); + + CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); + CFRelease(work_source_); + + CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + CFRelease(delayed_work_timer_); + + CFRelease(run_loop_); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { + // nesting_level_ will be incremented in EnterExitRunLoop, so set + // run_nesting_level_ accordingly. + int last_run_nesting_level = run_nesting_level_; + run_nesting_level_ = nesting_level_ + 1; + + Delegate* last_delegate = delegate_; + delegate_ = delegate; + + if (delegate) { + // If any work showed up but could not be dispatched for want of a + // delegate, set it up for dispatch again now that a delegate is + // available. + if (delegateless_work_) { + CFRunLoopSourceSignal(work_source_); + delegateless_work_ = false; + } + if (delegateless_delayed_work_) { + CFRunLoopSourceSignal(delayed_work_source_); + delegateless_delayed_work_ = false; + } + if (delegateless_idle_work_) { + CFRunLoopSourceSignal(idle_work_source_); + delegateless_idle_work_ = false; + } + } + + DoRun(delegate); + + // Restore the previous state of the object. + delegate_ = last_delegate; + run_nesting_level_ = last_run_nesting_level; +} + +// May be called on any thread. +void MessagePumpCFRunLoopBase::ScheduleWork() { + CFRunLoopSourceSignal(work_source_); + CFRunLoopWakeUp(run_loop_); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoopBase::ScheduleDelayedWork( + const TimeTicks& delayed_work_time) { + TimeDelta delta = delayed_work_time - TimeTicks::Now(); + delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF(); + CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // The timer won't fire again until it's reset. + self->delayed_work_fire_time_ = kCFTimeIntervalMax; + + // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. + // In order to establish the proper priority where delegate_->DoDelayedWork + // can only be called if delegate_->DoWork returns false, the timer used + // to schedule delayed work must signal a CFRunLoopSource set at a lower + // priority than the one used for delegate_->DoWork. + CFRunLoopSourceSignal(self->delayed_work_source_); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunWorkSource. +bool MessagePumpCFRunLoopBase::RunWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. Arrange to come back + // here when a delegate is available. + delegateless_work_ = true; + return false; + } + + // The NSApplication-based run loop only drains the autorelease pool at each + // UI event (NSEvent). The autorelease pool is not drained for each + // CFRunLoopSource target that's run. Use a local pool for any autoreleased + // objects if the app is not currently handling a UI event to ensure they're + // released promptly even in the absence of UI events. + MessagePumpScopedAutoreleasePool autorelease_pool(this); + + // Call DoWork once, and if something was done, arrange to come back here + // again as long as the loop is still running. + bool did_work = delegate_->DoWork(); + if (did_work) { + CFRunLoopSourceSignal(work_source_); + } + + return did_work; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunDelayedWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource. +bool MessagePumpCFRunLoopBase::RunDelayedWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. Arrange to come back + // here when a delegate is available. + delegateless_delayed_work_ = true; + return false; + } + + // The NSApplication-based run loop only drains the autorelease pool at each + // UI event (NSEvent). The autorelease pool is not drained for each + // CFRunLoopSource target that's run. Use a local pool for any autoreleased + // objects if the app is not currently handling a UI event to ensure they're + // released promptly even in the absence of UI events. + MessagePumpScopedAutoreleasePool autorelease_pool(this); + + TimeTicks next_time; + delegate_->DoDelayedWork(&next_time); + + bool more_work = !next_time.is_null(); + if (more_work) { + TimeDelta delay = next_time - TimeTicks::Now(); + if (delay > TimeDelta()) { + // There's more delayed work to be done in the future. + ScheduleDelayedWork(next_time); + } else { + // There's more delayed work to be done, and its time is in the past. + // Arrange to come back here directly as long as the loop is still + // running. + CFRunLoopSourceSignal(delayed_work_source_); + } + } + + return more_work; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunIdleWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource. +bool MessagePumpCFRunLoopBase::RunIdleWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. Arrange to come back + // here when a delegate is available. + delegateless_idle_work_ = true; + return false; + } + + // The NSApplication-based run loop only drains the autorelease pool at each + // UI event (NSEvent). The autorelease pool is not drained for each + // CFRunLoopSource target that's run. Use a local pool for any autoreleased + // objects if the app is not currently handling a UI event to ensure they're + // released promptly even in the absence of UI events. + MessagePumpScopedAutoreleasePool autorelease_pool(this); + + // Call DoIdleWork once, and if something was done, arrange to come back here + // again as long as the loop is still running. + bool did_work = delegate_->DoIdleWork(); + if (did_work) { + CFRunLoopSourceSignal(idle_work_source_); + } + + return did_work; +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + self->RunNestingDeferredWork(); +} + +// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource. +bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() { + if (!delegate_) { + // This point can be reached with a NULL delegate_ if Run is not on the + // stack but foreign code is spinning the CFRunLoop. There's no sense in + // attempting to do any work or signalling the work sources because + // without a delegate, work is not possible. + return false; + } + + // Immediately try work in priority order. + if (!RunWork()) { + if (!RunDelayedWork()) { + if (!RunIdleWork()) { + return false; + } + } else { + // There was no work, and delayed work was done. Arrange for the loop + // to try non-nestable idle work on a subsequent pass. + CFRunLoopSourceSignal(idle_work_source_); + } + } else { + // Work was done. Arrange for the loop to try non-nestable delayed and + // idle work on a subsequent pass. + CFRunLoopSourceSignal(delayed_work_source_); + CFRunLoopSourceSignal(idle_work_source_); + } + + return true; +} + +// Called before the run loop goes to sleep or exits, or processes sources. +void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() { + // deepest_nesting_level_ is set as run loops are entered. If the deepest + // level encountered is deeper than the current level, a nested loop + // (relative to the current level) ran since the last time nesting-deferred + // work was scheduled. When that situation is encountered, schedule + // nesting-deferred work in case any work was deferred because nested work + // was disallowed. + if (deepest_nesting_level_ > nesting_level_) { + deepest_nesting_level_ = nesting_level_; + CFRunLoopSourceSignal(nesting_deferred_work_source_); + } +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // Attempt to do some idle work before going to sleep. + self->RunIdleWork(); + + // The run loop is about to go to sleep. If any of the work done since it + // started or woke up resulted in a nested run loop running, + // nesting-deferred work may have accumulated. Schedule it for processing + // if appropriate. + self->MaybeScheduleNestingDeferredWork(); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + // The run loop has reached the top of the loop and is about to begin + // processing sources. If the last iteration of the loop at this nesting + // level did not sleep or exit, nesting-deferred work may have accumulated + // if a nested loop ran. Schedule nesting-deferred work for processing if + // appropriate. + self->MaybeScheduleNestingDeferredWork(); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + switch (activity) { + case kCFRunLoopEntry: + ++self->nesting_level_; + if (self->nesting_level_ > self->deepest_nesting_level_) { + self->deepest_nesting_level_ = self->nesting_level_; + } + break; + + case kCFRunLoopExit: + // Not all run loops go to sleep. If a run loop is stopped before it + // goes to sleep due to a CFRunLoopStop call, or if the timeout passed + // to CFRunLoopRunInMode expires, the run loop may proceed directly from + // handling sources to exiting without any sleep. This most commonly + // occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it + // to make a single pass through the loop and exit without sleep. Some + // native loops use CFRunLoop in this way. Because PreWaitObserver will + // not be called in these case, MaybeScheduleNestingDeferredWork needs + // to be called here, as the run loop exits. + // + // MaybeScheduleNestingDeferredWork consults self->nesting_level_ + // to determine whether to schedule nesting-deferred work. It expects + // the nesting level to be set to the depth of the loop that is going + // to sleep or exiting. It must be called before decrementing the + // value so that the value still corresponds to the level of the exiting + // loop. + self->MaybeScheduleNestingDeferredWork(); + --self->nesting_level_; + break; + + default: + break; + } + + self->EnterExitRunLoop(activity); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::PowerStateNotification(void* info, + io_service_t service, + uint32_t message_type, + void* message_argument) { + // CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which + // measures the number of seconds since 2001-01-01 00:00:00.0 Z. It is + // implemented in terms of kernel ticks, as in mach_absolute_time. While an + // offset and scale factor can be applied to convert between the two time + // bases at any time after boot, the kernel clock stops while the system is + // asleep, altering the offset. (The offset will also change when the + // real-time clock is adjusted.) CFRunLoopTimers are not readjusted to take + // this into account when the system wakes up, so any timers that were + // pending while the system was asleep will be delayed by the sleep + // duration. + // + // The MessagePump interface assumes that scheduled delayed work will be + // performed at the time ScheduleDelayedWork was asked to perform it. The + // delay caused by the CFRunLoopTimer not firing at the appropriate time + // results in a stall of queued delayed work when the system wakes up. + // With this limitation, scheduled work would not be performed until + // (system wake time + scheduled work time - system sleep time), while it + // would be expected to be performed at (scheduled work time). + // + // To work around this problem, when the system wakes up from sleep, if a + // delayed work timer is pending, it is rescheduled to fire at the original + // time that it was scheduled to fire. + // + // This mechanism is not resilient if the real-time clock does not maintain + // stable time while the system is sleeping, but it matches the behavior of + // the various other MessagePump implementations, and MessageLoop seems to + // be limited in the same way. + // + // References + // - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com, + // http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html + // - Apple Technical Q&A QA1340, "Registering and unregistering for sleep + // and wake notifications," + // http://developer.apple.com/mac/library/qa/qa2004/qa1340.html + // - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c, + // http://www.opensource.apple.com/ + + MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info); + + switch (message_type) { + case kIOMessageSystemWillPowerOn: + if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) { + CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_, + self->delayed_work_fire_time_); + } + break; + + case kIOMessageSystemWillSleep: + case kIOMessageCanSystemSleep: + // The system will wait for 30 seconds before entering sleep if neither + // IOAllowPowerChange nor IOCancelPowerChange are called. That would be + // pretty antisocial. + IOAllowPowerChange(self->root_power_domain_, + reinterpret_cast<long>(message_argument)); + break; + + default: + break; + } +} + +// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default +// implementation is a no-op. +void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {} + +// Base version returns a standard NSAutoreleasePool. +NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() { + return [[NSAutoreleasePool alloc] init]; +} + +MessagePumpCFRunLoop::MessagePumpCFRunLoop() : quit_pending_(false) {} + +// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were +// running lower on the run loop thread's stack when this object was created, +// the same number of CFRunLoopRun loops must be running for the outermost call +// to Run. Run/DoRun are reentrant after that point. +void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { + // This is completely identical to calling CFRunLoopRun(), except autorelease + // pool management is introduced. + int result; + do { + MessagePumpScopedAutoreleasePool autorelease_pool(this); + result = + CFRunLoopRunInMode(kCFRunLoopDefaultMode, kCFTimeIntervalMax, false); + } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoop::Quit() { + // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. + if (nesting_level() == run_nesting_level()) { + // This object is running the innermost loop, just stop it. + CFRunLoopStop(run_loop()); + } else { + // There's another loop running inside the loop managed by this object. + // In other words, someone else called CFRunLoopRunInMode on the same + // thread, deeper on the stack than the deepest Run call. Don't preempt + // other run loops, just mark this object to quit the innermost Run as + // soon as the other inner loops not managed by Run are done. + quit_pending_ = true; + } +} + +// Called by MessagePumpCFRunLoopBase::EnterExitObserver. +void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) { + if (activity == kCFRunLoopExit && nesting_level() == run_nesting_level() && + quit_pending_) { + // Quit was called while loops other than those managed by this object + // were running further inside a run loop managed by this object. Now + // that all unmanaged inner run loops are gone, stop the loop running + // just inside Run. + CFRunLoopStop(run_loop()); + quit_pending_ = false; + } +} + +MessagePumpNSRunLoop::MessagePumpNSRunLoop() : keep_running_(true) { + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); + source_context.perform = NoOp; + quit_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes); +} + +MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { + CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes); + CFRelease(quit_source_); +} + +void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { + while (keep_running_) { + // NSRunLoop manages autorelease pools itself. + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]; + } + + keep_running_ = true; +} + +void MessagePumpNSRunLoop::Quit() { + keep_running_ = false; + CFRunLoopSourceSignal(quit_source_); + CFRunLoopWakeUp(run_loop()); +} + +MessagePumpNSApplication::MessagePumpNSApplication() + : keep_running_(true), running_own_loop_(false) {} + +void MessagePumpNSApplication::DoRun(Delegate* delegate) { + bool last_running_own_loop_ = running_own_loop_; + + // TODO(dmaclach): Get rid of this gratuitous sharedApplication. + // Tests should be setting up their applications on their own. + [CrApplication sharedApplication]; + + if (![NSApp isRunning]) { + running_own_loop_ = false; + // NSApplication manages autorelease pools itself when run this way. + [NSApp run]; + } else { + running_own_loop_ = true; + NSDate* distant_future = [NSDate distantFuture]; + while (keep_running_) { + MessagePumpScopedAutoreleasePool autorelease_pool(this); + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:distant_future + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) { + [NSApp sendEvent:event]; + } + } + keep_running_ = true; + } + + running_own_loop_ = last_running_own_loop_; +} + +void MessagePumpNSApplication::Quit() { + if (!running_own_loop_) { + [NSApp stop:nil]; + } else { + keep_running_ = false; + } + + // Send a fake event to wake the loop up. + [NSApp postEvent:[NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:NULL + subtype:0 + data1:0 + data2:0] + atStart:NO]; +} + +// Prevents an autorelease pool from being created if the app is in the midst of +// handling a UI event because various parts of AppKit depend on objects that +// are created while handling a UI event to be autoreleased in the event loop. +// An example of this is NSWindowController. When a window with a window +// controller is closed it goes through a stack like this: +// (Several stack frames elided for clarity) +// +// #0 [NSWindowController autorelease] +// #1 DoAClose +// #2 MessagePumpCFRunLoopBase::DoWork() +// #3 [NSRunLoop run] +// #4 [NSButton performClick:] +// #5 [NSWindow sendEvent:] +// #6 [NSApp sendEvent:] +// #7 [NSApp run] +// +// -performClick: spins a nested run loop. If the pool created in DoWork was a +// standard NSAutoreleasePool, it would release the objects that were +// autoreleased into it once DoWork released it. This would cause the window +// controller, which autoreleased itself in frame #0, to release itself, and +// possibly free itself. Unfortunately this window controller controls the +// window in frame #5. When the stack is unwound to frame #5, the window would +// no longer exists and crashes may occur. Apple gets around this by never +// releasing the pool it creates in frame #4, and letting frame #7 clean it up +// when it cleans up the pool that wraps frame #7. When an autorelease pool is +// released it releases all other pools that were created after it on the +// autorelease pool stack. +// +// CrApplication is responsible for setting handlingSendEvent to true just +// before it sends the event throught the event handling mechanism, and +// returning it to its previous value once the event has been sent. +NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() { + NSAutoreleasePool* pool = nil; + DCHECK([NSApp isKindOfClass:[CrApplication class]]); + if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) { + pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool(); + } + return pool; +} + +// static +MessagePump* MessagePumpMac::Create() { + if ([NSThread isMainThread]) { + return new MessagePumpNSApplication; + } + + return new MessagePumpNSRunLoop; +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_qt.cc b/ipc/chromium/src/base/message_pump_qt.cc new file mode 100644 index 0000000000..d96e2c5e62 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_qt.cc @@ -0,0 +1,177 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <qabstracteventdispatcher.h> +#include <qevent.h> +#include <QCoreApplication> +#include <QThread> +#include <qtimer.h> + +#include "base/message_pump_qt.h" + +#include <fcntl.h> +#include <limits> +#include <math.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace { +// Cached QEvent user type, registered for our event system +static int sPokeEvent; +} // namespace + +namespace base { + +MessagePumpForUI::MessagePumpForUI() : state_(NULL), qt_pump(*this) {} + +MessagePumpForUI::~MessagePumpForUI() {} + +MessagePumpQt::MessagePumpQt(MessagePumpForUI& aPump) + : pump(aPump), mTimer(new QTimer(this)) { + // Register our custom event type, to use in qApp event loop + sPokeEvent = QEvent::registerEventType(); + connect(mTimer, SIGNAL(timeout()), this, SLOT(dispatchDelayed())); + mTimer->setSingleShot(true); +} + +MessagePumpQt::~MessagePumpQt() { + mTimer->stop(); + delete mTimer; +} + +bool MessagePumpQt::event(QEvent* e) { + if (e->type() == sPokeEvent) { + pump.HandleDispatch(); + return true; + } + return false; +} + +void MessagePumpQt::scheduleDelayedIfNeeded( + const TimeTicks& delayed_work_time) { + if (delayed_work_time.is_null()) { + return; + } + + if (mTimer->isActive()) { + mTimer->stop(); + } + + TimeDelta later = delayed_work_time - TimeTicks::Now(); + // later.InMilliseconds() returns an int64_t, QTimer only accepts int's for + // start(), std::min only works on exact same types. + int laterMsecs = later.InMilliseconds() > std::numeric_limits<int>::max() + ? std::numeric_limits<int>::max() + : later.InMilliseconds(); + mTimer->start(laterMsecs > 0 ? laterMsecs : 0); +} + +void MessagePumpQt::dispatchDelayed() { pump.HandleDispatch(); } + +void MessagePumpForUI::Run(Delegate* delegate) { + RunState state; + state.delegate = delegate; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunAllPending() works correctly. + bool more_work_is_plausible = true; + + RunState* previous_state = state_; + state_ = &state; + + for (;;) { + QEventLoop::ProcessEventsFlags block = QEventLoop::AllEvents; + if (!more_work_is_plausible) { + block |= QEventLoop::WaitForMoreEvents; + } + + QAbstractEventDispatcher* dispatcher = + QAbstractEventDispatcher::instance(QThread::currentThread()); + // An assertion seems too much here, as during startup, + // the dispatcher might not be ready yet. + if (!dispatcher) { + return; + } + + // processEvent's returns true if an event has been processed. + more_work_is_plausible = dispatcher->processEvents(block); + + if (state_->should_quit) { + break; + } + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) { + break; + } + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) { + break; + } + + qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); + + if (more_work_is_plausible) { + continue; + } + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) { + break; + } + } + + state_ = previous_state; +} + +void MessagePumpForUI::HandleDispatch() { + if (state_->should_quit) { + return; + } + + if (state_->delegate->DoWork()) { + // there might be more, see more_work_is_plausible + // variable above, that's why we ScheduleWork() to keep going. + ScheduleWork(); + } + + if (state_->should_quit) { + return; + } + + state_->delegate->DoDelayedWork(&delayed_work_time_); + qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); +} + +void MessagePumpForUI::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpForUI::ScheduleWork() { + QCoreApplication::postEvent(&qt_pump, new QEvent((QEvent::Type)sPokeEvent)); +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // On GLib implementation, a work source is defined which explicitly checks + // the time that has passed. Here, on Qt we can use a QTimer that enqueues our + // event signal in an event queue. + delayed_work_time_ = delayed_work_time; + qt_pump.scheduleDelayedIfNeeded(delayed_work_time_); +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_qt.h b/ipc/chromium/src/base/message_pump_qt.h new file mode 100644 index 0000000000..476c9757ac --- /dev/null +++ b/ipc/chromium/src/base/message_pump_qt.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_QT_H_ +#define BASE_MESSAGE_PUMP_QT_H_ + +#include <qobject.h> + +#include "base/message_pump.h" +#include "base/time.h" + +class QTimer; + +namespace base { + +class MessagePumpForUI; + +class MessagePumpQt : public QObject { + Q_OBJECT + + public: + MessagePumpQt(MessagePumpForUI& pump); + ~MessagePumpQt(); + + virtual bool event(QEvent* e); + void scheduleDelayedIfNeeded(const TimeTicks& delayed_work_time); + + public Q_SLOTS: + void dispatchDelayed(); + + private: + base::MessagePumpForUI& pump; + QTimer* mTimer; +}; + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// XP_LINUX platforms using QApplication event loop +class MessagePumpForUI : public MessagePump { + public: + MessagePumpForUI(); + ~MessagePumpForUI(); + + virtual void Run(Delegate* delegate); + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. + // HandleDispatch is called after the poll has completed. + void HandleDispatch(); + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState { + Delegate* delegate; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + }; + + RunState* state_; + + // This is the time when we need to do delayed work. + TimeTicks delayed_work_time_; + + // MessagePump implementation for Qt based on the GLib implement. + // On Qt we use a QObject base class and the + // default qApp in order to process events through QEventLoop. + MessagePumpQt qt_pump; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_QT_H_ diff --git a/ipc/chromium/src/base/message_pump_win.cc b/ipc/chromium/src/base/message_pump_win.cc new file mode 100644 index 0000000000..f0caa1db6c --- /dev/null +++ b/ipc/chromium/src/base/message_pump_win.cc @@ -0,0 +1,553 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_win.h" + +#include <math.h> + +#include "base/message_loop.h" +#include "base/histogram.h" +#include "base/win_util.h" +#include "mozilla/Maybe.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/ProfilerThreadSleep.h" +#include "WinUtils.h" + +using base::Time; + +namespace base { + +static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow"; + +// Message sent to get an additional time slice for pumping (processing) another +// task (a series of such messages creates a continuous task pump). +static const int kMsgHaveWork = WM_USER + 1; + +//----------------------------------------------------------------------------- +// MessagePumpWin public: + +void MessagePumpWin::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void MessagePumpWin::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void MessagePumpWin::WillProcessMessage(const MSG& msg) { + FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg)); +} + +void MessagePumpWin::DidProcessMessage(const MSG& msg) { + FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg)); +} + +void MessagePumpWin::RunWithDispatcher(Delegate* delegate, + Dispatcher* dispatcher) { + RunState s; + s.delegate = delegate; + s.dispatcher = dispatcher; + s.should_quit = false; + s.run_depth = state_ ? state_->run_depth + 1 : 1; + + RunState* previous_state = state_; + state_ = &s; + + DoRunLoop(); + + state_ = previous_state; +} + +void MessagePumpWin::Quit() { + DCHECK(state_); + state_->should_quit = true; +} + +//----------------------------------------------------------------------------- +// MessagePumpWin protected: + +int MessagePumpWin::GetCurrentDelay() const { + if (delayed_work_time_.is_null()) return -1; + + // Be careful here. TimeDelta has a precision of microseconds, but we want a + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or + // 6? It should be 6 to avoid executing delayed work too early. + double timeout = + ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF()); + + // If this value is negative, then we need to run delayed work soon. + int delay = static_cast<int>(timeout); + if (delay < 0) delay = 0; + + return delay; +} + +//----------------------------------------------------------------------------- +// MessagePumpForUI public: + +MessagePumpForUI::MessagePumpForUI() { InitMessageWnd(); } + +MessagePumpForUI::~MessagePumpForUI() { + DestroyWindow(message_hwnd_); + UnregisterClass(kWndClass, GetModuleHandle(NULL)); +} + +void MessagePumpForUI::ScheduleWork() { + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0); + + // In order to wake up any cross-process COM calls which may currently be + // pending on the main thread, we also have to post a UI message. + PostMessage(message_hwnd_, WM_NULL, 0, 0); +} + +void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // + // We would *like* to provide high resolution timers. Windows timers using + // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup + // mechanism because the application can enter modal windows loops where it + // is not running our MessageLoop; the only way to have our timers fire in + // these cases is to post messages there. + // + // To provide sub-10ms timers, we process timers directly from our run loop. + // For the common case, timers will be processed there as the run loop does + // its normal work. However, we *also* set the system timer so that WM_TIMER + // events fire. This mops up the case of timers not being able to work in + // modal message loops. It is possible for the SetTimer to pop and have no + // pending timers, because they could have already been processed by the + // run loop itself. + // + // We use a single SetTimer corresponding to the timer that will expire + // soonest. As new timers are created and destroyed, we update SetTimer. + // Getting a spurrious SetTimer event firing is benign, as we'll just be + // processing an empty timer queue. + // + delayed_work_time_ = delayed_work_time; + + int delay_msec = GetCurrentDelay(); + DCHECK(delay_msec >= 0); + if (delay_msec < USER_TIMER_MINIMUM) delay_msec = USER_TIMER_MINIMUM; + + // Create a WM_TIMER event that will wake us up to check for any pending + // timers (in case we are running within a nested, external sub-pump). + SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL); +} + +void MessagePumpForUI::PumpOutPendingPaintMessages() { + // If we are being called outside of the context of Run, then don't try to do + // any work. + if (!state_) return; + + // Create a mini-message-pump to force immediate processing of only Windows + // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking + // to get the job done. Actual common max is 4 peeks, but we'll be a little + // safe here. + const int kMaxPeekCount = 20; + int peek_count; + for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { + MSG msg; + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) break; + ProcessMessageHelper(msg); + if (state_->should_quit) break; + } +} + +//----------------------------------------------------------------------------- +// MessagePumpForUI private: + +// static +LRESULT CALLBACK MessagePumpForUI::WndProcThunk(HWND hwnd, UINT message, + WPARAM wparam, LPARAM lparam) { + switch (message) { + case kMsgHaveWork: + reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage(); + break; + case WM_TIMER: + reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage(); + break; + } + return DefWindowProc(hwnd, message, wparam, lparam); +} + +void MessagePumpForUI::DoRunLoop() { + // IF this was just a simple PeekMessage() loop (servicing all possible work + // queues), then Windows would try to achieve the following order according + // to MSDN documentation about PeekMessage with no filter): + // * Sent messages + // * Posted messages + // * Sent messages (again) + // * WM_PAINT messages + // * WM_TIMER messages + // + // Summary: none of the above classes is starved, and sent messages has twice + // the chance of being processed (i.e., reduced service time). + + for (;;) { + // If we do any work, we may create more messages etc., and more work may + // possibly be waiting in another task group. When we (for example) + // ProcessNextWindowsMessage(), there is a good chance there are still more + // messages waiting. On the other hand, when any of these methods return + // having done no work, then it is pretty unlikely that calling them again + // quickly will find any work to do. Finally, if they all say they had no + // work, then it is a good time to consider sleeping (waiting) for more + // work. + + bool more_work_is_plausible = ProcessNextWindowsMessage(); + if (state_->should_quit) break; + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + // If we did not process any delayed work, then we can assume that our + // existing WM_TIMER if any will fire when delayed work should run. We + // don't want to disturb that timer if it is already in flight. However, + // if we did do all remaining delayed work, then lets kill the WM_TIMER. + if (more_work_is_plausible && delayed_work_time_.is_null()) + KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); + if (state_->should_quit) break; + + if (more_work_is_plausible) continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) break; + + if (more_work_is_plausible) continue; + + WaitForWork(); // Wait (sleep) until we have work to do again. + } +} + +void MessagePumpForUI::InitMessageWnd() { + HINSTANCE hinst = GetModuleHandle(NULL); + + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = WndProcThunk; + wc.hInstance = hinst; + wc.lpszClassName = kWndClass; + RegisterClassEx(&wc); + + message_hwnd_ = + CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0); + DCHECK(message_hwnd_); +} + +void MessagePumpForUI::WaitForWork() { + AUTO_PROFILER_LABEL("MessagePumpForUI::WaitForWork", IDLE); + + // Wait until a message is available, up to the time needed by the timer + // manager to fire the next set of timers. + int delay = GetCurrentDelay(); + if (delay < 0) // Negative value means no timers waiting. + delay = INFINITE; + + mozilla::widget::WinUtils::WaitForMessage(delay); +} + +void MessagePumpForUI::HandleWorkMessage() { + // If we are being called outside of the context of Run, then don't try to do + // any work. This could correspond to a MessageBox call or something of that + // sort. + if (!state_) { + // Since we handled a kMsgHaveWork message, we must still update this flag. + InterlockedExchange(&have_work_, 0); + return; + } + + // Let whatever would have run had we not been putting messages in the queue + // run now. This is an attempt to make our dummy message not starve other + // messages that may be in the Windows message queue. + ProcessPumpReplacementMessage(); + + // Now give the delegate a chance to do some work. He'll let us know if he + // needs to do more work. + if (state_->delegate->DoWork()) ScheduleWork(); +} + +void MessagePumpForUI::HandleTimerMessage() { + KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this)); + + // If we are being called outside of the context of Run, then don't do + // anything. This could correspond to a MessageBox call or something of + // that sort. + if (!state_) return; + + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (!delayed_work_time_.is_null()) { + // A bit gratuitous to set delayed_work_time_ again, but oh well. + ScheduleDelayedWork(delayed_work_time_); + } +} + +bool MessagePumpForUI::ProcessNextWindowsMessage() { + // If there are sent messages in the queue then PeekMessage internally + // dispatches the message and returns false. We return true in this + // case to ensure that the message loop peeks again instead of calling + // MsgWaitForMultipleObjectsEx again. + bool sent_messages_in_queue = false; + DWORD queue_status = ::GetQueueStatus(QS_SENDMESSAGE); + if (HIWORD(queue_status) & QS_SENDMESSAGE) sent_messages_in_queue = true; + + MSG msg; + if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + return ProcessMessageHelper(msg); + + return sent_messages_in_queue; +} + +bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { + if (WM_QUIT == msg.message) { + // WM_QUIT is the standard way to exit a ::GetMessage() loop. Our + // MessageLoop has its own quit mechanism, so WM_QUIT is unexpected and + // should be ignored. + return true; + } + + // While running our main message pump, we discard kMsgHaveWork messages. + if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) + return ProcessPumpReplacementMessage(); + + WillProcessMessage(msg); + + if (state_->dispatcher) { + if (!state_->dispatcher->Dispatch(msg)) state_->should_quit = true; + } else { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + DidProcessMessage(msg); + return true; +} + +bool MessagePumpForUI::ProcessPumpReplacementMessage() { + // When we encounter a kMsgHaveWork message, this method is called to peek + // and process a replacement message, such as a WM_PAINT or WM_TIMER. The + // goal is to make the kMsgHaveWork as non-intrusive as possible, even though + // a continuous stream of such messages are posted. This method carefully + // peeks a message while there is no chance for a kMsgHaveWork to be pending, + // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to + // possibly be posted), and finally dispatches that peeked replacement. Note + // that the re-post of kMsgHaveWork may be asynchronous to this thread!! + + MSG msg; + bool have_message = false; + if (MessageLoop::current()->os_modal_loop()) { + // We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above. + have_message = ::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) || + ::PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE); + } else { + have_message = (0 != ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); + + if (have_message && msg.message == WM_NULL) + have_message = (0 != ::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); + } + + DCHECK(!have_message || kMsgHaveWork != msg.message || + msg.hwnd != message_hwnd_); + + // Since we discarded a kMsgHaveWork message, we must update the flag. + int old_have_work = InterlockedExchange(&have_work_, 0); + DCHECK(old_have_work); + + // We don't need a special time slice if we didn't have_message to process. + if (!have_message) return false; + + if (WM_QUIT == msg.message) { + // If we're in a nested ::GetMessage() loop then we must let that loop see + // the WM_QUIT in order for it to exit. If we're in DoRunLoop then the re- + // posted WM_QUIT will be either ignored, or handled, by + // ProcessMessageHelper() called directly from ProcessNextWindowsMessage(). + ::PostQuitMessage(static_cast<int>(msg.wParam)); + // Note: we *must not* ScheduleWork() here as WM_QUIT is a low-priority + // message on Windows (it is only returned by ::PeekMessage() when idle) : + // https://blogs.msdn.microsoft.com/oldnewthing/20051104-33/?p=33453. As + // such posting a kMsgHaveWork message via ScheduleWork() would cause an + // infinite loop (kMsgHaveWork message handled first means we end up here + // again and repost WM_QUIT+ScheduleWork() again, etc.). Not leaving a + // kMsgHaveWork message behind however is also problematic as unwinding + // multiple layers of nested ::GetMessage() loops can result in starving + // application tasks. TODO(https://crbug.com/890016) : Fix this. + + // The return value is mostly irrelevant but return true like we would after + // processing a QuitClosure() task. + return true; + } + + // Guarantee we'll get another time slice in the case where we go into native + // windows code. This ScheduleWork() may hurt performance a tiny bit when + // tasks appear very infrequently, but when the event queue is busy, the + // kMsgHaveWork events get (percentage wise) rarer and rarer. + ScheduleWork(); + return ProcessMessageHelper(msg); +} + +//----------------------------------------------------------------------------- +// MessagePumpForIO public: + +MessagePumpForIO::MessagePumpForIO() { + port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1)); + DCHECK(port_.IsValid()); +} + +void MessagePumpForIO::ScheduleWork() { + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + BOOL ret = + PostQueuedCompletionStatus(port_, 0, reinterpret_cast<ULONG_PTR>(this), + reinterpret_cast<OVERLAPPED*>(this)); + DCHECK(ret); +} + +void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) { + // We know that we can't be blocked right now since this method can only be + // called on the same thread as Run, so we only need to update our record of + // how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, + IOHandler* handler) { + ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler); + HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1); + DCHECK(port == port_.Get()); +} + +//----------------------------------------------------------------------------- +// MessagePumpForIO private: + +void MessagePumpForIO::DoRunLoop() { + for (;;) { + // If we do any work, we may create more messages etc., and more work may + // possibly be waiting in another task group. When we (for example) + // WaitForIOCompletion(), there is a good chance there are still more + // messages waiting. On the other hand, when any of these methods return + // having done no work, then it is pretty unlikely that calling them + // again quickly will find any work to do. Finally, if they all say they + // had no work, then it is a good time to consider sleeping (waiting) for + // more work. + + bool more_work_is_plausible = state_->delegate->DoWork(); + if (state_->should_quit) break; + + more_work_is_plausible |= WaitForIOCompletion(0, NULL); + if (state_->should_quit) break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) break; + + if (more_work_is_plausible) continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) break; + + if (more_work_is_plausible) continue; + + WaitForWork(); // Wait (sleep) until we have work to do again. + } +} + +// Wait until IO completes, up to the time needed by the timer manager to fire +// the next set of timers. +void MessagePumpForIO::WaitForWork() { + // We do not support nested IO message loops. This is to avoid messy + // recursion problems. + DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!"; + + int timeout = GetCurrentDelay(); + if (timeout < 0) // Negative value means no timers waiting. + timeout = INFINITE; + + WaitForIOCompletion(timeout, NULL); +} + +bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { + IOItem item; + if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) { + // We have to ask the system for another IO completion. + if (!GetIOItem(timeout, &item)) return false; + + if (ProcessInternalIOItem(item)) return true; + } + + if (item.context->handler) { + if (filter && item.handler != filter) { + // Save this item for later + completed_io_.push_back(item); + } else { + DCHECK(item.context->handler == item.handler); + item.handler->OnIOCompleted(item.context, item.bytes_transfered, + item.error); + } + } else { + // The handler must be gone by now, just cleanup the mess. + delete item.context; + } + return true; +} + +// Asks the OS for another IO completion result. +bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { + memset(item, 0, sizeof(*item)); + ULONG_PTR key = 0; + OVERLAPPED* overlapped = NULL; + BOOL success; + { + AUTO_PROFILER_LABEL("MessagePumpForIO::GetIOItem::Wait", IDLE); +#ifdef MOZ_GECKO_PROFILER + mozilla::Maybe<mozilla::AutoProfilerThreadSleep> profilerThreadSleep; + if (timeout != 0) { + profilerThreadSleep.emplace(); + } +#endif + success = GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, + &key, &overlapped, timeout); + } + if (!success) { + if (!overlapped) return false; // Nothing in the queue. + item->error = GetLastError(); + item->bytes_transfered = 0; + } + + item->handler = reinterpret_cast<IOHandler*>(key); + item->context = reinterpret_cast<IOContext*>(overlapped); + return true; +} + +bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { + if (this == reinterpret_cast<MessagePumpForIO*>(item.context) && + this == reinterpret_cast<MessagePumpForIO*>(item.handler)) { + // This is our internal completion. + DCHECK(!item.bytes_transfered); + InterlockedExchange(&have_work_, 0); + return true; + } + return false; +} + +// Returns a completion item that was previously received. +bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { + DCHECK(!completed_io_.empty()); + for (std::list<IOItem>::iterator it = completed_io_.begin(); + it != completed_io_.end(); ++it) { + if (!filter || it->handler == filter) { + *item = *it; + completed_io_.erase(it); + return true; + } + } + return false; +} + +} // namespace base diff --git a/ipc/chromium/src/base/message_pump_win.h b/ipc/chromium/src/base/message_pump_win.h new file mode 100644 index 0000000000..585a4d3ff5 --- /dev/null +++ b/ipc/chromium/src/base/message_pump_win.h @@ -0,0 +1,347 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_WIN_H_ +#define BASE_MESSAGE_PUMP_WIN_H_ + +#include <windows.h> + +#include <list> + +#include "base/message_pump.h" +#include "base/observer_list.h" +#include "base/scoped_handle.h" +#include "base/time.h" + +namespace base { + +// MessagePumpWin serves as the base for specialized versions of the MessagePump +// for Windows. It provides basic functionality like handling of observers and +// controlling the lifetime of the message pump. +class MessagePumpWin : public MessagePump { + public: + // An Observer is an object that receives global notifications from the + // MessageLoop. + // + // NOTE: An Observer implementation should be extremely fast! + // + class Observer { + public: + virtual ~Observer() {} + + // This method is called before processing a message. + // The message may be undefined in which case msg.message is 0 + virtual void WillProcessMessage(const MSG& msg) = 0; + + // This method is called when control returns from processing a UI message. + // The message may be undefined in which case msg.message is 0 + virtual void DidProcessMessage(const MSG& msg) = 0; + }; + + // Dispatcher is used during a nested invocation of Run to dispatch events. + // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not + // dispatch events (or invoke TranslateMessage), rather every message is + // passed to Dispatcher's Dispatch method for dispatch. It is up to the + // Dispatcher to dispatch, or not, the event. + // + // The nested loop is exited by either posting a quit, or returning false + // from Dispatch. + class Dispatcher { + public: + virtual ~Dispatcher() {} + // Dispatches the event. If true is returned processing continues as + // normal. If false is returned, the nested loop exits immediately. + virtual bool Dispatch(const MSG& msg) = 0; + }; + + MessagePumpWin() : have_work_(0), state_(NULL) {} + virtual ~MessagePumpWin() {} + + // Add an Observer, which will start receiving notifications immediately. + void AddObserver(Observer* observer); + + // Remove an Observer. It is safe to call this method while an Observer is + // receiving a notification callback. + void RemoveObserver(Observer* observer); + + // Give a chance to code processing additional messages to notify the + // message loop observers that another message has been processed. + void WillProcessMessage(const MSG& msg); + void DidProcessMessage(const MSG& msg); + + // Like MessagePump::Run, but MSG objects are routed through dispatcher. + void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher); + + // MessagePump methods: + virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); } + virtual void Quit(); + + protected: + struct RunState { + Delegate* delegate; + Dispatcher* dispatcher; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + }; + + virtual void DoRunLoop() = 0; + int GetCurrentDelay() const; + + ObserverList<Observer> observers_; + + // The time at which delayed work should run. + TimeTicks delayed_work_time_; + + // A boolean value used to indicate if there is a kMsgDoWork message pending + // in the Windows Message queue. There is at most one such message, and it + // can drive execution of tasks when a native message pump is running. + LONG have_work_; + + // State for the current invocation of Run. + RunState* state_; +}; + +//----------------------------------------------------------------------------- +// MessagePumpForUI extends MessagePumpWin with methods that are particular to a +// MessageLoop instantiated with TYPE_UI. +// +// MessagePumpForUI implements a "traditional" Windows message pump. It contains +// a nearly infinite loop that peeks out messages, and then dispatches them. +// Intermixed with those peeks are callouts to DoWork for pending tasks, and +// DoDelayedWork for pending timers. When there are no events to be serviced, +// this pump goes into a wait state. In most cases, this message pump handles +// all processing. +// +// However, when a task, or windows event, invokes on the stack a native dialog +// box or such, that window typically provides a bare bones (native?) message +// pump. That bare-bones message pump generally supports little more than a +// peek of the Windows message queue, followed by a dispatch of the peeked +// message. MessageLoop extends that bare-bones message pump to also service +// Tasks, at the cost of some complexity. +// +// The basic structure of the extension (refered to as a sub-pump) is that a +// special message, kMsgHaveWork, is repeatedly injected into the Windows +// Message queue. Each time the kMsgHaveWork message is peeked, checks are +// made for an extended set of events, including the availability of Tasks to +// run. +// +// After running a task, the special message kMsgHaveWork is again posted to +// the Windows Message queue, ensuring a future time slice for processing a +// future event. To prevent flooding the Windows Message queue, care is taken +// to be sure that at most one kMsgHaveWork message is EVER pending in the +// Window's Message queue. +// +// There are a few additional complexities in this system where, when there are +// no Tasks to run, this otherwise infinite stream of messages which drives the +// sub-pump is halted. The pump is automatically re-started when Tasks are +// queued. +// +// A second complexity is that the presence of this stream of posted tasks may +// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER. +// Such paint and timer events always give priority to a posted message, such as +// kMsgHaveWork messages. As a result, care is taken to do some peeking in +// between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork +// is peeked, and before a replacement kMsgHaveWork is posted). +// +// NOTE: Although it may seem odd that messages are used to start and stop this +// flow (as opposed to signaling objects, etc.), it should be understood that +// the native message pump will *only* respond to messages. As a result, it is +// an excellent choice. It is also helpful that the starter messages that are +// placed in the queue when new task arrive also awakens DoRunLoop. +// +class MessagePumpForUI : public MessagePumpWin { + public: + MessagePumpForUI(); + virtual ~MessagePumpForUI(); + + // MessagePump methods: + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Applications can call this to encourage us to process all pending WM_PAINT + // messages. This method will process all paint messages the Windows Message + // queue can provide, up to some fixed number (to avoid any infinite loops). + void PumpOutPendingPaintMessages(); + + protected: + virtual void DoRunLoop(); + + bool ProcessNextWindowsMessage(); + void InitMessageWnd(); + void WaitForWork(); + void HandleWorkMessage(); + void HandleTimerMessage(); + bool ProcessMessageHelper(const MSG& msg); + bool ProcessPumpReplacementMessage(); + + // A hidden message-only window. + HWND message_hwnd_; + + private: + static LRESULT CALLBACK WndProcThunk(HWND hwnd, UINT message, WPARAM wparam, + LPARAM lparam); +}; + +//----------------------------------------------------------------------------- +// MessagePumpForIO extends MessagePumpWin with methods that are particular to a +// MessageLoop instantiated with TYPE_IO. This version of MessagePump does not +// deal with Windows mesagges, and instead has a Run loop based on Completion +// Ports so it is better suited for IO operations. +// +class MessagePumpForIO : public MessagePumpWin { + public: + struct IOContext; + + // Clients interested in receiving OS notifications when asynchronous IO + // operations complete should implement this interface and register themselves + // with the message pump. + // + // Typical use #1: + // // Use only when there are no user's buffers involved on the actual IO, + // // so that all the cleanup can be done by the message pump. + // class MyFile : public IOHandler { + // MyFile() { + // ... + // context_ = new IOContext; + // context_->handler = this; + // message_pump->RegisterIOHandler(file_, this); + // } + // ~MyFile() { + // if (pending_) { + // // By setting the handler to NULL, we're asking for this context + // // to be deleted when received, without calling back to us. + // context_->handler = NULL; + // } else { + // delete context_; + // } + // } + // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + // DWORD error) { + // pending_ = false; + // } + // void DoSomeIo() { + // ... + // // The only buffer required for this operation is the overlapped + // // structure. + // ConnectNamedPipe(file_, &context_->overlapped); + // pending_ = true; + // } + // bool pending_; + // IOContext* context_; + // HANDLE file_; + // }; + // + // Typical use #2: + // class MyFile : public IOHandler { + // MyFile() { + // ... + // message_pump->RegisterIOHandler(file_, this); + // } + // // Plus some code to make sure that this destructor is not called + // // while there are pending IO operations. + // ~MyFile() { + // } + // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + // DWORD error) { + // ... + // delete context; + // } + // void DoSomeIo() { + // ... + // IOContext* context = new IOContext; + // // This is not used for anything. It just prevents the context from + // // being considered "abandoned". + // context->handler = this; + // ReadFile(file_, buffer, num_bytes, &read, &context->overlapped); + // } + // HANDLE file_; + // }; + // + // Typical use #3: + // Same as the previous example, except that in order to deal with the + // requirement stated for the destructor, the class calls WaitForIOCompletion + // from the destructor to block until all IO finishes. + // ~MyFile() { + // while(pending_) + // message_pump->WaitForIOCompletion(INFINITE, this); + // } + // + class IOHandler { + public: + virtual ~IOHandler() {} + // This will be called once the pending IO operation associated with + // |context| completes. |error| is the Win32 error code of the IO operation + // (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero + // on error. + virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + DWORD error) = 0; + }; + + // The extended context that should be used as the base structure on every + // overlapped IO operation. |handler| must be set to the registered IOHandler + // for the given file when the operation is started, and it can be set to NULL + // before the operation completes to indicate that the handler should not be + // called anymore, and instead, the IOContext should be deleted when the OS + // notifies the completion of this operation. Please remember that any buffers + // involved with an IO operation should be around until the callback is + // received, so this technique can only be used for IO that do not involve + // additional buffers (other than the overlapped structure itself). + struct IOContext { + OVERLAPPED overlapped; + IOHandler* handler; + }; + + MessagePumpForIO(); + virtual ~MessagePumpForIO() {} + + // MessagePump methods: + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time); + + // Register the handler to be used when asynchronous IO for the given file + // completes. The registration persists as long as |file_handle| is valid, so + // |handler| must be valid as long as there is pending IO for the given file. + void RegisterIOHandler(HANDLE file_handle, IOHandler* handler); + + // Waits for the next IO completion that should be processed by |filter|, for + // up to |timeout| milliseconds. Return true if any IO operation completed, + // regardless of the involved handler, and false if the timeout expired. If + // the completion port received any message and the involved IO handler + // matches |filter|, the callback is called before returning from this code; + // if the handler is not the one that we are looking for, the callback will + // be postponed for another time, so reentrancy problems can be avoided. + // External use of this method should be reserved for the rare case when the + // caller is willing to allow pausing regular task dispatching on this thread. + bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); + + private: + struct IOItem { + IOHandler* handler; + IOContext* context; + DWORD bytes_transfered; + DWORD error; + }; + + virtual void DoRunLoop(); + void WaitForWork(); + bool MatchCompletedIOItem(IOHandler* filter, IOItem* item); + bool GetIOItem(DWORD timeout, IOItem* item); + bool ProcessInternalIOItem(const IOItem& item); + + // The completion port associated with this thread. + ScopedHandle port_; + // This list will be empty almost always. It stores IO completions that have + // not been delivered yet because somebody was doing cleanup. + std::list<IOItem> completed_io_; +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_WIN_H_ diff --git a/ipc/chromium/src/base/object_watcher.cc b/ipc/chromium/src/base/object_watcher.cc new file mode 100644 index 0000000000..b749829a0e --- /dev/null +++ b/ipc/chromium/src/base/object_watcher.cc @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/object_watcher.h" + +#include "base/logging.h" + +namespace base { + +//----------------------------------------------------------------------------- + +class ObjectWatcher::Watch : public mozilla::Runnable { + public: + ObjectWatcher* watcher; // The associated ObjectWatcher instance + HANDLE object; // The object being watched + HANDLE wait_object; // Returned by RegisterWaitForSingleObject + MessageLoop* origin_loop; // Used to get back to the origin thread + Delegate* delegate; // Delegate to notify when signaled + bool did_signal; // DoneWaiting was called + + Watch() : mozilla::Runnable("ObjectWatcher::Watch") {} + + NS_IMETHOD Run() override { + // The watcher may have already been torn down, in which case we need to + // just get out of dodge. + if (!watcher) return NS_OK; + + DCHECK(did_signal); + watcher->StopWatching(); + + delegate->OnObjectSignaled(object); + + return NS_OK; + } +}; + +//----------------------------------------------------------------------------- + +ObjectWatcher::ObjectWatcher() : watch_(nullptr) {} + +ObjectWatcher::~ObjectWatcher() { StopWatching(); } + +bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) { + if (watch_) { + NOTREACHED() << "Already watching an object"; + return false; + } + + RefPtr<Watch> watch = new Watch; + watch->watcher = this; + watch->object = object; + watch->origin_loop = MessageLoop::current(); + watch->delegate = delegate; + watch->did_signal = false; + + // Since our job is to just notice when an object is signaled and report the + // result back to this thread, we can just run on a Windows wait thread. + DWORD wait_flags = WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE; + + if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting, + watch.get(), INFINITE, wait_flags)) { + NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError(); + return false; + } + + watch_ = watch.forget(); + + // We need to know if the current message loop is going away so we can + // prevent the wait thread from trying to access a dead message loop. + MessageLoop::current()->AddDestructionObserver(this); + return true; +} + +bool ObjectWatcher::StopWatching() { + if (!watch_) return false; + + // Make sure ObjectWatcher is used in a single-threaded fashion. + DCHECK(watch_->origin_loop == MessageLoop::current()); + + // If DoneWaiting is in progress, we wait for it to finish. We know whether + // DoneWaiting happened or not by inspecting the did_signal flag. + if (!UnregisterWaitEx(watch_->wait_object, INVALID_HANDLE_VALUE)) { + NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError(); + return false; + } + + // Make sure that we see any mutation to did_signal. This should be a no-op + // since we expect that UnregisterWaitEx resulted in a memory barrier, but + // just to be sure, we're going to be explicit. + MemoryBarrier(); + + // If the watch has been posted, then we need to make sure it knows not to do + // anything once it is run. + watch_->watcher = NULL; + + watch_ = nullptr; + + MessageLoop::current()->RemoveDestructionObserver(this); + return true; +} + +HANDLE ObjectWatcher::GetWatchedObject() { + if (!watch_) return NULL; + + return watch_->object; +} + +// static +void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) { + DCHECK(!timed_out); + + Watch* watch = static_cast<Watch*>(param); + RefPtr<Watch> addrefedWatch = watch; + + // Record that we ran this function. + watch->did_signal = true; + + // We rely on the locking in PostTask() to ensure that a memory barrier is + // provided, which in turn ensures our change to did_signal can be observed + // on the target thread. + if (watch->origin_loop->IsAcceptingTasks()) { + watch->origin_loop->PostTask(addrefedWatch.forget()); + } +} + +void ObjectWatcher::WillDestroyCurrentMessageLoop() { + // Need to shutdown the watch so that we don't try to access the MessageLoop + // after this point. + StopWatching(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/object_watcher.h b/ipc/chromium/src/base/object_watcher.h new file mode 100644 index 0000000000..1e1b27aa46 --- /dev/null +++ b/ipc/chromium/src/base/object_watcher.h @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_OBJECT_WATCHER_H_ +#define BASE_OBJECT_WATCHER_H_ + +#include <windows.h> +#ifdef GetClassName +# undef GetClassName +#endif + +#include "base/message_loop.h" + +namespace base { + +// A class that provides a means to asynchronously wait for a Windows object to +// become signaled. It is an abstraction around RegisterWaitForSingleObject +// that provides a notification callback, OnObjectSignaled, that runs back on +// the origin thread (i.e., the thread that called StartWatching). +// +// This class acts like a smart pointer such that when it goes out-of-scope, +// UnregisterWaitEx is automatically called, and any in-flight notification is +// suppressed. +// +// Typical usage: +// +// ``` +// class MyClass : public base::ObjectWatcher::Delegate { +// public: +// void DoStuffWhenSignaled(HANDLE object) { +// watcher_.StartWatching(object, this); +// } +// virtual void OnObjectSignaled(HANDLE object) { +// // OK, time to do stuff! +// } +// private: +// base::ObjectWatcher watcher_; +// }; +// ``` +// +// In the above example, MyClass wants to "do stuff" when object becomes +// signaled. ObjectWatcher makes this task easy. When MyClass goes out of +// scope, the watcher_ will be destroyed, and there is no need to worry about +// OnObjectSignaled being called on a deleted MyClass pointer. Easy! +// +////// +// +// Mozilla/Gecko addendum: +// +// An undocumented (but runtime-asserted) requirement for the above is that +// `MyClass` must be strictly thread-affine. In particular, `StartWatching()` +// and `StopWatching()` -- including the implicit `StopWatching()` call in +// `~ObjectWatcher()` -- must always be called on the same thread and from +// within the same `MessageLoop`. +// +// (If this did not hold, `OnObjectSignaled()` might be called on one thread +// while `MyClass` is in the middle of being destroyed on another.) +// +// This condition cannot be guaranteed for potentially-asynchronously-destroyed +// classes, nor for their owned or shared subobjects, nor for anything which +// might be sent to a non-thread-affine task queue. +// +class ObjectWatcher : public MessageLoop::DestructionObserver { + public: + class Delegate { + public: + virtual ~Delegate() {} + // Called from the MessageLoop when a signaled object is detected. To + // continue watching the object, AddWatch must be called again. + virtual void OnObjectSignaled(HANDLE object) = 0; + }; + + ObjectWatcher(); + ~ObjectWatcher(); + + // When the object is signaled, the given delegate is notified on the thread + // where StartWatching is called. The ObjectWatcher is not responsible for + // deleting the delegate. + // + // Returns true if the watch was started. Otherwise, false is returned. + // + bool StartWatching(HANDLE object, Delegate* delegate); + + // Stops watching. Does nothing if the watch has already completed. If the + // watch is still active, then it is canceled, and the associated delegate is + // not notified. + // + // Returns true if the watch was canceled. Otherwise, false is returned. + // + bool StopWatching(); + + // Returns the handle of the object being watched, or NULL if the object + // watcher is stopped. + HANDLE GetWatchedObject(); + + private: + // Called on a background thread when done waiting. + static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out); + + // MessageLoop::DestructionObserver implementation: + virtual void WillDestroyCurrentMessageLoop(); + + // Internal state. + class Watch; + RefPtr<Watch> watch_; + + DISALLOW_COPY_AND_ASSIGN(ObjectWatcher); +}; + +} // namespace base + +#endif // BASE_OBJECT_WATCHER_H_ diff --git a/ipc/chromium/src/base/observer_list.h b/ipc/chromium/src/base/observer_list.h new file mode 100644 index 0000000000..52b88329eb --- /dev/null +++ b/ipc/chromium/src/base/observer_list.h @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_OBSERVER_LIST_H__ +#define BASE_OBSERVER_LIST_H__ + +#include <algorithm> +#include <limits> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" + +#if defined(ANDROID) && defined(_STLP_STD_NAME) +using _STLP_STD_NAME::find; +#endif + +namespace base { + +/////////////////////////////////////////////////////////////////////////////// +// +// OVERVIEW: +// +// A container for a list of observers. Unlike a normal STL vector or list, +// this container can be modified during iteration without invalidating the +// iterator. So, it safely handles the case of an observer removing itself +// or other observers from the list while observers are being notified. +// +// TYPICAL USAGE: +// +// class MyWidget { +// public: +// ... +// +// class Observer { +// public: +// virtual void OnFoo(MyWidget* w) = 0; +// virtual void OnBar(MyWidget* w, int x, int y) = 0; +// }; +// +// void AddObserver(Observer* obs) { +// observer_list_.AddObserver(obs); +// } +// +// void RemoveObserver(Observer* obs) { +// observer_list_.RemoveObserver(obs); +// } +// +// void NotifyFoo() { +// FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this)); +// } +// +// void NotifyBar(int x, int y) { +// FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y)); +// } +// +// private: +// ObserverList<Observer> observer_list_; +// }; +// +// +/////////////////////////////////////////////////////////////////////////////// + +template <class ObserverType, bool check_empty = false> +class ObserverList { + public: + // Enumeration of which observers are notified. + enum NotificationType { + // Specifies that any observers added during notification are notified. + // This is the default type if non type is provided to the constructor. + NOTIFY_ALL, + + // Specifies that observers added while sending out notification are not + // notified. + NOTIFY_EXISTING_ONLY + }; + + ObserverList() : notify_depth_(0), type_(NOTIFY_ALL) {} + explicit ObserverList(NotificationType type) + : notify_depth_(0), type_(type) {} + ~ObserverList() { + // When check_empty is true, assert that the list is empty on destruction. + if (check_empty) { + Compact(); + DCHECK_EQ(observers_.size(), 0U); + } + } + + // Add an observer to the list. + void AddObserver(ObserverType* obs) { + DCHECK(find(observers_.begin(), observers_.end(), obs) == observers_.end()) + << "Observers can only be added once!"; + observers_.push_back(obs); + } + + // Remove an observer from the list. + void RemoveObserver(ObserverType* obs) { + typename ListType::iterator it = + std::find(observers_.begin(), observers_.end(), obs); + if (it != observers_.end()) { + if (notify_depth_) { + *it = 0; + } else { + observers_.erase(it); + } + } + } + + size_t size() const { return observers_.size(); } + + ObserverType* GetElementAt(int index) const { return observers_[index]; } + + // An iterator class that can be used to access the list of observers. See + // also the FOREACH_OBSERVER macro defined below. + class Iterator { + public: + explicit Iterator(const ObserverList<ObserverType>& list) + : list_(list), + index_(0), + max_index_(list.type_ == NOTIFY_ALL + ? std::numeric_limits<size_t>::max() + : list.observers_.size()) { + ++list_.notify_depth_; + } + + ~Iterator() { + if (--list_.notify_depth_ == 0) list_.Compact(); + } + + ObserverType* GetNext() { + ListType& observers = list_.observers_; + // Advance if the current element is null + size_t max_index = std::min(max_index_, observers.size()); + while (index_ < max_index && !observers[index_]) ++index_; + return index_ < max_index ? observers[index_++] : NULL; + } + + private: + const ObserverList<ObserverType>& list_; + size_t index_; + size_t max_index_; + }; + + private: + typedef std::vector<ObserverType*> ListType; + + void Compact() const { + typename ListType::iterator it = observers_.begin(); + while (it != observers_.end()) { + if (*it) { + ++it; + } else { + it = observers_.erase(it); + } + } + } + + // These are marked mutable to facilitate having NotifyAll be const. + mutable ListType observers_; + mutable int notify_depth_; + NotificationType type_; + + friend class ObserverList::Iterator; + + DISALLOW_EVIL_CONSTRUCTORS(ObserverList); +}; + +} // namespace base + +#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \ + do { \ + base::ObserverList<ObserverType>::Iterator it(observer_list); \ + ObserverType* obs; \ + while ((obs = it.GetNext()) != NULL) obs->func; \ + } while (0) + +#endif // BASE_OBSERVER_LIST_H__ diff --git a/ipc/chromium/src/base/pickle.cc b/ipc/chromium/src/base/pickle.cc new file mode 100644 index 0000000000..fa72edca20 --- /dev/null +++ b/ipc/chromium/src/base/pickle.cc @@ -0,0 +1,525 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/pickle.h" + +#include "mozilla/Alignment.h" +#include "mozilla/CheckedInt.h" +#include "mozilla/EndianUtils.h" +#include "mozilla/Telemetry.h" +#include "mozilla/ipc/ProtocolUtils.h" + +#include <stdlib.h> + +#include <limits> +#include <string> +#include <algorithm> +#include <type_traits> + +#include "nsDebug.h" + +//------------------------------------------------------------------------------ + +static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t), + "Insufficient alignment"); + +static const uint32_t kHeaderSegmentCapacity = 64; + +static const uint32_t kDefaultSegmentCapacity = 4096; + +static const char kBytePaddingMarker = char(0xbf); + +namespace { + +// We want to copy data to our payload as efficiently as possible. +// memcpy fits the bill for copying, but not all compilers or +// architectures support inlining memcpy from void*, which has unknown +// static alignment. However, we know that all the members of our +// payload will be aligned on memberAlignmentType boundaries. We +// therefore use that knowledge to construct a copier that will copy +// efficiently (via standard C++ assignment mechanisms) if the datatype +// needs that alignment or less, and memcpy otherwise. (The compiler +// may still inline memcpy, of course.) + +template <typename T, size_t size, bool hasSufficientAlignment> +struct Copier { + static void Copy(T* dest, const char* iter) { memcpy(dest, iter, sizeof(T)); } +}; + +// Copying 64-bit quantities happens often enough and can easily be made +// worthwhile on 32-bit platforms, so handle it specially. Only do it +// if 64-bit types aren't sufficiently aligned; the alignment +// requirements for them vary between 32-bit platforms. +#ifndef HAVE_64BIT_BUILD +template <typename T> +struct Copier<T, sizeof(uint64_t), false> { + static void Copy(T* dest, const char* iter) { +# if MOZ_LITTLE_ENDIAN + static const int loIndex = 0, hiIndex = 1; +# else + static const int loIndex = 1, hiIndex = 0; +# endif + static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*), + "Pointers have different alignments"); + const uint32_t* src = reinterpret_cast<const uint32_t*>(iter); + uint32_t* uint32dest = reinterpret_cast<uint32_t*>(dest); + uint32dest[loIndex] = src[loIndex]; + uint32dest[hiIndex] = src[hiIndex]; + } +}; +#endif + +template <typename T, size_t size> +struct Copier<T, size, true> { + static void Copy(T* dest, const char* iter) { + // The pointer ought to be properly aligned. + DCHECK_EQ((((uintptr_t)iter) & (MOZ_ALIGNOF(T) - 1)), 0); + *dest = *reinterpret_cast<const T*>(iter); + } +}; + +} // anonymous namespace + +PickleIterator::PickleIterator(const Pickle& pickle) + : iter_(pickle.buffers_.Iter()) { + iter_.Advance(pickle.buffers_, pickle.header_size_); +} + +template <typename T> +void PickleIterator::CopyInto(T* dest) { + static_assert(std::is_trivially_copyable<T>::value, + "Copied type must be a POD type"); + Copier<T, sizeof(T), + (MOZ_ALIGNOF(T) <= + sizeof(Pickle::memberAlignmentType))>::Copy(dest, iter_.Data()); +} + +bool Pickle::IteratorHasRoomFor(const PickleIterator& iter, + uint32_t len) const { + // Make sure we don't get into trouble where AlignInt(len) == 0. + MOZ_RELEASE_ASSERT(len < 64); + + return iter.iter_.HasRoomFor(AlignInt(len)); +} + +bool Pickle::HasBytesAvailable(const PickleIterator* iter, uint32_t len) const { + return iter->iter_.HasBytesAvailable(buffers_, len); +} + +void Pickle::UpdateIter(PickleIterator* iter, uint32_t bytes) const { + // Make sure we don't get into trouble where AlignInt(bytes) == 0. + MOZ_RELEASE_ASSERT(bytes < 64); + + iter->iter_.Advance(buffers_, AlignInt(bytes)); +} + +// Payload is sizeof(Pickle::memberAlignmentType) aligned. + +Pickle::Pickle(uint32_t header_size, size_t segment_capacity) + : buffers_(AlignInt(header_size), + segment_capacity ? segment_capacity : kHeaderSegmentCapacity, + segment_capacity ? segment_capacity : kDefaultSegmentCapacity), + header_(nullptr), + header_size_(AlignInt(header_size)) { + DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header)); + DCHECK(header_size_ <= kHeaderSegmentCapacity); + header_ = reinterpret_cast<Header*>(buffers_.Start()); + header_->payload_size = 0; +} + +Pickle::Pickle(uint32_t header_size, const char* data, uint32_t length) + : buffers_(length, AlignCapacity(length), kDefaultSegmentCapacity), + header_(nullptr), + header_size_(AlignInt(header_size)) { + DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header)); + DCHECK(header_size <= kHeaderSegmentCapacity); + MOZ_RELEASE_ASSERT(header_size <= length); + + header_ = reinterpret_cast<Header*>(buffers_.Start()); + memcpy(header_, data, length); +} + +Pickle::Pickle(Pickle&& other) + : buffers_(std::move(other.buffers_)), + header_(other.header_), + header_size_(other.header_size_) { + other.header_ = nullptr; +} + +Pickle::~Pickle() {} + +Pickle& Pickle::operator=(Pickle&& other) { + BufferList tmp = std::move(other.buffers_); + other.buffers_ = std::move(buffers_); + buffers_ = std::move(tmp); + + // std::swap(buffers_, other.buffers_); + std::swap(header_, other.header_); + std::swap(header_size_, other.header_size_); + return *this; +} + +void Pickle::CopyFrom(const Pickle& other) { + MOZ_ALWAYS_TRUE(buffers_.CopyFrom(other.buffers_)); + MOZ_ASSERT(other.header_ == + reinterpret_cast<const Header*>(other.buffers_.Start())); + + header_ = reinterpret_cast<Header*>(buffers_.Start()); + header_size_ = other.header_size_; +} + +bool Pickle::ReadBool(PickleIterator* iter, bool* result) const { + int tmp; + if (!ReadScalar(iter, &tmp)) return false; + + DCHECK(0 == tmp || 1 == tmp); + *result = tmp ? true : false; + + return true; +} + +bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const { + return ReadScalar(iter, result); +} + +bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const { + return ReadScalar(iter, result); +} + +bool Pickle::ReadInt(PickleIterator* iter, int* result) const { + return ReadScalar(iter, result); +} + +// Always written as a 64-bit value since the size for this type can +// differ between architectures. +bool Pickle::ReadLong(PickleIterator* iter, long* result) const { + int64_t big_result; + if (!ReadScalar(iter, &big_result)) return false; + + DCHECK(big_result <= LONG_MAX && big_result >= LONG_MIN); + *result = static_cast<long>(big_result); + + return true; +} + +// Always written as a 64-bit value since the size for this type can +// differ between architectures. +bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const { + uint64_t big_result; + if (!ReadScalar(iter, &big_result)) return false; + DCHECK(big_result <= ULONG_MAX); + *result = static_cast<unsigned long>(big_result); + + return true; +} + +bool Pickle::ReadLength(PickleIterator* iter, int* result) const { + if (!ReadScalar(iter, result)) return false; + return ((*result) >= 0); +} + +bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const { + return ReadScalar(iter, result); +} + +bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const { + return ReadScalar(iter, result); +} + +bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const { + return ReadScalar(iter, result); +} + +bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const { + return ReadScalar(iter, result); +} + +bool Pickle::ReadDouble(PickleIterator* iter, double* result) const { + return ReadScalar(iter, result); +} + +// Always written as a 64-bit value since the size for this type can +// differ between architectures. +bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const { + DCHECK(iter); + + int64_t big_result; + if (!ReadScalar(iter, &big_result)) return false; + + DCHECK(big_result <= std::numeric_limits<intptr_t>::max() && + big_result >= std::numeric_limits<intptr_t>::min()); + *result = static_cast<intptr_t>(big_result); + + return true; +} + +bool Pickle::ReadUnsignedChar(PickleIterator* iter, + unsigned char* result) const { + return ReadScalar(iter, result); +} + +bool Pickle::ReadString(PickleIterator* iter, std::string* result) const { + DCHECK(iter); + + int len; + if (!ReadLength(iter, &len)) return false; + + auto chars = mozilla::MakeUnique<char[]>(len); + if (!ReadBytesInto(iter, chars.get(), len)) { + return false; + } + result->assign(chars.get(), len); + + return true; +} + +bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const { + DCHECK(iter); + + int len; + if (!ReadLength(iter, &len)) return false; + // Avoid integer multiplication overflow. + if (len > INT_MAX / static_cast<int>(sizeof(wchar_t))) return false; + + auto chars = mozilla::MakeUnique<wchar_t[]>(len); + if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) { + return false; + } + result->assign(chars.get(), len); + + return true; +} + +bool Pickle::ReadBytesInto(PickleIterator* iter, void* data, + uint32_t length) const { + if (AlignInt(length) < length) { + return false; + } + + if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) { + return false; + } + + return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length); +} + +bool Pickle::IgnoreBytes(PickleIterator* iter, uint32_t length) const { + if (AlignInt(length) < length) { + return false; + } + + return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length)); +} + +#ifdef MOZ_PICKLE_SENTINEL_CHECKING +MOZ_NEVER_INLINE +bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const { + uint32_t found; + if (!ReadScalar(iter, &found)) { + return false; + } + return found == sentinel; +} + +bool Pickle::IgnoreSentinel(PickleIterator* iter) const { + uint32_t found; + return ReadUInt32(iter, &found); +} + +bool Pickle::WriteSentinel(uint32_t sentinel) { return WriteUInt32(sentinel); } +#endif + +void Pickle::EndRead(PickleIterator& iter, uint32_t ipcMsgType) const { + // FIXME: Deal with the footer somehow... + // DCHECK(iter.iter_.Done()); +} + +void Pickle::Truncate(PickleIterator* iter) { + size_t dropped = buffers_.Truncate(iter->iter_); + header_->payload_size -= dropped; +} + +static const char kBytePaddingData[4] = { + kBytePaddingMarker, + kBytePaddingMarker, + kBytePaddingMarker, + kBytePaddingMarker, +}; + +static void WritePadding(Pickle::BufferList& buffers, uint32_t padding) { + MOZ_RELEASE_ASSERT(padding <= 4); + if (padding) { + MOZ_ALWAYS_TRUE(buffers.WriteBytes(kBytePaddingData, padding)); + } +} + +void Pickle::BeginWrite(uint32_t length) { + // write at an alignment-aligned offset from the beginning of the header + uint32_t offset = AlignInt(header_->payload_size); + uint32_t padding = (header_size_ + offset) % sizeof(memberAlignmentType); + uint32_t new_size = offset + padding + AlignInt(length); + MOZ_RELEASE_ASSERT(new_size >= header_->payload_size); + + DCHECK(intptr_t(header_) % sizeof(memberAlignmentType) == 0); + +#ifdef HAVE_64BIT_BUILD + DCHECK_LE(length, std::numeric_limits<uint32_t>::max()); +#endif + + WritePadding(buffers_, padding); + + DCHECK((header_size_ + header_->payload_size + padding) % + sizeof(memberAlignmentType) == + 0); + + header_->payload_size = new_size; +} + +void Pickle::EndWrite(uint32_t length) { + uint32_t padding = AlignInt(length) - length; + WritePadding(buffers_, padding); +} + +bool Pickle::WriteBool(bool value) { return WriteInt(value ? 1 : 0); } + +bool Pickle::WriteInt16(int16_t value) { + return WriteBytes(&value, sizeof(value)); +} + +bool Pickle::WriteUInt16(uint16_t value) { + return WriteBytes(&value, sizeof(value)); +} + +bool Pickle::WriteInt(int value) { return WriteBytes(&value, sizeof(value)); } + +bool Pickle::WriteLong(long value) { + // Always written as a 64-bit value since the size for this type can + // differ between architectures. + return WriteInt64(int64_t(value)); +} + +bool Pickle::WriteULong(unsigned long value) { + // Always written as a 64-bit value since the size for this type can + // differ between architectures. + return WriteUInt64(uint64_t(value)); +} + +bool Pickle::WriteInt32(int32_t value) { + return WriteBytes(&value, sizeof(value)); +} + +bool Pickle::WriteUInt32(uint32_t value) { + return WriteBytes(&value, sizeof(value)); +} + +bool Pickle::WriteInt64(int64_t value) { + return WriteBytes(&value, sizeof(value)); +} + +bool Pickle::WriteUInt64(uint64_t value) { + return WriteBytes(&value, sizeof(value)); +} + +bool Pickle::WriteDouble(double value) { + return WriteBytes(&value, sizeof(value)); +} + +bool Pickle::WriteIntPtr(intptr_t value) { + // Always written as a 64-bit value since the size for this type can + // differ between architectures. + return WriteInt64(int64_t(value)); +} + +bool Pickle::WriteUnsignedChar(unsigned char value) { + return WriteBytes(&value, sizeof(value)); +} + +bool Pickle::WriteBytesZeroCopy(void* data, uint32_t data_len, + uint32_t capacity) { + BeginWrite(data_len); + + uint32_t new_capacity = AlignInt(capacity); +#ifndef MOZ_MEMORY + if (new_capacity > capacity) { + // If the buffer we were given is not large enough to contain padding + // after the data, reallocate it to make it so. When using jemalloc, + // we're guaranteed the buffer size is going to be at least 4-bytes + // aligned, so we skip realloc altogether. Even with other allocators, + // the realloc is likely not necessary, but we don't take chances. + // At least with ASan, it does matter to realloc to inform ASan we're + // going to use more data from the buffer (and let it actually realloc + // if it needs to). + data = realloc(data, new_capacity); + } +#endif + + // Shouldn't fail, because we're using InfallibleAllocPolicy. + MOZ_ALWAYS_TRUE(buffers_.WriteBytesZeroCopy(reinterpret_cast<char*>(data), + data_len, new_capacity)); + + EndWrite(data_len); + return true; +} + +bool Pickle::WriteBytes(const void* data, uint32_t data_len) { + BeginWrite(data_len); + + MOZ_ALWAYS_TRUE( + buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len)); + + EndWrite(data_len); + return true; +} + +bool Pickle::WriteString(const std::string& value) { + if (!WriteInt(static_cast<int>(value.size()))) return false; + + return WriteBytes(value.data(), static_cast<int>(value.size())); +} + +bool Pickle::WriteWString(const std::wstring& value) { + if (!WriteInt(static_cast<int>(value.size()))) return false; + + return WriteBytes(value.data(), + static_cast<int>(value.size() * sizeof(wchar_t))); +} + +bool Pickle::WriteData(const char* data, uint32_t length) { + return WriteInt(length) && WriteBytes(data, length); +} + +void Pickle::InputBytes(const char* data, uint32_t length) { + MOZ_ALWAYS_TRUE(buffers_.WriteBytes(data, length)); +} + +int32_t* Pickle::GetInt32PtrForTest(uint32_t offset) { + size_t pos = buffers_.Size() - offset; + BufferList::IterImpl iter(buffers_); + MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(buffers_, pos)); + return reinterpret_cast<int32_t*>(iter.Data()); +} + +// static +uint32_t Pickle::MessageSize(uint32_t header_size, const char* start, + const char* end) { + DCHECK(header_size == AlignInt(header_size)); + DCHECK(header_size <= + static_cast<memberAlignmentType>(kHeaderSegmentCapacity)); + + if (end < start) return 0; + size_t length = static_cast<size_t>(end - start); + if (length < sizeof(Header)) return 0; + + const Header* hdr = reinterpret_cast<const Header*>(start); + if (length < header_size) return 0; + + mozilla::CheckedInt<uint32_t> sum(header_size); + sum += hdr->payload_size; + + if (!sum.isValid()) return 0; + + return sum.value(); +} diff --git a/ipc/chromium/src/base/pickle.h b/ipc/chromium/src/base/pickle.h new file mode 100644 index 0000000000..5f64880848 --- /dev/null +++ b/ipc/chromium/src/base/pickle.h @@ -0,0 +1,284 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PICKLE_H__ +#define BASE_PICKLE_H__ + +#include <string> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/string16.h" + +#include "mozilla/Attributes.h" +#include "mozilla/BufferList.h" +#include "mozilla/mozalloc.h" +#include "mozilla/TimeStamp.h" +#if !defined(FUZZING) && (!defined(RELEASE_OR_BETA) || defined(DEBUG)) +# define MOZ_PICKLE_SENTINEL_CHECKING +#endif +class Pickle; +class PickleIterator { + public: + explicit PickleIterator(const Pickle& pickle); + + private: + friend class Pickle; + + mozilla::BufferList<InfallibleAllocPolicy>::IterImpl iter_; + + template <typename T> + void CopyInto(T* dest); +}; + +// This class provides facilities for basic binary value packing and unpacking. +// +// The Pickle class supports appending primitive values (ints, strings, etc.) +// to a pickle instance. The Pickle instance grows its internal memory buffer +// dynamically to hold the sequence of primitive values. The internal memory +// buffer is exposed as the "data" of the Pickle. This "data" can be passed +// to a Pickle object to initialize it for reading. +// +// When reading from a Pickle object, it is important for the consumer to know +// what value types to read and in what order to read them as the Pickle does +// not keep track of the type of data written to it. +// +// The Pickle's data has a header which contains the size of the Pickle's +// payload. It can optionally support additional space in the header. That +// space is controlled by the header_size parameter passed to the Pickle +// constructor. +// +class Pickle { + public: + ~Pickle(); + + Pickle() = delete; + + // Initialize a Pickle object with the specified header size in bytes, which + // must be greater-than-or-equal-to sizeof(Pickle::Header). The header size + // will be rounded up to ensure that the header size is 32bit-aligned. + explicit Pickle(uint32_t header_size, size_t segment_capacity = 0); + + Pickle(uint32_t header_size, const char* data, uint32_t length); + + Pickle(const Pickle& other) = delete; + + Pickle(Pickle&& other); + + // Performs a deep copy. + Pickle& operator=(const Pickle& other) = delete; + + Pickle& operator=(Pickle&& other); + + void CopyFrom(const Pickle& other); + + // Returns the size of the Pickle's data. + uint32_t size() const { return header_size_ + header_->payload_size; } + + typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList; + + const BufferList& Buffers() const { return buffers_; } + + uint32_t CurrentSize() const { return buffers_.Size(); } + + // Methods for reading the payload of the Pickle. To read from the start of + // the Pickle, initialize *iter to NULL. If successful, these methods return + // true. Otherwise, false is returned to indicate that the result could not + // be extracted. + [[nodiscard]] bool ReadBool(PickleIterator* iter, bool* result) const; + [[nodiscard]] bool ReadInt16(PickleIterator* iter, int16_t* result) const; + [[nodiscard]] bool ReadUInt16(PickleIterator* iter, uint16_t* result) const; + [[nodiscard]] bool ReadShort(PickleIterator* iter, short* result) const; + [[nodiscard]] bool ReadInt(PickleIterator* iter, int* result) const; + [[nodiscard]] bool ReadLong(PickleIterator* iter, long* result) const; + [[nodiscard]] bool ReadULong(PickleIterator* iter, + unsigned long* result) const; + [[nodiscard]] bool ReadInt32(PickleIterator* iter, int32_t* result) const; + [[nodiscard]] bool ReadUInt32(PickleIterator* iter, uint32_t* result) const; + [[nodiscard]] bool ReadInt64(PickleIterator* iter, int64_t* result) const; + [[nodiscard]] bool ReadUInt64(PickleIterator* iter, uint64_t* result) const; + [[nodiscard]] bool ReadDouble(PickleIterator* iter, double* result) const; + [[nodiscard]] bool ReadIntPtr(PickleIterator* iter, intptr_t* result) const; + [[nodiscard]] bool ReadUnsignedChar(PickleIterator* iter, + unsigned char* result) const; + [[nodiscard]] bool ReadString(PickleIterator* iter, + std::string* result) const; + [[nodiscard]] bool ReadWString(PickleIterator* iter, + std::wstring* result) const; + [[nodiscard]] bool ReadBytesInto(PickleIterator* iter, void* data, + uint32_t length) const; + + // Safer version of ReadInt() checks for the result not being negative. + // Use it for reading the object sizes. + [[nodiscard]] bool ReadLength(PickleIterator* iter, int* result) const; + + [[nodiscard]] bool IgnoreBytes(PickleIterator* iter, uint32_t length) const; + + [[nodiscard]] bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const +#ifdef MOZ_PICKLE_SENTINEL_CHECKING + ; +#else + { + return true; + } +#endif + + template <class T> + [[nodiscard]] bool ReadScalar(PickleIterator* iter, T* result) const { + DCHECK(iter); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return ReadBytesInto(iter, result, sizeof(*result)); + + iter->CopyInto(result); + + UpdateIter(iter, sizeof(*result)); + return true; + } + + bool IgnoreSentinel(PickleIterator* iter) const +#ifdef MOZ_PICKLE_SENTINEL_CHECKING + ; +#else + { + return true; + } +#endif + + // NOTE: The message type optional parameter should _only_ be called from + // generated IPDL code, as it is used to trigger the IPC_READ_LATENCY_MS + // telemetry probe. + void EndRead(PickleIterator& iter, uint32_t ipcMessageType = 0) const; + + // Returns true if the given iterator has at least |len| bytes remaining it, + // across all segments. If there is not that much data available, returns + // false. Generally used when reading a (len, data) pair from the message, + // before allocating |len| bytes of space, to ensure that reading |len| bytes + // will succeed. + bool HasBytesAvailable(const PickleIterator* iter, uint32_t len) const; + + // Truncate the message at the current point, discarding any data after this + // point in the message. + void Truncate(PickleIterator* iter); + + // Methods for adding to the payload of the Pickle. These values are + // appended to the end of the Pickle's payload. When reading values from a + // Pickle, it is important to read them in the order in which they were added + // to the Pickle. + bool WriteBool(bool value); + bool WriteInt16(int16_t value); + bool WriteUInt16(uint16_t value); + bool WriteInt(int value); + bool WriteLong(long value); + bool WriteULong(unsigned long value); + bool WriteInt32(int32_t value); + bool WriteUInt32(uint32_t value); + bool WriteInt64(int64_t value); + bool WriteUInt64(uint64_t value); + bool WriteDouble(double value); + bool WriteIntPtr(intptr_t value); + bool WriteUnsignedChar(unsigned char value); + bool WriteString(const std::string& value); + bool WriteWString(const std::wstring& value); + bool WriteData(const char* data, uint32_t length); + bool WriteBytes(const void* data, uint32_t data_len); + // Takes ownership of data + bool WriteBytesZeroCopy(void* data, uint32_t data_len, uint32_t capacity); + + bool WriteSentinel(uint32_t sentinel) +#ifdef MOZ_PICKLE_SENTINEL_CHECKING + ; +#else + { + return true; + } +#endif + + int32_t* GetInt32PtrForTest(uint32_t offset); + + void InputBytes(const char* data, uint32_t length); + + // Payload follows after allocation of Header (header size is customizable). + struct Header { + uint32_t payload_size; // Specifies the size of the payload. + }; + + // Returns the header, cast to a user-specified type T. The type T must be a + // subclass of Header and its size must correspond to the header_size passed + // to the Pickle constructor. + template <class T> + T* headerT() { + DCHECK(sizeof(T) == header_size_); + return static_cast<T*>(header_); + } + template <class T> + const T* headerT() const { + DCHECK(sizeof(T) == header_size_); + return static_cast<const T*>(header_); + } + + typedef uint32_t memberAlignmentType; + + protected: + uint32_t payload_size() const { return header_->payload_size; } + + // Resizes the buffer for use when writing the specified amount of data. Call + // EndWrite with the given length to pad out for the next write. + void BeginWrite(uint32_t length); + + // Completes the write operation by padding the data with poison bytes. Should + // be paired with BeginWrite, but it does not necessarily have to be called + // after the data is written. + void EndWrite(uint32_t length); + + // Round 'bytes' up to the next multiple of 'alignment'. 'alignment' must be + // a power of 2. + template <uint32_t alignment> + struct ConstantAligner { + static uint32_t align(int bytes) { + static_assert((alignment & (alignment - 1)) == 0, + "alignment must be a power of two"); + return (bytes + (alignment - 1)) & ~static_cast<uint32_t>(alignment - 1); + } + }; + + static uint32_t AlignInt(int bytes) { + return ConstantAligner<sizeof(memberAlignmentType)>::align(bytes); + } + + static uint32_t AlignCapacity(int bytes) { + return ConstantAligner<kSegmentAlignment>::align(bytes); + } + + // Returns true if the given iterator could point to data with the given + // length. If there is no room for the given data before the end of the + // payload, returns false. + bool IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const; + + // Moves the iterator by the given number of bytes, making sure it is aligned. + // Pointer (iterator) is NOT aligned, but the change in the pointer + // is guaranteed to be a multiple of sizeof(memberAlignmentType). + void UpdateIter(PickleIterator* iter, uint32_t bytes) const; + + // Figure out how big the message starting at range_start is. Returns 0 if + // there's no enough data to determine (i.e., if [range_start, range_end) does + // not contain enough of the message header to know the size). + static uint32_t MessageSize(uint32_t header_size, const char* range_start, + const char* range_end); + + // Segments capacities are aligned to 8 bytes to ensure that all reads/writes + // at 8-byte aligned offsets will be on 8-byte aligned pointers. + static const uint32_t kSegmentAlignment = 8; + + private: + friend class PickleIterator; + + BufferList buffers_; + Header* header_; + uint32_t header_size_; +}; + +#endif // BASE_PICKLE_H__ diff --git a/ipc/chromium/src/base/platform_file.h b/ipc/chromium/src/base/platform_file.h new file mode 100644 index 0000000000..2c03821539 --- /dev/null +++ b/ipc/chromium/src/base/platform_file.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PLATFORM_FILE_H_ +#define BASE_PLATFORM_FILE_H_ + +#if defined(XP_WIN) +# include <windows.h> +#endif + +#include <string> + +namespace base { + +#if defined(XP_WIN) +typedef HANDLE PlatformFile; +const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; +#else +typedef int PlatformFile; +const PlatformFile kInvalidPlatformFileValue = -1; +#endif + +enum PlatformFileFlags { + PLATFORM_FILE_OPEN = 1, + PLATFORM_FILE_CREATE = 2, + PLATFORM_FILE_OPEN_ALWAYS = 4, // May create a new file. + PLATFORM_FILE_CREATE_ALWAYS = 8, // May overwrite an old file. + PLATFORM_FILE_READ = 16, + PLATFORM_FILE_WRITE = 32, + PLATFORM_FILE_EXCLUSIVE_READ = 64, // EXCLUSIVE is opposite of Windows SHARE + PLATFORM_FILE_EXCLUSIVE_WRITE = 128, + PLATFORM_FILE_ASYNC = 256 +}; + +// Creates or open the given file. If PLATFORM_FILE_OPEN_ALWAYS is used, and +// |created| is provided, |created| will be set to true if the file was created +// or to false in case the file was just opened. +PlatformFile CreatePlatformFile(const std::wstring& name, int flags, + bool* created); + +} // namespace base + +#endif // BASE_PLATFORM_FILE_H_ diff --git a/ipc/chromium/src/base/platform_thread.h b/ipc/chromium/src/base/platform_thread.h new file mode 100644 index 0000000000..f3a65e8797 --- /dev/null +++ b/ipc/chromium/src/base/platform_thread.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WARNING: You should *NOT* be using this class directly. PlatformThread is +// the low-level platform-specific abstraction to the OS's threading interface. +// You should instead be using a message-loop driven Thread, see thread.h. + +#ifndef BASE_PLATFORM_THREAD_H_ +#define BASE_PLATFORM_THREAD_H_ + +#include "base/basictypes.h" + +// PlatformThreadHandle should not be assumed to be a numeric type, since the +// standard intends to allow pthread_t to be a structure. This means you +// should not initialize it to a value, like 0. If it's a member variable, the +// constructor can safely "value initialize" using () in the initializer list. +#if defined(XP_WIN) +# include <windows.h> +typedef DWORD PlatformThreadId; +typedef void* PlatformThreadHandle; // HANDLE +#else +# include <pthread.h> +typedef pthread_t PlatformThreadHandle; +# if defined(XP_LINUX) || defined(XP_OPENBSD) || defined(XP_SOLARIS) || \ + defined(__GLIBC__) +# include <unistd.h> +typedef pid_t PlatformThreadId; +# elif defined(__DragonFly__) || defined(XP_FREEBSD) || defined(XP_NETBSD) +# include <sys/types.h> +typedef lwpid_t PlatformThreadId; +# elif defined(XP_DARWIN) +# include <mach/mach.h> +typedef mach_port_t PlatformThreadId; +# endif +#endif + +// A namespace for low-level thread functions. +class PlatformThread { + public: + // Gets the current thread id, which may be useful for logging purposes. + static PlatformThreadId CurrentId(); + + // Yield the current thread so another thread can be scheduled. + static void YieldCurrentThread(); + + // Sleeps for the specified duration (units are milliseconds). + static void Sleep(int duration_ms); + + // Sets the thread name visible to a debugger. This has no effect otherwise. + static void SetName(const char* name); + + // Implement this interface to run code on a background thread. Your + // ThreadMain method will be called on the newly created thread. + class Delegate { + public: + virtual ~Delegate() {} + virtual void ThreadMain() = 0; + }; + + // Creates a new thread. The |stack_size| parameter can be 0 to indicate + // that the default stack size should be used. Upon success, + // |*thread_handle| will be assigned a handle to the newly created thread, + // and |delegate|'s ThreadMain method will be executed on the newly created + // thread. + // NOTE: When you are done with the thread handle, you must call Join to + // release system resources associated with the thread. You must ensure that + // the Delegate object outlives the thread. + static bool Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle); + + // CreateNonJoinable() does the same thing as Create() except the thread + // cannot be Join()'d. Therefore, it also does not output a + // PlatformThreadHandle. + static bool CreateNonJoinable(size_t stack_size, Delegate* delegate); + + // Joins with a thread created via the Create function. This function blocks + // the caller until the designated thread exits. This will invalidate + // |thread_handle|. + static void Join(PlatformThreadHandle thread_handle); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); +}; + +#endif // BASE_PLATFORM_THREAD_H_ diff --git a/ipc/chromium/src/base/platform_thread_mac.mm b/ipc/chromium/src/base/platform_thread_mac.mm new file mode 100644 index 0000000000..32917ff11e --- /dev/null +++ b/ipc/chromium/src/base/platform_thread_mac.mm @@ -0,0 +1,75 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/platform_thread.h" + +#import <Foundation/Foundation.h> +#include <dlfcn.h> + +#include "base/logging.h" +#include "base/scoped_nsautorelease_pool.h" + +// A simple class that demonstrates our impressive ability to do nothing. +@interface NoOp : NSObject + +// Does the deed. Or does it? ++ (void)noOp; + +@end + +@implementation NoOp + ++ (void)noOp { +} + +@end + +namespace base { + +// If Cocoa is to be used on more than one thread, it must know that the +// application is multithreaded. Since it's possible to enter Cocoa code +// from threads created by pthread_thread_create, Cocoa won't necessarily +// be aware that the application is multithreaded. Spawning an NSThread is +// enough to get Cocoa to set up for multithreaded operation, so this is done +// if necessary before pthread_thread_create spawns any threads. +// +// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html +void InitThreading() { + // this is called early in startup, before the event loop, so provide + // an autorelease pool to prevent leaks here + ScopedNSAutoreleasePool pool; + + static BOOL multithreaded = [NSThread isMultiThreaded]; + if (!multithreaded) { + [NSThread detachNewThreadSelector:@selector(noOp) + toTarget:[NoOp class] + withObject:nil]; + multithreaded = YES; + + DCHECK([NSThread isMultiThreaded]); + } +} + +} // namespace base + +// static +void PlatformThread::SetName(const char* name) { + // pthread_setname_np is only available in 10.6 or later, so test + // for it at runtime. + int (*dynamic_pthread_setname_np)(const char*); + *reinterpret_cast<void**>(&dynamic_pthread_setname_np) = + dlsym(RTLD_DEFAULT, "pthread_setname_np"); + if (!dynamic_pthread_setname_np) return; + + // Mac OS X does not expose the length limit of the name, so + // hardcode it. + const int kMaxNameLength = 63; + std::string shortened_name = std::string(name).substr(0, kMaxNameLength); + // pthread_setname() fails (harmlessly) in the sandbox, ignore when it does. + // See http://crbug.com/47058 + + // The name parameter is copied thus it's safe to release it after calling. + // Checked against the bionic implementation in bionic/libc/bionic/pthread.c + dynamic_pthread_setname_np(shortened_name.c_str()); +} diff --git a/ipc/chromium/src/base/platform_thread_posix.cc b/ipc/chromium/src/base/platform_thread_posix.cc new file mode 100644 index 0000000000..d28abbc91f --- /dev/null +++ b/ipc/chromium/src/base/platform_thread_posix.cc @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/platform_thread.h" + +#include <errno.h> +#include <sched.h> + +#if defined(XP_DARWIN) +# include <mach/mach.h> +#elif defined(XP_NETBSD) +# include <lwp.h> +#elif defined(XP_LINUX) +# include <sys/syscall.h> +# include <sys/prctl.h> +#endif + +#if !defined(XP_DARWIN) +# include <unistd.h> +#endif + +#if (defined(__DragonFly__) || defined(XP_FREEBSD) || defined(XP_OPENBSD)) && \ + !defined(__GLIBC__) +# include <pthread_np.h> +#endif + +#include "nsThreadUtils.h" + +#if defined(XP_DARWIN) +namespace base { +void InitThreading(); +} // namespace base +#endif + +static void* ThreadFunc(void* closure) { + PlatformThread::Delegate* delegate = + static_cast<PlatformThread::Delegate*>(closure); + delegate->ThreadMain(); + return NULL; +} + +// static +PlatformThreadId PlatformThread::CurrentId() { + // Pthreads doesn't have the concept of a thread ID, so we have to reach down + // into the kernel. +#if defined(XP_DARWIN) + mach_port_t port = mach_thread_self(); + mach_port_deallocate(mach_task_self(), port); + return port; +#elif defined(XP_LINUX) + return syscall(__NR_gettid); +#elif defined(XP_OPENBSD) || defined(XP_SOLARIS) || defined(__GLIBC__) + return (intptr_t)(pthread_self()); +#elif defined(XP_NETBSD) + return _lwp_self(); +#elif defined(__DragonFly__) + return lwp_gettid(); +#elif defined(XP_FREEBSD) + return pthread_getthreadid_np(); +#endif +} + +// static +void PlatformThread::YieldCurrentThread() { sched_yield(); } + +// static +void PlatformThread::Sleep(int duration_ms) { + struct timespec sleep_time, remaining; + + // Contains the portion of duration_ms >= 1 sec. + sleep_time.tv_sec = duration_ms / 1000; + duration_ms -= sleep_time.tv_sec * 1000; + + // Contains the portion of duration_ms < 1 sec. + sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds. + + while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) + sleep_time = remaining; +} + +#ifndef XP_DARWIN +// Mac is implemented in platform_thread_mac.mm. + +// static +void PlatformThread::SetName(const char* name) { + // On linux we can get the thread names to show up in the debugger by setting + // the process name for the LWP. We don't want to do this for the main + // thread because that would rename the process, causing tools like killall + // to stop working. + if (PlatformThread::CurrentId() == getpid()) return; + + // Using NS_SetCurrentThreadName, as opposed to using platform APIs directly, + // also sets the thread name on the PRThread wrapper, and allows us to + // retrieve it using PR_GetThreadName. + NS_SetCurrentThreadName(name); +} +#endif // !XP_DARWIN + +namespace { + +bool CreateThread(size_t stack_size, bool joinable, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* thread_handle) { +#if defined(XP_DARWIN) + base::InitThreading(); +#endif // XP_DARWIN + + bool success = false; + pthread_attr_t attributes; + pthread_attr_init(&attributes); + + // Pthreads are joinable by default, so only specify the detached attribute if + // the thread should be non-joinable. + if (!joinable) { + pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); + } + + if (stack_size == 0) stack_size = nsIThreadManager::DEFAULT_STACK_SIZE; + pthread_attr_setstacksize(&attributes, stack_size); + + success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate); + + pthread_attr_destroy(&attributes); + return success; +} + +} // anonymous namespace + +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + return CreateThread(stack_size, true /* joinable thread */, delegate, + thread_handle); +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + PlatformThreadHandle unused; + + bool result = CreateThread(stack_size, false /* non-joinable thread */, + delegate, &unused); + return result; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + pthread_join(thread_handle, NULL); +} diff --git a/ipc/chromium/src/base/platform_thread_win.cc b/ipc/chromium/src/base/platform_thread_win.cc new file mode 100644 index 0000000000..59eb45aff6 --- /dev/null +++ b/ipc/chromium/src/base/platform_thread_win.cc @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/platform_thread.h" + +#include "base/logging.h" +#include "base/win_util.h" + +#include "nsThreadUtils.h" + +namespace { + +DWORD __stdcall ThreadFunc(void* closure) { + PlatformThread::Delegate* delegate = + static_cast<PlatformThread::Delegate*>(closure); + delegate->ThreadMain(); + return 0; +} + +} // namespace + +// static +PlatformThreadId PlatformThread::CurrentId() { return GetCurrentThreadId(); } + +// static +void PlatformThread::YieldCurrentThread() { ::Sleep(0); } + +// static +void PlatformThread::Sleep(int duration_ms) { ::Sleep(duration_ms); } + +// static +void PlatformThread::SetName(const char* name) { + // Using NS_SetCurrentThreadName, as opposed to using platform APIs directly, + // also sets the thread name on the PRThread wrapper, and allows us to + // retrieve it using PR_GetThreadName. + NS_SetCurrentThreadName(name); +} + +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + unsigned int flags = 0; + if (stack_size > 0) { + flags = STACK_SIZE_PARAM_IS_A_RESERVATION; + } else { + stack_size = 0; + } + + // Using CreateThread here vs _beginthreadex makes thread creation a bit + // faster and doesn't require the loader lock to be available. Our code will + // have to work running on CreateThread() threads anyway, since we run code + // on the Windows thread pool, etc. For some background on the difference: + // http://www.microsoft.com/msj/1099/win32/win321099.aspx + *thread_handle = + CreateThread(NULL, stack_size, ThreadFunc, delegate, flags, NULL); + return *thread_handle != NULL; +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + PlatformThreadHandle thread_handle; + bool result = Create(stack_size, delegate, &thread_handle); + CloseHandle(thread_handle); + return result; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + DCHECK(thread_handle); + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + DWORD result = WaitForSingleObject(thread_handle, INFINITE); + DCHECK_EQ(WAIT_OBJECT_0, result); + + CloseHandle(thread_handle); +} diff --git a/ipc/chromium/src/base/port.h b/ipc/chromium/src/base/port.h new file mode 100644 index 0000000000..6006b56792 --- /dev/null +++ b/ipc/chromium/src/base/port.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PORT_H_ +#define BASE_PORT_H_ + +#include <stdarg.h> + +#ifdef _MSC_VER +# define GG_LONGLONG(x) x##I64 +# define GG_ULONGLONG(x) x##UI64 +#else +# define GG_LONGLONG(x) x##LL +# define GG_ULONGLONG(x) x##ULL +#endif + +// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including <stdint.h> +// to get the INTn_C and UINTn_C macros for integer constants. It's difficult +// to guarantee any specific ordering of header includes, so it's difficult to +// guarantee that the INTn_C macros can be defined by including <stdint.h> at +// any specific point. Provide GG_INTn_C macros instead. + +#define GG_INT8_C(x) (x) +#define GG_INT16_C(x) (x) +#define GG_INT32_C(x) (x) +#define GG_INT64_C(x) GG_LONGLONG(x) + +#define GG_UINT8_C(x) (x##U) +#define GG_UINT16_C(x) (x##U) +#define GG_UINT32_C(x) (x##U) +#define GG_UINT64_C(x) GG_ULONGLONG(x) + +namespace base { + +// It's possible for functions that use a va_list, such as StringPrintf, to +// invalidate the data in it upon use. The fix is to make a copy of the +// structure before using it and use that copy instead. va_copy is provided +// for this purpose. MSVC does not provide va_copy, so define an +// implementation here. It is not guaranteed that assignment is a copy, so the +// StringUtil.VariableArgsFunc unit test tests this capability. + +// The C standard says that va_copy is a "macro", not a function. Trying to +// use va_list as ref args to a function, as above, breaks some machines. +#if defined(__GNUC__) +# define base_va_copy(_a, _b) ::va_copy(_a, _b) +#elif defined(_MSC_VER) +# define base_va_copy(_a, _b) (_a = _b) +#else +# error No va_copy for your compiler +#endif + +} // namespace base + +// Define an OS-neutral wrapper for shared library entry points +#if defined(XP_WIN) +# define API_CALL __stdcall +#elif defined(XP_LINUX) || defined(XP_DARWIN) +# define API_CALL +#endif + +#endif // BASE_PORT_H_ diff --git a/ipc/chromium/src/base/process.h b/ipc/chromium/src/base/process.h new file mode 100644 index 0000000000..006afc1d3f --- /dev/null +++ b/ipc/chromium/src/base/process.h @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PROCESS_H_ +#define BASE_PROCESS_H_ + +#include "base/basictypes.h" + +#include <sys/types.h> +#ifdef XP_WIN +# include <windows.h> +#endif + +namespace base { + +// ProcessHandle is a platform specific type which represents the underlying OS +// handle to a process. +// ProcessId is a number which identifies the process in the OS. +#if defined(XP_WIN) +typedef HANDLE ProcessHandle; +typedef DWORD ProcessId; +// inttypes.h-like macro for ProcessId formatting. +# define PRIPID "lu" + +const ProcessHandle kInvalidProcessHandle = INVALID_HANDLE_VALUE; + +// In theory, on Windows, this is a valid process ID, but in practice they are +// currently divisible by four. Process IDs share the kernel handle allocation +// code and they are guaranteed to be divisible by four. +// As this could change for process IDs we shouldn't generally rely on this +// property, however even if that were to change, it seems safe to rely on this +// particular value never being used. +const ProcessId kInvalidProcessId = kuint32max; +#else +// On POSIX, our ProcessHandle will just be the PID. +typedef pid_t ProcessHandle; +typedef pid_t ProcessId; +// inttypes.h-like macro for ProcessId formatting. +# define PRIPID "d" + +const ProcessHandle kInvalidProcessHandle = -1; +const ProcessId kInvalidProcessId = -1; +#endif + +} // namespace base + +#endif // BASE_PROCESS_H_ diff --git a/ipc/chromium/src/base/process_util.h b/ipc/chromium/src/base/process_util.h new file mode 100644 index 0000000000..45426d616e --- /dev/null +++ b/ipc/chromium/src/base/process_util.h @@ -0,0 +1,342 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file/namespace contains utility functions for enumerating, ending and +// computing statistics of processes. + +#ifndef BASE_PROCESS_UTIL_H_ +#define BASE_PROCESS_UTIL_H_ + +#include "base/basictypes.h" + +#if defined(XP_WIN) +# include "mozilla/ipc/EnvironmentMap.h" +# include <windows.h> +# include <tlhelp32.h> +#elif defined(XP_LINUX) || defined(__GLIBC__) +# include <dirent.h> +# include <limits.h> +# include <sys/types.h> +#elif defined(XP_DARWIN) +# include <mach/mach.h> +#endif + +#include <cstddef> +#include <map> +#include <string> +#include <utility> +#include <vector> + +#include "base/process.h" + +#include "mozilla/UniquePtr.h" +#include "mozilla/Result.h" +#include "mozilla/ResultVariant.h" + +#include "mozilla/ipc/LaunchError.h" + +#if defined(MOZ_ENABLE_FORKSERVER) +# include "nsStringFwd.h" +# include "mozilla/ipc/FileDescriptorShuffle.h" + +namespace mozilla { +namespace ipc { +class FileDescriptor; +} +} // namespace mozilla +#endif + +#if defined(XP_DARWIN) +struct kinfo_proc; +#endif + +class CommandLine; + +namespace base { + +using mozilla::Err; +using mozilla::Ok; +using mozilla::Result; +using mozilla::ipc::LaunchError; + +enum ProcessArchitecture { + PROCESS_ARCH_INVALID = 0x0, + PROCESS_ARCH_I386 = 0x1, + PROCESS_ARCH_X86_64 = 0x2, + PROCESS_ARCH_PPC = 0x4, + PROCESS_ARCH_PPC_64 = 0x8, + PROCESS_ARCH_ARM = 0x10, + PROCESS_ARCH_ARM_64 = 0x20 +}; + +// A minimalistic but hopefully cross-platform set of exit codes. +// Do not change the enumeration values or you will break third-party +// installers. +enum { + PROCESS_END_NORMAL_TERMINATON = 0, + PROCESS_END_KILLED_BY_USER = 1, + PROCESS_END_PROCESS_WAS_HUNG = 2 +}; + +// Returns the id of the current process. +ProcessId GetCurrentProcId(); + +// Returns the ProcessHandle of the current process. +ProcessHandle GetCurrentProcessHandle(); + +// Converts a PID to a process handle. This handle must be closed by +// CloseProcessHandle when you are done with it. Returns true on success. +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle); + +// Converts a PID to a process handle. On Windows the handle is opened +// with more access rights and must only be used by trusted code. +// You have to close returned handle using CloseProcessHandle. Returns true +// on success. +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle); + +// Closes the process handle opened by OpenProcessHandle. +void CloseProcessHandle(ProcessHandle process); + +// Returns the unique ID for the specified process. This is functionally the +// same as Windows' GetProcessId(), but works on versions of Windows before +// Win XP SP1 as well. +ProcessId GetProcId(ProcessHandle process); + +#if defined(XP_UNIX) +// Close all file descriptors, except for std{in,out,err} and those +// for which the given function returns true. Only call this function +// in a child process where you know that there aren't any other +// threads. +void CloseSuperfluousFds(void* aCtx, bool (*aShouldPreserve)(void*, int)); + +typedef std::vector<std::pair<int, int> > file_handle_mapping_vector; +typedef std::map<std::string, std::string> environment_map; + +// Deleter for the array of strings allocated within BuildEnvironmentArray. +struct FreeEnvVarsArray { + void operator()(char** array); +}; + +typedef mozilla::UniquePtr<char*[], FreeEnvVarsArray> EnvironmentArray; +#endif + +struct LaunchOptions { + // If true, wait for the process to terminate. Otherwise, return + // immediately. + bool wait = false; + +#if defined(XP_WIN) + bool start_hidden = false; + + // Start as an independent process rather than a process that is closed by the + // parent job. This will pass the flag CREATE_BREAKAWAY_FROM_JOB. + bool start_independent = false; + + // Environment variables to be applied in addition to the current + // process's environment, replacing them where necessary. + EnvironmentMap env_map; + + std::vector<HANDLE> handles_to_inherit; +#endif +#if defined(XP_UNIX) + environment_map env_map; + + // If non-null, specifies the entire environment to use for the + // child process, instead of inheriting from the parent; env_map is + // ignored in that case. Note that the strings are allocated using + // malloc (e.g., with strdup), but the array of pointers is + // allocated with new[] and is terminated with a null pointer. + EnvironmentArray full_env; + + // If non-empty, set the child process's current working directory. + std::string workdir; + + // A mapping of (src fd -> dest fd) to propagate into the child + // process. All other fds will be closed, except std{in,out,err}. + file_handle_mapping_vector fds_to_remap; +#endif + +#if defined(MOZ_ENABLE_FORKSERVER) + bool use_forkserver = false; +#endif + +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + // These fields are used by the sandboxing code in SandboxLaunch.cpp. + // It's not ideal to have them here, but trying to abstract them makes + // it harder to serialize LaunchOptions for the fork server. + // + // (fork_flags holds extra flags for the clone() syscall, and + // sandbox_chroot indicates whether the child process will be given + // the ability to chroot() itself to an empty directory.) + int fork_flags = 0; + bool sandbox_chroot = false; +#endif + +#ifdef XP_DARWIN + // On macOS 10.14+, disclaims responsibility for the child process + // with respect to privacy/security permission prompts and + // decisions. Ignored if not supported by the OS. + bool disclaim = false; +# ifdef __aarch64__ + // The architecture to launch when launching a "universal" binary. + // Note: the implementation only supports launching x64 child + // processes from arm64 parent processes. + uint32_t arch = PROCESS_ARCH_INVALID; +# endif // __aarch64__ +#endif // XP_DARWIN +}; + +#if defined(XP_WIN) +// Runs the given application name with the given command line. Normally, the +// first command line argument should be the path to the process, and don't +// forget to quote it. +// +// Example (including literal quotes) +// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\" +// +// If process_handle is non-NULL, the process handle of the launched app will be +// stored there on a successful launch. +// NOTE: In this case, the caller is responsible for closing the handle so +// that it doesn't leak! +Result<Ok, LaunchError> LaunchApp(const std::wstring& cmdline, + const LaunchOptions& options, + ProcessHandle* process_handle); + +Result<Ok, LaunchError> LaunchApp(const CommandLine& cl, const LaunchOptions&, + ProcessHandle* process_handle); +#else +// Runs the application specified in argv[0] with the command line argv. +// +// The pid will be stored in process_handle if that pointer is +// non-null. +// +// Note that the first argument in argv must point to the filename, +// and must be fully specified (i.e., this will not search $PATH). +Result<Ok, LaunchError> LaunchApp(const std::vector<std::string>& argv, + LaunchOptions&& options, + ProcessHandle* process_handle); + +// Merge an environment map with the current environment. +// Existing variables are overwritten by env_vars_to_set. +EnvironmentArray BuildEnvironmentArray(const environment_map& env_vars_to_set); +#endif + +#if defined(MOZ_ENABLE_FORKSERVER) +/** + * Create and initialize a new process as a content process. + * + * This class is used only by the fork server. + * To create a new content process, two steps are + * - calling |ForkProcess()| to create a new process, and + * - calling |InitAppProcess()| in the new process, the child + * process, to initialize it for running WEB content later. + * + * The fork server can clean up it's resources in-between the first + * and second step, that is why two steps. + */ +class AppProcessBuilder { + public: + AppProcessBuilder(); + // This function will fork a new process for use as a + // content processes. + bool ForkProcess(const std::vector<std::string>& argv, + LaunchOptions&& options, ProcessHandle* process_handle); + // This function will be called in the child process to initializes + // the environment of the content process. It should be called + // after the message loop of the main thread, to make sure the fork + // server is destroyed properly in the child process. + // + // The message loop may allocate resources like file descriptors. + // If this function is called before the end of the loop, the + // reosurces may be destroyed while the loop is still alive. + void InitAppProcess(int* argcp, char*** argvp); + + private: + void ReplaceArguments(int* argcp, char*** argvp); + + mozilla::ipc::FileDescriptorShuffle shuffle_; + std::vector<std::string> argv_; +}; + +void InitForkServerProcess(); + +/** + * Make a FD not being closed when create a new content process. + * + * AppProcessBuilder would close most unrelated FDs for new content + * processes. You may want to reserve some of FDs to keep using them + * in content processes. + */ +void RegisterForkServerNoCloseFD(int aFd); +#endif + +// Attempts to kill the process identified by the given process +// entry structure, giving it the specified exit code. +// Returns true if this is successful, false otherwise. +bool KillProcess(ProcessHandle process, int exit_code); + +#ifdef XP_UNIX +// Returns whether the given process has exited. If it returns true, +// the process status has been consumed and `IsProcessDead` should not +// be called again on the same process (like `waitpid`). +// +// In various error cases (e.g., the process doesn't exist or isn't a +// child of this process) it will also return true to indicate that +// the caller should give up and not try again. +// +// If the `blocking` parameter is set to true, this function will try +// to block the calling thread indefinitely until the process exits. +// This may not be possible (if the child is also being debugged by +// the parent process, e.g. due to the crash reporter), in which case +// it will return false and the caller will need to wait and retry. +bool IsProcessDead(ProcessHandle handle, bool blocking = false); +#endif + +} // namespace base + +namespace mozilla { + +class EnvironmentLog { + public: + template <size_t N> + explicit EnvironmentLog(const char (&varname)[N]) + : EnvironmentLog(varname, N) {} + + ~EnvironmentLog() {} + + void print(const char* format, ...); + + private: + explicit EnvironmentLog(const char* varname, size_t len); + +#if defined(XP_WIN) + std::wstring fname_; +#else + std::string fname_; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(EnvironmentLog); +}; + +#if defined(MOZ_ENABLE_FORKSERVER) +typedef std::tuple<nsCString, nsCString> EnvVar; +typedef std::tuple<mozilla::ipc::FileDescriptor, int> FdMapping; +#endif + +} // namespace mozilla + +#if defined(XP_WIN) +// Undo the windows.h damage +# undef GetMessage +# undef CreateEvent +# undef GetClassName +# undef GetBinaryType +# undef RemoveDirectory +# undef LoadImage +# undef LoadIcon +#endif + +#endif // BASE_PROCESS_UTIL_H_ diff --git a/ipc/chromium/src/base/process_util_linux.cc b/ipc/chromium/src/base/process_util_linux.cc new file mode 100644 index 0000000000..4ea71906f2 --- /dev/null +++ b/ipc/chromium/src/base/process_util_linux.cc @@ -0,0 +1,368 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process_util.h" + +#include <string> +#include <sys/wait.h> +#include <unistd.h> + +#if defined(MOZ_CODE_COVERAGE) +# include "nsString.h" +#endif + +#include "mozilla/ipc/LaunchError.h" + +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) +# include "mozilla/SandboxLaunch.h" +#endif + +#if defined(MOZ_ENABLE_FORKSERVER) +# include <stdlib.h> +# include <fcntl.h> +# if defined(DEBUG) +# include "base/message_loop.h" +# endif +# include "mozilla/ipc/ForkServiceChild.h" + +# include "mozilla/Unused.h" +# include "mozilla/ScopeExit.h" +# include "mozilla/ipc/ProcessUtils.h" +# include "mozilla/ipc/SetProcessTitle.h" + +using namespace mozilla::ipc; +#endif + +#if defined(MOZ_CODE_COVERAGE) +# include "prenv.h" +# include "mozilla/ipc/EnvironmentMap.h" +#endif + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "mozilla/ipc/FileDescriptor.h" +#include "mozilla/ipc/FileDescriptorShuffle.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Result.h" + +// WARNING: despite the name, this file is also used on the BSDs and +// Solaris (basically, Unixes that aren't Mac OS), not just Linux. + +namespace { + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +#if defined(MOZ_ENABLE_FORKSERVER) +static mozilla::StaticAutoPtr<std::vector<int> > sNoCloseFDs; + +void RegisterForkServerNoCloseFD(int fd) { + if (!sNoCloseFDs) { + sNoCloseFDs = new std::vector<int>(); + } + sNoCloseFDs->push_back(fd); +} + +static bool IsNoCloseFd(int fd) { + if (!sNoCloseFDs) { + return false; + } + return std::any_of(sNoCloseFDs->begin(), sNoCloseFDs->end(), + [fd](int regfd) -> bool { return regfd == fd; }); +} + +AppProcessBuilder::AppProcessBuilder() {} + +static void ReplaceEnviroment(const LaunchOptions& options) { + for (auto& elt : options.env_map) { + setenv(elt.first.c_str(), elt.second.c_str(), 1); + } +} + +bool AppProcessBuilder::ForkProcess(const std::vector<std::string>& argv, + LaunchOptions&& options, + ProcessHandle* process_handle) { + auto cleanFDs = mozilla::MakeScopeExit([&] { + for (auto& elt : options.fds_to_remap) { + auto fd = std::get<0>(elt); + close(fd); + } + }); + +# if defined(XP_LINUX) && defined(MOZ_SANDBOX) + mozilla::SandboxLaunch launcher; + if (!launcher.Prepare(&options)) { + return false; + } +# else + struct { + pid_t Fork() { return fork(); } + } launcher; +# endif + + argv_ = argv; + if (!shuffle_.Init(options.fds_to_remap)) { + return false; + } + + // Avoid the content of the buffer being sent out by child processes + // repeatly. + fflush(stdout); + fflush(stderr); + + pid_t pid = launcher.Fork(); + // WARNING: if pid == 0, only async signal safe operations are permitted from + // here until exec or _exit. + // + // Specifically, heap allocation is not safe: the sandbox's fork substitute + // won't run the pthread_atfork handlers that fix up the malloc locks. + + if (pid < 0) { + return false; + } + + if (pid == 0) { + cleanFDs.release(); + ReplaceEnviroment(options); + } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); + if (options.wait) HANDLE_EINTR(waitpid(pid, 0, 0)); + } + + if (process_handle) *process_handle = pid; + + return true; +} + +void AppProcessBuilder::ReplaceArguments(int* argcp, char*** argvp) { + // Change argc & argv of main() with the arguments passing + // through IPC. + char** argv = new char*[argv_.size() + 1]; + char** p = argv; + for (auto& elt : argv_) { + *p++ = strdup(elt.c_str()); + } + *p = nullptr; + *argvp = argv; + *argcp = argv_.size(); + mozilla::SetProcessTitle(argv_); +} + +void AppProcessBuilder::InitAppProcess(int* argcp, char*** argvp) { + MOZ_ASSERT(MessageLoop::current() == nullptr, + "The message loop of the main thread should have been destroyed"); + + // The fork server handle SIGCHLD to read status of content + // processes to handle Zombies. But, it is not necessary for + // content processes. + signal(SIGCHLD, SIG_DFL); + + for (const auto& fds : shuffle_.Dup2Sequence()) { + int fd = HANDLE_EINTR(dup2(fds.first, fds.second)); + MOZ_RELEASE_ASSERT(fd == fds.second, "dup2 failed"); + } + + CloseSuperfluousFds(&shuffle_, [](void* ctx, int fd) { + return static_cast<decltype(&shuffle_)>(ctx)->MapsTo(fd) || IsNoCloseFd(fd); + }); + // Without this, the destructor of |shuffle_| would try to close FDs + // created by it, but they have been closed by + // |CloseSuperfluousFds()|. + shuffle_.Forget(); + + ReplaceArguments(argcp, argvp); +} + +static void InstallChildSignalHandler() { + // Eventually (bug 1752638) we'll want a real SIGCHLD handler, but + // for now, cause child processes to be automatically collected. + signal(SIGCHLD, SIG_IGN); +} + +static void ReserveFileDescriptors() { + // Reserve the lower positions of the file descriptors to make sure + // debug files and other files don't take these positions. So we + // can keep their file descriptors during CloseSuperfluousFds() with + // out any confliction with mapping passing from the parent process. + int fd = open("/dev/null", O_RDONLY); + for (int i = 1; i < 10; i++) { + mozilla::Unused << dup(fd); + } +} + +void InitForkServerProcess() { + InstallChildSignalHandler(); + ReserveFileDescriptors(); + SetThisProcessName("forkserver"); +} + +static Result<Ok, LaunchError> LaunchAppWithForkServer( + const std::vector<std::string>& argv, const LaunchOptions& options, + ProcessHandle* process_handle) { + MOZ_ASSERT(ForkServiceChild::Get()); + + // Check for unsupported options + MOZ_ASSERT(options.workdir.empty()); + MOZ_ASSERT(!options.full_env); + MOZ_ASSERT(!options.wait); + + ForkServiceChild::Args forkArgs; + +# if defined(XP_LINUX) && defined(MOZ_SANDBOX) + forkArgs.mForkFlags = options.fork_flags; + forkArgs.mChroot = options.sandbox_chroot; +# endif + + for (auto& arg : argv) { + forkArgs.mArgv.AppendElement(arg.c_str()); + } + for (auto& vv : options.env_map) { + forkArgs.mEnv.AppendElement(mozilla::EnvVar(nsCString(vv.first.c_str()), + nsCString(vv.second.c_str()))); + } + for (auto& fdmapping : options.fds_to_remap) { + forkArgs.mFdsRemap.AppendElement(mozilla::FdMapping( + mozilla::ipc::FileDescriptor(fdmapping.first), fdmapping.second)); + } + + return ForkServiceChild::Get()->SendForkNewSubprocess(forkArgs, + process_handle); +} +#endif // MOZ_ENABLE_FORKSERVER + +Result<Ok, LaunchError> LaunchApp(const std::vector<std::string>& argv, + LaunchOptions&& options, + ProcessHandle* process_handle) { +#if defined(MOZ_ENABLE_FORKSERVER) + if (options.use_forkserver && ForkServiceChild::Get()) { + return LaunchAppWithForkServer(argv, options, process_handle); + } +#endif + + mozilla::UniquePtr<char*[]> argv_cstr(new char*[argv.size() + 1]); + +#if defined(XP_LINUX) && defined(MOZ_SANDBOX) + mozilla::SandboxLaunch launcher; + if (!launcher.Prepare(&options)) { + return Err(LaunchError("SL::Prepare", errno)); + } +#else + struct { + pid_t Fork() { return fork(); } + } launcher; +#endif + + EnvironmentArray env_storage; + const EnvironmentArray& envp = + options.full_env ? options.full_env + : (env_storage = BuildEnvironmentArray(options.env_map)); + + // Init() there will call fcntl(F_DUPFD/F_DUPFD_CLOEXEC) under the hood in + // https://searchfox.org/mozilla-central/rev/55d5c4b9dffe5e59eb6b019c1a930ec9ada47e10/ipc/glue/FileDescriptorShuffle.cpp#72 + // so it will set errno. + mozilla::ipc::FileDescriptorShuffle shuffle; + if (!shuffle.Init(options.fds_to_remap)) { + CHROMIUM_LOG(WARNING) << "FileDescriptorShuffle::Init failed"; + return Err(LaunchError("FileDescriptorShuffle", errno)); + } + +#ifdef MOZ_CODE_COVERAGE + // Before gcc/clang 10 there is a gcda dump before the fork. + // This dump mustn't be interrupted by a SIGUSR1 else we may + // have a dead lock (see bug 1637377). + // So we just remove the handler and restore it after the fork + // It's up the child process to set it up. + // Once we switch to gcc/clang 10, we could just remove it in the child + // process + void (*ccovSigHandler)(int) = signal(SIGUSR1, SIG_IGN); + const char* gcov_child_prefix = PR_GetEnv("GCOV_CHILD_PREFIX"); +#endif + + pid_t pid = launcher.Fork(); + // WARNING: if pid == 0, only async signal safe operations are permitted from + // here until exec or _exit. + // + // Specifically, heap allocation is not safe: the sandbox's fork substitute + // won't run the pthread_atfork handlers that fix up the malloc locks. + + if (pid < 0) { + CHROMIUM_LOG(WARNING) << "fork() failed: " << strerror(errno); + return Err(LaunchError("fork", errno)); + } + + if (pid == 0) { + // In the child: + if (!options.workdir.empty()) { + if (chdir(options.workdir.c_str()) != 0) { + // See under execve about logging unsafety. + DLOG(ERROR) << "chdir failed " << options.workdir; + _exit(127); + } + } + + for (const auto& fds : shuffle.Dup2Sequence()) { + if (HANDLE_EINTR(dup2(fds.first, fds.second)) != fds.second) { + // This shouldn't happen, but check for it. And see below + // about logging being unsafe here, so this is debug only. + DLOG(ERROR) << "dup2 failed"; + _exit(127); + } + } + + CloseSuperfluousFds(&shuffle, [](void* aCtx, int aFd) { + return static_cast<decltype(&shuffle)>(aCtx)->MapsTo(aFd); + }); + + for (size_t i = 0; i < argv.size(); i++) + argv_cstr[i] = const_cast<char*>(argv[i].c_str()); + argv_cstr[argv.size()] = NULL; + +#ifdef MOZ_CODE_COVERAGE + if (gcov_child_prefix && !options.full_env) { + const pid_t child_pid = getpid(); + nsAutoCString new_gcov_prefix(gcov_child_prefix); + new_gcov_prefix.Append(std::to_string((size_t)child_pid)); + EnvironmentMap new_map = options.env_map; + new_map[ENVIRONMENT_LITERAL("GCOV_PREFIX")] = + ENVIRONMENT_STRING(new_gcov_prefix.get()); + // FIXME(bug 1783305): this won't work if full_env is set, and + // in general this block of code is doing things it shouldn't + // be (async signal unsafety). + env_storage = BuildEnvironmentArray(new_map); + } +#endif + + execve(argv_cstr[0], argv_cstr.get(), envp.get()); + // if we get here, we're in serious trouble and should complain loudly + // NOTE: This is async signal unsafe; it could deadlock instead. (But + // only on debug builds; otherwise it's a signal-safe no-op.) + DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0]; + _exit(127); + } + + // In the parent: + +#ifdef MOZ_CODE_COVERAGE + // Restore the handler for SIGUSR1 + signal(SIGUSR1, ccovSigHandler); +#endif + + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); + if (options.wait) HANDLE_EINTR(waitpid(pid, 0, 0)); + + if (process_handle) *process_handle = pid; + + return Ok(); +} + +} // namespace base diff --git a/ipc/chromium/src/base/process_util_mac.mm b/ipc/chromium/src/base/process_util_mac.mm new file mode 100644 index 0000000000..d3c91ee362 --- /dev/null +++ b/ipc/chromium/src/base/process_util_mac.mm @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process_util.h" + +#include <fcntl.h> +#include <os/availability.h> +#include <spawn.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <string> + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "mozilla/ipc/FileDescriptorShuffle.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Result.h" + +#include "mozilla/ipc/LaunchError.h" + +extern "C" { +// N.B. the syscalls are available back to 10.5, but the C wrappers +// only in 10.12. Fortunately, 10.15 is our current baseline. +int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12)); + +int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs, int disclaim) + API_AVAILABLE(macosx(10.14)); +} + +namespace { + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +Result<Ok, LaunchError> LaunchApp(const std::vector<std::string>& argv, + LaunchOptions&& options, + ProcessHandle* process_handle) { + Result<Ok, LaunchError> retval = Ok(); + + char* argv_copy[argv.size() + 1]; + for (size_t i = 0; i < argv.size(); i++) { + argv_copy[i] = const_cast<char*>(argv[i].c_str()); + } + argv_copy[argv.size()] = NULL; + + EnvironmentArray env_storage; + const EnvironmentArray& vars = + options.full_env ? options.full_env + : (env_storage = BuildEnvironmentArray(options.env_map)); + + posix_spawn_file_actions_t file_actions; + int err = posix_spawn_file_actions_init(&file_actions); + if (err != 0) { + DLOG(WARNING) << "posix_spawn_file_actions_init failed"; + return Err(LaunchError("posix_spawn_file_actions_init", err)); + } + auto file_actions_guard = mozilla::MakeScopeExit( + [&file_actions] { posix_spawn_file_actions_destroy(&file_actions); }); + + // Turn fds_to_remap array into a set of dup2 calls. + // + // Init() there will call fcntl(F_DUPFD/F_DUPFD_CLOEXEC) under the hood in + // https://searchfox.org/mozilla-central/rev/55d5c4b9dffe5e59eb6b019c1a930ec9ada47e10/ipc/glue/FileDescriptorShuffle.cpp#72 + // so it will set errno. + mozilla::ipc::FileDescriptorShuffle shuffle; + if (!shuffle.Init(options.fds_to_remap)) { + DLOG(WARNING) << "FileDescriptorShuffle::Init failed"; + return Err(LaunchError("FileDescriptorShuffle", errno)); + } + for (const auto& fd_map : shuffle.Dup2Sequence()) { + int src_fd = fd_map.first; + int dest_fd = fd_map.second; + + int rv = posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd); + if (rv != 0) { + DLOG(WARNING) << "posix_spawn_file_actions_adddup2 failed"; + return Err(LaunchError("posix_spawn_file_actions_adddup2", rv)); + } + } + + if (!options.workdir.empty()) { + int rv = posix_spawn_file_actions_addchdir_np(&file_actions, + options.workdir.c_str()); + if (rv != 0) { + DLOG(WARNING) << "posix_spawn_file_actions_addchdir_np failed"; + return Err(LaunchError("posix_spawn_file_actions_addchdir", rv)); + } + } + + // Initialize spawn attributes. + posix_spawnattr_t spawnattr; + err = posix_spawnattr_init(&spawnattr); + if (err != 0) { + DLOG(WARNING) << "posix_spawnattr_init failed"; + return Err(LaunchError("posix_spawnattr_init", err)); + } + auto spawnattr_guard = mozilla::MakeScopeExit( + [&spawnattr] { posix_spawnattr_destroy(&spawnattr); }); + +#if defined(XP_MACOSX) && defined(__aarch64__) + if (options.arch == PROCESS_ARCH_X86_64) { + cpu_type_t cpu_pref = CPU_TYPE_X86_64; + size_t count = 1; + size_t ocount = 0; + int rv = + posix_spawnattr_setbinpref_np(&spawnattr, count, &cpu_pref, &ocount); + if ((rv != 0) || (ocount != count)) { + DLOG(WARNING) << "posix_spawnattr_setbinpref_np failed"; + return Err(LaunchError("posix_spawnattr_setbinpref_np", rv)); + } + } +#endif + + if (options.disclaim) { + int err = responsibility_spawnattrs_setdisclaim(&spawnattr, 1); + if (err != 0) { + DLOG(WARNING) << "responsibility_spawnattrs_setdisclaim failed"; + return Err(LaunchError("responsibility_spawnattrs_setdisclaim", err)); + } + } + + // Prevent the child process from inheriting any file descriptors + // that aren't named in `file_actions`. (This is an Apple-specific + // extension to posix_spawn.) + err = posix_spawnattr_setflags(&spawnattr, POSIX_SPAWN_CLOEXEC_DEFAULT); + if (err != 0) { + DLOG(WARNING) << "posix_spawnattr_setflags failed"; + return Err(LaunchError("posix_spawnattr_setflags", err)); + } + + // Exempt std{in,out,err} from being closed by POSIX_SPAWN_CLOEXEC_DEFAULT. + for (int fd = 0; fd <= STDERR_FILENO; ++fd) { + err = posix_spawn_file_actions_addinherit_np(&file_actions, fd); + if (err != 0) { + DLOG(WARNING) << "posix_spawn_file_actions_addinherit_np failed"; + return Err(LaunchError("posix_spawn_file_actions_addinherit_np", err)); + } + } + + int pid = 0; + int spawn_succeeded = (posix_spawnp(&pid, argv_copy[0], &file_actions, + &spawnattr, argv_copy, vars.get()) == 0); + + bool process_handle_valid = pid > 0; + if (!spawn_succeeded || !process_handle_valid) { + DLOG(WARNING) << "posix_spawnp failed"; + retval = Err(LaunchError("posix_spawnp", spawn_succeeded)); + } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); + if (options.wait) HANDLE_EINTR(waitpid(pid, 0, 0)); + + if (process_handle) *process_handle = pid; + } + + return retval; +} + +} // namespace base diff --git a/ipc/chromium/src/base/process_util_posix.cc b/ipc/chromium/src/base/process_util_posix.cc new file mode 100644 index 0000000000..3229570ad8 --- /dev/null +++ b/ipc/chromium/src/base/process_util_posix.cc @@ -0,0 +1,388 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include <limits> +#include <set> + +#include "base/basictypes.h" +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/process_util.h" +#include "base/time.h" +#include "base/waitable_event.h" +#include "base/dir_reader_posix.h" + +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +// For PR_DuplicateEnvironment: +#include "prenv.h" +#include "prmem.h" + +#ifdef MOZ_ENABLE_FORKSERVER +# include "mozilla/ipc/ForkServiceChild.h" +#endif + +// We could configure-test for `waitid`, but it's been in POSIX for a +// long time and OpenBSD seems to be the only Unix we target that +// doesn't have it. Note that `waitid` is used to resolve a conflict +// with the crash reporter, which isn't available on OpenBSD. +#ifndef __OpenBSD__ +# define HAVE_WAITID +#endif + +namespace base { + +ProcessId GetCurrentProcId() { return getpid(); } + +ProcessHandle GetCurrentProcessHandle() { return GetCurrentProcId(); } + +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { + // On Posix platforms, process handles are the same as PIDs, so we + // don't need to do anything. + *handle = pid; + return true; +} + +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { + // On POSIX permissions are checked for each operation on process, + // not when opening a "handle". + return OpenProcessHandle(pid, handle); +} + +void CloseProcessHandle(ProcessHandle process) { + // See OpenProcessHandle, nothing to do. + return; +} + +ProcessId GetProcId(ProcessHandle process) { return process; } + +// Attempts to kill the process identified by the given process +// entry structure. Ignores specified exit_code; posix can't force that. +// Returns true if this is successful, false otherwise. +bool KillProcess(ProcessHandle process_id, int exit_code) { + // It's too easy to accidentally kill pid 0 (meaning the caller's + // process group) or pid -1 (all other processes killable by this + // user), and neither they nor other negative numbers (process + // groups) are legitimately used by this function's callers, so + // reject them all. + if (process_id <= 0) { + CHROMIUM_LOG(WARNING) << "base::KillProcess refusing to kill pid " + << process_id; + return false; + } + + bool result = kill(process_id, SIGTERM) == 0; + + if (!result && (errno == ESRCH)) { + result = true; + } + + if (!result) DLOG(ERROR) << "Unable to terminate process."; + + return result; +} + +#ifdef ANDROID +typedef unsigned long int rlim_t; +#endif + +// A class to handle auto-closing of DIR*'s. +class ScopedDIRClose { + public: + inline void operator()(DIR* x) const { + if (x) { + closedir(x); + } + } +}; +typedef mozilla::UniquePtr<DIR, ScopedDIRClose> ScopedDIR; + +void CloseSuperfluousFds(void* aCtx, bool (*aShouldPreserve)(void*, int)) { + // DANGER: no calls to malloc (or locks, etc.) are allowed from now on: + // https://crbug.com/36678 + // Also, beware of STL iterators: https://crbug.com/331459 +#if defined(ANDROID) + static const rlim_t kSystemDefaultMaxFds = 1024; + static const char kFDDir[] = "/proc/self/fd"; +#elif defined(XP_LINUX) || defined(XP_SOLARIS) + static const rlim_t kSystemDefaultMaxFds = 8192; + static const char kFDDir[] = "/proc/self/fd"; +#elif defined(XP_DARWIN) + static const rlim_t kSystemDefaultMaxFds = 256; + static const char kFDDir[] = "/dev/fd"; +#elif defined(__DragonFly__) || defined(XP_FREEBSD) || defined(XP_NETBSD) || \ + defined(XP_OPENBSD) + // the getrlimit below should never fail, so whatever .. + static const rlim_t kSystemDefaultMaxFds = 1024; + // at least /dev/fd will exist + static const char kFDDir[] = "/dev/fd"; +#endif + + // Get the maximum number of FDs possible. + struct rlimit nofile; + rlim_t max_fds; + if (getrlimit(RLIMIT_NOFILE, &nofile)) { + // getrlimit failed. Take a best guess. + max_fds = kSystemDefaultMaxFds; + DLOG(ERROR) << "getrlimit(RLIMIT_NOFILE) failed: " << errno; + } else { + max_fds = nofile.rlim_cur; + } + + if (max_fds > INT_MAX) max_fds = INT_MAX; + + DirReaderPosix fd_dir(kFDDir); + + if (!fd_dir.IsValid()) { + // Fallback case: Try every possible fd. + for (rlim_t i = 0; i < max_fds; ++i) { + const int fd = static_cast<int>(i); + if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO || + aShouldPreserve(aCtx, fd)) { + continue; + } + + // Since we're just trying to close anything we can find, + // ignore any error return values of close(). + close(fd); + } + return; + } + + const int dir_fd = fd_dir.fd(); + + for (; fd_dir.Next();) { + // Skip . and .. entries. + if (fd_dir.name()[0] == '.') continue; + + char* endptr; + errno = 0; + const long int fd = strtol(fd_dir.name(), &endptr, 10); + if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno) continue; + if (fd == dir_fd) continue; + if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO || + aShouldPreserve(aCtx, fd)) { + continue; + } + + // When running under Valgrind, Valgrind opens several FDs for its + // own use and will complain if we try to close them. All of + // these FDs are >= |max_fds|, so we can check against that here + // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758 + if (fd < static_cast<int>(max_fds)) { + int ret = IGNORE_EINTR(close(fd)); + if (ret != 0) { + DLOG(ERROR) << "Problem closing fd"; + } + } + } +} + +bool IsProcessDead(ProcessHandle handle, bool blocking) { + auto handleForkServer = [handle]() -> mozilla::Maybe<bool> { +#ifdef MOZ_ENABLE_FORKSERVER + if (errno == ECHILD && mozilla::ipc::ForkServiceChild::Get()) { + // We only know if a process exists, but not if it has crashed. + // + // Since content processes are not direct children of the chrome + // process any more, it is impossible to use |waitpid()| to wait for + // them. + const int r = kill(handle, 0); + // FIXME: for unexpected errors we should probably log a warning + // and return true, so that the caller doesn't loop / hang / + // try to kill the process. (Bug 1658072 will rewrite this code.) + return mozilla::Some(r < 0 && errno == ESRCH); + } +#else + mozilla::Unused << handle; +#endif + return mozilla::Nothing(); + }; + +#ifdef HAVE_WAITID + + // We use `WNOWAIT` to read the process status without + // side-effecting it, in case it's something unexpected like a + // ptrace-stop for the crash reporter. If is an exit, the call is + // reissued (see the end of this function) without that flag in + // order to collect the process. + siginfo_t si{}; + const int wflags = WEXITED | WNOWAIT | (blocking ? 0 : WNOHANG); + int result = HANDLE_EINTR(waitid(P_PID, handle, &si, wflags)); + if (result == -1) { + if (auto forkServerReturn = handleForkServer()) { + return *forkServerReturn; + } + + // This shouldn't happen, but sometimes it does. The error is + // probably ECHILD and the reason is probably that a pid was + // waited on again after a previous wait reclaimed its zombie. + // (It could also occur if the process isn't a direct child, but + // don't do that.) This is bad, because it risks interfering with + // an unrelated child process if the pid is reused. + // + // So, lacking reliable information, we indicate that the process + // is dead, in the hope that the caller will give up and stop + // calling us. See also bug 943174 and bug 933680. + CHROMIUM_LOG(ERROR) << "waitid failed pid:" << handle << " errno:" << errno; + return true; + } + + if (si.si_pid == 0) { + // the child hasn't exited yet. + return false; + } + + DCHECK(si.si_pid == handle); + switch (si.si_code) { + case CLD_STOPPED: + case CLD_CONTINUED: + DCHECK(false) << "waitid returned an event type that it shouldn't have"; + [[fallthrough]]; + case CLD_TRAPPED: + CHROMIUM_LOG(WARNING) << "ignoring non-exit event for process " << handle; + return false; + + case CLD_KILLED: + case CLD_DUMPED: + CHROMIUM_LOG(WARNING) + << "process " << handle << " exited on signal " << si.si_status; + break; + + case CLD_EXITED: + if (si.si_status != 0) { + CHROMIUM_LOG(WARNING) + << "process " << handle << " exited with status " << si.si_status; + } + break; + + default: + CHROMIUM_LOG(ERROR) << "unexpected waitid si_code value: " << si.si_code; + DCHECK(false); + // This shouldn't happen, but assume that the process exited to + // avoid the caller possibly ending up in a loop. + } + + // Now consume the status / collect the dead process + const int old_si_code = si.si_code; + si.si_pid = 0; + // In theory it shouldn't matter either way if we use `WNOHANG` at + // this point, but just in case, avoid unexpected blocking. + result = HANDLE_EINTR(waitid(P_PID, handle, &si, WEXITED | WNOHANG)); + DCHECK(result == 0); + DCHECK(si.si_pid == handle); + DCHECK(si.si_code == old_si_code); + return true; + +#else // no waitid + + int status; + const int result = waitpid(handle, &status, blocking ? 0 : WNOHANG); + if (result == -1) { + if (auto forkServerReturn = handleForkServer()) { + return *forkServerReturn; + } + + CHROMIUM_LOG(ERROR) << "waitpid failed pid:" << handle + << " errno:" << errno; + return true; + } + if (result == 0) { + return false; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { + CHROMIUM_LOG(WARNING) << "process " << handle << " exited with status " + << WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + CHROMIUM_LOG(WARNING) << "process " << handle << " exited on signal " + << WTERMSIG(status); + } + return true; + +#endif // waitid +} + +void FreeEnvVarsArray::operator()(char** array) { + for (char** varPtr = array; *varPtr != nullptr; ++varPtr) { + free(*varPtr); + } + delete[] array; +} + +EnvironmentArray BuildEnvironmentArray(const environment_map& env_vars_to_set) { + base::environment_map combined_env_vars = env_vars_to_set; + char** environ = PR_DuplicateEnvironment(); + for (char** varPtr = environ; *varPtr != nullptr; ++varPtr) { + std::string varString = *varPtr; + size_t equalPos = varString.find_first_of('='); + std::string varName = varString.substr(0, equalPos); + std::string varValue = varString.substr(equalPos + 1); + if (combined_env_vars.find(varName) == combined_env_vars.end()) { + combined_env_vars[varName] = varValue; + } + PR_Free(*varPtr); // PR_DuplicateEnvironment() uses PR_Malloc(). + } + PR_Free(environ); // PR_DuplicateEnvironment() uses PR_Malloc(). + + EnvironmentArray array(new char*[combined_env_vars.size() + 1]); + size_t i = 0; + for (const auto& key_val : combined_env_vars) { + std::string entry(key_val.first); + entry += "="; + entry += key_val.second; + array[i] = strdup(entry.c_str()); + i++; + } + array[i] = nullptr; + return array; +} + +} // namespace base + +namespace mozilla { + +EnvironmentLog::EnvironmentLog(const char* varname, size_t len) { + const char* e = getenv(varname); + if (e && *e) { + fname_ = e; + } +} + +void EnvironmentLog::print(const char* format, ...) { + if (!fname_.size()) return; + + FILE* f; + if (fname_.compare("-") == 0) { + f = fdopen(dup(STDOUT_FILENO), "a"); + } else { + f = fopen(fname_.c_str(), "a"); + } + + if (!f) return; + + va_list a; + va_start(a, format); + vfprintf(f, format, a); + va_end(a); + fclose(f); +} + +} // namespace mozilla diff --git a/ipc/chromium/src/base/process_util_win.cc b/ipc/chromium/src/base/process_util_win.cc new file mode 100644 index 0000000000..338917ef98 --- /dev/null +++ b/ipc/chromium/src/base/process_util_win.cc @@ -0,0 +1,392 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process_util.h" + +#include <windows.h> +#include <winternl.h> +#include <psapi.h> +#include <io.h> +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif + +#include "base/command_line.h" +#include "base/histogram.h" +#include "base/logging.h" +#include "base/win_util.h" + +#include "mozilla/ipc/LaunchError.h" +#include "mozilla/Result.h" + +#include <algorithm> +#include <stdio.h> +#include <stdlib.h> + +namespace { + +typedef BOOL(WINAPI* InitializeProcThreadAttributeListFn)( + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwAttributeCount, + DWORD dwFlags, PSIZE_T lpSize); + +typedef BOOL(WINAPI* DeleteProcThreadAttributeListFn)( + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList); + +typedef BOOL(WINAPI* UpdateProcThreadAttributeFn)( + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwFlags, + DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize, PVOID lpPreviousValue, + PSIZE_T lpReturnSize); + +static InitializeProcThreadAttributeListFn InitializeProcThreadAttributeListPtr; +static DeleteProcThreadAttributeListFn DeleteProcThreadAttributeListPtr; +static UpdateProcThreadAttributeFn UpdateProcThreadAttributePtr; + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +ProcessId GetCurrentProcId() { return ::GetCurrentProcessId(); } + +ProcessHandle GetCurrentProcessHandle() { return ::GetCurrentProcess(); } + +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { + // TODO(phajdan.jr): Take even more permissions out of this list. + ProcessHandle result = + OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE | + PROCESS_QUERY_INFORMATION | SYNCHRONIZE, + FALSE, pid); + + if (result == NULL) { + return false; + } + + *handle = result; + return true; +} + +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { + ProcessHandle result = + OpenProcess(PROCESS_DUP_HANDLE | PROCESS_TERMINATE | + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | SYNCHRONIZE, + FALSE, pid); + + if (result == NULL) { + return false; + } + + *handle = result; + return true; +} + +void CloseProcessHandle(ProcessHandle process) { + // closing a handle twice on Windows can be catastrophic - after the first + // close the handle value may be reused, so the second close will kill that + // other new handle. + BOOL ok = CloseHandle(process); + DCHECK(ok); +} + +ProcessId GetProcId(ProcessHandle process) { + if (process == base::kInvalidProcessHandle || process == nullptr) { + return 0; + } + // This returns 0 if we have insufficient rights to query the process handle. + // Invalid handles or non-process handles will cause a diagnostic assert. + ProcessId result = GetProcessId(process); +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED + CHECK(result != 0 || GetLastError() != ERROR_INVALID_HANDLE) + << "process handle = " << process; +#endif + return result; +} + +// from sandbox_policy_base.cc in a later version of the chromium ipc code... +bool IsInheritableHandle(HANDLE handle) { + if (!handle) return false; + if (handle == INVALID_HANDLE_VALUE) return false; + // File handles (FILE_TYPE_DISK) and pipe handles are known to be + // inheritable. Console handles (FILE_TYPE_CHAR) are not + // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST. + DWORD handle_type = GetFileType(handle); + return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE; +} + +void LoadThreadAttributeFunctions() { + HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); + InitializeProcThreadAttributeListPtr = + reinterpret_cast<InitializeProcThreadAttributeListFn>( + GetProcAddress(kernel32, "InitializeProcThreadAttributeList")); + DeleteProcThreadAttributeListPtr = + reinterpret_cast<DeleteProcThreadAttributeListFn>( + GetProcAddress(kernel32, "DeleteProcThreadAttributeList")); + UpdateProcThreadAttributePtr = reinterpret_cast<UpdateProcThreadAttributeFn>( + GetProcAddress(kernel32, "UpdateProcThreadAttribute")); +} + +// Creates and returns a "thread attribute list" to pass to the child process. +// On return, is a pointer to a THREAD_ATTRIBUTE_LIST or NULL if either the +// functions we need aren't available (eg, XP or earlier) or the functions we +// need failed. +// The result of this function must be passed to FreeThreadAttributeList. +// Note that the pointer to the HANDLE array ends up embedded in the result of +// this function and must stay alive until FreeThreadAttributeList is called, +// hence it is passed in so the owner is the caller of this function. +LPPROC_THREAD_ATTRIBUTE_LIST CreateThreadAttributeList(HANDLE* handlesToInherit, + int handleCount) { + if (!InitializeProcThreadAttributeListPtr || + !DeleteProcThreadAttributeListPtr || !UpdateProcThreadAttributePtr) + LoadThreadAttributeFunctions(); + // shouldn't happen as we are only called for Vista+, but better safe than + // sorry... + if (!InitializeProcThreadAttributeListPtr || + !DeleteProcThreadAttributeListPtr || !UpdateProcThreadAttributePtr) + return NULL; + + SIZE_T threadAttrSize; + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL; + + if (!(*InitializeProcThreadAttributeListPtr)(NULL, 1, 0, &threadAttrSize) && + GetLastError() != ERROR_INSUFFICIENT_BUFFER) + goto fail; + lpAttributeList = + reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST>(malloc(threadAttrSize)); + if (!lpAttributeList || !(*InitializeProcThreadAttributeListPtr)( + lpAttributeList, 1, 0, &threadAttrSize)) + goto fail; + + if (!(*UpdateProcThreadAttributePtr)( + lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + handlesToInherit, sizeof(handlesToInherit[0]) * handleCount, NULL, + NULL)) { + (*DeleteProcThreadAttributeListPtr)(lpAttributeList); + goto fail; + } + return lpAttributeList; + +fail: + if (lpAttributeList) free(lpAttributeList); + return NULL; +} + +// Frees the data returned by CreateThreadAttributeList. +void FreeThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList) { + // must be impossible to get a NULL DeleteProcThreadAttributeListPtr, as + // we already checked it existed when creating the data we are now freeing. + (*DeleteProcThreadAttributeListPtr)(lpAttributeList); + free(lpAttributeList); +} + +// The next two functions are from chromium/base/environment.cc +// +// Parses a null-terminated input string of an environment block. The key is +// placed into the given string, and the total length of the line, including +// the terminating null, is returned. +static size_t ParseEnvLine(const NativeEnvironmentString::value_type* input, + NativeEnvironmentString* key) { + // Skip to the equals or end of the string, this is the key. + size_t cur = 0; + while (input[cur] && input[cur] != '=') cur++; + *key = NativeEnvironmentString(&input[0], cur); + + // Now just skip to the end of the string. + while (input[cur]) cur++; + return cur + 1; +} + +std::wstring AlterEnvironment(const wchar_t* env, + const EnvironmentMap& changes) { + std::wstring result; + + // First copy all unmodified values to the output. + size_t cur_env = 0; + std::wstring key; + while (env[cur_env]) { + const wchar_t* line = &env[cur_env]; + size_t line_length = ParseEnvLine(line, &key); + + // Keep only values not specified in the change vector. + EnvironmentMap::const_iterator found_change = changes.find(key); + if (found_change == changes.end()) result.append(line, line_length); + + cur_env += line_length; + } + + // Now append all modified and new values. + for (EnvironmentMap::const_iterator i = changes.begin(); i != changes.end(); + ++i) { + if (!i->second.empty()) { + result.append(i->first); + result.push_back('='); + result.append(i->second); + result.push_back(0); + } + } + + // An additional null marks the end of the list. We always need a double-null + // in case nothing was added above. + if (result.empty()) result.push_back(0); + result.push_back(0); + return result; +} + +Result<Ok, LaunchError> LaunchApp(const std::wstring& cmdline, + const LaunchOptions& options, + ProcessHandle* process_handle) { + // We want to inherit the std handles so dump() statements and assertion + // messages in the child process can be seen - but we *do not* want to + // blindly have all handles inherited. Vista and later has a technique + // where only specified handles are inherited - so we use this technique. + // If that fails we just don't inherit anything. + DWORD dwCreationFlags = 0; + BOOL bInheritHandles = FALSE; + + // We use a STARTUPINFOEX, but if we can't do the thread attribute thing, we + // just pass the size of a STARTUPINFO. + STARTUPINFOEX startup_info_ex; + ZeroMemory(&startup_info_ex, sizeof(startup_info_ex)); + STARTUPINFO& startup_info = startup_info_ex.StartupInfo; + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEOFFFEEDBACK; + startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW; + + // Per the comment in CreateThreadAttributeList, lpAttributeList will contain + // a pointer to handlesToInherit, so make sure they have the same lifetime. + LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = NULL; + std::vector<HANDLE> handlesToInherit; + for (HANDLE h : options.handles_to_inherit) { + if (SetHandleInformation(h, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT) == + 0) { + MOZ_DIAGNOSTIC_ASSERT(false, "SetHandleInformation failed"); + return Err(LaunchError("SetHandleInformation", GetLastError())); + } + handlesToInherit.push_back(h); + } + + // setup our handle array first - if we end up with no handles that can + // be inherited we can avoid trying to do the ThreadAttributeList dance... + HANDLE stdOut = ::GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE stdErr = ::GetStdHandle(STD_ERROR_HANDLE); + + if (IsInheritableHandle(stdOut)) handlesToInherit.push_back(stdOut); + if (stdErr != stdOut && IsInheritableHandle(stdErr)) + handlesToInherit.push_back(stdErr); + + if (!handlesToInherit.empty()) { + lpAttributeList = CreateThreadAttributeList(handlesToInherit.data(), + handlesToInherit.size()); + if (lpAttributeList) { + // it's safe to inherit handles, so arrange for that... + startup_info.cb = sizeof(startup_info_ex); + startup_info.dwFlags |= STARTF_USESTDHANDLES; + startup_info.hStdOutput = stdOut; + startup_info.hStdError = stdErr; + startup_info.hStdInput = INVALID_HANDLE_VALUE; + startup_info_ex.lpAttributeList = lpAttributeList; + dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT; + bInheritHandles = TRUE; + } + } + + dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT; + if (options.start_independent) { + dwCreationFlags |= CREATE_BREAKAWAY_FROM_JOB; + } + + LPTCH original_environment = GetEnvironmentStrings(); + base::NativeEnvironmentString new_environment = + AlterEnvironment(original_environment, options.env_map); + // Ignore return value? What can we do? + FreeEnvironmentStrings(original_environment); + LPVOID new_env_ptr = (void*)new_environment.data(); + + PROCESS_INFORMATION process_info; + + BOOL createdOK = CreateProcess( + NULL, const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL, bInheritHandles, + dwCreationFlags, new_env_ptr, NULL, &startup_info, &process_info); + if (lpAttributeList) FreeThreadAttributeList(lpAttributeList); + if (!createdOK) { + DLOG(WARNING) << "CreateProcess Failed: " << GetLastError(); + return Err(LaunchError("CreateProcess", GetLastError())); + } + + gProcessLog.print("==> process %d launched child process %d (%S)\n", + GetCurrentProcId(), process_info.dwProcessId, + cmdline.c_str()); + + // Handles must be closed or they will leak + CloseHandle(process_info.hThread); + + if (options.wait) WaitForSingleObject(process_info.hProcess, INFINITE); + + // If the caller wants the process handle, we won't close it. + if (process_handle) { + *process_handle = process_info.hProcess; + } else { + CloseHandle(process_info.hProcess); + } + return Ok(); +} + +Result<Ok, LaunchError> LaunchApp(const CommandLine& cl, + const LaunchOptions& options, + ProcessHandle* process_handle) { + return LaunchApp(cl.command_line_string(), options, process_handle); +} + +bool KillProcess(ProcessHandle process, int exit_code) { + // INVALID_HANDLE_VALUE is not actually an invalid handle value, but + // instead is the value returned by GetCurrentProcess(). nullptr, + // in contrast, *is* an invalid handle value. Both values are too + // easy to accidentally try to kill, and neither is legitimately + // used by this function's callers, so reject them. + if (!process || process == INVALID_HANDLE_VALUE) { + CHROMIUM_LOG(WARNING) + << "base::KillProcess refusing to terminate process handle " << process; + return false; + } + bool result = (TerminateProcess(process, exit_code) != FALSE); + if (!result) { + DLOG(ERROR) << "Unable to terminate process: " << GetLastError(); + } + return result; +} + +} // namespace base + +namespace mozilla { + +EnvironmentLog::EnvironmentLog(const char* varname, size_t len) { + wchar_t wvarname[len]; + std::copy(varname, varname + len, wvarname); + const wchar_t* e = _wgetenv(wvarname); + if (e && *e) { + fname_ = e; + } +} + +void EnvironmentLog::print(const char* format, ...) { + if (!fname_.size()) return; + + FILE* f; + if (fname_.compare(L"-") == 0) { + f = fdopen(dup(STDOUT_FILENO), "a"); + } else { + f = _wfopen(fname_.c_str(), L"a"); + } + + if (!f) return; + + va_list a; + va_start(a, format); + vfprintf(f, format, a); + va_end(a); + fclose(f); +} + +} // namespace mozilla diff --git a/ipc/chromium/src/base/rand_util.cc b/ipc/chromium/src/base/rand_util.cc new file mode 100644 index 0000000000..bf14baa5d4 --- /dev/null +++ b/ipc/chromium/src/base/rand_util.cc @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/rand_util.h" + +#include <math.h> + +#include <limits> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "mozilla/RandomNum.h" + +namespace base { + +int RandInt(int min, int max) { + DCHECK(min <= max); + + uint64_t range = static_cast<int64_t>(max) - min + 1; + mozilla::Maybe<uint64_t> number = mozilla::RandomUint64(); + MOZ_RELEASE_ASSERT(number.isSome()); + int result = min + static_cast<int>(number.value() % range); + DCHECK(result >= min && result <= max); + return result; +} + +} // namespace base diff --git a/ipc/chromium/src/base/rand_util.h b/ipc/chromium/src/base/rand_util.h new file mode 100644 index 0000000000..963fc69599 --- /dev/null +++ b/ipc/chromium/src/base/rand_util.h @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_RAND_UTIL_H_ +#define BASE_RAND_UTIL_H_ + +#include "base/basictypes.h" + +namespace base { + +// Returns a random number between min and max (inclusive). Thread-safe. +int RandInt(int min, int max); + +} // namespace base + +#endif // BASE_RAND_UTIL_H_ diff --git a/ipc/chromium/src/base/revocable_store.cc b/ipc/chromium/src/base/revocable_store.cc new file mode 100644 index 0000000000..b3d7414ee2 --- /dev/null +++ b/ipc/chromium/src/base/revocable_store.cc @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/revocable_store.h" + +#include "base/logging.h" + +RevocableStore::Revocable::Revocable(RevocableStore* store) + : store_reference_(store->owning_reference_) { + // We AddRef() the owning reference. + DCHECK(store_reference_->store()); + store_reference_->store()->Add(this); +} + +RevocableStore::RevocableStore() { + // Create a new owning reference. + owning_reference_ = new StoreRef(this); +} + +RevocableStore::~RevocableStore() { + // Revoke all the items in the store. + owning_reference_->set_store(NULL); +} + +void RevocableStore::Add(Revocable* item) { + DCHECK(!item->revoked()); +} + +void RevocableStore::RevokeAll() { + // We revoke all the existing items in the store and reset our count. + owning_reference_->set_store(NULL); + + // Then we create a new owning reference for new items that get added. + // This Release()s the old owning reference, allowing it to be freed after + // all the items that were in the store are eventually destroyed. + owning_reference_ = new StoreRef(this); +} diff --git a/ipc/chromium/src/base/revocable_store.h b/ipc/chromium/src/base/revocable_store.h new file mode 100644 index 0000000000..789c011cde --- /dev/null +++ b/ipc/chromium/src/base/revocable_store.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_REVOCABLE_STORE_H_ +#define BASE_REVOCABLE_STORE_H_ + +#include "base/basictypes.h" +#include "nsISupportsImpl.h" + +// |RevocableStore| is a container of items that can be removed from the store. +class RevocableStore { + public: + // A |StoreRef| is used to link the |RevocableStore| to its items. There is + // one StoreRef per store, and each item holds a reference to it. If the + // store wishes to revoke its items, it sets |store_| to null. Items are + // permitted to release their reference to the |StoreRef| when they no longer + // require the store. + class StoreRef final { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StoreRef) + explicit StoreRef(RevocableStore* aStore) : store_(aStore) {} + + void set_store(RevocableStore* aStore) { store_ = aStore; } + RevocableStore* store() const { return store_; } + + protected: + ~StoreRef() {} + + private: + RevocableStore* store_; + + DISALLOW_EVIL_CONSTRUCTORS(StoreRef); + }; + + // An item in the store. On construction, the object adds itself to the + // store. + class Revocable { + public: + explicit Revocable(RevocableStore* store); + ~Revocable() = default; + + // This item has been revoked if it no longer has a pointer to the store. + bool revoked() const { return !store_reference_->store(); } + + private: + // We hold a reference to the store through this ref pointer. We release + // this reference on destruction. + RefPtr<StoreRef> store_reference_; + + DISALLOW_EVIL_CONSTRUCTORS(Revocable); + }; + + RevocableStore(); + ~RevocableStore(); + + // Revokes all the items in the store. + void RevokeAll(); + + private: + friend class Revocable; + + // Adds an item to the store. To add an item to the store, construct it + // with a pointer to the store. + void Add(Revocable* item); + + // This is the reference the unrevoked items in the store hold. + RefPtr<StoreRef> owning_reference_; + + DISALLOW_EVIL_CONSTRUCTORS(RevocableStore); +}; + +#endif // BASE_REVOCABLE_STORE_H_ diff --git a/ipc/chromium/src/base/scoped_bstr_win.h b/ipc/chromium/src/base/scoped_bstr_win.h new file mode 100644 index 0000000000..4e7e133421 --- /dev/null +++ b/ipc/chromium/src/base/scoped_bstr_win.h @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_BSTR_WIN_H_ +#define BASE_SCOPED_BSTR_WIN_H_ + +#include "base/basictypes.h" // needed to pick up XP_WIN + +#include "base/logging.h" + +#include <windows.h> +#include <oleauto.h> + +// Manages a BSTR string pointer. +// The class interface is based on scoped_ptr. +class ScopedBstr { + public: + ScopedBstr() : bstr_(NULL) {} + + // Constructor to create a new BSTR. + // NOTE: Do not pass a BSTR to this constructor expecting ownership to + // be transferred - even though it compiles! ;-) + explicit ScopedBstr(const wchar_t* non_bstr); + ~ScopedBstr(); + + // Give ScopedBstr ownership over an already allocated BSTR or NULL. + // If you need to allocate a new BSTR instance, use |allocate| instead. + void Reset(BSTR bstr = NULL); + + // Releases ownership of the BSTR to the caller. + BSTR Release(); + + // Creates a new BSTR from a wide string. + // If you already have a BSTR and want to transfer ownership to the + // ScopedBstr instance, call |reset| instead. + // Returns a pointer to the new BSTR, or NULL if allocation failed. + BSTR Allocate(const wchar_t* wide_str); + + // Allocates a new BSTR with the specified number of bytes. + // Returns a pointer to the new BSTR, or NULL if allocation failed. + BSTR AllocateBytes(int bytes); + + // Sets the allocated length field of the already-allocated BSTR to be + // |bytes|. This is useful when the BSTR was preallocated with e.g. + // SysAllocStringLen or SysAllocStringByteLen (call |AllocateBytes|) and + // then not all the bytes are being used. + // Note that if you want to set the length to a specific number of characters, + // you need to multiply by sizeof(wchar_t). Oddly, there's no public API to + // set the length, so we do this ourselves by hand. + // + // NOTE: The actual allocated size of the BSTR MUST be >= bytes. + // That responsibility is with the caller. + void SetByteLen(uint32_t bytes); + + // Swap values of two ScopedBstr's. + void Swap(ScopedBstr& bstr2); + + // Retrieves the pointer address. + // Used to receive BSTRs as out arguments (and take ownership). + // The function DCHECKs on the current value being NULL. + // Usage: GetBstr(bstr.Receive()); + BSTR* Receive(); + + // Returns number of chars in the BSTR. + uint32_t Length() const; + + // Returns the number of bytes allocated for the BSTR. + uint32_t ByteLength() const; + + operator BSTR() const { return bstr_; } + + protected: + BSTR bstr_; + + private: + // Forbid comparison of ScopedBstr types. You should never have the same + // BSTR owned by two different scoped_ptrs. + bool operator==(const ScopedBstr& bstr2) const; + bool operator!=(const ScopedBstr& bstr2) const; + DISALLOW_COPY_AND_ASSIGN(ScopedBstr); +}; + +// Template class to generate a BSTR from a static wide string +// without touching the heap. Use this class via the StackBstrVar and +// StackBstr macros. +template <uint32_t string_bytes> +class StackBstrT { + public: + // Try to stay as const as we can in an attempt to avoid someone + // using the class incorrectly (e.g. by supplying a variable instead + // of a verbatim string. We also have an assert in the constructor + // as an extra runtime check since the const-ness only catches one case. + explicit StackBstrT(const wchar_t* const str) { + // The BSTR API uses UINT, but we prefer uint32_t. + // Make sure we'll know about it if these types don't match. + COMPILE_ASSERT(sizeof(uint32_t) == sizeof(UINT), UintToUint32); + COMPILE_ASSERT(sizeof(wchar_t) == sizeof(OLECHAR), WcharToOlechar); + + // You shouldn't pass string pointers to this constructor since + // there's no way for the compiler to calculate the length of the + // string (string_bytes will be equal to pointer size in those cases). + DCHECK(lstrlenW(str) == (string_bytes / sizeof(bstr_.str_[0])) - 1) + << "not expecting a string pointer"; + memcpy(bstr_.str_, str, string_bytes); + bstr_.len_ = string_bytes - sizeof(wchar_t); + } + + operator BSTR() { return bstr_.str_; } + + protected: + struct BstrInternal { + uint32_t len_; + wchar_t str_[string_bytes / sizeof(wchar_t)]; + } bstr_; +}; + +// Use this macro to generate an inline BSTR from a wide string. +// This is about 6 times faster than using the SysAllocXxx functions to +// allocate a BSTR and helps with keeping heap fragmentation down. +// Example: +// DoBstrStuff(StackBstr(L"This is my BSTR")); +// Where DoBstrStuff is: +// HRESULT DoBstrStuff(BSTR bstr) { ... } +#define StackBstr(str) static_cast<BSTR>(StackBstrT<sizeof(str)>(str)) + +// If you need a named BSTR variable that's based on a fixed string +// (e.g. if the BSTR is used inside a loop or more than one place), +// use StackBstrVar to declare a variable. +// Example: +// StackBstrVar(L"my_property", myprop); +// for (int i = 0; i < objects.length(); ++i) +// ProcessValue(objects[i].GetProp(myprop)); // GetProp accepts BSTR +#define StackBstrVar(str, var) StackBstrT<sizeof(str)> var(str) + +#endif // BASE_SCOPED_BSTR_WIN_H_ diff --git a/ipc/chromium/src/base/scoped_cftyperef.h b/ipc/chromium/src/base/scoped_cftyperef.h new file mode 100644 index 0000000000..9cd3cfa967 --- /dev/null +++ b/ipc/chromium/src/base/scoped_cftyperef.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_CFTYPEREF_H_ +#define BASE_SCOPED_CFTYPEREF_H_ + +#include <CoreFoundation/CoreFoundation.h> +#include "base/basictypes.h" + +// scoped_cftyperef<> is patterned after scoped_ptr<>, but maintains ownership +// of a CoreFoundation object: any object that can be represented as a +// CFTypeRef. Style deviations here are solely for compatibility with +// scoped_ptr<>'s interface, with which everyone is already familiar. +// +// When scoped_cftyperef<> takes ownership of an object (in the constructor or +// in reset()), it takes over the caller's existing ownership claim. The +// caller must own the object it gives to scoped_cftyperef<>, and relinquishes +// an ownership claim to that object. scoped_cftyperef<> does not call +// CFRetain(). +template <typename CFT> +class scoped_cftyperef { + public: + typedef CFT element_type; + + explicit scoped_cftyperef(CFT object = NULL) : object_(object) {} + + ~scoped_cftyperef() { + if (object_) CFRelease(object_); + } + + void reset(CFT object = NULL) { + if (object_) CFRelease(object_); + object_ = object; + } + + bool operator==(CFT that) const { return object_ == that; } + + bool operator!=(CFT that) const { return object_ != that; } + + operator CFT() const { return object_; } + + CFT get() const { return object_; } + + void swap(scoped_cftyperef& that) { + CFT temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + // scoped_cftyperef<>::release() is like scoped_ptr<>::release. It is NOT + // a wrapper for CFRelease(). To force a scoped_cftyperef<> object to call + // CFRelease(), use scoped_cftyperef<>::reset(). + CFT release() { + CFT temp = object_; + object_ = NULL; + return temp; + } + + private: + CFT object_; + + DISALLOW_COPY_AND_ASSIGN(scoped_cftyperef); +}; + +#endif // BASE_SCOPED_CFTYPEREF_H_ diff --git a/ipc/chromium/src/base/scoped_handle.h b/ipc/chromium/src/base/scoped_handle.h new file mode 100644 index 0000000000..97bb7f1240 --- /dev/null +++ b/ipc/chromium/src/base/scoped_handle.h @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_HANDLE_H_ +#define BASE_SCOPED_HANDLE_H_ + +#include <stdio.h> + +#include "base/basictypes.h" + +#if defined(XP_WIN) +# include "base/scoped_handle_win.h" +#endif + +class ScopedStdioHandle { + public: + ScopedStdioHandle() : handle_(NULL) {} + + explicit ScopedStdioHandle(FILE* handle) : handle_(handle) {} + + ~ScopedStdioHandle() { Close(); } + + void Close() { + if (handle_) { + fclose(handle_); + handle_ = NULL; + } + } + + FILE* get() const { return handle_; } + + FILE* Take() { + FILE* temp = handle_; + handle_ = NULL; + return temp; + } + + void Set(FILE* newhandle) { + Close(); + handle_ = newhandle; + } + + private: + FILE* handle_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedStdioHandle); +}; + +#endif // BASE_SCOPED_HANDLE_H_ diff --git a/ipc/chromium/src/base/scoped_handle_win.h b/ipc/chromium/src/base/scoped_handle_win.h new file mode 100644 index 0000000000..e8e419b7c5 --- /dev/null +++ b/ipc/chromium/src/base/scoped_handle_win.h @@ -0,0 +1,193 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_HANDLE_WIN_H_ +#define BASE_SCOPED_HANDLE_WIN_H_ + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/logging.h" + +// Used so we always remember to close the handle. +// The class interface matches that of ScopedStdioHandle in addition to an +// IsValid() method since invalid handles on windows can be either NULL or +// INVALID_HANDLE_VALUE (-1). +// +// Example: +// ScopedHandle hfile(CreateFile(...)); +// if (!hfile.Get()) +// ...process error +// ReadFile(hfile.Get(), ...); +// +// To sqirrel the handle away somewhere else: +// secret_handle_ = hfile.Take(); +// +// To explicitly close the handle: +// hfile.Close(); +class ScopedHandle { + public: + ScopedHandle() : handle_(NULL) {} + + explicit ScopedHandle(HANDLE h) : handle_(NULL) { Set(h); } + + ~ScopedHandle() { Close(); } + + // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL + // usage for errors. + bool IsValid() const { return handle_ != NULL; } + + void Set(HANDLE new_handle) { + Close(); + + // Windows is inconsistent about invalid handles, so we always use NULL + if (new_handle != INVALID_HANDLE_VALUE) handle_ = new_handle; + } + + HANDLE Get() { return handle_; } + + operator HANDLE() { return handle_; } + + HANDLE Take() { + // transfers ownership away from this object + HANDLE h = handle_; + handle_ = NULL; + return h; + } + + void Close() { + if (handle_) { + if (!::CloseHandle(handle_)) { + NOTREACHED(); + } + handle_ = NULL; + } + } + + private: + HANDLE handle_; + DISALLOW_EVIL_CONSTRUCTORS(ScopedHandle); +}; + +// Like ScopedHandle, but for HANDLEs returned from FindFile(). +class ScopedFindFileHandle { + public: + explicit ScopedFindFileHandle(HANDLE handle) : handle_(handle) { + // Windows is inconsistent about invalid handles, so we always use NULL + if (handle_ == INVALID_HANDLE_VALUE) handle_ = NULL; + } + + ~ScopedFindFileHandle() { + if (handle_) FindClose(handle_); + } + + // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL + // usage for errors. + bool IsValid() const { return handle_ != NULL; } + + operator HANDLE() { return handle_; } + + private: + HANDLE handle_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedFindFileHandle); +}; + +// Like ScopedHandle but for HDC. Only use this on HDCs returned from +// CreateCompatibleDC. For an HDC returned by GetDC, use ReleaseDC instead. +class ScopedHDC { + public: + ScopedHDC() : hdc_(NULL) {} + explicit ScopedHDC(HDC h) : hdc_(h) {} + + ~ScopedHDC() { Close(); } + + HDC Get() { return hdc_; } + + void Set(HDC h) { + Close(); + hdc_ = h; + } + + operator HDC() { return hdc_; } + + private: + void Close() { +#ifdef NOGDI + assert(false); +#else + if (hdc_) DeleteDC(hdc_); +#endif // NOGDI + } + + HDC hdc_; + DISALLOW_EVIL_CONSTRUCTORS(ScopedHDC); +}; + +// Like ScopedHandle but for GDI objects. +template <class T> +class ScopedGDIObject { + public: + ScopedGDIObject() : object_(NULL) {} + explicit ScopedGDIObject(T object) : object_(object) {} + + ~ScopedGDIObject() { Close(); } + + T Get() { return object_; } + + void Set(T object) { + if (object_ && object != object_) Close(); + object_ = object; + } + + ScopedGDIObject& operator=(T object) { + Set(object); + return *this; + } + + operator T() { return object_; } + + private: + void Close() { + if (object_) DeleteObject(object_); + } + + T object_; + DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject); +}; + +// Typedefs for some common use cases. +typedef ScopedGDIObject<HBITMAP> ScopedBitmap; +typedef ScopedGDIObject<HRGN> ScopedHRGN; +typedef ScopedGDIObject<HFONT> ScopedHFONT; + +// Like ScopedHandle except for HGLOBAL. +template <class T> +class ScopedHGlobal { + public: + explicit ScopedHGlobal(HGLOBAL glob) : glob_(glob) { + data_ = static_cast<T*>(GlobalLock(glob_)); + } + ~ScopedHGlobal() { GlobalUnlock(glob_); } + + T* get() { return data_; } + + size_t Size() const { return GlobalSize(glob_); } + + T* operator->() const { + assert(data_ != 0); + return data_; + } + + private: + HGLOBAL glob_; + + T* data_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedHGlobal); +}; + +#endif // BASE_SCOPED_HANDLE_WIN_H_ diff --git a/ipc/chromium/src/base/scoped_nsautorelease_pool.h b/ipc/chromium/src/base/scoped_nsautorelease_pool.h new file mode 100644 index 0000000000..37d173b028 --- /dev/null +++ b/ipc/chromium/src/base/scoped_nsautorelease_pool.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_NSAUTORELEASE_POOL_H_ +#define BASE_SCOPED_NSAUTORELEASE_POOL_H_ + +#include "base/basictypes.h" + +#if defined(XP_DARWIN) +# if defined(__OBJC__) +@class NSAutoreleasePool; +# else // __OBJC__ +class NSAutoreleasePool; +# endif // __OBJC__ +#endif // XP_DARWIN + +namespace base { + +// On the Mac, ScopedNSAutoreleasePool allocates an NSAutoreleasePool when +// instantiated and sends it a -drain message when destroyed. This allows an +// autorelease pool to be maintained in ordinary C++ code without bringing in +// any direct Objective-C dependency. +// +// On other platforms, ScopedNSAutoreleasePool is an empty object with no +// effects. This allows it to be used directly in cross-platform code without +// ugly #ifdefs. +class ScopedNSAutoreleasePool { + public: +#if !defined(XP_DARWIN) + ScopedNSAutoreleasePool() {} + void Recycle() {} +#else // XP_DARWIN + ScopedNSAutoreleasePool(); + ~ScopedNSAutoreleasePool(); + + // Clear out the pool in case its position on the stack causes it to be + // alive for long periods of time (such as the entire length of the app). + // Only use then when you're certain the items currently in the pool are + // no longer needed. + void Recycle(); + + private: + NSAutoreleasePool* autorelease_pool_; +#endif // XP_DARWIN + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedNSAutoreleasePool); +}; + +} // namespace base + +#endif // BASE_SCOPED_NSAUTORELEASE_POOL_H_ diff --git a/ipc/chromium/src/base/scoped_nsautorelease_pool.mm b/ipc/chromium/src/base/scoped_nsautorelease_pool.mm new file mode 100644 index 0000000000..174fb821d6 --- /dev/null +++ b/ipc/chromium/src/base/scoped_nsautorelease_pool.mm @@ -0,0 +1,30 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_nsautorelease_pool.h" + +#import <Foundation/Foundation.h> + +#include "base/logging.h" + +namespace base { + +ScopedNSAutoreleasePool::ScopedNSAutoreleasePool() + : autorelease_pool_([[NSAutoreleasePool alloc] init]) { + DCHECK(autorelease_pool_); +} + +ScopedNSAutoreleasePool::~ScopedNSAutoreleasePool() { + [autorelease_pool_ drain]; +} + +// Cycle the internal pool, allowing everything there to get cleaned up and +// start anew. +void ScopedNSAutoreleasePool::Recycle() { + [autorelease_pool_ drain]; + autorelease_pool_ = [[NSAutoreleasePool alloc] init]; + DCHECK(autorelease_pool_); +} + +} // namespace base diff --git a/ipc/chromium/src/base/scoped_nsobject.h b/ipc/chromium/src/base/scoped_nsobject.h new file mode 100644 index 0000000000..d1ef77d749 --- /dev/null +++ b/ipc/chromium/src/base/scoped_nsobject.h @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_NSOBJECT_H_ +#define BASE_SCOPED_NSOBJECT_H_ + +#import <Foundation/Foundation.h> +#include "base/basictypes.h" + +// scoped_nsobject<> is patterned after scoped_ptr<>, but maintains ownership +// of an NSObject subclass object. Style deviations here are solely for +// compatibility with scoped_ptr<>'s interface, with which everyone is already +// familiar. +// +// When scoped_nsobject<> takes ownership of an object (in the constructor or +// in reset()), it takes over the caller's existing ownership claim. The +// caller must own the object it gives to scoped_nsobject<>, and relinquishes +// an ownership claim to that object. scoped_nsobject<> does not call +// -retain. +template <typename NST> +class scoped_nsobject { + public: + typedef NST* element_type; + + explicit scoped_nsobject(NST* object = nil) : object_(object) {} + + ~scoped_nsobject() { [object_ release]; } + + void reset(NST* object = nil) { + [object_ release]; + object_ = object; + } + + bool operator==(NST* that) const { return object_ == that; } + + bool operator!=(NST* that) const { return object_ != that; } + + operator NST*() const { return object_; } + + NST* get() const { return object_; } + + void swap(scoped_nsobject& that) { + NST* temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + // scoped_nsobject<>::release() is like scoped_ptr<>::release. It is NOT + // a wrapper for [object_ release]. To force a scoped_nsobject<> object to + // call [object_ release], use scoped_nsobject<>::reset(). + NST* release() { + NST* temp = object_; + object_ = nil; + return temp; + } + + private: + NST* object_; + + DISALLOW_COPY_AND_ASSIGN(scoped_nsobject); +}; + +#endif // BASE_SCOPED_NSOBJECT_H_ diff --git a/ipc/chromium/src/base/set_process_title_linux.cc b/ipc/chromium/src/base/set_process_title_linux.cc new file mode 100644 index 0000000000..fed166a524 --- /dev/null +++ b/ipc/chromium/src/base/set_process_title_linux.cc @@ -0,0 +1,206 @@ +// Copyright 2009 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file implements BSD-style setproctitle() for Linux. +// It is written such that it can easily be compiled outside Chromium. +// +// (This copy has been modified for use in the Mozilla codebase.) +// +// The Linux kernel sets up two locations in memory to pass arguments and +// environment variables to processes. First, there are two char* arrays stored +// one after another: argv and environ. A pointer to argv is passed to main(), +// while glibc sets the global variable |environ| to point at the latter. Both +// of these arrays are terminated by a null pointer; the environment array is +// also followed by some empty space to allow additional variables to be added. +// +// These arrays contain pointers to a second location in memory, where the +// strings themselves are stored one after another: first all the arguments, +// then the environment variables. +// +// When the kernel reads the command line arguments for a process, it looks at +// the range of memory that it initially used for the argument list. If the +// terminating '\0' character is still where it expects, nothing further is +// done. If it has been overwritten, the kernel will scan up to the size of +// a page looking for another. +// +// Thus to change the process title, we must move any arguments and environment +// variables out of the way to make room for a potentially longer title, and +// then overwrite the memory pointed to by argv[0] with a single replacement +// string, making sure its size does not exceed the available space. +// +// See the following kernel commit for the details of the contract between +// kernel and setproctitle: +// https://github.com/torvalds/linux/commit/2954152298c37804dab49d630aa959625b50cf64 +// +// It is perhaps worth noting that patches to add a system call to Linux for +// this, like in BSD, have never made it in: this is the "official" way to do +// this on Linux. Presumably it is not in glibc due to some disagreement over +// this position within the glibc project, leaving applications caught in the +// middle. (Also, only a very few applications need or want this anyway.) + +#include "base/set_process_title_linux.h" + +#include "mozilla/UniquePtrExtensions.h" + +#include <fcntl.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <string> +#include <vector> + +extern char** environ; + +// g_orig_argv0 is the original process name found in argv[0]. +// It is set to a copy of argv[0] in setproctitle_init. It is nullptr if +// setproctitle_init was unsuccessful or not called. +static const char* g_orig_argv0 = nullptr; + +// Following pointers hold the initial argv/envp memory range. +// They are initialized in setproctitle_init and are used to overwrite the +// argv/envp memory range with a new process title to be read by the kernel. +// They are nullptr if setproctitle_init was unsuccessful or not called. +// Note that g_envp_start is not necessary because it is the same as g_argv_end. +static char* g_argv_start = nullptr; +static char* g_argv_end = nullptr; +static char* g_envp_end = nullptr; + +void setproctitle(const char* fmt, ...) { + va_list ap; + + // Sanity check before we try and set the process title. + // The BSD version allows a null fmt to restore the original title. + if (!g_orig_argv0 || !fmt) { + return; + } + + // The title can be up to the end of envp. + const size_t avail_size = g_envp_end - g_argv_start - 1; + + // Linux 4.18--5.2 have a bug where we can never set a process title + // shorter than the initial argv. Check if the bug exists in the current + // kernel on the first call of setproctitle. + static const bool buggy_kernel = [avail_size]() { + // Attempt to set an empty title. This will set cmdline to: + // "" (on Linux --4.17) + // "\0\0\0...\0\0\0.\0" (on Linux 4.18--5.2) + // "\0" (on Linux 5.3--) + memset(g_argv_start, 0, avail_size + 1); + g_argv_end[-1] = '.'; + + mozilla::UniqueFileHandle fd( + open("/proc/self/cmdline", O_RDONLY | O_CLOEXEC)); + if (!fd) { + return false; + } + + // We just want to see if there are at least 2 bytes in the file; + // we don't need to read the whole contents. Short reads probably + // aren't possible given how this procfs node is implemented, but + // it's not much more code to handle it anyway. + char buf[2]; + ssize_t total_read = 0; + while (total_read < 2) { + ssize_t rd = read(fd.get(), buf, 2); + if (rd <= 0) { + return false; + } + total_read += rd; + } + return true; + }(); + + memset(g_argv_start, 0, avail_size + 1); + + size_t size; + va_start(ap, fmt); + if (fmt[0] == '-') { + size = vsnprintf(g_argv_start, avail_size, &fmt[1], ap); + } else { + size = snprintf(g_argv_start, avail_size, "%s ", g_orig_argv0); + if (size < avail_size) { + size += vsnprintf(&g_argv_start[size], avail_size - size, fmt, ap); + } + } + va_end(ap); + + // Kernel looks for a null terminator instead of the initial argv space + // when the end of the space is not terminated with a null. + // https://github.com/torvalds/linux/commit/d26d0cd97c88eb1a5704b42e41ab443406807810 + // + // If the length of the new title is shorter than the original argv space, + // set the last byte of the space to an arbitrary non-null character to tell + // the kernel that setproctitle was called. + // + // On buggy kernels we can never make the process title shorter than the + // initial argv. In that case, just leave the remaining bytes filled with + // null characters. + const size_t argv_size = g_argv_end - g_argv_start - 1; + if (!buggy_kernel && size < argv_size) { + g_argv_end[-1] = '.'; + } +} + +// A version of this built into glibc would not need this function, since +// it could stash the argv pointer in __libc_start_main(). But we need it. +void setproctitle_init(char** main_argv) { + static bool init_called = false; + if (init_called) { + return; + } + init_called = true; + + if (!main_argv) { + return; + } + + // Verify that the memory layout matches expectation. + char** const argv = main_argv; + char* argv_start = argv[0]; + char* p = argv_start; + for (size_t i = 0; argv[i]; ++i) { + if (p != argv[i]) { + return; + } + p += strlen(p) + 1; + } + char* argv_end = p; + size_t environ_size = 0; + for (size_t i = 0; environ[i]; ++i, ++environ_size) { + if (p != environ[i]) { + return; + } + p += strlen(p) + 1; + } + char* envp_end = p; + + // Copy the arg and env strings into the heap. Leak Sanitizer + // doesn't seem to object to these strdup()s; if it ever does, we + // can always ensure the pointers are reachable from globals or add + // a suppresion for this function. + // + // Note that Chromium's version of this code didn't copy the + // arguments; this is probably because they access args via the + // CommandLine class, which copies into a std::vector<std::string>, + // but in general that's not a safe assumption for Gecko. + for (size_t i = 0; argv[i]; ++i) { + argv[i] = strdup(argv[i]); + } + for (size_t i = 0; environ[i]; ++i) { + environ[i] = strdup(environ[i]); + } + + if (!argv[0]) { + return; + } + + g_orig_argv0 = argv[0]; + g_argv_start = argv_start; + g_argv_end = argv_end; + g_envp_end = envp_end; +} diff --git a/ipc/chromium/src/base/set_process_title_linux.h b/ipc/chromium/src/base/set_process_title_linux.h new file mode 100644 index 0000000000..e950ce0619 --- /dev/null +++ b/ipc/chromium/src/base/set_process_title_linux.h @@ -0,0 +1,12 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BASE_PROCESS_TITLE_LINUX_H_ +#define BASE_PROCESS_TITLE_LINUX_H_ + +void setproctitle(const char* fmt, ...); +void setproctitle_init(char** main_argv); + +#endif // BASE_PROCESS_TITLE_LINUX_H_ diff --git a/ipc/chromium/src/base/shared_memory.h b/ipc/chromium/src/base/shared_memory.h new file mode 100644 index 0000000000..7735577334 --- /dev/null +++ b/ipc/chromium/src/base/shared_memory.h @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SHARED_MEMORY_H_ +#define BASE_SHARED_MEMORY_H_ + +#if defined(XP_UNIX) +# include <sys/types.h> +# include <semaphore.h> +#endif +#include <string> + +#include "base/basictypes.h" +#include "base/process.h" +#include "mozilla/Attributes.h" +#include "mozilla/UniquePtrExtensions.h" + +namespace base { + +// SharedMemoryHandle is a platform specific type which represents +// the underlying OS handle to a shared memory segment. +typedef mozilla::UniqueFileHandle SharedMemoryHandle; + +// Platform abstraction for shared memory. Provides a C++ wrapper +// around the OS primitive for a memory mapped file. +class SharedMemory { + public: + // Create a new SharedMemory object. + SharedMemory() = default; + + // Create a new SharedMemory object from an existing, open + // shared memory file. + SharedMemory(SharedMemoryHandle init_handle, bool read_only) + : SharedMemory() { + SetHandle(std::move(init_handle), read_only); + } + + // Move constructor; transfers ownership. + SharedMemory(SharedMemory&& other) = default; + + // Destructor. Will close any open files. + ~SharedMemory(); + + // Initialize a new SharedMemory object from an existing, open + // shared memory file. + bool SetHandle(SharedMemoryHandle handle, bool read_only); + + // Return true iff the given handle is valid (i.e. not the distingished + // invalid value; NULL for a HANDLE and -1 for a file descriptor) + static bool IsHandleValid(const SharedMemoryHandle& handle); + + // IsHandleValid applied to this object's handle. + bool IsValid() const { return static_cast<bool>(mapped_file_); } + + // Return invalid handle (see comment above for exact definition). + static SharedMemoryHandle NULLHandle(); + + // Creates a shared memory segment. + // Returns true on success, false on failure. + bool Create(size_t size) { return CreateInternal(size, false); } + + // Creates a shared memory segment that supports the Freeze() + // method; see below. (Warning: creating freezeable shared memory + // within a sandboxed process isn't possible on some platforms.) + // Returns true on success, false on failure. + bool CreateFreezeable(size_t size) { return CreateInternal(size, true); } + + // Maps the shared memory into the caller's address space. + // Returns true on success, false otherwise. The memory address + // is accessed via the memory() accessor. + // + // If the specified fixed address is not null, it is the address that the + // shared memory must be mapped at. Returns false if the shared memory + // could not be mapped at that address. + bool Map(size_t bytes, void* fixed_address = nullptr); + + // Unmaps the shared memory from the caller's address space. + void Unmap() { memory_ = nullptr; } + + // Get the size of the opened shared memory backing file. + // Note: This size is only available to the creator of the + // shared memory, and not to those that opened shared memory + // created externally. + // Returns 0 if not opened or unknown. + size_t max_size() const { return max_size_; } + + // Gets a pointer to the opened memory space if it has been + // Mapped via Map(). Returns NULL if it is not mapped. + void* memory() const { return memory_.get(); } + + // Extracts the underlying file handle, returning a RAII type. + // If `unmap_view` is true, this unmaps the memory as a side-effect (and + // cleans up any OS-specific resources). + mozilla::UniqueFileHandle TakeHandle(bool unmap_view = true) { + mozilla::UniqueFileHandle handle = std::move(mapped_file_); + Close(unmap_view); + return handle; + } + + // Creates a copy of the underlying file handle, returning a RAII type. + // This operation may fail, in which case the returned file handle will be + // invalid. + mozilla::UniqueFileHandle CloneHandle(); + + // Make the shared memory object read-only, such that it cannot be + // written even if it's sent to an untrusted process. If it was + // mapped in this process, it will be unmapped. The object must + // have been created with CreateFreezeable(), and must not have + // already been shared to another process. + // + // (See bug 1479960 comment #0 for OS-specific implementation + // details.) + [[nodiscard]] bool Freeze() { + Unmap(); + return ReadOnlyCopy(this); + } + + // Similar to Freeze(), but assigns the read-only capability to + // another SharedMemory object and leaves this object's mapping in + // place and writeable. This can be used to broadcast data to + // several untrusted readers without copying. + // + // The other constraints of Freeze() still apply: this object closes + // its handle (as if by `Close(false)`), it cannot have been + // previously shared, and the read-only handle cannot be used to + // write the memory even by a malicious process. + // + // (The out parameter can currently be the same as `this` if and + // only if the memory was unmapped, but this is an implementation + // detail and shouldn't be relied on; for that use case Freeze() + // should be used instead.) + [[nodiscard]] bool ReadOnlyCopy(SharedMemory* ro_out); + + // Closes the open shared memory segment. + // It is safe to call Close repeatedly. + void Close(bool unmap_view = true); + + // Returns a page-aligned address at which the given number of bytes could + // probably be mapped. Returns NULL on error or if there is insufficient + // contiguous address space to map the required number of pages. + // + // Note that there is no guarantee that the given address space will actually + // be free by the time this function returns, since another thread might map + // something there in the meantime. + static void* FindFreeAddressSpace(size_t size); + +#ifdef XP_UNIX + // If named POSIX shm is being used, append the prefix (including + // the leading '/') that would be used by a process with the given + // pid to the given string and return true. If not, return false. + // (This is public so that the Linux sandboxing code can use it.) + static bool AppendPosixShmPrefix(std::string* str, pid_t pid); + // Similar, but simply returns whether POSIX shm is in use. + static bool UsingPosixShm(); +#endif + + private: + bool CreateInternal(size_t size, bool freezeable); + + // Unmapping shared memory requires the mapped size on Unix but not + // Windows; this encapsulates that difference. + struct MappingDeleter { +#ifdef XP_UNIX + // A default-constructed deleter must be used only with nullptr + // (to allow default-constructing UniqueMapping). A deleter with + // a size must be used at most once. + size_t mapped_size_ = 0; + explicit MappingDeleter(size_t size) : mapped_size_(size) {} +#endif + MappingDeleter() = default; + void operator()(void* ptr); + }; + using UniqueMapping = mozilla::UniquePtr<void, MappingDeleter>; + + UniqueMapping memory_; + size_t max_size_ = 0; + mozilla::UniqueFileHandle mapped_file_; +#if defined(XP_WIN) + // If true indicates this came from an external source so needs extra checks + // before being mapped. + bool external_section_ = false; +#elif !defined(ANDROID) + mozilla::UniqueFileHandle frozen_file_; + bool is_memfd_ = false; +#endif + bool read_only_ = false; + bool freezeable_ = false; + + DISALLOW_EVIL_CONSTRUCTORS(SharedMemory); +}; + +} // namespace base + +#endif // BASE_SHARED_MEMORY_H_ diff --git a/ipc/chromium/src/base/shared_memory_posix.cc b/ipc/chromium/src/base/shared_memory_posix.cc new file mode 100644 index 0000000000..bfe20e343a --- /dev/null +++ b/ipc/chromium/src/base/shared_memory_posix.cc @@ -0,0 +1,563 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/shared_memory.h" + +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> + +#ifdef ANDROID +# include "mozilla/Ashmem.h" +#endif + +#ifdef XP_LINUX +# include "linux_memfd_defs.h" +#endif +#ifdef MOZ_WIDGET_GTK +# include "mozilla/WidgetUtilsGtk.h" +#endif + +#ifdef __FreeBSD__ +# include <sys/capsicum.h> +#endif + +#ifdef MOZ_VALGRIND +# include <valgrind/valgrind.h> +#endif + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "mozilla/Atomics.h" +#include "mozilla/Maybe.h" +#include "mozilla/ProfilerThreadSleep.h" +#include "mozilla/UniquePtrExtensions.h" +#include "prenv.h" + +namespace base { + +void SharedMemory::MappingDeleter::operator()(void* ptr) { + // Check that this isn't a default-constructed deleter. (`munmap` + // is specified to fail with `EINVAL` if the length is 0, so this + // assertion isn't load-bearing.) + DCHECK(mapped_size_ != 0); + munmap(ptr, mapped_size_); + // Guard against multiple calls of the same deleter, which shouldn't + // happen (but could, if `UniquePtr::reset` were used). Calling + // `munmap` with an incorrect non-zero length would be bad. + mapped_size_ = 0; +} + +SharedMemory::~SharedMemory() { + // This is almost equal to the default destructor, except for the + // warning message about unfrozen freezable memory. + Close(); +} + +bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) { + DCHECK(!mapped_file_); +#ifndef ANDROID + DCHECK(!frozen_file_); +#endif + + freezeable_ = false; + mapped_file_ = std::move(handle); + read_only_ = read_only; + // is_memfd_ only matters for freezing, which isn't possible + return true; +} + +// static +bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { + return handle != nullptr; +} + +// static +SharedMemoryHandle SharedMemory::NULLHandle() { return nullptr; } + +// static +bool SharedMemory::UsingPosixShm() { + // Undocumented feature of AppendPosixShmPrefix to reduce code + // duplication: if the string pointer is null, it's ignored. + return AppendPosixShmPrefix(nullptr, 0); +} + +#ifdef ANDROID + +// Android has its own shared memory API, ashmem. It doesn't support +// POSIX shm_open, and the memfd support (see below) also doesn't work +// because its SELinux policy prevents the procfs operations we'd use +// (see bug 1670277 for more details). + +// static +bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { + return false; +} + +bool SharedMemory::CreateInternal(size_t size, bool freezeable) { + read_only_ = false; + + DCHECK(size > 0); + DCHECK(!mapped_file_); + + int fd = mozilla::android::ashmem_create(nullptr, size); + if (fd < 0) { + CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); + return false; + } + + mapped_file_.reset(fd); + max_size_ = size; + freezeable_ = freezeable; + return true; +} + +bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { + DCHECK(mapped_file_); + DCHECK(!read_only_); + CHECK(freezeable_); + + if (ro_out == this) { + DCHECK(!memory_); + } + + if (mozilla::android::ashmem_setProt(mapped_file_.get(), PROT_READ) != 0) { + CHROMIUM_LOG(WARNING) << "failed to set ashmem read-only: " + << strerror(errno); + return false; + } + + mozilla::UniqueFileHandle ro_file = std::move(mapped_file_); + + freezeable_ = false; + ro_out->Close(); + ro_out->mapped_file_ = std::move(ro_file); + ro_out->max_size_ = max_size_; + ro_out->read_only_ = true; + ro_out->freezeable_ = false; + + return true; +} + +#else // not Android + +// memfd_create is a nonstandard interface for creating anonymous +// shared memory accessible as a file descriptor but not tied to any +// filesystem. It first appeared in Linux 3.17, and was adopted by +// FreeBSD in version 13. + +# if !defined(HAVE_MEMFD_CREATE) && defined(XP_LINUX) && \ + defined(SYS_memfd_create) + +// Older libc versions (e.g., glibc before 2.27) don't have the +// wrapper, but we can supply our own; see `linux_memfd_defs.h`. + +static int memfd_create(const char* name, unsigned int flags) { + return syscall(SYS_memfd_create, name, flags); +} + +# define HAVE_MEMFD_CREATE 1 +# endif + +// memfd supports having "seals" applied to the file, to prevent +// various types of changes (which apply to all fds referencing the +// file). Unfortunately, we can't rely on F_SEAL_WRITE to implement +// Freeze(); see the comments in ReadOnlyCopy() below. +// +// Instead, to prevent a child process from regaining write access to +// a read-only copy, the OS must also provide a way to remove write +// permissions at the file descriptor level. This next section +// attempts to accomplish that. + +# ifdef HAVE_MEMFD_CREATE +# ifdef XP_LINUX +# define USE_MEMFD_CREATE 1 + +// To create a read-only duplicate of an fd, we can use procfs; the +// same operation could restore write access, but sandboxing prevents +// child processes from accessing /proc. +// +// (Note: if this ever changes to not use /proc, also reconsider how +// and if HaveMemfd should check whether this works.) + +static int DupReadOnly(int fd) { + std::string path = StringPrintf("/proc/self/fd/%d", fd); + // procfs opens probably won't EINTR, but checking for it can't hurt + return HANDLE_EINTR(open(path.c_str(), O_RDONLY | O_CLOEXEC)); +} + +# elif defined(__FreeBSD__) +# define USE_MEMFD_CREATE 1 + +// FreeBSD's Capsicum framework allows irrevocably restricting the +// operations permitted on a file descriptor. + +static int DupReadOnly(int fd) { + int rofd = dup(fd); + if (rofd < 0) { + return -1; + } + + cap_rights_t rights; + cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R); + if (cap_rights_limit(rofd, &rights) < 0) { + int err = errno; + close(rofd); + errno = err; + return -1; + } + + return rofd; +} + +# else // unhandled OS +# warning "OS has memfd_create but no DupReadOnly implementation" +# endif // OS selection +# endif // HAVE_MEMFD_CREATE + +// Runtime detection for memfd support. +static bool HaveMemfd() { +# ifdef USE_MEMFD_CREATE + static const bool kHave = [] { + mozilla::UniqueFileHandle fd( + memfd_create("mozilla-ipc-test", MFD_CLOEXEC | MFD_ALLOW_SEALING)); + if (!fd) { + DCHECK_EQ(errno, ENOSYS); + return false; + } + + // Verify that DupReadOnly works; on Linux it's known to fail if: + // + // * SELinux assigns the memfd a type for which this process's + // domain doesn't have "open" permission; this is always the + // case on Android but could occur on desktop as well + // + // * /proc (used by the DupReadOnly implementation) isn't mounted, + // which is a configuration that the Tor Browser project is + // interested in as a way to reduce fingerprinting risk + // + // Sandboxed processes on Linux also can't use it if sandboxing + // has already been started, but that's expected. It should be + // safe for sandboxed child processes to use memfd even if an + // unsandboxed process couldn't freeze them, because freezing + // isn't allowed (or meaningful) for memory created by another + // process. + + if (!PR_GetEnv("MOZ_SANDBOXED")) { + mozilla::UniqueFileHandle rofd(DupReadOnly(fd.get())); + if (!rofd) { + CHROMIUM_LOG(WARNING) << "read-only dup failed (" << strerror(errno) + << "); not using memfd"; + return false; + } + } + return true; + }(); + return kHave; +# else + return false; +# endif // USE_MEMFD_CREATE +} + +// static +bool SharedMemory::AppendPosixShmPrefix(std::string* str, pid_t pid) { + if (HaveMemfd()) { + return false; + } + // See also UsingPosixShm(). + if (!str) { + return true; + } + *str += '/'; +# ifdef MOZ_WIDGET_GTK + // The Snap package environment doesn't provide a private /dev/shm + // (it's used for communication with services like PulseAudio); + // instead AppArmor is used to restrict access to it. Anything with + // this prefix is allowed: + if (const char* snap = mozilla::widget::GetSnapInstanceName()) { + StringAppendF(str, "snap.%s.", snap); + } +# endif // XP_LINUX + // Hopefully the "implementation defined" name length limit is long + // enough for this. + StringAppendF(str, "org.mozilla.ipc.%d.", static_cast<int>(pid)); + return true; +} + +bool SharedMemory::CreateInternal(size_t size, bool freezeable) { + read_only_ = false; + + DCHECK(size > 0); + DCHECK(!mapped_file_); + DCHECK(!frozen_file_); + + mozilla::UniqueFileHandle fd; + mozilla::UniqueFileHandle frozen_fd; + bool is_memfd = false; + +# ifdef USE_MEMFD_CREATE + if (HaveMemfd()) { + const unsigned flags = MFD_CLOEXEC | (freezeable ? MFD_ALLOW_SEALING : 0); + fd.reset(memfd_create("mozilla-ipc", flags)); + if (!fd) { + // In general it's too late to fall back here -- in a sandboxed + // child process, shm_open is already blocked. And it shouldn't + // be necessary. + CHROMIUM_LOG(WARNING) << "failed to create memfd: " << strerror(errno); + return false; + } + is_memfd = true; + if (freezeable) { + frozen_fd.reset(DupReadOnly(fd.get())); + if (!frozen_fd) { + CHROMIUM_LOG(WARNING) + << "failed to create read-only memfd: " << strerror(errno); + return false; + } + } + } +# endif + + if (!fd) { + // Generic Unix: shm_open + shm_unlink + do { + // The names don't need to be unique, but it saves time if they + // usually are. + static mozilla::Atomic<size_t> sNameCounter; + std::string name; + CHECK(AppendPosixShmPrefix(&name, getpid())); + StringAppendF(&name, "%zu", sNameCounter++); + // O_EXCL means the names being predictable shouldn't be a problem. + fd.reset(HANDLE_EINTR( + shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600))); + if (fd) { + if (freezeable) { + frozen_fd.reset(HANDLE_EINTR(shm_open(name.c_str(), O_RDONLY, 0400))); + if (!frozen_fd) { + int open_err = errno; + shm_unlink(name.c_str()); + DLOG(FATAL) << "failed to re-open freezeable shm: " + << strerror(open_err); + return false; + } + } + if (shm_unlink(name.c_str()) != 0) { + // This shouldn't happen, but if it does: assume the file is + // in fact leaked, and bail out now while it's still 0-length. + DLOG(FATAL) << "failed to unlink shm: " << strerror(errno); + return false; + } + } + } while (!fd && errno == EEXIST); + } + + if (!fd) { + CHROMIUM_LOG(WARNING) << "failed to open shm: " << strerror(errno); + return false; + } + + mozilla::Maybe<int> fallocateError; +# if defined(HAVE_POSIX_FALLOCATE) + // Using posix_fallocate will ensure that there's actually space for this + // file. Otherwise we end up with a sparse file that can give SIGBUS if we + // run out of space while writing to it. (This doesn't apply to memfd.) + if (!is_memfd) { + int rv; + // Avoid repeated interruptions of posix_fallocate by the profiler's + // SIGPROF sampling signal. Indicating "thread sleep" here means we'll + // get up to one interruption but not more. See bug 1658847 for more. + // This has to be scoped outside the HANDLE_RV_EINTR retry loop. + { + AUTO_PROFILER_THREAD_SLEEP; + + rv = HANDLE_RV_EINTR( + posix_fallocate(fd.get(), 0, static_cast<off_t>(size))); + } + + // Some filesystems have trouble with posix_fallocate. For now, we must + // fallback ftruncate and accept the allocation failures like we do + // without posix_fallocate. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1618914 + if (rv != 0 && rv != EOPNOTSUPP && rv != EINVAL && rv != ENODEV) { + CHROMIUM_LOG(WARNING) + << "fallocate failed to set shm size: " << strerror(rv); + return false; + } + fallocateError = mozilla::Some(rv); + } +# endif + + // If posix_fallocate isn't supported / relevant for this type of + // file (either failed with an expected error, or wasn't attempted), + // then set the size with ftruncate: + if (fallocateError != mozilla::Some(0)) { + int rv = HANDLE_EINTR(ftruncate(fd.get(), static_cast<off_t>(size))); + if (rv != 0) { + int ftruncate_errno = errno; + if (fallocateError) { + CHROMIUM_LOG(WARNING) << "fallocate failed to set shm size: " + << strerror(*fallocateError); + } + CHROMIUM_LOG(WARNING) + << "ftruncate failed to set shm size: " << strerror(ftruncate_errno); + return false; + } + } + + mapped_file_ = std::move(fd); + frozen_file_ = std::move(frozen_fd); + max_size_ = size; + freezeable_ = freezeable; + is_memfd_ = is_memfd; + return true; +} + +bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { + DCHECK(mapped_file_); + DCHECK(!read_only_); + CHECK(freezeable_); + + if (ro_out == this) { + DCHECK(!memory_); + } + +# ifdef USE_MEMFD_CREATE +# ifdef MOZ_VALGRIND + // Valgrind allows memfd_create but doesn't understand F_ADD_SEALS. + static const bool haveSeals = RUNNING_ON_VALGRIND == 0; +# else + static const bool haveSeals = true; +# endif + static const bool useSeals = !PR_GetEnv("MOZ_SHM_NO_SEALS"); + if (is_memfd_ && haveSeals && useSeals) { + // Seals are added to the file as defense-in-depth. The primary + // method of access control is creating a read-only fd (using + // procfs in this case) and requiring that sandboxes processes not + // have access to /proc/self/fd to regain write permission; this + // is the same as with shm_open. + // + // Unfortunately, F_SEAL_WRITE is unreliable: if the process + // forked while there was a writeable mapping, it will inherit a + // copy of the mapping, which causes the seal to fail. + // + // (Also, in the future we may want to split this into separate + // classes for mappings and shared memory handles, which would + // complicate identifying the case where `F_SEAL_WRITE` would be + // possible even in the absence of races with fork.) + // + // However, Linux 5.1 added F_SEAL_FUTURE_WRITE, which prevents + // write operations afterwards, but existing writeable mappings + // are unaffected (similar to ashmem protection semantics). + + const int seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; + int sealError = EINVAL; + +# ifdef F_SEAL_FUTURE_WRITE + sealError = + fcntl(mapped_file_.get(), F_ADD_SEALS, seals | F_SEAL_FUTURE_WRITE) == 0 + ? 0 + : errno; +# endif // F_SEAL_FUTURE_WRITE + if (sealError == EINVAL) { + sealError = + fcntl(mapped_file_.get(), F_ADD_SEALS, seals) == 0 ? 0 : errno; + } + if (sealError != 0) { + CHROMIUM_LOG(WARNING) << "failed to seal memfd: " << strerror(errno); + return false; + } + } +# else // !USE_MEMFD_CREATE + DCHECK(!is_memfd_); +# endif + + DCHECK(frozen_file_); + DCHECK(mapped_file_); + mapped_file_ = nullptr; + mozilla::UniqueFileHandle ro_file = std::move(frozen_file_); + + DCHECK(ro_file); + freezeable_ = false; + ro_out->Close(); + ro_out->mapped_file_ = std::move(ro_file); + ro_out->max_size_ = max_size_; + ro_out->read_only_ = true; + ro_out->freezeable_ = false; + + return true; +} + +#endif // not Android + +#ifndef MAP_NORESERVE +# define MAP_NORESERVE 0 +#endif + +bool SharedMemory::Map(size_t bytes, void* fixed_address) { + if (!mapped_file_) { + return false; + } + DCHECK(!memory_); + + // Don't use MAP_FIXED when a fixed_address was specified, since that can + // replace pages that are alread mapped at that address. + void* mem = + mmap(fixed_address, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), + MAP_SHARED, mapped_file_.get(), 0); + + if (mem == MAP_FAILED) { + CHROMIUM_LOG(WARNING) << "Call to mmap failed: " << strerror(errno); + return false; + } + + if (fixed_address && mem != fixed_address) { + bool munmap_succeeded = munmap(mem, bytes) == 0; + DCHECK(munmap_succeeded) << "Call to munmap failed, errno=" << errno; + return false; + } + + memory_ = UniqueMapping(mem, MappingDeleter(bytes)); + return true; +} + +void* SharedMemory::FindFreeAddressSpace(size_t size) { + void* memory = mmap(nullptr, size, PROT_NONE, + MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0); + if (memory == MAP_FAILED) { + return nullptr; + } + munmap(memory, size); + return memory; +} + +SharedMemoryHandle SharedMemory::CloneHandle() { + freezeable_ = false; + const int new_fd = dup(mapped_file_.get()); + if (new_fd < 0) { + CHROMIUM_LOG(WARNING) << "failed to duplicate file descriptor: " + << strerror(errno); + return nullptr; + } + return mozilla::UniqueFileHandle(new_fd); +} +void SharedMemory::Close(bool unmap_view) { + if (unmap_view) { + Unmap(); + } + + mapped_file_ = nullptr; +#ifndef ANDROID + if (frozen_file_) { + CHROMIUM_LOG(WARNING) << "freezeable shared memory was never frozen"; + frozen_file_ = nullptr; + } +#endif +} + +} // namespace base diff --git a/ipc/chromium/src/base/shared_memory_win.cc b/ipc/chromium/src/base/shared_memory_win.cc new file mode 100644 index 0000000000..8269912156 --- /dev/null +++ b/ipc/chromium/src/base/shared_memory_win.cc @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/shared_memory.h" + +#include "base/logging.h" +#include "base/win_util.h" +#include "base/string_util.h" +#include "mozilla/ipc/ProtocolUtils.h" +#include "mozilla/RandomNum.h" +#include "nsDebug.h" +#include "nsString.h" +#ifdef MOZ_MEMORY +# include "mozmemory_utils.h" +#endif + +namespace { +// NtQuerySection is an internal (but believed to be stable) API and the +// structures it uses are defined in nt_internals.h. +// So we have to define them ourselves. +typedef enum _SECTION_INFORMATION_CLASS { + SectionBasicInformation, +} SECTION_INFORMATION_CLASS; + +typedef struct _SECTION_BASIC_INFORMATION { + PVOID BaseAddress; + ULONG Attributes; + LARGE_INTEGER Size; +} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; + +typedef ULONG(__stdcall* NtQuerySectionType)( + HANDLE SectionHandle, SECTION_INFORMATION_CLASS SectionInformationClass, + PVOID SectionInformation, ULONG SectionInformationLength, + PULONG ResultLength); + +// Checks if the section object is safe to map. At the moment this just means +// it's not an image section. +bool IsSectionSafeToMap(HANDLE handle) { + static NtQuerySectionType nt_query_section_func = + reinterpret_cast<NtQuerySectionType>( + ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "NtQuerySection")); + DCHECK(nt_query_section_func); + + // The handle must have SECTION_QUERY access for this to succeed. + SECTION_BASIC_INFORMATION basic_information = {}; + ULONG status = + nt_query_section_func(handle, SectionBasicInformation, &basic_information, + sizeof(basic_information), nullptr); + if (status) { + return false; + } + + return (basic_information.Attributes & SEC_IMAGE) != SEC_IMAGE; +} + +} // namespace + +namespace base { + +void SharedMemory::MappingDeleter::operator()(void* ptr) { + UnmapViewOfFile(ptr); +} + +SharedMemory::~SharedMemory() = default; + +bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) { + DCHECK(!mapped_file_); + + external_section_ = true; + freezeable_ = false; // just in case + mapped_file_ = std::move(handle); + read_only_ = read_only; + return true; +} + +// static +bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { + return handle != nullptr; +} + +// static +SharedMemoryHandle SharedMemory::NULLHandle() { return nullptr; } + +// Wrapper around CreateFileMappingW for pagefile-backed regions. When out of +// memory, may attempt to stall and retry rather than returning immediately, in +// hopes that the page file is about to be expanded by Windows. (bug 1822383, +// bug 1716727) +// +// This method is largely a copy of the MozVirtualAlloc method from +// mozjemalloc.cpp, which implements this strategy for VirtualAlloc calls, +// except re-purposed to handle CreateFileMapping. +static HANDLE MozCreateFileMappingW( + LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, + DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName) { +#ifdef MOZ_MEMORY + constexpr auto IsOOMError = [] { + return ::GetLastError() == ERROR_COMMITMENT_LIMIT; + }; + + { + HANDLE handle = ::CreateFileMappingW( + INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect, + dwMaximumSizeHigh, dwMaximumSizeLow, lpName); + if (MOZ_LIKELY(handle)) { + MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE, + "::CreateFileMapping should return NULL, not " + "INVALID_HANDLE_VALUE, on failure"); + return handle; + } + + // We can't do anything for errors other than OOM. + if (!IsOOMError()) { + return nullptr; + } + } + + // Retry as many times as desired (possibly zero). + const mozilla::StallSpecs stallSpecs = mozilla::GetAllocatorStallSpecs(); + + const auto ret = + stallSpecs.StallAndRetry(&::Sleep, [&]() -> std::optional<HANDLE> { + HANDLE handle = ::CreateFileMappingW( + INVALID_HANDLE_VALUE, lpFileMappingAttributes, flProtect, + dwMaximumSizeHigh, dwMaximumSizeLow, lpName); + + if (handle) { + MOZ_DIAGNOSTIC_ASSERT(handle != INVALID_HANDLE_VALUE, + "::CreateFileMapping should return NULL, not " + "INVALID_HANDLE_VALUE, on failure"); + return handle; + } + + // Failure for some reason other than OOM. + if (!IsOOMError()) { + return nullptr; + } + + return std::nullopt; + }); + + return ret.value_or(nullptr); +#else + return ::CreateFileMappingW(INVALID_HANDLE_VALUE, lpFileMappingAttributes, + flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, + lpName); +#endif +} + +bool SharedMemory::CreateInternal(size_t size, bool freezeable) { + DCHECK(!mapped_file_); + read_only_ = false; + + // If the shared memory object has no DACL, any process can + // duplicate its handles with any access rights; e.g., re-add write + // access to a read-only handle. To prevent that, we give it an + // empty DACL, so that no process can do that. + SECURITY_ATTRIBUTES sa, *psa = nullptr; + SECURITY_DESCRIPTOR sd; + ACL dacl; + + if (freezeable) { + psa = &sa; + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + if (NS_WARN_IF(!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) || + NS_WARN_IF( + !InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) || + NS_WARN_IF(!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))) { + return false; + } + } + + mapped_file_.reset(MozCreateFileMappingW(psa, PAGE_READWRITE, 0, + static_cast<DWORD>(size), nullptr)); + if (!mapped_file_) return false; + + max_size_ = size; + freezeable_ = freezeable; + return true; +} + +bool SharedMemory::ReadOnlyCopy(SharedMemory* ro_out) { + DCHECK(!read_only_); + CHECK(freezeable_); + + if (ro_out == this) { + DCHECK(!memory_); + } + + HANDLE ro_handle; + if (!::DuplicateHandle(GetCurrentProcess(), mapped_file_.release(), + GetCurrentProcess(), &ro_handle, + GENERIC_READ | FILE_MAP_READ, false, + DUPLICATE_CLOSE_SOURCE)) { + // DUPLICATE_CLOSE_SOURCE applies even if there is an error. + return false; + } + + freezeable_ = false; + + ro_out->Close(); + ro_out->mapped_file_.reset(ro_handle); + ro_out->max_size_ = max_size_; + ro_out->read_only_ = true; + ro_out->freezeable_ = false; + ro_out->external_section_ = external_section_; + + return true; +} + +bool SharedMemory::Map(size_t bytes, void* fixed_address) { + if (!mapped_file_) { + return false; + } + + if (external_section_ && !IsSectionSafeToMap(mapped_file_.get())) { + return false; + } + + void* mem = MapViewOfFileEx( + mapped_file_.get(), + read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, bytes, + fixed_address); + if (mem) { + MOZ_ASSERT(!fixed_address || mem == fixed_address, + "MapViewOfFileEx returned an expected address"); + memory_.reset(mem); + return true; + } + return false; +} + +void* SharedMemory::FindFreeAddressSpace(size_t size) { + void* memory = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); + if (memory) { + VirtualFree(memory, 0, MEM_RELEASE); + } + return memory; +} + +SharedMemoryHandle SharedMemory::CloneHandle() { + freezeable_ = false; + HANDLE handle = INVALID_HANDLE_VALUE; + if (DuplicateHandle(GetCurrentProcess(), mapped_file_.get(), + GetCurrentProcess(), &handle, 0, false, + DUPLICATE_SAME_ACCESS)) { + return SharedMemoryHandle(handle); + } + NS_WARNING("DuplicateHandle Failed!"); + return nullptr; +} + +void SharedMemory::Close(bool unmap_view) { + if (unmap_view) { + Unmap(); + } + + mapped_file_ = nullptr; +} + +} // namespace base diff --git a/ipc/chromium/src/base/stack_container.h b/ipc/chromium/src/base/stack_container.h new file mode 100644 index 0000000000..d9add9904c --- /dev/null +++ b/ipc/chromium/src/base/stack_container.h @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STACK_CONTAINER_H_ +#define BASE_STACK_CONTAINER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" + +// This allocator can be used with STL containers to provide a stack buffer +// from which to allocate memory and overflows onto the heap. This stack buffer +// would be allocated on the stack and allows us to avoid heap operations in +// some situations. +// +// STL likes to make copies of allocators, so the allocator itself can't hold +// the data. Instead, we make the creator responsible for creating a +// StackAllocator::Source which contains the data. Copying the allocator +// merely copies the pointer to this shared source, so all allocators created +// based on our allocator will share the same stack buffer. +// +// This stack buffer implementation is very simple. The first allocation that +// fits in the stack buffer will use the stack buffer. Any subsequent +// allocations will not use the stack buffer, even if there is unused room. +// This makes it appropriate for array-like containers, but the caller should +// be sure to reserve() in the container up to the stack buffer size. Otherwise +// the container will allocate a small array which will "use up" the stack +// buffer. +template <typename T, size_t stack_capacity> +class StackAllocator : public std::allocator<T> { + public: + typedef typename std::allocator<T>::pointer pointer; + typedef typename std::allocator<T>::size_type size_type; + + // Backing store for the allocator. The container owner is responsible for + // maintaining this for as long as any containers using this allocator are + // live. + struct Source { + Source() : used_stack_buffer_(false) {} + + // Casts the buffer in its right type. + T* stack_buffer() { return reinterpret_cast<T*>(stack_buffer_); } + const T* stack_buffer() const { + return reinterpret_cast<const T*>(stack_buffer_); + } + + // + // IMPORTANT: Take care to ensure that stack_buffer_ is aligned + // since it is used to mimic an array of T. + // Be careful while declaring any unaligned types (like bool) + // before stack_buffer_. + // + + // The buffer itself. It is not of type T because we don't want the + // constructors and destructors to be automatically called. Define a POD + // buffer of the right size instead. + char stack_buffer_[sizeof(T[stack_capacity])]; + + // Set when the stack buffer is used for an allocation. We do not track + // how much of the buffer is used, only that somebody is using it. + bool used_stack_buffer_; + }; + + // Used by containers when they want to refer to an allocator of type U. + template <typename U> + struct rebind { + typedef StackAllocator<U, stack_capacity> other; + }; + + // For the straight up copy c-tor, we can share storage. + StackAllocator(const StackAllocator<T, stack_capacity>& rhs) + : source_(rhs.source_) {} + + // ISO C++ requires the following constructor to be defined, + // and std::vector in VC++2008SP1 Release fails with an error + // in the class _Container_base_aux_alloc_real (from <xutility>) + // if the constructor does not exist. + // For this constructor, we cannot share storage; there's + // no guarantee that the Source buffer of Ts is large enough + // for Us. + // TODO: If we were fancy pants, perhaps we could share storage + // iff sizeof(T) == sizeof(U). + template <typename U, size_t other_capacity> + explicit StackAllocator(const StackAllocator<U, other_capacity>& other) + : source_(NULL) {} + + explicit StackAllocator(Source* source) : source_(source) {} + + // Actually do the allocation. Use the stack buffer if nobody has used it yet + // and the size requested fits. Otherwise, fall through to the standard + // allocator. + pointer allocate(size_type n, void* hint = 0) { + if (source_ != NULL && !source_->used_stack_buffer_ && + n <= stack_capacity) { + source_->used_stack_buffer_ = true; + return source_->stack_buffer(); + } else { + return std::allocator<T>::allocate(n, hint); + } + } + + // Free: when trying to free the stack buffer, just mark it as free. For + // non-stack-buffer pointers, just fall though to the standard allocator. + void deallocate(pointer p, size_type n) { + if (source_ != NULL && p == source_->stack_buffer()) + source_->used_stack_buffer_ = false; + else + std::allocator<T>::deallocate(p, n); + } + + private: + Source* source_; +}; + +// A wrapper around STL containers that maintains a stack-sized buffer that the +// initial capacity of the vector is based on. Growing the container beyond the +// stack capacity will transparently overflow onto the heap. The container must +// support reserve(). +// +// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this +// type. This object is really intended to be used only internally. You'll want +// to use the wrappers below for different types. +template <typename TContainerType, int stack_capacity> +class StackContainer { + public: + typedef TContainerType ContainerType; + typedef typename ContainerType::value_type ContainedType; + typedef StackAllocator<ContainedType, stack_capacity> Allocator; + + // Allocator must be constructed before the container! + StackContainer() : allocator_(&stack_data_), container_(allocator_) { + // Make the container use the stack allocation by reserving our buffer size + // before doing anything else. + container_.reserve(stack_capacity); + } + + // Getters for the actual container. + // + // Danger: any copies of this made using the copy constructor must have + // shorter lifetimes than the source. The copy will share the same allocator + // and therefore the same stack buffer as the original. Use std::copy to + // copy into a "real" container for longer-lived objects. + ContainerType& container() { return container_; } + const ContainerType& container() const { return container_; } + + // Support operator-> to get to the container. This allows nicer syntax like: + // StackContainer<...> foo; + // std::sort(foo->begin(), foo->end()); + ContainerType* operator->() { return &container_; } + const ContainerType* operator->() const { return &container_; } + +#ifdef UNIT_TEST + // Retrieves the stack source so that that unit tests can verify that the + // buffer is being used properly. + const typename Allocator::Source& stack_data() const { return stack_data_; } +#endif + + protected: + typename Allocator::Source stack_data_; + Allocator allocator_; + ContainerType container_; + + DISALLOW_EVIL_CONSTRUCTORS(StackContainer); +}; + +// StackString +template <size_t stack_capacity> +class StackString + : public StackContainer< + std::basic_string<char, std::char_traits<char>, + StackAllocator<char, stack_capacity> >, + stack_capacity> { + public: + StackString() + : StackContainer<std::basic_string<char, std::char_traits<char>, + StackAllocator<char, stack_capacity> >, + stack_capacity>() {} + + private: + DISALLOW_EVIL_CONSTRUCTORS(StackString); +}; + +// StackWString +template <size_t stack_capacity> +class StackWString + : public StackContainer< + std::basic_string<wchar_t, std::char_traits<wchar_t>, + StackAllocator<wchar_t, stack_capacity> >, + stack_capacity> { + public: + StackWString() + : StackContainer< + std::basic_string<wchar_t, std::char_traits<wchar_t>, + StackAllocator<wchar_t, stack_capacity> >, + stack_capacity>() {} + + private: + DISALLOW_EVIL_CONSTRUCTORS(StackWString); +}; + +// StackVector +// +// Example: +// StackVector<int, 16> foo; +// foo->push_back(22); // we have overloaded operator-> +// foo[0] = 10; // as well as operator[] +template <typename T, size_t stack_capacity> +class StackVector + : public StackContainer<std::vector<T, StackAllocator<T, stack_capacity> >, + stack_capacity> { + public: + StackVector() + : StackContainer<std::vector<T, StackAllocator<T, stack_capacity> >, + stack_capacity>() {} + + // We need to put this in STL containers sometimes, which requires a copy + // constructor. We can't call the regular copy constructor because that will + // take the stack buffer from the original. Here, we create an empty object + // and make a stack buffer of its own. + StackVector(const StackVector<T, stack_capacity>& other) + : StackContainer<std::vector<T, StackAllocator<T, stack_capacity> >, + stack_capacity>() { + this->container().assign(other->begin(), other->end()); + } + + StackVector<T, stack_capacity>& operator=( + const StackVector<T, stack_capacity>& other) { + this->container().assign(other->begin(), other->end()); + return *this; + } + + // Vectors are commonly indexed, which isn't very convenient even with + // operator-> (using "->at()" does exception stuff we don't want). + T& operator[](size_t i) { return this->container().operator[](i); } + const T& operator[](size_t i) const { + return this->container().operator[](i); + } +}; + +#endif // BASE_STACK_CONTAINER_H_ diff --git a/ipc/chromium/src/base/stl_util-inl.h b/ipc/chromium/src/base/stl_util-inl.h new file mode 100644 index 0000000000..034c16049c --- /dev/null +++ b/ipc/chromium/src/base/stl_util-inl.h @@ -0,0 +1,435 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// STL utility functions. Usually, these replace built-in, but slow(!), +// STL functions with more efficient versions. + +#ifndef BASE_STL_UTIL_INL_H_ +#define BASE_STL_UTIL_INL_H_ + +#include <string.h> // for memcpy +#include <functional> +#include <set> +#include <string> +#include <vector> +#include <cassert> + +// Clear internal memory of an STL object. +// STL clear()/reserve(0) does not always free internal memory allocated +// This function uses swap/destructor to ensure the internal memory is freed. +template <class T> +void STLClearObject(T* obj) { + T tmp; + tmp.swap(*obj); + obj->reserve(0); // this is because sometimes "T tmp" allocates objects with + // memory (arena implementation?). use reserve() + // to clear() even if it doesn't always work +} + +// Reduce memory usage on behalf of object if it is using more than +// "bytes" bytes of space. By default, we clear objects over 1MB. +template <class T> +inline void STLClearIfBig(T* obj, size_t limit = 1 << 20) { + if (obj->capacity() >= limit) { + STLClearObject(obj); + } else { + obj->clear(); + } +} + +// Reserve space for STL object. +// STL's reserve() will always copy. +// This function avoid the copy if we already have capacity +template <class T> +void STLReserveIfNeeded(T* obj, int new_size) { + if (obj->capacity() < new_size) // increase capacity + obj->reserve(new_size); + else if (obj->size() > new_size) // reduce size + obj->resize(new_size); +} + +// STLDeleteContainerPointers() +// For a range within a container of pointers, calls delete +// (non-array version) on these pointers. +// NOTE: for these three functions, we could just implement a DeleteObject +// functor and then call for_each() on the range and functor, but this +// requires us to pull in all of algorithm.h, which seems expensive. +// For hash_[multi]set, it is important that this deletes behind the iterator +// because the hash_set may call the hash function on the iterator when it is +// advanced, which could result in the hash function trying to deference a +// stale pointer. +template <class ForwardIterator> +void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete *temp; + } +} + +// STLDeleteContainerPairPointers() +// For a range within a container of pairs, calls delete +// (non-array version) on BOTH items in the pairs. +// NOTE: Like STLDeleteContainerPointers, it is important that this deletes +// behind the iterator because if both the key and value are deleted, the +// container may call the hash function on the iterator when it is advanced, +// which could result in the hash function trying to dereference a stale +// pointer. +template <class ForwardIterator> +void STLDeleteContainerPairPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + delete temp->second; + } +} + +// STLDeleteContainerPairFirstPointers() +// For a range within a container of pairs, calls delete (non-array version) +// on the FIRST item in the pairs. +// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. +template <class ForwardIterator> +void STLDeleteContainerPairFirstPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + } +} + +// STLDeleteContainerPairSecondPointers() +// For a range within a container of pairs, calls delete +// (non-array version) on the SECOND item in the pairs. +template <class ForwardIterator> +void STLDeleteContainerPairSecondPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + delete begin->second; + ++begin; + } +} + +template <typename T> +inline void STLAssignToVector(std::vector<T>* vec, const T* ptr, size_t n) { + vec->resize(n); + memcpy(&vec->front(), ptr, n * sizeof(T)); +} + +/***** Hack to allow faster assignment to a vector *****/ + +// This routine speeds up an assignment of 32 bytes to a vector from +// about 250 cycles per assignment to about 140 cycles. +// +// Usage: +// STLAssignToVectorChar(&vec, ptr, size); +// STLAssignToString(&str, ptr, size); + +inline void STLAssignToVectorChar(std::vector<char>* vec, const char* ptr, + size_t n) { + STLAssignToVector(vec, ptr, n); +} + +inline void STLAssignToString(std::string* str, const char* ptr, size_t n) { + str->resize(n); + memcpy(&*str->begin(), ptr, n); +} + +// To treat a possibly-empty vector as an array, use these functions. +// If you know the array will never be empty, you can use &*v.begin() +// directly, but that is allowed to dump core if v is empty. This +// function is the most efficient code that will work, taking into +// account how our STL is actually implemented. THIS IS NON-PORTABLE +// CODE, so call us instead of repeating the nonportable code +// everywhere. If our STL implementation changes, we will need to +// change this as well. + +template <typename T> +inline T* vector_as_array(std::vector<T>* v) { +#ifdef NDEBUG + return &*v->begin(); +#else + return v->empty() ? NULL : &*v->begin(); +#endif +} + +template <typename T> +inline const T* vector_as_array(const std::vector<T>* v) { +#ifdef NDEBUG + return &*v->begin(); +#else + return v->empty() ? NULL : &*v->begin(); +#endif +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) +// proposes this as the method. According to Matt Austern, this should +// already work on all current implementations. +inline char* string_as_array(std::string* str) { + // DO NOT USE const_cast<char*>(str->data())! See the unittest for why. + return str->empty() ? NULL : &*str->begin(); +} + +// These are methods that test two hash maps/sets for equality. These exist +// because the == operator in the STL can return false when the maps/sets +// contain identical elements. This is because it compares the internal hash +// tables which may be different if the order of insertions and deletions +// differed. + +template <class HashSet> +inline bool HashSetEquality(const HashSet& set_a, const HashSet& set_b) { + if (set_a.size() != set_b.size()) return false; + for (typename HashSet::const_iterator i = set_a.begin(); i != set_a.end(); + ++i) { + if (set_b.find(*i) == set_b.end()) return false; + } + return true; +} + +template <class HashMap> +inline bool HashMapEquality(const HashMap& map_a, const HashMap& map_b) { + if (map_a.size() != map_b.size()) return false; + for (typename HashMap::const_iterator i = map_a.begin(); i != map_a.end(); + ++i) { + typename HashMap::const_iterator j = map_b.find(i->first); + if (j == map_b.end()) return false; + if (i->second != j->second) return false; + } + return true; +} + +// The following functions are useful for cleaning up STL containers +// whose elements point to allocated memory. + +// STLDeleteElements() deletes all the elements in an STL container and clears +// the container. This function is suitable for use with a vector, set, +// hash_set, or any other STL container which defines sensible begin(), end(), +// and clear() methods. +// +// If container is NULL, this function is a no-op. +// +// As an alternative to calling STLDeleteElements() directly, consider +// STLElementDeleter (defined below), which ensures that your container's +// elements are deleted when the STLElementDeleter goes out of scope. +template <class T> +void STLDeleteElements(T* container) { + if (!container) return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +// Given an STL container consisting of (key, value) pairs, STLDeleteValues +// deletes all the "value" components and clears the container. Does nothing +// in the case it's given a NULL pointer. + +template <class T> +void STLDeleteValues(T* v) { + if (!v) return; + for (typename T::iterator i = v->begin(); i != v->end(); ++i) { + delete i->second; + } + v->clear(); +} + +// The following classes provide a convenient way to delete all elements or +// values from STL containers when they goes out of scope. This greatly +// simplifies code that creates temporary objects and has multiple return +// statements. Example: +// +// vector<MyProto *> tmp_proto; +// STLElementDeleter<vector<MyProto *> > d(&tmp_proto); +// if (...) return false; +// ... +// return success; + +// Given a pointer to an STL container this class will delete all the element +// pointers when it goes out of scope. + +template <class STLContainer> +class STLElementDeleter { + public: + explicit STLElementDeleter(STLContainer* ptr) : container_ptr_(ptr) {} + ~STLElementDeleter() { STLDeleteElements(container_ptr_); } + + private: + STLContainer* container_ptr_; +}; + +// Given a pointer to an STL container this class will delete all the value +// pointers when it goes out of scope. + +template <class STLContainer> +class STLValueDeleter { + public: + explicit STLValueDeleter(STLContainer* ptr) : container_ptr_(ptr) {} + ~STLValueDeleter() { STLDeleteValues(container_ptr_); } + + private: + STLContainer* container_ptr_; +}; + +// Forward declare some callback classes in callback.h for STLBinaryFunction +template <class R, class T1, class T2> +class ResultCallback2; + +// STLBinaryFunction is a wrapper for the ResultCallback2 class in callback.h +// It provides an operator () method instead of a Run method, so it may be +// passed to STL functions in <algorithm>. +// +// The client should create callback with NewPermanentCallback, and should +// delete callback after it is done using the STLBinaryFunction. + +template <class Result, class Arg1, class Arg2> +class STLBinaryFunction : public std::binary_function<Arg1, Arg2, Result> { + public: + typedef ResultCallback2<Result, Arg1, Arg2> Callback; + + explicit STLBinaryFunction(Callback* callback) : callback_(callback) { + assert(callback_); + } + + Result operator()(Arg1 arg1, Arg2 arg2) { return callback_->Run(arg1, arg2); } + + private: + Callback* callback_; +}; + +// STLBinaryPredicate is a specialized version of STLBinaryFunction, where the +// return type is bool and both arguments have type Arg. It can be used +// wherever STL requires a StrictWeakOrdering, such as in sort() or +// lower_bound(). +// +// templated typedefs are not supported, so instead we use inheritance. + +template <class Arg> +class STLBinaryPredicate : public STLBinaryFunction<bool, Arg, Arg> { + public: + typedef typename STLBinaryPredicate<Arg>::Callback Callback; + explicit STLBinaryPredicate(Callback* callback) + : STLBinaryFunction<bool, Arg, Arg>(callback) {} +}; + +// Functors that compose arbitrary unary and binary functions with a +// function that "projects" one of the members of a pair. +// Specifically, if p1 and p2, respectively, are the functions that +// map a pair to its first and second, respectively, members, the +// table below summarizes the functions that can be constructed: +// +// * UnaryOperate1st<pair>(f) returns the function x -> f(p1(x)) +// * UnaryOperate2nd<pair>(f) returns the function x -> f(p2(x)) +// * BinaryOperate1st<pair>(f) returns the function (x,y) -> f(p1(x),p1(y)) +// * BinaryOperate2nd<pair>(f) returns the function (x,y) -> f(p2(x),p2(y)) +// +// A typical usage for these functions would be when iterating over +// the contents of an STL map. For other sample usage, see the unittest. + +template <typename Pair, typename UnaryOp> +class UnaryOperateOnFirst + : public std::unary_function<Pair, typename UnaryOp::result_type> { + public: + UnaryOperateOnFirst() {} + + explicit UnaryOperateOnFirst(const UnaryOp& f) : f_(f) {} + + typename UnaryOp::result_type operator()(const Pair& p) const { + return f_(p.first); + } + + private: + UnaryOp f_; +}; + +template <typename Pair, typename UnaryOp> +UnaryOperateOnFirst<Pair, UnaryOp> UnaryOperate1st(const UnaryOp& f) { + return UnaryOperateOnFirst<Pair, UnaryOp>(f); +} + +template <typename Pair, typename UnaryOp> +class UnaryOperateOnSecond + : public std::unary_function<Pair, typename UnaryOp::result_type> { + public: + UnaryOperateOnSecond() {} + + explicit UnaryOperateOnSecond(const UnaryOp& f) : f_(f) {} + + typename UnaryOp::result_type operator()(const Pair& p) const { + return f_(p.second); + } + + private: + UnaryOp f_; +}; + +template <typename Pair, typename UnaryOp> +UnaryOperateOnSecond<Pair, UnaryOp> UnaryOperate2nd(const UnaryOp& f) { + return UnaryOperateOnSecond<Pair, UnaryOp>(f); +} + +template <typename Pair, typename BinaryOp> +class BinaryOperateOnFirst + : public std::binary_function<Pair, Pair, typename BinaryOp::result_type> { + public: + BinaryOperateOnFirst() {} + + explicit BinaryOperateOnFirst(const BinaryOp& f) : f_(f) {} + + typename BinaryOp::result_type operator()(const Pair& p1, + const Pair& p2) const { + return f_(p1.first, p2.first); + } + + private: + BinaryOp f_; +}; + +template <typename Pair, typename BinaryOp> +BinaryOperateOnFirst<Pair, BinaryOp> BinaryOperate1st(const BinaryOp& f) { + return BinaryOperateOnFirst<Pair, BinaryOp>(f); +} + +template <typename Pair, typename BinaryOp> +class BinaryOperateOnSecond + : public std::binary_function<Pair, Pair, typename BinaryOp::result_type> { + public: + BinaryOperateOnSecond() {} + + explicit BinaryOperateOnSecond(const BinaryOp& f) : f_(f) {} + + typename BinaryOp::result_type operator()(const Pair& p1, + const Pair& p2) const { + return f_(p1.second, p2.second); + } + + private: + BinaryOp f_; +}; + +template <typename Pair, typename BinaryOp> +BinaryOperateOnSecond<Pair, BinaryOp> BinaryOperate2nd(const BinaryOp& f) { + return BinaryOperateOnSecond<Pair, BinaryOp>(f); +} + +// Translates a set into a vector. +template <typename T> +std::vector<T> SetToVector(const std::set<T>& values) { + std::vector<T> result; + result.reserve(values.size()); + result.insert(result.begin(), values.begin(), values.end()); + return result; +} + +#endif // BASE_STL_UTIL_INL_H_ diff --git a/ipc/chromium/src/base/string16.cc b/ipc/chromium/src/base/string16.cc new file mode 100644 index 0000000000..8a0c406eff --- /dev/null +++ b/ipc/chromium/src/base/string16.cc @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string16.h" + +#if defined(XP_WIN) + +# error This file should not be used on 2-byte wchar_t systems +// If this winds up being needed on 2-byte wchar_t systems, either the +// definitions below can be used, or the host system's wide character +// functions like wmemcmp can be wrapped. + +#else + +# include "base/string_util.h" + +namespace base { + +int c16memcmp(const char16* s1, const char16* s2, size_t n) { + // We cannot call memcmp because that changes the semantics. + while (n-- > 0) { + if (*s1 != *s2) { + // We cannot use (*s1 - *s2) because char16 is unsigned. + return ((*s1 < *s2) ? -1 : 1); + } + ++s1; + ++s2; + } + return 0; +} + +size_t c16len(const char16* s) { + const char16* s_orig = s; + while (*s) { + ++s; + } + return s - s_orig; +} + +const char16* c16memchr(const char16* s, char16 c, size_t n) { + while (n-- > 0) { + if (*s == c) { + return s; + } + ++s; + } + return 0; +} + +char16* c16memmove(char16* s1, const char16* s2, size_t n) { + return reinterpret_cast<char16*>(memmove(s1, s2, n * sizeof(char16))); +} + +char16* c16memcpy(char16* s1, const char16* s2, size_t n) { + return reinterpret_cast<char16*>(memcpy(s1, s2, n * sizeof(char16))); +} + +char16* c16memset(char16* s, char16 c, size_t n) { + char16* s_orig = s; + while (n-- > 0) { + *s = c; + ++s; + } + return s_orig; +} + +} // namespace base + +template class std::basic_string<char16, base::string16_char_traits>; + +#endif diff --git a/ipc/chromium/src/base/string16.h b/ipc/chromium/src/base/string16.h new file mode 100644 index 0000000000..c7cc1868e2 --- /dev/null +++ b/ipc/chromium/src/base/string16.h @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRING16_H_ +#define BASE_STRING16_H_ + +// WHAT: +// A version of std::basic_string that provides 2-byte characters even when +// wchar_t is not implemented as a 2-byte type. You can access this class as +// string16. We also define char16, which string16 is based upon. +// +// WHY: +// On Windows, wchar_t is 2 bytes, and it can conveniently handle UTF-16/UCS-2 +// data. Plenty of existing code operates on strings encoded as UTF-16. +// +// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make +// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails +// at run time, because it calls some functions (like wcslen) that come from +// the system's native C library -- which was built with a 4-byte wchar_t! +// It's wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it's +// entirely improper on those systems where the encoding of wchar_t is defined +// as UTF-32. +// +// Here, we define string16, which is similar to std::wstring but replaces all +// libc functions with custom, 2-byte-char compatible routines. It is capable +// of carrying UTF-16-encoded data. + +#include <stdio.h> +#include <string> + +#include "base/basictypes.h" + +#if defined(XP_WIN) + +typedef wchar_t char16; +typedef std::wstring string16; + +#else + +typedef uint16_t char16; + +namespace base { + +// char16 versions of the functions required by string16_char_traits; these +// are based on the wide character functions of similar names ("w" or "wcs" +// instead of "c16"). +int c16memcmp(const char16* s1, const char16* s2, size_t n); +size_t c16len(const char16* s); +const char16* c16memchr(const char16* s, char16 c, size_t n); +char16* c16memmove(char16* s1, const char16* s2, size_t n); +char16* c16memcpy(char16* s1, const char16* s2, size_t n); +char16* c16memset(char16* s, char16 c, size_t n); + +struct string16_char_traits { + typedef char16 char_type; + typedef int int_type; + + // int_type needs to be able to hold each possible value of char_type, and in + // addition, the distinct value of eof(). + COMPILE_ASSERT(sizeof(int_type) > sizeof(char_type), unexpected_type_width); + + typedef std::streamoff off_type; + typedef mbstate_t state_type; + typedef std::fpos<state_type> pos_type; + + static void assign(char_type& c1, const char_type& c2) { c1 = c2; } + + static bool eq(const char_type& c1, const char_type& c2) { return c1 == c2; } + static bool lt(const char_type& c1, const char_type& c2) { return c1 < c2; } + + static int compare(const char_type* s1, const char_type* s2, size_t n) { + return c16memcmp(s1, s2, n); + } + + static size_t length(const char_type* s) { return c16len(s); } + + static const char_type* find(const char_type* s, size_t n, + const char_type& a) { + return c16memchr(s, a, n); + } + + static char_type* move(char_type* s1, const char_type* s2, int_type n) { + return c16memmove(s1, s2, n); + } + + static char_type* copy(char_type* s1, const char_type* s2, size_t n) { + return c16memcpy(s1, s2, n); + } + + static char_type* assign(char_type* s, size_t n, char_type a) { + return c16memset(s, a, n); + } + + static int_type not_eof(const int_type& c) { + return eq_int_type(c, eof()) ? 0 : c; + } + + static char_type to_char_type(const int_type& c) { return char_type(c); } + + static int_type to_int_type(const char_type& c) { return int_type(c); } + + static bool eq_int_type(const int_type& c1, const int_type& c2) { + return c1 == c2; + } + + static int_type eof() { return static_cast<int_type>(EOF); } +}; + +} // namespace base + +// The string class will be explicitly instantiated only once, in string16.cc. +// +// std::basic_string<> in GNU libstdc++ contains a static data member, +// _S_empty_rep_storage, to represent empty strings. When an operation such +// as assignment or destruction is performed on a string, causing its existing +// data member to be invalidated, it must not be freed if this static data +// member is being used. Otherwise, it counts as an attempt to free static +// (and not allocated) data, which is a memory error. +// +// Generally, due to C++ template magic, _S_empty_rep_storage will be marked +// as a coalesced symbol, meaning that the linker will combine multiple +// instances into a single one when generating output. +// +// If a string class is used by multiple shared libraries, a problem occurs. +// Each library will get its own copy of _S_empty_rep_storage. When strings +// are passed across a library boundary for alteration or destruction, memory +// errors will result. GNU libstdc++ contains a configuration option, +// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which +// disables the static data member optimization, but it's a good optimization +// and non-STL code is generally at the mercy of the system's STL +// configuration. Fully-dynamic strings are not the default for GNU libstdc++ +// libstdc++ itself or for the libstdc++ installations on the systems we care +// about, such as Mac OS X and relevant flavors of Linux. +// +// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 . +// +// To avoid problems, string classes need to be explicitly instantiated only +// once, in exactly one library. All other string users see it via an "extern" +// declaration. This is precisely how GNU libstdc++ handles +// std::basic_string<char> (string) and std::basic_string<wchar_t> (wstring). +// +// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2), +// in which the linker does not fully coalesce symbols when dead code +// stripping is enabled. This bug causes the memory errors described above +// to occur even when a std::basic_string<> does not cross shared library +// boundaries, such as in statically-linked executables. +// +// TODO(mark): File this bug with Apple and update this note with a bug number. + +extern template class std::basic_string<char16, base::string16_char_traits>; + +typedef std::basic_string<char16, base::string16_char_traits> string16; + +extern std::ostream& operator<<(std::ostream& out, const string16& str); + +#endif + +#endif // BASE_STRING16_H_ diff --git a/ipc/chromium/src/base/string_piece.cc b/ipc/chromium/src/base/string_piece.cc new file mode 100644 index 0000000000..7b83df0758 --- /dev/null +++ b/ipc/chromium/src/base/string_piece.cc @@ -0,0 +1,190 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Copied from strings/stringpiece.cc with modifications + +#include <algorithm> +#include <ostream> + +#include "base/string_piece.h" + +typedef StringPiece::size_type size_type; + +std::ostream& operator<<(std::ostream& o, const StringPiece& piece) { + o.write(piece.data(), static_cast<std::streamsize>(piece.size())); + return o; +} + +bool operator==(const StringPiece& x, const StringPiece& y) { + if (x.size() != y.size()) return false; + + return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; +} + +void StringPiece::CopyToString(std::string* target) const { + target->assign(!empty() ? data() : "", size()); +} + +void StringPiece::AppendToString(std::string* target) const { + if (!empty()) target->append(data(), size()); +} + +size_type StringPiece::copy(char* buf, size_type n, size_type pos) const { + size_type ret = std::min(length_ - pos, n); + memcpy(buf, ptr_ + pos, ret); + return ret; +} + +size_type StringPiece::find(const StringPiece& s, size_type pos) const { + if (pos > length_) return npos; + + const char* result = + std::search(ptr_ + pos, ptr_ + length_, s.ptr_, s.ptr_ + s.length_); + const size_type xpos = result - ptr_; + return xpos + s.length_ <= length_ ? xpos : npos; +} + +size_type StringPiece::find(char c, size_type pos) const { + if (pos >= length_) return npos; + + const char* result = std::find(ptr_ + pos, ptr_ + length_, c); + return result != ptr_ + length_ ? result - ptr_ : npos; +} + +size_type StringPiece::rfind(const StringPiece& s, size_type pos) const { + if (length_ < s.length_) return npos; + + if (s.empty()) return std::min(length_, pos); + + const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_; + const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_); + return result != last ? result - ptr_ : npos; +} + +size_type StringPiece::rfind(char c, size_type pos) const { + if (length_ == 0) return npos; + + for (size_type i = std::min(pos, length_ - 1);; --i) { + if (ptr_[i] == c) return i; + if (i == 0) break; + } + return npos; +} + +// For each character in characters_wanted, sets the index corresponding +// to the ASCII code of that character to 1 in table. This is used by +// the find_.*_of methods below to tell whether or not a character is in +// the lookup table in constant time. +// The argument `table' must be an array that is large enough to hold all +// the possible values of an unsigned char. Thus it should be be declared +// as follows: +// bool table[UCHAR_MAX + 1] +static inline void BuildLookupTable(const StringPiece& characters_wanted, + bool* table) { + const size_type length = characters_wanted.length(); + const char* const data = characters_wanted.data(); + for (size_type i = 0; i < length; ++i) { + table[static_cast<unsigned char>(data[i])] = true; + } +} + +size_type StringPiece::find_first_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0 || s.length_ == 0) return npos; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) return find_first_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = {false}; + BuildLookupTable(s, lookup); + for (size_type i = pos; i < length_; ++i) { + if (lookup[static_cast<unsigned char>(ptr_[i])]) { + return i; + } + } + return npos; +} + +size_type StringPiece::find_first_not_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0) return npos; + + if (s.length_ == 0) return 0; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) return find_first_not_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = {false}; + BuildLookupTable(s, lookup); + for (size_type i = pos; i < length_; ++i) { + if (!lookup[static_cast<unsigned char>(ptr_[i])]) { + return i; + } + } + return npos; +} + +size_type StringPiece::find_first_not_of(char c, size_type pos) const { + if (length_ == 0) return npos; + + for (; pos < length_; ++pos) { + if (ptr_[pos] != c) { + return pos; + } + } + return npos; +} + +size_type StringPiece::find_last_of(const StringPiece& s, size_type pos) const { + if (length_ == 0 || s.length_ == 0) return npos; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) return find_last_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = {false}; + BuildLookupTable(s, lookup); + for (size_type i = std::min(pos, length_ - 1);; --i) { + if (lookup[static_cast<unsigned char>(ptr_[i])]) return i; + if (i == 0) break; + } + return npos; +} + +size_type StringPiece::find_last_not_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0) return npos; + + size_type i = std::min(pos, length_ - 1); + if (s.length_ == 0) return i; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) return find_last_not_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = {false}; + BuildLookupTable(s, lookup); + for (;; --i) { + if (!lookup[static_cast<unsigned char>(ptr_[i])]) return i; + if (i == 0) break; + } + return npos; +} + +size_type StringPiece::find_last_not_of(char c, size_type pos) const { + if (length_ == 0) return npos; + + for (size_type i = std::min(pos, length_ - 1);; --i) { + if (ptr_[i] != c) return i; + if (i == 0) break; + } + return npos; +} + +StringPiece StringPiece::substr(size_type pos, size_type n) const { + if (pos > length_) pos = length_; + if (n > length_ - pos) n = length_ - pos; + return StringPiece(ptr_ + pos, n); +} + +const StringPiece::size_type StringPiece::npos = size_type(-1); diff --git a/ipc/chromium/src/base/string_piece.h b/ipc/chromium/src/base/string_piece.h new file mode 100644 index 0000000000..8ebdb281d1 --- /dev/null +++ b/ipc/chromium/src/base/string_piece.h @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Copied from strings/stringpiece.h with modifications +// +// A string-like object that points to a sized piece of memory. +// +// Functions or methods may use const StringPiece& parameters to accept either +// a "const char*" or a "string" value that will be implicitly converted to +// a StringPiece. The implicit conversion means that it is often appropriate +// to include this .h file in other files rather than forward-declaring +// StringPiece as would be appropriate for most other Google classes. +// +// Systematic usage of StringPiece is encouraged as it will reduce unnecessary +// conversions from "const char*" to "string" and back again. +// + +#ifndef BASE_STRING_PIECE_H_ +#define BASE_STRING_PIECE_H_ + +#include <algorithm> +#include <iosfwd> +#include <string> + +#include "base/basictypes.h" + +class StringPiece { + public: + typedef size_t size_type; + + private: + const char* ptr_; + size_type length_; + + public: + // We provide non-explicit singleton constructors so users can pass + // in a "const char*" or a "string" wherever a "StringPiece" is + // expected. + StringPiece() : ptr_(NULL), length_(0) {} + MOZ_IMPLICIT StringPiece(const char* str) + : ptr_(str), length_((str == NULL) ? 0 : strlen(str)) {} + MOZ_IMPLICIT StringPiece(const std::string& str) + : ptr_(str.data()), length_(str.size()) {} + StringPiece(const char* offset, size_type len) : ptr_(offset), length_(len) {} + + // data() may return a pointer to a buffer with embedded NULs, and the + // returned buffer may or may not be null terminated. Therefore it is + // typically a mistake to pass data() to a routine that expects a NUL + // terminated string. + const char* data() const { return ptr_; } + size_type size() const { return length_; } + size_type length() const { return length_; } + bool empty() const { return length_ == 0; } + + void clear() { + ptr_ = NULL; + length_ = 0; + } + void set(const char* aData, size_type aLen) { + ptr_ = aData; + length_ = aLen; + } + void set(const char* str) { + ptr_ = str; + length_ = str ? strlen(str) : 0; + } + void set(const void* aData, size_type aLen) { + ptr_ = reinterpret_cast<const char*>(aData); + length_ = aLen; + } + + char operator[](size_type i) const { return ptr_[i]; } + + void remove_prefix(size_type n) { + ptr_ += n; + length_ -= n; + } + + void remove_suffix(size_type n) { length_ -= n; } + + int compare(const StringPiece& x) const { + int r = wordmemcmp(ptr_, x.ptr_, std::min(length_, x.length_)); + if (r == 0) { + if (length_ < x.length_) + r = -1; + else if (length_ > x.length_) + r = +1; + } + return r; + } + + std::string as_string() const { + // std::string doesn't like to take a NULL pointer even with a 0 size. + return std::string(!empty() ? data() : "", size()); + } + + void CopyToString(std::string* target) const; + void AppendToString(std::string* target) const; + + // Does "this" start with "x" + bool starts_with(const StringPiece& x) const { + return ((length_ >= x.length_) && + (wordmemcmp(ptr_, x.ptr_, x.length_) == 0)); + } + + // Does "this" end with "x" + bool ends_with(const StringPiece& x) const { + return ((length_ >= x.length_) && + (wordmemcmp(ptr_ + (length_ - x.length_), x.ptr_, x.length_) == 0)); + } + + // standard STL container boilerplate + typedef char value_type; + typedef const char* pointer; + typedef const char& reference; + typedef const char& const_reference; + typedef ptrdiff_t difference_type; + static const size_type npos; + typedef const char* const_iterator; + typedef const char* iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + typedef std::reverse_iterator<iterator> reverse_iterator; + iterator begin() const { return ptr_; } + iterator end() const { return ptr_ + length_; } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(ptr_ + length_); + } + const_reverse_iterator rend() const { return const_reverse_iterator(ptr_); } + + size_type max_size() const { return length_; } + size_type capacity() const { return length_; } + + size_type copy(char* buf, size_type n, size_type pos = 0) const; + + size_type find(const StringPiece& s, size_type pos = 0) const; + size_type find(char c, size_type pos = 0) const; + size_type rfind(const StringPiece& s, size_type pos = npos) const; + size_type rfind(char c, size_type pos = npos) const; + + size_type find_first_of(const StringPiece& s, size_type pos = 0) const; + size_type find_first_of(char c, size_type pos = 0) const { + return find(c, pos); + } + size_type find_first_not_of(const StringPiece& s, size_type pos = 0) const; + size_type find_first_not_of(char c, size_type pos = 0) const; + size_type find_last_of(const StringPiece& s, size_type pos = npos) const; + size_type find_last_of(char c, size_type pos = npos) const { + return rfind(c, pos); + } + size_type find_last_not_of(const StringPiece& s, size_type pos = npos) const; + size_type find_last_not_of(char c, size_type pos = npos) const; + + StringPiece substr(size_type pos, size_type n = npos) const; + + static int wordmemcmp(const char* p, const char* p2, size_type N) { + return memcmp(p, p2, N); + } +}; + +bool operator==(const ::StringPiece& x, const ::StringPiece& y); + +inline bool operator!=(const ::StringPiece& x, const ::StringPiece& y) { + return !(x == y); +} + +inline bool operator<(const ::StringPiece& x, const ::StringPiece& y) { + const int r = ::StringPiece::wordmemcmp(x.data(), y.data(), + std::min(x.size(), y.size())); + return ((r < 0) || ((r == 0) && (x.size() < y.size()))); +} + +inline bool operator>(const ::StringPiece& x, const ::StringPiece& y) { + return y < x; +} + +inline bool operator<=(const ::StringPiece& x, const ::StringPiece& y) { + return !(x > y); +} + +inline bool operator>=(const ::StringPiece& x, const ::StringPiece& y) { + return !(x < y); +} + +// allow StringPiece to be logged (needed for unit testing). +extern std::ostream& operator<<(std::ostream& o, const ::StringPiece& piece); + +#endif // BASE_STRING_PIECE_H_ diff --git a/ipc/chromium/src/base/string_util.cc b/ipc/chromium/src/base/string_util.cc new file mode 100644 index 0000000000..39b8463cb1 --- /dev/null +++ b/ipc/chromium/src/base/string_util.cc @@ -0,0 +1,719 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string_util.h" + +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <wchar.h> +#include <wctype.h> + +#include <algorithm> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace { + +// Hack to convert any char-like type to its unsigned counterpart. +// For example, it will convert char, signed char and unsigned char to unsigned +// char. +template <typename T> +struct ToUnsigned { + typedef T Unsigned; +}; + +template <> +struct ToUnsigned<char> { + typedef unsigned char Unsigned; +}; +template <> +struct ToUnsigned<signed char> { + typedef unsigned char Unsigned; +}; +template <> +struct ToUnsigned<wchar_t> { +#if defined(XP_WIN) + typedef unsigned short Unsigned; +#else + typedef uint32_t Unsigned; +#endif +}; +template <> +struct ToUnsigned<short> { + typedef unsigned short Unsigned; +}; + +// Generalized string-to-number conversion. +// +// StringToNumberTraits should provide: +// - a typedef for string_type, the STL string type used as input. +// - a typedef for value_type, the target numeric type. +// - a static function, convert_func, which dispatches to an appropriate +// strtol-like function and returns type value_type. +// - a static function, valid_func, which validates |input| and returns a bool +// indicating whether it is in proper form. This is used to check for +// conditions that convert_func tolerates but should result in +// StringToNumber returning false. For strtol-like funtions, valid_func +// should check for leading whitespace. +template <typename StringToNumberTraits> +bool StringToNumber(const typename StringToNumberTraits::string_type& input, + typename StringToNumberTraits::value_type* output) { + typedef StringToNumberTraits traits; + + errno = 0; // Thread-safe? It is on at least Mac, Linux, and Windows. + typename traits::string_type::value_type* endptr = NULL; + typename traits::value_type value = + traits::convert_func(input.c_str(), &endptr); + *output = value; + + // Cases to return false: + // - If errno is ERANGE, there was an overflow or underflow. + // - If the input string is empty, there was nothing to parse. + // - If endptr does not point to the end of the string, there are either + // characters remaining in the string after a parsed number, or the string + // does not begin with a parseable number. endptr is compared to the + // expected end given the string's stated length to correctly catch cases + // where the string contains embedded NUL characters. + // - valid_func determines that the input is not in preferred form. + return errno == 0 && !input.empty() && + input.c_str() + input.length() == endptr && traits::valid_func(input); +} + +class StringToLongTraits { + public: + typedef std::string string_type; + typedef long value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { + return strtol(str, endptr, kBase); + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class String16ToLongTraits { + public: + typedef string16 string_type; + typedef long value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#if defined(XP_WIN) + return wcstol(str, endptr, kBase); +#else + std::string ascii_string = UTF16ToASCII(string16(str)); + char* ascii_end = NULL; + value_type ret = strtol(ascii_string.c_str(), &ascii_end, kBase); + if (ascii_string.c_str() + ascii_string.length() == ascii_end) { + *endptr = + const_cast<string_type::value_type*>(str) + ascii_string.length(); + } + return ret; +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); + } +}; + +class StringToInt64Traits { + public: + typedef std::string string_type; + typedef int64_t value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#ifdef XP_WIN + return _strtoi64(str, endptr, kBase); +#else // assume XP_UNIX + return strtoll(str, endptr, kBase); +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class String16ToInt64Traits { + public: + typedef string16 string_type; + typedef int64_t value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#ifdef XP_WIN + return _wcstoi64(str, endptr, kBase); +#else // assume XP_UNIX + std::string ascii_string = UTF16ToASCII(string16(str)); + char* ascii_end = NULL; + value_type ret = strtoll(ascii_string.c_str(), &ascii_end, kBase); + if (ascii_string.c_str() + ascii_string.length() == ascii_end) { + *endptr = + const_cast<string_type::value_type*>(str) + ascii_string.length(); + } + return ret; +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); + } +}; + +} // namespace + +namespace base { + +bool IsWprintfFormatPortable(const wchar_t* format) { + for (const wchar_t* position = format; *position != '\0'; ++position) { + if (*position == '%') { + bool in_specification = true; + bool modifier_l = false; + while (in_specification) { + // Eat up characters until reaching a known specifier. + if (*++position == '\0') { + // The format string ended in the middle of a specification. Call + // it portable because no unportable specifications were found. The + // string is equally broken on all platforms. + return true; + } + + if (*position == 'l') { + // 'l' is the only thing that can save the 's' and 'c' specifiers. + modifier_l = true; + } else if (((*position == 's' || *position == 'c') && !modifier_l) || + *position == 'S' || *position == 'C' || *position == 'F' || + *position == 'D' || *position == 'O' || *position == 'U') { + // Not portable. + return false; + } + + if (wcschr(L"diouxXeEfgGaAcspn%", *position)) { + // Portable, keep scanning the rest of the format string. + in_specification = false; + } + } + } + } + + return true; +} + +} // namespace base + +static const wchar_t kWhitespaceWide[] = { + 0x0009, // <control-0009> to <control-000D> + 0x000A, 0x000B, 0x000C, 0x000D, + 0x0020, // Space + 0x0085, // <control-0085> + 0x00A0, // No-Break Space + 0x1680, // Ogham Space Mark + 0x180E, // Mongolian Vowel Separator + 0x2000, // En Quad to Hair Space + 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, + 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, + 0x200C, // Zero Width Non-Joiner + 0x2028, // Line Separator + 0x2029, // Paragraph Separator + 0x202F, // Narrow No-Break Space + 0x205F, // Medium Mathematical Space + 0x3000, // Ideographic Space + 0}; +static const char kWhitespaceASCII[] = { + 0x09, // <control-0009> to <control-000D> + 0x0A, 0x0B, 0x0C, 0x0D, + 0x20, // Space + 0}; + +template <typename STR> +TrimPositions TrimStringT(const STR& input, + const typename STR::value_type trim_chars[], + TrimPositions positions, STR* output) { + // Find the edges of leading/trailing whitespace as desired. + const typename STR::size_type last_char = input.length() - 1; + const typename STR::size_type first_good_char = + (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0; + const typename STR::size_type last_good_char = + (positions & TRIM_TRAILING) ? input.find_last_not_of(trim_chars) + : last_char; + + // When the string was all whitespace, report that we stripped off whitespace + // from whichever position the caller was interested in. For empty input, we + // stripped no whitespace, but we still need to clear |output|. + if (input.empty() || (first_good_char == STR::npos) || + (last_good_char == STR::npos)) { + bool input_was_empty = input.empty(); // in case output == &input + output->clear(); + return input_was_empty ? TRIM_NONE : positions; + } + + // Trim the whitespace. + *output = input.substr(first_good_char, last_good_char - first_good_char + 1); + + // Return where we trimmed from. + return static_cast<TrimPositions>( + ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | + ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); +} + +TrimPositions TrimWhitespace(const std::wstring& input, TrimPositions positions, + std::wstring* output) { + return TrimStringT(input, kWhitespaceWide, positions, output); +} + +TrimPositions TrimWhitespaceASCII(const std::string& input, + TrimPositions positions, + std::string* output) { + return TrimStringT(input, kWhitespaceASCII, positions, output); +} + +// This function is only for backward-compatibility. +// To be removed when all callers are updated. +TrimPositions TrimWhitespace(const std::string& input, TrimPositions positions, + std::string* output) { + return TrimWhitespaceASCII(input, positions, output); +} + +std::string WideToASCII(const std::wstring& wide) { + DCHECK(IsStringASCII(wide)); + return std::string(wide.begin(), wide.end()); +} + +std::wstring ASCIIToWide(const std::string& ascii) { + DCHECK(IsStringASCII(ascii)); + return std::wstring(ascii.begin(), ascii.end()); +} + +std::string UTF16ToASCII(const string16& utf16) { + DCHECK(IsStringASCII(utf16)); + return std::string(utf16.begin(), utf16.end()); +} + +string16 ASCIIToUTF16(const std::string& ascii) { + DCHECK(IsStringASCII(ascii)); + return string16(ascii.begin(), ascii.end()); +} + +template <class STR> +static bool DoIsStringASCII(const STR& str) { + for (size_t i = 0; i < str.length(); i++) { + typename ToUnsigned<typename STR::value_type>::Unsigned c = str[i]; + if (c > 0x7F) return false; + } + return true; +} + +bool IsStringASCII(const std::wstring& str) { return DoIsStringASCII(str); } + +#if !defined(XP_WIN) +bool IsStringASCII(const string16& str) { return DoIsStringASCII(str); } +#endif + +bool IsStringASCII(const std::string& str) { return DoIsStringASCII(str); } + +// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter +// is the size of the buffer. These return the number of characters in the +// formatted string excluding the NUL terminator. If the buffer is not +// large enough to accommodate the formatted string without truncation, they +// return the number of characters that would be in the fully-formatted string +// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms). +inline int vsnprintfT(char* buffer, size_t buf_size, const char* format, + va_list argptr) { + return base::vsnprintf(buffer, buf_size, format, argptr); +} + +inline int vsnprintfT(wchar_t* buffer, size_t buf_size, const wchar_t* format, + va_list argptr) { + return base::vswprintf(buffer, buf_size, format, argptr); +} + +// Templatized backend for StringPrintF/StringAppendF. This does not finalize +// the va_list, the caller is expected to do that. +template <class StringType> +static void StringAppendVT(StringType* dst, + const typename StringType::value_type* format, + va_list ap) { + // First try with a small fixed size buffer. + // This buffer size should be kept in sync with StringUtilTest.GrowBoundary + // and StringUtilTest.StringPrintfBounds. + typename StringType::value_type stack_buf[1024]; + + va_list backup_ap; + base_va_copy(backup_ap, ap); + +#if !defined(XP_WIN) + errno = 0; +#endif + int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, backup_ap); + va_end(backup_ap); + + if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) { + // It fit. + dst->append(stack_buf, result); + return; + } + + // Repeatedly increase buffer size until it fits. + int mem_length = arraysize(stack_buf); + while (true) { + if (result < 0) { +#if !defined(XP_WIN) + // On Windows, vsnprintfT always returns the number of characters in a + // fully-formatted string, so if we reach this point, something else is + // wrong and no amount of buffer-doubling is going to fix it. + if (errno != 0 && errno != EOVERFLOW) +#endif + { + // If an error other than overflow occurred, it's never going to work. + DLOG(WARNING) << "Unable to printf the requested string due to error."; + return; + } + // Try doubling the buffer size. + mem_length *= 2; + } else { + // We need exactly "result + 1" characters. + mem_length = result + 1; + } + + if (mem_length > 32 * 1024 * 1024) { + // That should be plenty, don't try anything larger. This protects + // against huge allocations when using vsnprintfT implementations that + // return -1 for reasons other than overflow without setting errno. + DLOG(WARNING) << "Unable to printf the requested string due to size."; + return; + } + + std::vector<typename StringType::value_type> mem_buf(mem_length); + + // Restore the va_list before we use it again. + base_va_copy(backup_ap, ap); + + result = vsnprintfT(&mem_buf[0], mem_length, format, ap); + va_end(backup_ap); + + if ((result >= 0) && (result < mem_length)) { + // It fit. + dst->append(&mem_buf[0], result); + return; + } + } +} + +namespace { + +template <typename STR, typename INT, typename UINT, bool NEG> +struct IntToStringT { + // This is to avoid a compiler warning about unary minus on unsigned type. + // For example, say you had the following code: + // template <typename INT> + // INT abs(INT value) { return value < 0 ? -value : value; } + // Even though if INT is unsigned, it's impossible for value < 0, so the + // unary minus will never be taken, the compiler will still generate a + // warning. We do a little specialization dance... + template <typename INT2, typename UINT2, bool NEG2> + struct ToUnsignedT {}; + + template <typename INT2, typename UINT2> + struct ToUnsignedT<INT2, UINT2, false> { + static UINT2 ToUnsigned(INT2 value) { return static_cast<UINT2>(value); } + }; + + template <typename INT2, typename UINT2> + struct ToUnsignedT<INT2, UINT2, true> { + static UINT2 ToUnsigned(INT2 value) { + return static_cast<UINT2>(value < 0 ? -value : value); + } + }; + + // This set of templates is very similar to the above templates, but + // for testing whether an integer is negative. + template <typename INT2, bool NEG2> + struct TestNegT {}; + template <typename INT2> + struct TestNegT<INT2, false> { + static bool TestNeg(INT2 value) { + // value is unsigned, and can never be negative. + return false; + } + }; + template <typename INT2> + struct TestNegT<INT2, true> { + static bool TestNeg(INT2 value) { return value < 0; } + }; + + static STR IntToString(INT value) { + // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. + // So round up to allocate 3 output characters per byte, plus 1 for '-'. + const int kOutputBufSize = 3 * sizeof(INT) + 1; + + // Allocate the whole string right away, we will right back to front, and + // then return the substr of what we ended up using. + STR outbuf(kOutputBufSize, 0); + + bool is_neg = TestNegT<INT, NEG>::TestNeg(value); + // Even though is_neg will never be true when INT is parameterized as + // unsigned, even the presence of the unary operation causes a warning. + UINT res = ToUnsignedT<INT, UINT, NEG>::ToUnsigned(value); + + for (typename STR::iterator it = outbuf.end();;) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast<typename STR::value_type>((res % 10) + '0'); + res /= 10; + + // We're done.. + if (res == 0) { + if (is_neg) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast<typename STR::value_type>('-'); + } + return STR(it, outbuf.end()); + } + } + NOTREACHED(); + return STR(); + } +}; + +} // namespace + +std::string IntToString(int value) { + return IntToStringT<std::string, int, unsigned int, true>::IntToString(value); +} +std::wstring IntToWString(int value) { + return IntToStringT<std::wstring, int, unsigned int, true>::IntToString( + value); +} +std::string UintToString(unsigned int value) { + return IntToStringT<std::string, unsigned int, unsigned int, + false>::IntToString(value); +} +std::wstring UintToWString(unsigned int value) { + return IntToStringT<std::wstring, unsigned int, unsigned int, + false>::IntToString(value); +} +std::string Int64ToString(int64_t value) { + return IntToStringT<std::string, int64_t, uint64_t, true>::IntToString(value); +} +std::wstring Int64ToWString(int64_t value) { + return IntToStringT<std::wstring, int64_t, uint64_t, true>::IntToString( + value); +} +std::string Uint64ToString(uint64_t value) { + return IntToStringT<std::string, uint64_t, uint64_t, false>::IntToString( + value); +} +std::wstring Uint64ToWString(uint64_t value) { + return IntToStringT<std::wstring, uint64_t, uint64_t, false>::IntToString( + value); +} + +// Lower-level routine that takes a va_list and appends to a specified +// string. All other routines are just convenience wrappers around it. +static void StringAppendV(std::string* dst, const char* format, va_list ap) { + StringAppendVT(dst, format, ap); +} + +static void StringAppendV(std::wstring* dst, const wchar_t* format, + va_list ap) { + StringAppendVT(dst, format, ap); +} + +std::string StringPrintf(const char* format, ...) { + va_list ap; + va_start(ap, format); + std::string result; + StringAppendV(&result, format, ap); + va_end(ap); + return result; +} + +std::wstring StringPrintf(const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + std::wstring result; + StringAppendV(&result, format, ap); + va_end(ap); + return result; +} + +const std::string& SStringPrintf(std::string* dst, const char* format, ...) { + va_list ap; + va_start(ap, format); + dst->clear(); + StringAppendV(dst, format, ap); + va_end(ap); + return *dst; +} + +const std::wstring& SStringPrintf(std::wstring* dst, const wchar_t* format, + ...) { + va_list ap; + va_start(ap, format); + dst->clear(); + StringAppendV(dst, format, ap); + va_end(ap); + return *dst; +} + +void StringAppendF(std::string* dst, const char* format, ...) { + va_list ap; + va_start(ap, format); + StringAppendV(dst, format, ap); + va_end(ap); +} + +void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + StringAppendV(dst, format, ap); + va_end(ap); +} + +template <typename STR> +static void SplitStringT(const STR& str, const typename STR::value_type s, + bool trim_whitespace, std::vector<STR>* r) { + size_t last = 0; + size_t i; + size_t c = str.size(); + for (i = 0; i <= c; ++i) { + if (i == c || str[i] == s) { + size_t len = i - last; + STR tmp = str.substr(last, len); + if (trim_whitespace) { + STR t_tmp; + TrimWhitespace(tmp, TRIM_ALL, &t_tmp); + r->push_back(t_tmp); + } else { + r->push_back(tmp); + } + last = i + 1; + } + } +} + +void SplitString(const std::wstring& str, wchar_t s, + std::vector<std::wstring>* r) { + SplitStringT(str, s, true, r); +} + +void SplitString(const std::string& str, char s, std::vector<std::string>* r) { + SplitStringT(str, s, true, r); +} + +// For the various *ToInt conversions, there are no *ToIntTraits classes to use +// because there's no such thing as strtoi. Use *ToLongTraits through a cast +// instead, requiring that long and int are compatible and equal-width. They +// are on our target platforms. + +// XXX Sigh. + +#if !defined(HAVE_64BIT_BUILD) +bool StringToInt(const std::string& input, int* output) { + COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_strtol_to_int); + return StringToNumber<StringToLongTraits>(input, + reinterpret_cast<long*>(output)); +} + +bool StringToInt(const string16& input, int* output) { + COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_wcstol_to_int); + return StringToNumber<String16ToLongTraits>(input, + reinterpret_cast<long*>(output)); +} + +#else +bool StringToInt(const std::string& input, int* output) { + long tmp; + bool ok = StringToNumber<StringToLongTraits>(input, &tmp); + if (!ok || tmp > kint32max) { + return false; + } + *output = static_cast<int>(tmp); + return true; +} + +bool StringToInt(const string16& input, int* output) { + long tmp; + bool ok = StringToNumber<String16ToLongTraits>(input, &tmp); + if (!ok || tmp > kint32max) { + return false; + } + *output = static_cast<int>(tmp); + return true; +} +#endif // !defined(HAVE_64BIT_BUILD) + +bool StringToInt64(const std::string& input, int64_t* output) { + return StringToNumber<StringToInt64Traits>(input, output); +} + +bool StringToInt64(const string16& input, int64_t* output) { + return StringToNumber<String16ToInt64Traits>(input, output); +} + +int StringToInt(const std::string& value) { + int result; + StringToInt(value, &result); + return result; +} + +int StringToInt(const string16& value) { + int result; + StringToInt(value, &result); + return result; +} + +int64_t StringToInt64(const std::string& value) { + int64_t result; + StringToInt64(value, &result); + return result; +} + +int64_t StringToInt64(const string16& value) { + int64_t result; + StringToInt64(value, &result); + return result; +} + +// The following code is compatible with the OpenBSD lcpy interface. See: +// http://www.gratisoft.us/todd/papers/strlcpy.html +// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c + +namespace { + +template <typename CHAR> +size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { + for (size_t i = 0; i < dst_size; ++i) { + if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. + return i; + } + + // We were left off at dst_size. We over copied 1 byte. Null terminate. + if (dst_size != 0) dst[dst_size - 1] = 0; + + // Count the rest of the |src|, and return it's length in characters. + while (src[dst_size]) ++dst_size; + return dst_size; +} + +} // namespace + +size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { + return lcpyT<char>(dst, src, dst_size); +} +size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { + return lcpyT<wchar_t>(dst, src, dst_size); +} diff --git a/ipc/chromium/src/base/string_util.h b/ipc/chromium/src/base/string_util.h new file mode 100644 index 0000000000..b6adcd96d7 --- /dev/null +++ b/ipc/chromium/src/base/string_util.h @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This file defines utility functions for working with strings. + +#ifndef BASE_STRING_UTIL_H_ +#define BASE_STRING_UTIL_H_ + +#include <stdarg.h> // va_list +#include <ctype.h> + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/string16.h" +#include "base/string_piece.h" // For implicit conversions. + +// Safe standard library wrappers for all platforms. + +namespace base { + +// C standard-library functions like "strncasecmp" and "snprintf" that aren't +// cross-platform are provided as "base::strncasecmp", and their prototypes +// are listed below. These functions are then implemented as inline calls +// to the platform-specific equivalents in the platform-specific headers. + +// Compare the two strings s1 and s2 without regard to case using +// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if +// s2 > s1 according to a lexicographic comparison. +int strcasecmp(const char* s1, const char* s2); + +// Compare up to count characters of s1 and s2 without regard to case using +// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if +// s2 > s1 according to a lexicographic comparison. +int strncasecmp(const char* s1, const char* s2, size_t count); + +// Wrapper for vsnprintf that always null-terminates and always returns the +// number of characters that would be in an untruncated formatted +// string, even when truncation occurs. +int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments); + +// vswprintf always null-terminates, but when truncation occurs, it will either +// return -1 or the number of characters that would be in an untruncated +// formatted string. The actual return value depends on the underlying +// C library's vswprintf implementation. +int vswprintf(wchar_t* buffer, size_t size, const wchar_t* format, + va_list arguments); + +// Some of these implementations need to be inlined. + +inline int snprintf(char* buffer, size_t size, const char* format, ...) { + va_list arguments; + va_start(arguments, format); + int result = vsnprintf(buffer, size, format, arguments); + va_end(arguments); + return result; +} + +inline int swprintf(wchar_t* buffer, size_t size, const wchar_t* format, ...) { + va_list arguments; + va_start(arguments, format); + int result = vswprintf(buffer, size, format, arguments); + va_end(arguments); + return result; +} + +// BSD-style safe and consistent string copy functions. +// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|. +// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as +// long as |dst_size| is not 0. Returns the length of |src| in characters. +// If the return value is >= dst_size, then the output was truncated. +// NOTE: All sizes are in number of characters, NOT in bytes. +size_t strlcpy(char* dst, const char* src, size_t dst_size); +size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size); + +// Scan a wprintf format string to determine whether it's portable across a +// variety of systems. This function only checks that the conversion +// specifiers used by the format string are supported and have the same meaning +// on a variety of systems. It doesn't check for other errors that might occur +// within a format string. +// +// Nonportable conversion specifiers for wprintf are: +// - 's' and 'c' without an 'l' length modifier. %s and %c operate on char +// data on all systems except Windows, which treat them as wchar_t data. +// Use %ls and %lc for wchar_t data instead. +// - 'S' and 'C', which operate on wchar_t data on all systems except Windows, +// which treat them as char data. Use %ls and %lc for wchar_t data +// instead. +// - 'F', which is not identified by Windows wprintf documentation. +// - 'D', 'O', and 'U', which are deprecated and not available on all systems. +// Use %ld, %lo, and %lu instead. +// +// Note that there is no portable conversion specifier for char data when +// working with wprintf. +// +// This function is intended to be called from base::vswprintf. +bool IsWprintfFormatPortable(const wchar_t* format); + +} // namespace base + +#if defined(XP_WIN) +# include "base/string_util_win.h" +#else +# include "base/string_util_posix.h" +#endif + +// Trims any whitespace from either end of the input string. Returns where +// whitespace was found. +// The non-wide version has two functions: +// * TrimWhitespaceASCII() +// This function is for ASCII strings and only looks for ASCII whitespace; +// * TrimWhitespaceUTF8() +// This function is for UTF-8 strings and looks for Unicode whitespace. +// Please choose the best one according to your usage. +// NOTE: Safe to use the same variable for both input and output. +enum TrimPositions { + TRIM_NONE = 0, + TRIM_LEADING = 1 << 0, + TRIM_TRAILING = 1 << 1, + TRIM_ALL = TRIM_LEADING | TRIM_TRAILING +}; +TrimPositions TrimWhitespace(const std::wstring& input, TrimPositions positions, + std::wstring* output); +TrimPositions TrimWhitespaceASCII(const std::string& input, + TrimPositions positions, std::string* output); + +// Deprecated. This function is only for backward compatibility and calls +// TrimWhitespaceASCII(). +TrimPositions TrimWhitespace(const std::string& input, TrimPositions positions, + std::string* output); + +// Searches for CR or LF characters. Removes all contiguous whitespace +// strings that contain them. This is useful when trying to deal with text +// copied from terminals. +// Returns |text, with the following three transformations: +// (1) Leading and trailing whitespace is trimmed. +// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace +// sequences containing a CR or LF are trimmed. +// (3) All other whitespace sequences are converted to single spaces. +std::wstring CollapseWhitespace(const std::wstring& text, + bool trim_sequences_with_line_breaks); + +// These convert between ASCII (7-bit) and Wide/UTF16 strings. +std::string WideToASCII(const std::wstring& wide); +std::wstring ASCIIToWide(const std::string& ascii); +std::string UTF16ToASCII(const string16& utf16); +string16 ASCIIToUTF16(const std::string& ascii); + +// These convert between UTF-8, -16, and -32 strings. They are potentially slow, +// so avoid unnecessary conversions. The low-level versions return a boolean +// indicating whether the conversion was 100% valid. In this case, it will still +// do the best it can and put the result in the output buffer. The versions that +// return strings ignore this error and just return the best conversion +// possible. +bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output); +std::string WideToUTF8(const std::wstring& wide); +bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output); +std::wstring UTF8ToWide(const ::StringPiece& utf8); + +bool IsStringASCII(const std::wstring& str); +bool IsStringASCII(const std::string& str); +bool IsStringASCII(const string16& str); + +// Specialized string-conversion functions. +std::string IntToString(int value); +std::wstring IntToWString(int value); +std::string UintToString(unsigned int value); +std::wstring UintToWString(unsigned int value); +std::string Int64ToString(int64_t value); +std::wstring Int64ToWString(int64_t value); +std::string Uint64ToString(uint64_t value); +std::wstring Uint64ToWString(uint64_t value); +// The DoubleToString methods convert the double to a string format that +// ignores the locale. If you want to use locale specific formatting, use ICU. +std::string DoubleToString(double value); +std::wstring DoubleToWString(double value); + +// Perform a best-effort conversion of the input string to a numeric type, +// setting |*output| to the result of the conversion. Returns true for +// "perfect" conversions; returns false in the following cases: +// - Overflow/underflow. |*output| will be set to the maximum value supported +// by the data type. +// - Trailing characters in the string after parsing the number. |*output| +// will be set to the value of the number that was parsed. +// - No characters parseable as a number at the beginning of the string. +// |*output| will be set to 0. +// - Empty string. |*output| will be set to 0. +bool StringToInt(const std::string& input, int* output); +bool StringToInt(const string16& input, int* output); +bool StringToInt64(const std::string& input, int64_t* output); +bool StringToInt64(const string16& input, int64_t* output); + +// Convenience forms of the above, when the caller is uninterested in the +// boolean return value. These return only the |*output| value from the +// above conversions: a best-effort conversion when possible, otherwise, 0. +int StringToInt(const std::string& value); +int StringToInt(const string16& value); +int64_t StringToInt64(const std::string& value); +int64_t StringToInt64(const string16& value); + +// Return a C++ string given printf-like input. +std::string StringPrintf(const char* format, ...); +std::wstring StringPrintf(const wchar_t* format, ...); + +// Store result into a supplied string and return it +const std::string& SStringPrintf(std::string* dst, const char* format, ...); +const std::wstring& SStringPrintf(std::wstring* dst, const wchar_t* format, + ...); + +// Append result to a supplied string +void StringAppendF(std::string* dst, const char* format, ...); +void StringAppendF(std::wstring* dst, const wchar_t* format, ...); + +//----------------------------------------------------------------------------- + +// Splits |str| into a vector of strings delimited by |s|. Append the results +// into |r| as they appear. If several instances of |s| are contiguous, or if +// |str| begins with or ends with |s|, then an empty string is inserted. +// +// Every substring is trimmed of any leading or trailing white space. +void SplitString(const std::wstring& str, wchar_t s, + std::vector<std::wstring>* r); +void SplitString(const std::string& str, char s, std::vector<std::string>* r); + +#endif // BASE_STRING_UTIL_H_ diff --git a/ipc/chromium/src/base/string_util_posix.h b/ipc/chromium/src/base/string_util_posix.h new file mode 100644 index 0000000000..02818a6441 --- /dev/null +++ b/ipc/chromium/src/base/string_util_posix.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRING_UTIL_POSIX_H_ +#define BASE_STRING_UTIL_POSIX_H_ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <wchar.h> + +#include "base/logging.h" + +namespace base { + +// Chromium code style is to not use malloc'd strings; this is only for use +// for interaction with APIs that require it. +inline char* strdup(const char* str) { return ::strdup(str); } + +inline int strcasecmp(const char* string1, const char* string2) { + return ::strcasecmp(string1, string2); +} + +inline int strncasecmp(const char* string1, const char* string2, size_t count) { + return ::strncasecmp(string1, string2, count); +} + +inline int vsnprintf(char* buffer, size_t size, const char* format, + va_list arguments) { + return ::vsnprintf(buffer, size, format, arguments); +} + +inline int vswprintf(wchar_t* buffer, size_t size, const wchar_t* format, + va_list arguments) { + DCHECK(IsWprintfFormatPortable(format)); + return ::vswprintf(buffer, size, format, arguments); +} + +} // namespace base + +#endif // BASE_STRING_UTIL_POSIX_H_ diff --git a/ipc/chromium/src/base/string_util_win.h b/ipc/chromium/src/base/string_util_win.h new file mode 100644 index 0000000000..7f8586889b --- /dev/null +++ b/ipc/chromium/src/base/string_util_win.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRING_UTIL_WIN_H_ +#define BASE_STRING_UTIL_WIN_H_ + +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <wchar.h> + +#include "base/logging.h" + +namespace base { + +// Chromium code style is to not use malloc'd strings; this is only for use +// for interaction with APIs that require it. +inline char* strdup(const char* str) { return _strdup(str); } + +inline int vsnprintf(char* buffer, size_t size, const char* format, + va_list arguments) { + int length = vsnprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) return _vscprintf(format, arguments); + return length; +} + +inline int vswprintf(wchar_t* buffer, size_t size, const wchar_t* format, + va_list arguments) { + DCHECK(IsWprintfFormatPortable(format)); + + int length = _vsnwprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) return _vscwprintf(format, arguments); + return length; +} + +} // namespace base + +#endif // BASE_STRING_UTIL_WIN_H_ diff --git a/ipc/chromium/src/base/sys_string_conversions.h b/ipc/chromium/src/base/sys_string_conversions.h new file mode 100644 index 0000000000..e3a51964a7 --- /dev/null +++ b/ipc/chromium/src/base/sys_string_conversions.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SYS_STRING_CONVERSIONS_H_ +#define BASE_SYS_STRING_CONVERSIONS_H_ + +// Provides system-dependent string type conversions for cases where it's +// necessary to not use ICU. Generally, you should not need this in Chrome, +// but it is used in some shared code. Dependencies should be minimal. + +#include <string> +#include "base/basictypes.h" +#include "base/string16.h" + +class StringPiece; + +namespace base { + +// Converts between wide and UTF-8 representations of a string. On error, the +// result is system-dependent. +std::string SysWideToUTF8(const std::wstring& wide); +std::wstring SysUTF8ToWide(const StringPiece& utf8); + +// Converts between wide and the system multi-byte representations of a string. +// DANGER: This will lose information and can change (on Windows, this can +// change between reboots). +std::string SysWideToNativeMB(const std::wstring& wide); +std::wstring SysNativeMBToWide(const StringPiece& native_mb); + +} // namespace base + +#endif // BASE_SYS_STRING_CONVERSIONS_H_ diff --git a/ipc/chromium/src/base/sys_string_conversions_mac.mm b/ipc/chromium/src/base/sys_string_conversions_mac.mm new file mode 100644 index 0000000000..c33ffef6bc --- /dev/null +++ b/ipc/chromium/src/base/sys_string_conversions_mac.mm @@ -0,0 +1,126 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sys_string_conversions.h" + +#import <Foundation/Foundation.h> + +#include <vector> + +#include "base/scoped_cftyperef.h" +#include "base/string_piece.h" + +namespace base { + +namespace { + +// Convert the supplied CFString into the specified encoding, and return it as +// an STL string of the template type. Returns an empty string on failure. +// +// Do not assert in this function since it is used by the asssertion code! +template <typename StringType> +static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring, + CFStringEncoding encoding) { + CFIndex length = CFStringGetLength(cfstring); + if (length == 0) return StringType(); + + CFRange whole_string = CFRangeMake(0, length); + CFIndex out_size; + CFIndex converted = CFStringGetBytes(cfstring, whole_string, encoding, + 0, // lossByte + false, // isExternalRepresentation + NULL, // buffer + 0, // maxBufLen + &out_size); + if (converted == 0 || out_size == 0) return StringType(); + + // out_size is the number of UInt8-sized units needed in the destination. + // A buffer allocated as UInt8 units might not be properly aligned to + // contain elements of StringType::value_type. Use a container for the + // proper value_type, and convert out_size by figuring the number of + // value_type elements per UInt8. Leave room for a NUL terminator. + typename StringType::size_type elements = + out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1; + + std::vector<typename StringType::value_type> out_buffer(elements); + converted = + CFStringGetBytes(cfstring, whole_string, encoding, + 0, // lossByte + false, // isExternalRepresentation + reinterpret_cast<UInt8*>(&out_buffer[0]), out_size, + NULL); // usedBufLen + if (converted == 0) return StringType(); + + out_buffer[elements - 1] = '\0'; + return StringType(&out_buffer[0], elements - 1); +} + +// Given an STL string |in| with an encoding specified by |in_encoding|, +// convert it to |out_encoding| and return it as an STL string of the +// |OutStringType| template type. Returns an empty string on failure. +// +// Do not assert in this function since it is used by the asssertion code! +template <typename InStringType, typename OutStringType> +static OutStringType STLStringToSTLStringWithEncodingsT( + const InStringType& in, CFStringEncoding in_encoding, + CFStringEncoding out_encoding) { + typename InStringType::size_type in_length = in.length(); + if (in_length == 0) return OutStringType(); + + scoped_cftyperef<CFStringRef> cfstring(CFStringCreateWithBytesNoCopy( + NULL, reinterpret_cast<const UInt8*>(in.data()), + in_length * sizeof(typename InStringType::value_type), in_encoding, false, + kCFAllocatorNull)); + if (!cfstring) return OutStringType(); + + return CFStringToSTLStringWithEncodingT<OutStringType>(cfstring, + out_encoding); +} + +// Given an STL string |in| with an encoding specified by |in_encoding|, +// return it as a CFStringRef. Returns NULL on failure. +template <typename StringType> +static CFStringRef STLStringToCFStringWithEncodingsT( + const StringType& in, CFStringEncoding in_encoding) { + typename StringType::size_type in_length = in.length(); + if (in_length == 0) return CFSTR(""); + + return CFStringCreateWithBytes( + kCFAllocatorDefault, reinterpret_cast<const UInt8*>(in.data()), + in_length * sizeof(typename StringType::value_type), in_encoding, false); +} + +// Specify the byte ordering explicitly, otherwise CFString will be confused +// when strings don't carry BOMs, as they typically won't. +static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8; + +#ifdef __BIG_ENDIAN__ +static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE; +#elif defined(__LITTLE_ENDIAN__) +static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE; +#endif // __LITTLE_ENDIAN__ + +} // namespace + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToUTF8(const std::wstring& wide) { + return STLStringToSTLStringWithEncodingsT<std::wstring, std::string>( + wide, kWideStringEncoding, kNarrowStringEncoding); +} + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysUTF8ToWide(const StringPiece& utf8) { + return STLStringToSTLStringWithEncodingsT<StringPiece, std::wstring>( + utf8, kNarrowStringEncoding, kWideStringEncoding); +} + +std::string SysWideToNativeMB(const std::wstring& wide) { + return SysWideToUTF8(wide); +} + +std::wstring SysNativeMBToWide(const StringPiece& native_mb) { + return SysUTF8ToWide(native_mb); +} + +} // namespace base diff --git a/ipc/chromium/src/base/sys_string_conversions_win.cc b/ipc/chromium/src/base/sys_string_conversions_win.cc new file mode 100644 index 0000000000..148685ca7e --- /dev/null +++ b/ipc/chromium/src/base/sys_string_conversions_win.cc @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sys_string_conversions.h" + +#include <windows.h> + +#include "base/string_piece.h" + +namespace base { + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysMultiByteToWide(const StringPiece& mb, uint32_t code_page) { + if (mb.empty()) return std::wstring(); + + int mb_length = static_cast<int>(mb.length()); + // Compute the length of the buffer. + int charcount = + MultiByteToWideChar(code_page, 0, mb.data(), mb_length, NULL, 0); + if (charcount == 0) return std::wstring(); + + std::wstring wide; + wide.resize(charcount); + MultiByteToWideChar(code_page, 0, mb.data(), mb_length, &wide[0], charcount); + + return wide; +} + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToMultiByte(const std::wstring& wide, uint32_t code_page) { + int wide_length = static_cast<int>(wide.length()); + if (wide_length == 0) return std::string(); + + // Compute the length of the buffer we'll need. + int charcount = WideCharToMultiByte(code_page, 0, wide.data(), wide_length, + NULL, 0, NULL, NULL); + if (charcount == 0) return std::string(); + + std::string mb; + mb.resize(charcount); + WideCharToMultiByte(code_page, 0, wide.data(), wide_length, &mb[0], charcount, + NULL, NULL); + + return mb; +} + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToUTF8(const std::wstring& wide) { + return SysWideToMultiByte(wide, CP_UTF8); +} + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysUTF8ToWide(const StringPiece& utf8) { + return SysMultiByteToWide(utf8, CP_UTF8); +} + +std::string SysWideToNativeMB(const std::wstring& wide) { + return SysWideToMultiByte(wide, CP_ACP); +} + +std::wstring SysNativeMBToWide(const StringPiece& native_mb) { + return SysMultiByteToWide(native_mb, CP_ACP); +} + +} // namespace base diff --git a/ipc/chromium/src/base/task.h b/ipc/chromium/src/base/task.h new file mode 100644 index 0000000000..9b1ae7feff --- /dev/null +++ b/ipc/chromium/src/base/task.h @@ -0,0 +1,227 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TASK_H_ +#define BASE_TASK_H_ + +#include "base/revocable_store.h" +#include "base/tuple.h" + +#include "nsISupportsImpl.h" +#include "nsThreadUtils.h" + +#include <type_traits> +#include <utility> + +// Helper functions so that we can call a function a pass it arguments that come +// from a Tuple. + +namespace details { + +// Call the given method on the given object. Arguments are passed by move +// semantics from the given tuple. If the tuple has length N, the sequence must +// be IndexSequence<0, 1, ..., N-1>. +template <size_t... Indices, class ObjT, class Method, typename... Args> +void CallMethod(std::index_sequence<Indices...>, ObjT* obj, Method method, + std::tuple<Args...>& arg) { + (obj->*method)(std::move(std::get<Indices>(arg))...); +} + +// Same as above, but call a function. +template <size_t... Indices, typename Function, typename... Args> +void CallFunction(std::index_sequence<Indices...>, Function function, + std::tuple<Args...>& arg) { + (*function)(std::move(std::get<Indices>(arg))...); +} + +} // namespace details + +// Call a method on the given object. Arguments are passed by move semantics +// from the given tuple. +template <class ObjT, class Method, typename... Args> +void DispatchTupleToMethod(ObjT* obj, Method method, std::tuple<Args...>& arg) { + details::CallMethod(std::index_sequence_for<Args...>{}, obj, method, arg); +} + +// Same as above, but call a function. +template <typename Function, typename... Args> +void DispatchTupleToFunction(Function function, std::tuple<Args...>& arg) { + details::CallFunction(std::index_sequence_for<Args...>{}, function, arg); +} + +// General task implementations ------------------------------------------------ + +// Task to delete an object +template <class T> +class DeleteTask : public mozilla::CancelableRunnable { + public: + explicit DeleteTask(T* obj) + : mozilla::CancelableRunnable("DeleteTask"), obj_(obj) {} + NS_IMETHOD Run() override { + delete obj_; + return NS_OK; + } + virtual nsresult Cancel() override { + obj_ = NULL; + return NS_OK; + } + + private: + T* MOZ_UNSAFE_REF( + "The validity of this pointer must be enforced by " + "external factors.") obj_; +}; + +// RunnableMethodTraits -------------------------------------------------------- +// +// This traits-class is used by RunnableMethod to manage the lifetime of the +// callee object. By default, it is assumed that the callee supports AddRef +// and Release methods. A particular class can specialize this template to +// define other lifetime management. For example, if the callee is known to +// live longer than the RunnableMethod object, then a RunnableMethodTraits +// struct could be defined with empty RetainCallee and ReleaseCallee methods. + +template <class T> +struct RunnableMethodTraits { + static void RetainCallee(T* obj) { obj->AddRef(); } + static void ReleaseCallee(T* obj) { obj->Release(); } +}; + +// This allows using the NewRunnableMethod() functions with a const pointer +// to the callee object. See the similar support in nsRefPtr for a rationale +// of why this is reasonable. +template <class T> +struct RunnableMethodTraits<const T> { + static void RetainCallee(const T* obj) { const_cast<T*>(obj)->AddRef(); } + static void ReleaseCallee(const T* obj) { const_cast<T*>(obj)->Release(); } +}; + +// RunnableMethod and RunnableFunction ----------------------------------------- +// +// Runnable methods are a type of task that call a function on an object when +// they are run. We implement both an object and a set of NewRunnableMethod and +// NewRunnableFunction functions for convenience. These functions are +// overloaded and will infer the template types, simplifying calling code. +// +// The template definitions all use the following names: +// T - the class type of the object you're supplying +// this is not needed for the Static version of the call +// Method/Function - the signature of a pointer to the method or function you +// want to call +// Param - the parameter(s) to the method, possibly packed as a Tuple +// A - the first parameter (if any) to the method +// B - the second parameter (if any) to the mathod +// +// Put these all together and you get an object that can call a method whose +// signature is: +// R T::MyFunction([A[, B]]) +// +// Usage: +// PostTask(NewRunnableMethod(object, &Object::method[, a[, b]]) +// PostTask(NewRunnableFunction(&function[, a[, b]]) + +// RunnableMethod and NewRunnableMethod implementation ------------------------- + +template <class T, class Method, class Params> +class RunnableMethod : public mozilla::CancelableRunnable, + public RunnableMethodTraits<T> { + public: + RunnableMethod(T* obj, Method meth, Params&& params) + : mozilla::CancelableRunnable("RunnableMethod"), + obj_(obj), + meth_(meth), + params_(std::forward<Params>(params)) { + this->RetainCallee(obj_); + } + ~RunnableMethod() { ReleaseCallee(); } + + NS_IMETHOD Run() override { + if (obj_) DispatchTupleToMethod(obj_, meth_, params_); + return NS_OK; + } + + virtual nsresult Cancel() override { + ReleaseCallee(); + return NS_OK; + } + + private: + void ReleaseCallee() { + if (obj_) { + RunnableMethodTraits<T>::ReleaseCallee(obj_); + obj_ = nullptr; + } + } + + // This is owning because of the RetainCallee and ReleaseCallee calls in the + // constructor and destructor. + T* MOZ_OWNING_REF obj_; + Method meth_; + Params params_; +}; + +namespace dont_add_new_uses_of_this { + +// Don't add new uses of this!!!! +template <class T, class Method, typename... Args> +inline already_AddRefed<mozilla::Runnable> NewRunnableMethod(T* object, + Method method, + Args&&... args) { + typedef std::tuple<std::decay_t<Args>...> ArgsTuple; + RefPtr<mozilla::Runnable> t = new RunnableMethod<T, Method, ArgsTuple>( + object, method, std::make_tuple(std::forward<Args>(args)...)); + return t.forget(); +} + +} // namespace dont_add_new_uses_of_this + +// RunnableFunction and NewRunnableFunction implementation --------------------- + +template <class Function, class Params> +class RunnableFunction : public mozilla::CancelableRunnable { + public: + RunnableFunction(const char* name, Function function, Params&& params) + : mozilla::CancelableRunnable(name), + function_(function), + params_(std::forward<Params>(params)) {} + + ~RunnableFunction() {} + + NS_IMETHOD Run() override { + if (function_) DispatchTupleToFunction(function_, params_); + return NS_OK; + } + + virtual nsresult Cancel() override { + function_ = nullptr; + return NS_OK; + } + + Function function_; + Params params_; +}; + +template <class Function, typename... Args> +inline already_AddRefed<mozilla::CancelableRunnable> +NewCancelableRunnableFunction(const char* name, Function function, + Args&&... args) { + typedef std::tuple<std::decay_t<Args>...> ArgsTuple; + RefPtr<mozilla::CancelableRunnable> t = + new RunnableFunction<Function, ArgsTuple>( + name, function, std::make_tuple(std::forward<Args>(args)...)); + return t.forget(); +} + +template <class Function, typename... Args> +inline already_AddRefed<mozilla::Runnable> NewRunnableFunction( + const char* name, Function function, Args&&... args) { + typedef std::tuple<std::decay_t<Args>...> ArgsTuple; + RefPtr<mozilla::Runnable> t = new RunnableFunction<Function, ArgsTuple>( + name, function, std::make_tuple(std::forward<Args>(args)...)); + return t.forget(); +} + +#endif // BASE_TASK_H_ diff --git a/ipc/chromium/src/base/thread.cc b/ipc/chromium/src/base/thread.cc new file mode 100644 index 0000000000..c856272162 --- /dev/null +++ b/ipc/chromium/src/base/thread.cc @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread.h" + +#include "base/string_util.h" +#include "base/thread_local.h" +#include "base/waitable_event.h" +#include "GeckoProfiler.h" +#include "mozilla/EventQueue.h" +#include "mozilla/IOInterposer.h" +#include "mozilla/ThreadEventQueue.h" +#include "nsThreadUtils.h" +#include "nsThreadManager.h" + +namespace base { + +// This task is used to trigger the message loop to exit. +class ThreadQuitTask : public mozilla::Runnable { + public: + ThreadQuitTask() : mozilla::Runnable("ThreadQuitTask") {} + NS_IMETHOD Run() override { + MessageLoop::current()->Quit(); + Thread::SetThreadWasQuitProperly(true); + return NS_OK; + } +}; + +// Used to pass data to ThreadMain. This structure is allocated on the stack +// from within StartWithOptions. +struct Thread::StartupData { + // We get away with a const reference here because of how we are allocated. + const Thread::Options& options; + + // Used to synchronize thread startup. + WaitableEvent event; + + explicit StartupData(const Options& opt) + : options(opt), event(false, false) {} +}; + +Thread::Thread(const char* name) + : startup_data_(NULL), + thread_(0), + message_loop_(NULL), + thread_id_(0), + name_(name) { + MOZ_COUNT_CTOR(base::Thread); +} + +Thread::~Thread() { + MOZ_COUNT_DTOR(base::Thread); + Stop(); +} + +namespace { + +// We use this thread-local variable to record whether or not a thread exited +// because its Stop method was called. This allows us to catch cases where +// MessageLoop::Quit() is called directly, which is unexpected when using a +// Thread to setup and run a MessageLoop. + +static base::ThreadLocalBoolean& get_tls_bool() { + static base::ThreadLocalBoolean tls_ptr; + return tls_ptr; +} + +} // namespace + +void Thread::SetThreadWasQuitProperly(bool flag) { get_tls_bool().Set(flag); } + +bool Thread::GetThreadWasQuitProperly() { + bool quit_properly = true; +#ifndef NDEBUG + quit_properly = get_tls_bool().Get(); +#endif + return quit_properly; +} + +bool Thread::Start() { return StartWithOptions(Options()); } + +bool Thread::StartWithOptions(const Options& options) { + DCHECK(!message_loop_); + + SetThreadWasQuitProperly(false); + + StartupData startup_data(options); + startup_data_ = &startup_data; + + if (!PlatformThread::Create(options.stack_size, this, &thread_)) { + DLOG(ERROR) << "failed to create thread"; + startup_data_ = NULL; // Record that we failed to start. + return false; + } + + // Wait for the thread to start and initialize message_loop_ + startup_data.event.Wait(); + + DCHECK(message_loop_); + return true; +} + +void Thread::Stop() { + if (!thread_was_started()) return; + + // We should only be called on the same thread that started us. + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); + + // StopSoon may have already been called. + if (message_loop_) { + RefPtr<ThreadQuitTask> task = new ThreadQuitTask(); + message_loop_->PostTask(task.forget()); + } + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + // + // TODO(darin): Unfortunately, we need to keep message_loop_ around until + // the thread exits. Some consumers are abusing the API. Make them stop. + // + PlatformThread::Join(thread_); + + // The thread can't receive messages anymore. + message_loop_ = NULL; + + // The thread no longer needs to be joined. + startup_data_ = NULL; +} + +void Thread::StopSoon() { + if (!message_loop_) return; + + // We should only be called on the same thread that started us. + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); + + // We had better have a message loop at this point! If we do not, then it + // most likely means that the thread terminated unexpectedly, probably due + // to someone calling Quit() on our message loop directly. + DCHECK(message_loop_); + + RefPtr<ThreadQuitTask> task = new ThreadQuitTask(); + message_loop_->PostTask(task.forget()); +} + +void Thread::ThreadMain() { + nsCOMPtr<nsIThread> xpcomThread; + auto loopType = startup_data_->options.message_loop_type; + if (loopType == MessageLoop::TYPE_MOZILLA_NONMAINTHREAD || + loopType == MessageLoop::TYPE_MOZILLA_NONMAINUITHREAD) { + auto queue = mozilla::MakeRefPtr<mozilla::ThreadEventQueue>( + mozilla::MakeUnique<mozilla::EventQueue>()); + xpcomThread = nsThreadManager::get().CreateCurrentThread(queue); + } else { + xpcomThread = NS_GetCurrentThread(); + } + + AUTO_PROFILER_REGISTER_THREAD(name_.c_str()); + mozilla::IOInterposer::RegisterCurrentThread(); + + // The message loop for this thread. + MessageLoop message_loop(startup_data_->options.message_loop_type, + xpcomThread); + + xpcomThread = nullptr; + + // Complete the initialization of our Thread object. + thread_id_ = PlatformThread::CurrentId(); + PlatformThread::SetName(name_.c_str()); + NS_SetCurrentThreadName(name_.c_str()); + message_loop.set_thread_name(name_); + message_loop.set_hang_timeouts(startup_data_->options.transient_hang_timeout, + startup_data_->options.permanent_hang_timeout); + message_loop_ = &message_loop; + + // Let the thread do extra initialization. + // Let's do this before signaling we are started. + Init(); + + startup_data_->event.Signal(); + // startup_data_ can't be touched anymore since the starting thread is now + // unlocked. + + message_loop.Run(); + + // Let the thread do extra cleanup. + CleanUp(); + + // Assert that MessageLoop::Quit was called by ThreadQuitTask. + DCHECK(GetThreadWasQuitProperly()); + + mozilla::IOInterposer::UnregisterCurrentThread(); + + // We can't receive messages anymore. + message_loop_ = NULL; + thread_id_ = 0; +} + +} // namespace base diff --git a/ipc/chromium/src/base/thread.h b/ipc/chromium/src/base/thread.h new file mode 100644 index 0000000000..7b9a5975d3 --- /dev/null +++ b/ipc/chromium/src/base/thread.h @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREAD_H_ +#define BASE_THREAD_H_ + +#include <stdint.h> +#include <string> + +#include "base/message_loop.h" +#include "base/platform_thread.h" + +namespace base { + +// A simple thread abstraction that establishes a MessageLoop on a new thread. +// The consumer uses the MessageLoop of the thread to cause code to execute on +// the thread. When this object is destroyed the thread is terminated. All +// pending tasks queued on the thread's message loop will run to completion +// before the thread is terminated. +class Thread : PlatformThread::Delegate { + public: + struct Options { + // Specifies the type of message loop that will be allocated on the thread. + MessageLoop::Type message_loop_type; + + // Specifies the maximum stack size that the thread is allowed to use. + // This does not necessarily correspond to the thread's initial stack size. + // A value of 0 indicates that the default maximum should be used. + size_t stack_size; + + // Specifies the transient and permanent hang timeouts for background hang + // monitoring. A value of 0 indicates there is no timeout. + uint32_t transient_hang_timeout; + uint32_t permanent_hang_timeout; + + Options() + : message_loop_type(MessageLoop::TYPE_DEFAULT), + stack_size(0), + transient_hang_timeout(0), + permanent_hang_timeout(0) {} + Options(MessageLoop::Type type, size_t size) + : message_loop_type(type), + stack_size(size), + transient_hang_timeout(0), + permanent_hang_timeout(0) {} + }; + + // Constructor. + // name is a display string to identify the thread. + explicit Thread(const char* name); + + // Destroys the thread, stopping it if necessary. + // + // NOTE: If you are subclassing from Thread, and you wish for your CleanUp + // method to be called, then you need to call Stop() from your destructor. + // + virtual ~Thread(); + + // Starts the thread. Returns true if the thread was successfully started; + // otherwise, returns false. Upon successful return, the message_loop() + // getter will return non-null. + // + // Note: This function can't be called on Windows with the loader lock held; + // i.e. during a DllMain, global object construction or destruction, atexit() + // callback. + bool Start(); + + // Starts the thread. Behaves exactly like Start in addition to allow to + // override the default options. + // + // Note: This function can't be called on Windows with the loader lock held; + // i.e. during a DllMain, global object construction or destruction, atexit() + // callback. + bool StartWithOptions(const Options& options); + + // Signals the thread to exit and returns once the thread has exited. After + // this method returns, the Thread object is completely reset and may be used + // as if it were newly constructed (i.e., Start may be called again). + // + // Stop may be called multiple times and is simply ignored if the thread is + // already stopped. + // + // NOTE: This method is optional. It is not strictly necessary to call this + // method as the Thread's destructor will take care of stopping the thread if + // necessary. + // + void Stop(); + + // Signals the thread to exit in the near future. + // + // WARNING: This function is not meant to be commonly used. Use at your own + // risk. Calling this function will cause message_loop() to become invalid in + // the near future. This function was created to workaround a specific + // deadlock on Windows with printer worker thread. In any other case, Stop() + // should be used. + // + // StopSoon should not be called multiple times as it is risky to do so. It + // could cause a timing issue in message_loop() access. Call Stop() to reset + // the thread object once it is known that the thread has quit. + void StopSoon(); + + // Returns the message loop for this thread. Use the MessageLoop's + // PostTask methods to execute code on the thread. This only returns + // non-null after a successful call to Start. After Stop has been called, + // this will return NULL. + // + // NOTE: You must not call this MessageLoop's Quit method directly. Use + // the Thread's Stop method instead. + // + MessageLoop* message_loop() const { return message_loop_; } + + // Set the name of this thread (for display in debugger too). + const std::string& thread_name() { return name_; } + + // The native thread handle. + PlatformThreadHandle thread_handle() { return thread_; } + + // The thread ID. + PlatformThreadId thread_id() const { return thread_id_; } + + // Reset thread ID as current thread. + PlatformThreadId reset_thread_id() { + thread_id_ = PlatformThread::CurrentId(); + return thread_id_; + } + + // Returns true if the thread has been started, and not yet stopped. + // When a thread is running, the thread_id_ is non-zero. + bool IsRunning() const { return thread_id_ != 0; } + + protected: + // Called just prior to starting the message loop + virtual void Init() {} + + // Called just after the message loop ends + virtual void CleanUp() {} + + static void SetThreadWasQuitProperly(bool flag); + static bool GetThreadWasQuitProperly(); + + private: + // PlatformThread::Delegate methods: + virtual void ThreadMain() override; + + // We piggy-back on the startup_data_ member to know if we successfully + // started the thread. This way we know that we need to call Join. + bool thread_was_started() const { return startup_data_ != NULL; } + + // Used to pass data to ThreadMain. + struct StartupData; + StartupData* startup_data_; + + // The thread's handle. + PlatformThreadHandle thread_; + + // The thread's message loop. Valid only while the thread is alive. Set + // by the created thread. + MessageLoop* message_loop_; + + // Our thread's ID. + PlatformThreadId thread_id_; + + // The name of the thread. Used for debugging purposes. + std::string name_; + + friend class ThreadQuitTask; + + DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +} // namespace base + +#endif // BASE_THREAD_H_ diff --git a/ipc/chromium/src/base/thread_local.h b/ipc/chromium/src/base/thread_local.h new file mode 100644 index 0000000000..c031875d01 --- /dev/null +++ b/ipc/chromium/src/base/thread_local.h @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WARNING: Thread local storage is a bit tricky to get right. Please make +// sure that this is really the proper solution for what you're trying to +// achieve. Don't prematurely optimize, most likely you can just use a Lock. +// +// These classes implement a warpper around the platform's TLS storage +// mechanism. On construction, they will allocate a TLS slot, and free the +// TLS slot on destruction. No memory management (creation or destruction) is +// handled. This means for uses of ThreadLocalPointer, you must correctly +// manage the memory yourself, these classes will not destroy the pointer for +// you. There are no at-thread-exit actions taken by these classes. +// +// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or +// destruction, so memory management must be handled elsewhere. The first call +// to Get() on a thread will return NULL. You can update the pointer with a +// call to Set(). +// +// ThreadLocalBoolean wraps a bool. It will default to false if it has never +// been set otherwise with Set(). +// +// Thread Safety: An instance of ThreadLocalStorage is completely thread safe +// once it has been created. If you want to dynamically create an instance, +// you must of course properly deal with safety and race conditions. This +// means a function-level static initializer is generally inappropiate. +// +// Example usage: +// // My class is logically attached to a single thread. We cache a pointer +// // on the thread it was created on, so we can implement current(). +// MyClass::MyClass() { +// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL); +// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this); +// } +// +// MyClass::~MyClass() { +// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL); +// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL); +// } +// +// // Return the current MyClass associated with the calling thread, can be +// // NULL if there isn't a MyClass associated. +// MyClass* MyClass::current() { +// return Singleton<ThreadLocalPointer<MyClass> >::get()->Get(); +// } + +#ifndef BASE_THREAD_LOCAL_H_ +#define BASE_THREAD_LOCAL_H_ + +#include "base/basictypes.h" + +#if defined(XP_WIN) +# include <windows.h> +#else +# include <pthread.h> +#endif + +namespace base { + +// Helper functions that abstract the cross-platform APIs. Do not use directly. +struct ThreadLocalPlatform { +#if defined(XP_WIN) + typedef DWORD SlotType; +#else + typedef pthread_key_t SlotType; +#endif + + static void AllocateSlot(SlotType& slot); + static void FreeSlot(SlotType& slot); + static void* GetValueFromSlot(SlotType& slot); + static void SetValueInSlot(SlotType& slot, void* value); +}; + +template <typename Type> +class ThreadLocalPointer { + public: + ThreadLocalPointer() : slot_() { ThreadLocalPlatform::AllocateSlot(slot_); } + + ThreadLocalPointer(const ThreadLocalPointer&) = delete; + ThreadLocalPointer& operator=(const ThreadLocalPointer&) = delete; + + ~ThreadLocalPointer() { ThreadLocalPlatform::FreeSlot(slot_); } + + Type* Get() { + return static_cast<Type*>(ThreadLocalPlatform::GetValueFromSlot(slot_)); + } + + void Set(Type* ptr) { ThreadLocalPlatform::SetValueInSlot(slot_, ptr); } + + private: + typedef ThreadLocalPlatform::SlotType SlotType; + + SlotType slot_; +}; + +class ThreadLocalBoolean { + public: + ThreadLocalBoolean() {} + + ThreadLocalBoolean(const ThreadLocalBoolean&) = delete; + ThreadLocalBoolean& operator=(const ThreadLocalBoolean&) = delete; + + ~ThreadLocalBoolean() {} + + bool Get() { return tlp_.Get() != NULL; } + + void Set(bool val) { + uintptr_t intVal = val ? 1 : 0; + tlp_.Set(reinterpret_cast<void*>(intVal)); + } + + private: + ThreadLocalPointer<void> tlp_; +}; + +} // namespace base + +#endif // BASE_THREAD_LOCAL_H_ diff --git a/ipc/chromium/src/base/thread_local_posix.cc b/ipc/chromium/src/base/thread_local_posix.cc new file mode 100644 index 0000000000..050a6e4b92 --- /dev/null +++ b/ipc/chromium/src/base/thread_local_posix.cc @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread_local.h" + +#include <pthread.h> + +#include "base/logging.h" + +namespace base { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + int error = pthread_key_create(&slot, NULL); + CHECK(error == 0); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType& slot) { + int error = pthread_key_delete(slot); + DCHECK(error == 0); +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { + return pthread_getspecific(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) { + int error = pthread_setspecific(slot, value); + CHECK(error == 0); +} + +} // namespace base diff --git a/ipc/chromium/src/base/thread_local_storage.h b/ipc/chromium/src/base/thread_local_storage.h new file mode 100644 index 0000000000..1134aa6ad9 --- /dev/null +++ b/ipc/chromium/src/base/thread_local_storage.h @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREAD_LOCAL_STORAGE_H_ +#define BASE_THREAD_LOCAL_STORAGE_H_ + +#include "base/basictypes.h" + +#if defined(XP_UNIX) +# include <pthread.h> +#endif + +// Wrapper for thread local storage. This class doesn't do much except provide +// an API for portability. +class ThreadLocalStorage { + public: + // Prototype for the TLS destructor function, which can be optionally used to + // cleanup thread local storage on thread exit. 'value' is the data that is + // stored in thread local storage. + typedef void (*TLSDestructorFunc)(void* value); + + // A key representing one value stored in TLS. + class Slot { + public: + explicit Slot(TLSDestructorFunc destructor = NULL); + + // This constructor should be used for statics. + // It returns an uninitialized Slot. + explicit Slot(base::LinkerInitialized x) {} + + // Set up the TLS slot. Called by the constructor. + // 'destructor' is a pointer to a function to perform per-thread cleanup of + // this object. If set to NULL, no cleanup is done for this TLS slot. + // Returns false on error. + bool Initialize(TLSDestructorFunc destructor); + + // Free a previously allocated TLS 'slot'. + // If a destructor was set for this slot, removes + // the destructor so that remaining threads exiting + // will not free data. + void Free(); + + // Get the thread-local value stored in slot 'slot'. + // Values are guaranteed to initially be zero. + void* Get() const; + + // Set the thread-local value stored in slot 'slot' to + // value 'value'. + void Set(void* value); + + bool initialized() const { return initialized_; } + + private: + // The internals of this struct should be considered private. + bool initialized_; +#if defined(XP_WIN) + int slot_; +#else + pthread_key_t key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(Slot); + }; + +#if defined(XP_WIN) + // Function called when on thread exit to call TLS + // destructor functions. This function is used internally. + static void ThreadExit(); + + private: + // Function to lazily initialize our thread local storage. + static void** Initialize(); + + private: + // The maximum number of 'slots' in our thread local storage stack. + // For now, this is fixed. We could either increase statically, or + // we could make it dynamic in the future. + static const int kThreadLocalStorageSize = 64; + + static long tls_key_; + static long tls_max_; + static TLSDestructorFunc tls_destructors_[kThreadLocalStorageSize]; +#endif // XP_WIN + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); +}; + +// Temporary backwards-compatible name. +// TODO(evanm): replace all usage of TLSSlot. +typedef ThreadLocalStorage::Slot TLSSlot; + +#endif // BASE_THREAD_LOCAL_STORAGE_H_ diff --git a/ipc/chromium/src/base/thread_local_win.cc b/ipc/chromium/src/base/thread_local_win.cc new file mode 100644 index 0000000000..2eb68e63b3 --- /dev/null +++ b/ipc/chromium/src/base/thread_local_win.cc @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread_local.h" + +#include <windows.h> + +#include "base/logging.h" + +namespace base { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + slot = TlsAlloc(); + CHECK(slot != TLS_OUT_OF_INDEXES); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType& slot) { + if (!TlsFree(slot)) { + NOTREACHED() << "Failed to deallocate tls slot with TlsFree()."; + } +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { + return TlsGetValue(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) { + if (!TlsSetValue(slot, value)) { + CHECK(false) + << "Failed to TlsSetValue()."; + } +} + +} // namespace base diff --git a/ipc/chromium/src/base/time.cc b/ipc/chromium/src/base/time.cc new file mode 100644 index 0000000000..2d76878005 --- /dev/null +++ b/ipc/chromium/src/base/time.cc @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "prtime.h" + +#include "base/logging.h" + +namespace base { + +// TimeDelta ------------------------------------------------------------------ + +int TimeDelta::InDays() const { + return static_cast<int>(delta_ / Time::kMicrosecondsPerDay); +} + +int TimeDelta::InHours() const { + return static_cast<int>(delta_ / Time::kMicrosecondsPerHour); +} + +int TimeDelta::InMinutes() const { + return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute); +} + +double TimeDelta::InSecondsF() const { + return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond; +} + +int64_t TimeDelta::InSeconds() const { + return delta_ / Time::kMicrosecondsPerSecond; +} + +double TimeDelta::InMillisecondsF() const { + return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond; +} + +int64_t TimeDelta::InMilliseconds() const { + return delta_ / Time::kMicrosecondsPerMillisecond; +} + +int64_t TimeDelta::InMicroseconds() const { return delta_; } + +// Time ----------------------------------------------------------------------- + +// static +Time Time::FromTimeT(time_t tt) { + if (tt == 0) return Time(); // Preserve 0 so we can tell it doesn't exist. + return Time((tt * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset); +} + +time_t Time::ToTimeT() const { + if (us_ == 0) return 0; // Preserve 0 so we can tell it doesn't exist. + return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond; +} + +// static +Time Time::FromDoubleT(double dt) { + return Time((dt * static_cast<double>(kMicrosecondsPerSecond)) + + kTimeTToMicrosecondsOffset); +} + +double Time::ToDoubleT() const { + if (us_ == 0) return 0; // Preserve 0 so we can tell it doesn't exist. + return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) / + static_cast<double>(kMicrosecondsPerSecond)); +} + +Time Time::LocalMidnight() const { + Exploded exploded; + LocalExplode(&exploded); + exploded.hour = 0; + exploded.minute = 0; + exploded.second = 0; + exploded.millisecond = 0; + return FromLocalExploded(exploded); +} + +// static +bool Time::FromString(const wchar_t* time_string, Time* parsed_time) { + DCHECK((time_string != NULL) && (parsed_time != NULL)); + std::string ascii_time_string = SysWideToUTF8(time_string); + if (ascii_time_string.length() == 0) return false; + PRTime result_time = 0; + PRStatus result = + PR_ParseTimeString(ascii_time_string.c_str(), PR_FALSE, &result_time); + if (PR_SUCCESS != result) return false; + result_time += kTimeTToMicrosecondsOffset; + *parsed_time = Time(result_time); + return true; +} + +} // namespace base diff --git a/ipc/chromium/src/base/time.h b/ipc/chromium/src/base/time.h new file mode 100644 index 0000000000..cc9bc8e30e --- /dev/null +++ b/ipc/chromium/src/base/time.h @@ -0,0 +1,392 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Time represents an absolute point in time, internally represented as +// microseconds (s/1,000,000) since a platform-dependent epoch. Each +// platform's epoch, along with other system-dependent clock interface +// routines, is defined in time_PLATFORM.cc. +// +// TimeDelta represents a duration of time, internally represented in +// microseconds. +// +// TimeTicks represents an abstract time that is always incrementing for use +// in measuring time durations. It is internally represented in microseconds. +// It can not be converted to a human-readable time, but is guaranteed not to +// decrease (if the user changes the computer clock, Time::Now() may actually +// decrease or jump). +// +// These classes are represented as only a 64-bit value, so they can be +// efficiently passed by value. + +#ifndef BASE_TIME_H_ +#define BASE_TIME_H_ + +#include <time.h> + +#include "base/basictypes.h" + +namespace base { + +class Time; +class TimeTicks; + +// This unit test does a lot of manual time manipulation. +class PageLoadTrackerUnitTest; + +// TimeDelta ------------------------------------------------------------------ + +class TimeDelta { + public: + TimeDelta() : delta_(0) {} + + // Converts units of time to TimeDeltas. + static TimeDelta FromDays(int64_t days); + static TimeDelta FromHours(int64_t hours); + static TimeDelta FromMinutes(int64_t minutes); + static TimeDelta FromSeconds(int64_t secs); + static TimeDelta FromMilliseconds(int64_t ms); + static TimeDelta FromMicroseconds(int64_t us); + + // Returns the internal numeric value of the TimeDelta object. Please don't + // use this and do arithmetic on it, as it is more error prone than using the + // provided operators. + int64_t ToInternalValue() const { return delta_; } + + // Returns the time delta in some unit. The F versions return a floating + // point value, the "regular" versions return a rounded-down value. + int InDays() const; + int InHours() const; + int InMinutes() const; + double InSecondsF() const; + int64_t InSeconds() const; + double InMillisecondsF() const; + int64_t InMilliseconds() const; + int64_t InMicroseconds() const; + + TimeDelta& operator=(TimeDelta other) { + delta_ = other.delta_; + return *this; + } + + // Computations with other deltas. + TimeDelta operator+(TimeDelta other) const { + return TimeDelta(delta_ + other.delta_); + } + TimeDelta operator-(TimeDelta other) const { + return TimeDelta(delta_ - other.delta_); + } + + TimeDelta& operator+=(TimeDelta other) { + delta_ += other.delta_; + return *this; + } + TimeDelta& operator-=(TimeDelta other) { + delta_ -= other.delta_; + return *this; + } + TimeDelta operator-() const { return TimeDelta(-delta_); } + + // Computations with ints, note that we only allow multiplicative operations + // with ints, and additive operations with other deltas. + TimeDelta operator*(int64_t a) const { return TimeDelta(delta_ * a); } + TimeDelta operator/(int64_t a) const { return TimeDelta(delta_ / a); } + TimeDelta& operator*=(int64_t a) { + delta_ *= a; + return *this; + } + TimeDelta& operator/=(int64_t a) { + delta_ /= a; + return *this; + } + int64_t operator/(TimeDelta a) const { return delta_ / a.delta_; } + + // Defined below because it depends on the definition of the other classes. + Time operator+(Time t) const; + TimeTicks operator+(TimeTicks t) const; + + // Comparison operators. + bool operator==(TimeDelta other) const { return delta_ == other.delta_; } + bool operator!=(TimeDelta other) const { return delta_ != other.delta_; } + bool operator<(TimeDelta other) const { return delta_ < other.delta_; } + bool operator<=(TimeDelta other) const { return delta_ <= other.delta_; } + bool operator>(TimeDelta other) const { return delta_ > other.delta_; } + bool operator>=(TimeDelta other) const { return delta_ >= other.delta_; } + + private: + friend class Time; + friend class TimeTicks; + friend TimeDelta operator*(int64_t a, TimeDelta td); + + // Constructs a delta given the duration in microseconds. This is private + // to avoid confusion by callers with an integer constructor. Use + // FromSeconds, FromMilliseconds, etc. instead. + explicit TimeDelta(int64_t delta_us) : delta_(delta_us) {} + + // Delta in microseconds. + int64_t delta_; +}; + +inline TimeDelta operator*(int64_t a, TimeDelta td) { + return TimeDelta(a * td.delta_); +} + +// Time ----------------------------------------------------------------------- + +// Represents a wall clock time. +class Time { + public: + static const int64_t kMillisecondsPerSecond = 1000; + static const int64_t kMicrosecondsPerMillisecond = 1000; + static const int64_t kMicrosecondsPerSecond = + kMicrosecondsPerMillisecond * kMillisecondsPerSecond; + static const int64_t kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60; + static const int64_t kMicrosecondsPerHour = kMicrosecondsPerMinute * 60; + static const int64_t kMicrosecondsPerDay = kMicrosecondsPerHour * 24; + static const int64_t kMicrosecondsPerWeek = kMicrosecondsPerDay * 7; + static const int64_t kNanosecondsPerMicrosecond = 1000; + static const int64_t kNanosecondsPerSecond = + kNanosecondsPerMicrosecond * kMicrosecondsPerSecond; + + // Represents an exploded time that can be formatted nicely. This is kind of + // like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few + // additions and changes to prevent errors. + struct Exploded { + int year; // Four digit year "2007" + signed char month; // 1-based month (values 1 = January, etc.) + signed char day_of_week; // 0-based day of week (0 = Sunday, etc.) + signed char day_of_month; // 1-based day of month (1-31) + signed char hour; // Hour within the current day (0-23) + signed char minute; // Minute within the current hour (0-59) + signed char second; // Second within the current minute (0-59 plus + // leap seconds which may take it up to 60). + int millisecond; // Milliseconds within the current second (0-999) + }; + + // Contains the NULL time. Use Time::Now() to get the current time. + explicit Time() : us_(0) {} + + // Returns true if the time object has not been initialized. + bool is_null() const { return us_ == 0; } + + // Returns the current time. Watch out, the system might adjust its clock + // in which case time will actually go backwards. We don't guarantee that + // times are increasing, or that two calls to Now() won't be the same. + static Time Now(); + + // Returns the current time. Same as Now() except that this function always + // uses system time so that there are no discrepancies between the returned + // time and system time even on virtual environments including our test bot. + // For timing sensitive unittests, this function should be used. + static Time NowFromSystemTime(); + + // Converts to/from time_t in UTC and a Time class. + // TODO(brettw) this should be removed once everybody starts using the |Time| + // class. + static Time FromTimeT(time_t tt); + time_t ToTimeT() const; + + // Converts time to/from a double which is the number of seconds since epoch + // (Jan 1, 1970). Webkit uses this format to represent time. + static Time FromDoubleT(double dt); + double ToDoubleT() const; + + // Converts an exploded structure representing either the local time or UTC + // into a Time class. + static Time FromUTCExploded(const Exploded& exploded) { + return FromExploded(false, exploded); + } + static Time FromLocalExploded(const Exploded& exploded) { + return FromExploded(true, exploded); + } + + // Converts an integer value representing Time to a class. This is used + // when deserializing a |Time| structure, using a value known to be + // compatible. It is not provided as a constructor because the integer type + // may be unclear from the perspective of a caller. + static Time FromInternalValue(int64_t us) { return Time(us); } + + // Converts a string representation of time to a Time object. + // An example of a time string which is converted is as below:- + // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified + // in the input string, we assume local time. + // TODO(iyengar) Move the FromString/FromTimeT/ToTimeT/FromFileTime to + // a new time converter class. + static bool FromString(const wchar_t* time_string, Time* parsed_time); + + // For serializing, use FromInternalValue to reconstitute. Please don't use + // this and do arithmetic on it, as it is more error prone than using the + // provided operators. + int64_t ToInternalValue() const { return us_; } + + // Fills the given exploded structure with either the local time or UTC from + // this time structure (containing UTC). + void UTCExplode(Exploded* exploded) const { return Explode(false, exploded); } + void LocalExplode(Exploded* exploded) const { + return Explode(true, exploded); + } + + // Rounds this time down to the nearest day in local time. It will represent + // midnight on that day. + Time LocalMidnight() const; + + Time& operator=(Time other) { + us_ = other.us_; + return *this; + } + + // Compute the difference between two times. + TimeDelta operator-(Time other) const { return TimeDelta(us_ - other.us_); } + + // Modify by some time delta. + Time& operator+=(TimeDelta delta) { + us_ += delta.delta_; + return *this; + } + Time& operator-=(TimeDelta delta) { + us_ -= delta.delta_; + return *this; + } + + // Return a new time modified by some delta. + Time operator+(TimeDelta delta) const { return Time(us_ + delta.delta_); } + Time operator-(TimeDelta delta) const { return Time(us_ - delta.delta_); } + + // Comparison operators + bool operator==(Time other) const { return us_ == other.us_; } + bool operator!=(Time other) const { return us_ != other.us_; } + bool operator<(Time other) const { return us_ < other.us_; } + bool operator<=(Time other) const { return us_ <= other.us_; } + bool operator>(Time other) const { return us_ > other.us_; } + bool operator>=(Time other) const { return us_ >= other.us_; } + + private: + friend class TimeDelta; + + // Explodes the given time to either local time |is_local = true| or UTC + // |is_local = false|. + void Explode(bool is_local, Exploded* exploded) const; + + // Unexplodes a given time assuming the source is either local time + // |is_local = true| or UTC |is_local = false|. + static Time FromExploded(bool is_local, const Exploded& exploded); + + explicit Time(int64_t us) : us_(us) {} + + // The representation of Jan 1, 1970 UTC in microseconds since the + // platform-dependent epoch. + static const int64_t kTimeTToMicrosecondsOffset; + + // Time in microseconds in UTC. + int64_t us_; +}; + +inline Time TimeDelta::operator+(Time t) const { return Time(t.us_ + delta_); } + +// Inline the TimeDelta factory methods, for fast TimeDelta construction. + +// static +inline TimeDelta TimeDelta::FromDays(int64_t days) { + return TimeDelta(days * Time::kMicrosecondsPerDay); +} + +// static +inline TimeDelta TimeDelta::FromHours(int64_t hours) { + return TimeDelta(hours * Time::kMicrosecondsPerHour); +} + +// static +inline TimeDelta TimeDelta::FromMinutes(int64_t minutes) { + return TimeDelta(minutes * Time::kMicrosecondsPerMinute); +} + +// static +inline TimeDelta TimeDelta::FromSeconds(int64_t secs) { + return TimeDelta(secs * Time::kMicrosecondsPerSecond); +} + +// static +inline TimeDelta TimeDelta::FromMilliseconds(int64_t ms) { + return TimeDelta(ms * Time::kMicrosecondsPerMillisecond); +} + +// static +inline TimeDelta TimeDelta::FromMicroseconds(int64_t us) { + return TimeDelta(us); +} + +// TimeTicks ------------------------------------------------------------------ + +class TimeTicks { + public: + TimeTicks() : ticks_(0) {} + TimeTicks(const TimeTicks&) = default; + + // Platform-dependent tick count representing "right now." + // The resolution of this clock is ~1-15ms. Resolution varies depending + // on hardware/operating system configuration. + static TimeTicks Now(); + + // Returns true if this object has not been initialized. + bool is_null() const { return ticks_ == 0; } + + // Returns the internal numeric value of the TimeTicks object. + int64_t ToInternalValue() const { return ticks_; } + + TimeTicks& operator=(TimeTicks other) { + ticks_ = other.ticks_; + return *this; + } + + // Compute the difference between two times. + TimeDelta operator-(TimeTicks other) const { + return TimeDelta(ticks_ - other.ticks_); + } + + // Modify by some time delta. + TimeTicks& operator+=(TimeDelta delta) { + ticks_ += delta.delta_; + return *this; + } + TimeTicks& operator-=(TimeDelta delta) { + ticks_ -= delta.delta_; + return *this; + } + + // Return a new TimeTicks modified by some delta. + TimeTicks operator+(TimeDelta delta) const { + return TimeTicks(ticks_ + delta.delta_); + } + TimeTicks operator-(TimeDelta delta) const { + return TimeTicks(ticks_ - delta.delta_); + } + + // Comparison operators + bool operator==(TimeTicks other) const { return ticks_ == other.ticks_; } + bool operator!=(TimeTicks other) const { return ticks_ != other.ticks_; } + bool operator<(TimeTicks other) const { return ticks_ < other.ticks_; } + bool operator<=(TimeTicks other) const { return ticks_ <= other.ticks_; } + bool operator>(TimeTicks other) const { return ticks_ > other.ticks_; } + bool operator>=(TimeTicks other) const { return ticks_ >= other.ticks_; } + + protected: + friend class TimeDelta; + friend class PageLoadTrackerUnitTest; + + // Please use Now() to create a new object. This is for internal use + // and testing. Ticks is in microseconds. + explicit TimeTicks(int64_t ticks) : ticks_(ticks) {} + + // Tick count in microseconds. + int64_t ticks_; +}; + +inline TimeTicks TimeDelta::operator+(TimeTicks t) const { + return TimeTicks(t.ticks_ + delta_); +} + +} // namespace base + +#endif // BASE_TIME_H_ diff --git a/ipc/chromium/src/base/time_mac.cc b/ipc/chromium/src/base/time_mac.cc new file mode 100644 index 0000000000..6ab1515601 --- /dev/null +++ b/ipc/chromium/src/base/time_mac.cc @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time.h" + +#include <CoreFoundation/CFDate.h> +#include <CoreFoundation/CFTimeZone.h> +#include <mach/mach_time.h> +#include <sys/time.h> +#include <time.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/scoped_cftyperef.h" + +namespace base { + +// The Time routines in this file use Mach and CoreFoundation APIs, since the +// POSIX definition of time_t in Mac OS X wraps around after 2038--and +// there are already cookie expiration dates, etc., past that time out in +// the field. Using CFDate prevents that problem, and using mach_absolute_time +// for TimeTicks gives us nice high-resolution interval timing. + +// Time ----------------------------------------------------------------------- + +// The internal representation of Time uses a 64-bit microsecond count +// from 1970-01-01 00:00:00 UTC. Core Foundation uses a double second count +// since 2001-01-01 00:00:00 UTC. + +// Some functions in time.cc use time_t directly, so we provide a zero offset +// for them. The epoch is 1970-01-01 00:00:00 UTC. +// static +const int64_t Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0); + +// static +Time Time::Now() { + CFAbsoluteTime now = + CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970; + return Time(static_cast<int64_t>(now * kMicrosecondsPerSecond)); +} + +// static +Time Time::NowFromSystemTime() { + // Just use Now() because Now() returns the system time. + return Now(); +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + CFGregorianDate date; + date.second = + exploded.second + + exploded.millisecond / static_cast<double>(kMillisecondsPerSecond); + date.minute = exploded.minute; + date.hour = exploded.hour; + date.day = exploded.day_of_month; + date.month = exploded.month; + date.year = exploded.year; + + scoped_cftyperef<CFTimeZoneRef> time_zone(is_local ? CFTimeZoneCopySystem() + : NULL); + CFAbsoluteTime seconds = CFGregorianDateGetAbsoluteTime(date, time_zone) + + kCFAbsoluteTimeIntervalSince1970; + return Time(static_cast<int64_t>(seconds * kMicrosecondsPerSecond)); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + CFAbsoluteTime seconds = (static_cast<double>(us_) / kMicrosecondsPerSecond) - + kCFAbsoluteTimeIntervalSince1970; + + scoped_cftyperef<CFTimeZoneRef> time_zone(is_local ? CFTimeZoneCopySystem() + : NULL); + CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(seconds, time_zone); + + exploded->year = date.year; + exploded->month = date.month; + exploded->day_of_month = date.day; + exploded->hour = date.hour; + exploded->minute = date.minute; + exploded->second = date.second; + exploded->millisecond = + static_cast<int>(date.second * kMillisecondsPerSecond) % + kMillisecondsPerSecond; +} + +// TimeTicks ------------------------------------------------------------------ + +// static +TimeTicks TimeTicks::Now() { + uint64_t absolute_micro; + + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + // Zero-initialization of statics guarantees that denom will be 0 before + // calling mach_timebase_info. mach_timebase_info will never set denom to + // 0 as that would be invalid, so the zero-check can be used to determine + // whether mach_timebase_info has already been called. This is + // recommended by Apple's QA1398. + kern_return_t kr = mach_timebase_info(&timebase_info); + DCHECK(kr == KERN_SUCCESS); + } + + // mach_absolute_time is it when it comes to ticks on the Mac. Other calls + // with less precision (such as TickCount) just call through to + // mach_absolute_time. + + // timebase_info converts absolute time tick units into nanoseconds. Convert + // to microseconds up front to stave off overflows. + absolute_micro = mach_absolute_time() / Time::kNanosecondsPerMicrosecond * + timebase_info.numer / timebase_info.denom; + + // Don't bother with the rollover handling that the Windows version does. + // With numer and denom = 1 (the expected case), the 64-bit absolute time + // reported in nanoseconds is enough to last nearly 585 years. + + return TimeTicks(absolute_micro); +} + +} // namespace base diff --git a/ipc/chromium/src/base/time_posix.cc b/ipc/chromium/src/base/time_posix.cc new file mode 100644 index 0000000000..1f122bb698 --- /dev/null +++ b/ipc/chromium/src/base/time_posix.cc @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time.h" + +#ifdef XP_DARWIN +# include <mach/mach_time.h> +#endif +#include <sys/time.h> +#if defined(ANDROID) && !defined(__LP64__) +# include <time64.h> +#else +# include <time.h> +#endif +#if defined(ANDROID) || defined(XP_UNIX) +# include <unistd.h> +#endif + +#include <limits> +#include <cstdint> + +#include "base/logging.h" + +namespace base { + +// The Time routines in this file use standard POSIX routines, or almost- +// standard routines in the case of timegm. We need to use a Mach-specific +// function for TimeTicks::Now() on Mac OS X. + +// Time ----------------------------------------------------------------------- + +// Some functions in time.cc use time_t directly, so we provide a zero offset +// for them. The epoch is 1970-01-01 00:00:00 UTC. +// static +const int64_t Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0); + +// static +Time Time::Now() { + struct timeval tv; + struct timezone tz = {0, 0}; // UTC + if (gettimeofday(&tv, &tz) != 0) { + DCHECK(0) << "Could not determine time of day"; + } + // Combine seconds and microseconds in a 64-bit field containing microseconds + // since the epoch. That's enough for nearly 600 centuries. + return Time(tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec); +} + +// static +Time Time::NowFromSystemTime() { + // Just use Now() because Now() returns the system time. + return Now(); +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + struct tm timestruct; + timestruct.tm_sec = exploded.second; + timestruct.tm_min = exploded.minute; + timestruct.tm_hour = exploded.hour; + timestruct.tm_mday = exploded.day_of_month; + timestruct.tm_mon = exploded.month - 1; + timestruct.tm_year = exploded.year - 1900; + timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this + timestruct.tm_yday = 0; // mktime/timegm ignore this + timestruct.tm_isdst = -1; // attempt to figure it out +#ifndef XP_SOLARIS + timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore + timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore +#endif + + time_t seconds; +#ifdef ANDROID + seconds = mktime(×truct); +#else + if (is_local) + seconds = mktime(×truct); + else + seconds = timegm(×truct); +#endif + + int64_t milliseconds; + // Handle overflow. Clamping the range to what mktime and timegm might + // return is the best that can be done here. It's not ideal, but it's better + // than failing here or ignoring the overflow case and treating each time + // overflow as one second prior to the epoch. + if (seconds == -1 && (exploded.year < 1969 || exploded.year > 1970)) { + // If exploded.year is 1969 or 1970, take -1 as correct, with the + // time indicating 1 second prior to the epoch. (1970 is allowed to handle + // time zone and DST offsets.) Otherwise, return the most future or past + // time representable. Assumes the time_t epoch is 1970-01-01 00:00:00 UTC. + // + // The minimum and maximum representible times that mktime and timegm could + // return are used here instead of values outside that range to allow for + // proper round-tripping between exploded and counter-type time + // representations in the presence of possible truncation to time_t by + // division and use with other functions that accept time_t. + // + // When representing the most distant time in the future, add in an extra + // 999ms to avoid the time being less than any other possible value that + // this function can return. + + // Take care to avoid overflows when time_t is int64_t. + if (exploded.year < 1969) { + int64_t min_seconds = (sizeof(time_t) < sizeof(int64_t)) + ? std::numeric_limits<time_t>::min() + : std::numeric_limits<int32_t>::min(); + milliseconds = min_seconds * kMillisecondsPerSecond; + } else { + int64_t max_seconds = (sizeof(time_t) < sizeof(int64_t)) + ? std::numeric_limits<time_t>::max() + : std::numeric_limits<int32_t>::max(); + milliseconds = max_seconds * kMillisecondsPerSecond; + milliseconds += kMillisecondsPerSecond - 1; + } + } else { + milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond; + } + + return Time(milliseconds * kMicrosecondsPerMillisecond); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + // Time stores times with microsecond resolution, but Exploded only carries + // millisecond resolution, so begin by being lossy. + int64_t milliseconds = us_ / kMicrosecondsPerMillisecond; + time_t seconds = milliseconds / kMillisecondsPerSecond; + + struct tm timestruct; + if (is_local) + localtime_r(&seconds, ×truct); + else + gmtime_r(&seconds, ×truct); + + exploded->year = timestruct.tm_year + 1900; + exploded->month = timestruct.tm_mon + 1; + exploded->day_of_week = timestruct.tm_wday; + exploded->day_of_month = timestruct.tm_mday; + exploded->hour = timestruct.tm_hour; + exploded->minute = timestruct.tm_min; + exploded->second = timestruct.tm_sec; + exploded->millisecond = milliseconds % kMillisecondsPerSecond; +} + +// TimeTicks ------------------------------------------------------------------ + +// static +TimeTicks TimeTicks::Now() { + uint64_t absolute_micro; + +#if defined(XP_DARWIN) + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + // Zero-initialization of statics guarantees that denom will be 0 before + // calling mach_timebase_info. mach_timebase_info will never set denom to + // 0 as that would be invalid, so the zero-check can be used to determine + // whether mach_timebase_info has already been called. This is + // recommended by Apple's QA1398. + kern_return_t kr = mach_timebase_info(&timebase_info); + DCHECK(kr == KERN_SUCCESS); + } + + // mach_absolute_time is it when it comes to ticks on the Mac. Other calls + // with less precision (such as TickCount) just call through to + // mach_absolute_time. + + // timebase_info converts absolute time tick units into nanoseconds. Convert + // to microseconds up front to stave off overflows. + absolute_micro = mach_absolute_time() / Time::kNanosecondsPerMicrosecond * + timebase_info.numer / timebase_info.denom; + + // Don't bother with the rollover handling that the Windows version does. + // With numer and denom = 1 (the expected case), the 64-bit absolute time + // reported in nanoseconds is enough to last nearly 585 years. + +#elif defined(XP_OPENBSD) || defined(XP_UNIX) && \ + defined(_POSIX_MONOTONIC_CLOCK) && \ + _POSIX_MONOTONIC_CLOCK >= 0 + + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + NOTREACHED() << "clock_gettime(CLOCK_MONOTONIC) failed."; + return TimeTicks(); + } + + absolute_micro = + (static_cast<int64_t>(ts.tv_sec) * Time::kMicrosecondsPerSecond) + + (static_cast<int64_t>(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond); + +#else // _POSIX_MONOTONIC_CLOCK +# error No usable tick clock function on this platform. +#endif // _POSIX_MONOTONIC_CLOCK + + return TimeTicks(absolute_micro); +} + +} // namespace base diff --git a/ipc/chromium/src/base/time_win.cc b/ipc/chromium/src/base/time_win.cc new file mode 100644 index 0000000000..d39416cd6d --- /dev/null +++ b/ipc/chromium/src/base/time_win.cc @@ -0,0 +1,263 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Windows Timer Primer +// +// A good article: http://www.ddj.com/windows/184416651 +// A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 +// +// The default windows timer, GetSystemTimeAsFileTime is not very precise. +// It is only good to ~15.5ms. +// +// QueryPerformanceCounter is the logical choice for a high-precision timer. +// However, it is known to be buggy on some hardware. Specifically, it can +// sometimes "jump". On laptops, QPC can also be very expensive to call. +// It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower +// on laptops. A unittest exists which will show the relative cost of various +// timers on any system. +// +// The next logical choice is timeGetTime(). timeGetTime has a precision of +// 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other +// applications on the system. By default, precision is only 15.5ms. +// Unfortunately, we don't want to call timeBeginPeriod because we don't +// want to affect other applications. Further, on mobile platforms, use of +// faster multimedia timers can hurt battery life. See the intel +// article about this here: +// http://softwarecommunity.intel.com/articles/eng/1086.htm +// +// To work around all this, we're going to generally use timeGetTime(). We +// will only increase the system-wide timer if we're not running on battery +// power. Using timeBeginPeriod(1) is a requirement in order to make our +// message loop waits have the same resolution that our time measurements +// do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when +// there is nothing else to waken the Wait. + +#include "base/time.h" + +#ifndef __MINGW32__ +# pragma comment(lib, "winmm.lib") +#endif +#include <windows.h> +#include <mmsystem.h> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "mozilla/Casting.h" +#include "mozilla/StaticMutex.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; +using mozilla::BitwiseCast; + +namespace { + +// From MSDN, FILETIME "Contains a 64-bit value representing the number of +// 100-nanosecond intervals since January 1, 1601 (UTC)." +int64_t FileTimeToMicroseconds(const FILETIME& ft) { + // Need to BitwiseCast to fix alignment, then divide by 10 to convert + // 100-nanoseconds to milliseconds. This only works on little-endian + // machines. + return BitwiseCast<int64_t>(ft) / 10; +} + +void MicrosecondsToFileTime(int64_t us, FILETIME* ft) { + DCHECK(us >= 0) << "Time is less than 0, negative values are not " + "representable in FILETIME"; + + // Multiply by 10 to convert milliseconds to 100-nanoseconds. BitwiseCast will + // handle alignment problems. This only works on little-endian machines. + *ft = BitwiseCast<FILETIME>(us * 10); +} + +int64_t CurrentWallclockMicroseconds() { + FILETIME ft; + ::GetSystemTimeAsFileTime(&ft); + return FileTimeToMicroseconds(ft); +} + +// Time between resampling the un-granular clock for this API. 60 seconds. +const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond; + +int64_t initial_time = 0; +TimeTicks initial_ticks; + +void InitializeClock() { + initial_ticks = TimeTicks::Now(); + initial_time = CurrentWallclockMicroseconds(); +} + +} // namespace + +// Time ----------------------------------------------------------------------- + +// The internal representation of Time uses FILETIME, whose epoch is 1601-01-01 +// 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the +// number of leap year days between 1601 and 1970: (1970-1601)/4 excluding +// 1700, 1800, and 1900. +// static +const int64_t Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); + +// static +Time Time::Now() { + if (initial_time == 0) InitializeClock(); + + // We implement time using the high-resolution timers so that we can get + // timeouts which are smaller than 10-15ms. If we just used + // CurrentWallclockMicroseconds(), we'd have the less-granular timer. + // + // To make this work, we initialize the clock (initial_time) and the + // counter (initial_ctr). To compute the initial time, we can check + // the number of ticks that have elapsed, and compute the delta. + // + // To avoid any drift, we periodically resync the counters to the system + // clock. + while (true) { + TimeTicks ticks = TimeTicks::Now(); + + // Calculate the time elapsed since we started our timer + TimeDelta elapsed = ticks - initial_ticks; + + // Check if enough time has elapsed that we need to resync the clock. + if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) { + InitializeClock(); + continue; + } + + return Time(elapsed + Time(initial_time)); + } +} + +// static +Time Time::NowFromSystemTime() { + // Force resync. + InitializeClock(); + return Time(initial_time); +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + // Create the system struct representing our exploded time. It will either be + // in local time or UTC. + SYSTEMTIME st; + st.wYear = exploded.year; + st.wMonth = exploded.month; + st.wDayOfWeek = exploded.day_of_week; + st.wDay = exploded.day_of_month; + st.wHour = exploded.hour; + st.wMinute = exploded.minute; + st.wSecond = exploded.second; + st.wMilliseconds = exploded.millisecond; + + // Convert to FILETIME. + FILETIME ft; + if (!SystemTimeToFileTime(&st, &ft)) { + NOTREACHED() << "Unable to convert time"; + return Time(0); + } + + // Ensure that it's in UTC. + if (is_local) { + FILETIME utc_ft; + LocalFileTimeToFileTime(&ft, &utc_ft); + return Time(FileTimeToMicroseconds(utc_ft)); + } + return Time(FileTimeToMicroseconds(ft)); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + // FILETIME in UTC. + FILETIME utc_ft; + MicrosecondsToFileTime(us_, &utc_ft); + + // FILETIME in local time if necessary. + BOOL success = TRUE; + FILETIME ft; + if (is_local) + success = FileTimeToLocalFileTime(&utc_ft, &ft); + else + ft = utc_ft; + + // FILETIME in SYSTEMTIME (exploded). + SYSTEMTIME st; + if (!success || !FileTimeToSystemTime(&ft, &st)) { + NOTREACHED() << "Unable to convert time, don't know why"; + ZeroMemory(exploded, sizeof(*exploded)); + return; + } + + exploded->year = st.wYear; + exploded->month = st.wMonth; + exploded->day_of_week = st.wDayOfWeek; + exploded->day_of_month = st.wDay; + exploded->hour = st.wHour; + exploded->minute = st.wMinute; + exploded->second = st.wSecond; + exploded->millisecond = st.wMilliseconds; +} + +// TimeTicks ------------------------------------------------------------------ +namespace { + +// We define a wrapper to adapt between the __stdcall and __cdecl call of the +// mock function, and to avoid a static constructor. Assigning an import to a +// function pointer directly would require setup code to fetch from the IAT. +DWORD timeGetTimeWrapper() { return timeGetTime(); } + +DWORD (*tick_function)(void) = &timeGetTimeWrapper; + +// This setup is a little gross: the `now` instance lives until libxul is +// unloaded, but leak checking runs prior to that, and would see a Mutex +// instance contained in NowSingleton as still live. Said instance would +// be reported as a leak...but it's not, really. To avoid that, we need +// to use StaticMutex (which is not leak-checked), but StaticMutex can't +// be a member variable. So we have to have this separate static variable. +static mozilla::StaticMutex sNowSingletonLock; + +// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic +// because it returns the number of milliseconds since Windows has started, +// which will roll over the 32-bit value every ~49 days. We try to track +// rollover ourselves, which works if TimeTicks::Now() is called at least every +// 49 days. +class NowSingleton { + public: + TimeDelta Now() { + mozilla::StaticMutexAutoLock locked(sNowSingletonLock); + // We should hold the lock while calling tick_function to make sure that + // we keep our last_seen_ stay correctly in sync. + DWORD now = tick_function(); + if (now < last_seen_) + rollover_ += + TimeDelta::FromMilliseconds(GG_LONGLONG(0x100000000)); // ~49.7 days. + last_seen_ = now; + return TimeDelta::FromMilliseconds(now) + rollover_; + } + + static NowSingleton& instance() { + static NowSingleton now; + return now; + } + + private: + explicit NowSingleton() + : rollover_(TimeDelta::FromMilliseconds(0)), last_seen_(0) {} + ~NowSingleton() = default; + + TimeDelta rollover_ MOZ_GUARDED_BY( + sNowSingletonLock); // Accumulation of time lost due to rollover. + DWORD last_seen_ + MOZ_GUARDED_BY(sNowSingletonLock); // The last timeGetTime value we saw, + // to detect rollover. + + DISALLOW_COPY_AND_ASSIGN(NowSingleton); +}; + +} // namespace + +// static +TimeTicks TimeTicks::Now() { + return TimeTicks() + NowSingleton::instance().Now(); +} diff --git a/ipc/chromium/src/base/timer.cc b/ipc/chromium/src/base/timer.cc new file mode 100644 index 0000000000..b2d880797a --- /dev/null +++ b/ipc/chromium/src/base/timer.cc @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/timer.h" + +#include "base/message_loop.h" + +namespace base { + +void BaseTimer_Helper::OrphanDelayedTask() { + if (delayed_task_) { + delayed_task_->timer_ = nullptr; + delayed_task_ = nullptr; + } +} + +void BaseTimer_Helper::InitiateDelayedTask(TimerTask* timer_task) { + OrphanDelayedTask(); + + delayed_task_ = timer_task; + delayed_task_->timer_ = this; + RefPtr<TimerTask> addrefedTask = timer_task; + MessageLoop::current()->PostDelayedTask( + addrefedTask.forget(), + static_cast<int>(timer_task->delay_.InMilliseconds())); +} + +} // namespace base diff --git a/ipc/chromium/src/base/timer.h b/ipc/chromium/src/base/timer.h new file mode 100644 index 0000000000..eceea8d23b --- /dev/null +++ b/ipc/chromium/src/base/timer.h @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// OneShotTimer and RepeatingTimer provide a simple timer API. As the names +// suggest, OneShotTimer calls you back once after a time delay expires. +// RepeatingTimer on the other hand calls you back periodically with the +// prescribed time interval. +// +// OneShotTimer and RepeatingTimer both cancel the timer when they go out of +// scope, which makes it easy to ensure that you do not get called when your +// object has gone out of scope. Just instantiate a OneShotTimer or +// RepeatingTimer as a member variable of the class for which you wish to +// receive timer events. +// +// Sample RepeatingTimer usage: +// +// class MyClass { +// public: +// void StartDoingStuff() { +// timer_.Start(TimeDelta::FromSeconds(1), this, &MyClass::DoStuff); +// } +// void StopDoingStuff() { +// timer_.Stop(); +// } +// private: +// void DoStuff() { +// // This method is called every second to do stuff. +// ... +// } +// base::RepeatingTimer<MyClass> timer_; +// }; +// +// Both OneShotTimer and RepeatingTimer also support a Reset method, which +// allows you to easily defer the timer event until the timer delay passes once +// again. So, in the above example, if 0.5 seconds have already passed, +// calling Reset on timer_ would postpone DoStuff by another 1 second. In +// other words, Reset is shorthand for calling Stop and then Start again with +// the same arguments. + +#ifndef BASE_TIMER_H_ +#define BASE_TIMER_H_ + +// IMPORTANT: If you change timer code, make sure that all tests (including +// disabled ones) from timer_unittests.cc pass locally. Some are disabled +// because they're flaky on the buildbot, but when you run them locally you +// should be able to tell the difference. + +#include "base/logging.h" +#include "base/task.h" +#include "base/time.h" + +class MessageLoop; + +namespace base { + +//----------------------------------------------------------------------------- +// This class is an implementation detail of OneShotTimer and RepeatingTimer. +// Please do not use this class directly. +// +// This class exists to share code between BaseTimer<T> template instantiations. +// +class BaseTimer_Helper { + public: + // Stops the timer. + ~BaseTimer_Helper() { OrphanDelayedTask(); } + + // Returns true if the timer is running (i.e., not stopped). + bool IsRunning() const { return !!delayed_task_; } + + // Returns the current delay for this timer. May only call this method when + // the timer is running! + TimeDelta GetCurrentDelay() const { + DCHECK(IsRunning()); + return delayed_task_->delay_; + } + + protected: + BaseTimer_Helper() {} + + // We have access to the timer_ member so we can orphan this task. + class TimerTask : public mozilla::Runnable { + public: + explicit TimerTask(TimeDelta delay) + : mozilla::Runnable("base::BaseTimer_Helper::TimerTask"), + delay_(delay) { + // timer_ is set in InitiateDelayedTask. + } + virtual ~TimerTask() {} + BaseTimer_Helper* timer_; + TimeDelta delay_; + }; + + // Used to orphan delayed_task_ so that when it runs it does nothing. + void OrphanDelayedTask(); + + // Used to initiated a new delayed task. This has the side-effect of + // orphaning delayed_task_ if it is non-null. + void InitiateDelayedTask(TimerTask* timer_task); + + RefPtr<TimerTask> delayed_task_; + + DISALLOW_COPY_AND_ASSIGN(BaseTimer_Helper); +}; + +//----------------------------------------------------------------------------- +// This class is an implementation detail of OneShotTimer and RepeatingTimer. +// Please do not use this class directly. +template <class Receiver, bool kIsRepeating> +class BaseTimer : public BaseTimer_Helper { + public: + typedef void (Receiver::*ReceiverMethod)(); + + // Call this method to start the timer. It is an error to call this method + // while the timer is already running. + void Start(TimeDelta delay, Receiver* receiver, ReceiverMethod method) { + DCHECK(!IsRunning()); + InitiateDelayedTask(new TimerTask(delay, receiver, method)); + } + + // Call this method to stop the timer. It is a no-op if the timer is not + // running. + void Stop() { OrphanDelayedTask(); } + + // Call this method to reset the timer delay of an already running timer. + void Reset() { + DCHECK(IsRunning()); + InitiateDelayedTask(static_cast<TimerTask*>(delayed_task_.get())->Clone()); + } + + private: + typedef BaseTimer<Receiver, kIsRepeating> SelfType; + + class TimerTask : public BaseTimer_Helper::TimerTask { + public: + TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method) + : BaseTimer_Helper::TimerTask(delay), + receiver_(receiver), + method_(method) {} + + virtual ~TimerTask() { + // This task may be getting cleared because the MessageLoop has been + // destructed. If so, don't leave the Timer with a dangling pointer + // to this now-defunct task. + ClearBaseTimer(); + } + + NS_IMETHOD Run() override { + if (!timer_) // timer_ is null if we were orphaned. + return NS_OK; + if (kIsRepeating) + ResetBaseTimer(); + else + ClearBaseTimer(); + DispatchToMethod(receiver_, method_, Tuple0()); + return NS_OK; + } + + TimerTask* Clone() const { + return new TimerTask(delay_, receiver_, method_); + } + + private: + // Inform the Base that the timer is no longer active. + void ClearBaseTimer() { + if (timer_) { + SelfType* self = static_cast<SelfType*>(timer_); + // It is possible that the Timer has already been reset, and that this + // Task is old. So, if the Timer points to a different task, assume + // that the Timer has already taken care of properly setting the task. + if (self->delayed_task_ == this) self->delayed_task_ = nullptr; + // By now the delayed_task_ in the Timer does not point to us anymore. + // We should reset our own timer_ because the Timer can not do this + // for us in its destructor. + timer_ = NULL; + } + } + + // Inform the Base that we're resetting the timer. + void ResetBaseTimer() { + DCHECK(timer_); + DCHECK(kIsRepeating); + SelfType* self = static_cast<SelfType*>(timer_); + self->Reset(); + } + + Receiver* receiver_; + ReceiverMethod method_; + }; +}; + +//----------------------------------------------------------------------------- +// A simple, one-shot timer. See usage notes at the top of the file. +template <class Receiver> +class OneShotTimer : public BaseTimer<Receiver, false> {}; + +//----------------------------------------------------------------------------- +// A simple, repeating timer. See usage notes at the top of the file. +template <class Receiver> +class RepeatingTimer : public BaseTimer<Receiver, true> {}; + +//----------------------------------------------------------------------------- +// A Delay timer is like The Button from Lost. Once started, you have to keep +// calling Reset otherwise it will call the given method in the MessageLoop +// thread. +// +// Once created, it is inactive until Reset is called. Once |delay| seconds have +// passed since the last call to Reset, the callback is made. Once the callback +// has been made, it's inactive until Reset is called again. +// +// If destroyed, the timeout is canceled and will not occur even if already +// inflight. +template <class Receiver> +class DelayTimer { + public: + typedef void (Receiver::*ReceiverMethod)(); + + DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method) + : receiver_(receiver), method_(method), delay_(delay) {} + + void Reset() { DelayFor(delay_); } + + private: + void DelayFor(TimeDelta delay) { + trigger_time_ = Time::Now() + delay; + + // If we already have a timer that will expire at or before the given delay, + // then we have nothing more to do now. + if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay) return; + + // The timer isn't running, or will expire too late, so restart it. + timer_.Stop(); + timer_.Start(delay, this, &DelayTimer<Receiver>::Check); + } + + void Check() { + if (trigger_time_.is_null()) return; + + // If we have not waited long enough, then wait some more. + const Time now = Time::Now(); + if (now < trigger_time_) { + DelayFor(trigger_time_ - now); + return; + } + + (receiver_->*method_)(); + } + + Receiver* const receiver_; + const ReceiverMethod method_; + const TimeDelta delay_; + + OneShotTimer<DelayTimer<Receiver> > timer_; + Time trigger_time_; +}; + +} // namespace base + +#endif // BASE_TIMER_H_ diff --git a/ipc/chromium/src/base/tuple.h b/ipc/chromium/src/base/tuple.h new file mode 100644 index 0000000000..62655784d4 --- /dev/null +++ b/ipc/chromium/src/base/tuple.h @@ -0,0 +1,838 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A Tuple is a generic templatized container, similar in concept to std::pair. +// There are classes Tuple0 to Tuple6, cooresponding to the number of elements +// it contains. The convenient MakeTuple() function takes 0 to 6 arguments, +// and will construct and return the appropriate Tuple object. The functions +// DispatchToMethod and DispatchToFunction take a function pointer or instance +// and method pointer, and unpack a tuple into arguments to the call. +// +// Tuple elements are copied by value, and stored in the tuple. See the unit +// tests for more details of how/when the values are copied. +// +// Example usage: +// // These two methods of creating a Tuple are identical. +// Tuple2<int, const char*> tuple_a(1, "wee"); +// Tuple2<int, const char*> tuple_b = MakeTuple(1, "wee"); +// +// void SomeFunc(int a, const char* b) { } +// DispatchToFunction(&SomeFunc, tuple_a); // SomeFunc(1, "wee") +// DispatchToFunction( +// &SomeFunc, MakeTuple(10, "foo")); // SomeFunc(10, "foo") +// +// struct { void SomeMeth(int a, int b, int c) { } } foo; +// DispatchToMethod(&foo, &Foo::SomeMeth, MakeTuple(1, 2, 3)); +// // foo->SomeMeth(1, 2, 3); + +#ifndef BASE_TUPLE_H__ +#define BASE_TUPLE_H__ + +// Traits ---------------------------------------------------------------------- +// +// A simple traits class for tuple arguments. +// +// ValueType: the bare, nonref version of a type (same as the type for nonrefs). +// RefType: the ref version of a type (same as the type for refs). +// ParamType: what type to pass to functions (refs should not be constified). + +template <class P> +struct TupleTraits { + typedef P ValueType; + typedef P& RefType; + typedef const P& ParamType; +}; + +template <class P> +struct TupleTraits<P&> { + typedef P ValueType; + typedef P& RefType; + typedef P& ParamType; +}; + +// Tuple ----------------------------------------------------------------------- +// +// This set of classes is useful for bundling 0 or more heterogeneous data types +// into a single variable. The advantage of this is that it greatly simplifies +// function objects that need to take an arbitrary number of parameters; see +// RunnableMethod and IPC::MessageWithTuple. +// +// Tuple0 is supplied to act as a 'void' type. It can be used, for example, +// when dispatching to a function that accepts no arguments (see the +// Dispatchers below). +// Tuple1<A> is rarely useful. One such use is when A is non-const ref that you +// want filled by the dispatchee, and the tuple is merely a container for that +// output (a "tier"). See MakeRefTuple and its usages. + +struct Tuple0 { + typedef Tuple0 ValueTuple; + typedef Tuple0 RefTuple; +}; + +template <class A> +struct Tuple1 { + public: + typedef A TypeA; + typedef Tuple1<typename TupleTraits<A>::ValueType> ValueTuple; + typedef Tuple1<typename TupleTraits<A>::RefType> RefTuple; + + Tuple1() {} + explicit Tuple1(typename TupleTraits<A>::ParamType aA) : a(aA) {} + + A a; +}; + +template <class A, class B> +struct Tuple2 { + public: + typedef A TypeA; + typedef B TypeB; + typedef Tuple2<typename TupleTraits<A>::ValueType, + typename TupleTraits<B>::ValueType> + ValueTuple; + typedef Tuple2<typename TupleTraits<A>::RefType, + typename TupleTraits<B>::RefType> + RefTuple; + + Tuple2() {} + Tuple2(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB) + : a(aA), b(aB) {} + + A a; + B b; +}; + +template <class A, class B, class C> +struct Tuple3 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef Tuple3<typename TupleTraits<A>::ValueType, + typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType> + ValueTuple; + typedef Tuple3<typename TupleTraits<A>::RefType, + typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType> + RefTuple; + + Tuple3() {} + Tuple3(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC) + : a(aA), b(aB), c(aC) {} + + A a; + B b; + C c; +}; + +template <class A, class B, class C, class D> +struct Tuple4 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef Tuple4< + typename TupleTraits<A>::ValueType, typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType, typename TupleTraits<D>::ValueType> + ValueTuple; + typedef Tuple4< + typename TupleTraits<A>::RefType, typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType, typename TupleTraits<D>::RefType> + RefTuple; + + Tuple4() {} + Tuple4(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC, + typename TupleTraits<D>::ParamType aD) + : a(aA), b(aB), c(aC), d(aD) {} + + A a; + B b; + C c; + D d; +}; + +template <class A, class B, class C, class D, class E> +struct Tuple5 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef Tuple5< + typename TupleTraits<A>::ValueType, typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType, typename TupleTraits<D>::ValueType, + typename TupleTraits<E>::ValueType> + ValueTuple; + typedef Tuple5< + typename TupleTraits<A>::RefType, typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType, typename TupleTraits<D>::RefType, + typename TupleTraits<E>::RefType> + RefTuple; + + Tuple5() {} + Tuple5(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC, + typename TupleTraits<D>::ParamType aD, + typename TupleTraits<E>::ParamType aE) + : a(aA), b(aB), c(aC), d(aD), e(aE) {} + + A a; + B b; + C c; + D d; + E e; +}; + +template <class A, class B, class C, class D, class E, class F> +struct Tuple6 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + typedef Tuple6< + typename TupleTraits<A>::ValueType, typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType, typename TupleTraits<D>::ValueType, + typename TupleTraits<E>::ValueType, typename TupleTraits<F>::ValueType> + ValueTuple; + typedef Tuple6< + typename TupleTraits<A>::RefType, typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType, typename TupleTraits<D>::RefType, + typename TupleTraits<E>::RefType, typename TupleTraits<F>::RefType> + RefTuple; + + Tuple6() {} + Tuple6(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC, + typename TupleTraits<D>::ParamType aD, + typename TupleTraits<E>::ParamType aE, + typename TupleTraits<F>::ParamType aF) + : a(aA), b(aB), c(aC), d(aD), e(aE), f(aF) {} + + A a; + B b; + C c; + D d; + E e; + F f; +}; + +template <class A, class B, class C, class D, class E, class F, class G> +struct Tuple7 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + typedef G TypeG; + typedef Tuple7< + typename TupleTraits<A>::ValueType, typename TupleTraits<B>::ValueType, + typename TupleTraits<C>::ValueType, typename TupleTraits<D>::ValueType, + typename TupleTraits<E>::ValueType, typename TupleTraits<F>::ValueType, + typename TupleTraits<G>::ValueType> + ValueTuple; + typedef Tuple7< + typename TupleTraits<A>::RefType, typename TupleTraits<B>::RefType, + typename TupleTraits<C>::RefType, typename TupleTraits<D>::RefType, + typename TupleTraits<E>::RefType, typename TupleTraits<F>::RefType, + typename TupleTraits<G>::RefType> + RefTuple; + + Tuple7() {} + Tuple7(typename TupleTraits<A>::ParamType aA, + typename TupleTraits<B>::ParamType aB, + typename TupleTraits<C>::ParamType aC, + typename TupleTraits<D>::ParamType aD, + typename TupleTraits<E>::ParamType aE, + typename TupleTraits<F>::ParamType aF, + typename TupleTraits<G>::ParamType aG) + : a(aA), b(aB), c(aC), d(aD), e(aE), f(aF), g(aG) {} + + A a; + B b; + C c; + D d; + E e; + F f; + G g; +}; + +// Tuple creators ------------------------------------------------------------- +// +// Helper functions for constructing tuples while inferring the template +// argument types. + +namespace base { + +inline Tuple0 MakeTuple() { return Tuple0(); } + +template <class A> +inline Tuple1<A> MakeTuple(const A& a) { + return Tuple1<A>(a); +} + +template <class A, class B> +inline Tuple2<A, B> MakeTuple(const A& a, const B& b) { + return Tuple2<A, B>(a, b); +} + +template <class A, class B, class C> +inline Tuple3<A, B, C> MakeTuple(const A& a, const B& b, const C& c) { + return Tuple3<A, B, C>(a, b, c); +} + +template <class A, class B, class C, class D> +inline Tuple4<A, B, C, D> MakeTuple(const A& a, const B& b, const C& c, + const D& d) { + return Tuple4<A, B, C, D>(a, b, c, d); +} + +template <class A, class B, class C, class D, class E> +inline Tuple5<A, B, C, D, E> MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e) { + return Tuple5<A, B, C, D, E>(a, b, c, d, e); +} + +template <class A, class B, class C, class D, class E, class F> +inline Tuple6<A, B, C, D, E, F> MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e, const F& f) { + return Tuple6<A, B, C, D, E, F>(a, b, c, d, e, f); +} + +template <class A, class B, class C, class D, class E, class F, class G> +inline Tuple7<A, B, C, D, E, F, G> MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e, const F& f, + const G& g) { + return Tuple7<A, B, C, D, E, F, G>(a, b, c, d, e, f, g); +} + +} // end namespace base + +// The following set of helpers make what Boost refers to as "Tiers" - a tuple +// of references. + +template <class A> +inline Tuple1<A&> MakeRefTuple(A& a) { + return Tuple1<A&>(a); +} + +template <class A, class B> +inline Tuple2<A&, B&> MakeRefTuple(A& a, B& b) { + return Tuple2<A&, B&>(a, b); +} + +template <class A, class B, class C> +inline Tuple3<A&, B&, C&> MakeRefTuple(A& a, B& b, C& c) { + return Tuple3<A&, B&, C&>(a, b, c); +} + +template <class A, class B, class C, class D> +inline Tuple4<A&, B&, C&, D&> MakeRefTuple(A& a, B& b, C& c, D& d) { + return Tuple4<A&, B&, C&, D&>(a, b, c, d); +} + +template <class A, class B, class C, class D, class E> +inline Tuple5<A&, B&, C&, D&, E&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e) { + return Tuple5<A&, B&, C&, D&, E&>(a, b, c, d, e); +} + +template <class A, class B, class C, class D, class E, class F> +inline Tuple6<A&, B&, C&, D&, E&, F&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e, + F& f) { + return Tuple6<A&, B&, C&, D&, E&, F&>(a, b, c, d, e, f); +} + +template <class A, class B, class C, class D, class E, class F, class G> +inline Tuple7<A&, B&, C&, D&, E&, F&, G&> MakeRefTuple(A& a, B& b, C& c, D& d, + E& e, F& f, G& g) { + return Tuple7<A&, B&, C&, D&, E&, F&, G&>(a, b, c, d, e, f, g); +} + +// Dispatchers ---------------------------------------------------------------- +// +// Helper functions that call the given method on an object, with the unpacked +// tuple arguments. Notice that they all have the same number of arguments, +// so you need only write: +// DispatchToMethod(object, &Object::method, args); +// This is very useful for templated dispatchers, since they don't need to know +// what type |args| is. + +// Non-Static Dispatchers with no out params. + +template <class ObjT, class Method> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) { + (obj->*method)(); +} + +template <class ObjT, class Method, class A> +inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) { + (obj->*method)(arg); +} + +template <class ObjT, class Method, class A> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) { + (obj->*method)(arg.a); +} + +template <class ObjT, class Method, class A, class B> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<A, B>& arg) { + (obj->*method)(arg.a, arg.b); +} + +template <class ObjT, class Method, class A, class B, class C> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<A, B, C>& arg) { + (obj->*method)(arg.a, arg.b, arg.c); +} + +template <class ObjT, class Method, class A, class B, class C, class D> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<A, B, C, D>& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d); +} + +template <class ObjT, class Method, class A, class B, class C, class D, class E> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<A, B, C, D, E>& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template <class ObjT, class Method, class A, class B, class C, class D, class E, + class F> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<A, B, C, D, E, F>& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +template <class ObjT, class Method, class A, class B, class C, class D, class E, + class F, class G> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple7<A, B, C, D, E, F, G>& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g); +} + +// Static Dispatchers with no out params. + +template <class Function> +inline void DispatchToFunction(Function function, const Tuple0& arg) { + (*function)(); +} + +template <class Function, class A> +inline void DispatchToFunction(Function function, const A& arg) { + (*function)(arg); +} + +template <class Function, class A> +inline void DispatchToFunction(Function function, const Tuple1<A>& arg) { + (*function)(arg.a); +} + +template <class Function, class A, class B> +inline void DispatchToFunction(Function function, const Tuple2<A, B>& arg) { + (*function)(arg.a, arg.b); +} + +template <class Function, class A, class B, class C> +inline void DispatchToFunction(Function function, const Tuple3<A, B, C>& arg) { + (*function)(arg.a, arg.b, arg.c); +} + +template <class Function, class A, class B, class C, class D> +inline void DispatchToFunction(Function function, + const Tuple4<A, B, C, D>& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d); +} + +template <class Function, class A, class B, class C, class D, class E> +inline void DispatchToFunction(Function function, + const Tuple5<A, B, C, D, E>& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template <class Function, class A, class B, class C, class D, class E, class F> +inline void DispatchToFunction(Function function, + const Tuple6<A, B, C, D, E, F>& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +// Dispatchers with 0 out param (as a Tuple0). + +template <class ObjT, class Method> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg, + Tuple0*) { + (obj->*method)(); +} + +template <class ObjT, class Method, class A> +inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) { + (obj->*method)(arg); +} + +template <class ObjT, class Method, class A> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg, + Tuple0*) { + (obj->*method)(arg.a); +} + +template <class ObjT, class Method, class A, class B> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple2<A, B>& arg, + Tuple0*) { + (obj->*method)(arg.a, arg.b); +} + +template <class ObjT, class Method, class A, class B, class C> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<A, B, C>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c); +} + +template <class ObjT, class Method, class A, class B, class C, class D> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<A, B, C, D>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d); +} + +template <class ObjT, class Method, class A, class B, class C, class D, class E> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<A, B, C, D, E>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template <class ObjT, class Method, class A, class B, class C, class D, class E, + class F> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<A, B, C, D, E, F>& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +// Dispatchers with 1 out param. + +template <class ObjT, class Method, class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& in, + Tuple1<OutA>* out) { + (obj->*method)(&out->a); +} + +template <class ObjT, class Method, class InA, class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, const InA& in, + Tuple1<OutA>* out) { + (obj->*method)(in, &out->a); +} + +template <class ObjT, class Method, class InA, class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<InA>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, &out->a); +} + +template <class ObjT, class Method, class InA, class InB, class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, &out->a); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class InF, class OutA> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple1<OutA>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a); +} + +// Dispatchers with 2 out params. + +template <class ObjT, class Method, class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(&out->a, &out->b); +} + +template <class ObjT, class Method, class InA, class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, const InA& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in, &out->a, &out->b); +} + +template <class ObjT, class Method, class InA, class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<InA>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, &out->a, &out->b); +} + +template <class ObjT, class Method, class InA, class InB, class OutA, + class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class OutA, + class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class InF, class OutA, class OutB> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple2<OutA, OutB>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b); +} + +// Dispatchers with 3 out params. + +template <class ObjT, class Method, class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(&out->a, &out->b, &out->c); +} + +template <class ObjT, class Method, class InA, class OutA, class OutB, + class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, const InA& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in, &out->a, &out->b, &out->c); +} + +template <class ObjT, class Method, class InA, class OutA, class OutB, + class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<InA>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c); +} + +template <class ObjT, class Method, class InA, class InB, class OutA, + class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class OutA, + class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class InF, class OutA, class OutB, class OutC> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple3<OutA, OutB, OutC>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b, &out->c); +} + +// Dispatchers with 4 out params. + +template <class ObjT, class Method, class OutA, class OutB, class OutC, + class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(&out->a, &out->b, &out->c, &out->d); +} + +template <class ObjT, class Method, class InA, class OutA, class OutB, + class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, const InA& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in, &out->a, &out->b, &out->c, &out->d); +} + +template <class ObjT, class Method, class InA, class OutA, class OutB, + class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<InA>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d); +} + +template <class ObjT, class Method, class InA, class InB, class OutA, + class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class OutA, + class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c, + &out->d); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class InF, class OutA, class OutB, class OutC, class OutD> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple4<OutA, OutB, OutC, OutD>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b, &out->c, + &out->d); +} + +// Dispatchers with 5 out params. + +template <class ObjT, class Method, class OutA, class OutB, class OutC, + class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e); +} + +template <class ObjT, class Method, class InA, class OutA, class OutB, + class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, const InA& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template <class ObjT, class Method, class InA, class OutA, class OutB, + class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<InA>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template <class ObjT, class Method, class InA, class InB, class OutA, + class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2<InA, InB>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class OutA, + class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3<InA, InB, InC>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4<InA, InB, InC, InD>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d, + &out->e); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class OutA, class OutB, class OutC, class OutD, class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5<InA, InB, InC, InD, InE>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c, + &out->d, &out->e); +} + +template <class ObjT, class Method, class InA, class InB, class InC, class InD, + class InE, class InF, class OutA, class OutB, class OutC, class OutD, + class OutE> +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6<InA, InB, InC, InD, InE, InF>& in, + Tuple5<OutA, OutB, OutC, OutD, OutE>* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b, &out->c, + &out->d, &out->e); +} + +#endif // BASE_TUPLE_H__ diff --git a/ipc/chromium/src/base/waitable_event.h b/ipc/chromium/src/base/waitable_event.h new file mode 100644 index 0000000000..7c85435804 --- /dev/null +++ b/ipc/chromium/src/base/waitable_event.h @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_WAITABLE_EVENT_H_ +#define BASE_WAITABLE_EVENT_H_ + +#include "base/basictypes.h" + +#if defined(XP_WIN) +# include <windows.h> +#endif + +#if defined(XP_UNIX) +# include <list> +# include <utility> +# include "base/condition_variable.h" +# include "base/lock.h" +# include "nsISupportsImpl.h" +#endif + +#include "base/message_loop.h" + +namespace base { + +// This replaces INFINITE from Win32 +static const int kNoTimeout = -1; + +class TimeDelta; + +// A WaitableEvent can be a useful thread synchronization tool when you want to +// allow one thread to wait for another thread to finish some work. For +// non-Windows systems, this can only be used from within a single address +// space. +// +// Use a WaitableEvent when you would otherwise use a Lock+ConditionVariable to +// protect a simple boolean value. However, if you find yourself using a +// WaitableEvent in conjunction with a Lock to wait for a more complex state +// change (e.g., for an item to be added to a queue), then you should probably +// be using a ConditionVariable instead of a WaitableEvent. +// +// NOTE: On Windows, this class provides a subset of the functionality afforded +// by a Windows event object. This is intentional. If you are writing Windows +// specific code and you need other features of a Windows event, then you might +// be better off just using an Windows event directly. +class WaitableEvent { + public: + // If manual_reset is true, then to set the event state to non-signaled, a + // consumer must call the Reset method. If this parameter is false, then the + // system automatically resets the event state to non-signaled after a single + // waiting thread has been released. + WaitableEvent(bool manual_reset, bool initially_signaled); + + ~WaitableEvent(); + + // Put the event in the un-signaled state. + void Reset(); + + // Put the event in the signaled state. Causing any thread blocked on Wait + // to be woken up. + void Signal(); + + // Returns true if the event is in the signaled state, else false. If this + // is not a manual reset event, then this test will cause a reset. + bool IsSignaled(); + + // Wait indefinitely for the event to be signaled. Returns true if the event + // was signaled, else false is returned to indicate that waiting failed. + bool Wait(); + + // Wait up until max_time has passed for the event to be signaled. Returns + // true if the event was signaled. If this method returns false, then it + // does not necessarily mean that max_time was exceeded. + bool TimedWait(const TimeDelta& max_time); + +#if defined(XP_WIN) + HANDLE handle() const { return handle_; } +#endif + + // Wait, synchronously, on multiple events. + // waitables: an array of WaitableEvent pointers + // count: the number of elements in @waitables + // + // returns: the index of a WaitableEvent which has been signaled. + // + // You MUST NOT delete any of the WaitableEvent objects while this wait is + // happening. + static size_t WaitMany(WaitableEvent** waitables, size_t count); + + // For asynchronous waiting, see WaitableEventWatcher + + // This is a private helper class. It's here because it's used by friends of + // this class (such as WaitableEventWatcher) to be able to enqueue elements + // of the wait-list + class Waiter { + public: + // Signal the waiter to wake up. + // + // Consider the case of a Waiter which is in multiple WaitableEvent's + // wait-lists. Each WaitableEvent is automatic-reset and two of them are + // signaled at the same time. Now, each will wake only the first waiter in + // the wake-list before resetting. However, if those two waiters happen to + // be the same object (as can happen if another thread didn't have a chance + // to dequeue the waiter from the other wait-list in time), two auto-resets + // will have happened, but only one waiter has been signaled! + // + // Because of this, a Waiter may "reject" a wake by returning false. In + // this case, the auto-reset WaitableEvent shouldn't act as if anything has + // been notified. + virtual bool Fire(WaitableEvent* signaling_event) = 0; + + // Waiters may implement this in order to provide an extra condition for + // two Waiters to be considered equal. In WaitableEvent::Dequeue, if the + // pointers match then this function is called as a final check. See the + // comments in ~Handle for why. + virtual bool Compare(void* tag) = 0; + }; + + private: + friend class WaitableEventWatcher; + +#if defined(XP_WIN) + HANDLE handle_; +#else + // On Windows, one can close a HANDLE which is currently being waited on. The + // MSDN documentation says that the resulting behaviour is 'undefined', but + // it doesn't crash. However, if we were to include the following members + // directly then, on POSIX, one couldn't use WaitableEventWatcher to watch an + // event which gets deleted. This mismatch has bitten us several times now, + // so we have a kernel of the WaitableEvent, which is reference counted. + // WaitableEventWatchers may then take a reference and thus match the Windows + // behaviour. + struct WaitableEventKernel final { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WaitableEventKernel) + WaitableEventKernel(bool manual_reset, bool initially_signaled) + : manual_reset_(manual_reset), signaled_(initially_signaled) {} + + bool Dequeue(Waiter* waiter, void* tag); + + Lock lock_; + const bool manual_reset_; + bool signaled_; + std::list<Waiter*> waiters_; + + protected: + ~WaitableEventKernel() {} + }; + + RefPtr<WaitableEventKernel> kernel_; + + bool SignalAll(); + bool SignalOne(); + void Enqueue(Waiter* waiter); + + // When dealing with arrays of WaitableEvent*, we want to sort by the address + // of the WaitableEvent in order to have a globally consistent locking order. + // In that case we keep them, in sorted order, in an array of pairs where the + // second element is the index of the WaitableEvent in the original, + // unsorted, array. + typedef std::pair<WaitableEvent*, size_t> WaiterAndIndex; + static size_t EnqueueMany(WaiterAndIndex* waitables, size_t count, + Waiter* waiter); +#endif + + DISALLOW_COPY_AND_ASSIGN(WaitableEvent); +}; + +} // namespace base + +#endif // BASE_WAITABLE_EVENT_H_ diff --git a/ipc/chromium/src/base/waitable_event_posix.cc b/ipc/chromium/src/base/waitable_event_posix.cc new file mode 100644 index 0000000000..ad63876016 --- /dev/null +++ b/ipc/chromium/src/base/waitable_event_posix.cc @@ -0,0 +1,362 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/waitable_event.h" + +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/message_loop.h" + +// ----------------------------------------------------------------------------- +// A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't +// support cross-process events (where one process can signal an event which +// others are waiting on). Because of this, we can avoid having one thread per +// listener in several cases. +// +// The WaitableEvent maintains a list of waiters, protected by a lock. Each +// waiter is either an async wait, in which case we have a Task and the +// MessageLoop to run it on, or a blocking wait, in which case we have the +// condition variable to signal. +// +// Waiting involves grabbing the lock and adding oneself to the wait list. Async +// waits can be canceled, which means grabbing the lock and removing oneself +// from the list. +// +// Waiting on multiple events is handled by adding a single, synchronous wait to +// the wait-list of many events. An event passes a pointer to itself when +// firing a waiter and so we can store that pointer to find out which event +// triggered. +// ----------------------------------------------------------------------------- + +namespace base { + +// ----------------------------------------------------------------------------- +// This is just an abstract base class for waking the two types of waiters +// ----------------------------------------------------------------------------- +WaitableEvent::WaitableEvent(bool manual_reset, bool initially_signaled) + : kernel_(new WaitableEventKernel(manual_reset, initially_signaled)) {} + +WaitableEvent::~WaitableEvent() {} + +void WaitableEvent::Reset() { + AutoLock locked(kernel_->lock_); + kernel_->signaled_ = false; +} + +void WaitableEvent::Signal() { + AutoLock locked(kernel_->lock_); + + if (kernel_->signaled_) return; + + if (kernel_->manual_reset_) { + SignalAll(); + kernel_->signaled_ = true; + } else { + // In the case of auto reset, if no waiters were woken, we remain + // signaled. + if (!SignalOne()) kernel_->signaled_ = true; + } +} + +bool WaitableEvent::IsSignaled() { + AutoLock locked(kernel_->lock_); + + const bool result = kernel_->signaled_; + if (result && !kernel_->manual_reset_) kernel_->signaled_ = false; + return result; +} + +// ----------------------------------------------------------------------------- +// Synchronous waits + +// ----------------------------------------------------------------------------- +// This is an synchronous waiter. The thread is waiting on the given condition +// variable and the fired flag in this object. +// ----------------------------------------------------------------------------- +class SyncWaiter : public WaitableEvent::Waiter { + public: + SyncWaiter(ConditionVariable* cv, Lock* lock) + : fired_(false), cv_(cv), lock_(lock), signaling_event_(NULL) {} + + bool Fire(WaitableEvent* signaling_event) override { + lock_->Acquire(); + const bool previous_value = fired_; + fired_ = true; + if (!previous_value) signaling_event_ = signaling_event; + lock_->Release(); + + if (previous_value) return false; + + cv_->Broadcast(); + + // SyncWaiters are stack allocated on the stack of the blocking thread. + return true; + } + + WaitableEvent* signaled_event() const { return signaling_event_; } + + // --------------------------------------------------------------------------- + // These waiters are always stack allocated and don't delete themselves. Thus + // there's no problem and the ABA tag is the same as the object pointer. + // --------------------------------------------------------------------------- + bool Compare(void* tag) override { return this == tag; } + + // --------------------------------------------------------------------------- + // Called with lock held. + // --------------------------------------------------------------------------- + bool fired() const { return fired_; } + + // --------------------------------------------------------------------------- + // During a TimedWait, we need a way to make sure that an auto-reset + // WaitableEvent doesn't think that this event has been signaled between + // unlocking it and removing it from the wait-list. Called with lock held. + // --------------------------------------------------------------------------- + void Disable() { fired_ = true; } + + private: + bool fired_; + ConditionVariable* const cv_; + Lock* const lock_; + WaitableEvent* signaling_event_; // The WaitableEvent which woke us +}; + +bool WaitableEvent::TimedWait(const TimeDelta& max_time) { + const TimeTicks end_time(TimeTicks::Now() + max_time); + const bool finite_time = max_time.ToInternalValue() >= 0; + + kernel_->lock_.Acquire(); + if (kernel_->signaled_) { + if (!kernel_->manual_reset_) { + // In this case we were signaled when we had no waiters. Now that + // someone has waited upon us, we can automatically reset. + kernel_->signaled_ = false; + } + + kernel_->lock_.Release(); + return true; + } + + Lock lock; + lock.Acquire(); + ConditionVariable cv(&lock); + SyncWaiter sw(&cv, &lock); + + Enqueue(&sw); + kernel_->lock_.Release(); + // We are violating locking order here by holding the SyncWaiter lock but not + // the WaitableEvent lock. However, this is safe because we don't lock @lock_ + // again before unlocking it. + + for (;;) { + const TimeTicks current_time(TimeTicks::Now()); + + if (sw.fired() || (finite_time && current_time >= end_time)) { + const bool return_value = sw.fired(); + + // We can't acquire @lock_ before releasing @lock (because of locking + // order), however, inbetween the two a signal could be fired and @sw + // would accept it, however we will still return false, so the signal + // would be lost on an auto-reset WaitableEvent. Thus we call Disable + // which makes sw::Fire return false. + sw.Disable(); + lock.Release(); + + kernel_->lock_.Acquire(); + kernel_->Dequeue(&sw, &sw); + kernel_->lock_.Release(); + + return return_value; + } + + if (finite_time) { + const TimeDelta max_wait(end_time - current_time); + cv.TimedWait(max_wait); + } else { + cv.Wait(); + } + } +} + +bool WaitableEvent::Wait() { return TimedWait(TimeDelta::FromSeconds(-1)); } + +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Synchronous waiting on multiple objects. + +static bool // StrictWeakOrdering +cmp_fst_addr(const std::pair<WaitableEvent*, unsigned>& a, + const std::pair<WaitableEvent*, unsigned>& b) { + return a.first < b.first; +} + +// static +size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables, size_t count) { + DCHECK(count) << "Cannot wait on no events"; + + // We need to acquire the locks in a globally consistent order. Thus we sort + // the array of waitables by address. We actually sort a pairs so that we can + // map back to the original index values later. + std::vector<std::pair<WaitableEvent*, size_t> > waitables; + waitables.reserve(count); + for (size_t i = 0; i < count; ++i) + waitables.push_back(std::make_pair(raw_waitables[i], i)); + + DCHECK_EQ(count, waitables.size()); + + sort(waitables.begin(), waitables.end(), cmp_fst_addr); + + // The set of waitables must be distinct. Since we have just sorted by + // address, we can check this cheaply by comparing pairs of consecutive + // elements. + for (size_t i = 0; i < waitables.size() - 1; ++i) { + DCHECK(waitables[i].first != waitables[i + 1].first); + } + + Lock lock; + ConditionVariable cv(&lock); + SyncWaiter sw(&cv, &lock); + + const size_t r = EnqueueMany(&waitables[0], count, &sw); + if (r) { + // One of the events is already signaled. The SyncWaiter has not been + // enqueued anywhere. EnqueueMany returns the count of remaining waitables + // when the signaled one was seen, so the index of the signaled event is + // @count - @r. + return waitables[count - r].second; + } + + // At this point, we hold the locks on all the WaitableEvents and we have + // enqueued our waiter in them all. + lock.Acquire(); + // Release the WaitableEvent locks in the reverse order + for (size_t i = 0; i < count; ++i) { + waitables[count - (1 + i)].first->kernel_->lock_.Release(); + } + + for (;;) { + if (sw.fired()) break; + + cv.Wait(); + } + lock.Release(); + + // The address of the WaitableEvent which fired is stored in the SyncWaiter. + WaitableEvent* const signaled_event = sw.signaled_event(); + // This will store the index of the raw_waitables which fired. + size_t signaled_index = 0; + + // Take the locks of each WaitableEvent in turn (except the signaled one) and + // remove our SyncWaiter from the wait-list + for (size_t i = 0; i < count; ++i) { + if (raw_waitables[i] != signaled_event) { + raw_waitables[i]->kernel_->lock_.Acquire(); + // There's no possible ABA issue with the address of the SyncWaiter here + // because it lives on the stack. Thus the tag value is just the pointer + // value again. + raw_waitables[i]->kernel_->Dequeue(&sw, &sw); + raw_waitables[i]->kernel_->lock_.Release(); + } else { + signaled_index = i; + } + } + + return signaled_index; +} + +// ----------------------------------------------------------------------------- +// If return value == 0: +// The locks of the WaitableEvents have been taken in order and the Waiter has +// been enqueued in the wait-list of each. None of the WaitableEvents are +// currently signaled +// else: +// None of the WaitableEvent locks are held. The Waiter has not been enqueued +// in any of them and the return value is the index of the first WaitableEvent +// which was signaled, from the end of the array. +// ----------------------------------------------------------------------------- +// static +size_t WaitableEvent::EnqueueMany(std::pair<WaitableEvent*, size_t>* waitables, + size_t count, Waiter* waiter) { + if (!count) return 0; + + waitables[0].first->kernel_->lock_.Acquire(); + if (waitables[0].first->kernel_->signaled_) { + if (!waitables[0].first->kernel_->manual_reset_) + waitables[0].first->kernel_->signaled_ = false; + waitables[0].first->kernel_->lock_.Release(); + return count; + } + + const size_t r = EnqueueMany(waitables + 1, count - 1, waiter); + if (r) { + waitables[0].first->kernel_->lock_.Release(); + } else { + waitables[0].first->Enqueue(waiter); + } + + return r; +} + +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Private functions... + +// ----------------------------------------------------------------------------- +// Wake all waiting waiters. Called with lock held. +// ----------------------------------------------------------------------------- +bool WaitableEvent::SignalAll() { + bool signaled_at_least_one = false; + + for (std::list<Waiter*>::iterator i = kernel_->waiters_.begin(); + i != kernel_->waiters_.end(); ++i) { + if ((*i)->Fire(this)) signaled_at_least_one = true; + } + + kernel_->waiters_.clear(); + return signaled_at_least_one; +} + +// --------------------------------------------------------------------------- +// Try to wake a single waiter. Return true if one was woken. Called with lock +// held. +// --------------------------------------------------------------------------- +bool WaitableEvent::SignalOne() { + for (;;) { + if (kernel_->waiters_.empty()) return false; + + const bool r = (*kernel_->waiters_.begin())->Fire(this); + kernel_->waiters_.pop_front(); + if (r) return true; + } +} + +// ----------------------------------------------------------------------------- +// Add a waiter to the list of those waiting. Called with lock held. +// ----------------------------------------------------------------------------- +void WaitableEvent::Enqueue(Waiter* waiter) { + kernel_->waiters_.push_back(waiter); +} + +// ----------------------------------------------------------------------------- +// Remove a waiter from the list of those waiting. Return true if the waiter was +// actually removed. Called with lock held. +// ----------------------------------------------------------------------------- +bool WaitableEvent::WaitableEventKernel::Dequeue(Waiter* waiter, void* tag) { + for (std::list<Waiter*>::iterator i = waiters_.begin(); i != waiters_.end(); + ++i) { + if (*i == waiter && (*i)->Compare(tag)) { + waiters_.erase(i); + return true; + } + } + + return false; +} + +// ----------------------------------------------------------------------------- + +} // namespace base diff --git a/ipc/chromium/src/base/waitable_event_win.cc b/ipc/chromium/src/base/waitable_event_win.cc new file mode 100644 index 0000000000..ca94c5cdd9 --- /dev/null +++ b/ipc/chromium/src/base/waitable_event_win.cc @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/waitable_event.h" + +#include <math.h> +#include <windows.h> + +#include "base/logging.h" +#include "base/time.h" + +namespace base { + +WaitableEvent::WaitableEvent(bool manual_reset, bool signaled) + : handle_(CreateEvent(NULL, manual_reset, signaled, NULL)) { + // We're probably going to crash anyways if this is ever NULL, so we might as + // well make our stack reports more informative by crashing here. + CHECK(handle_); +} + +WaitableEvent::~WaitableEvent() { CloseHandle(handle_); } + +void WaitableEvent::Reset() { ResetEvent(handle_); } + +void WaitableEvent::Signal() { SetEvent(handle_); } + +bool WaitableEvent::IsSignaled() { + return TimedWait(TimeDelta::FromMilliseconds(0)); +} + +bool WaitableEvent::Wait() { + DWORD result = WaitForSingleObject(handle_, INFINITE); + // It is most unexpected that this should ever fail. Help consumers learn + // about it if it should ever fail. + DCHECK(result == WAIT_OBJECT_0) << "WaitForSingleObject failed"; + return result == WAIT_OBJECT_0; +} + +bool WaitableEvent::TimedWait(const TimeDelta& max_time) { + DCHECK(max_time >= TimeDelta::FromMicroseconds(0)); + // Be careful here. TimeDelta has a precision of microseconds, but this API + // is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6? + // It should be 6 to avoid returning too early. + double timeout = ceil(max_time.InMillisecondsF()); + DWORD result = WaitForSingleObject(handle_, static_cast<DWORD>(timeout)); + switch (result) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + } + // It is most unexpected that this should ever fail. Help consumers learn + // about it if it should ever fail. + NOTREACHED() << "WaitForSingleObject failed"; + return false; +} + +// static +size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) { + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + CHECK(count <= MAXIMUM_WAIT_OBJECTS) + << "Can only wait on " << MAXIMUM_WAIT_OBJECTS << " with WaitMany"; + + for (size_t i = 0; i < count; ++i) handles[i] = events[i]->handle(); + + DWORD result = + WaitForMultipleObjects(count, handles, + FALSE, // don't wait for all the objects + INFINITE); // no timeout + if (result >= WAIT_OBJECT_0 + count) { + NOTREACHED() << "WaitForMultipleObjects failed: " << GetLastError(); + return 0; + } + + return result - WAIT_OBJECT_0; +} + +} // namespace base diff --git a/ipc/chromium/src/base/win_util.cc b/ipc/chromium/src/base/win_util.cc new file mode 100644 index 0000000000..611603f56e --- /dev/null +++ b/ipc/chromium/src/base/win_util.cc @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/win_util.h" + +#include <map> +#include <sddl.h> + +#include "base/logging.h" +#include "base/string_util.h" + +namespace win_util { + +std::wstring FormatMessage(unsigned messageid) { + wchar_t* string_buffer = NULL; + ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, messageid, 0, + reinterpret_cast<wchar_t*>(&string_buffer), 0, NULL); + + std::wstring formatted_string; + if (string_buffer) { + formatted_string = string_buffer; + LocalFree(reinterpret_cast<HLOCAL>(string_buffer)); + } else { + // The formating failed. simply convert the message value into a string. + SStringPrintf(&formatted_string, L"message number %d", messageid); + } + return formatted_string; +} + +std::wstring FormatLastWin32Error() { return FormatMessage(GetLastError()); } + +} // namespace win_util diff --git a/ipc/chromium/src/base/win_util.h b/ipc/chromium/src/base/win_util.h new file mode 100644 index 0000000000..b37411f60d --- /dev/null +++ b/ipc/chromium/src/base/win_util.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_WIN_UTIL_H__ +#define BASE_WIN_UTIL_H__ + +#include <windows.h> +#include <aclapi.h> + +#include <string> + +namespace win_util { + +// Uses the last Win32 error to generate a human readable message string. +std::wstring FormatLastWin32Error(); + +} // namespace win_util + +#endif // BASE_WIN_UTIL_H__ |