import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

41
ipc/app/Makefile.in Normal file
View file

@ -0,0 +1,41 @@
# 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 MOZ_WINCONSOLE
ifdef MOZ_DEBUG
MOZ_WINCONSOLE = 1
else
MOZ_WINCONSOLE = 0
endif
endif
# This switches $(INSTALL) to copy mode, like $(SYSINSTALL), so things that
# shouldn't get 755 perms need $(IFLAGS1) for either way of calling nsinstall.
NSDISTMODE = copy
include $(topsrcdir)/config/config.mk
include $(topsrcdir)/config/rules.mk
ifneq ($(MOZ_WIDGET_TOOLKIT),android)
#LIBS += ../contentproc/$(LIB_PREFIX)plugin-container.$(LIB_SUFFIX)
endif
ifeq ($(OS_ARCH),WINNT) #{
# Note the manifest file exists in the tree, so we use the explicit filename
# here.
EXTRA_DEPS += plugin-container.exe.manifest
endif #}
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) #{
libs::
$(NSINSTALL) -D $(DIST)/bin/$(PROGRAM).app
rsync -a -C --exclude '*.in' $(srcdir)/macbuild/Contents $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app
sed -e 's/%PROGRAM%/$(MOZ_CHILD_PROCESS_NAME)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app/Contents/Info.plist
sed -e 's/%APP_NAME%/$(MOZ_APP_DISPLAYNAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | \
iconv -f UTF-8 -t UTF-16 > $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app/Contents/Resources/English.lproj/InfoPlist.strings
$(NSINSTALL) -D $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app/Contents/MacOS
$(NSINSTALL) $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME) $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app/Contents/MacOS
endif #}

View file

@ -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: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "../contentproc/plugin-container.cpp"
#include "mozilla/WindowsDllBlocklist.h"
int
main(int argc, char *argv[])
{
#ifdef HAS_DLL_BLOCKLIST
DllBlocklist_Initialize();
#endif
return content_process_main(argc, argv);
}

View file

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: sw=4 ts=4 et :
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <dlfcn.h>
#include <android/log.h>
int
main(int argc, char* argv[])
{
// Check for the absolute minimum number of args we need to move
// forward here. We expect the last arg to be the child process type.
if (argc < 2)
return 1;
void *mozloader_handle = dlopen("libmozglue.so", RTLD_LAZY);
if (!mozloader_handle) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoChildLoad",
"Couldn't load mozloader because %s", dlerror());
return 1;
}
typedef int (*ChildProcessInit_t)(int, char**);
ChildProcessInit_t fChildProcessInit =
(ChildProcessInit_t)dlsym(mozloader_handle, "ChildProcessInit");
if (!fChildProcessInit) {
__android_log_print(ANDROID_LOG_ERROR, "GeckoChildLoad",
"Couldn't load cpi_t because %s", dlerror());
return 1;
}
return fChildProcessInit(argc, argv);
}

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>%PROGRAM%</string>
<key>CFBundleIdentifier</key>
<string>org.mozilla.plugincontainer</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSMinimumSystemVersion</key>
<string>10.5</string>
<key>LSMinimumSystemVersionByArchitecture</key>
<dict>
<key>i386</key>
<string>10.5.0</string>
<key>x86_64</key>
<string>10.6.0</string>
</dict>
<key>LSUIElement</key>
<string>1</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1 @@
APPL????

View file

@ -0,0 +1,7 @@
/* 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/. */
/* Localized versions of Info.plist keys */
CFBundleName = "%APP_NAME%";

6
ipc/app/module.ver Normal file
View file

@ -0,0 +1,6 @@
WIN32_MODULE_COMPANYNAME=Mozilla Corporation
WIN32_MODULE_PRODUCTVERSION=@MOZ_APP_WINVERSION@
WIN32_MODULE_PRODUCTVERSION_STRING=@MOZ_APP_VERSION@
WIN32_MODULE_DESCRIPTION=Plugin Container for @MOZ_APP_DISPLAYNAME@
WIN32_MODULE_PRODUCTNAME=@MOZ_APP_DISPLAYNAME@
WIN32_MODULE_NAME=@MOZ_APP_DISPLAYNAME@

114
ipc/app/moz.build Normal file
View file

@ -0,0 +1,114 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
# Any changes that affect Android need to be made in pie/moz.build as well.
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
Program(CONFIG['MOZ_CHILD_PROCESS_NAME'])
SOURCES += [
'MozillaRuntimeMainAndroid.cpp',
]
DIRS += ['pie']
else:
GeckoProgram(CONFIG['MOZ_CHILD_PROCESS_NAME'], linkage='dependent')
SOURCES += [
'MozillaRuntimeMain.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'/toolkit/xre',
'/xpcom/base',
]
# We link GMPLoader into plugin-container on desktop so that its code is
# covered by the desktop DRM vendor's voucher.
if CONFIG['OS_TARGET'] != 'Android':
SOURCES += [
'../../dom/media/gmp/GMPLoader.cpp',
]
USE_LIBS += [
'rlz',
]
# DELAYLOAD_DLLS in this block ensures that the DLL blocklist is functional
if CONFIG['OS_ARCH'] == 'WINNT':
DELAYLOAD_DLLS += [
'nss3.dll',
]
if CONFIG['MOZ_SANDBOX']:
# For sandbox includes and the include dependencies those have
LOCAL_INCLUDES += [
'/security/sandbox/chromium',
'/security/sandbox/chromium-shim',
]
USE_LIBS += [
'sandbox_s',
]
DELAYLOAD_DLLS += [
'winmm.dll',
'user32.dll',
]
DELAYLOAD_DLLS += [
'xul.dll',
]
if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] in ('Linux', 'Android'):
USE_LIBS += [
'mozsandbox',
]
# gcc lto likes to put the top level asm in syscall.cc in a different partition
# from the function using it which breaks the build. Work around that by
# forcing there to be only one partition.
if '-flto' in CONFIG['OS_CXXFLAGS'] and not CONFIG['CLANG_CXX']:
LDFLAGS += ['--param lto-partitions=1']
if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] == 'Darwin':
# For sandbox includes and the include dependencies those have
LOCAL_INCLUDES += [
'/security/sandbox/chromium',
'/security/sandbox/chromium-shim',
]
USE_LIBS += [
'mozsandbox',
]
if CONFIG['_MSC_VER']:
# Always enter a Windows program through wmain, whether or not we're
# a console application.
WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
LDFLAGS += CONFIG['MOZ_ALLOW_HEAP_EXECUTE_FLAGS']
# Control the default heap size.
# This is the heap returned by GetProcessHeap().
# As we use the CRT heap, the default size is too large and wastes VM.
#
# The default heap size is 1MB on Win32.
# The heap will grow if need be.
#
# Set it to 256k. See bug 127069.
if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
LDFLAGS += ['/HEAP:0x40000']
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
OS_LIBS += [
'binder',
'utils',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wshadow']
DEFINES['MOZ_PLUGIN_CONTAINER'] = 1;

30
ipc/app/pie/moz.build Normal file
View file

@ -0,0 +1,30 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
Program(CONFIG['MOZ_CHILD_PROCESS_NAME_PIE'])
SOURCES += [
'../MozillaRuntimeMainAndroid.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'/toolkit/xre',
'/xpcom/base',
]
if CONFIG['MOZ_SANDBOX']:
USE_LIBS += [
'mozsandbox',
]
# gcc lto likes to put the top level asm in syscall.cc in a different partition
# from the function using it which breaks the build. Work around that by
# forcing there to be only one partition.
if '-flto' in CONFIG['OS_CXXFLAGS'] and not CONFIG['CLANG_CXX']:
LDFLAGS += ['--param lto-partitions=1']
LDFLAGS += ['-pie']

View file

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="plugin-container"
type="win32"
/>
<description>Firefox Runtime</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:security>
<ms_asmv3:requestedPrivileges>
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
</ms_asmv3:requestedPrivileges>
</ms_asmv3:security>
</ms_asmv3:trustInfo>
<ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM</dpiAware>
</ms_asmv3:windowsSettings>
</ms_asmv3:application>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
</assembly>

View file

@ -0,0 +1,17 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
include('/ipc/chromium/chromium-config.mozbuild')
Library('chromium_atomics')
# This test is copied from ../moz.build for atomicops_internals_mutex.cc
ost = CONFIG['OS_TEST']
if '86' not in ost and 'arm' not in ost and 'aarch64' != ost and 'mips' not in ost:
SOURCES += [
'../src/base/atomicops_internals_mutex.cc',
'../src/base/lock_impl_posix.cc',
]

View file

@ -0,0 +1,69 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
LOCAL_INCLUDES += [
'!/ipc/ipdl/_ipdlheaders',
'/ipc/chromium/src',
'/ipc/glue',
]
if CONFIG['OS_ARCH'] == 'WINNT':
OS_LIBS += [
'psapi',
'shell32',
'dbghelp',
]
DEFINES.update({
'UNICODE': True,
'_UNICODE': True,
'_CRT_RAND_S': True,
'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS': True,
'_SECURE_ATL': True,
'CHROMIUM_BUILD': True,
'U_STATIC_IMPLEMENTATION': True,
'OS_WIN': 1,
'WIN32': True,
'_WIN32': True,
'_WINDOWS': True,
'WIN32_LEAN_AND_MEAN': True,
})
if CONFIG['_MSC_VER']:
DEFINES['COMPILER_MSVC'] = True
else:
DEFINES['OS_POSIX'] = 1
if CONFIG['OS_ARCH'] == 'Darwin':
DEFINES['OS_MACOSX'] = 1
elif CONFIG['OS_ARCH'] == 'DragonFly':
DEFINES.update({
'OS_DRAGONFLY': 1,
'OS_BSD': 1,
})
elif CONFIG['OS_ARCH'] == 'FreeBSD' or CONFIG['OS_ARCH'] == 'GNU_kFreeBSD':
DEFINES.update({
'OS_FREEBSD': 1,
'OS_BSD': 1,
})
elif CONFIG['OS_ARCH'] == 'NetBSD':
DEFINES.update({
'OS_NETBSD': 1,
'OS_BSD': 1,
})
elif CONFIG['OS_ARCH'] == 'OpenBSD':
DEFINES.update({
'OS_OPENBSD': 1,
'OS_BSD': 1,
})
else:
DEFINES['OS_LINUX'] = 1

160
ipc/chromium/moz.build Normal file
View file

@ -0,0 +1,160 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
libevent_path_prefix = 'src/third_party'
include(libevent_path_prefix + '/libeventcommon.mozbuild')
UNIFIED_SOURCES += [
'src/base/at_exit.cc',
'src/base/command_line.cc',
'src/base/file_path.cc',
'src/base/file_util.cc',
'src/base/histogram.cc',
'src/base/lock.cc',
'src/base/logging.cc',
'src/base/message_loop.cc',
'src/base/message_pump_default.cc',
'src/base/pickle.cc',
'src/base/rand_util.cc',
'src/base/revocable_store.cc',
'src/base/string_piece.cc',
'src/base/string_util.cc',
'src/base/thread.cc',
'src/base/time.cc',
'src/base/timer.cc',
'src/chrome/common/child_process.cc',
'src/chrome/common/child_process_host.cc',
'src/chrome/common/child_thread.cc',
'src/chrome/common/chrome_switches.cc',
'src/chrome/common/ipc_channel.cc',
'src/chrome/common/ipc_message.cc',
]
if os_win:
SOURCES += [
'src/base/condition_variable_win.cc',
'src/base/cpu.cc',
'src/base/file_util_win.cc',
'src/base/lock_impl_win.cc',
'src/base/message_pump_win.cc',
'src/base/object_watcher.cc',
'src/base/platform_file_win.cc',
'src/base/platform_thread_win.cc',
'src/base/process_util_win.cc',
'src/base/process_win.cc',
'src/base/rand_util_win.cc',
'src/base/shared_memory_win.cc',
'src/base/sys_info_win.cc',
'src/base/sys_string_conversions_win.cc',
'src/base/thread_local_storage_win.cc',
'src/base/thread_local_win.cc',
'src/base/time_win.cc',
'src/base/waitable_event_win.cc',
'src/base/win_util.cc',
'src/chrome/common/ipc_channel_win.cc',
'src/chrome/common/process_watcher_win.cc',
'src/chrome/common/transport_dib_win.cc',
]
elif not CONFIG['MOZ_SYSTEM_LIBEVENT']:
DIRS += ['src/third_party']
if os_posix:
UNIFIED_SOURCES += [
'src/base/condition_variable_posix.cc',
'src/base/file_descriptor_shuffle.cc',
'src/base/file_util_posix.cc',
'src/base/lock_impl_posix.cc',
'src/base/message_pump_libevent.cc',
'src/base/platform_file_posix.cc',
'src/base/platform_thread_posix.cc',
'src/base/process_posix.cc',
'src/base/process_util_posix.cc',
'src/base/rand_util_posix.cc',
'src/base/shared_memory_posix.cc',
'src/base/string16.cc',
'src/base/sys_info_posix.cc',
'src/base/thread_local_posix.cc',
'src/base/thread_local_storage_posix.cc',
'src/base/waitable_event_posix.cc',
'src/chrome/common/file_descriptor_set_posix.cc',
'src/chrome/common/ipc_channel_posix.cc',
'src/chrome/common/process_watcher_posix_sigchld.cc',
]
if os_macosx:
UNIFIED_SOURCES += [
'src/base/chrome_application_mac.mm',
'src/base/file_util_mac.mm',
'src/base/mac_util.mm',
'src/base/message_pump_mac.mm',
'src/base/process_util_mac.mm',
'src/base/scoped_nsautorelease_pool.mm',
'src/base/sys_info_mac.cc',
'src/base/sys_string_conversions_mac.mm',
'src/base/time_mac.cc',
'src/chrome/common/mach_ipc_mac.mm',
'src/chrome/common/mach_message_source_mac.cc',
'src/chrome/common/transport_dib_mac.cc',
]
SOURCES += [
# This file cannot be built in unified mode because of the redefinition
# of NoOp.
'src/base/platform_thread_mac.mm',
]
if os_bsd:
SOURCES += [
'src/base/atomicops_internals_x86_gcc.cc',
'src/base/time_posix.cc',
]
if CONFIG['OS_ARCH'] == 'GNU_kFreeBSD':
SOURCES += [
'src/base/process_util_linux.cc'
]
else:
SOURCES += [
'src/base/process_util_bsd.cc'
]
if os_linux:
SOURCES += [
'src/base/atomicops_internals_x86_gcc.cc',
'src/base/process_util_linux.cc',
'src/base/time_posix.cc',
]
if CONFIG['OS_TARGET'] == 'Android':
UNIFIED_SOURCES += [
'src/base/message_pump_android.cc',
]
DEFINES['ANDROID'] = True
DEFINES['_POSIX_MONOTONIC_CLOCK'] = 0
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
DEFINES['HAVE_ANDROID_OS'] = True
if os_bsd or os_linux:
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
SOURCES += [
'src/base/message_pump_glib.cc',
]
ost = CONFIG['OS_TEST']
if '86' not in ost and 'arm' not in ost and 'aarch64' != ost and 'mips' not in ost:
SOURCES += [
'src/base/atomicops_internals_mutex.cc',
]
CXXFLAGS += CONFIG['TK_CFLAGS']
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
DIRS += [
'atomics',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

27
ipc/chromium/src/LICENSE Normal file
View file

@ -0,0 +1,27 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,74 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/at_exit.h"
#include "base/logging.h"
namespace base {
// Keep a stack of registered AtExitManagers. We always operate on the most
// recent, and we should never have more than one outside of testing, when we
// use the shadow version of the constructor. We don't protect this for
// thread-safe access, since it will only be modified in testing.
static AtExitManager* g_top_manager = NULL;
AtExitManager::AtExitManager() : next_manager_(NULL) {
DCHECK(!g_top_manager);
g_top_manager = this;
}
AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) {
DCHECK(shadow || !g_top_manager);
g_top_manager = this;
}
AtExitManager::~AtExitManager() {
if (!g_top_manager) {
NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager";
return;
}
DCHECK(g_top_manager == this);
ProcessCallbacksNow();
g_top_manager = next_manager_;
}
// static
void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) {
if (!g_top_manager) {
NOTREACHED() << "Tried to RegisterCallback without an AtExitManager";
return;
}
DCHECK(func);
AutoLock lock(g_top_manager->lock_);
g_top_manager->stack_.push(CallbackAndParam(func, param));
}
// static
void AtExitManager::ProcessCallbacksNow() {
if (!g_top_manager) {
NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager";
return;
}
AutoLock lock(g_top_manager->lock_);
while (!g_top_manager->stack_.empty()) {
CallbackAndParam callback_and_param = g_top_manager->stack_.top();
g_top_manager->stack_.pop();
callback_and_param.func_(callback_and_param.param_);
}
}
// static
bool AtExitManager::AlreadyRegistered() {
return !!g_top_manager;
}
} // namespace base

View file

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_AT_EXIT_H_
#define BASE_AT_EXIT_H_
#include <stack>
#include "base/basictypes.h"
#include "base/lock.h"
namespace base {
// This class provides a facility similar to the CRT atexit(), except that
// we control when the callbacks are executed. Under Windows for a DLL they
// happen at a really bad time and under the loader lock. This facility is
// mostly used by base::Singleton.
//
// The usage is simple. Early in the main() or WinMain() scope create an
// AtExitManager object on the stack:
// int main(...) {
// base::AtExitManager exit_manager;
//
// }
// When the exit_manager object goes out of scope, all the registered
// callbacks and singleton destructors will be called.
class AtExitManager {
protected:
// This constructor will allow this instance of AtExitManager to be created
// even if one already exists. This should only be used for testing!
// AtExitManagers are kept on a global stack, and it will be removed during
// destruction. This allows you to shadow another AtExitManager.
explicit AtExitManager(bool shadow);
public:
typedef void (*AtExitCallbackType)(void*);
AtExitManager();
// The dtor calls all the registered callbacks. Do not try to register more
// callbacks after this point.
~AtExitManager();
// Registers the specified function to be called at exit. The prototype of
// the callback function is void func().
static void RegisterCallback(AtExitCallbackType func, void* param);
// Calls the functions registered with RegisterCallback in LIFO order. It
// is possible to register new callbacks after calling this function.
static void ProcessCallbacksNow();
static bool AlreadyRegistered();
private:
struct CallbackAndParam {
CallbackAndParam(AtExitCallbackType func, void* param)
: func_(func), param_(param) { }
AtExitCallbackType func_;
void* param_;
};
Lock lock_;
std::stack<CallbackAndParam> stack_;
AtExitManager* next_manager_; // Stack of managers to allow shadowing.
DISALLOW_COPY_AND_ASSIGN(AtExitManager);
};
} // namespace base
#endif // BASE_AT_EXIT_H_

View file

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ATOMIC_SEQUENCE_NUM_H_
#define BASE_ATOMIC_SEQUENCE_NUM_H_
#include "base/atomicops.h"
#include "base/basictypes.h"
namespace base {
class AtomicSequenceNumber;
// Static (POD) AtomicSequenceNumber that MUST be used in global scope (or
// non-function scope) ONLY. This implementation does not generate any static
// initializer. Note that it does not implement any constructor which means
// that its fields are not initialized except when it is stored in the global
// data section (.data in ELF). If you want to allocate an atomic sequence
// number on the stack (or heap), please use the AtomicSequenceNumber class
// declared below.
class StaticAtomicSequenceNumber {
public:
inline int GetNext() {
return static_cast<int>(
base::subtle::NoBarrier_AtomicIncrement(&seq_, 1) - 1);
}
private:
friend class AtomicSequenceNumber;
inline void Reset() {
base::subtle::Release_Store(&seq_, 0);
}
base::subtle::Atomic32 seq_;
};
// AtomicSequenceNumber that can be stored and used safely (i.e. its fields are
// always initialized as opposed to StaticAtomicSequenceNumber declared above).
// Please use StaticAtomicSequenceNumber if you want to declare an atomic
// sequence number in the global scope.
class AtomicSequenceNumber {
public:
AtomicSequenceNumber() {
seq_.Reset();
}
inline int GetNext() {
return seq_.GetNext();
}
private:
StaticAtomicSequenceNumber seq_;
DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber);
};
} // namespace base
#endif // BASE_ATOMIC_SEQUENCE_NUM_H_

View file

@ -0,0 +1,155 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// For atomic operations on reference counts, see atomic_refcount.h.
// For atomic operations on sequence numbers, see atomic_sequence_num.h.
// The routines exported by this module are subtle. If you use them, even if
// you get the code right, it will depend on careful reasoning about atomicity
// and memory ordering; it will be less readable, and harder to maintain. If
// you plan to use these routines, you should have a good reason, such as solid
// evidence that performance would otherwise suffer, or there being no
// alternative. You should assume only properties explicitly guaranteed by the
// specifications in this file. You are almost certainly _not_ writing code
// just for the x86; if you assume x86 semantics, x86 hardware bugs and
// implementations on other archtectures will cause your code to break. If you
// do not know what you are doing, avoid these routines, and use a Mutex.
//
// It is incorrect to make direct assignments to/from an atomic variable.
// You should use one of the Load or Store routines. The NoBarrier
// versions are provided when no barriers are needed:
// NoBarrier_Store()
// NoBarrier_Load()
// Although there are currently no compiler enforcement, you are encouraged
// to use these.
//
#ifndef BASE_ATOMICOPS_H_
#define BASE_ATOMICOPS_H_
#include "base/basictypes.h"
#include "base/port.h"
namespace base {
namespace subtle {
// Bug 1308991. We need this for /Wp64, to mark it safe for AtomicWord casting.
#ifndef OS_WIN
#define __w64
#endif
typedef __w64 int32_t Atomic32;
#ifdef ARCH_CPU_64_BITS
typedef int64_t Atomic64;
#endif
// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
// Atomic64 routines below, depending on your architecture.
#ifdef OS_OPENBSD
#ifdef ARCH_CPU_64_BITS
typedef Atomic64 AtomicWord;
#else
typedef Atomic32 AtomicWord;
#endif // ARCH_CPU_64_BITS
#else
typedef intptr_t AtomicWord;
#endif // OS_OPENBSD
// Atomically execute:
// result = *ptr;
// if (*ptr == old_value)
// *ptr = new_value;
// return result;
//
// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
// Always return the old value of "*ptr"
//
// This routine implies no memory barriers.
Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
// Atomically store new_value into *ptr, returning the previous value held in
// *ptr. This routine implies no memory barriers.
Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value);
// Atomically increment *ptr by "increment". Returns the new value of
// *ptr with the increment applied. This routine implies no memory barriers.
Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment);
Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment);
// These following lower-level operations are typically useful only to people
// implementing higher-level synchronization operations like spinlocks,
// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or
// a store with appropriate memory-ordering instructions. "Acquire" operations
// ensure that no later memory access can be reordered ahead of the operation.
// "Release" operations ensure that no previous memory access can be reordered
// after the operation. "Barrier" operations have both "Acquire" and "Release"
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
// access.
Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
void MemoryBarrier();
void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
void Acquire_Store(volatile Atomic32* ptr, Atomic32 value);
void Release_Store(volatile Atomic32* ptr, Atomic32 value);
Atomic32 NoBarrier_Load(volatile const Atomic32* ptr);
Atomic32 Acquire_Load(volatile const Atomic32* ptr);
Atomic32 Release_Load(volatile const Atomic32* ptr);
// 64-bit atomic operations (only available on 64-bit processors).
#ifdef ARCH_CPU_64_BITS
Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value);
Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment);
Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value);
void Acquire_Store(volatile Atomic64* ptr, Atomic64 value);
void Release_Store(volatile Atomic64* ptr, Atomic64 value);
Atomic64 NoBarrier_Load(volatile const Atomic64* ptr);
Atomic64 Acquire_Load(volatile const Atomic64* ptr);
Atomic64 Release_Load(volatile const Atomic64* ptr);
#endif // CPU_ARCH_64_BITS
} // namespace base::subtle
} // namespace base
// Include our platform specific implementation.
#if defined(OS_WIN) && defined(ARCH_CPU_X86_FAMILY)
#include "base/atomicops_internals_x86_msvc.h"
#elif defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY)
#include "base/atomicops_internals_x86_macosx.h"
#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
#include "base/atomicops_internals_x86_gcc.h"
#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL)
#include "base/atomicops_internals_arm_gcc.h"
#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM64)
#include "base/atomicops_internals_arm64_gcc.h"
#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS)
#include "base/atomicops_internals_mips_gcc.h"
#elif defined(COMPILER_GCC) && defined(ARCH_CPU_PPC_FAMILY)
#include "base/atomicops_internals_ppc_gcc.h"
#else
#include "base/atomicops_internals_mutex.h"
#endif
#endif // BASE_ATOMICOPS_H_

View file

@ -0,0 +1,362 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is an internal atomic implementation, use base/atomicops.h instead.
// TODO(rmcilroy): Investigate whether we can use __sync__ intrinsics instead of
// the hand coded assembly without introducing perf regressions.
// TODO(rmcilroy): Investigate whether we can use acquire / release versions of
// exclusive load / store assembly instructions and do away with
// the barriers.
#ifndef BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
#define BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
#if defined(OS_QNX)
#include <sys/cpuinline.h>
#endif
namespace base {
namespace subtle {
inline void MemoryBarrier() {
__asm__ __volatile__ ( // NOLINT
"dmb ish \n\t" // Data memory barrier.
::: "memory"
); // NOLINT
}
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev;
int32_t temp;
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %w[prev], %[ptr] \n\t" // Load the previous value.
"cmp %w[prev], %w[old_value] \n\t"
"bne 1f \n\t"
"stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value.
"cbnz %w[temp], 0b \n\t" // Retry if it did not work.
"1: \n\t"
"clrex \n\t" // In case we didn't swap.
: [prev]"=&r" (prev),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [old_value]"r" (old_value),
[new_value]"r" (new_value)
: "memory", "cc"
); // NOLINT
return prev;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 result;
int32_t temp;
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %w[result], %[ptr] \n\t" // Load the previous value.
"stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value.
"cbnz %w[temp], 0b \n\t" // Retry if it did not work.
: [result]"=&r" (result),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [new_value]"r" (new_value)
: "memory"
); // NOLINT
return result;
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
Atomic32 result;
int32_t temp;
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %w[result], %[ptr] \n\t" // Load the previous value.
"add %w[result], %w[result], %w[increment]\n\t"
"stxr %w[temp], %w[result], %[ptr] \n\t" // Try to store the result.
"cbnz %w[temp], 0b \n\t" // Retry on failure.
: [result]"=&r" (result),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [increment]"r" (increment)
: "memory"
); // NOLINT
return result;
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
MemoryBarrier();
Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment);
MemoryBarrier();
return result;
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev;
int32_t temp;
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %w[prev], %[ptr] \n\t" // Load the previous value.
"cmp %w[prev], %w[old_value] \n\t"
"bne 1f \n\t"
"stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value.
"cbnz %w[temp], 0b \n\t" // Retry if it did not work.
"dmb ish \n\t" // Data memory barrier.
"1: \n\t"
// If the compare failed the 'dmb' is unnecessary, but we still need a
// 'clrex'.
"clrex \n\t"
: [prev]"=&r" (prev),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [old_value]"r" (old_value),
[new_value]"r" (new_value)
: "memory", "cc"
); // NOLINT
return prev;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev;
int32_t temp;
MemoryBarrier();
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %w[prev], %[ptr] \n\t" // Load the previous value.
"cmp %w[prev], %w[old_value] \n\t"
"bne 1f \n\t"
"stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value.
"cbnz %w[temp], 0b \n\t" // Retry if it did not work.
"1: \n\t"
// If the compare failed the we still need a 'clrex'.
"clrex \n\t"
: [prev]"=&r" (prev),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [old_value]"r" (old_value),
[new_value]"r" (new_value)
: "memory", "cc"
); // NOLINT
return prev;
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
// 64-bit versions of the operations.
// See the 32-bit versions for comments.
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev;
int32_t temp;
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %[prev], %[ptr] \n\t"
"cmp %[prev], %[old_value] \n\t"
"bne 1f \n\t"
"stxr %w[temp], %[new_value], %[ptr] \n\t"
"cbnz %w[temp], 0b \n\t"
"1: \n\t"
"clrex \n\t"
: [prev]"=&r" (prev),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [old_value]"r" (old_value),
[new_value]"r" (new_value)
: "memory", "cc"
); // NOLINT
return prev;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
Atomic64 result;
int32_t temp;
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %[result], %[ptr] \n\t"
"stxr %w[temp], %[new_value], %[ptr] \n\t"
"cbnz %w[temp], 0b \n\t"
: [result]"=&r" (result),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [new_value]"r" (new_value)
: "memory"
); // NOLINT
return result;
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
Atomic64 result;
int32_t temp;
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %[result], %[ptr] \n\t"
"add %[result], %[result], %[increment] \n\t"
"stxr %w[temp], %[result], %[ptr] \n\t"
"cbnz %w[temp], 0b \n\t"
: [result]"=&r" (result),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [increment]"r" (increment)
: "memory"
); // NOLINT
return result;
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
MemoryBarrier();
Atomic64 result = NoBarrier_AtomicIncrement(ptr, increment);
MemoryBarrier();
return result;
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev;
int32_t temp;
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %[prev], %[ptr] \n\t"
"cmp %[prev], %[old_value] \n\t"
"bne 1f \n\t"
"stxr %w[temp], %[new_value], %[ptr] \n\t"
"cbnz %w[temp], 0b \n\t"
"dmb ish \n\t"
"1: \n\t"
"clrex \n\t"
: [prev]"=&r" (prev),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [old_value]"r" (old_value),
[new_value]"r" (new_value)
: "memory", "cc"
); // NOLINT
return prev;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev;
int32_t temp;
MemoryBarrier();
__asm__ __volatile__ ( // NOLINT
"0: \n\t"
"ldxr %[prev], %[ptr] \n\t"
"cmp %[prev], %[old_value] \n\t"
"bne 1f \n\t"
"stxr %w[temp], %[new_value], %[ptr] \n\t"
"cbnz %w[temp], 0b \n\t"
"1: \n\t"
"clrex \n\t"
: [prev]"=&r" (prev),
[temp]"=&r" (temp),
[ptr]"+Q" (*ptr)
: [old_value]"r" (old_value),
[new_value]"r" (new_value)
: "memory", "cc"
); // NOLINT
return prev;
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier();
return *ptr;
}
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_

View file

@ -0,0 +1,126 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is an internal atomic implementation, use base/atomicops.h instead.
//
// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
namespace base {
namespace subtle {
// 0xffff0fc0 is the hard coded address of a function provided by
// the kernel which implements an atomic compare-exchange. On older
// ARM architecture revisions (pre-v6) this may be implemented using
// a syscall. This address is stable, and in active use (hard coded)
// by at least glibc-2.7 and the Android C library.
typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value,
Atomic32 new_value,
volatile Atomic32* ptr);
LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg __attribute__((weak)) =
(LinuxKernelCmpxchgFunc) 0xffff0fc0;
typedef void (*LinuxKernelMemoryBarrierFunc)(void);
LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
(LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value = *ptr;
do {
if (!pLinuxKernelCmpxchg(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (pLinuxKernelCmpxchg(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return Barrier_AtomicIncrement(ptr, increment);
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
for (;;) {
// Atomic exchange the old value with an incremented one.
Atomic32 old_value = *ptr;
Atomic32 new_value = old_value + increment;
if (pLinuxKernelCmpxchg(old_value, new_value,
const_cast<Atomic32*>(ptr)) == 0) {
// The exchange took place as expected.
return new_value;
}
// Otherwise, *ptr changed mid-loop and we need to retry.
}
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void MemoryBarrier() {
pLinuxKernelMemoryBarrier();
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_

View file

@ -0,0 +1,305 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file is an internal atomic implementation, use base/atomicops.h instead.
//
// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears.
#ifndef GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_MIPS_GCC_H_
#define GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_MIPS_GCC_H_
namespace base {
namespace subtle {
// Atomically execute:
// result = *ptr;
// if (*ptr == old_value)
// *ptr = new_value;
// return result;
//
// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
// Always return the old value of "*ptr"
//
// This routine implies no memory barriers.
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev, tmp;
__asm__ __volatile__(".set push\n"
".set noreorder\n"
"1:\n"
"ll %0, %5\n" // prev = *ptr
"bne %0, %3, 2f\n" // if (prev != old_value) goto 2
"move %2, %4\n" // tmp = new_value
"sc %2, %1\n" // *ptr = tmp (with atomic check)
"beqz %2, 1b\n" // start again on atomic error
"nop\n" // delay slot nop
"2:\n"
".set pop\n"
: "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
: "r" (old_value), "r" (new_value), "m" (*ptr)
: "memory");
return prev;
}
// Atomically store new_value into *ptr, returning the previous value held in
// *ptr. This routine implies no memory barriers.
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 temp, old;
__asm__ __volatile__(".set push\n"
".set noreorder\n"
"1:\n"
"ll %1, %4\n" // old = *ptr
"move %0, %3\n" // temp = new_value
"sc %0, %2\n" // *ptr = temp (with atomic check)
"beqz %0, 1b\n" // start again on atomic error
"nop\n" // delay slot nop
".set pop\n"
: "=&r" (temp), "=&r" (old), "=m" (*ptr)
: "r" (new_value), "m" (*ptr)
: "memory");
return old;
}
// Atomically increment *ptr by "increment". Returns the new value of
// *ptr with the increment applied. This routine implies no memory barriers.
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
Atomic32 temp, temp2;
__asm__ __volatile__(".set push\n"
".set noreorder\n"
"1:\n"
"ll %0, %4\n" // temp = *ptr
"addu %1, %0, %3\n" // temp2 = temp + increment
"sc %1, %2\n" // *ptr = temp2 (with atomic check)
"beqz %1, 1b\n" // start again on atomic error
"addu %1, %0, %3\n" // temp2 = temp + increment
".set pop\n"
: "=&r" (temp), "=&r" (temp2), "=m" (*ptr)
: "Ir" (increment), "m" (*ptr)
: "memory");
// temp2 now holds the final value.
return temp2;
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
MemoryBarrier();
Atomic32 res = NoBarrier_AtomicIncrement(ptr, increment);
MemoryBarrier();
return res;
}
// "Acquire" operations
// ensure that no later memory access can be reordered ahead of the operation.
// "Release" operations ensure that no previous memory access can be reordered
// after the operation. "Barrier" operations have both "Acquire" and "Release"
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
// access.
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
MemoryBarrier();
return res;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
MemoryBarrier();
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void MemoryBarrier() {
__asm__ __volatile__("sync" : : : "memory");
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
#if defined(__LP64__)
// 64-bit versions of the atomic ops.
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev, tmp;
__asm__ __volatile__(".set push\n"
".set noreorder\n"
"1:\n"
"lld %0, %5\n" // prev = *ptr
"bne %0, %3, 2f\n" // if (prev != old_value) goto 2
"move %2, %4\n" // tmp = new_value
"scd %2, %1\n" // *ptr = tmp (with atomic check)
"beqz %2, 1b\n" // start again on atomic error
"nop\n" // delay slot nop
"2:\n"
".set pop\n"
: "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
: "r" (old_value), "r" (new_value), "m" (*ptr)
: "memory");
return prev;
}
// Atomically store new_value into *ptr, returning the previous value held in
// *ptr. This routine implies no memory barriers.
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
Atomic64 temp, old;
__asm__ __volatile__(".set push\n"
".set noreorder\n"
"1:\n"
"lld %1, %4\n" // old = *ptr
"move %0, %3\n" // temp = new_value
"scd %0, %2\n" // *ptr = temp (with atomic check)
"beqz %0, 1b\n" // start again on atomic error
"nop\n" // delay slot nop
".set pop\n"
: "=&r" (temp), "=&r" (old), "=m" (*ptr)
: "r" (new_value), "m" (*ptr)
: "memory");
return old;
}
// Atomically increment *ptr by "increment". Returns the new value of
// *ptr with the increment applied. This routine implies no memory barriers.
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
Atomic64 temp, temp2;
__asm__ __volatile__(".set push\n"
".set noreorder\n"
"1:\n"
"lld %0, %4\n" // temp = *ptr
"daddu %1, %0, %3\n" // temp2 = temp + increment
"scd %1, %2\n" // *ptr = temp2 (with atomic check)
"beqz %1, 1b\n" // start again on atomic error
"daddu %1, %0, %3\n" // temp2 = temp + increment
".set pop\n"
: "=&r" (temp), "=&r" (temp2), "=m" (*ptr)
: "Ir" (increment), "m" (*ptr)
: "memory");
// temp2 now holds the final value.
return temp2;
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
MemoryBarrier();
Atomic64 res = NoBarrier_AtomicIncrement(ptr, increment);
MemoryBarrier();
return res;
}
// "Acquire" operations
// ensure that no later memory access can be reordered ahead of the operation.
// "Release" operations ensure that no previous memory access can be reordered
// after the operation. "Barrier" operations have both "Acquire" and "Release"
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
// access.
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
MemoryBarrier();
return res;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
MemoryBarrier();
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier();
return *ptr;
}
#endif
} // namespace base::subtle
} // namespace base
#endif // GOOGLE_PROTOBUF_ATOMICOPS_INTERNALS_MIPS_GCC_H_

View file

@ -0,0 +1,15 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/atomicops.h"
namespace base {
namespace subtle {
Lock gAtomicsMutex;
} // namespace subtle
} // namespace base

View file

@ -0,0 +1,251 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This file is an internal atomic implementation, use
// base/atomicops.h instead.
//
// This is a very slow fallback implementation of atomic operations
// that uses a mutex instead of atomic instructions.
//
// (NB: a small "optimization" here would be using a spinlock instead
// of a blocking mutex, but it's probably not worth the time.)
#ifndef base_atomicops_internals_mutex_h
#define base_atomicops_internals_mutex_h
#include "base/lock.h"
namespace base {
namespace subtle {
extern Lock gAtomicsMutex;
template<typename T>
T Locked_CAS(volatile T* ptr, T old_value, T new_value) {
AutoLock _(gAtomicsMutex);
T current_value = *ptr;
if (current_value == old_value)
*ptr = new_value;
return current_value;
}
template<typename T>
T Locked_AtomicExchange(volatile T* ptr, T new_value) {
AutoLock _(gAtomicsMutex);
T current_value = *ptr;
*ptr = new_value;
return current_value;
}
template<typename T>
T Locked_AtomicIncrement(volatile T* ptr, T increment) {
AutoLock _(gAtomicsMutex);
return *ptr += increment;
}
template<typename T>
void Locked_Store(volatile T* ptr, T value) {
AutoLock _(gAtomicsMutex);
*ptr = value;
}
template<typename T>
T Locked_Load(volatile const T* ptr) {
AutoLock _(gAtomicsMutex);
return *ptr;
}
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return Locked_CAS(ptr, old_value, new_value);
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
return Locked_AtomicExchange(ptr, new_value);
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return Locked_AtomicIncrement(ptr, increment);
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return Locked_AtomicIncrement(ptr, increment);
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return Locked_CAS(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return Locked_CAS(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
return Locked_Store(ptr, value);
}
inline void MemoryBarrier() {
AutoLock _(gAtomicsMutex);
// lock/unlock work as a barrier here
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
return Locked_Store(ptr, value);
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
return Locked_Store(ptr, value);
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return Locked_Load(ptr);
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
return NoBarrier_Load(ptr);
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
return Locked_Load(ptr);
}
#ifdef ARCH_CPU_64_BITS
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
return Locked_CAS(ptr, old_value, new_value);
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
return Locked_AtomicExchange(ptr, new_value);
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
return Locked_AtomicIncrement(ptr, increment);
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
return Locked_AtomicIncrement(ptr, increment);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
return Locked_Store(ptr, value);
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
return Locked_CAS(ptr, old_value, new_value);
}
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
return Locked_Store(ptr, value);
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
return Locked_Store(ptr, value);
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return Locked_Load(ptr);
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
return Locked_Load(ptr);
}
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
return Locked_Load(ptr);
}
#endif // ARCH_CPU_64_BITS
#ifdef OS_MACOSX
// From atomicops_internals_x86_macosx.h:
//
// MacOS uses long for intptr_t, AtomicWord and Atomic32 are always
// different on the Mac, even when they are the same size. We need
// to explicitly cast from AtomicWord to Atomic32/64 to implement
// the AtomicWord interface.
inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return Locked_CAS(ptr, old_value, new_value);
}
inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr,
AtomicWord new_value) {
return Locked_AtomicExchange(ptr, new_value);
}
inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr,
AtomicWord increment) {
return Locked_AtomicIncrement(ptr, increment);
}
inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr,
AtomicWord increment) {
return Locked_AtomicIncrement(ptr, increment);
}
inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return Locked_CAS(ptr, old_value, new_value);
}
inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return Locked_CAS(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) {
return Locked_Store(ptr, value);
}
inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) {
return Locked_Store(ptr, value);
}
inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) {
return Locked_Store(ptr, value);
}
inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) {
return Locked_Load(ptr);
}
inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) {
return Locked_Load(ptr);
}
inline AtomicWord Release_Load(volatile const AtomicWord* ptr) {
return Locked_Load(ptr);
}
#endif // OS_MACOSX
} // namespace subtle
} // namespace base
#endif // base_atomicops_internals_mutex_h

View file

@ -0,0 +1,134 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright 2012 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is an internal atomic implementation, use atomicops.h instead.
//
#ifndef BASE_ATOMICOPS_INTERNALS_PPC_H_
#define BASE_ATOMICOPS_INTERNALS_PPC_H_
namespace base {
namespace subtle {
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return (__sync_val_compare_and_swap(ptr, old_value, new_value));
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (__sync_bool_compare_and_swap(ptr, old_value, new_value) == false);
return old_value;
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return Barrier_AtomicIncrement(ptr, increment);
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
for (;;) {
Atomic32 old_value = *ptr;
Atomic32 new_value = old_value + increment;
if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
return new_value;
// The exchange took place as expected.
}
// Otherwise, *ptr changed mid-loop and we need to retry.
}
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value, Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void MemoryBarrier() {
__asm__ __volatile__("sync" : : : "memory"); }
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { return *ptr; }
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
#ifdef ARCH_CPU_PPC64
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
return (__sync_val_compare_and_swap(ptr, old_value, new_value));
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
old_value = *ptr;
} while (__sync_bool_compare_and_swap(ptr, old_value, new_value) == false);
return old_value;
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
return Barrier_AtomicIncrement(ptr, increment);
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
for (;;) {
Atomic64 old_value = *ptr;
Atomic64 new_value = old_value + increment;
if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
return new_value;
// The exchange took place as expected.
}
// Otherwise, *ptr changed mid-loop and we need to retry.
}
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value, Atomic64 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { return *ptr; }
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier();
return *ptr;
}
#endif
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_PPC_GCC_H_

View file

@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This module gets enough CPU information to optimize the
// atomicops module on x86.
#include <string.h>
#include "base/atomicops.h"
#include "base/basictypes.h"
// This file only makes sense with atomicops_internals_x86_gcc.h -- it
// depends on structs that are defined in that file. If atomicops.h
// doesn't sub-include that file, then we aren't needed, and shouldn't
// try to do anything.
#ifdef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
// Inline cpuid instruction. In PIC compilations, %ebx contains the address
// of the global offset table. To avoid breaking such executables, this code
// must preserve that register's value across cpuid instructions.
#if defined(__i386__)
#define cpuid(a, b, c, d, inp) \
asm ("mov %%ebx, %%edi\n" \
"cpuid\n" \
"xchg %%edi, %%ebx\n" \
: "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
#elif defined (__x86_64__)
#define cpuid(a, b, c, d, inp) \
asm ("mov %%rbx, %%rdi\n" \
"cpuid\n" \
"xchg %%rdi, %%rbx\n" \
: "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
#endif
#if defined(cpuid) // initialize the struct only on x86
// Set the flags so that code will run correctly and conservatively, so even
// if we haven't been initialized yet, we're probably single threaded, and our
// default values should hopefully be pretty safe.
struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = {
false, // bug can't exist before process spawns multiple threads
false, // no SSE2
};
// Initialize the AtomicOps_Internalx86CPUFeatures struct.
static void AtomicOps_Internalx86CPUFeaturesInit() {
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
// Get vendor string (issue CPUID with eax = 0)
cpuid(eax, ebx, ecx, edx, 0);
char vendor[13];
memcpy(vendor, &ebx, 4);
memcpy(vendor + 4, &edx, 4);
memcpy(vendor + 8, &ecx, 4);
vendor[12] = 0;
// get feature flags in ecx/edx, and family/model in eax
cpuid(eax, ebx, ecx, edx, 1);
int family = (eax >> 8) & 0xf; // family and model fields
int model = (eax >> 4) & 0xf;
if (family == 0xf) { // use extended family and model fields
family += (eax >> 20) & 0xff;
model += ((eax >> 16) & 0xf) << 4;
}
// Opteron Rev E has a bug in which on very rare occasions a locked
// instruction doesn't act as a read-acquire barrier if followed by a
// non-locked read-modify-write instruction. Rev F has this bug in
// pre-release versions, but not in versions released to customers,
// so we test only for Rev E, which is family 15, model 32..63 inclusive.
if (strcmp(vendor, "AuthenticAMD") == 0 && // AMD
family == 15 &&
32 <= model && model <= 63) {
AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = true;
} else {
AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = false;
}
// edx bit 26 is SSE2 which we use to tell use whether we can use mfence
AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1);
}
namespace {
class AtomicOpsx86Initializer {
public:
AtomicOpsx86Initializer() {
AtomicOps_Internalx86CPUFeaturesInit();
}
};
// A global to get use initialized on startup via static initialization :/
AtomicOpsx86Initializer g_initer;
} // namespace
#endif // if x86
#endif // ifdef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_

View file

@ -0,0 +1,261 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is an internal atomic implementation, use base/atomicops.h instead.
#ifndef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
#define BASE_ATOMICOPS_INTERNALS_X86_GCC_H_
// This struct is not part of the public API of this module; clients may not
// use it.
// Features of this x86. Values may not be correct before main() is run,
// but are set conservatively.
struct AtomicOps_x86CPUFeatureStruct {
bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence
// after acquire compare-and-swap.
bool has_sse2; // Processor has SSE2.
};
extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures;
#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
namespace base {
namespace subtle {
// 32-bit low-level operations on any platform.
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev;
__asm__ __volatile__("lock; cmpxchgl %1,%2"
: "=a" (prev)
: "q" (new_value), "m" (*ptr), "0" (old_value)
: "memory");
return prev;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
__asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg.
: "=r" (new_value)
: "m" (*ptr), "0" (new_value)
: "memory");
return new_value; // Now it's the previous value.
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
Atomic32 temp = increment;
__asm__ __volatile__("lock; xaddl %0,%1"
: "+r" (temp), "+m" (*ptr)
: : "memory");
// temp now holds the old value of *ptr
return temp + increment;
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
Atomic32 temp = increment;
__asm__ __volatile__("lock; xaddl %0,%1"
: "+r" (temp), "+m" (*ptr)
: : "memory");
// temp now holds the old value of *ptr
if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
__asm__ __volatile__("lfence" : : : "memory");
}
return temp + increment;
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
__asm__ __volatile__("lfence" : : : "memory");
}
return x;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
#if defined(__x86_64__)
// 64-bit implementations of memory barrier can be simpler, because it
// "mfence" is guaranteed to exist.
inline void MemoryBarrier() {
__asm__ __volatile__("mfence" : : : "memory");
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
#else
inline void MemoryBarrier() {
if (AtomicOps_Internalx86CPUFeatures.has_sse2) {
__asm__ __volatile__("mfence" : : : "memory");
} else { // mfence is faster but not present on PIII
Atomic32 x = 0;
NoBarrier_AtomicExchange(&x, 0); // acts as a barrier on PIII
}
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
if (AtomicOps_Internalx86CPUFeatures.has_sse2) {
*ptr = value;
__asm__ __volatile__("mfence" : : : "memory");
} else {
NoBarrier_AtomicExchange(ptr, value);
// acts as a barrier on PIII
}
}
#endif
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
ATOMICOPS_COMPILER_BARRIER();
*ptr = value; // An x86 store acts as a release barrier.
// See comments in Atomic64 version of Release_Store(), below.
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr; // An x86 load acts as a acquire barrier.
// See comments in Atomic64 version of Release_Store(), below.
ATOMICOPS_COMPILER_BARRIER();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
#if defined(__x86_64__)
// 64-bit low-level operations on 64-bit platform.
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev;
__asm__ __volatile__("lock; cmpxchgq %1,%2"
: "=a" (prev)
: "q" (new_value), "m" (*ptr), "0" (old_value)
: "memory");
return prev;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
__asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg.
: "=r" (new_value)
: "m" (*ptr), "0" (new_value)
: "memory");
return new_value; // Now it's the previous value.
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
Atomic64 temp = increment;
__asm__ __volatile__("lock; xaddq %0,%1"
: "+r" (temp), "+m" (*ptr)
: : "memory");
// temp now contains the previous value of *ptr
return temp + increment;
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
Atomic64 temp = increment;
__asm__ __volatile__("lock; xaddq %0,%1"
: "+r" (temp), "+m" (*ptr)
: : "memory");
// temp now contains the previous value of *ptr
if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
__asm__ __volatile__("lfence" : : : "memory");
}
return temp + increment;
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
/* XXX/cjones: no idea if this is necessary... */
if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) {
__asm__ __volatile__("lfence" : : : "memory");
}
return x;
}
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
ATOMICOPS_COMPILER_BARRIER();
*ptr = value; // An x86 store acts as a release barrier
// for current AMD/Intel chips as of Jan 2008.
// See also Acquire_Load(), below.
// When new chips come out, check:
// IA-32 Intel Architecture Software Developer's Manual, Volume 3:
// System Programming Guide, Chatper 7: Multiple-processor management,
// Section 7.2, Memory Ordering.
// Last seen at:
// http://developer.intel.com/design/pentium4/manuals/index_new.htm
//
// x86 stores/loads fail to act as barriers for a few instructions (clflush
// maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are
// not generated by the compiler, and are rare. Users of these instructions
// need to know about cache behaviour in any case since all of these involve
// either flushing cache lines or non-temporal cache hints.
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr; // An x86 load acts as a acquire barrier,
// for current AMD/Intel chips as of Jan 2008.
// See also Release_Store(), above.
ATOMICOPS_COMPILER_BARRIER();
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier();
return *ptr;
}
#endif // defined(__x86_64__)
} // namespace base::subtle
} // namespace base
#undef ATOMICOPS_COMPILER_BARRIER
#endif // BASE_ATOMICOPS_INTERNALS_X86_GCC_H_

View file

@ -0,0 +1,281 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is an internal atomic implementation, use base/atomicops.h instead.
#ifndef BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_
#define BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_
#include <libkern/OSAtomic.h>
namespace base {
namespace subtle {
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap32(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr,
Atomic32 increment) {
return OSAtomicAdd32(increment, const_cast<Atomic32*>(ptr));
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr,
Atomic32 increment) {
return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr));
}
inline void MemoryBarrier() {
OSMemoryBarrier();
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32Barrier(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
return Acquire_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
MemoryBarrier();
return *ptr;
}
#ifdef __LP64__
// 64-bit implementation on 64-bit platform
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap64(old_value, new_value,
const_cast<Atomic64*>(ptr)));
return old_value;
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr,
Atomic64 increment) {
return OSAtomicAdd64(increment, const_cast<Atomic64*>(ptr));
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr,
Atomic64 increment) {
return OSAtomicAdd64Barrier(increment, const_cast<Atomic64*>(ptr));
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64Barrier(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
// The lib kern interface does not distinguish between
// Acquire and Release memory barriers; they are equivalent.
return Acquire_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) {
*ptr = value;
MemoryBarrier();
}
inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64 *ptr) {
MemoryBarrier();
return *ptr;
}
#endif // defined(__LP64__)
// MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different
// on the Mac, even when they are the same size. We need to explicitly cast
// from AtomicWord to Atomic32/64 to implement the AtomicWord interface.
#ifdef __LP64__
#define AtomicWordCastType Atomic64
#else
#define AtomicWordCastType Atomic32
#endif
inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return NoBarrier_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr,
AtomicWord new_value) {
return NoBarrier_AtomicExchange(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), new_value);
}
inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr,
AtomicWord increment) {
return NoBarrier_AtomicIncrement(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), increment);
}
inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr,
AtomicWord increment) {
return Barrier_AtomicIncrement(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), increment);
}
inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return base::subtle::Acquire_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return base::subtle::Release_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) {
NoBarrier_Store(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
}
inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) {
return base::subtle::Acquire_Store(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
}
inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) {
return base::subtle::Release_Store(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
}
inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) {
return NoBarrier_Load(
reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
}
inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) {
return base::subtle::Acquire_Load(
reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
}
inline AtomicWord Release_Load(volatile const AtomicWord* ptr) {
return base::subtle::Release_Load(
reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
}
#undef AtomicWordCastType
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_

View file

@ -0,0 +1,178 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is an internal atomic implementation, use base/atomicops.h instead.
#ifndef BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_
#define BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_
#include <windows.h>
namespace base {
namespace subtle {
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
LONG result = InterlockedCompareExchange(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(new_value),
static_cast<LONG>(old_value));
return static_cast<Atomic32>(result);
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
LONG result = InterlockedExchange(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(new_value));
return static_cast<Atomic32>(result);
}
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return InterlockedExchangeAdd(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(increment)) + increment;
}
inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
return Barrier_AtomicIncrement(ptr, increment);
}
inline void MemoryBarrier() {
// We use MemoryBarrier from WinNT.h
::MemoryBarrier();
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
NoBarrier_AtomicExchange(ptr, value);
// acts as a barrier in this implementation
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value; // works w/o barrier for current Intel chips as of June 2005
// See comments in Atomic64 version of Release_Store() below.
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
return value;
}
inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
#if defined(_WIN64)
// 64-bit low-level operations on 64-bit platform.
COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
PVOID result = InterlockedCompareExchangePointer(
reinterpret_cast<volatile PVOID*>(ptr),
reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
return reinterpret_cast<Atomic64>(result);
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
PVOID result = InterlockedExchangePointer(
reinterpret_cast<volatile PVOID*>(ptr),
reinterpret_cast<PVOID>(new_value));
return reinterpret_cast<Atomic64>(result);
}
inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
return InterlockedExchangeAdd64(
reinterpret_cast<volatile LONGLONG*>(ptr),
static_cast<LONGLONG>(increment)) + increment;
}
inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
Atomic64 increment) {
return Barrier_AtomicIncrement(ptr, increment);
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
NoBarrier_AtomicExchange(ptr, value);
// acts as a barrier in this implementation
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value; // works w/o barrier for current Intel chips as of June 2005
// When new chips come out, check:
// IA-32 Intel Architecture Software Developer's Manual, Volume 3:
// System Programming Guide, Chatper 7: Multiple-processor management,
// Section 7.2, Memory Ordering.
// Last seen at:
// http://developer.intel.com/design/pentium4/manuals/index_new.htm
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr;
return value;
}
inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier();
return *ptr;
}
#endif // defined(_WIN64)
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_

View file

@ -0,0 +1,272 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_BASICTYPES_H_
#define BASE_BASICTYPES_H_
#include <limits.h> // So we can set the bounds of our types
#include <stddef.h> // For size_t
#include <string.h> // for memcpy
#include "base/port.h" // Types that only need exist on certain systems
#include "mozilla/Assertions.h"
#include "mozilla/IntegerPrintfMacros.h"
// A type to represent a Unicode code-point value. As of Unicode 4.0,
// such values require up to 21 bits.
// (For type-checking on pointers, make this explicitly signed,
// and it should always be the signed version of whatever int32_t is.)
typedef signed int char32;
const uint8_t kuint8max = (( uint8_t) 0xFF);
const uint16_t kuint16max = ((uint16_t) 0xFFFF);
const uint32_t kuint32max = ((uint32_t) 0xFFFFFFFF);
const uint64_t kuint64max = ((uint64_t) GG_LONGLONG(0xFFFFFFFFFFFFFFFF));
const int8_t kint8min = (( int8_t) 0x80);
const int8_t kint8max = (( int8_t) 0x7F);
const int16_t kint16min = (( int16_t) 0x8000);
const int16_t kint16max = (( int16_t) 0x7FFF);
const int32_t kint32min = (( int32_t) 0x80000000);
const int32_t kint32max = (( int32_t) 0x7FFFFFFF);
const int64_t kint64min = (( int64_t) GG_LONGLONG(0x8000000000000000));
const int64_t kint64max = (( int64_t) GG_LONGLONG(0x7FFFFFFFFFFFFFFF));
// Platform- and hardware-dependent printf specifiers
# if defined(OS_POSIX)
# define PRId64L "I64d"
# define PRIu64L "I64u"
# define PRIx64L "I64x"
# elif defined(OS_WIN)
# define PRId64L L"I64d"
# define PRIu64L L"I64u"
# define PRIx64L L"I64x"
# endif
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#undef DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
// An older, deprecated, politically incorrect name for the above.
#undef DISALLOW_EVIL_CONSTRUCTORS
#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName)
// A macro to disallow all the implicit constructors, namely the
// default constructor, copy constructor and operator= functions.
//
// This should be used in the private: declarations for a class
// that wants to prevent anyone from instantiating it. This is
// especially useful for classes containing only static methods.
#undef DISALLOW_IMPLICIT_CONSTRUCTORS
#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
TypeName(); \
DISALLOW_COPY_AND_ASSIGN(TypeName)
// The arraysize(arr) macro returns the # of elements in an array arr.
// The expression is a compile-time constant, and therefore can be
// used in defining new arrays, for example. If you use arraysize on
// a pointer by mistake, you will get a compile-time error.
//
// One caveat is that arraysize() doesn't accept any array of an
// anonymous type or a type defined inside a function. In these rare
// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
// due to a limitation in C++'s template system. The limitation might
// eventually be removed, but it hasn't happened yet.
// This template function declaration is used in defining arraysize.
// Note that the function doesn't need an implementation, as we only
// use its type.
template <typename T, size_t N>
char (&ArraySizeHelper(T (&array)[N]))[N];
// That gcc wants both of these prototypes seems mysterious. VC, for
// its part, can't decide which to use (another mystery). Matching of
// template overloads: the final frontier.
#ifndef _MSC_VER
template <typename T, size_t N>
char (&ArraySizeHelper(const T (&array)[N]))[N];
#endif
#define arraysize(array) (sizeof(ArraySizeHelper(array)))
// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
// but can be used on anonymous types or types defined inside
// functions. It's less safe than arraysize as it accepts some
// (although not all) pointers. Therefore, you should use arraysize
// whenever possible.
//
// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
// size_t.
//
// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
//
// "warning: division by zero in ..."
//
// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
//
// The following comments are on the implementation details, and can
// be ignored by the users.
//
// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
// the array) and sizeof(*(arr)) (the # of bytes in one array
// element). If the former is divisible by the latter, perhaps arr is
// indeed an array, in which case the division result is the # of
// elements in the array. Otherwise, arr cannot possibly be an array,
// and we generate a compiler error to prevent the code from
// compiling.
//
// Since the size of bool is implementation-defined, we need to cast
// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
// result has type size_t.
//
// This macro is not perfect as it wrongfully accepts certain
// pointers, namely where the pointer size is divisible by the pointee
// size. Since all our code has to go through a 32-bit compiler,
// where a pointer is 4 bytes, this means all pointers to a type whose
// size is 3 or greater than 4 will be (righteously) rejected.
#define ARRAYSIZE_UNSAFE(a) \
((sizeof(a) / sizeof(*(a))) / \
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
// Use implicit_cast as a safe version of static_cast or const_cast
// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
// a const pointer to Foo).
// When you use implicit_cast, the compiler checks that the cast is safe.
// Such explicit implicit_casts are necessary in surprisingly many
// situations where C++ demands an exact type match instead of an
// argument type convertable to a target type.
//
// The From type can be inferred, so the preferred syntax for using
// implicit_cast is the same as for static_cast etc.:
//
// implicit_cast<ToType>(expr)
//
// implicit_cast would have been part of the C++ standard library,
// but the proposal was submitted too late. It will probably make
// its way into the language in the future.
template<typename To, typename From>
inline To implicit_cast(From const &f) {
return f;
}
// The COMPILE_ASSERT macro (below) creates an otherwise-unused typedef. This
// triggers compiler warnings with gcc 4.8 and higher, so mark the typedef
// as permissibly-unused to disable the warnings.
# if defined(__GNUC__)
# define COMPILE_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused))
# else
# define COMPILE_ASSERT_UNUSED_ATTRIBUTE /* nothing */
# endif
// The COMPILE_ASSERT macro can be used to verify that a compile time
// expression is true. For example, you could use it to verify the
// size of a static array:
//
// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES,
// content_type_names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
//
// The second argument to the macro is the name of the variable. If
// the expression is false, most compilers will issue a warning/error
// containing the name of the variable.
// Avoid multiple definitions for webrtc
#if !defined(COMPILE_ASSERT)
template <bool>
struct CompileAssert {
};
#define COMPILE_ASSERT(expr, msg) \
typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] \
COMPILE_ASSERT_UNUSED_ATTRIBUTE
#endif
// Implementation details of COMPILE_ASSERT:
//
// - COMPILE_ASSERT works by defining an array type that has -1
// elements (and thus is invalid) when the expression is false.
//
// - The simpler definition
//
// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
//
// does not work, as gcc supports variable-length arrays whose sizes
// are determined at run-time (this is gcc's extension and not part
// of the C++ standard). As a result, gcc fails to reject the
// following code with the simple definition:
//
// int foo;
// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
// // not a compile-time constant.
//
// - By using the type CompileAssert<(bool(expr))>, we ensures that
// expr is a compile-time constant. (Template arguments must be
// determined at compile-time.)
//
// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
//
// CompileAssert<bool(expr)>
//
// instead, these compilers will refuse to compile
//
// COMPILE_ASSERT(5 > 0, some_message);
//
// (They seem to think the ">" in "5 > 0" marks the end of the
// template argument list.)
//
// - The array size is (bool(expr) ? 1 : -1), instead of simply
//
// ((expr) ? 1 : -1).
//
// This is to avoid running into a bug in MS VC 7.1, which
// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
// MetatagId refers to metatag-id that we assign to
// each metatag <name, value> pair..
typedef uint32_t MetatagId;
// Argument type used in interfaces that can optionally take ownership
// of a passed in argument. If TAKE_OWNERSHIP is passed, the called
// object takes ownership of the argument. Otherwise it does not.
enum Ownership {
DO_NOT_TAKE_OWNERSHIP,
TAKE_OWNERSHIP
};
// The following enum should be used only as a constructor argument to indicate
// that the variable has static storage class, and that the constructor should
// do nothing to its state. It indicates to the reader that it is legal to
// declare a static instance of the class, provided the constructor is given
// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
// static variable that has a constructor or a destructor because invocation
// order is undefined. However, IF the type can be initialized by filling with
// zeroes (which the loader does for static variables), AND the destructor also
// does nothing to the storage, AND there are no virtual methods, then a
// constructor declared as
// explicit MyClass(base::LinkerInitialized x) {}
// and invoked as
// static MyClass my_variable_name(base::LINKER_INITIALIZED);
namespace base {
enum LinkerInitialized { LINKER_INITIALIZED };
} // base
#include "nscore.h" // pick up mozalloc operator new() etc.
#endif // BASE_BASICTYPES_H_

View file

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CHROME_APPLICATION_MAC_H_
#define BASE_CHROME_APPLICATION_MAC_H_
#import <AppKit/AppKit.h>
#include "base/basictypes.h"
#include "base/scoped_nsobject.h"
// Event hooks must implement this protocol.
@protocol CrApplicationEventHookProtocol
- (void)hookForEvent:(NSEvent*)theEvent;
@end
@interface CrApplication : NSApplication {
@private
BOOL handlingSendEvent_;
// Array of objects implementing the CrApplicationEventHookProtocol
scoped_nsobject<NSMutableArray> eventHooks_;
}
@property(readonly,
getter=isHandlingSendEvent,
nonatomic) BOOL handlingSendEvent;
// Add or remove an event hook to be called for every sendEvent:
// that the application receives. These handlers are called before
// the normal [NSApplication sendEvent:] call is made.
// This is not a good alternative to a nested event loop. It should
// be used only when normal event logic and notification breaks down
// (e.g. when clicking outside a canBecomeKey:NO window to "switch
// context" out of it).
- (void)addEventHook:(id<CrApplicationEventHookProtocol>)hook;
- (void)removeEventHook:(id<CrApplicationEventHookProtocol>)hook;
+ (NSApplication*)sharedApplication;
@end
namespace chrome_application_mac {
// Controls the state of |handlingSendEvent_| in the event loop so that it is
// reset properly.
class ScopedSendingEvent {
public:
ScopedSendingEvent();
~ScopedSendingEvent();
private:
CrApplication* app_;
BOOL handling_;
DISALLOW_COPY_AND_ASSIGN(ScopedSendingEvent);
};
} // chrome_application_mac
#endif // BASE_CHROME_APPLICATION_MAC_H_

View file

@ -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

View file

@ -0,0 +1,394 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#if defined(OS_WIN)
#include <windows.h>
#include <shellapi.h>
#endif
#include <algorithm>
#include "base/logging.h"
#include "base/singleton.h"
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
CommandLine* CommandLine::current_process_commandline_ = NULL;
// Since we use a lazy match, make sure that longer versions (like L"--")
// are listed before shorter versions (like L"-") of similar prefixes.
#if defined(OS_WIN)
const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
const wchar_t kSwitchTerminator[] = L"--";
const wchar_t kSwitchValueSeparator[] = L"=";
#elif defined(OS_POSIX)
// Unixes don't use slash as a switch.
const char* const kSwitchPrefixes[] = {"--", "-"};
const char kSwitchTerminator[] = "--";
const char kSwitchValueSeparator[] = "=";
#endif
#if defined(OS_WIN)
// Lowercase a string. This is used to lowercase switch names.
// Is this what we really want? It seems crazy to me. I've left it in
// for backwards compatibility on Windows.
static void Lowercase(std::wstring* parameter) {
transform(parameter->begin(), parameter->end(), parameter->begin(),
tolower);
}
#endif
#if defined(OS_WIN)
void CommandLine::ParseFromString(const std::wstring& command_line) {
TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
if (command_line_string_.empty())
return;
int num_args = 0;
wchar_t** args = NULL;
args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
// Populate program_ with the trimmed version of the first arg.
TrimWhitespace(args[0], TRIM_ALL, &program_);
bool parse_switches = true;
for (int i = 1; i < num_args; ++i) {
std::wstring arg;
TrimWhitespace(args[i], TRIM_ALL, &arg);
if (!parse_switches) {
loose_values_.push_back(arg);
continue;
}
if (arg == kSwitchTerminator) {
parse_switches = false;
continue;
}
std::string switch_string;
std::wstring switch_value;
if (IsSwitch(arg, &switch_string, &switch_value)) {
switches_[switch_string] = switch_value;
} else {
loose_values_.push_back(arg);
}
}
if (args)
LocalFree(args);
}
CommandLine::CommandLine(const std::wstring& program) {
if (!program.empty()) {
program_ = program;
command_line_string_ = L'"' + program + L'"';
}
}
#elif defined(OS_POSIX)
CommandLine::CommandLine(int argc, const char* const* argv) {
for (int i = 0; i < argc; ++i)
argv_.push_back(argv[i]);
InitFromArgv();
}
CommandLine::CommandLine(const std::vector<std::string>& argv) {
argv_ = argv;
InitFromArgv();
}
void CommandLine::InitFromArgv() {
bool parse_switches = true;
for (size_t i = 1; i < argv_.size(); ++i) {
const std::string& arg = argv_[i];
if (!parse_switches) {
loose_values_.push_back(arg);
continue;
}
if (arg == kSwitchTerminator) {
parse_switches = false;
continue;
}
std::string switch_string;
std::string switch_value;
if (IsSwitch(arg, &switch_string, &switch_value)) {
switches_[switch_string] = switch_value;
} else {
loose_values_.push_back(arg);
}
}
}
CommandLine::CommandLine(const std::wstring& program) {
argv_.push_back(WideToASCII(program));
}
#endif
// static
bool CommandLine::IsSwitch(const StringType& parameter_string,
std::string* switch_string,
StringType* switch_value) {
switch_string->clear();
switch_value->clear();
for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
StringType prefix(kSwitchPrefixes[i]);
if (parameter_string.find(prefix) != 0)
continue;
const size_t switch_start = prefix.length();
const size_t equals_position = parameter_string.find(
kSwitchValueSeparator, switch_start);
StringType switch_native;
if (equals_position == StringType::npos) {
switch_native = parameter_string.substr(switch_start);
} else {
switch_native = parameter_string.substr(
switch_start, equals_position - switch_start);
*switch_value = parameter_string.substr(equals_position + 1);
}
#if defined(OS_WIN)
Lowercase(&switch_native);
*switch_string = WideToASCII(switch_native);
#else
*switch_string = switch_native;
#endif
return true;
}
return false;
}
// static
void CommandLine::Init(int argc, const char* const* argv) {
DCHECK(current_process_commandline_ == NULL);
#if defined(OS_WIN)
current_process_commandline_ = new CommandLine;
current_process_commandline_->ParseFromString(::GetCommandLineW());
#elif defined(OS_POSIX)
current_process_commandline_ = new CommandLine(argc, argv);
#endif
}
void CommandLine::Terminate() {
DCHECK(current_process_commandline_ != NULL);
delete current_process_commandline_;
current_process_commandline_ = NULL;
}
bool CommandLine::HasSwitch(const std::wstring& switch_string) const {
std::wstring lowercased_switch(switch_string);
#if defined(OS_WIN)
Lowercase(&lowercased_switch);
#endif
return switches_.find(WideToASCII(lowercased_switch)) != switches_.end();
}
std::wstring CommandLine::GetSwitchValue(
const std::wstring& switch_string) const {
std::wstring lowercased_switch(switch_string);
#if defined(OS_WIN)
Lowercase(&lowercased_switch);
#endif
std::map<std::string, StringType>::const_iterator result =
switches_.find(WideToASCII(lowercased_switch));
if (result == switches_.end()) {
return L"";
} else {
#if defined(OS_WIN)
return result->second;
#else
return ASCIIToWide(result->second);
#endif
}
}
#if defined(OS_WIN)
std::vector<std::wstring> CommandLine::GetLooseValues() const {
return loose_values_;
}
std::wstring CommandLine::program() const {
return program_;
}
#else
std::vector<std::wstring> CommandLine::GetLooseValues() const {
std::vector<std::wstring> values;
for (size_t i = 0; i < loose_values_.size(); ++i)
values.push_back(ASCIIToWide(loose_values_[i]));
return values;
}
std::wstring CommandLine::program() const {
DCHECK(argv_.size() > 0);
return ASCIIToWide(argv_[0]);
}
#endif
// static
std::wstring CommandLine::PrefixedSwitchString(
const std::wstring& switch_string) {
return StringPrintf(L"%ls%ls",
kSwitchPrefixes[0],
switch_string.c_str());
}
// static
std::wstring CommandLine::PrefixedSwitchStringWithValue(
const std::wstring& switch_string, const std::wstring& value_string) {
if (value_string.empty()) {
return PrefixedSwitchString(switch_string);
}
return StringPrintf(L"%ls%ls%ls%ls",
kSwitchPrefixes[0],
switch_string.c_str(),
kSwitchValueSeparator,
value_string.c_str());
}
#if defined(OS_WIN)
void CommandLine::AppendSwitch(const std::wstring& switch_string) {
std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string);
command_line_string_.append(L" ");
command_line_string_.append(prefixed_switch_string);
switches_[WideToASCII(switch_string)] = L"";
}
// Quote a string if necessary, such that CommandLineToArgvW() will
// always process it as a single argument.
static std::wstring WindowsStyleQuote(const std::wstring& arg) {
// We follow the quoting rules of CommandLineToArgvW.
// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
if (arg.find_first_of(L" \\\"\t") == std::wstring::npos) {
// No quoting necessary.
return arg;
}
std::wstring out;
out.push_back(L'"');
for (size_t i = 0; i < arg.size(); ++i) {
if (arg[i] == '\\') {
// Find the extent of this run of backslashes.
size_t start = i, end = start + 1;
for (; end < arg.size() && arg[end] == '\\'; ++end)
/* empty */;
size_t backslash_count = end - start;
// Backslashes are escapes only if the run is followed by a double quote.
// Since we also will end the string with a double quote, we escape for
// either a double quote or the end of the string.
if (end == arg.size() || arg[end] == '"') {
// To quote, we need to output 2x as many backslashes.
backslash_count *= 2;
}
for (size_t j = 0; j < backslash_count; ++j)
out.push_back('\\');
// Advance i to one before the end to balance i++ in loop.
i = end - 1;
} else if (arg[i] == '"') {
out.push_back('\\');
out.push_back('"');
} else {
out.push_back(arg[i]);
}
}
out.push_back('"');
return out;
}
void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string,
const std::wstring& value_string) {
std::wstring quoted_value_string = WindowsStyleQuote(value_string);
std::wstring combined_switch_string =
PrefixedSwitchStringWithValue(switch_string, quoted_value_string);
command_line_string_.append(L" ");
command_line_string_.append(combined_switch_string);
switches_[WideToASCII(switch_string)] = value_string;
}
void CommandLine::AppendLooseValue(const std::wstring& value) {
command_line_string_.append(L" ");
command_line_string_.append(WindowsStyleQuote(value));
}
void CommandLine::AppendArguments(const CommandLine& other,
bool include_program) {
// Verify include_program is used correctly.
// Logic could be shorter but this is clearer.
DCHECK(include_program ? !other.program().empty() : other.program().empty());
command_line_string_ += L" " + other.command_line_string_;
std::map<std::string, StringType>::const_iterator i;
for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
switches_[i->first] = i->second;
}
void CommandLine::PrependWrapper(const std::wstring& wrapper) {
// The wrapper may have embedded arguments (like "gdb --args"). In this case,
// we don't pretend to do anything fancy, we just split on spaces.
std::vector<std::wstring> wrapper_and_args;
SplitString(wrapper, ' ', &wrapper_and_args);
program_ = wrapper_and_args[0];
command_line_string_ = wrapper + L" " + command_line_string_;
}
#elif defined(OS_POSIX)
void CommandLine::AppendSwitch(const std::wstring& switch_string) {
std::string ascii_switch = WideToASCII(switch_string);
argv_.push_back(kSwitchPrefixes[0] + ascii_switch);
switches_[ascii_switch] = "";
}
void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string,
const std::wstring& value_string) {
std::string ascii_switch = WideToASCII(switch_string);
std::string ascii_value = WideToASCII(value_string);
argv_.push_back(kSwitchPrefixes[0] + ascii_switch +
kSwitchValueSeparator + ascii_value);
switches_[ascii_switch] = ascii_value;
}
void CommandLine::AppendLooseValue(const std::wstring& value) {
argv_.push_back(WideToASCII(value));
}
void CommandLine::AppendArguments(const CommandLine& other,
bool include_program) {
// Verify include_program is used correctly.
// Logic could be shorter but this is clearer.
DCHECK(include_program ? !other.program().empty() : other.program().empty());
size_t first_arg = include_program ? 0 : 1;
for (size_t i = first_arg; i < other.argv_.size(); ++i)
argv_.push_back(other.argv_[i]);
std::map<std::string, StringType>::const_iterator i;
for (i = other.switches_.begin(); i != other.switches_.end(); ++i)
switches_[i->first] = i->second;
}
void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) {
// The wrapper may have embedded arguments (like "gdb --args"). In this case,
// we don't pretend to do anything fancy, we just split on spaces.
const std::string wrapper = WideToASCII(wrapper_wide);
std::vector<std::string> wrapper_and_args;
SplitString(wrapper, ' ', &wrapper_and_args);
argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end());
}
#endif

View file

@ -0,0 +1,197 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This class works with command lines: building and parsing.
// Switches can optionally have a value attached using an equals sign,
// as in "-switch=value". Arguments that aren't prefixed with a
// switch prefix are considered "loose parameters". Switch names are
// case-insensitive. An argument of "--" will terminate switch
// parsing, causing everything after to be considered as loose
// parameters.
// There is a singleton read-only CommandLine that represents the command
// line that the current process was started with. It must be initialized
// in main() (or whatever the platform's equivalent function is).
#ifndef BASE_COMMAND_LINE_H_
#define BASE_COMMAND_LINE_H_
#include "build/build_config.h"
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
class InProcessBrowserTest;
class CommandLine {
public:
#if defined(OS_WIN)
// Creates a parsed version of the given command-line string.
// The program name is assumed to be the first item in the string.
void ParseFromString(const std::wstring& command_line);
#elif defined(OS_POSIX)
// Initialize from an argv vector (or directly from main()'s argv).
CommandLine(int argc, const char* const* argv);
explicit CommandLine(const std::vector<std::string>& argv);
#endif
// Construct a new, empty command line.
// |program| is the name of the program to run (aka argv[0]).
// TODO(port): should be a FilePath.
explicit CommandLine(const std::wstring& program);
// Initialize the current process CommandLine singleton. On Windows,
// ignores its arguments (we instead parse GetCommandLineW()
// directly) because we don't trust the CRT's parsing of the command
// line, but it still must be called to set up the command line.
static void Init(int argc, const char* const* argv);
// Destroys the current process CommandLine singleton. This is necessary if
// you want to reset the base library to its initial state (for example in an
// outer library that needs to be able to terminate, and be re-initialized).
// If Init is called only once, e.g. in main(), calling Terminate() is not
// necessary.
static void Terminate();
// Get the singleton CommandLine representing the current process's
// command line.
static const CommandLine* ForCurrentProcess() {
DCHECK(current_process_commandline_);
return current_process_commandline_;
}
static bool IsInitialized() {
return !!current_process_commandline_;
}
// Returns true if this command line contains the given switch.
// (Switch names are case-insensitive.)
bool HasSwitch(const std::wstring& switch_string) const;
// Returns the value associated with the given switch. If the
// switch has no value or isn't present, this method returns
// the empty string.
std::wstring GetSwitchValue(const std::wstring& switch_string) const;
// Get the remaining arguments to the command.
// WARNING: this is incorrect on POSIX; we must do string conversions.
std::vector<std::wstring> GetLooseValues() const;
#if defined(OS_WIN)
// Returns the original command line string.
const std::wstring& command_line_string() const {
return command_line_string_;
}
#elif defined(OS_POSIX)
// Returns the original command line string as a vector of strings.
const std::vector<std::string>& argv() const {
return argv_;
}
#endif
// Returns the program part of the command line string (the first item).
std::wstring program() const;
// Return a copy of the string prefixed with a switch prefix.
// Used internally.
static std::wstring PrefixedSwitchString(const std::wstring& switch_string);
// Return a copy of the string prefixed with a switch prefix,
// and appended with the given value. Used internally.
static std::wstring PrefixedSwitchStringWithValue(
const std::wstring& switch_string,
const std::wstring& value_string);
// Appends the given switch string (preceded by a space and a switch
// prefix) to the given string.
void AppendSwitch(const std::wstring& switch_string);
// Appends the given switch string (preceded by a space and a switch
// prefix) to the given string, with the given value attached.
void AppendSwitchWithValue(const std::wstring& switch_string,
const std::wstring& value_string);
// Append a loose value to the command line.
void AppendLooseValue(const std::wstring& value);
#if defined(OS_WIN)
void AppendLooseValue(const wchar_t* value) {
AppendLooseValue(std::wstring(value));
}
#endif
// Append the arguments from another command line to this one.
// If |include_program| is true, include |other|'s program as well.
void AppendArguments(const CommandLine& other,
bool include_program);
// On POSIX systems it's common to run processes via a wrapper (like
// "valgrind" or "gdb --args").
void PrependWrapper(const std::wstring& wrapper);
private:
friend class InProcessBrowserTest;
CommandLine() {}
// Used by InProcessBrowserTest.
static CommandLine* ForCurrentProcessMutable() {
DCHECK(current_process_commandline_);
return current_process_commandline_;
}
// The singleton CommandLine instance representing the current process's
// command line.
static CommandLine* current_process_commandline_;
// We store a platform-native version of the command line, used when building
// up a new command line to be executed. This ifdef delimits that code.
#if defined(OS_WIN)
// The quoted, space-separated command-line string.
std::wstring command_line_string_;
// The name of the program.
std::wstring program_;
// The type of native command line arguments.
typedef std::wstring StringType;
#elif defined(OS_POSIX)
// The argv array, with the program name in argv_[0].
std::vector<std::string> argv_;
// The type of native command line arguments.
typedef std::string StringType;
// Shared by the two POSIX constructor forms. Initalize from argv_.
void InitFromArgv();
#endif
// Returns true and fills in |switch_string| and |switch_value|
// if |parameter_string| represents a switch.
static bool IsSwitch(const StringType& parameter_string,
std::string* switch_string,
StringType* switch_value);
// Parsed-out values.
std::map<std::string, StringType> switches_;
// Non-switch command-line arguments.
std::vector<StringType> loose_values_;
// We allow copy constructors, because a common pattern is to grab a
// copy of the current process's command line and then add some
// flags to it. E.g.:
// CommandLine cl(*CommandLine::ForCurrentProcess());
// cl.AppendSwitch(...);
};
#endif // BASE_COMMAND_LINE_H_

View file

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_COMPILER_SPECIFIC_H_
#define BASE_COMPILER_SPECIFIC_H_
#include "build/build_config.h"
#if defined(COMPILER_MSVC)
// Macros for suppressing and disabling warnings on MSVC.
//
// Warning numbers are enumerated at:
// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
//
// The warning pragma:
// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
//
// Using __pragma instead of #pragma inside macros:
// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and
// for the next line of the source file.
#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress:n))
// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
// The warning remains disabled until popped by MSVC_POP_WARNING.
#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
__pragma(warning(disable:n))
// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level
// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all
// warnings.
#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n))
// Pop effects of innermost MSVC_PUSH_* macro.
#define MSVC_POP_WARNING() __pragma(warning(pop))
#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off))
#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on))
// Allows |this| to be passed as an argument in constructor initializer lists.
// This uses push/pop instead of the seemingly simpler suppress feature to avoid
// having the warning be disabled for more than just |code|.
//
// Example usage:
// Foo::Foo() : x(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(y(this)), z(3) {}
//
// Compiler warning C4355: 'this': used in base member initializer list:
// http://msdn.microsoft.com/en-us/library/3c594ae3(VS.80).aspx
#define ALLOW_THIS_IN_INITIALIZER_LIST(code) MSVC_PUSH_DISABLE_WARNING(4355) \
code \
MSVC_POP_WARNING()
#else // Not MSVC
#define MSVC_SUPPRESS_WARNING(n)
#define MSVC_PUSH_DISABLE_WARNING(n)
#define MSVC_PUSH_WARNING_LEVEL(n)
#define MSVC_POP_WARNING()
#define MSVC_DISABLE_OPTIMIZE()
#define MSVC_ENABLE_OPTIMIZE()
#define ALLOW_THIS_IN_INITIALIZER_LIST(code) code
#endif // COMPILER_MSVC
#if defined(COMPILER_GCC)
#define ALLOW_UNUSED __attribute__((unused))
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else // Not GCC
#define ALLOW_UNUSED
#define WARN_UNUSED_RESULT
#endif
#endif // BASE_COMPILER_SPECIFIC_H_

View file

@ -0,0 +1,176 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ConditionVariable wraps pthreads condition variable synchronization or, on
// Windows, simulates it. This functionality is very helpful for having
// several threads wait for an event, as is common with a thread pool managed
// by a master. The meaning of such an event in the (worker) thread pool
// scenario is that additional tasks are now available for processing. It is
// used in Chrome in the DNS prefetching system to notify worker threads that
// a queue now has items (tasks) which need to be tended to. A related use
// would have a pool manager waiting on a ConditionVariable, waiting for a
// thread in the pool to announce (signal) that there is now more room in a
// (bounded size) communications queue for the manager to deposit tasks, or,
// as a second example, that the queue of tasks is completely empty and all
// workers are waiting.
//
// USAGE NOTE 1: spurious signal events are possible with this and
// most implementations of condition variables. As a result, be
// *sure* to retest your condition before proceeding. The following
// is a good example of doing this correctly:
//
// while (!work_to_be_done()) Wait(...);
//
// In contrast do NOT do the following:
//
// if (!work_to_be_done()) Wait(...); // Don't do this.
//
// Especially avoid the above if you are relying on some other thread only
// issuing a signal up *if* there is work-to-do. There can/will
// be spurious signals. Recheck state on waiting thread before
// assuming the signal was intentional. Caveat caller ;-).
//
// USAGE NOTE 2: Broadcast() frees up all waiting threads at once,
// which leads to contention for the locks they all held when they
// called Wait(). This results in POOR performance. A much better
// approach to getting a lot of threads out of Wait() is to have each
// thread (upon exiting Wait()) call Signal() to free up another
// Wait'ing thread. Look at condition_variable_unittest.cc for
// both examples.
//
// Broadcast() can be used nicely during teardown, as it gets the job
// done, and leaves no sleeping threads... and performance is less
// critical at that point.
//
// The semantics of Broadcast() are carefully crafted so that *all*
// threads that were waiting when the request was made will indeed
// get signaled. Some implementations mess up, and don't signal them
// all, while others allow the wait to be effectively turned off (for
// a while while waiting threads come around). This implementation
// appears correct, as it will not "lose" any signals, and will guarantee
// that all threads get signaled by Broadcast().
//
// This implementation offers support for "performance" in its selection of
// which thread to revive. Performance, in direct contrast with "fairness,"
// assures that the thread that most recently began to Wait() is selected by
// Signal to revive. Fairness would (if publicly supported) assure that the
// thread that has Wait()ed the longest is selected. The default policy
// may improve performance, as the selected thread may have a greater chance of
// having some of its stack data in various CPU caches.
//
// For a discussion of the many very subtle implementation details, see the FAQ
// at the end of condition_variable_win.cc.
#ifndef BASE_CONDITION_VARIABLE_H_
#define BASE_CONDITION_VARIABLE_H_
#include "base/lock.h"
namespace base {
class TimeDelta;
}
class ConditionVariable {
public:
// Construct a cv for use with ONLY one user lock.
explicit ConditionVariable(Lock* user_lock);
~ConditionVariable();
// Wait() releases the caller's critical section atomically as it starts to
// sleep, and the reacquires it when it is signaled.
void Wait();
void TimedWait(const base::TimeDelta& max_time);
// Broadcast() revives all waiting threads.
void Broadcast();
// Signal() revives one waiting thread.
void Signal();
private:
#if defined(OS_WIN)
// Define Event class that is used to form circularly linked lists.
// The list container is an element with NULL as its handle_ value.
// The actual list elements have a non-zero handle_ value.
// All calls to methods MUST be done under protection of a lock so that links
// can be validated. Without the lock, some links might asynchronously
// change, and the assertions would fail (as would list change operations).
class Event {
public:
// Default constructor with no arguments creates a list container.
Event();
~Event();
// InitListElement transitions an instance from a container, to an element.
void InitListElement();
// Methods for use on lists.
bool IsEmpty() const;
void PushBack(Event* other);
Event* PopFront();
Event* PopBack();
// Methods for use on list elements.
// Accessor method.
HANDLE handle() const;
// Pull an element from a list (if it's in one).
Event* Extract();
// Method for use on a list element or on a list.
bool IsSingleton() const;
private:
// Provide pre/post conditions to validate correct manipulations.
bool ValidateAsDistinct(Event* other) const;
bool ValidateAsItem() const;
bool ValidateAsList() const;
bool ValidateLinks() const;
HANDLE handle_;
Event* next_;
Event* prev_;
DISALLOW_COPY_AND_ASSIGN(Event);
};
// Note that RUNNING is an unlikely number to have in RAM by accident.
// This helps with defensive destructor coding in the face of user error.
enum RunState { SHUTDOWN = 0, RUNNING = 64213 };
// Internal implementation methods supporting Wait().
Event* GetEventForWaiting();
void RecycleEvent(Event* used_event);
RunState run_state_;
// Private critical section for access to member data.
Lock internal_lock_;
// Lock that is acquired before calling Wait().
Lock& user_lock_;
// Events that threads are blocked on.
Event waiting_list_;
// Free list for old events.
Event recycling_list_;
int recycling_list_size_;
// The number of allocated, but not yet deleted events.
int allocation_counter_;
#elif defined(OS_POSIX)
pthread_cond_t condition_;
pthread_mutex_t* user_mutex_;
#endif
DISALLOW_COPY_AND_ASSIGN(ConditionVariable);
};
#endif // BASE_CONDITION_VARIABLE_H_

View file

@ -0,0 +1,91 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/condition_variable.h"
#include <errno.h>
#include <sys/time.h>
#include "base/lock.h"
#include "base/lock_impl.h"
#include "base/logging.h"
#include "base/time.h"
using base::Time;
using base::TimeDelta;
ConditionVariable::ConditionVariable(Lock* user_lock)
: user_mutex_(user_lock->lock_impl()->os_lock()) {
int rv = 0;
#if !defined(OS_MACOSX) && \
!(defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
pthread_condattr_t attrs;
rv = pthread_condattr_init(&attrs);
DCHECK_EQ(0, rv);
pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
rv = pthread_cond_init(&condition_, &attrs);
pthread_condattr_destroy(&attrs);
#else
rv = pthread_cond_init(&condition_, NULL);
#endif
DCHECK_EQ(0, rv);
}
ConditionVariable::~ConditionVariable() {
int rv = pthread_cond_destroy(&condition_);
DCHECK(rv == 0);
}
void ConditionVariable::Wait() {
int rv = pthread_cond_wait(&condition_, user_mutex_);
DCHECK(rv == 0);
}
void ConditionVariable::TimedWait(const TimeDelta& max_time) {
int64_t usecs = max_time.InMicroseconds();
struct timespec relative_time;
relative_time.tv_sec = usecs / Time::kMicrosecondsPerSecond;
relative_time.tv_nsec =
(usecs % Time::kMicrosecondsPerSecond) * Time::kNanosecondsPerMicrosecond;
#if defined(OS_MACOSX)
int rv = pthread_cond_timedwait_relative_np(
&condition_, user_mutex_, &relative_time);
#else
// The timeout argument to pthread_cond_timedwait is in absolute time.
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec absolute_time;
absolute_time.tv_sec = now.tv_sec;
absolute_time.tv_nsec = now.tv_nsec;
absolute_time.tv_sec += relative_time.tv_sec;
absolute_time.tv_nsec += relative_time.tv_nsec;
absolute_time.tv_sec += absolute_time.tv_nsec / Time::kNanosecondsPerSecond;
absolute_time.tv_nsec %= Time::kNanosecondsPerSecond;
DCHECK_GE(absolute_time.tv_sec, now.tv_sec); // Overflow paranoia
#if defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
int rv = pthread_cond_timedwait_monotonic_np(
&condition_, user_mutex_, &absolute_time);
#else
int rv = pthread_cond_timedwait(&condition_, user_mutex_, &absolute_time);
#endif // OS_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
#endif // OS_MACOSX
DCHECK(rv == 0 || rv == ETIMEDOUT);
}
void ConditionVariable::Broadcast() {
int rv = pthread_cond_broadcast(&condition_);
DCHECK(rv == 0);
}
void ConditionVariable::Signal() {
int rv = pthread_cond_signal(&condition_);
DCHECK(rv == 0);
}

View file

@ -0,0 +1,448 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/condition_variable.h"
#include <stack>
#include "base/lock.h"
#include "base/logging.h"
#include "base/time.h"
using base::TimeDelta;
ConditionVariable::ConditionVariable(Lock* user_lock)
: user_lock_(*user_lock),
run_state_(RUNNING),
allocation_counter_(0),
recycling_list_size_(0) {
DCHECK(user_lock);
}
ConditionVariable::~ConditionVariable() {
AutoLock auto_lock(internal_lock_);
run_state_ = SHUTDOWN; // Prevent any more waiting.
DCHECK_EQ(recycling_list_size_, allocation_counter_);
if (recycling_list_size_ != allocation_counter_) { // Rare shutdown problem.
// There are threads of execution still in this->TimedWait() and yet the
// caller has instigated the destruction of this instance :-/.
// A common reason for such "overly hasty" destruction is that the caller
// was not willing to wait for all the threads to terminate. Such hasty
// actions are a violation of our usage contract, but we'll give the
// waiting thread(s) one last chance to exit gracefully (prior to our
// destruction).
// Note: waiting_list_ *might* be empty, but recycling is still pending.
AutoUnlock auto_unlock(internal_lock_);
Broadcast(); // Make sure all waiting threads have been signaled.
Sleep(10); // Give threads a chance to grab internal_lock_.
// All contained threads should be blocked on user_lock_ by now :-).
} // Reacquire internal_lock_.
DCHECK_EQ(recycling_list_size_, allocation_counter_);
}
void ConditionVariable::Wait() {
// Default to "wait forever" timing, which means have to get a Signal()
// or Broadcast() to come out of this wait state.
TimedWait(TimeDelta::FromMilliseconds(INFINITE));
}
void ConditionVariable::TimedWait(const TimeDelta& max_time) {
Event* waiting_event;
HANDLE handle;
{
AutoLock auto_lock(internal_lock_);
if (RUNNING != run_state_) return; // Destruction in progress.
waiting_event = GetEventForWaiting();
handle = waiting_event->handle();
DCHECK(handle);
} // Release internal_lock.
{
AutoUnlock unlock(user_lock_); // Release caller's lock
WaitForSingleObject(handle, static_cast<DWORD>(max_time.InMilliseconds()));
// Minimize spurious signal creation window by recycling asap.
AutoLock auto_lock(internal_lock_);
RecycleEvent(waiting_event);
// Release internal_lock_
} // Reacquire callers lock to depth at entry.
}
// Broadcast() is guaranteed to signal all threads that were waiting (i.e., had
// a cv_event internally allocated for them) before Broadcast() was called.
void ConditionVariable::Broadcast() {
std::stack<HANDLE> handles; // See FAQ-question-10.
{
AutoLock auto_lock(internal_lock_);
if (waiting_list_.IsEmpty())
return;
while (!waiting_list_.IsEmpty())
// This is not a leak from waiting_list_. See FAQ-question 12.
handles.push(waiting_list_.PopBack()->handle());
} // Release internal_lock_.
while (!handles.empty()) {
SetEvent(handles.top());
handles.pop();
}
}
// Signal() will select one of the waiting threads, and signal it (signal its
// cv_event). For better performance we signal the thread that went to sleep
// most recently (LIFO). If we want fairness, then we wake the thread that has
// been sleeping the longest (FIFO).
void ConditionVariable::Signal() {
HANDLE handle;
{
AutoLock auto_lock(internal_lock_);
if (waiting_list_.IsEmpty())
return; // No one to signal.
// Only performance option should be used.
// This is not a leak from waiting_list. See FAQ-question 12.
handle = waiting_list_.PopBack()->handle(); // LIFO.
} // Release internal_lock_.
SetEvent(handle);
}
// GetEventForWaiting() provides a unique cv_event for any caller that needs to
// wait. This means that (worst case) we may over time create as many cv_event
// objects as there are threads simultaneously using this instance's Wait()
// functionality.
ConditionVariable::Event* ConditionVariable::GetEventForWaiting() {
// We hold internal_lock, courtesy of Wait().
Event* cv_event;
if (0 == recycling_list_size_) {
DCHECK(recycling_list_.IsEmpty());
cv_event = new Event();
cv_event->InitListElement();
allocation_counter_++;
// CHECK_NE is not defined in our codebase, so we have to use CHECK
CHECK(cv_event->handle());
} else {
cv_event = recycling_list_.PopFront();
recycling_list_size_--;
}
waiting_list_.PushBack(cv_event);
return cv_event;
}
// RecycleEvent() takes a cv_event that was previously used for Wait()ing, and
// recycles it for use in future Wait() calls for this or other threads.
// Note that there is a tiny chance that the cv_event is still signaled when we
// obtain it, and that can cause spurious signals (if/when we re-use the
// cv_event), but such is quite rare (see FAQ-question-5).
void ConditionVariable::RecycleEvent(Event* used_event) {
// We hold internal_lock, courtesy of Wait().
// If the cv_event timed out, then it is necessary to remove it from
// waiting_list_. If it was selected by Broadcast() or Signal(), then it is
// already gone.
used_event->Extract(); // Possibly redundant
recycling_list_.PushBack(used_event);
recycling_list_size_++;
}
//------------------------------------------------------------------------------
// The next section provides the implementation for the private Event class.
//------------------------------------------------------------------------------
// Event provides a doubly-linked-list of events for use exclusively by the
// ConditionVariable class.
// This custom container was crafted because no simple combination of STL
// classes appeared to support the functionality required. The specific
// unusual requirement for a linked-list-class is support for the Extract()
// method, which can remove an element from a list, potentially for insertion
// into a second list. Most critically, the Extract() method is idempotent,
// turning the indicated element into an extracted singleton whether it was
// contained in a list or not. This functionality allows one (or more) of
// threads to do the extraction. The iterator that identifies this extractable
// element (in this case, a pointer to the list element) can be used after
// arbitrary manipulation of the (possibly) enclosing list container. In
// general, STL containers do not provide iterators that can be used across
// modifications (insertions/extractions) of the enclosing containers, and
// certainly don't provide iterators that can be used if the identified
// element is *deleted* (removed) from the container.
// It is possible to use multiple redundant containers, such as an STL list,
// and an STL map, to achieve similar container semantics. This container has
// only O(1) methods, while the corresponding (multiple) STL container approach
// would have more complex O(log(N)) methods (yeah... N isn't that large).
// Multiple containers also makes correctness more difficult to assert, as
// data is redundantly stored and maintained, which is generally evil.
ConditionVariable::Event::Event() : handle_(0) {
next_ = prev_ = this; // Self referencing circular.
}
ConditionVariable::Event::~Event() {
if (0 == handle_) {
// This is the list holder
while (!IsEmpty()) {
Event* cv_event = PopFront();
DCHECK(cv_event->ValidateAsItem());
delete cv_event;
}
}
DCHECK(IsSingleton());
if (0 != handle_) {
int ret_val = CloseHandle(handle_);
DCHECK(ret_val);
}
}
// Change a container instance permanently into an element of a list.
void ConditionVariable::Event::InitListElement() {
DCHECK(!handle_);
handle_ = CreateEvent(NULL, false, false, NULL);
CHECK(handle_);
}
// Methods for use on lists.
bool ConditionVariable::Event::IsEmpty() const {
DCHECK(ValidateAsList());
return IsSingleton();
}
void ConditionVariable::Event::PushBack(Event* other) {
DCHECK(ValidateAsList());
DCHECK(other->ValidateAsItem());
DCHECK(other->IsSingleton());
// Prepare other for insertion.
other->prev_ = prev_;
other->next_ = this;
// Cut into list.
prev_->next_ = other;
prev_ = other;
DCHECK(ValidateAsDistinct(other));
}
ConditionVariable::Event* ConditionVariable::Event::PopFront() {
DCHECK(ValidateAsList());
DCHECK(!IsSingleton());
return next_->Extract();
}
ConditionVariable::Event* ConditionVariable::Event::PopBack() {
DCHECK(ValidateAsList());
DCHECK(!IsSingleton());
return prev_->Extract();
}
// Methods for use on list elements.
// Accessor method.
HANDLE ConditionVariable::Event::handle() const {
DCHECK(ValidateAsItem());
return handle_;
}
// Pull an element from a list (if it's in one).
ConditionVariable::Event* ConditionVariable::Event::Extract() {
DCHECK(ValidateAsItem());
if (!IsSingleton()) {
// Stitch neighbors together.
next_->prev_ = prev_;
prev_->next_ = next_;
// Make extractee into a singleton.
prev_ = next_ = this;
}
DCHECK(IsSingleton());
return this;
}
// Method for use on a list element or on a list.
bool ConditionVariable::Event::IsSingleton() const {
DCHECK(ValidateLinks());
return next_ == this;
}
// Provide pre/post conditions to validate correct manipulations.
bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const {
return ValidateLinks() && other->ValidateLinks() && (this != other);
}
bool ConditionVariable::Event::ValidateAsItem() const {
return (0 != handle_) && ValidateLinks();
}
bool ConditionVariable::Event::ValidateAsList() const {
return (0 == handle_) && ValidateLinks();
}
bool ConditionVariable::Event::ValidateLinks() const {
// Make sure both of our neighbors have links that point back to us.
// We don't do the O(n) check and traverse the whole loop, and instead only
// do a local check to (and returning from) our immediate neighbors.
return (next_->prev_ == this) && (prev_->next_ == this);
}
/*
FAQ On subtle implementation details:
1) What makes this problem subtle? Please take a look at "Strategies
for Implementing POSIX Condition Variables on Win32" by Douglas
C. Schmidt and Irfan Pyarali.
http://www.cs.wustl.edu/~schmidt/win32-cv-1.html It includes
discussions of numerous flawed strategies for implementing this
functionality. I'm not convinced that even the final proposed
implementation has semantics that are as nice as this implementation
(especially with regard to Broadcast() and the impact on threads that
try to Wait() after a Broadcast() has been called, but before all the
original waiting threads have been signaled).
2) Why can't you use a single wait_event for all threads that call
Wait()? See FAQ-question-1, or consider the following: If a single
event were used, then numerous threads calling Wait() could release
their cs locks, and be preempted just before calling
WaitForSingleObject(). If a call to Broadcast() was then presented on
a second thread, it would be impossible to actually signal all
waiting(?) threads. Some number of SetEvent() calls *could* be made,
but there could be no guarantee that those led to to more than one
signaled thread (SetEvent()'s may be discarded after the first!), and
there could be no guarantee that the SetEvent() calls didn't just
awaken "other" threads that hadn't even started waiting yet (oops).
Without any limit on the number of requisite SetEvent() calls, the
system would be forced to do many such calls, allowing many new waits
to receive spurious signals.
3) How does this implementation cause spurious signal events? The
cause in this implementation involves a race between a signal via
time-out and a signal via Signal() or Broadcast(). The series of
actions leading to this are:
a) Timer fires, and a waiting thread exits the line of code:
WaitForSingleObject(waiting_event, max_time.InMilliseconds());
b) That thread (in (a)) is randomly pre-empted after the above line,
leaving the waiting_event reset (unsignaled) and still in the
waiting_list_.
c) A call to Signal() (or Broadcast()) on a second thread proceeds, and
selects the waiting cv_event (identified in step (b)) as the event to revive
via a call to SetEvent().
d) The Signal() method (step c) calls SetEvent() on waiting_event (step b).
e) The waiting cv_event (step b) is now signaled, but no thread is
waiting on it.
f) When that waiting_event (step b) is reused, it will immediately
be signaled (spuriously).
4) Why do you recycle events, and cause spurious signals? First off,
the spurious events are very rare. They can only (I think) appear
when the race described in FAQ-question-3 takes place. This should be
very rare. Most(?) uses will involve only timer expiration, or only
Signal/Broadcast() actions. When both are used, it will be rare that
the race will appear, and it would require MANY Wait() and signaling
activities. If this implementation did not recycle events, then it
would have to create and destroy events for every call to Wait().
That allocation/deallocation and associated construction/destruction
would be costly (per wait), and would only be a rare benefit (when the
race was "lost" and a spurious signal took place). That would be bad
(IMO) optimization trade-off. Finally, such spurious events are
allowed by the specification of condition variables (such as
implemented in Vista), and hence it is better if any user accommodates
such spurious events (see usage note in condition_variable.h).
5) Why don't you reset events when you are about to recycle them, or
about to reuse them, so that the spurious signals don't take place?
The thread described in FAQ-question-3 step c may be pre-empted for an
arbitrary length of time before proceeding to step d. As a result,
the wait_event may actually be re-used *before* step (e) is reached.
As a result, calling reset would not help significantly.
6) How is it that the callers lock is released atomically with the
entry into a wait state? We commit to the wait activity when we
allocate the wait_event for use in a given call to Wait(). This
allocation takes place before the caller's lock is released (and
actually before our internal_lock_ is released). That allocation is
the defining moment when "the wait state has been entered," as that
thread *can* now be signaled by a call to Broadcast() or Signal().
Hence we actually "commit to wait" before releasing the lock, making
the pair effectively atomic.
8) Why do you need to lock your data structures during waiting, as the
caller is already in possession of a lock? We need to Acquire() and
Release() our internal lock during Signal() and Broadcast(). If we tried
to use a callers lock for this purpose, we might conflict with their
external use of the lock. For example, the caller may use to consistently
hold a lock on one thread while calling Signal() on another, and that would
block Signal().
9) Couldn't a more efficient implementation be provided if you
preclude using more than one external lock in conjunction with a
single ConditionVariable instance? Yes, at least it could be viewed
as a simpler API (since you don't have to reiterate the lock argument
in each Wait() call). One of the constructors now takes a specific
lock as an argument, and a there are corresponding Wait() calls that
don't specify a lock now. It turns that the resulting implmentation
can't be made more efficient, as the internal lock needs to be used by
Signal() and Broadcast(), to access internal data structures. As a
result, I was not able to utilize the user supplied lock (which is
being used by the user elsewhere presumably) to protect the private
member access.
9) Since you have a second lock, how can be be sure that there is no
possible deadlock scenario? Our internal_lock_ is always the last
lock acquired, and the first one released, and hence a deadlock (due
to critical section problems) is impossible as a consequence of our
lock.
10) When doing a Broadcast(), why did you copy all the events into
an STL queue, rather than making a linked-loop, and iterating over it?
The iterating during Broadcast() is done so outside the protection
of the internal lock. As a result, other threads, such as the thread
wherein a related event is waiting, could asynchronously manipulate
the links around a cv_event. As a result, the link structure cannot
be used outside a lock. Broadcast() could iterate over waiting
events by cycling in-and-out of the protection of the internal_lock,
but that appears more expensive than copying the list into an STL
stack.
11) Why did the lock.h file need to be modified so much for this
change? Central to a Condition Variable is the atomic release of a
lock during a Wait(). This places Wait() functionality exactly
mid-way between the two classes, Lock and Condition Variable. Given
that there can be nested Acquire()'s of locks, and Wait() had to
Release() completely a held lock, it was necessary to augment the Lock
class with a recursion counter. Even more subtle is the fact that the
recursion counter (in a Lock) must be protected, as many threads can
access it asynchronously. As a positive fallout of this, there are
now some DCHECKS to be sure no one Release()s a Lock more than they
Acquire()ed it, and there is ifdef'ed functionality that can detect
nested locks (legal under windows, but not under Posix).
12) Why is it that the cv_events removed from list in Broadcast() and Signal()
are not leaked? How are they recovered?? The cv_events that appear to leak are
taken from the waiting_list_. For each element in that list, there is currently
a thread in or around the WaitForSingleObject() call of Wait(), and those
threads have references to these otherwise leaked events. They are passed as
arguments to be recycled just aftre returning from WaitForSingleObject().
13) Why did you use a custom container class (the linked list), when STL has
perfectly good containers, such as an STL list? The STL list, as with any
container, does not guarantee the utility of an iterator across manipulation
(such as insertions and deletions) of the underlying container. The custom
double-linked-list container provided that assurance. I don't believe any
combination of STL containers provided the services that were needed at the same
O(1) efficiency as the custom linked list. The unusual requirement
for the container class is that a reference to an item within a container (an
iterator) needed to be maintained across an arbitrary manipulation of the
container. This requirement exposes itself in the Wait() method, where a
waiting_event must be selected prior to the WaitForSingleObject(), and then it
must be used as part of recycling to remove the related instance from the
waiting_list. A hash table (STL map) could be used, but I was embarrased to
use a complex and relatively low efficiency container when a doubly linked list
provided O(1) performance in all required operations. Since other operations
to provide performance-and/or-fairness required queue (FIFO) and list (LIFO)
containers, I would also have needed to use an STL list/queue as well as an STL
map. In the end I decided it would be "fun" to just do it right, and I
put so many assertions (DCHECKs) into the container class that it is trivial to
code review and validate its correctness.
*/

View file

@ -0,0 +1,56 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/cpu.h"
#include <intrin.h>
#include <string>
namespace base {
CPU::CPU()
: type_(0),
family_(0),
model_(0),
stepping_(0),
ext_model_(0),
ext_family_(0),
cpu_vendor_("unknown") {
Initialize();
}
void CPU::Initialize() {
int cpu_info[4] = {-1};
char cpu_string[0x20];
// __cpuid with an InfoType argument of 0 returns the number of
// valid Ids in CPUInfo[0] and the CPU identification string in
// the other three array elements. The CPU identification string is
// not in linear order. The code below arranges the information
// in a human readable form.
//
// More info can be found here:
// http://msdn.microsoft.com/en-us/library/hskdteyh.aspx
__cpuid(cpu_info, 0);
int num_ids = cpu_info[0];
memset(cpu_string, 0, sizeof(cpu_string));
*(reinterpret_cast<int*>(cpu_string)) = cpu_info[1];
*(reinterpret_cast<int*>(cpu_string+4)) = cpu_info[3];
*(reinterpret_cast<int*>(cpu_string+8)) = cpu_info[2];
// Interpret CPU feature information.
if (num_ids > 0) {
__cpuid(cpu_info, 1);
stepping_ = cpu_info[0] & 0xf;
model_ = (cpu_info[0] >> 4) & 0xf;
family_ = (cpu_info[0] >> 8) & 0xf;
type_ = (cpu_info[0] >> 12) & 0x3;
ext_model_ = (cpu_info[0] >> 16) & 0xf;
ext_family_ = (cpu_info[0] >> 20) & 0xff;
cpu_vendor_ = cpu_string;
}
}
} // namespace base

View file

@ -0,0 +1,44 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_CPU_H_
#define BASE_CPU_H_
#include <string>
namespace base {
// Query information about the processor.
class CPU {
public:
// Constructor
CPU();
// Accessors for CPU information.
const std::string& vendor_name() const { return cpu_vendor_; }
int stepping() const { return stepping_; }
int model() const { return model_; }
int family() const { return family_; }
int type() const { return type_; }
int extended_model() const { return ext_model_; }
int extended_family() const { return ext_family_; }
private:
// Query the processor for CPUID information.
void Initialize();
int type_; // process type
int family_; // family of the processor
int model_; // model of processor
int stepping_; // processor revision number
int ext_model_;
int ext_family_;
std::string cpu_vendor_;
};
} // namespace base
#endif // BASE_CPU_H_

View file

@ -0,0 +1,103 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// derived from dir_reader_linux.h
#ifndef BASE_DIR_READER_BSD_H_
#define BASE_DIR_READER_BSD_H_
#pragma once
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <unistd.h>
#include "base/logging.h"
#include "base/eintr_wrapper.h"
// See the comments in dir_reader_posix.h about this.
namespace base {
class DirReaderBSD {
public:
explicit DirReaderBSD(const char* directory_path)
#ifdef O_DIRECTORY
: fd_(open(directory_path, O_RDONLY | O_DIRECTORY)),
#else
: fd_(open(directory_path, O_RDONLY)),
#endif
offset_(0),
size_(0) {
memset(buf_, 0, sizeof(buf_));
}
~DirReaderBSD() {
if (fd_ >= 0) {
if (HANDLE_EINTR(close(fd_)))
DLOG(ERROR) << "Failed to close directory handle";
}
}
bool IsValid() const {
return fd_ >= 0;
}
// Move to the next entry returning false if the iteration is complete.
bool Next() {
if (size_) {
struct dirent* dirent = reinterpret_cast<struct dirent*>(&buf_[offset_]);
#ifdef OS_DRAGONFLY
offset_ += _DIRENT_DIRSIZ(dirent);
#else
offset_ += dirent->d_reclen;
#endif
}
if (offset_ != size_)
return true;
const int r = getdents(fd_, buf_, sizeof(buf_));
if (r == 0)
return false;
if (r == -1) {
DLOG(ERROR) << "getdents returned an error: " << errno;
return false;
}
size_ = r;
offset_ = 0;
return true;
}
const char* name() const {
if (!size_)
return NULL;
const struct dirent* dirent =
reinterpret_cast<const struct dirent*>(&buf_[offset_]);
return dirent->d_name;
}
int fd() const {
return fd_;
}
static bool IsFallback() {
return false;
}
private:
const int fd_;
char buf_[512];
size_t offset_, size_;
DISALLOW_COPY_AND_ASSIGN(DirReaderBSD);
};
} // namespace base
#endif // BASE_DIR_READER_BSD_H_

View file

@ -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_

View file

@ -0,0 +1,101 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DIR_READER_LINUX_H_
#define BASE_DIR_READER_LINUX_H_
#pragma once
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "base/logging.h"
#include "base/eintr_wrapper.h"
// See the comments in dir_reader_posix.h about this.
namespace base {
struct linux_dirent {
uint64_t d_ino;
int64_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[0];
};
class DirReaderLinux {
public:
explicit DirReaderLinux(const char* directory_path)
: fd_(open(directory_path, O_RDONLY | O_DIRECTORY)),
offset_(0),
size_(0) {
memset(buf_, 0, sizeof(buf_));
}
~DirReaderLinux() {
if (fd_ >= 0) {
if (HANDLE_EINTR(close(fd_)))
DLOG(ERROR) << "Failed to close directory handle";
}
}
bool IsValid() const {
return fd_ >= 0;
}
// Move to the next entry returning false if the iteration is complete.
bool Next() {
if (size_) {
linux_dirent* dirent = reinterpret_cast<linux_dirent*>(&buf_[offset_]);
offset_ += dirent->d_reclen;
}
if (offset_ != size_)
return true;
const int r = syscall(__NR_getdents64, fd_, buf_, sizeof(buf_));
if (r == 0)
return false;
if (r == -1) {
DLOG(ERROR) << "getdents64 returned an error: " << errno;
return false;
}
size_ = r;
offset_ = 0;
return true;
}
const char* name() const {
if (!size_)
return NULL;
const linux_dirent* dirent =
reinterpret_cast<const linux_dirent*>(&buf_[offset_]);
return dirent->d_name;
}
int fd() const {
return fd_;
}
static bool IsFallback() {
return false;
}
private:
const int fd_;
unsigned char buf_[512];
size_t offset_, size_;
DISALLOW_COPY_AND_ASSIGN(DirReaderLinux);
};
} // namespace base
#endif // BASE_DIR_READER_LINUX_H_

View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_DIR_READER_POSIX_H_
#define BASE_DIR_READER_POSIX_H_
#pragma once
#include "build/build_config.h"
// This header provides a class, DirReaderPosix, which allows one to open and
// read from directories without allocating memory. For the interface, see
// the generic fallback in dir_reader_fallback.h.
// Mac note: OS X has getdirentries, but it only works if we restrict Chrome to
// 32-bit inodes. There is a getdirentries64 syscall in 10.6, but it's not
// wrapped and the direct syscall interface is unstable. Using an unstable API
// seems worse than falling back to enumerating all file descriptors so we will
// probably never implement this on the Mac.
#if defined(OS_LINUX)
#include "base/dir_reader_linux.h"
#elif defined(OS_BSD) && !defined(__GLIBC__)
#include "base/dir_reader_bsd.h"
#else
#include "base/dir_reader_fallback.h"
#endif
namespace base {
#if defined(OS_LINUX)
typedef DirReaderLinux DirReaderPosix;
#elif defined(OS_BSD) && !defined(__GLIBC__)
typedef DirReaderBSD DirReaderPosix;
#else
typedef DirReaderFallback DirReaderPosix;
#endif
} // namespace base
#endif // BASE_DIR_READER_POSIX_H_

View file

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This provides a wrapper around system calls which may be interrupted by a
// signal and return EINTR. See man 7 signal.
//
// On Windows, this wrapper macro does nothing.
#ifndef BASE_EINTR_WRAPPER_H_
#define BASE_EINTR_WRAPPER_H_
#include "build/build_config.h"
#if defined(OS_POSIX)
#include <errno.h>
#define HANDLE_EINTR(x) ({ \
typeof(x) __eintr_result__; \
do { \
__eintr_result__ = x; \
} while (__eintr_result__ == -1 && errno == EINTR); \
__eintr_result__;\
})
#else
#define HANDLE_EINTR(x) x
#endif // OS_POSIX
#endif // !BASE_EINTR_WRAPPER_H_

View file

@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_FILE_DESCRIPTOR_POSIX_H_
#define BASE_FILE_DESCRIPTOR_POSIX_H_
namespace base {
// -----------------------------------------------------------------------------
// We introduct a special structure for file descriptors in order that we are
// able to use template specialisation to special-case their handling.
//
// WARNING: (Chromium only) There are subtleties to consider if serialising
// these objects over IPC. See comments in chrome/common/ipc_message_utils.h
// above the template specialisation for this structure.
// -----------------------------------------------------------------------------
struct FileDescriptor {
FileDescriptor()
: fd(-1),
auto_close(false) { }
FileDescriptor(int ifd, bool iauto_close)
: fd(ifd),
auto_close(iauto_close) { }
int fd;
// If true, this file descriptor should be closed after it has been used. For
// example an IPC system might interpret this flag as indicating that the
// file descriptor it has been given should be closed after use.
bool auto_close;
};
} // namespace base
#endif // BASE_FILE_DESCRIPTOR_POSIX_H_

View file

@ -0,0 +1,96 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/file_descriptor_shuffle.h"
#include <errno.h>
#include <unistd.h>
#include "base/eintr_wrapper.h"
#include "base/logging.h"
namespace base {
bool PerformInjectiveMultimapDestructive(
InjectiveMultimap* m, InjectionDelegate* delegate) {
static const size_t kMaxExtraFDs = 16;
int extra_fds[kMaxExtraFDs];
unsigned next_extra_fd = 0;
// DANGER: this function may not allocate.
for (InjectiveMultimap::iterator i = m->begin(); i != m->end(); ++i) {
int temp_fd = -1;
// We DCHECK the injectiveness of the mapping.
for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) {
DCHECK(i->dest != j->dest) << "Both fd " << i->source
<< " and " << j->source << " map to " << i->dest;
}
const bool is_identity = i->source == i->dest;
for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) {
if (!is_identity && i->dest == j->source) {
if (temp_fd == -1) {
if (!delegate->Duplicate(&temp_fd, i->dest))
return false;
if (next_extra_fd < kMaxExtraFDs) {
extra_fds[next_extra_fd++] = temp_fd;
} else {
DLOG(ERROR) << "PerformInjectiveMultimapDestructive overflowed "
<< "extra_fds. Leaking file descriptors!";
}
}
j->source = temp_fd;
j->close = false;
}
if (i->close && i->source == j->dest)
i->close = false;
if (i->close && i->source == j->source) {
i->close = false;
j->close = true;
}
}
if (!is_identity) {
if (!delegate->Move(i->source, i->dest))
return false;
}
if (!is_identity && i->close)
delegate->Close(i->source);
}
for (unsigned i = 0; i < next_extra_fd; i++)
delegate->Close(extra_fds[i]);
return true;
}
bool PerformInjectiveMultimap(const InjectiveMultimap& m_in,
InjectionDelegate* delegate) {
InjectiveMultimap m(m_in);
return PerformInjectiveMultimapDestructive(&m, delegate);
}
bool FileDescriptorTableInjection::Duplicate(int* result, int fd) {
*result = HANDLE_EINTR(dup(fd));
return *result >= 0;
}
bool FileDescriptorTableInjection::Move(int src, int dest) {
return HANDLE_EINTR(dup2(src, dest)) != -1;
}
void FileDescriptorTableInjection::Close(int fd) {
HANDLE_EINTR(close(fd));
}
} // namespace base

View file

@ -0,0 +1,83 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_FILE_DESCRIPTOR_SHUFFLE_H_
#define BASE_FILE_DESCRIPTOR_SHUFFLE_H_
#include "mozilla/Attributes.h"
// This code exists to perform the shuffling of file descriptors which is
// commonly needed when forking subprocesses. The naive approve is very simple,
// just call dup2 to setup the desired descriptors, but wrong. It's tough to
// handle the edge cases (like mapping 0 -> 1, 1 -> 0) correctly.
//
// In order to unittest this code, it's broken into the abstract action (an
// injective multimap) and the concrete code for dealing with file descriptors.
// Users should use the code like this:
// base::InjectiveMultimap file_descriptor_map;
// file_descriptor_map.push_back(base::InjectionArc(devnull, 0, true));
// file_descriptor_map.push_back(base::InjectionArc(devnull, 2, true));
// file_descriptor_map.push_back(base::InjectionArc(pipe[1], 1, true));
// base::ShuffleFileDescriptors(file_descriptor_map);
//
// and trust the the Right Thing will get done.
#include <vector>
namespace base {
// A Delegate which performs the actions required to perform an injective
// multimapping in place.
class InjectionDelegate {
public:
// Duplicate |fd|, an element of the domain, and write a fresh element of the
// domain into |result|. Returns true iff successful.
virtual bool Duplicate(int* result, int fd) = 0;
// Destructively move |src| to |dest|, overwriting |dest|. Returns true iff
// successful.
virtual bool Move(int src, int dest) = 0;
// Delete an element of the domain.
virtual void Close(int fd) = 0;
};
// An implementation of the InjectionDelegate interface using the file
// descriptor table of the current process as the domain.
class FileDescriptorTableInjection : public InjectionDelegate {
virtual bool Duplicate(int* result, int fd) override;
virtual bool Move(int src, int dest) override;
virtual void Close(int fd) override;
};
// A single arc of the directed graph which describes an injective multimapping.
struct InjectionArc {
InjectionArc(int in_source, int in_dest, bool in_close)
: source(in_source),
dest(in_dest),
close(in_close) {
}
int source;
int dest;
bool close; // if true, delete the source element after performing the
// mapping.
};
typedef std::vector<InjectionArc> InjectiveMultimap;
bool PerformInjectiveMultimap(const InjectiveMultimap& map,
InjectionDelegate* delegate);
bool PerformInjectiveMultimapDestructive(InjectiveMultimap* map,
InjectionDelegate* delegate);
// This function will not call malloc but will mutate |map|
static inline bool ShuffleFileDescriptors(InjectiveMultimap *map) {
FileDescriptorTableInjection delegate;
return PerformInjectiveMultimapDestructive(map, &delegate);
}
} // namespace base
#endif // !BASE_FILE_DESCRIPTOR_SHUFFLE_H_

View file

@ -0,0 +1,332 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fstream>
#include "base/file_path.h"
#include "base/logging.h"
// These includes are just for the *Hack functions, and should be removed
// when those functions are removed.
#include "base/string_piece.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/");
#else // FILE_PATH_USES_WIN_SEPARATORS
const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/");
#endif // FILE_PATH_USES_WIN_SEPARATORS
const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL(".");
const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL("..");
const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.');
namespace {
// If this FilePath contains a drive letter specification, returns the
// position of the last character of the drive letter specification,
// otherwise returns npos. This can only be true on Windows, when a pathname
// begins with a letter followed by a colon. On other platforms, this always
// returns npos.
FilePath::StringType::size_type FindDriveLetter(
const FilePath::StringType& path) {
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
// This is dependent on an ASCII-based character set, but that's a
// reasonable assumption. iswalpha can be too inclusive here.
if (path.length() >= 2 && path[1] == L':' &&
((path[0] >= L'A' && path[0] <= L'Z') ||
(path[0] >= L'a' && path[0] <= L'z'))) {
return 1;
}
#endif // FILE_PATH_USES_DRIVE_LETTERS
return FilePath::StringType::npos;
}
bool IsPathAbsolute(const FilePath::StringType& path) {
#if defined(FILE_PATH_USES_DRIVE_LETTERS)
FilePath::StringType::size_type letter = FindDriveLetter(path);
if (letter != FilePath::StringType::npos) {
// Look for a separator right after the drive specification.
return path.length() > letter + 1 &&
FilePath::IsSeparator(path[letter + 1]);
}
// Look for a pair of leading separators.
return path.length() > 1 &&
FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]);
#else // FILE_PATH_USES_DRIVE_LETTERS
// Look for a separator in the first position.
return path.length() > 0 && FilePath::IsSeparator(path[0]);
#endif // FILE_PATH_USES_DRIVE_LETTERS
}
} // namespace
bool FilePath::IsSeparator(CharType character) {
for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) {
if (character == kSeparators[i]) {
return true;
}
}
return false;
}
// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't
// guaranteed to not modify their input strings, and in fact are implemented
// differently in this regard on different platforms. Don't use them, but
// adhere to their behavior.
FilePath FilePath::DirName() const {
FilePath new_path(path_);
new_path.StripTrailingSeparatorsInternal();
// The drive letter, if any, always needs to remain in the output. If there
// is no drive letter, as will always be the case on platforms which do not
// support drive letters, letter will be npos, or -1, so the comparisons and
// resizes below using letter will still be valid.
StringType::size_type letter = FindDriveLetter(new_path.path_);
StringType::size_type last_separator =
new_path.path_.find_last_of(kSeparators, StringType::npos,
arraysize(kSeparators) - 1);
if (last_separator == StringType::npos) {
// path_ is in the current directory.
new_path.path_.resize(letter + 1);
} else if (last_separator == letter + 1) {
// path_ is in the root directory.
new_path.path_.resize(letter + 2);
} else if (last_separator == letter + 2 &&
IsSeparator(new_path.path_[letter + 1])) {
// path_ is in "//" (possibly with a drive letter); leave the double
// separator intact indicating alternate root.
new_path.path_.resize(letter + 3);
} else if (last_separator != 0) {
// path_ is somewhere else, trim the basename.
new_path.path_.resize(last_separator);
}
new_path.StripTrailingSeparatorsInternal();
if (!new_path.path_.length())
new_path.path_ = kCurrentDirectory;
return new_path;
}
FilePath FilePath::BaseName() const {
FilePath new_path(path_);
new_path.StripTrailingSeparatorsInternal();
// The drive letter, if any, is always stripped.
StringType::size_type letter = FindDriveLetter(new_path.path_);
if (letter != StringType::npos) {
new_path.path_.erase(0, letter + 1);
}
// Keep everything after the final separator, but if the pathname is only
// one character and it's a separator, leave it alone.
StringType::size_type last_separator =
new_path.path_.find_last_of(kSeparators, StringType::npos,
arraysize(kSeparators) - 1);
if (last_separator != StringType::npos &&
last_separator < new_path.path_.length() - 1) {
new_path.path_.erase(0, last_separator + 1);
}
return new_path;
}
FilePath::StringType FilePath::Extension() const {
// BaseName() calls StripTrailingSeparators, so cases like /foo.baz/// work.
StringType base = BaseName().value();
// Special case "." and ".."
if (base == kCurrentDirectory || base == kParentDirectory)
return StringType();
const StringType::size_type last_dot = base.rfind(kExtensionSeparator);
if (last_dot == StringType::npos)
return StringType();
return StringType(base, last_dot);
}
FilePath FilePath::RemoveExtension() const {
StringType ext = Extension();
// It's important to check Extension() since that verifies that the
// kExtensionSeparator actually appeared in the last path component.
if (ext.empty())
return FilePath(path_);
// Since Extension() verified that the extension is in fact in the last path
// component, this substr will effectively strip trailing separators.
const StringType::size_type last_dot = path_.rfind(kExtensionSeparator);
return FilePath(path_.substr(0, last_dot));
}
FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const {
if (suffix.empty())
return FilePath(path_);
if (path_.empty())
return FilePath();
StringType base = BaseName().value();
if (base.empty())
return FilePath();
if (*(base.end() - 1) == kExtensionSeparator) {
// Special case "." and ".."
if (base == kCurrentDirectory || base == kParentDirectory) {
return FilePath();
}
}
StringType ext = Extension();
StringType ret = RemoveExtension().value();
ret.append(suffix);
ret.append(ext);
return FilePath(ret);
}
FilePath FilePath::ReplaceExtension(const StringType& extension) const {
if (path_.empty())
return FilePath();
StringType base = BaseName().value();
if (base.empty())
return FilePath();
if (*(base.end() - 1) == kExtensionSeparator) {
// Special case "." and ".."
if (base == kCurrentDirectory || base == kParentDirectory) {
return FilePath();
}
}
FilePath no_ext = RemoveExtension();
// If the new extension is "" or ".", then just remove the current extension.
if (extension.empty() || extension == StringType(1, kExtensionSeparator))
return no_ext;
StringType str = no_ext.value();
if (extension[0] != kExtensionSeparator)
str.append(1, kExtensionSeparator);
str.append(extension);
return FilePath(str);
}
FilePath FilePath::Append(const StringType& component) const {
DCHECK(!IsPathAbsolute(component));
if (path_.compare(kCurrentDirectory) == 0) {
// Append normally doesn't do any normalization, but as a special case,
// when appending to kCurrentDirectory, just return a new path for the
// component argument. Appending component to kCurrentDirectory would
// serve no purpose other than needlessly lengthening the path, and
// it's likely in practice to wind up with FilePath objects containing
// only kCurrentDirectory when calling DirName on a single relative path
// component.
return FilePath(component);
}
FilePath new_path(path_);
new_path.StripTrailingSeparatorsInternal();
// Don't append a separator if the path is empty (indicating the current
// directory) or if the path component is empty (indicating nothing to
// append).
if (component.length() > 0 && new_path.path_.length() > 0) {
// Don't append a separator if the path still ends with a trailing
// separator after stripping (indicating the root directory).
if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) {
// Don't append a separator if the path is just a drive letter.
if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) {
new_path.path_.append(1, kSeparators[0]);
}
}
}
new_path.path_.append(component);
return new_path;
}
FilePath FilePath::Append(const FilePath& component) const {
return Append(component.value());
}
FilePath FilePath::AppendASCII(const std::string& component) const {
DCHECK(IsStringASCII(component));
#if defined(OS_WIN)
return Append(ASCIIToWide(component));
#elif defined(OS_POSIX)
return Append(component);
#endif
}
bool FilePath::IsAbsolute() const {
return IsPathAbsolute(path_);
}
#if defined(OS_POSIX)
// See file_path.h for a discussion of the encoding of paths on POSIX
// platforms. These *Hack() functions are not quite correct, but they're
// only temporary while we fix the remainder of the code.
// Remember to remove the #includes at the top when you remove these.
// static
FilePath FilePath::FromWStringHack(const std::wstring& wstring) {
return FilePath(base::SysWideToNativeMB(wstring));
}
std::wstring FilePath::ToWStringHack() const {
return base::SysNativeMBToWide(path_);
}
#elif defined(OS_WIN)
// static
FilePath FilePath::FromWStringHack(const std::wstring& wstring) {
return FilePath(wstring);
}
std::wstring FilePath::ToWStringHack() const {
return path_;
}
#endif
void FilePath::OpenInputStream(std::ifstream& stream) const {
stream.open(
#ifndef __MINGW32__
path_.c_str(),
#else
base::SysWideToNativeMB(path_).c_str(),
#endif
std::ios::in | std::ios::binary);
}
FilePath FilePath::StripTrailingSeparators() const {
FilePath new_path(path_);
new_path.StripTrailingSeparatorsInternal();
return new_path;
}
void FilePath::StripTrailingSeparatorsInternal() {
// If there is no drive letter, start will be 1, which will prevent stripping
// the leading separator if there is only one separator. If there is a drive
// letter, start will be set appropriately to prevent stripping the first
// separator following the drive letter, if a separator immediately follows
// the drive letter.
StringType::size_type start = FindDriveLetter(path_) + 2;
StringType::size_type last_stripped = StringType::npos;
for (StringType::size_type pos = path_.length();
pos > start && IsSeparator(path_[pos - 1]);
--pos) {
// If the string only has two separators and they're at the beginning,
// don't strip them, unless the string began with more than two separators.
if (pos != start + 1 || last_stripped == start + 2 ||
!IsSeparator(path_[start - 1])) {
path_.resize(pos - 1);
last_stripped = pos;
}
}
}

View file

@ -0,0 +1,277 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// FilePath is a container for pathnames stored in a platform's native string
// type, providing containers for manipulation in according with the
// platform's conventions for pathnames. It supports the following path
// types:
//
// POSIX Windows
// --------------- ----------------------------------
// Fundamental type char[] wchar_t[]
// Encoding unspecified* UTF-16
// Separator / \, tolerant of /
// Drive letters no case-insensitive A-Z followed by :
// Alternate root // (surprise!) \\, for UNC paths
//
// * The encoding need not be specified on POSIX systems, although some
// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8.
// Linux does not specify an encoding, but in practice, the locale's
// character set may be used.
//
// FilePath objects are intended to be used anywhere paths are. An
// application may pass FilePath objects around internally, masking the
// underlying differences between systems, only differing in implementation
// where interfacing directly with the system. For example, a single
// OpenFile(const FilePath &) function may be made available, allowing all
// callers to operate without regard to the underlying implementation. On
// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might
// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This
// allows each platform to pass pathnames around without requiring conversions
// between encodings, which has an impact on performance, but more imporantly,
// has an impact on correctness on platforms that do not have well-defined
// encodings for pathnames.
//
// Several methods are available to perform common operations on a FilePath
// object, such as determining the parent directory (DirName), isolating the
// final path component (BaseName), and appending a relative pathname string
// to an existing FilePath object (Append). These methods are highly
// recommended over attempting to split and concatenate strings directly.
// These methods are based purely on string manipulation and knowledge of
// platform-specific pathname conventions, and do not consult the filesystem
// at all, making them safe to use without fear of blocking on I/O operations.
// These methods do not function as mutators but instead return distinct
// instances of FilePath objects, and are therefore safe to use on const
// objects. The objects themselves are safe to share between threads.
//
// To aid in initialization of FilePath objects from string literals, a
// FILE_PATH_LITERAL macro is provided, which accounts for the difference
// between char[]-based pathnames on POSIX systems and wchar_t[]-based
// pathnames on Windows.
//
// Because a FilePath object should not be instantiated at the global scope,
// instead, use a FilePath::CharType[] and initialize it with
// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the
// character array. Example:
//
// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt");
// |
// | void Function() {
// | FilePath log_file_path(kLogFileName);
// | [...]
// | }
#ifndef BASE_FILE_PATH_H_
#define BASE_FILE_PATH_H_
#include <string>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/hash_tables.h"
// Windows-style drive letter support and pathname separator characters can be
// enabled and disabled independently, to aid testing. These #defines are
// here so that the same setting can be used in both the implementation and
// in the unit test.
#if defined(OS_WIN)
#define FILE_PATH_USES_DRIVE_LETTERS
#define FILE_PATH_USES_WIN_SEPARATORS
#endif // OS_WIN
// An abstraction to isolate users from the differences between native
// pathnames on different platforms.
class FilePath {
public:
#if defined(OS_POSIX)
// On most platforms, native pathnames are char arrays, and the encoding
// may or may not be specified. On Mac OS X, native pathnames are encoded
// in UTF-8.
typedef std::string StringType;
#elif defined(OS_WIN)
// On Windows, for Unicode-aware applications, native pathnames are wchar_t
// arrays encoded in UTF-16.
typedef std::wstring StringType;
#endif // OS_WIN
typedef StringType::value_type CharType;
// Null-terminated array of separators used to separate components in
// hierarchical paths. Each character in this array is a valid separator,
// but kSeparators[0] is treated as the canonical separator and will be used
// when composing pathnames.
static const CharType kSeparators[];
// A special path component meaning "this directory."
static const CharType kCurrentDirectory[];
// A special path component meaning "the parent directory."
static const CharType kParentDirectory[];
// The character used to identify a file extension.
static const CharType kExtensionSeparator;
FilePath() {}
FilePath(const FilePath& that) : path_(that.path_) {}
explicit FilePath(const StringType& path) : path_(path) {}
#if defined(OS_WIN)
explicit FilePath(const wchar_t* path) : path_(path) {}
#endif
FilePath& operator=(const FilePath& that) {
path_ = that.path_;
return *this;
}
bool operator==(const FilePath& that) const {
return path_ == that.path_;
}
bool operator!=(const FilePath& that) const {
return path_ != that.path_;
}
// Required for some STL containers and operations
bool operator<(const FilePath& that) const {
return path_ < that.path_;
}
const StringType& value() const { return path_; }
bool empty() const { return path_.empty(); }
// Returns true if |character| is in kSeparators.
static bool IsSeparator(CharType character);
// Returns a FilePath corresponding to the directory containing the path
// named by this object, stripping away the file component. If this object
// only contains one component, returns a FilePath identifying
// kCurrentDirectory. If this object already refers to the root directory,
// returns a FilePath identifying the root directory.
FilePath DirName() const;
// Returns a FilePath corresponding to the last path component of this
// object, either a file or a directory. If this object already refers to
// the root directory, returns a FilePath identifying the root directory;
// this is the only situation in which BaseName will return an absolute path.
FilePath BaseName() const;
// Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if
// the file has no extension. If non-empty, Extension() will always start
// with precisely one ".". The following code should always work regardless
// of the value of path.
// new_path = path.RemoveExtension().value().append(path.Extension());
// ASSERT(new_path == path.value());
// NOTE: this is different from the original file_util implementation which
// returned the extension without a leading "." ("jpg" instead of ".jpg")
StringType Extension() const;
// Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg"
// NOTE: this is slightly different from the similar file_util implementation
// which returned simply 'jojo'.
FilePath RemoveExtension() const;
// Inserts |suffix| after the file name portion of |path| but before the
// extension. Returns "" if BaseName() == "." or "..".
// Examples:
// path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg"
// path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg"
// path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)"
// path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)"
FilePath InsertBeforeExtension(const StringType& suffix) const;
// Replaces the extension of |file_name| with |extension|. If |file_name|
// does not have an extension, them |extension| is added. If |extension| is
// empty, then the extension is removed from |file_name|.
// Returns "" if BaseName() == "." or "..".
FilePath ReplaceExtension(const StringType& extension) const;
// Returns a FilePath by appending a separator and the supplied path
// component to this object's path. Append takes care to avoid adding
// excessive separators if this object's path already ends with a separator.
// If this object's path is kCurrentDirectory, a new FilePath corresponding
// only to |component| is returned. |component| must be a relative path;
// it is an error to pass an absolute path.
FilePath Append(const StringType& component) const WARN_UNUSED_RESULT;
FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT;
// Although Windows StringType is std::wstring, since the encoding it uses for
// paths is well defined, it can handle ASCII path components as well.
// Mac uses UTF8, and since ASCII is a subset of that, it works there as well.
// On Linux, although it can use any 8-bit encoding for paths, we assume that
// ASCII is a valid subset, regardless of the encoding, since many operating
// system paths will always be ASCII.
FilePath AppendASCII(const std::string& component) const WARN_UNUSED_RESULT;
// Returns true if this FilePath contains an absolute path. On Windows, an
// absolute path begins with either a drive letter specification followed by
// a separator character, or with two separator characters. On POSIX
// platforms, an absolute path begins with a separator character.
bool IsAbsolute() const;
// Returns a copy of this FilePath that does not end with a trailing
// separator.
FilePath StripTrailingSeparators() const;
// Calls open on given ifstream instance
void OpenInputStream(std::ifstream &stream) const;
// Older Chromium code assumes that paths are always wstrings.
// This function converts a wstring to a FilePath, and is useful to smooth
// porting that old code to the FilePath API.
// It has "Hack" in its name so people feel bad about using it.
// TODO(port): remove these functions.
static FilePath FromWStringHack(const std::wstring& wstring);
// Older Chromium code assumes that paths are always wstrings.
// This function produces a wstring from a FilePath, and is useful to smooth
// porting that old code to the FilePath API.
// It has "Hack" in its name so people feel bad about using it.
// TODO(port): remove these functions.
std::wstring ToWStringHack() const;
private:
// Remove trailing separators from this object. If the path is absolute, it
// will never be stripped any more than to refer to the absolute root
// directory, so "////" will become "/", not "". A leading pair of
// separators is never stripped, to support alternate roots. This is used to
// support UNC paths on Windows.
void StripTrailingSeparatorsInternal();
StringType path_;
};
// Macros for string literal initialization of FilePath::CharType[].
#if defined(OS_POSIX)
#define FILE_PATH_LITERAL(x) x
#elif defined(OS_WIN)
#define FILE_PATH_LITERAL(x) L ## x
#endif // OS_WIN
// Implement hash function so that we can use FilePaths in hashsets and maps.
#if defined(COMPILER_GCC) && !defined(ANDROID)
namespace __gnu_cxx {
template<>
struct hash<FilePath> {
size_t operator()(const FilePath& f) const {
return hash<FilePath::StringType>()(f.value());
}
};
} // namespace __gnu_cxx
#elif defined(COMPILER_MSVC)
namespace stdext {
inline size_t hash_value(const FilePath& f) {
return hash_value(f.value());
}
} // namespace stdext
#endif // COMPILER
#endif // BASE_FILE_PATH_H_

View file

@ -0,0 +1,244 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/file_util.h"
#if defined(OS_WIN)
#include <io.h>
#endif
#include <stdio.h>
#if defined(ANDROID) || defined(OS_POSIX)
#include <unistd.h>
#endif
#include <fstream>
#include "base/file_path.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/string_piece.h"
#include "base/sys_string_conversions.h"
namespace {
const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
} // namespace
namespace file_util {
bool EndsWithSeparator(const FilePath& path) {
FilePath::StringType value = path.value();
if (value.empty())
return false;
return FilePath::IsSeparator(value[value.size() - 1]);
}
void TrimTrailingSeparator(std::wstring* dir) {
while (dir->length() > 1 && EndsWithSeparator(dir))
dir->resize(dir->length() - 1);
}
FilePath::StringType GetFileExtensionFromPath(const FilePath& path) {
FilePath::StringType file_name = path.BaseName().value();
const FilePath::StringType::size_type last_dot =
file_name.rfind(kExtensionSeparator);
return FilePath::StringType(last_dot == FilePath::StringType::npos ?
FILE_PATH_LITERAL("") :
file_name, last_dot+1);
}
void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
FilePath::StringType& value =
const_cast<FilePath::StringType&>(path->value());
const FilePath::StringType::size_type last_dot =
value.rfind(kExtensionSeparator);
const FilePath::StringType::size_type last_separator =
value.find_last_of(FilePath::StringType(FilePath::kSeparators));
if (last_dot == FilePath::StringType::npos ||
(last_separator != std::wstring::npos && last_dot < last_separator)) {
// The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
// We should just append the suffix to the entire path.
value.append(suffix);
return;
}
value.insert(last_dot, suffix);
}
void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) {
FilePath::StringType clean_extension;
// If the new extension is "" or ".", then we will just remove the current
// extension.
if (!extension.empty() &&
extension != FilePath::StringType(&kExtensionSeparator, 1)) {
if (extension[0] != kExtensionSeparator)
clean_extension.append(&kExtensionSeparator, 1);
clean_extension.append(extension);
}
FilePath::StringType& value =
const_cast<FilePath::StringType&>(path->value());
const FilePath::StringType::size_type last_dot =
value.rfind(kExtensionSeparator);
const FilePath::StringType::size_type last_separator =
value.find_last_of(FilePath::StringType(FilePath::kSeparators));
// Erase the current extension, if any.
if ((last_dot > last_separator ||
last_separator == FilePath::StringType::npos) &&
last_dot != FilePath::StringType::npos)
value.erase(last_dot);
value.append(clean_extension);
}
FILE* CreateAndOpenTemporaryFile(FilePath* path) {
FilePath directory;
if (!GetTempDir(&directory))
return NULL;
return CreateAndOpenTemporaryFileInDir(directory, path);
}
bool GetFileSize(const FilePath& file_path, int64_t* file_size) {
FileInfo info;
if (!GetFileInfo(file_path, &info))
return false;
*file_size = info.size;
return true;
}
bool CloseFile(FILE* file) {
if (file == NULL)
return true;
return fclose(file) == 0;
}
// Deprecated functions ----------------------------------------------------
bool AbsolutePath(std::wstring* path_str) {
FilePath path(FilePath::FromWStringHack(*path_str));
if (!AbsolutePath(&path))
return false;
*path_str = path.ToWStringHack();
return true;
}
void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
if (!path) {
NOTREACHED();
return; // Don't crash in this function in release builds.
}
if (!EndsWithSeparator(path))
path->push_back(FilePath::kSeparators[0]);
path->append(new_ending);
}
bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
return CopyFile(FilePath::FromWStringHack(from_path),
FilePath::FromWStringHack(to_path));
}
bool CreateDirectory(const std::wstring& full_path) {
return CreateDirectory(FilePath::FromWStringHack(full_path));
}
bool CreateNewTempDirectory(const std::wstring& prefix,
std::wstring* new_temp_path) {
#if defined(OS_WIN)
FilePath::StringType dir_prefix(prefix);
#elif defined(OS_POSIX)
FilePath::StringType dir_prefix = WideToUTF8(prefix);
#endif
FilePath temp_path;
if (!CreateNewTempDirectory(dir_prefix, &temp_path))
return false;
*new_temp_path = temp_path.ToWStringHack();
return true;
}
bool CreateTemporaryFileName(std::wstring* temp_file) {
FilePath temp_file_path;
if (!CreateTemporaryFileName(&temp_file_path))
return false;
*temp_file = temp_file_path.ToWStringHack();
return true;
}
bool Delete(const std::wstring& path) {
return Delete(FilePath::FromWStringHack(path));
}
bool DirectoryExists(const std::wstring& path) {
return DirectoryExists(FilePath::FromWStringHack(path));
}
bool EndsWithSeparator(std::wstring* path) {
return EndsWithSeparator(FilePath::FromWStringHack(*path));
}
bool EndsWithSeparator(const std::wstring& path) {
return EndsWithSeparator(FilePath::FromWStringHack(path));
}
bool GetCurrentDirectory(std::wstring* path_str) {
FilePath path;
if (!GetCurrentDirectory(&path))
return false;
*path_str = path.ToWStringHack();
return true;
}
std::wstring GetFileExtensionFromPath(const std::wstring& path) {
FilePath::StringType extension =
GetFileExtensionFromPath(FilePath::FromWStringHack(path));
#if defined(OS_WIN)
return extension;
#elif defined(OS_POSIX)
return UTF8ToWide(extension);
#endif
}
bool GetFileInfo(const std::wstring& file_path, FileInfo* results) {
return GetFileInfo(FilePath::FromWStringHack(file_path), results);
}
std::wstring GetFilenameFromPath(const std::wstring& path) {
if (path.empty() || EndsWithSeparator(path))
return std::wstring();
return FilePath::FromWStringHack(path).BaseName().ToWStringHack();
}
bool GetFileSize(const std::wstring& file_path, int64_t* file_size) {
return GetFileSize(FilePath::FromWStringHack(file_path), file_size);
}
bool GetTempDir(std::wstring* path_str) {
FilePath path;
if (!GetTempDir(&path))
return false;
*path_str = path.ToWStringHack();
return true;
}
FILE* OpenFile(const std::wstring& filename, const char* mode) {
return OpenFile(FilePath::FromWStringHack(filename), mode);
}
bool PathExists(const std::wstring& path) {
return PathExists(FilePath::FromWStringHack(path));
}
bool PathIsWritable(const std::wstring& path) {
return PathIsWritable(FilePath::FromWStringHack(path));
}
int ReadFile(const std::wstring& filename, char* data, int size) {
return ReadFile(FilePath::FromWStringHack(filename), data, size);
}
bool SetCurrentDirectory(const std::wstring& directory) {
return SetCurrentDirectory(FilePath::FromWStringHack(directory));
}
void UpOneDirectory(std::wstring* dir) {
FilePath path = FilePath::FromWStringHack(*dir);
FilePath directory = path.DirName();
// If there is no separator, we will get back kCurrentDirectory.
// In this case don't change |dir|.
if (directory.value() != FilePath::kCurrentDirectory)
*dir = directory.ToWStringHack();
}
int WriteFile(const std::wstring& filename, const char* data, int size) {
return WriteFile(FilePath::FromWStringHack(filename), data, size);
}
} // namespace

View file

@ -0,0 +1,229 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contains utility functions for dealing with the local
// filesystem.
#ifndef BASE_FILE_UTIL_H_
#define BASE_FILE_UTIL_H_
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#elif defined(ANDROID)
#include <sys/stat.h>
#elif defined(OS_POSIX)
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include <stdio.h>
#include <stack>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/file_path.h"
namespace file_util {
//-----------------------------------------------------------------------------
// Functions that operate purely on a path string w/o touching the filesystem:
// Returns true if the given path ends with a path separator character.
bool EndsWithSeparator(const FilePath& path);
// These two versions are both deprecated. TODO(estade): remove them.
bool EndsWithSeparator(std::wstring* path);
bool EndsWithSeparator(const std::wstring& path);
// Modifies a string by trimming all trailing separators from the end.
// Deprecated. FilePath does this automatically, and if it's constructed from a
// path with a trailing separator, StripTrailingSeparators() may be used.
void TrimTrailingSeparator(std::wstring* dir);
// Strips the topmost directory from the end of 'dir'. Assumes 'dir' does not
// refer to a file.
// If 'dir' is a root directory, return without change.
// Deprecated. Use FilePath::DirName instead.
void UpOneDirectory(std::wstring* dir);
// Returns the filename portion of 'path', without any leading \'s or /'s.
// Deprecated. Use FilePath::BaseName instead.
std::wstring GetFilenameFromPath(const std::wstring& path);
// Deprecated compatibility function. Use FilePath::Extension.
FilePath::StringType GetFileExtensionFromPath(const FilePath& path);
// Deprecated temporary compatibility function.
std::wstring GetFileExtensionFromPath(const std::wstring& path);
// Appends new_ending to path, adding a separator between the two if necessary.
void AppendToPath(std::wstring* path, const std::wstring& new_ending);
// Convert provided relative path into an absolute path. Returns false on
// error. On POSIX, this function fails if the path does not exist.
bool AbsolutePath(FilePath* path);
// Deprecated temporary compatibility function.
bool AbsolutePath(std::wstring* path);
// Deprecated compatibility function. Use FilePath::InsertBeforeExtension.
void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix);
// Deprecated compatibility function. Use FilePath::ReplaceExtension.
void ReplaceExtension(FilePath* file_name,
const FilePath::StringType& extension);
#if defined(OS_WIN)
// Deprecated temporary compatibility functions.
void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix);
void ReplaceExtension(std::wstring* file_name, const std::wstring& extension);
#endif
//-----------------------------------------------------------------------------
// Functions that involve filesystem access or modification:
// Deletes the given path, whether it's a file or a directory.
// If it's a directory, it's perfectly happy to delete all of the
// directory's contents.
// Returns true if successful, false otherwise.
bool Delete(const FilePath& path);
// Deprecated temporary compatibility function.
bool Delete(const std::wstring& path);
// Copies a single file. Use CopyDirectory to copy directories.
bool CopyFile(const FilePath& from_path, const FilePath& to_path);
// Deprecated temporary compatibility function.
bool CopyFile(const std::wstring& from_path, const std::wstring& to_path);
// Returns true if the given path exists on the local filesystem,
// false otherwise.
bool PathExists(const FilePath& path);
// Deprecated temporary compatibility function.
bool PathExists(const std::wstring& path);
// Returns true if the given path is writable by the user, false otherwise.
bool PathIsWritable(const FilePath& path);
// Deprecated temporary compatibility function.
bool PathIsWritable(const std::wstring& path);
// Returns true if the given path exists and is a directory, false otherwise.
bool DirectoryExists(const FilePath& path);
// Deprecated temporary compatibility function.
bool DirectoryExists(const std::wstring& path);
#if defined(OS_POSIX)
// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
// in |buffer|. This function is protected against EINTR and partial reads.
// Returns true iff |bytes| bytes have been successfuly read from |fd|.
bool ReadFromFD(int fd, char* buffer, size_t bytes);
#endif // defined(OS_POSIX)
// Get the temporary directory provided by the system.
bool GetTempDir(FilePath* path);
// Deprecated temporary compatibility function.
bool GetTempDir(std::wstring* path);
// Get a temporary directory for shared memory files.
// Only useful on POSIX; redirects to GetTempDir() on Windows.
bool GetShmemTempDir(FilePath* path);
// Creates a temporary file. The full path is placed in |path|, and the
// function returns true if was successful in creating the file. The file will
// be empty and all handles closed after this function returns.
// TODO(erikkay): rename this function and track down all of the callers.
// (Clarification of erik's comment: the intent is to rename the BlahFileName()
// calls into BlahFile(), since they create temp files (not temp filenames).)
bool CreateTemporaryFileName(FilePath* path);
// Deprecated temporary compatibility function.
bool CreateTemporaryFileName(std::wstring* temp_file);
// Create and open a temporary file. File is opened for read/write.
// The full path is placed in |path|, and the function returns true if
// was successful in creating and opening the file.
FILE* CreateAndOpenTemporaryFile(FilePath* path);
// Like above but for shmem files. Only useful for POSIX.
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path);
// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|.
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path);
// Same as CreateTemporaryFileName but the file is created in |dir|.
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
std::wstring* temp_file);
// Create a new directory under TempPath. If prefix is provided, the new
// directory name is in the format of prefixyyyy.
// NOTE: prefix is ignored in the POSIX implementation.
// TODO(erikkay): is this OK?
// If success, return true and output the full path of the directory created.
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
FilePath* new_temp_path);
// Deprecated temporary compatibility function.
bool CreateNewTempDirectory(const std::wstring& prefix,
std::wstring* new_temp_path);
// Creates a directory, as well as creating any parent directories, if they
// don't exist. Returns 'true' on successful creation, or if the directory
// already exists.
bool CreateDirectory(const FilePath& full_path);
// Deprecated temporary compatibility function.
bool CreateDirectory(const std::wstring& full_path);
// Returns the file size. Returns true on success.
bool GetFileSize(const FilePath& file_path, int64_t* file_size);
// Deprecated temporary compatibility function.
bool GetFileSize(const std::wstring& file_path, int64_t* file_size);
// Used to hold information about a given file path. See GetFileInfo below.
struct FileInfo {
// The size of the file in bytes. Undefined when is_directory is true.
int64_t size;
// True if the file corresponds to a directory.
bool is_directory;
// Add additional fields here as needed.
};
// Returns information about the given file path.
bool GetFileInfo(const FilePath& file_path, FileInfo* info);
// Deprecated temporary compatibility function.
bool GetFileInfo(const std::wstring& file_path, FileInfo* info);
// Wrapper for fopen-like calls. Returns non-NULL FILE* on success.
FILE* OpenFile(const FilePath& filename, const char* mode);
// Deprecated temporary compatibility functions.
FILE* OpenFile(const std::string& filename, const char* mode);
FILE* OpenFile(const std::wstring& filename, const char* mode);
// Closes file opened by OpenFile. Returns true on success.
bool CloseFile(FILE* file);
// Reads the given number of bytes from the file into the buffer. Returns
// the number of read bytes, or -1 on error.
int ReadFile(const FilePath& filename, char* data, int size);
// Deprecated temporary compatibility function.
int ReadFile(const std::wstring& filename, char* data, int size);
// Writes the given buffer into the file, overwriting any data that was
// previously there. Returns the number of bytes written, or -1 on error.
int WriteFile(const FilePath& filename, const char* data, int size);
// Deprecated temporary compatibility function.
int WriteFile(const std::wstring& filename, const char* data, int size);
// Gets the current working directory for the process.
bool GetCurrentDirectory(FilePath* path);
// Deprecated temporary compatibility function.
bool GetCurrentDirectory(std::wstring* path);
// Sets the current working directory for the process.
bool SetCurrentDirectory(const FilePath& path);
// Deprecated temporary compatibility function.
bool SetCurrentDirectory(const std::wstring& current_directory);
} // namespace file_util
#endif // BASE_FILE_UTIL_H_

View file

@ -0,0 +1,35 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/file_util.h"
#import <Cocoa/Cocoa.h>
#include <copyfile.h>
#include "base/file_path.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/scoped_nsautorelease_pool.h"
namespace file_util {
bool GetTempDir(FilePath* path) {
base::ScopedNSAutoreleasePool autorelease_pool;
NSString* tmp = NSTemporaryDirectory();
if (tmp == nil)
return false;
*path = FilePath([tmp fileSystemRepresentation]);
return true;
}
bool GetShmemTempDir(FilePath* path) {
return GetTempDir(path);
}
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
return (copyfile(from_path.value().c_str(),
to_path.value().c_str(), NULL, COPYFILE_ALL) == 0);
}
} // namespace

View file

@ -0,0 +1,337 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/file_util.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <libgen.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#define _DARWIN_USE_64_BIT_INODE // Use 64-bit inode data structures
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <fstream>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/eintr_wrapper.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "base/time.h"
namespace file_util {
#if defined(GOOGLE_CHROME_BUILD)
static const char* kTempFileName = "com.google.chrome.XXXXXX";
#else
static const char* kTempFileName = "org.chromium.XXXXXX";
#endif
bool AbsolutePath(FilePath* path) {
char full_path[PATH_MAX];
if (realpath(path->value().c_str(), full_path) == NULL)
return false;
*path = FilePath(full_path);
return true;
}
// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
// which works both with and without the recursive flag. I'm not sure we need
// that functionality. If not, remove from file_util_win.cc, otherwise add it
// here.
bool Delete(const FilePath& path) {
const char* path_str = path.value().c_str();
struct stat file_info;
int test = stat(path_str, &file_info);
if (test != 0) {
// The Windows version defines this condition as success.
bool ret = (errno == ENOENT || errno == ENOTDIR);
return ret;
}
if (!S_ISDIR(file_info.st_mode))
return (unlink(path_str) == 0);
return (rmdir(path_str) == 0);
}
bool PathExists(const FilePath& path) {
struct stat file_info;
return (stat(path.value().c_str(), &file_info) == 0);
}
bool PathIsWritable(const FilePath& path) {
FilePath test_path(path);
struct stat file_info;
if (stat(test_path.value().c_str(), &file_info) != 0) {
// If the path doesn't exist, test the parent dir.
test_path = test_path.DirName();
// If the parent dir doesn't exist, then return false (the path is not
// directly writable).
if (stat(test_path.value().c_str(), &file_info) != 0)
return false;
}
if (S_IWOTH & file_info.st_mode)
return true;
if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode))
return true;
if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode))
return true;
return false;
}
bool DirectoryExists(const FilePath& path) {
struct stat file_info;
if (stat(path.value().c_str(), &file_info) == 0)
return S_ISDIR(file_info.st_mode);
return false;
}
bool ReadFromFD(int fd, char* buffer, size_t bytes) {
size_t total_read = 0;
while (total_read < bytes) {
ssize_t bytes_read =
HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read));
if (bytes_read <= 0)
break;
total_read += bytes_read;
}
return total_read == bytes;
}
// Creates and opens a temporary file in |directory|, returning the
// file descriptor. |path| is set to the temporary file path.
// Note TODO(erikkay) comment in header for BlahFileName() calls; the
// intent is to rename these files BlahFile() (since they create
// files, not filenames). This function does NOT unlink() the file.
int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
*path = directory.Append(kTempFileName);
const std::string& tmpdir_string = path->value();
// this should be OK since mkstemp just replaces characters in place
char* buffer = const_cast<char*>(tmpdir_string.c_str());
return mkstemp(buffer);
}
bool CreateTemporaryFileName(FilePath* path) {
FilePath directory;
if (!GetTempDir(&directory))
return false;
int fd = CreateAndOpenFdForTemporaryFile(directory, path);
if (fd < 0)
return false;
close(fd);
return true;
}
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
FilePath directory;
if (!GetShmemTempDir(&directory))
return NULL;
return CreateAndOpenTemporaryFileInDir(directory, path);
}
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
int fd = CreateAndOpenFdForTemporaryFile(dir, path);
if (fd < 0)
return NULL;
return fdopen(fd, "a+");
}
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
std::wstring* temp_file) {
// Not implemented yet.
NOTREACHED();
return false;
}
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
FilePath* new_temp_path) {
FilePath tmpdir;
if (!GetTempDir(&tmpdir))
return false;
tmpdir = tmpdir.Append(kTempFileName);
std::string tmpdir_string = tmpdir.value();
#ifdef ANDROID
char* dtemp = NULL;
#else
// this should be OK since mkdtemp just replaces characters in place
char* buffer = const_cast<char*>(tmpdir_string.c_str());
char* dtemp = mkdtemp(buffer);
#endif
if (!dtemp)
return false;
*new_temp_path = FilePath(dtemp);
return true;
}
bool CreateDirectory(const FilePath& full_path) {
std::vector<FilePath> subpaths;
// Collect a list of all parent directories.
FilePath last_path = full_path;
subpaths.push_back(full_path);
for (FilePath path = full_path.DirName();
path.value() != last_path.value(); path = path.DirName()) {
subpaths.push_back(path);
last_path = path;
}
// Iterate through the parents and create the missing ones.
for (std::vector<FilePath>::reverse_iterator i = subpaths.rbegin();
i != subpaths.rend(); ++i) {
if (!DirectoryExists(*i)) {
if (mkdir(i->value().c_str(), 0777) != 0)
return false;
}
}
return true;
}
bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
struct stat file_info;
if (stat(file_path.value().c_str(), &file_info) != 0)
return false;
results->is_directory = S_ISDIR(file_info.st_mode);
results->size = file_info.st_size;
return true;
}
FILE* OpenFile(const std::string& filename, const char* mode) {
return OpenFile(FilePath(filename), mode);
}
FILE* OpenFile(const FilePath& filename, const char* mode) {
return fopen(filename.value().c_str(), mode);
}
int ReadFile(const FilePath& filename, char* data, int size) {
int fd = open(filename.value().c_str(), O_RDONLY);
if (fd < 0)
return -1;
int ret_value = HANDLE_EINTR(read(fd, data, size));
HANDLE_EINTR(close(fd));
return ret_value;
}
int WriteFile(const FilePath& filename, const char* data, int size) {
int fd = creat(filename.value().c_str(), 0666);
if (fd < 0)
return -1;
// Allow for partial writes
ssize_t bytes_written_total = 0;
do {
ssize_t bytes_written_partial =
HANDLE_EINTR(write(fd, data + bytes_written_total,
size - bytes_written_total));
if (bytes_written_partial < 0) {
HANDLE_EINTR(close(fd));
return -1;
}
bytes_written_total += bytes_written_partial;
} while (bytes_written_total < size);
HANDLE_EINTR(close(fd));
return bytes_written_total;
}
// Gets the current working directory for the process.
bool GetCurrentDirectory(FilePath* dir) {
char system_buffer[PATH_MAX] = "";
if (!getcwd(system_buffer, sizeof(system_buffer))) {
NOTREACHED();
return false;
}
*dir = FilePath(system_buffer);
return true;
}
// Sets the current working directory for the process.
bool SetCurrentDirectory(const FilePath& path) {
int ret = chdir(path.value().c_str());
return !ret;
}
#if !defined(OS_MACOSX)
bool GetTempDir(FilePath* path) {
const char* tmp = getenv("TMPDIR");
if (tmp)
*path = FilePath(tmp);
else
*path = FilePath("/tmp");
return true;
}
bool GetShmemTempDir(FilePath* path) {
#if defined(OS_LINUX) && !defined(ANDROID)
*path = FilePath("/dev/shm");
return true;
#else
return GetTempDir(path);
#endif
}
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
int infile = open(from_path.value().c_str(), O_RDONLY);
if (infile < 0)
return false;
int outfile = creat(to_path.value().c_str(), 0666);
if (outfile < 0) {
close(infile);
return false;
}
const size_t kBufferSize = 32768;
std::vector<char> buffer(kBufferSize);
bool result = true;
while (result) {
ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size()));
if (bytes_read < 0) {
result = false;
break;
}
if (bytes_read == 0)
break;
// Allow for partial writes
ssize_t bytes_written_per_read = 0;
do {
ssize_t bytes_written_partial = HANDLE_EINTR(write(
outfile,
&buffer[bytes_written_per_read],
bytes_read - bytes_written_per_read));
if (bytes_written_partial < 0) {
result = false;
break;
}
bytes_written_per_read += bytes_written_partial;
} while (bytes_written_per_read < bytes_read);
}
if (HANDLE_EINTR(close(infile)) < 0)
result = false;
if (HANDLE_EINTR(close(outfile)) < 0)
result = false;
return result;
}
#endif // !defined(OS_MACOSX)
} // namespace file_util

View file

@ -0,0 +1,356 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/file_util.h"
#include <windows.h>
#include <shellapi.h>
#include <shlobj.h>
#include <time.h>
#include <string>
#include "base/file_path.h"
#include "base/logging.h"
#include "base/scoped_handle.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/win_util.h"
namespace file_util {
bool AbsolutePath(FilePath* path) {
wchar_t file_path_buf[MAX_PATH];
if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH))
return false;
*path = FilePath(file_path_buf);
return true;
}
bool Delete(const FilePath& path) {
if (path.value().length() >= MAX_PATH)
return false;
// Use DeleteFile; it should be faster. DeleteFile
// fails if passed a directory though, which is why we fall through on
// failure to the SHFileOperation.
if (DeleteFile(path.value().c_str()) != 0)
return true;
// SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
// so we have to use wcscpy because wcscpy_s writes non-NULLs
// into the rest of the buffer.
wchar_t double_terminated_path[MAX_PATH + 1] = {0};
#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
wcscpy(double_terminated_path, path.value().c_str());
SHFILEOPSTRUCT file_operation = {0};
file_operation.wFunc = FO_DELETE;
file_operation.pFrom = double_terminated_path;
file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
int err = SHFileOperation(&file_operation);
// Some versions of Windows return ERROR_FILE_NOT_FOUND when
// deleting an empty directory.
return (err == 0 || err == ERROR_FILE_NOT_FOUND);
}
bool CopyFile(const FilePath& from_path, const FilePath& to_path) {
// NOTE: I suspect we could support longer paths, but that would involve
// analyzing all our usage of files.
if (from_path.value().length() >= MAX_PATH ||
to_path.value().length() >= MAX_PATH) {
return false;
}
return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
false) != 0);
}
bool ShellCopy(const FilePath& from_path, const FilePath& to_path,
bool recursive) {
// NOTE: I suspect we could support longer paths, but that would involve
// analyzing all our usage of files.
if (from_path.value().length() >= MAX_PATH ||
to_path.value().length() >= MAX_PATH) {
return false;
}
// SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
// so we have to use wcscpy because wcscpy_s writes non-NULLs
// into the rest of the buffer.
wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
wcscpy(double_terminated_path_from, from_path.value().c_str());
#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
wcscpy(double_terminated_path_to, to_path.value().c_str());
SHFILEOPSTRUCT file_operation = {0};
file_operation.wFunc = FO_COPY;
file_operation.pFrom = double_terminated_path_from;
file_operation.pTo = double_terminated_path_to;
file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION |
FOF_NOCONFIRMMKDIR;
if (!recursive)
file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
return (SHFileOperation(&file_operation) == 0);
}
bool PathExists(const FilePath& path) {
return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES);
}
bool PathIsWritable(const FilePath& path) {
HANDLE dir =
CreateFile(path.value().c_str(), FILE_ADD_FILE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (dir == INVALID_HANDLE_VALUE)
return false;
CloseHandle(dir);
return true;
}
bool DirectoryExists(const FilePath& path) {
DWORD fileattr = GetFileAttributes(path.value().c_str());
if (fileattr != INVALID_FILE_ATTRIBUTES)
return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
return false;
}
bool GetTempDir(FilePath* path) {
wchar_t temp_path[MAX_PATH + 1];
DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
if (path_len >= MAX_PATH || path_len <= 0)
return false;
// TODO(evanm): the old behavior of this function was to always strip the
// trailing slash. We duplicate this here, but it shouldn't be necessary
// when everyone is using the appropriate FilePath APIs.
std::wstring path_str(temp_path);
TrimTrailingSeparator(&path_str);
*path = FilePath(path_str);
return true;
}
bool GetShmemTempDir(FilePath* path) {
return GetTempDir(path);
}
bool CreateTemporaryFileName(FilePath* path) {
std::wstring temp_path, temp_file;
if (!GetTempDir(&temp_path))
return false;
if (CreateTemporaryFileNameInDir(temp_path, &temp_file)) {
*path = FilePath(temp_file);
return true;
}
return false;
}
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) {
return CreateAndOpenTemporaryFile(path);
}
// On POSIX we have semantics to create and open a temporary file
// atomically.
// TODO(jrg): is there equivalent call to use on Windows instead of
// going 2-step?
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
std::wstring wstring_path;
if (!CreateTemporaryFileNameInDir(dir.value(), &wstring_path)) {
return NULL;
}
*path = FilePath(wstring_path);
// Open file in binary mode, to avoid problems with fwrite. On Windows
// it replaces \n's with \r\n's, which may surprise you.
// Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx
return OpenFile(*path, "wb+");
}
bool CreateTemporaryFileNameInDir(const std::wstring& dir,
std::wstring* temp_file) {
wchar_t temp_name[MAX_PATH + 1];
if (!GetTempFileName(dir.c_str(), L"", 0, temp_name))
return false; // fail!
DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
if (path_len > MAX_PATH + 1 || path_len == 0)
return false; // fail!
temp_file->assign(temp_name, path_len);
return true;
}
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
FilePath* new_temp_path) {
FilePath system_temp_dir;
if (!GetTempDir(&system_temp_dir))
return false;
FilePath path_to_create;
srand(static_cast<uint32_t>(time(NULL)));
int count = 0;
while (count < 50) {
// Try create a new temporary directory with random generated name. If
// the one exists, keep trying another path name until we reach some limit.
path_to_create = system_temp_dir;
std::wstring new_dir_name;
new_dir_name.assign(prefix);
new_dir_name.append(IntToWString(rand() % kint16max));
path_to_create = path_to_create.Append(new_dir_name);
if (::CreateDirectory(path_to_create.value().c_str(), NULL))
break;
count++;
}
if (count == 50) {
return false;
}
*new_temp_path = path_to_create;
return true;
}
bool CreateDirectory(const FilePath& full_path) {
if (DirectoryExists(full_path))
return true;
int err = SHCreateDirectoryEx(NULL, full_path.value().c_str(), NULL);
return err == ERROR_SUCCESS;
}
bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesEx(file_path.ToWStringHack().c_str(),
GetFileExInfoStandard, &attr)) {
return false;
}
ULARGE_INTEGER size;
size.HighPart = attr.nFileSizeHigh;
size.LowPart = attr.nFileSizeLow;
results->size = size.QuadPart;
results->is_directory =
(attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
return true;
}
FILE* OpenFile(const FilePath& filename, const char* mode) {
std::wstring w_mode = ASCIIToWide(std::string(mode));
FILE* file;
if (_wfopen_s(&file, filename.value().c_str(), w_mode.c_str()) != 0) {
return NULL;
}
return file;
}
FILE* OpenFile(const std::string& filename, const char* mode) {
FILE* file;
if (fopen_s(&file, filename.c_str(), mode) != 0) {
return NULL;
}
return file;
}
int ReadFile(const FilePath& filename, char* data, int size) {
ScopedHandle file(CreateFile(filename.value().c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN,
NULL));
if (file == INVALID_HANDLE_VALUE)
return -1;
int ret_value;
DWORD read;
if (::ReadFile(file, data, size, &read, NULL) && read == size) {
ret_value = static_cast<int>(read);
} else {
ret_value = -1;
}
return ret_value;
}
int WriteFile(const FilePath& filename, const char* data, int size) {
ScopedHandle file(CreateFile(filename.value().c_str(),
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
0,
NULL));
if (file == INVALID_HANDLE_VALUE) {
CHROMIUM_LOG(WARNING) << "CreateFile failed for path " << filename.value() <<
" error code=" << GetLastError() <<
" error text=" << win_util::FormatLastWin32Error();
return -1;
}
DWORD written;
BOOL result = ::WriteFile(file, data, size, &written, NULL);
if (result && written == size)
return static_cast<int>(written);
if (!result) {
// WriteFile failed.
CHROMIUM_LOG(WARNING) << "writing file " << filename.value() <<
" failed, error code=" << GetLastError() <<
" description=" << win_util::FormatLastWin32Error();
} else {
// Didn't write all the bytes.
CHROMIUM_LOG(WARNING) << "wrote" << written << " bytes to " <<
filename.value() << " expected " << size;
}
return -1;
}
// Gets the current working directory for the process.
bool GetCurrentDirectory(FilePath* dir) {
wchar_t system_buffer[MAX_PATH];
system_buffer[0] = 0;
DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer);
if (len == 0 || len > MAX_PATH)
return false;
// TODO(evanm): the old behavior of this function was to always strip the
// trailing slash. We duplicate this here, but it shouldn't be necessary
// when everyone is using the appropriate FilePath APIs.
std::wstring dir_str(system_buffer);
file_util::TrimTrailingSeparator(&dir_str);
*dir = FilePath(dir_str);
return true;
}
// Sets the current working directory for the process.
bool SetCurrentDirectory(const FilePath& directory) {
BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
return ret != 0;
}
// Deprecated functions ----------------------------------------------------
void InsertBeforeExtension(std::wstring* path_str,
const std::wstring& suffix) {
FilePath path(*path_str);
InsertBeforeExtension(&path, suffix);
path_str->assign(path.value());
}
void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) {
FilePath path(*file_name);
ReplaceExtension(&path, extension);
file_name->assign(path.value());
}
} // namespace file_util

View file

@ -0,0 +1,136 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
//
// Deal with the differences between Microsoft and GNU implemenations
// of hash_map. Allows all platforms to use |base::hash_map| and
// |base::hash_set|.
// eg:
// base::hash_map<int> my_map;
// base::hash_set<int> my_set;
//
#ifndef BASE_HASH_TABLES_H_
#define BASE_HASH_TABLES_H_
#include "build/build_config.h"
#include "base/string16.h"
#if defined(COMPILER_MSVC) || (defined(ANDROID) && defined(_STLP_STD_NAME))
#ifdef COMPILER_MSVC
#pragma push_macro("_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS")
#define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
#endif
// Suppress -Wshadow warnings from stlport headers.
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wshadow"
# if MOZ_GCC_VERSION_AT_LEAST(4, 9, 0)
# pragma GCC diagnostic ignored "-Wshadow-local"
# endif
#endif
#include <hash_map>
#include <hash_set>
#ifdef __GNUC__
# if MOZ_GCC_VERSION_AT_LEAST(4, 9, 0)
# pragma GCC diagnostic pop // -Wshadow-local
# endif
# pragma GCC diagnostic pop // -Wshadow
#endif
#ifdef COMPILER_MSVC
#pragma pop_macro("_SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS")
#endif
namespace base {
#ifdef ANDROID
using _STLP_STD_NAME::hash_map;
using _STLP_STD_NAME::hash_set;
#else
using stdext::hash_map;
using stdext::hash_set;
#endif
}
#elif defined(COMPILER_GCC)
// This is a hack to disable the gcc 4.4 warning about hash_map and hash_set
// being deprecated. We can get rid of this when we upgrade to VS2008 and we
// can use <tr1/unordered_map> and <tr1/unordered_set>.
#ifdef __DEPRECATED
#define CHROME_OLD__DEPRECATED __DEPRECATED
#undef __DEPRECATED
#endif
#include <ext/hash_map>
#include <ext/hash_set>
#include <string>
#ifdef CHROME_OLD__DEPRECATED
#define __DEPRECATED CHROME_OLD__DEPRECATED
#undef CHROME_OLD__DEPRECATED
#endif
namespace base {
using __gnu_cxx::hash_map;
using __gnu_cxx::hash_set;
} // namespace base
namespace __gnu_cxx {
// The GNU C++ library provides identiy hash functions for many integral types,
// but not for |long long|. This hash function will truncate if |size_t| is
// narrower than |long long|. This is probably good enough for what we will
// use it for.
#define DEFINE_TRIVIAL_HASH(integral_type) \
template<> \
struct hash<integral_type> { \
std::size_t operator()(integral_type value) const { \
return static_cast<std::size_t>(value); \
} \
}
DEFINE_TRIVIAL_HASH(long long);
DEFINE_TRIVIAL_HASH(unsigned long long);
#undef DEFINE_TRIVIAL_HASH
// Implement string hash functions so that strings of various flavors can
// be used as keys in STL maps and sets. The hash algorithm comes from the
// GNU C++ library, in <tr1/functional>. It is duplicated here because GCC
// versions prior to 4.3.2 are unable to compile <tr1/functional> when RTTI
// is disabled, as it is in our build.
#define DEFINE_STRING_HASH(string_type) \
template<> \
struct hash<string_type> { \
std::size_t operator()(const string_type& s) const { \
std::size_t result = 0; \
for (string_type::const_iterator i = s.begin(); i != s.end(); ++i) \
result = (result * 131) + *i; \
return result; \
} \
}
DEFINE_STRING_HASH(std::string);
DEFINE_STRING_HASH(std::wstring);
#if defined(WCHAR_T_IS_UTF32)
// If string16 and std::wstring are not the same type, provide a
// specialization for string16.
DEFINE_STRING_HASH(string16);
#endif // WCHAR_T_IS_UTF32
#undef DEFINE_STRING_HASH
} // namespace __gnu_cxx
#endif // COMPILER
#endif // BASE_HASH_TABLES_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,780 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Histogram is an object that aggregates statistics, and can summarize them in
// various forms, including ASCII graphical, HTML, and numerically (as a
// vector of numbers corresponding to each of the aggregating buckets).
// It supports calls to accumulate either time intervals (which are processed
// as integral number of milliseconds), or arbitrary integral units.
// The default layout of buckets is exponential. For example, buckets might
// contain (sequentially) the count of values in the following intervals:
// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity)
// That bucket allocation would actually result from construction of a histogram
// for values between 1 and 64, with 8 buckets, such as:
// Histogram count(L"some name", 1, 64, 8);
// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity)
// are not counted by the constructor in the user supplied "bucket_count"
// argument.
// The above example has an exponential ratio of 2 (doubling the bucket width
// in each consecutive bucket. The Histogram class automatically calculates
// the smallest ratio that it can use to construct the number of buckets
// selected in the constructor. An another example, if you had 50 buckets,
// and millisecond time values from 1 to 10000, then the ratio between
// consecutive bucket widths will be approximately somewhere around the 50th
// root of 10000. This approach provides very fine grain (narrow) buckets
// at the low end of the histogram scale, but allows the histogram to cover a
// gigantic range with the addition of very few buckets.
// Histograms use a pattern involving a function static variable, that is a
// pointer to a histogram. This static is explicitly initialized on any thread
// that detects a uninitialized (NULL) pointer. The potentially racy
// initialization is not a problem as it is always set to point to the same
// value (i.e., the FactoryGet always returns the same value). FactoryGet
// is also completely thread safe, which results in a completely thread safe,
// and relatively fast, set of counters. To avoid races at shutdown, the static
// pointer is NOT deleted, and we leak the histograms at process termination.
#ifndef BASE_METRICS_HISTOGRAM_H_
#define BASE_METRICS_HISTOGRAM_H_
#pragma once
#include "mozilla/Atomics.h"
#include "mozilla/MemoryReporting.h"
#include <map>
#include <string>
#include <vector>
#include "base/time.h"
#include "base/lock.h"
namespace base {
//------------------------------------------------------------------------------
// Provide easy general purpose histogram in a macro, just like stats counters.
// The first four macros use 50 buckets.
#define HISTOGRAM_TIMES(name, sample) HISTOGRAM_CUSTOM_TIMES( \
name, sample, base::TimeDelta::FromMilliseconds(1), \
base::TimeDelta::FromSeconds(10), 50)
#define HISTOGRAM_COUNTS(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 1000000, 50)
#define HISTOGRAM_COUNTS_100(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 100, 50)
#define HISTOGRAM_COUNTS_10000(name, sample) HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 10000, 50)
#define HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::Histogram::FactoryGet(name, min, max, bucket_count, \
base::Histogram::kNoFlags); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->Add(sample); \
} while (0)
#define HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
// For folks that need real specific times, use this to select a precise range
// of times you want plotted, and the number of buckets you want used.
#define HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
base::Histogram::kNoFlags); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->AddTime(sample); \
} while (0)
// DO NOT USE THIS. It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES.
#define HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
base::Histogram::kNoFlags); \
DCHECK_EQ(name, counter->histogram_name()); \
if ((sample) < (max)) counter->AddTime(sample); \
} while (0)
// Support histograming of an enumerated value. The samples should always be
// less than boundary_value.
#define HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::LinearHistogram::FactoryGet(name, 1, boundary_value, \
boundary_value + 1, base::Histogram::kNoFlags); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->Add(sample); \
} while (0)
#define HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::CustomHistogram::FactoryGet(name, custom_ranges, \
base::Histogram::kNoFlags); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->Add(sample); \
} while (0)
//------------------------------------------------------------------------------
// Define Debug vs non-debug flavors of macros.
#ifndef NDEBUG
#define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample)
#define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample)
#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) HISTOGRAM_PERCENTAGE(\
name, under_one_hundred)
#define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count)
#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \
HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count)
#define DHISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count)
#define DHISTOGRAM_ENUMERATION(name, sample, boundary_value) \
HISTOGRAM_ENUMERATION(name, sample, boundary_value)
#define DHISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges)
#else // NDEBUG
#define DHISTOGRAM_TIMES(name, sample) do {} while (0)
#define DHISTOGRAM_COUNTS(name, sample) do {} while (0)
#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) do {} while (0)
#define DHISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \
do {} while (0)
#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \
do {} while (0)
#define DHISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
do {} while (0)
#define DHISTOGRAM_ENUMERATION(name, sample, boundary_value) do {} while (0)
#define DHISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
do {} while (0)
#endif // NDEBUG
//------------------------------------------------------------------------------
// The following macros provide typical usage scenarios for callers that wish
// to record histogram data, and have the data submitted/uploaded via UMA.
// Not all systems support such UMA, but if they do, the following macros
// should work with the service.
#define UMA_HISTOGRAM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
name, sample, base::TimeDelta::FromMilliseconds(1), \
base::TimeDelta::FromSeconds(10), 50)
#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
name, sample, base::TimeDelta::FromMilliseconds(10), \
base::TimeDelta::FromMinutes(3), 50)
// Use this macro when times can routinely be much longer than 10 seconds.
#define UMA_HISTOGRAM_LONG_TIMES(name, sample) UMA_HISTOGRAM_CUSTOM_TIMES( \
name, sample, base::TimeDelta::FromMilliseconds(1), \
base::TimeDelta::FromHours(1), 50)
#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
base::Histogram::kUmaTargetedHistogramFlag); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->AddTime(sample); \
} while (0)
// DO NOT USE THIS. It is being phased out, in favor of HISTOGRAM_CUSTOM_TIMES.
#define UMA_HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::Histogram::FactoryTimeGet(name, min, max, bucket_count, \
base::Histogram::kUmaTargetedHistogramFlag); \
DCHECK_EQ(name, counter->histogram_name()); \
if ((sample) < (max)) counter->AddTime(sample); \
} while (0)
#define UMA_HISTOGRAM_COUNTS(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 1000000, 50)
#define UMA_HISTOGRAM_COUNTS_100(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 100, 50)
#define UMA_HISTOGRAM_COUNTS_10000(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 10000, 50)
#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::Histogram::FactoryGet(name, min, max, bucket_count, \
base::Histogram::kUmaTargetedHistogramFlag); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->Add(sample); \
} while (0)
#define UMA_HISTOGRAM_MEMORY_KB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1000, 500000, 50)
#define UMA_HISTOGRAM_MEMORY_MB(name, sample) UMA_HISTOGRAM_CUSTOM_COUNTS( \
name, sample, 1, 1000, 50)
#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
UMA_HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
#define UMA_HISTOGRAM_BOOLEAN(name, sample) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::BooleanHistogram::FactoryGet(name, \
base::Histogram::kUmaTargetedHistogramFlag); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->AddBoolean(sample); \
} while (0)
#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::LinearHistogram::FactoryGet(name, 1, boundary_value, \
boundary_value + 1, base::Histogram::kUmaTargetedHistogramFlag); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->Add(sample); \
} while (0)
#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) do { \
static base::Histogram* counter(NULL); \
if (!counter) \
counter = base::CustomHistogram::FactoryGet(name, custom_ranges, \
base::Histogram::kUmaTargetedHistogramFlag); \
DCHECK_EQ(name, counter->histogram_name()); \
counter->Add(sample); \
} while (0)
//------------------------------------------------------------------------------
class BooleanHistogram;
class CustomHistogram;
class Histogram;
class LinearHistogram;
class Histogram {
public:
typedef int Sample; // Used for samples (and ranges of samples).
typedef int Count; // Used to count samples in a bucket.
static const Sample kSampleType_MAX = INT_MAX;
// Initialize maximum number of buckets in histograms as 16,384.
static const size_t kBucketCount_MAX;
typedef std::vector<Count> Counts;
typedef std::vector<Sample> Ranges;
// These enums are used to facilitate deserialization of renderer histograms
// into the browser.
enum ClassType {
HISTOGRAM,
LINEAR_HISTOGRAM,
BOOLEAN_HISTOGRAM,
FLAG_HISTOGRAM,
COUNT_HISTOGRAM,
CUSTOM_HISTOGRAM,
NOT_VALID_IN_RENDERER
};
enum BucketLayout {
EXPONENTIAL,
LINEAR,
CUSTOM
};
enum Flags {
kNoFlags = 0,
kUmaTargetedHistogramFlag = 0x1, // Histogram should be UMA uploaded.
kHexRangePrintingFlag = 0x8000 // Fancy bucket-naming supported.
};
enum Inconsistencies {
NO_INCONSISTENCIES = 0x0,
RANGE_CHECKSUM_ERROR = 0x1,
BUCKET_ORDER_ERROR = 0x2,
COUNT_HIGH_ERROR = 0x4,
COUNT_LOW_ERROR = 0x8,
NEVER_EXCEEDED_VALUE = 0x10
};
struct DescriptionPair {
Sample sample;
const char* description; // Null means end of a list of pairs.
};
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
//----------------------------------------------------------------------------
// Statistic values, developed over the life of the histogram.
class SampleSet {
public:
explicit SampleSet();
~SampleSet();
// None of the methods in this class are thread-safe. Callers
// must deal with locking themselves.
// Adjust size of counts_ for use with given histogram.
void Resize(const Histogram& histogram);
// Accessor for histogram to make routine additions.
void Accumulate(Sample value, Count count, size_t index);
// Arithmetic manipulation of corresponding elements of the set.
void Add(const SampleSet& other);
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
Count counts(size_t i) const {
return counts_[i];
}
Count TotalCount() const;
int64_t sum() const {
return sum_;
}
int64_t redundant_count() const {
return redundant_count_;
}
size_t size() const {
return counts_.size();
}
protected:
// Actual histogram data is stored in buckets, showing the count of values
// that fit into each bucket.
Counts counts_;
// Save simple stats locally. Note that this MIGHT get done in base class
// without shared memory at some point.
int64_t sum_; // sum of samples.
// To help identify memory corruption, we reduntantly save the number of
// samples we've accumulated into all of our buckets. We can compare this
// count to the sum of the counts in all buckets, and detect problems. Note
// that due to races in histogram accumulation (if a histogram is indeed
// updated on several threads simultaneously), the tallies might mismatch,
// and also the snapshotting code may asynchronously get a mismatch (though
// generally either race based mismatch cause is VERY rare).
int64_t redundant_count_;
};
//----------------------------------------------------------------------------
// minimum should start from 1. 0 is invalid as a minimum. 0 is an implicit
// default underflow bucket.
static Histogram* FactoryGet(const std::string& name,
Sample minimum,
Sample maximum,
size_t bucket_count,
Flags flags);
static Histogram* FactoryTimeGet(const std::string& name,
base::TimeDelta minimum,
base::TimeDelta maximum,
size_t bucket_count,
Flags flags);
void Add(int value);
void Subtract(int value);
// TODO: Currently recording_enabled_ is not used by any Histogram class, but
// rather examined only by the telemetry code (via IsRecordingEnabled).
// Move handling to Histogram's Add() etc after simplifying Histogram.
void SetRecordingEnabled(bool aEnabled) { recording_enabled_ = aEnabled; };
bool IsRecordingEnabled() const { return recording_enabled_; };
// This method is an interface, used only by BooleanHistogram.
virtual void AddBoolean(bool value);
// Accept a TimeDelta to increment.
void AddTime(TimeDelta time) {
Add(static_cast<int>(time.InMilliseconds()));
}
virtual void AddSampleSet(const SampleSet& sample);
virtual void Clear();
// This method is an interface, used only by LinearHistogram.
virtual void SetRangeDescriptions(const DescriptionPair descriptions[]);
// The following methods provide graphical histogram displays.
void WriteHTMLGraph(std::string* output) const;
void WriteAscii(bool graph_it, const std::string& newline,
std::string* output) const;
// Support generic flagging of Histograms.
// 0x1 Currently used to mark this histogram to be recorded by UMA..
// 0x8000 means print ranges in hex.
void SetFlags(Flags flags) { flags_ = static_cast<Flags> (flags_ | flags); }
void ClearFlags(Flags flags) { flags_ = static_cast<Flags>(flags_ & ~flags); }
int flags() const { return flags_; }
// Check to see if bucket ranges, counts and tallies in the snapshot are
// consistent with the bucket ranges and checksums in our histogram. This can
// produce a false-alarm if a race occurred in the reading of the data during
// a SnapShot process, but should otherwise be false at all times (unless we
// have memory over-writes, or DRAM failures).
virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const;
//----------------------------------------------------------------------------
// Accessors for factory constuction, serialization and testing.
//----------------------------------------------------------------------------
virtual ClassType histogram_type() const;
const std::string& histogram_name() const { return histogram_name_; }
Sample declared_min() const { return declared_min_; }
Sample declared_max() const { return declared_max_; }
virtual Sample ranges(size_t i) const;
uint32_t range_checksum() const { return range_checksum_; }
virtual size_t bucket_count() const;
// Do a safe atomic snapshot of sample data. The caller is assumed to
// have exclusive access to the destination, |*sample|, and no locking
// of it is done here.
virtual void SnapshotSample(SampleSet* sample) const;
virtual bool HasConstructorArguments(Sample minimum, Sample maximum,
size_t bucket_count);
virtual bool HasConstructorTimeDeltaArguments(TimeDelta minimum,
TimeDelta maximum,
size_t bucket_count);
// Return true iff the range_checksum_ matches current ranges_ vector.
bool HasValidRangeChecksum() const;
protected:
Histogram(const std::string& name, Sample minimum,
Sample maximum, size_t bucket_count);
Histogram(const std::string& name, TimeDelta minimum,
TimeDelta maximum, size_t bucket_count);
virtual ~Histogram();
// Initialize ranges_ mapping.
void InitializeBucketRange();
// Method to override to skip the display of the i'th bucket if it's empty.
virtual bool PrintEmptyBucket(size_t index) const;
//----------------------------------------------------------------------------
// Methods to override to create histogram with different bucket widths.
//----------------------------------------------------------------------------
// Find bucket to increment for sample value.
virtual size_t BucketIndex(Sample value) const;
// Get normalized size, relative to the ranges_[i].
virtual double GetBucketSize(Count current, size_t i) const;
// Recalculate range_checksum_.
void ResetRangeChecksum();
// Return a string description of what goes in a given bucket.
// Most commonly this is the numeric value, but in derived classes it may
// be a name (or string description) given to the bucket.
virtual const std::string GetAsciiBucketRange(size_t it) const;
//----------------------------------------------------------------------------
// Methods to override to create thread safe histogram.
//----------------------------------------------------------------------------
// Update all our internal data, including histogram
virtual void Accumulate(Sample value, Count count, size_t index);
//----------------------------------------------------------------------------
// Accessors for derived classes.
//----------------------------------------------------------------------------
void SetBucketRange(size_t i, Sample value);
// Validate that ranges_ was created sensibly (top and bottom range
// values relate properly to the declared_min_ and declared_max_)..
bool ValidateBucketRanges() const;
virtual uint32_t CalculateRangeChecksum() const;
// Finally, provide the state that changes with the addition of each new
// sample.
SampleSet sample_;
private:
friend class StatisticsRecorder; // To allow it to delete duplicates.
// Post constructor initialization.
void Initialize();
// Checksum function for accumulating range values into a checksum.
static uint32_t Crc32(uint32_t sum, Sample range);
//----------------------------------------------------------------------------
// Helpers for emitting Ascii graphic. Each method appends data to output.
// Find out how large the (graphically) the largest bucket will appear to be.
double GetPeakBucketSize(const SampleSet& snapshot) const;
// Write a common header message describing this histogram.
void WriteAsciiHeader(const SampleSet& snapshot,
Count sample_count, std::string* output) const;
// Write information about previous, current, and next buckets.
// Information such as cumulative percentage, etc.
void WriteAsciiBucketContext(const int64_t past, const Count current,
const int64_t remaining, const size_t i,
std::string* output) const;
// Write textual description of the bucket contents (relative to histogram).
// Output is the count in the buckets, as well as the percentage.
void WriteAsciiBucketValue(Count current, double scaled_sum,
std::string* output) const;
// Produce actual graph (set of blank vs non blank char's) for a bucket.
void WriteAsciiBucketGraph(double current_size, double max_size,
std::string* output) const;
//----------------------------------------------------------------------------
// Table for generating Crc32 values.
static const uint32_t kCrcTable[256];
//----------------------------------------------------------------------------
// Invariant values set at/near construction time
// ASCII version of original name given to the constructor. All identically
// named instances will be coalesced cross-project.
const std::string histogram_name_;
Sample declared_min_; // Less than this goes into counts_[0]
Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1].
size_t bucket_count_; // Dimension of counts_[].
// Flag the histogram for recording by UMA via metric_services.h.
Flags flags_;
// For each index, show the least value that can be stored in the
// corresponding bucket. We also append one extra element in this array,
// containing kSampleType_MAX, to make calculations easy.
// The dimension of ranges_ is bucket_count + 1.
Ranges ranges_;
// For redundancy, we store a checksum of all the sample ranges when ranges
// are generated. If ever there is ever a difference, then the histogram must
// have been corrupted.
uint32_t range_checksum_;
// When false, new samples are completely ignored.
mozilla::Atomic<bool, mozilla::Relaxed> recording_enabled_;
DISALLOW_COPY_AND_ASSIGN(Histogram);
};
//------------------------------------------------------------------------------
// LinearHistogram is a more traditional histogram, with evenly spaced
// buckets.
class LinearHistogram : public Histogram {
public:
virtual ~LinearHistogram();
/* minimum should start from 1. 0 is as minimum is invalid. 0 is an implicit
default underflow bucket. */
static Histogram* FactoryGet(const std::string& name,
Sample minimum,
Sample maximum,
size_t bucket_count,
Flags flags);
static Histogram* FactoryTimeGet(const std::string& name,
TimeDelta minimum,
TimeDelta maximum,
size_t bucket_count,
Flags flags);
// Overridden from Histogram:
virtual ClassType histogram_type() const;
virtual void Accumulate(Sample value, Count count, size_t index);
// Store a list of number/text values for use in rendering the histogram.
// The last element in the array has a null in its "description" slot.
virtual void SetRangeDescriptions(const DescriptionPair descriptions[]);
protected:
LinearHistogram(const std::string& name, Sample minimum,
Sample maximum, size_t bucket_count);
LinearHistogram(const std::string& name, TimeDelta minimum,
TimeDelta maximum, size_t bucket_count);
// Initialize ranges_ mapping.
void InitializeBucketRange();
virtual double GetBucketSize(Count current, size_t i) const;
// If we have a description for a bucket, then return that. Otherwise
// let parent class provide a (numeric) description.
virtual const std::string GetAsciiBucketRange(size_t i) const;
// Skip printing of name for numeric range if we have a name (and if this is
// an empty bucket).
virtual bool PrintEmptyBucket(size_t index) const;
private:
// For some ranges, we store a printable description of a bucket range.
// If there is no desciption, then GetAsciiBucketRange() uses parent class
// to provide a description.
typedef std::map<Sample, std::string> BucketDescriptionMap;
BucketDescriptionMap bucket_description_;
DISALLOW_COPY_AND_ASSIGN(LinearHistogram);
};
//------------------------------------------------------------------------------
// BooleanHistogram is a histogram for booleans.
class BooleanHistogram : public LinearHistogram {
public:
static Histogram* FactoryGet(const std::string& name, Flags flags);
virtual ClassType histogram_type() const;
virtual void AddBoolean(bool value);
virtual void Accumulate(Sample value, Count count, size_t index);
protected:
explicit BooleanHistogram(const std::string& name);
DISALLOW_COPY_AND_ASSIGN(BooleanHistogram);
};
//------------------------------------------------------------------------------
// FlagHistogram is like boolean histogram, but only allows a single off/on value.
class FlagHistogram : public BooleanHistogram
{
public:
static Histogram *FactoryGet(const std::string &name, Flags flags);
virtual ClassType histogram_type() const;
virtual void Accumulate(Sample value, Count count, size_t index);
virtual void AddSampleSet(const SampleSet& sample);
virtual void Clear();
private:
explicit FlagHistogram(const std::string &name);
bool mSwitched;
DISALLOW_COPY_AND_ASSIGN(FlagHistogram);
};
// CountHistogram only allows a single monotic counter value.
class CountHistogram : public LinearHistogram
{
public:
static Histogram *FactoryGet(const std::string &name, Flags flags);
virtual ClassType histogram_type() const;
virtual void Accumulate(Sample value, Count count, size_t index);
virtual void AddSampleSet(const SampleSet& sample);
private:
explicit CountHistogram(const std::string &name);
DISALLOW_COPY_AND_ASSIGN(CountHistogram);
};
//------------------------------------------------------------------------------
// CustomHistogram is a histogram for a set of custom integers.
class CustomHistogram : public Histogram {
public:
static Histogram* FactoryGet(const std::string& name,
const std::vector<Sample>& custom_ranges,
Flags flags);
// Overridden from Histogram:
virtual ClassType histogram_type() const;
protected:
CustomHistogram(const std::string& name,
const std::vector<Sample>& custom_ranges);
// Initialize ranges_ mapping.
void InitializedCustomBucketRange(const std::vector<Sample>& custom_ranges);
virtual double GetBucketSize(Count current, size_t i) const;
DISALLOW_COPY_AND_ASSIGN(CustomHistogram);
};
//------------------------------------------------------------------------------
// StatisticsRecorder handles all histograms in the system. It provides a
// general place for histograms to register, and supports a global API for
// accessing (i.e., dumping, or graphing) the data in all the histograms.
class StatisticsRecorder {
public:
typedef std::vector<Histogram*> Histograms;
StatisticsRecorder();
~StatisticsRecorder();
// Find out if histograms can now be registered into our list.
static bool IsActive();
// Register, or add a new histogram to the collection of statistics. If an
// identically named histogram is already registered, then the argument
// |histogram| will deleted. The returned value is always the registered
// histogram (either the argument, or the pre-existing registered histogram).
static Histogram* RegisterOrDeleteDuplicate(Histogram* histogram);
// Methods for printing histograms. Only histograms which have query as
// a substring are written to output (an empty string will process all
// registered histograms).
static void WriteHTMLGraph(const std::string& query, std::string* output);
static void WriteGraph(const std::string& query, std::string* output);
// Method for extracting histograms which were marked for use by UMA.
static void GetHistograms(Histograms* output);
// Find a histogram by name. It matches the exact name. This method is thread
// safe. If a matching histogram is not found, then the |histogram| is
// not changed.
static bool FindHistogram(const std::string& query, Histogram** histogram);
static bool dump_on_exit() { return dump_on_exit_; }
static void set_dump_on_exit(bool enable) { dump_on_exit_ = enable; }
// GetSnapshot copies some of the pointers to registered histograms into the
// caller supplied vector (Histograms). Only histograms with names matching
// query are returned. The query must be a substring of histogram name for its
// pointer to be copied.
static void GetSnapshot(const std::string& query, Histograms* snapshot);
private:
// We keep all registered histograms in a map, from name to histogram.
typedef std::map<std::string, Histogram*> HistogramMap;
static HistogramMap* histograms_;
// lock protects access to the above map.
static Lock* lock_;
// Dump all known histograms to log.
static bool dump_on_exit_;
DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder);
};
} // namespace base
#endif // BASE_METRICS_HISTOGRAM_H_

View file

@ -0,0 +1,107 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_ID_MAP_H__
#define BASE_ID_MAP_H__
#include "base/basictypes.h"
#include "base/hash_tables.h"
#include "base/logging.h"
// This object maintains a list of IDs that can be quickly converted to
// pointers to objects. It is implemented as a hash table, optimized for
// relatively small data sets (in the common case, there will be exactly one
// item in the list).
//
// Items can be inserted into the container with arbitrary ID, but the caller
// must ensure they are unique. Inserting IDs and relying on automatically
// generated ones is not allowed because they can collide.
template<class T>
class IDMap {
private:
typedef base::hash_map<int32_t, T*> HashTable;
typedef typename HashTable::iterator iterator;
public:
// support const iterators over the items
// Note, use iterator->first to get the ID, iterator->second to get the T*
typedef typename HashTable::const_iterator const_iterator;
IDMap() : next_id_(1) {
}
IDMap(const IDMap& other) : next_id_(other.next_id_),
data_(other.data_) {
}
const_iterator begin() const {
return data_.begin();
}
const_iterator end() const {
return data_.end();
}
// Adds a view with an automatically generated unique ID. See AddWithID.
int32_t Add(T* data) {
int32_t this_id = next_id_;
DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
data_[this_id] = data;
next_id_++;
return this_id;
}
// Adds a new data member with the specified ID. The ID must not be in
// the list. The caller either must generate all unique IDs itself and use
// this function, or allow this object to generate IDs and call Add. These
// two methods may not be mixed, or duplicate IDs may be generated
void AddWithID(T* data, int32_t id) {
DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
data_[id] = data;
}
void Remove(int32_t id) {
iterator i = data_.find(id);
if (i == data_.end()) {
NOTREACHED() << "Attempting to remove an item not in the list";
return;
}
data_.erase(i);
}
bool IsEmpty() const {
return data_.empty();
}
void Clear() {
data_.clear();
}
bool HasData(const T* data) const {
// XXX would like to use <algorithm> here ...
for (const_iterator it = begin(); it != end(); ++it)
if (data == it->second)
return true;
return false;
}
T* Lookup(int32_t id) const {
const_iterator i = data_.find(id);
if (i == data_.end())
return NULL;
return i->second;
}
size_t size() const {
return data_.size();
}
protected:
// The next ID that we will return from Add()
int32_t next_id_;
HashTable data_;
};
#endif // BASE_ID_MAP_H__

View file

@ -0,0 +1,176 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// A "smart" pointer type with reference tracking. Every pointer to a
// particular object is kept on a circular linked list. When the last pointer
// to an object is destroyed or reassigned, the object is deleted.
//
// Used properly, this deletes the object when the last reference goes away.
// There are several caveats:
// - Like all reference counting schemes, cycles lead to leaks.
// - Each smart pointer is actually two pointers (8 bytes instead of 4).
// - Every time a pointer is released, the entire list of pointers to that
// object is traversed. This class is therefore NOT SUITABLE when there
// will often be more than two or three pointers to a particular object.
// - References are only tracked as long as linked_ptr<> objects are copied.
// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
// will happen (double deletion).
//
// A good use of this class is storing object references in STL containers.
// You can safely put linked_ptr<> in a vector<>.
// Other uses may not be as good.
//
// Note: If you use an incomplete type with linked_ptr<>, the class
// *containing* linked_ptr<> must have a constructor and destructor (even
// if they do nothing!).
//
// Thread Safety:
// A linked_ptr is NOT thread safe. Copying a linked_ptr object is
// effectively a read-write operation.
//
// Alternative: to linked_ptr is shared_ptr, which
// - is also two pointers in size (8 bytes for 32 bit addresses)
// - is thread safe for copying and deletion
// - supports weak_ptrs
#ifndef BASE_LINKED_PTR_H_
#define BASE_LINKED_PTR_H_
#include "base/logging.h" // for CHECK macros
// This is used internally by all instances of linked_ptr<>. It needs to be
// a non-template class because different types of linked_ptr<> can refer to
// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
// So, it needs to be possible for different types of linked_ptr to participate
// in the same circular linked list, so we need a single class type here.
//
// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
class linked_ptr_internal {
public:
// Create a new circle that includes only this instance.
void join_new() {
next_ = this;
}
// Join an existing circle.
void join(linked_ptr_internal const* ptr) {
next_ = ptr->next_;
ptr->next_ = this;
}
// Leave whatever circle we're part of. Returns true iff we were the
// last member of the circle. Once this is done, you can join() another.
bool depart() {
if (next_ == this) return true;
linked_ptr_internal const* p = next_;
while (p->next_ != this) p = p->next_;
p->next_ = next_;
return false;
}
private:
mutable linked_ptr_internal const* next_;
};
template <typename T>
class linked_ptr {
public:
typedef T element_type;
// Take over ownership of a raw pointer. This should happen as soon as
// possible after the object is created.
explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
~linked_ptr() { depart(); }
// Copy an existing linked_ptr<>, adding ourselves to the list of references.
template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
linked_ptr(linked_ptr const& ptr) { DCHECK_NE(&ptr, this); copy(&ptr); }
// Assignment releases the old value and acquires the new.
template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
depart();
copy(&ptr);
return *this;
}
linked_ptr& operator=(linked_ptr const& ptr) {
if (&ptr != this) {
depart();
copy(&ptr);
}
return *this;
}
// Smart pointer members.
void reset(T* ptr = NULL) { depart(); capture(ptr); }
T* get() const { return value_; }
T* operator->() const { return value_; }
T& operator*() const { return *value_; }
// Release ownership of the pointed object and returns it.
// Sole ownership by this linked_ptr object is required.
T* release() {
bool last = link_.depart();
CHECK(last);
T* v = value_;
value_ = NULL;
return v;
}
bool operator==(const T* p) const { return value_ == p; }
bool operator!=(const T* p) const { return value_ != p; }
template <typename U>
bool operator==(linked_ptr<U> const& ptr) const {
return value_ == ptr.get();
}
template <typename U>
bool operator!=(linked_ptr<U> const& ptr) const {
return value_ != ptr.get();
}
private:
template <typename U>
friend class linked_ptr;
T* value_;
linked_ptr_internal link_;
void depart() {
if (link_.depart()) delete value_;
}
void capture(T* ptr) {
value_ = ptr;
link_.join_new();
}
template <typename U> void copy(linked_ptr<U> const* ptr) {
value_ = ptr->get();
if (value_)
link_.join(&ptr->link_);
else
link_.join_new();
}
};
template<typename T> inline
bool operator==(T* ptr, const linked_ptr<T>& x) {
return ptr == x.get();
}
template<typename T> inline
bool operator!=(T* ptr, const linked_ptr<T>& x) {
return ptr != x.get();
}
// A function to convert T* into linked_ptr<T>
// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
template <typename T>
linked_ptr<T> make_linked_ptr(T* ptr) {
return linked_ptr<T>(ptr);
}
#endif // BASE_LINKED_PTR_H_

View file

@ -0,0 +1,9 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Lock class.
// Depricated file. See lock_impl_*.cc for platform specific versions.

View file

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_LOCK_H_
#define BASE_LOCK_H_
#include "base/lock_impl.h"
// A convenient wrapper for an OS specific critical section.
class Lock {
public:
Lock() : lock_() {}
~Lock() {}
void Acquire() { lock_.Lock(); }
void Release() { lock_.Unlock(); }
// If the lock is not held, take it and return true. If the lock is already
// held by another thread, immediately return false.
bool Try() { return lock_.Try(); }
// In debug builds this method checks that the lock has been acquired by the
// calling thread. If the lock has not been acquired, then the method
// will DCHECK(). In non-debug builds, the LockImpl's implementation of
// AssertAcquired() is an empty inline method.
void AssertAcquired() const { return lock_.AssertAcquired(); }
// Return the underlying lock implementation.
// TODO(awalker): refactor lock and condition variables so that this is
// unnecessary.
LockImpl* lock_impl() { return &lock_; }
private:
LockImpl lock_; // Platform specific underlying lock implementation.
DISALLOW_COPY_AND_ASSIGN(Lock);
};
// A helper class that acquires the given Lock while the AutoLock is in scope.
class AutoLock {
public:
explicit AutoLock(Lock& lock) : lock_(lock) {
lock_.Acquire();
}
~AutoLock() {
lock_.AssertAcquired();
lock_.Release();
}
private:
Lock& lock_;
DISALLOW_COPY_AND_ASSIGN(AutoLock);
};
// AutoUnlock is a helper that will Release() the |lock| argument in the
// constructor, and re-Acquire() it in the destructor.
class AutoUnlock {
public:
explicit AutoUnlock(Lock& lock) : lock_(lock) {
// We require our caller to have the lock.
lock_.AssertAcquired();
lock_.Release();
}
~AutoUnlock() {
lock_.Acquire();
}
private:
Lock& lock_;
DISALLOW_COPY_AND_ASSIGN(AutoUnlock);
};
#endif // BASE_LOCK_H_

View file

@ -0,0 +1,79 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_LOCK_IMPL_H_
#define BASE_LOCK_IMPL_H_
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#elif defined(OS_POSIX)
#include <pthread.h>
#endif
#include "base/basictypes.h"
#include "base/platform_thread.h"
// This class implements the underlying platform-specific spin-lock mechanism
// used for the Lock class. Most users should not use LockImpl directly, but
// should instead use Lock.
class LockImpl {
public:
#if defined(OS_WIN)
typedef CRITICAL_SECTION OSLockType;
#elif defined(OS_POSIX)
typedef pthread_mutex_t OSLockType;
#endif
LockImpl();
~LockImpl();
// If the lock is not held, take it and return true. If the lock is already
// held by something else, immediately return false.
bool Try();
// Take the lock, blocking until it is available if necessary.
void Lock();
// Release the lock. This must only be called by the lock's holder: after
// a successful call to Try, or a call to Lock.
void Unlock();
// Debug-only method that will DCHECK() if the lock is not acquired by the
// current thread. In non-debug builds, no check is performed.
// Because linux and mac condition variables modify the underlyning lock
// through the os_lock() method, runtime assertions can not be done on those
// builds.
#if defined(NDEBUG) || !defined(OS_WIN)
void AssertAcquired() const {}
#else
void AssertAcquired() const;
#endif
// Return the native underlying lock. Not supported for Windows builds.
// TODO(awalker): refactor lock and condition variables so that this is
// unnecessary.
#if !defined(OS_WIN)
OSLockType* os_lock() { return &os_lock_; }
#endif
private:
OSLockType os_lock_;
#if !defined(NDEBUG) && defined(OS_WIN)
// All private data is implicitly protected by lock_.
// Be VERY careful to only access members under that lock.
PlatformThreadId owning_thread_id_;
int32_t recursion_count_shadow_;
bool recursion_used_; // Allow debugging to continued after a DCHECK().
#endif // NDEBUG
DISALLOW_COPY_AND_ASSIGN(LockImpl);
};
#endif // BASE_LOCK_IMPL_H_

View file

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/lock_impl.h"
#include <errno.h>
#include "base/logging.h"
LockImpl::LockImpl() {
#ifndef NDEBUG
// In debug, setup attributes for lock error checking.
pthread_mutexattr_t mta;
int rv = pthread_mutexattr_init(&mta);
DCHECK_EQ(rv, 0);
rv = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK);
DCHECK_EQ(rv, 0);
rv = pthread_mutex_init(&os_lock_, &mta);
DCHECK_EQ(rv, 0);
rv = pthread_mutexattr_destroy(&mta);
DCHECK_EQ(rv, 0);
#else
// In release, go with the default lock attributes.
pthread_mutex_init(&os_lock_, NULL);
#endif
}
LockImpl::~LockImpl() {
int rv = pthread_mutex_destroy(&os_lock_);
DCHECK_EQ(rv, 0);
}
bool LockImpl::Try() {
int rv = pthread_mutex_trylock(&os_lock_);
DCHECK(rv == 0 || rv == EBUSY);
return rv == 0;
}
void LockImpl::Lock() {
int rv = pthread_mutex_lock(&os_lock_);
DCHECK_EQ(rv, 0);
}
void LockImpl::Unlock() {
int rv = pthread_mutex_unlock(&os_lock_);
DCHECK_EQ(rv, 0);
}

View file

@ -0,0 +1,75 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/lock_impl.h"
#include "base/logging.h"
// NOTE: Although windows critical sections support recursive locks, we do not
// allow this, and we will commonly fire a DCHECK() if a thread attempts to
// acquire the lock a second time (while already holding it).
LockImpl::LockImpl() {
#ifndef NDEBUG
recursion_count_shadow_ = 0;
recursion_used_ = false;
owning_thread_id_ = 0;
#endif // NDEBUG
// The second parameter is the spin count, for short-held locks it avoid the
// contending thread from going to sleep which helps performance greatly.
::InitializeCriticalSectionAndSpinCount(&os_lock_, 2000);
}
LockImpl::~LockImpl() {
::DeleteCriticalSection(&os_lock_);
}
bool LockImpl::Try() {
if (::TryEnterCriticalSection(&os_lock_) != FALSE) {
#ifndef NDEBUG
// ONLY access data after locking.
owning_thread_id_ = PlatformThread::CurrentId();
DCHECK_NE(owning_thread_id_, 0);
recursion_count_shadow_++;
if (2 == recursion_count_shadow_ && !recursion_used_) {
recursion_used_ = true;
DCHECK(false); // Catch accidental redundant lock acquisition.
}
#endif
return true;
}
return false;
}
void LockImpl::Lock() {
::EnterCriticalSection(&os_lock_);
#ifndef NDEBUG
// ONLY access data after locking.
owning_thread_id_ = PlatformThread::CurrentId();
DCHECK_NE(owning_thread_id_, 0);
recursion_count_shadow_++;
if (2 == recursion_count_shadow_ && !recursion_used_) {
recursion_used_ = true;
DCHECK(false); // Catch accidental redundant lock acquisition.
}
#endif // NDEBUG
}
void LockImpl::Unlock() {
#ifndef NDEBUG
--recursion_count_shadow_; // ONLY access while lock is still held.
DCHECK(0 <= recursion_count_shadow_);
owning_thread_id_ = 0;
#endif // NDEBUG
::LeaveCriticalSection(&os_lock_);
}
// In non-debug builds, this method is declared as an empty inline method.
#ifndef NDEBUG
void LockImpl::AssertAcquired() const {
DCHECK(recursion_count_shadow_ > 0);
DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId());
}
#endif

View file

@ -0,0 +1,99 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/logging.h"
#include "prmem.h"
#include "prprf.h"
#include "base/string_util.h"
#include "nsXPCOM.h"
namespace mozilla {
Logger::~Logger()
{
LogLevel prlevel = LogLevel::Debug;
int xpcomlevel = -1;
switch (mSeverity) {
case LOG_INFO:
prlevel = LogLevel::Debug;
xpcomlevel = -1;
break;
case LOG_WARNING:
prlevel = LogLevel::Warning;
xpcomlevel = NS_DEBUG_WARNING;
break;
case LOG_ERROR:
prlevel = LogLevel::Error;
xpcomlevel = NS_DEBUG_WARNING;
break;
case LOG_ERROR_REPORT:
prlevel = LogLevel::Error;
xpcomlevel = NS_DEBUG_ASSERTION;
break;
case LOG_FATAL:
prlevel = LogLevel::Error;
xpcomlevel = NS_DEBUG_ABORT;
break;
}
MOZ_LOG(gChromiumPRLog, prlevel, ("%s:%i: %s", mFile, mLine, mMsg ? mMsg : "<no message>"));
if (xpcomlevel != -1)
NS_DebugBreak(xpcomlevel, mMsg, NULL, mFile, mLine);
PR_Free(mMsg);
}
void
Logger::printf(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
mMsg = PR_vsprintf_append(mMsg, fmt, args);
va_end(args);
}
LazyLogModule Logger::gChromiumPRLog("chromium");
} // namespace mozilla
mozilla::Logger&
operator<<(mozilla::Logger& log, const char* s)
{
log.printf("%s", s);
return log;
}
mozilla::Logger&
operator<<(mozilla::Logger& log, const std::string& s)
{
log.printf("%s", s.c_str());
return log;
}
mozilla::Logger&
operator<<(mozilla::Logger& log, int i)
{
log.printf("%i", i);
return log;
}
mozilla::Logger&
operator<<(mozilla::Logger& log, const std::wstring& s)
{
log.printf("%s", WideToASCII(s).c_str());
return log;
}
mozilla::Logger&
operator<<(mozilla::Logger& log, void* p)
{
log.printf("%p", p);
return log;
}

View file

@ -0,0 +1,130 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_LOGGING_H_
#define BASE_LOGGING_H_
#include <string>
#include <cstring>
#include "base/basictypes.h"
#include "mozilla/Logging.h"
#ifdef NO_CHROMIUM_LOGGING
#include <sstream>
#endif
// Replace the Chromium logging code with NSPR-based logging code and
// some C++ wrappers to emulate std::ostream
namespace mozilla {
enum LogSeverity {
LOG_INFO,
LOG_WARNING,
LOG_ERROR,
LOG_ERROR_REPORT,
LOG_FATAL,
LOG_0 = LOG_ERROR
};
class Logger
{
public:
Logger(LogSeverity severity, const char* file, int line)
: mSeverity(severity)
, mFile(file)
, mLine(line)
, mMsg(NULL)
{ }
~Logger();
// not private so that the operator<< overloads can get to it
void printf(const char* fmt, ...);
private:
static mozilla::LazyLogModule gChromiumPRLog;
// static PRLogModuleInfo* GetLog();
LogSeverity mSeverity;
const char* mFile;
int mLine;
char* mMsg;
DISALLOW_EVIL_CONSTRUCTORS(Logger);
};
class LogWrapper
{
public:
LogWrapper(LogSeverity severity, const char* file, int line) :
log(severity, file, line) { }
operator Logger&() const { return log; }
private:
mutable Logger log;
DISALLOW_EVIL_CONSTRUCTORS(LogWrapper);
};
struct EmptyLog
{
};
} // namespace mozilla
mozilla::Logger& operator<<(mozilla::Logger& log, const char* s);
mozilla::Logger& operator<<(mozilla::Logger& log, const std::string& s);
mozilla::Logger& operator<<(mozilla::Logger& log, int i);
mozilla::Logger& operator<<(mozilla::Logger& log, const std::wstring& s);
mozilla::Logger& operator<<(mozilla::Logger& log, void* p);
template<class T>
const mozilla::EmptyLog& operator <<(const mozilla::EmptyLog& log, const T&)
{
return log;
}
#ifdef NO_CHROMIUM_LOGGING
#define CHROMIUM_LOG(info) std::stringstream()
#define LOG_IF(info, condition) if (!(condition)) std::stringstream()
#else
#define CHROMIUM_LOG(info) mozilla::LogWrapper(mozilla::LOG_ ## info, __FILE__, __LINE__)
#define LOG_IF(info, condition) \
if (!(condition)) mozilla::LogWrapper(mozilla::LOG_ ## info, __FILE__, __LINE__)
#endif
#ifdef DEBUG
#define DLOG(info) CHROMIUM_LOG(info)
#define DLOG_IF(info) LOG_IF(info)
#define DCHECK(condition) CHECK(condition)
#else
#define DLOG(info) mozilla::EmptyLog()
#define DLOG_IF(info, condition) mozilla::EmptyLog()
#define DCHECK(condition) while (false && (condition)) mozilla::EmptyLog()
#endif
#undef LOG_ASSERT
#define LOG_ASSERT(cond) CHECK(0)
#define DLOG_ASSERT(cond) DCHECK(0)
#define NOTREACHED() CHROMIUM_LOG(ERROR)
#define NOTIMPLEMENTED() CHROMIUM_LOG(ERROR)
#undef CHECK
#define CHECK(condition) LOG_IF(WARNING, condition)
#define DCHECK_EQ(v1, v2) DCHECK((v1) == (v2))
#define DCHECK_NE(v1, v2) DCHECK((v1) != (v2))
#define DCHECK_LE(v1, v2) DCHECK((v1) <= (v2))
#define DCHECK_LT(v1, v2) DCHECK((v1) < (v2))
#define DCHECK_GE(v1, v2) DCHECK((v1) >= (v2))
#define DCHECK_GT(v1, v2) DCHECK((v1) > (v2))
#endif // BASE_LOGGING_H_

View file

@ -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_

View file

@ -0,0 +1,34 @@
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/mac_util.h"
#include <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
#include "base/file_path.h"
#include "base/logging.h"
#include "base/scoped_cftyperef.h"
#include "base/sys_string_conversions.h"
namespace mac_util {
// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled
bool AmIBundled() {
ProcessSerialNumber psn = {0, kCurrentProcess};
FSRef fsref;
if (GetProcessBundleLocation(&psn, &fsref) != noErr)
return false;
FSCatalogInfo info;
if (FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info,
NULL, NULL, NULL) != noErr) {
return false;
}
return info.nodeFlags & kFSNodeIsDirectoryMask;
}
} // namespace mac_util

View file

@ -0,0 +1,579 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_loop.h"
#include <algorithm>
#include "mozilla/Atomics.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_pump_default.h"
#include "base/string_util.h"
#include "base/thread_local.h"
#if defined(OS_MACOSX)
#include "base/message_pump_mac.h"
#endif
#if defined(OS_POSIX)
#include "base/message_pump_libevent.h"
#endif
#if defined(OS_LINUX) || defined(OS_BSD)
#if defined(MOZ_WIDGET_GTK)
#include "base/message_pump_glib.h"
#endif
#endif
#ifdef ANDROID
#include "base/message_pump_android.h"
#endif
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
#include "TracedTaskCommon.h"
#endif
#include "MessagePump.h"
using base::Time;
using base::TimeDelta;
using base::TimeTicks;
using mozilla::Move;
using mozilla::Runnable;
static base::ThreadLocalPointer<MessageLoop>& get_tls_ptr() {
static base::ThreadLocalPointer<MessageLoop> tls_ptr;
return tls_ptr;
}
//------------------------------------------------------------------------------
// Logical events for Histogram profiling. Run with -message-loop-histogrammer
// to get an accounting of messages and actions taken on each thread.
static const int kTaskRunEvent = 0x1;
static const int kTimerEvent = 0x2;
// Provide range of message IDs for use in histogramming and debug display.
static const int kLeastNonZeroMessageId = 1;
static const int kMaxMessageId = 1099;
static const int kNumberOfDistinctMessagesDisplayed = 1100;
//------------------------------------------------------------------------------
#if defined(OS_WIN)
// Upon a SEH exception in this thread, it restores the original unhandled
// exception filter.
static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) {
::SetUnhandledExceptionFilter(old_filter);
return EXCEPTION_CONTINUE_SEARCH;
}
// Retrieves a pointer to the current unhandled exception filter. There
// is no standalone getter method.
static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL;
top_filter = ::SetUnhandledExceptionFilter(0);
::SetUnhandledExceptionFilter(top_filter);
return top_filter;
}
#endif // defined(OS_WIN)
//------------------------------------------------------------------------------
// static
MessageLoop* MessageLoop::current() {
return get_tls_ptr().Get();
}
static mozilla::Atomic<int32_t> message_loop_id_seq(0);
MessageLoop::MessageLoop(Type type, nsIThread* aThread)
: type_(type),
id_(++message_loop_id_seq),
nestable_tasks_allowed_(true),
exception_restoration_(false),
state_(NULL),
run_depth_base_(1),
#ifdef OS_WIN
os_modal_loop_(false),
#endif // OS_WIN
transient_hang_timeout_(0),
permanent_hang_timeout_(0),
next_sequence_num_(0) {
DCHECK(!current()) << "should only have one message loop per thread";
get_tls_ptr().Set(this);
switch (type_) {
case TYPE_MOZILLA_PARENT:
MOZ_RELEASE_ASSERT(!aThread);
pump_ = new mozilla::ipc::MessagePump(aThread);
return;
case TYPE_MOZILLA_CHILD:
MOZ_RELEASE_ASSERT(!aThread);
pump_ = new mozilla::ipc::MessagePumpForChildProcess();
// There is a MessageLoop Run call from XRE_InitChildProcess
// and another one from MessagePumpForChildProcess. The one
// from MessagePumpForChildProcess becomes the base, so we need
// to set run_depth_base_ to 2 or we'll never be able to process
// Idle tasks.
run_depth_base_ = 2;
return;
case TYPE_MOZILLA_NONMAINTHREAD:
pump_ = new mozilla::ipc::MessagePumpForNonMainThreads(aThread);
return;
#if defined(OS_WIN)
case TYPE_MOZILLA_NONMAINUITHREAD:
pump_ = new mozilla::ipc::MessagePumpForNonMainUIThreads(aThread);
return;
#endif
default:
// Create one of Chromium's standard MessageLoop types below.
break;
}
#if defined(OS_WIN)
// TODO(rvargas): Get rid of the OS guards.
if (type_ == TYPE_DEFAULT) {
pump_ = new base::MessagePumpDefault();
} else if (type_ == TYPE_IO) {
pump_ = new base::MessagePumpForIO();
} else {
DCHECK(type_ == TYPE_UI);
pump_ = new base::MessagePumpForUI();
}
#elif defined(OS_POSIX)
if (type_ == TYPE_UI) {
#if defined(OS_MACOSX)
pump_ = base::MessagePumpMac::Create();
#elif defined(OS_LINUX) || defined(OS_BSD)
pump_ = new base::MessagePumpForUI();
#endif // OS_LINUX
} else if (type_ == TYPE_IO) {
pump_ = new base::MessagePumpLibevent();
} else {
pump_ = new base::MessagePumpDefault();
}
#endif // OS_POSIX
}
MessageLoop::~MessageLoop() {
DCHECK(this == current());
// Let interested parties have one last shot at accessing this.
FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
WillDestroyCurrentMessageLoop());
DCHECK(!state_);
// Clean up any unprocessed tasks, but take care: deleting a task could
// result in the addition of more tasks (e.g., via DeleteSoon). We set a
// limit on the number of times we will allow a deleted task to generate more
// tasks. Normally, we should only pass through this loop once or twice. If
// we end up hitting the loop limit, then it is probably due to one task that
// is being stubborn. Inspect the queues to see who is left.
bool did_work;
for (int i = 0; i < 100; ++i) {
DeletePendingTasks();
ReloadWorkQueue();
// If we end up with empty queues, then break out of the loop.
did_work = DeletePendingTasks();
if (!did_work)
break;
}
DCHECK(!did_work);
// OK, now make it so that no one can find us.
get_tls_ptr().Set(NULL);
}
void MessageLoop::AddDestructionObserver(DestructionObserver *obs) {
DCHECK(this == current());
destruction_observers_.AddObserver(obs);
}
void MessageLoop::RemoveDestructionObserver(DestructionObserver *obs) {
DCHECK(this == current());
destruction_observers_.RemoveObserver(obs);
}
void MessageLoop::Run() {
AutoRunState save_state(this);
RunHandler();
}
// Runs the loop in two different SEH modes:
// enable_SEH_restoration_ = false : any unhandled exception goes to the last
// one that calls SetUnhandledExceptionFilter().
// enable_SEH_restoration_ = true : any unhandled exception goes to the filter
// that was existed before the loop was run.
void MessageLoop::RunHandler() {
#if defined(OS_WIN)
if (exception_restoration_) {
LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
MOZ_SEH_TRY {
RunInternal();
} MOZ_SEH_EXCEPT(SEHFilter(current_filter)) {
}
return;
}
#endif
RunInternal();
}
//------------------------------------------------------------------------------
void MessageLoop::RunInternal() {
DCHECK(this == current());
pump_->Run(this);
}
//------------------------------------------------------------------------------
// Wrapper functions for use in above message loop framework.
bool MessageLoop::ProcessNextDelayedNonNestableTask() {
if (state_->run_depth > run_depth_base_)
return false;
if (deferred_non_nestable_work_queue_.empty())
return false;
RefPtr<Runnable> task = deferred_non_nestable_work_queue_.front().task.forget();
deferred_non_nestable_work_queue_.pop();
RunTask(task.forget());
return true;
}
//------------------------------------------------------------------------------
void MessageLoop::Quit() {
DCHECK(current() == this);
if (state_) {
state_->quit_received = true;
} else {
NOTREACHED() << "Must be inside Run to call Quit";
}
}
void MessageLoop::PostTask(already_AddRefed<Runnable> task) {
PostTask_Helper(Move(task), 0);
}
void MessageLoop::PostDelayedTask(already_AddRefed<Runnable> task, int delay_ms) {
PostTask_Helper(Move(task), delay_ms);
}
void MessageLoop::PostIdleTask(already_AddRefed<Runnable> task) {
DCHECK(current() == this);
MOZ_ASSERT(NS_IsMainThread());
PendingTask pending_task(Move(task), false);
deferred_non_nestable_work_queue_.push(Move(pending_task));
}
// Possibly called on a background thread!
void MessageLoop::PostTask_Helper(already_AddRefed<Runnable> task, int delay_ms) {
if (nsIEventTarget* target = pump_->GetXPCOMThread()) {
nsresult rv;
if (delay_ms) {
rv = target->DelayedDispatch(Move(task), delay_ms);
} else {
rv = target->Dispatch(Move(task), 0);
}
MOZ_ALWAYS_SUCCEEDS(rv);
return;
}
PendingTask pending_task(Move(task), true);
if (delay_ms > 0) {
pending_task.delayed_run_time =
TimeTicks::Now() + TimeDelta::FromMilliseconds(delay_ms);
} else {
DCHECK(delay_ms == 0) << "delay should not be negative";
}
// Warning: Don't try to short-circuit, and handle this thread's tasks more
// directly, as it could starve handling of foreign threads. Put every task
// into this queue.
RefPtr<base::MessagePump> pump;
{
AutoLock locked(incoming_queue_lock_);
incoming_queue_.push(Move(pending_task));
pump = pump_;
}
// Since the incoming_queue_ may contain a task that destroys this message
// loop, we cannot exit incoming_queue_lock_ until we are done with |this|.
// We use a stack-based reference to the message pump so that we can call
// ScheduleWork outside of incoming_queue_lock_.
pump->ScheduleWork();
}
void MessageLoop::SetNestableTasksAllowed(bool allowed) {
if (nestable_tasks_allowed_ != allowed) {
nestable_tasks_allowed_ = allowed;
if (!nestable_tasks_allowed_)
return;
// Start the native pump if we are not already pumping.
pump_->ScheduleWorkForNestedLoop();
}
}
void MessageLoop::ScheduleWork() {
// Start the native pump if we are not already pumping.
pump_->ScheduleWork();
}
bool MessageLoop::NestableTasksAllowed() const {
return nestable_tasks_allowed_;
}
//------------------------------------------------------------------------------
void MessageLoop::RunTask(already_AddRefed<Runnable> aTask) {
DCHECK(nestable_tasks_allowed_);
// Execute the task and assume the worst: It is probably not reentrant.
nestable_tasks_allowed_ = false;
RefPtr<Runnable> task = aTask;
task->Run();
task = nullptr;
nestable_tasks_allowed_ = true;
}
bool MessageLoop::DeferOrRunPendingTask(PendingTask&& pending_task) {
if (pending_task.nestable || state_->run_depth <= run_depth_base_) {
RunTask(pending_task.task.forget());
// Show that we ran a task (Note: a new one might arrive as a
// consequence!).
return true;
}
// We couldn't run the task now because we're in a nested message loop
// and the task isn't nestable.
deferred_non_nestable_work_queue_.push(Move(pending_task));
return false;
}
void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) {
// Move to the delayed work queue. Initialize the sequence number
// before inserting into the delayed_work_queue_. The sequence number
// is used to faciliate FIFO sorting when two tasks have the same
// delayed_run_time value.
PendingTask new_pending_task(pending_task);
new_pending_task.sequence_num = next_sequence_num_++;
delayed_work_queue_.push(Move(new_pending_task));
}
void MessageLoop::ReloadWorkQueue() {
// We can improve performance of our loading tasks from incoming_queue_ to
// work_queue_ by waiting until the last minute (work_queue_ is empty) to
// load. That reduces the number of locks-per-task significantly when our
// queues get large.
if (!work_queue_.empty())
return; // Wait till we *really* need to lock and load.
// Acquire all we can from the inter-thread queue with one lock acquisition.
{
AutoLock lock(incoming_queue_lock_);
if (incoming_queue_.empty())
return;
std::swap(incoming_queue_, work_queue_);
DCHECK(incoming_queue_.empty());
}
}
bool MessageLoop::DeletePendingTasks() {
MOZ_ASSERT(work_queue_.empty());
bool did_work = !deferred_non_nestable_work_queue_.empty();
while (!deferred_non_nestable_work_queue_.empty()) {
deferred_non_nestable_work_queue_.pop();
}
did_work |= !delayed_work_queue_.empty();
while (!delayed_work_queue_.empty()) {
delayed_work_queue_.pop();
}
return did_work;
}
bool MessageLoop::DoWork() {
if (!nestable_tasks_allowed_) {
// Task can't be executed right now.
return false;
}
for (;;) {
ReloadWorkQueue();
if (work_queue_.empty())
break;
// Execute oldest task.
do {
PendingTask pending_task = Move(work_queue_.front());
work_queue_.pop();
if (!pending_task.delayed_run_time.is_null()) {
// NB: Don't move, because we use this later!
AddToDelayedWorkQueue(pending_task);
// If we changed the topmost task, then it is time to re-schedule.
if (delayed_work_queue_.top().task == pending_task.task)
pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
} else {
if (DeferOrRunPendingTask(Move(pending_task)))
return true;
}
} while (!work_queue_.empty());
}
// Nothing happened.
return false;
}
bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) {
*next_delayed_work_time = TimeTicks();
return false;
}
if (delayed_work_queue_.top().delayed_run_time > TimeTicks::Now()) {
*next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
return false;
}
PendingTask pending_task = delayed_work_queue_.top();
delayed_work_queue_.pop();
if (!delayed_work_queue_.empty())
*next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;
return DeferOrRunPendingTask(Move(pending_task));
}
bool MessageLoop::DoIdleWork() {
if (ProcessNextDelayedNonNestableTask())
return true;
if (state_->quit_received)
pump_->Quit();
return false;
}
//------------------------------------------------------------------------------
// MessageLoop::AutoRunState
MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) {
// Make the loop reference us.
previous_state_ = loop_->state_;
if (previous_state_) {
run_depth = previous_state_->run_depth + 1;
} else {
run_depth = 1;
}
loop_->state_ = this;
// Initialize the other fields:
quit_received = false;
#if defined(OS_WIN)
dispatcher = NULL;
#endif
}
MessageLoop::AutoRunState::~AutoRunState() {
loop_->state_ = previous_state_;
}
//------------------------------------------------------------------------------
// MessageLoop::PendingTask
bool MessageLoop::PendingTask::operator<(const PendingTask& other) const {
// Since the top of a priority queue is defined as the "greatest" element, we
// need to invert the comparison here. We want the smaller time to be at the
// top of the heap.
if (delayed_run_time < other.delayed_run_time)
return false;
if (delayed_run_time > other.delayed_run_time)
return true;
// If the times happen to match, then we use the sequence number to decide.
// Compare the difference to support integer roll-over.
return (sequence_num - other.sequence_num) > 0;
}
//------------------------------------------------------------------------------
// MessageLoopForUI
#if defined(OS_WIN)
void MessageLoopForUI::Run(Dispatcher* dispatcher) {
AutoRunState save_state(this);
state_->dispatcher = dispatcher;
RunHandler();
}
void MessageLoopForUI::AddObserver(Observer* observer) {
pump_win()->AddObserver(observer);
}
void MessageLoopForUI::RemoveObserver(Observer* observer) {
pump_win()->RemoveObserver(observer);
}
void MessageLoopForUI::WillProcessMessage(const MSG& message) {
pump_win()->WillProcessMessage(message);
}
void MessageLoopForUI::DidProcessMessage(const MSG& message) {
pump_win()->DidProcessMessage(message);
}
void MessageLoopForUI::PumpOutPendingPaintMessages() {
pump_ui()->PumpOutPendingPaintMessages();
}
#endif // defined(OS_WIN)
//------------------------------------------------------------------------------
// MessageLoopForIO
#if defined(OS_WIN)
void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) {
pump_io()->RegisterIOHandler(file, handler);
}
bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
return pump_io()->WaitForIOCompletion(timeout, filter);
}
#elif defined(OS_POSIX)
bool MessageLoopForIO::WatchFileDescriptor(int fd,
bool persistent,
Mode mode,
FileDescriptorWatcher *controller,
Watcher *delegate) {
return pump_libevent()->WatchFileDescriptor(
fd,
persistent,
static_cast<base::MessagePumpLibevent::Mode>(mode),
controller,
delegate);
}
bool
MessageLoopForIO::CatchSignal(int sig,
SignalEvent* sigevent,
SignalWatcher* delegate)
{
return pump_libevent()->CatchSignal(sig, sigevent, delegate);
}
#endif

View file

@ -0,0 +1,558 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MESSAGE_LOOP_H_
#define BASE_MESSAGE_LOOP_H_
#include <deque>
#include <queue>
#include <string>
#include <vector>
#include <map>
#include "base/lock.h"
#include "base/message_pump.h"
#include "base/observer_list.h"
#if defined(OS_WIN)
// We need this to declare base::MessagePumpWin::Dispatcher, which we should
// really just eliminate.
#include "base/message_pump_win.h"
#elif defined(OS_POSIX)
#include "base/message_pump_libevent.h"
#endif
#include "nsAutoPtr.h"
#include "nsThreadUtils.h"
class nsIThread;
namespace mozilla {
namespace ipc {
class DoWorkRunnable;
} /* namespace ipc */
} /* namespace mozilla */
// A MessageLoop is used to process events for a particular thread. There is
// at most one MessageLoop instance per thread.
//
// Events include at a minimum Task instances submitted to PostTask or those
// managed by TimerManager. Depending on the type of message pump used by the
// MessageLoop other events such as UI messages may be processed. On Windows
// APC calls (as time permits) and signals sent to a registered set of HANDLEs
// may also be processed.
//
// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called
// on the thread where the MessageLoop's Run method executes.
//
// NOTE: MessageLoop has task reentrancy protection. This means that if a
// task is being processed, a second task cannot start until the first task is
// finished. Reentrancy can happen when processing a task, and an inner
// message pump is created. That inner pump then processes native messages
// which could implicitly start an inner task. Inner message pumps are created
// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions
// (DoDragDrop), printer functions (StartDoc) and *many* others.
//
// Sample workaround when inner task processing is needed:
// bool old_state = MessageLoop::current()->NestableTasksAllowed();
// MessageLoop::current()->SetNestableTasksAllowed(true);
// HRESULT hr = DoDragDrop(...); // Implicitly runs a modal message loop here.
// MessageLoop::current()->SetNestableTasksAllowed(old_state);
// // Process hr (the result returned by DoDragDrop().
//
// Please be SURE your task is reentrant (nestable) and all global variables
// are stable and accessible before calling SetNestableTasksAllowed(true).
//
class MessageLoop : public base::MessagePump::Delegate {
friend class mozilla::ipc::DoWorkRunnable;
public:
// A DestructionObserver is notified when the current MessageLoop is being
// destroyed. These obsevers are notified prior to MessageLoop::current()
// being changed to return NULL. This gives interested parties the chance to
// do final cleanup that depends on the MessageLoop.
//
// NOTE: Any tasks posted to the MessageLoop during this notification will
// not be run. Instead, they will be deleted.
//
class DestructionObserver {
public:
virtual ~DestructionObserver() {}
virtual void WillDestroyCurrentMessageLoop() = 0;
};
// Add a DestructionObserver, which will start receiving notifications
// immediately.
void AddDestructionObserver(DestructionObserver* destruction_observer);
// Remove a DestructionObserver. It is safe to call this method while a
// DestructionObserver is receiving a notification callback.
void RemoveDestructionObserver(DestructionObserver* destruction_observer);
// The "PostTask" family of methods call the task's Run method asynchronously
// from within a message loop at some point in the future.
//
// With the PostTask variant, tasks are invoked in FIFO order, inter-mixed
// with normal UI or IO event processing. With the PostDelayedTask variant,
// tasks are called after at least approximately 'delay_ms' have elapsed.
//
// The NonNestable variants work similarly except that they promise never to
// dispatch the task from a nested invocation of MessageLoop::Run. Instead,
// such tasks get deferred until the top-most MessageLoop::Run is executing.
//
// The MessageLoop takes ownership of the Task, and deletes it after it has
// been Run().
//
// NOTE: These methods may be called on any thread. The Task will be invoked
// on the thread that executes MessageLoop::Run().
void PostTask(already_AddRefed<mozilla::Runnable> task);
void PostDelayedTask(already_AddRefed<mozilla::Runnable> task, int delay_ms);
// PostIdleTask is not thread safe and should be called on this thread
void PostIdleTask(already_AddRefed<mozilla::Runnable> task);
// Run the message loop.
void Run();
// Signals the Run method to return after it is done processing all pending
// messages. This method may only be called on the same thread that called
// Run, and Run must still be on the call stack.
//
// Use QuitTask if you need to Quit another thread's MessageLoop, but note
// that doing so is fairly dangerous if the target thread makes nested calls
// to MessageLoop::Run. The problem being that you won't know which nested
// run loop you are quiting, so be careful!
//
void Quit();
// Invokes Quit on the current MessageLoop when run. Useful to schedule an
// arbitrary MessageLoop to Quit.
class QuitTask : public mozilla::Runnable {
public:
NS_IMETHOD Run() override {
MessageLoop::current()->Quit();
return NS_OK;
}
};
// A MessageLoop has a particular type, which indicates the set of
// asynchronous events it may process in addition to tasks and timers.
//
// TYPE_DEFAULT
// This type of ML only supports tasks and timers.
//
// TYPE_UI
// This type of ML also supports native UI events (e.g., Windows messages).
// See also MessageLoopForUI.
//
// TYPE_IO
// This type of ML also supports asynchronous IO. See also
// MessageLoopForIO.
//
// TYPE_MOZILLA_CHILD
// This type of ML is used in Mozilla child processes which initialize
// XPCOM and use the gecko event loop.
//
// TYPE_MOZILLA_PARENT
// This type of ML is used in Mozilla parent processes which initialize
// XPCOM and use the gecko event loop.
//
// TYPE_MOZILLA_NONMAINTHREAD
// This type of ML is used in Mozilla parent processes which initialize
// XPCOM and use the nsThread event loop.
//
// TYPE_MOZILLA_NONMAINUITHREAD
// This type of ML is used in Mozilla processes which initialize XPCOM
// and use TYPE_UI loop logic.
//
enum Type {
TYPE_DEFAULT,
TYPE_UI,
TYPE_IO,
TYPE_MOZILLA_CHILD,
TYPE_MOZILLA_PARENT,
TYPE_MOZILLA_NONMAINTHREAD,
TYPE_MOZILLA_NONMAINUITHREAD
};
// Normally, it is not necessary to instantiate a MessageLoop. Instead, it
// is typical to make use of the current thread's MessageLoop instance.
explicit MessageLoop(Type type = TYPE_DEFAULT, nsIThread* aThread = nullptr);
~MessageLoop();
// Returns the type passed to the constructor.
Type type() const { return type_; }
// Unique, non-repeating ID for this message loop.
int32_t id() const { return id_; }
// Optional call to connect the thread name with this loop.
void set_thread_name(const std::string& aThreadName) {
DCHECK(thread_name_.empty()) << "Should not rename this thread!";
thread_name_ = aThreadName;
}
const std::string& thread_name() const { return thread_name_; }
// Returns the MessageLoop object for the current thread, or null if none.
static MessageLoop* current();
// Enables or disables the recursive task processing. This happens in the case
// of recursive message loops. Some unwanted message loop may occurs when
// using common controls or printer functions. By default, recursive task
// processing is disabled.
//
// The specific case where tasks get queued is:
// - The thread is running a message loop.
// - It receives a task #1 and execute it.
// - The task #1 implicitly start a message loop, like a MessageBox in the
// unit test. This can also be StartDoc or GetSaveFileName.
// - The thread receives a task #2 before or while in this second message
// loop.
// - With NestableTasksAllowed set to true, the task #2 will run right away.
// Otherwise, it will get executed right after task #1 completes at "thread
// message loop level".
void SetNestableTasksAllowed(bool allowed);
void ScheduleWork();
bool NestableTasksAllowed() const;
// Enables or disables the restoration during an exception of the unhandled
// exception filter that was active when Run() was called. This can happen
// if some third party code call SetUnhandledExceptionFilter() and never
// restores the previous filter.
void set_exception_restoration(bool restore) {
exception_restoration_ = restore;
}
#if defined(OS_WIN)
void set_os_modal_loop(bool os_modal_loop) {
os_modal_loop_ = os_modal_loop;
}
bool & os_modal_loop() {
return os_modal_loop_;
}
#endif // OS_WIN
// Set the timeouts for background hang monitoring.
// A value of 0 indicates there is no timeout.
void set_hang_timeouts(uint32_t transient_timeout_ms,
uint32_t permanent_timeout_ms) {
transient_hang_timeout_ = transient_timeout_ms;
permanent_hang_timeout_ = permanent_timeout_ms;
}
uint32_t transient_hang_timeout() const {
return transient_hang_timeout_;
}
uint32_t permanent_hang_timeout() const {
return permanent_hang_timeout_;
}
//----------------------------------------------------------------------------
protected:
struct RunState {
// Used to count how many Run() invocations are on the stack.
int run_depth;
// Used to record that Quit() was called, or that we should quit the pump
// once it becomes idle.
bool quit_received;
#if defined(OS_WIN)
base::MessagePumpWin::Dispatcher* dispatcher;
#endif
};
class AutoRunState : RunState {
public:
explicit AutoRunState(MessageLoop* loop);
~AutoRunState();
private:
MessageLoop* loop_;
RunState* previous_state_;
};
// This structure is copied around by value.
struct PendingTask {
RefPtr<mozilla::Runnable> task; // The task to run.
base::TimeTicks delayed_run_time; // The time when the task should be run.
int sequence_num; // Secondary sort key for run time.
bool nestable; // OK to dispatch from a nested loop.
PendingTask(already_AddRefed<mozilla::Runnable> aTask, bool aNestable)
: task(aTask), sequence_num(0), nestable(aNestable) {
}
PendingTask(PendingTask&& aOther)
: task(aOther.task.forget()),
delayed_run_time(aOther.delayed_run_time),
sequence_num(aOther.sequence_num),
nestable(aOther.nestable) {
}
// std::priority_queue<T>::top is dumb, so we have to have this.
PendingTask(const PendingTask& aOther)
: task(aOther.task),
delayed_run_time(aOther.delayed_run_time),
sequence_num(aOther.sequence_num),
nestable(aOther.nestable) {
}
PendingTask& operator=(const PendingTask& aOther)
{
task = aOther.task;
delayed_run_time = aOther.delayed_run_time;
sequence_num = aOther.sequence_num;
nestable = aOther.nestable;
return *this;
}
// Used to support sorting.
bool operator<(const PendingTask& other) const;
};
typedef std::queue<PendingTask> TaskQueue;
typedef std::priority_queue<PendingTask> DelayedTaskQueue;
#if defined(OS_WIN)
base::MessagePumpWin* pump_win() {
return static_cast<base::MessagePumpWin*>(pump_.get());
}
#elif defined(OS_POSIX)
base::MessagePumpLibevent* pump_libevent() {
return static_cast<base::MessagePumpLibevent*>(pump_.get());
}
#endif
// A function to encapsulate all the exception handling capability in the
// stacks around the running of a main message loop. It will run the message
// loop in a SEH try block or not depending on the set_SEH_restoration()
// flag.
void RunHandler();
// A surrounding stack frame around the running of the message loop that
// supports all saving and restoring of state, as is needed for any/all (ugly)
// recursive calls.
void RunInternal();
// Called to process any delayed non-nestable tasks.
bool ProcessNextDelayedNonNestableTask();
//----------------------------------------------------------------------------
// Run a work_queue_ task or new_task, and delete it (if it was processed by
// PostTask). If there are queued tasks, the oldest one is executed and
// new_task is queued. new_task is optional and can be NULL. In this NULL
// case, the method will run one pending task (if any exist). Returns true if
// it executes a task. Queued tasks accumulate only when there is a
// non-nestable task currently processing, in which case the new_task is
// appended to the list work_queue_. Such re-entrancy generally happens when
// an unrequested message pump (typical of a native dialog) is executing in
// the context of a task.
bool QueueOrRunTask(already_AddRefed<mozilla::Runnable> new_task);
// Runs the specified task and deletes it.
void RunTask(already_AddRefed<mozilla::Runnable> task);
// Calls RunTask or queues the pending_task on the deferred task list if it
// cannot be run right now. Returns true if the task was run.
bool DeferOrRunPendingTask(PendingTask&& pending_task);
// Adds the pending task to delayed_work_queue_.
void AddToDelayedWorkQueue(const PendingTask& pending_task);
// Load tasks from the incoming_queue_ into work_queue_ if the latter is
// empty. The former requires a lock to access, while the latter is directly
// accessible on this thread.
void ReloadWorkQueue();
// Delete tasks that haven't run yet without running them. Used in the
// destructor to make sure all the task's destructors get called. Returns
// true if some work was done.
bool DeletePendingTasks();
// Post a task to our incomming queue.
void PostTask_Helper(already_AddRefed<mozilla::Runnable> task, int delay_ms);
// base::MessagePump::Delegate methods:
virtual bool DoWork() override;
virtual bool DoDelayedWork(base::TimeTicks* next_delayed_work_time) override;
virtual bool DoIdleWork() override;
Type type_;
int32_t id_;
// A list of tasks that need to be processed by this instance. Note that
// this queue is only accessed (push/pop) by our current thread.
TaskQueue work_queue_;
// Contains delayed tasks, sorted by their 'delayed_run_time' property.
DelayedTaskQueue delayed_work_queue_;
// A queue of non-nestable tasks that we had to defer because when it came
// time to execute them we were in a nested message loop. They will execute
// once we're out of nested message loops.
TaskQueue deferred_non_nestable_work_queue_;
RefPtr<base::MessagePump> pump_;
base::ObserverList<DestructionObserver> destruction_observers_;
// A recursion block that prevents accidentally running additonal tasks when
// insider a (accidentally induced?) nested message pump.
bool nestable_tasks_allowed_;
bool exception_restoration_;
std::string thread_name_;
// A null terminated list which creates an incoming_queue of tasks that are
// aquired under a mutex for processing on this instance's thread. These tasks
// have not yet been sorted out into items for our work_queue_ vs items that
// will be handled by the TimerManager.
TaskQueue incoming_queue_;
// Protect access to incoming_queue_.
Lock incoming_queue_lock_;
RunState* state_;
int run_depth_base_;
#if defined(OS_WIN)
// Should be set to true before calling Windows APIs like TrackPopupMenu, etc
// which enter a modal message loop.
bool os_modal_loop_;
#endif
// Timeout values for hang monitoring
uint32_t transient_hang_timeout_;
uint32_t permanent_hang_timeout_;
// The next sequence number to use for delayed tasks.
int next_sequence_num_;
DISALLOW_COPY_AND_ASSIGN(MessageLoop);
};
//-----------------------------------------------------------------------------
// MessageLoopForUI extends MessageLoop with methods that are particular to a
// MessageLoop instantiated with TYPE_UI.
//
// This class is typically used like so:
// MessageLoopForUI::current()->...call some method...
//
class MessageLoopForUI : public MessageLoop {
public:
explicit MessageLoopForUI(Type aType=TYPE_UI) : MessageLoop(aType) {
}
// Returns the MessageLoopForUI of the current thread.
static MessageLoopForUI* current() {
MessageLoop* loop = MessageLoop::current();
if (!loop)
return NULL;
Type type = loop->type();
DCHECK(type == MessageLoop::TYPE_UI ||
type == MessageLoop::TYPE_MOZILLA_PARENT ||
type == MessageLoop::TYPE_MOZILLA_CHILD);
return static_cast<MessageLoopForUI*>(loop);
}
#if defined(OS_WIN)
typedef base::MessagePumpWin::Dispatcher Dispatcher;
typedef base::MessagePumpWin::Observer Observer;
// Please see MessagePumpWin for definitions of these methods.
void Run(Dispatcher* dispatcher);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
void WillProcessMessage(const MSG& message);
void DidProcessMessage(const MSG& message);
void PumpOutPendingPaintMessages();
protected:
// TODO(rvargas): Make this platform independent.
base::MessagePumpForUI* pump_ui() {
return static_cast<base::MessagePumpForUI*>(pump_.get());
}
#endif // defined(OS_WIN)
};
// Do not add any member variables to MessageLoopForUI! This is important b/c
// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra
// data that you need should be stored on the MessageLoop's pump_ instance.
COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI),
MessageLoopForUI_should_not_have_extra_member_variables);
//-----------------------------------------------------------------------------
// MessageLoopForIO extends MessageLoop with methods that are particular to a
// MessageLoop instantiated with TYPE_IO.
//
// This class is typically used like so:
// MessageLoopForIO::current()->...call some method...
//
class MessageLoopForIO : public MessageLoop {
public:
MessageLoopForIO() : MessageLoop(TYPE_IO) {
}
// Returns the MessageLoopForIO of the current thread.
static MessageLoopForIO* current() {
MessageLoop* loop = MessageLoop::current();
DCHECK_EQ(MessageLoop::TYPE_IO, loop->type());
return static_cast<MessageLoopForIO*>(loop);
}
#if defined(OS_WIN)
typedef base::MessagePumpForIO::IOHandler IOHandler;
typedef base::MessagePumpForIO::IOContext IOContext;
// Please see MessagePumpWin for definitions of these methods.
void RegisterIOHandler(HANDLE file_handle, IOHandler* handler);
bool WaitForIOCompletion(DWORD timeout, IOHandler* filter);
protected:
// TODO(rvargas): Make this platform independent.
base::MessagePumpForIO* pump_io() {
return static_cast<base::MessagePumpForIO*>(pump_.get());
}
#elif defined(OS_POSIX)
typedef base::MessagePumpLibevent::Watcher Watcher;
typedef base::MessagePumpLibevent::FileDescriptorWatcher
FileDescriptorWatcher;
typedef base::LineWatcher LineWatcher;
enum Mode {
WATCH_READ = base::MessagePumpLibevent::WATCH_READ,
WATCH_WRITE = base::MessagePumpLibevent::WATCH_WRITE,
WATCH_READ_WRITE = base::MessagePumpLibevent::WATCH_READ_WRITE
};
// Please see MessagePumpLibevent for definition.
bool WatchFileDescriptor(int fd,
bool persistent,
Mode mode,
FileDescriptorWatcher *controller,
Watcher *delegate);
typedef base::MessagePumpLibevent::SignalEvent SignalEvent;
typedef base::MessagePumpLibevent::SignalWatcher SignalWatcher;
bool CatchSignal(int sig,
SignalEvent* sigevent,
SignalWatcher* delegate);
#endif // defined(OS_POSIX)
};
// Do not add any member variables to MessageLoopForIO! This is important b/c
// MessageLoopForIO is often allocated via MessageLoop(TYPE_IO). Any extra
// data that you need should be stored on the MessageLoop's pump_ instance.
COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForIO),
MessageLoopForIO_should_not_have_extra_member_variables);
#endif // BASE_MESSAGE_LOOP_H_

View file

@ -0,0 +1,143 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MESSAGE_PUMP_H_
#define BASE_MESSAGE_PUMP_H_
#include "nsISupportsImpl.h"
class nsIEventTarget;
namespace base {
class TimeTicks;
class MessagePump {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessagePump)
// Please see the comments above the Run method for an illustration of how
// these delegate methods are used.
class Delegate {
public:
virtual ~Delegate() {}
// Called from within Run in response to ScheduleWork or when the message
// pump would otherwise call DoDelayedWork. Returns true to indicate that
// work was done. DoDelayedWork will not be called if DoWork returns true.
virtual bool DoWork() = 0;
// Called from within Run in response to ScheduleDelayedWork or when the
// message pump would otherwise sleep waiting for more work. Returns true
// to indicate that delayed work was done. DoIdleWork will not be called
// if DoDelayedWork returns true. Upon return |next_delayed_work_time|
// indicates the time when DoDelayedWork should be called again. If
// |next_delayed_work_time| is null (per Time::is_null), then the queue of
// future delayed work (timer events) is currently empty, and no additional
// calls to this function need to be scheduled.
virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0;
// Called from within Run just before the message pump goes to sleep.
// Returns true to indicate that idle work was done.
virtual bool DoIdleWork() = 0;
};
// The Run method is called to enter the message pump's run loop.
//
// Within the method, the message pump is responsible for processing native
// messages as well as for giving cycles to the delegate periodically. The
// message pump should take care to mix delegate callbacks with native
// message processing so neither type of event starves the other of cycles.
//
// The anatomy of a typical run loop:
//
// for (;;) {
// bool did_work = DoInternalWork();
// if (should_quit_)
// break;
//
// did_work |= delegate_->DoWork();
// if (should_quit_)
// break;
//
// did_work |= delegate_->DoDelayedWork();
// if (should_quit_)
// break;
//
// if (did_work)
// continue;
//
// did_work = delegate_->DoIdleWork();
// if (should_quit_)
// break;
//
// if (did_work)
// continue;
//
// WaitForWork();
// }
//
// Here, DoInternalWork is some private method of the message pump that is
// responsible for dispatching the next UI message or notifying the next IO
// completion (for example). WaitForWork is a private method that simply
// blocks until there is more work of any type to do.
//
// Notice that the run loop cycles between calling DoInternalWork, DoWork,
// and DoDelayedWork methods. This helps ensure that neither work queue
// starves the other. This is important for message pumps that are used to
// drive animations, for example.
//
// Notice also that after each callout to foreign code, the run loop checks
// to see if it should quit. The Quit method is responsible for setting this
// flag. No further work is done once the quit flag is set.
//
// NOTE: Care must be taken to handle Run being called again from within any
// of the callouts to foreign code. Native message pumps may also need to
// deal with other native message pumps being run outside their control
// (e.g., the MessageBox API on Windows pumps UI messages!). To be specific,
// the callouts (DoWork and DoDelayedWork) MUST still be provided even in
// nested sub-loops that are "seemingly" outside the control of this message
// pump. DoWork in particular must never be starved for time slices unless
// it returns false (meaning it has run out of things to do).
//
virtual void Run(Delegate* delegate) = 0;
// Quit immediately from the most recently entered run loop. This method may
// only be used on the thread that called Run.
virtual void Quit() = 0;
// Schedule a DoWork callback to happen reasonably soon. Does nothing if a
// DoWork callback is already scheduled. This method may be called from any
// thread. Once this call is made, DoWork should not be "starved" at least
// until it returns a value of false.
virtual void ScheduleWork() = 0;
// This method may only called from the thread that called Run.
//
// Ensure that DoWork will be called if a nested loop is entered.
// If a MessagePump can already guarantee that DoWork will be called
// "reasonably soon", this method can be a no-op to avoid expensive
// atomic tests and/or syscalls required for ScheduleWork().
virtual void ScheduleWorkForNestedLoop() { ScheduleWork(); };
// Schedule a DoDelayedWork callback to happen at the specified time,
// cancelling any pending DoDelayedWork callback. This method may only be
// used on the thread that called Run.
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0;
// If returned, just use the nsThread.
virtual nsIEventTarget* GetXPCOMThread()
{
return nullptr;
}
protected:
virtual ~MessagePump() {};
};
} // namespace base
#endif // BASE_MESSAGE_PUMP_H_

View file

@ -0,0 +1,125 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_pump_android.h"
#include <fcntl.h>
#include <math.h>
#include "base/eintr_wrapper.h"
#include "base/logging.h"
#include "base/platform_thread.h"
namespace mozilla {
bool ProcessNextEvent();
void NotifyEvent();
}
namespace base {
MessagePumpForUI::MessagePumpForUI()
: state_(NULL)
, pump(*this)
{
}
MessagePumpForUI::~MessagePumpForUI() {
}
MessagePumpAndroid::MessagePumpAndroid(MessagePumpForUI &aPump)
: pump(aPump)
{
}
MessagePumpAndroid::~MessagePumpAndroid()
{
}
void MessagePumpForUI::Run(Delegate* delegate) {
RunState state;
state.delegate = delegate;
state.should_quit = false;
state.run_depth = state_ ? state_->run_depth + 1 : 1;
// We really only do a single task for each iteration of the loop. If we
// have done something, assume there is likely something more to do. This
// will mean that we don't block on the message pump until there was nothing
// more to do. We also set this to true to make sure not to block on the
// first iteration of the loop, so RunAllPending() works correctly.
state.more_work_is_plausible = true;
RunState* previous_state = state_;
state_ = &state;
// We run our own loop instead of using g_main_loop_quit in one of the
// callbacks. This is so we only quit our own loops, and we don't quit
// nested loops run by others. TODO(deanm): Is this what we want?
while (!state_->should_quit) {
mozilla::ProcessNextEvent();
if (work_scheduled) {
work_scheduled = false;
HandleDispatch();
}
}
state_ = previous_state;
}
void MessagePumpForUI::HandleDispatch() {
// We should only ever have a single message on the wakeup pipe, since we
// are only signaled when the queue went from empty to non-empty. The qApp
// poll will tell us whether there was data, so this read shouldn't block.
if (state_->should_quit)
return;
state_->more_work_is_plausible = false;
if (state_->delegate->DoWork())
state_->more_work_is_plausible = true;
if (state_->should_quit)
return;
if (state_->delegate->DoDelayedWork(&delayed_work_time_))
state_->more_work_is_plausible = true;
if (state_->should_quit)
return;
// Don't do idle work if we think there are more important things
// that we could be doing.
if (state_->more_work_is_plausible)
return;
if (state_->delegate->DoIdleWork())
state_->more_work_is_plausible = true;
if (state_->should_quit)
return;
}
void MessagePumpForUI::Quit() {
if (state_) {
state_->should_quit = true;
} else {
NOTREACHED() << "Quit called outside Run!";
}
}
void MessagePumpForUI::ScheduleWork() {
// This can be called on any thread, so we don't want to touch any state
// variables as we would then need locks all over. This ensures that if
// we are sleeping in a poll that we will wake up.
work_scheduled = true;
mozilla::NotifyEvent();
}
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
// We need to wake up the loop in case the poll timeout needs to be
// adjusted. This will cause us to try to do work, but that's ok.
delayed_work_time_ = delayed_work_time;
ScheduleWork();
}
} // namespace base

View file

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MESSAGE_PUMP_ANDROID_H_
#define BASE_MESSAGE_PUMP_ANDROID_H_
#include "base/message_pump.h"
#include "base/time.h"
namespace base {
class MessagePumpForUI;
class MessagePumpAndroid {
public:
MessagePumpAndroid(MessagePumpForUI &pump);
~MessagePumpAndroid();
private:
base::MessagePumpForUI &pump;
};
// This class implements a MessagePump needed for TYPE_UI MessageLoops on
// Android
class MessagePumpForUI : public MessagePump {
public:
MessagePumpForUI();
~MessagePumpForUI();
virtual void Run(Delegate* delegate);
virtual void Quit();
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
// Internal methods used for processing the pump callbacks. They are
// public for simplicity but should not be used directly.
// HandleDispatch is called after the poll has completed.
void HandleDispatch();
private:
// We may make recursive calls to Run, so we save state that needs to be
// separate between them in this structure type.
struct RunState {
Delegate* delegate;
// Used to flag that the current Run() invocation should return ASAP.
bool should_quit;
// Used to count how many Run() invocations are on the stack.
int run_depth;
// Used internally for controlling whether we want a message pump
// iteration to be blocking or not.
bool more_work_is_plausible;
};
RunState* state_;
// This is the time when we need to do delayed work.
TimeTicks delayed_work_time_;
bool work_scheduled;
// MessagePump implementation for Android based on the GLib implement.
MessagePumpAndroid pump;
DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
};
} // namespace base
#endif // BASE_MESSAGE_PUMP_ANDROID_H_

View file

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_pump_default.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/scoped_nsautorelease_pool.h"
#include "GeckoProfiler.h"
#include "mozilla/BackgroundHangMonitor.h"
namespace base {
MessagePumpDefault::MessagePumpDefault()
: keep_running_(true),
event_(false, false) {
}
void MessagePumpDefault::Run(Delegate* delegate) {
DCHECK(keep_running_) << "Quit must have been called outside of Run!";
const MessageLoop* const loop = MessageLoop::current();
mozilla::BackgroundHangMonitor hangMonitor(
loop->thread_name().c_str(),
loop->transient_hang_timeout(),
loop->permanent_hang_timeout());
for (;;) {
ScopedNSAutoreleasePool autorelease_pool;
hangMonitor.NotifyActivity();
bool did_work = delegate->DoWork();
if (!keep_running_)
break;
hangMonitor.NotifyActivity();
did_work |= delegate->DoDelayedWork(&delayed_work_time_);
if (!keep_running_)
break;
if (did_work)
continue;
hangMonitor.NotifyActivity();
did_work = delegate->DoIdleWork();
if (!keep_running_)
break;
if (did_work)
continue;
if (delayed_work_time_.is_null()) {
hangMonitor.NotifyWait();
PROFILER_LABEL("MessagePump", "Wait",
js::ProfileEntry::Category::OTHER);
{
GeckoProfilerSleepRAII profiler_sleep;
event_.Wait();
}
} else {
TimeDelta delay = delayed_work_time_ - TimeTicks::Now();
if (delay > TimeDelta()) {
hangMonitor.NotifyWait();
PROFILER_LABEL("MessagePump", "Wait",
js::ProfileEntry::Category::OTHER);
{
GeckoProfilerSleepRAII profiler_sleep;
event_.TimedWait(delay);
}
} else {
// It looks like delayed_work_time_ indicates a time in the past, so we
// need to call DoDelayedWork now.
delayed_work_time_ = TimeTicks();
}
}
// Since event_ is auto-reset, we don't need to do anything special here
// other than service each delegate method.
}
keep_running_ = true;
}
void MessagePumpDefault::Quit() {
keep_running_ = false;
}
void MessagePumpDefault::ScheduleWork() {
// Since this can be called on any thread, we need to ensure that our Run
// loop wakes up.
event_.Signal();
}
void MessagePumpDefault::ScheduleDelayedWork(
const TimeTicks& delayed_work_time) {
// We know that we can't be blocked on Wait right now since this method can
// only be called on the same thread as Run, so we only need to update our
// record of how long to sleep when we do sleep.
delayed_work_time_ = delayed_work_time;
}
} // namespace base

View file

@ -0,0 +1,43 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MESSAGE_PUMP_DEFAULT_H_
#define BASE_MESSAGE_PUMP_DEFAULT_H_
#include "base/message_pump.h"
#include "base/time.h"
#include "base/waitable_event.h"
namespace base {
class MessagePumpDefault : public MessagePump {
public:
MessagePumpDefault();
~MessagePumpDefault() {}
// MessagePump methods:
virtual void Run(Delegate* delegate);
virtual void Quit();
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
protected:
// This flag is set to false when Run should return.
bool keep_running_;
// Used to sleep until there is more work to do.
WaitableEvent event_;
// The time at which we should call DoDelayedWork.
TimeTicks delayed_work_time_;
private:
DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault);
};
} // namespace base
#endif // BASE_MESSAGE_PUMP_DEFAULT_H_

View file

@ -0,0 +1,341 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_pump_glib.h"
#include <fcntl.h>
#include <math.h>
#include <gtk/gtk.h>
#include <glib.h>
#include "base/eintr_wrapper.h"
#include "base/logging.h"
#include "base/platform_thread.h"
namespace {
// Return a timeout suitable for the glib loop, -1 to block forever,
// 0 to return right away, or a timeout in milliseconds from now.
int GetTimeIntervalMilliseconds(const base::TimeTicks& from) {
if (from.is_null())
return -1;
// Be careful here. TimeDelta has a precision of microseconds, but we want a
// value in milliseconds. If there are 5.5ms left, should the delay be 5 or
// 6? It should be 6 to avoid executing delayed work too early.
int delay = static_cast<int>(
ceil((from - base::TimeTicks::Now()).InMillisecondsF()));
// If this value is negative, then we need to run delayed work soon.
return delay < 0 ? 0 : delay;
}
// A brief refresher on GLib:
// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
// On each iteration of the GLib pump, it calls each source's Prepare function.
// This function should return TRUE if it wants GLib to call its Dispatch, and
// FALSE otherwise. It can also set a timeout in this case for the next time
// Prepare should be called again (it may be called sooner).
// After the Prepare calls, GLib does a poll to check for events from the
// system. File descriptors can be attached to the sources. The poll may block
// if none of the Prepare calls returned TRUE. It will block indefinitely, or
// by the minimum time returned by a source in Prepare.
// After the poll, GLib calls Check for each source that returned FALSE
// from Prepare. The return value of Check has the same meaning as for Prepare,
// making Check a second chance to tell GLib we are ready for Dispatch.
// Finally, GLib calls Dispatch for each source that is ready. If Dispatch
// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive
// (i.e., you can call Run from them), but Prepare and Check cannot.
// Finalize is called when the source is destroyed.
// NOTE: It is common for subsytems to want to process pending events while
// doing intensive work, for example the flash plugin. They usually use the
// following pattern (recommended by the GTK docs):
// while (gtk_events_pending()) {
// gtk_main_iteration();
// }
//
// gtk_events_pending just calls g_main_context_pending, which does the
// following:
// - Call prepare on all the sources.
// - Do the poll with a timeout of 0 (not blocking).
// - Call check on all the sources.
// - *Does not* call dispatch on the sources.
// - Return true if any of prepare() or check() returned true.
//
// gtk_main_iteration just calls g_main_context_iteration, which does the whole
// thing, respecting the timeout for the poll (and block, although it is
// expected not to if gtk_events_pending returned true), and call dispatch.
//
// Thus it is important to only return true from prepare or check if we
// actually have events or work to do. We also need to make sure we keep
// internal state consistent so that if prepare/check return true when called
// from gtk_events_pending, they will still return true when called right
// after, from gtk_main_iteration.
//
// For the GLib pump we try to follow the Windows UI pump model:
// - Whenever we receive a wakeup event or the timer for delayed work expires,
// we run DoWork and/or DoDelayedWork. That part will also run in the other
// event pumps.
// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main
// loop, around event handling.
struct WorkSource : public GSource {
base::MessagePumpForUI* pump;
};
gboolean WorkSourcePrepare(GSource* source,
gint* timeout_ms) {
*timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare();
// We always return FALSE, so that our timeout is honored. If we were
// to return TRUE, the timeout would be considered to be 0 and the poll
// would never block. Once the poll is finished, Check will be called.
return FALSE;
}
gboolean WorkSourceCheck(GSource* source) {
// Only return TRUE if Dispatch should be called.
return static_cast<WorkSource*>(source)->pump->HandleCheck();
}
gboolean WorkSourceDispatch(GSource* source,
GSourceFunc unused_func,
gpointer unused_data) {
static_cast<WorkSource*>(source)->pump->HandleDispatch();
// Always return TRUE so our source stays registered.
return TRUE;
}
// I wish these could be const, but g_source_new wants non-const.
GSourceFuncs WorkSourceFuncs = {
WorkSourcePrepare,
WorkSourceCheck,
WorkSourceDispatch,
NULL
};
} // namespace
namespace base {
MessagePumpForUI::MessagePumpForUI()
: state_(NULL),
context_(g_main_context_default()),
wakeup_gpollfd_(new GPollFD),
pipe_full_(false) {
// Create our wakeup pipe, which is used to flag when work was scheduled.
int fds[2];
CHECK(pipe(fds) == 0);
wakeup_pipe_read_ = fds[0];
wakeup_pipe_write_ = fds[1];
wakeup_gpollfd_->fd = wakeup_pipe_read_;
wakeup_gpollfd_->events = G_IO_IN;
work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
static_cast<WorkSource*>(work_source_)->pump = this;
g_source_add_poll(work_source_, wakeup_gpollfd_.get());
// Use a low priority so that we let other events in the queue go first.
g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE);
// This is needed to allow Run calls inside Dispatch.
g_source_set_can_recurse(work_source_, TRUE);
g_source_attach(work_source_, context_);
gdk_event_handler_set(&EventDispatcher, this, NULL);
}
MessagePumpForUI::~MessagePumpForUI() {
gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event),
this, NULL);
g_source_destroy(work_source_);
g_source_unref(work_source_);
close(wakeup_pipe_read_);
close(wakeup_pipe_write_);
}
void MessagePumpForUI::RunWithDispatcher(Delegate* delegate,
Dispatcher* dispatcher) {
#ifndef NDEBUG
// Make sure we only run this on one thread. GTK only has one message pump
// so we can only have one UI loop per process.
static PlatformThreadId thread_id = PlatformThread::CurrentId();
DCHECK(thread_id == PlatformThread::CurrentId()) <<
"Running MessagePumpForUI on two different threads; "
"this is unsupported by GLib!";
#endif
RunState state;
state.delegate = delegate;
state.dispatcher = dispatcher;
state.should_quit = false;
state.run_depth = state_ ? state_->run_depth + 1 : 1;
state.has_work = false;
RunState* previous_state = state_;
state_ = &state;
// We really only do a single task for each iteration of the loop. If we
// have done something, assume there is likely something more to do. This
// will mean that we don't block on the message pump until there was nothing
// more to do. We also set this to true to make sure not to block on the
// first iteration of the loop, so RunAllPending() works correctly.
bool more_work_is_plausible = true;
// We run our own loop instead of using g_main_loop_quit in one of the
// callbacks. This is so we only quit our own loops, and we don't quit
// nested loops run by others. TODO(deanm): Is this what we want?
for (;;) {
// Don't block if we think we have more work to do.
bool block = !more_work_is_plausible;
// g_main_context_iteration returns true if events have been dispatched.
more_work_is_plausible = g_main_context_iteration(context_, block);
if (state_->should_quit)
break;
more_work_is_plausible |= state_->delegate->DoWork();
if (state_->should_quit)
break;
more_work_is_plausible |=
state_->delegate->DoDelayedWork(&delayed_work_time_);
if (state_->should_quit)
break;
if (more_work_is_plausible)
continue;
more_work_is_plausible = state_->delegate->DoIdleWork();
if (state_->should_quit)
break;
}
state_ = previous_state;
}
// Return the timeout we want passed to poll.
int MessagePumpForUI::HandlePrepare() {
// We know we have work, but we haven't called HandleDispatch yet. Don't let
// the pump block so that we can do some processing.
if (state_ && // state_ may be null during tests.
state_->has_work)
return 0;
// We don't think we have work to do, but make sure not to block
// longer than the next time we need to run delayed work.
return GetTimeIntervalMilliseconds(delayed_work_time_);
}
bool MessagePumpForUI::HandleCheck() {
if (!state_) // state_ may be null during tests.
return false;
// We should only ever have a single message on the wakeup pipe since we only
// write to the pipe when pipe_full_ is false. The glib poll will tell us
// whether there was data, so this read shouldn't block.
if (wakeup_gpollfd_->revents & G_IO_IN) {
pipe_full_ = false;
char msg;
if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') {
NOTREACHED() << "Error reading from the wakeup pipe.";
}
// Since we ate the message, we need to record that we have more work,
// because HandleCheck() may be called without HandleDispatch being called
// afterwards.
state_->has_work = true;
}
if (state_->has_work)
return true;
if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) {
// The timer has expired. That condition will stay true until we process
// that delayed work, so we don't need to record this differently.
return true;
}
return false;
}
void MessagePumpForUI::HandleDispatch() {
state_->has_work = false;
if (state_->delegate->DoWork()) {
// NOTE: on Windows at this point we would call ScheduleWork (see
// MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here,
// instead of posting a message on the wakeup pipe, we can avoid the
// syscalls and just signal that we have more work.
state_->has_work = true;
}
if (state_->should_quit)
return;
state_->delegate->DoDelayedWork(&delayed_work_time_);
}
void MessagePumpForUI::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void MessagePumpForUI::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void MessagePumpForUI::WillProcessEvent(GdkEvent* event) {
FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event));
}
void MessagePumpForUI::DidProcessEvent(GdkEvent* event) {
FOR_EACH_OBSERVER(Observer, observers_, DidProcessEvent(event));
}
void MessagePumpForUI::Quit() {
if (state_) {
state_->should_quit = true;
} else {
NOTREACHED() << "Quit called outside Run!";
}
}
void MessagePumpForUI::ScheduleWork() {
bool was_full = pipe_full_.exchange(true);
if (was_full) {
return;
}
// This can be called on any thread, so we don't want to touch any state
// variables as we would then need locks all over. This ensures that if
// we are sleeping in a poll that we will wake up.
char msg = '!';
if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) {
NOTREACHED() << "Could not write to the UI message loop wakeup pipe!";
}
}
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
// We need to wake up the loop in case the poll timeout needs to be
// adjusted. This will cause us to try to do work, but that's ok.
delayed_work_time_ = delayed_work_time;
ScheduleWork();
}
// static
void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) {
MessagePumpForUI* message_pump = reinterpret_cast<MessagePumpForUI*>(data);
message_pump->WillProcessEvent(event);
if (message_pump->state_ && // state_ may be null during tests.
message_pump->state_->dispatcher) {
if (!message_pump->state_->dispatcher->Dispatch(event))
message_pump->state_->should_quit = true;
} else {
gtk_main_do_event(event);
}
message_pump->DidProcessEvent(event);
}
} // namespace base

View file

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MESSAGE_PUMP_GLIB_H_
#define BASE_MESSAGE_PUMP_GLIB_H_
#include "base/message_pump.h"
#include "base/observer_list.h"
#include "base/time.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Atomics.h"
typedef union _GdkEvent GdkEvent;
typedef struct _GMainContext GMainContext;
typedef struct _GPollFD GPollFD;
typedef struct _GSource GSource;
namespace base {
// This class implements a MessagePump needed for TYPE_UI MessageLoops on
// OS_LINUX platforms using GLib.
class MessagePumpForUI : public MessagePump {
public:
// Observer is notified prior to a GdkEvent event being dispatched. As
// Observers are notified of every change, they have to be FAST!
class Observer {
public:
virtual ~Observer() {}
// This method is called before processing a message.
virtual void WillProcessEvent(GdkEvent* event) = 0;
// This method is called after processing a message.
virtual void DidProcessEvent(GdkEvent* event) = 0;
};
// Dispatcher is used during a nested invocation of Run to dispatch events.
// If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
// dispatch events (or invoke gtk_main_do_event), rather every event is
// passed to Dispatcher's Dispatch method for dispatch. It is up to the
// Dispatcher to dispatch, or not, the event.
//
// The nested loop is exited by either posting a quit, or returning false
// from Dispatch.
class Dispatcher {
public:
virtual ~Dispatcher() {}
// Dispatches the event. If true is returned processing continues as
// normal. If false is returned, the nested loop exits immediately.
virtual bool Dispatch(GdkEvent* event) = 0;
};
MessagePumpForUI();
virtual ~MessagePumpForUI();
// Like MessagePump::Run, but GdkEvent objects are routed through dispatcher.
virtual void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher);
virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); }
virtual void Quit();
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
// Internal methods used for processing the pump callbacks. They are
// public for simplicity but should not be used directly. HandlePrepare
// is called during the prepare step of glib, and returns a timeout that
// will be passed to the poll. HandleCheck is called after the poll
// has completed, and returns whether or not HandleDispatch should be called.
// HandleDispatch is called if HandleCheck returned true.
int HandlePrepare();
bool HandleCheck();
void HandleDispatch();
// Adds an Observer, which will start receiving notifications immediately.
void AddObserver(Observer* observer);
// Removes an Observer. It is safe to call this method while an Observer is
// receiving a notification callback.
void RemoveObserver(Observer* observer);
private:
// We may make recursive calls to Run, so we save state that needs to be
// separate between them in this structure type.
struct RunState {
Delegate* delegate;
Dispatcher* dispatcher;
// Used to flag that the current Run() invocation should return ASAP.
bool should_quit;
// Used to count how many Run() invocations are on the stack.
int run_depth;
// This keeps the state of whether the pump got signaled that there was new
// work to be done. Since we eat the message on the wake up pipe as soon as
// we get it, we keep that state here to stay consistent.
bool has_work;
};
// Invoked from EventDispatcher. Notifies all observers we're about to
// process an event.
void WillProcessEvent(GdkEvent* event);
// Invoked from EventDispatcher. Notifies all observers we processed an
// event.
void DidProcessEvent(GdkEvent* event);
// Callback prior to gdk dispatching an event.
static void EventDispatcher(GdkEvent* event, void* data);
RunState* state_;
// This is a GLib structure that we can add event sources to. We use the
// default GLib context, which is the one to which all GTK events are
// dispatched.
GMainContext* context_;
// This is the time when we need to do delayed work.
TimeTicks delayed_work_time_;
// The work source. It is shared by all calls to Run and destroyed when
// the message pump is destroyed.
GSource* work_source_;
// We use a wakeup pipe to make sure we'll get out of the glib polling phase
// when another thread has scheduled us to do some work. There is a glib
// mechanism g_main_context_wakeup, but this won't guarantee that our event's
// Dispatch() will be called.
int wakeup_pipe_read_;
int wakeup_pipe_write_;
// Use an autoptr to avoid needing the definition of GPollFD in the header.
mozilla::UniquePtr<GPollFD> wakeup_gpollfd_;
mozilla::Atomic<bool> pipe_full_;
// List of observers.
ObserverList<Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
};
} // namespace base
#endif // BASE_MESSAGE_PUMP_GLIB_H_

View file

@ -0,0 +1,458 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_pump_libevent.h"
#include <errno.h>
#include <fcntl.h>
#if defined(ANDROID) || defined(OS_POSIX)
#include <unistd.h>
#endif
#include "eintr_wrapper.h"
#include "base/logging.h"
#include "base/scoped_nsautorelease_pool.h"
#include "base/time.h"
#include "nsDependentSubstring.h"
#include "event.h"
#include "mozilla/UniquePtr.h"
// This macro checks that the _EVENT_SIZEOF_* constants defined in
// ipc/chromiume/src/third_party/<platform>/event2/event-config.h are correct.
#if defined(_EVENT_SIZEOF_SHORT)
#define CHECK_EVENT_SIZEOF(TYPE, type) \
static_assert(_EVENT_SIZEOF_##TYPE == sizeof(type), \
"bad _EVENT_SIZEOF_"#TYPE);
#elif defined(EVENT__SIZEOF_SHORT)
#define CHECK_EVENT_SIZEOF(TYPE, type) \
static_assert(EVENT__SIZEOF_##TYPE == sizeof(type), \
"bad EVENT__SIZEOF_"#TYPE);
#else
#error Cannot find libevent type sizes
#endif
CHECK_EVENT_SIZEOF(LONG, long);
CHECK_EVENT_SIZEOF(LONG_LONG, long long);
CHECK_EVENT_SIZEOF(PTHREAD_T, pthread_t);
CHECK_EVENT_SIZEOF(SHORT, short);
CHECK_EVENT_SIZEOF(SIZE_T, size_t);
CHECK_EVENT_SIZEOF(VOID_P, void*);
// Lifecycle of struct event
// Libevent uses two main data structures:
// struct event_base (of which there is one per message pump), and
// struct event (of which there is roughly one per socket).
// The socket's struct event is created in
// MessagePumpLibevent::WatchFileDescriptor(),
// is owned by the FileDescriptorWatcher, and is destroyed in
// StopWatchingFileDescriptor().
// It is moved into and out of lists in struct event_base by
// the libevent functions event_add() and event_del().
//
// TODO(dkegel):
// At the moment bad things happen if a FileDescriptorWatcher
// is active after its MessagePumpLibevent has been destroyed.
// See MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop
// Not clear yet whether that situation occurs in practice,
// but if it does, we need to fix it.
namespace base {
// Return 0 on success
// Too small a function to bother putting in a library?
static int SetNonBlocking(int fd) {
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
flags = 0;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher()
: is_persistent_(false),
event_(NULL) {
}
MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() {
if (event_) {
StopWatchingFileDescriptor();
}
}
void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e,
bool is_persistent) {
DCHECK(e);
DCHECK(event_ == NULL);
is_persistent_ = is_persistent;
event_ = e;
}
event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() {
struct event *e = event_;
event_ = NULL;
return e;
}
bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() {
event* e = ReleaseEvent();
if (e == NULL)
return true;
// event_del() is a no-op if the event isn't active.
int rv = event_del(e);
delete e;
return (rv == 0);
}
// Called if a byte is received on the wakeup pipe.
void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) {
base::MessagePumpLibevent* that =
static_cast<base::MessagePumpLibevent*>(context);
DCHECK(that->wakeup_pipe_out_ == socket);
// Remove and discard the wakeup byte.
char buf;
int nread = HANDLE_EINTR(read(socket, &buf, 1));
DCHECK_EQ(nread, 1);
// Tell libevent to break out of inner loop.
event_base_loopbreak(that->event_base_);
}
MessagePumpLibevent::MessagePumpLibevent()
: keep_running_(true),
in_run_(false),
event_base_(event_base_new()),
wakeup_pipe_in_(-1),
wakeup_pipe_out_(-1) {
if (!Init())
NOTREACHED();
}
bool MessagePumpLibevent::Init() {
int fds[2];
if (pipe(fds)) {
DLOG(ERROR) << "pipe() failed, errno: " << errno;
return false;
}
if (SetNonBlocking(fds[0])) {
DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno;
return false;
}
if (SetNonBlocking(fds[1])) {
DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno;
return false;
}
wakeup_pipe_out_ = fds[0];
wakeup_pipe_in_ = fds[1];
wakeup_event_ = new event;
event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST,
OnWakeup, this);
event_base_set(event_base_, wakeup_event_);
if (event_add(wakeup_event_, 0))
return false;
return true;
}
MessagePumpLibevent::~MessagePumpLibevent() {
DCHECK(wakeup_event_);
DCHECK(event_base_);
event_del(wakeup_event_);
delete wakeup_event_;
if (wakeup_pipe_in_ >= 0)
close(wakeup_pipe_in_);
if (wakeup_pipe_out_ >= 0)
close(wakeup_pipe_out_);
event_base_free(event_base_);
}
bool MessagePumpLibevent::WatchFileDescriptor(int fd,
bool persistent,
Mode mode,
FileDescriptorWatcher *controller,
Watcher *delegate) {
DCHECK(fd > 0);
DCHECK(controller);
DCHECK(delegate);
DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE);
int event_mask = persistent ? EV_PERSIST : 0;
if ((mode & WATCH_READ) != 0) {
event_mask |= EV_READ;
}
if ((mode & WATCH_WRITE) != 0) {
event_mask |= EV_WRITE;
}
// |should_delete_event| is true if we're modifying an event that's currently
// active in |controller|.
// If we're modifying an existing event and there's an error then we need to
// tell libevent to clean it up via event_delete() before returning.
bool should_delete_event = true;
mozilla::UniquePtr<event> evt(controller->ReleaseEvent());
if (evt.get() == NULL) {
should_delete_event = false;
// Ownership is transferred to the controller.
evt = mozilla::MakeUnique<event>();
} else {
// It's illegal to use this function to listen on 2 separate fds with the
// same |controller|.
if (EVENT_FD(evt.get()) != fd) {
NOTREACHED() << "FDs don't match" << EVENT_FD(evt.get()) << "!=" << fd;
return false;
}
// Make sure we don't pick up any funky internal libevent masks.
int old_interest_mask = evt.get()->ev_events &
(EV_READ | EV_WRITE | EV_PERSIST);
// Combine old/new event masks.
event_mask |= old_interest_mask;
// Must disarm the event before we can reuse it.
event_del(evt.get());
}
// Set current interest mask and message pump for this event.
event_set(evt.get(), fd, event_mask, OnLibeventNotification,
delegate);
// Tell libevent which message pump this socket will belong to when we add it.
if (event_base_set(event_base_, evt.get()) != 0) {
if (should_delete_event) {
event_del(evt.get());
}
return false;
}
// Add this socket to the list of monitored sockets.
if (event_add(evt.get(), NULL) != 0) {
if (should_delete_event) {
event_del(evt.get());
}
return false;
}
// Transfer ownership of evt to controller.
controller->Init(evt.release(), persistent);
return true;
}
void MessagePumpLibevent::OnLibeventNotification(int fd, short flags,
void* context) {
Watcher* watcher = static_cast<Watcher*>(context);
if (flags & EV_WRITE) {
watcher->OnFileCanWriteWithoutBlocking(fd);
}
if (flags & EV_READ) {
watcher->OnFileCanReadWithoutBlocking(fd);
}
}
MessagePumpLibevent::SignalEvent::SignalEvent() :
event_(NULL)
{
}
MessagePumpLibevent::SignalEvent::~SignalEvent()
{
if (event_) {
StopCatching();
}
}
void
MessagePumpLibevent::SignalEvent::Init(event *e)
{
DCHECK(e);
DCHECK(event_ == NULL);
event_ = e;
}
bool
MessagePumpLibevent::SignalEvent::StopCatching()
{
// XXX/cjones: this code could be shared with
// FileDescriptorWatcher. ironic that libevent is "more"
// object-oriented than this C++
event* e = ReleaseEvent();
if (e == NULL)
return true;
// event_del() is a no-op if the event isn't active.
int rv = event_del(e);
delete e;
return (rv == 0);
}
event *
MessagePumpLibevent::SignalEvent::ReleaseEvent()
{
event *e = event_;
event_ = NULL;
return e;
}
bool
MessagePumpLibevent::CatchSignal(int sig,
SignalEvent* sigevent,
SignalWatcher* delegate)
{
DCHECK(sig > 0);
DCHECK(sigevent);
DCHECK(delegate);
// TODO if we want to support re-using SignalEvents, this code needs
// to jump through the same hoops as WatchFileDescriptor(). Not
// needed at present
DCHECK(NULL == sigevent->event_);
mozilla::UniquePtr<event> evt = mozilla::MakeUnique<event>();
signal_set(evt.get(), sig, OnLibeventSignalNotification, delegate);
if (event_base_set(event_base_, evt.get()))
return false;
if (signal_add(evt.get(), NULL))
return false;
// Transfer ownership of evt to controller.
sigevent->Init(evt.release());
return true;
}
void
MessagePumpLibevent::OnLibeventSignalNotification(int sig, short flags,
void* context)
{
DCHECK(sig > 0);
DCHECK(EV_SIGNAL == flags);
DCHECK(context);
reinterpret_cast<SignalWatcher*>(context)->OnSignal(sig);
}
// Reentrant!
void MessagePumpLibevent::Run(Delegate* delegate) {
DCHECK(keep_running_) << "Quit must have been called outside of Run!";
bool old_in_run = in_run_;
in_run_ = true;
for (;;) {
ScopedNSAutoreleasePool autorelease_pool;
bool did_work = delegate->DoWork();
if (!keep_running_)
break;
did_work |= delegate->DoDelayedWork(&delayed_work_time_);
if (!keep_running_)
break;
if (did_work)
continue;
did_work = delegate->DoIdleWork();
if (!keep_running_)
break;
if (did_work)
continue;
// EVLOOP_ONCE tells libevent to only block once,
// but to service all pending events when it wakes up.
if (delayed_work_time_.is_null()) {
event_base_loop(event_base_, EVLOOP_ONCE);
} else {
TimeDelta delay = delayed_work_time_ - TimeTicks::Now();
if (delay > TimeDelta()) {
struct timeval poll_tv;
poll_tv.tv_sec = delay.InSeconds();
poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond;
event_base_loopexit(event_base_, &poll_tv);
event_base_loop(event_base_, EVLOOP_ONCE);
} else {
// It looks like delayed_work_time_ indicates a time in the past, so we
// need to call DoDelayedWork now.
delayed_work_time_ = TimeTicks();
}
}
}
keep_running_ = true;
in_run_ = old_in_run;
}
void MessagePumpLibevent::Quit() {
DCHECK(in_run_);
// Tell both libevent and Run that they should break out of their loops.
keep_running_ = false;
ScheduleWork();
}
void MessagePumpLibevent::ScheduleWork() {
// Tell libevent (in a threadsafe way) that it should break out of its loop.
char buf = 0;
int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1));
DCHECK(nwrite == 1 || errno == EAGAIN)
<< "[nwrite:" << nwrite << "] [errno:" << errno << "]";
}
void MessagePumpLibevent::ScheduleDelayedWork(
const TimeTicks& delayed_work_time) {
// We know that we can't be blocked on Wait right now since this method can
// only be called on the same thread as Run, so we only need to update our
// record of how long to sleep when we do sleep.
delayed_work_time_ = delayed_work_time;
}
void LineWatcher::OnFileCanReadWithoutBlocking(int aFd)
{
ssize_t length = 0;
while (true) {
length = read(aFd, mReceiveBuffer.get(), mBufferSize - mReceivedIndex);
DCHECK(length <= ssize_t(mBufferSize - mReceivedIndex));
if (length <= 0) {
if (length < 0) {
if (errno == EINTR) {
continue; // retry system call when interrupted
}
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return; // no data available: return and re-poll
}
DLOG(ERROR) << "Can't read from fd, error " << errno;
} else {
DLOG(ERROR) << "End of file";
}
// At this point, assume that we can't actually access
// the socket anymore, and indicate an error.
OnError();
mReceivedIndex = 0;
return;
}
while (length-- > 0) {
DCHECK(mReceivedIndex < mBufferSize);
if (mReceiveBuffer[mReceivedIndex] == mTerminator) {
nsDependentCSubstring message(mReceiveBuffer.get(), mReceivedIndex);
OnLineRead(aFd, message);
if (length > 0) {
DCHECK(mReceivedIndex < (mBufferSize - 1));
memmove(&mReceiveBuffer[0], &mReceiveBuffer[mReceivedIndex + 1], length);
}
mReceivedIndex = 0;
} else {
mReceivedIndex++;
}
}
}
}
} // namespace base

View file

@ -0,0 +1,220 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MESSAGE_PUMP_LIBEVENT_H_
#define BASE_MESSAGE_PUMP_LIBEVENT_H_
#include "base/message_pump.h"
#include "base/time.h"
#include "mozilla/UniquePtr.h"
// Declare structs we need from libevent.h rather than including it
struct event_base;
struct event;
class nsDependentCSubstring;
namespace base {
// Class to monitor sockets and issue callbacks when sockets are ready for I/O
// TODO(dkegel): add support for background file IO somehow
class MessagePumpLibevent : public MessagePump {
public:
// Object returned by WatchFileDescriptor to manage further watching.
class FileDescriptorWatcher {
public:
FileDescriptorWatcher();
~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor.
// NOTE: These methods aren't called StartWatching()/StopWatching() to
// avoid confusion with the win32 ObjectWatcher class.
// Stop watching the FD, always safe to call. No-op if there's nothing
// to do.
bool StopWatchingFileDescriptor();
private:
// Called by MessagePumpLibevent, ownership of |e| is transferred to this
// object.
void Init(event* e, bool is_persistent);
// Used by MessagePumpLibevent to take ownership of event_.
event *ReleaseEvent();
friend class MessagePumpLibevent;
private:
bool is_persistent_; // false if this event is one-shot.
event* event_;
DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
};
// Used with WatchFileDescptor to asynchronously monitor the I/O readiness of
// a File Descriptor.
class Watcher {
public:
virtual ~Watcher() {}
// Called from MessageLoop::Run when an FD can be read from/written to
// without blocking
virtual void OnFileCanReadWithoutBlocking(int fd) = 0;
virtual void OnFileCanWriteWithoutBlocking(int fd) = 0;
};
MessagePumpLibevent();
enum Mode {
WATCH_READ = 1 << 0,
WATCH_WRITE = 1 << 1,
WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
};
// Have the current thread's message loop watch for a a situation in which
// reading/writing to the FD can be performed without Blocking.
// Callers must provide a preallocated FileDescriptorWatcher object which
// can later be used to manage the Lifetime of this event.
// If a FileDescriptorWatcher is passed in which is already attached to
// an event, then the effect is cumulative i.e. after the call |controller|
// will watch both the previous event and the new one.
// If an error occurs while calling this method in a cumulative fashion, the
// event previously attached to |controller| is aborted.
// Returns true on success.
// TODO(dkegel): switch to edge-triggered readiness notification
bool WatchFileDescriptor(int fd,
bool persistent,
Mode mode,
FileDescriptorWatcher *controller,
Watcher *delegate);
// This is analagous to FileDescriptorWatcher above, which really is
// just a wrapper around libevent's |struct event|. This class acts
// as a sort of "scoped event watcher" in that it guarantees that
// when this class is out of scope, the signal-event it wraps is
// removed from libevent's guts.
//
// XXX/cjones: this isn't my favorite API, but preserving it in
// order to match code above
class SignalEvent {
friend class MessagePumpLibevent;
public:
SignalEvent();
~SignalEvent(); // implicitly calls StopCatching()
// Have libevent forget this event.
bool StopCatching();
private:
void Init(event* e);
event* ReleaseEvent();
event* event_;
DISALLOW_COPY_AND_ASSIGN(SignalEvent);
};
class SignalWatcher {
public:
virtual ~SignalWatcher() {}
// Called from MessageLoop::Run when |sig| has been delivered to
// this process
virtual void OnSignal(int sig) = 0;
};
// Have the current thread's message loop catch the signal |sig|.
// Multiple watchers can catch the same signal; they're all notified
// upon its delivery. Callers must provide a preallocated
// SignalEvent object which can be used to manage the lifetime of
// this event. Returns true on success.
bool CatchSignal(int sig,
SignalEvent* sigevent,
SignalWatcher* delegate);
// MessagePump methods:
virtual void Run(Delegate* delegate) override;
virtual void Quit() override;
virtual void ScheduleWork() override;
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
protected:
virtual ~MessagePumpLibevent();
private:
// Risky part of constructor. Returns true on success.
bool Init();
// This flag is set to false when Run should return.
bool keep_running_;
// This flag is set when inside Run.
bool in_run_;
// The time at which we should call DoDelayedWork.
TimeTicks delayed_work_time_;
// Libevent dispatcher. Watches all sockets registered with it, and sends
// readiness callbacks when a socket is ready for I/O.
event_base* event_base_;
// Called by libevent to tell us a registered FD can be read/written to.
static void OnLibeventNotification(int fd, short flags,
void* context);
// Called by libevent upon receiving a signal
static void OnLibeventSignalNotification(int sig, short flags,
void* context);
// Unix pipe used to implement ScheduleWork()
// ... callback; called by libevent inside Run() when pipe is ready to read
static void OnWakeup(int socket, short flags, void* context);
// ... write end; ScheduleWork() writes a single byte to it
int wakeup_pipe_in_;
// ... read end; OnWakeup reads it and then breaks Run() out of its sleep
int wakeup_pipe_out_;
// ... libevent wrapper for read end
event* wakeup_event_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent);
};
/**
* LineWatcher overrides OnFileCanReadWithoutBlocking. It separates the read
* data by mTerminator and passes each line to OnLineRead.
*/
class LineWatcher : public MessagePumpLibevent::Watcher
{
public:
LineWatcher(char aTerminator, int aBufferSize) : mReceivedIndex(0),
mBufferSize(aBufferSize),
mTerminator(aTerminator)
{
mReceiveBuffer = mozilla::MakeUnique<char[]>(mBufferSize);
}
~LineWatcher() {}
protected:
/**
* OnError will be called when |read| returns error. Derived class should
* implement this function to handle error cases when needed.
*/
virtual void OnError() {}
virtual void OnLineRead(int aFd, nsDependentCSubstring& aMessage) = 0;
virtual void OnFileCanWriteWithoutBlocking(int /* aFd */) override {}
private:
virtual void OnFileCanReadWithoutBlocking(int aFd) final override;
mozilla::UniquePtr<char[]> mReceiveBuffer;
int mReceivedIndex;
int mBufferSize;
char mTerminator;
};
} // namespace base
#endif // BASE_MESSAGE_PUMP_LIBEVENT_H_

View file

@ -0,0 +1,279 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The basis for all native run loops on the Mac is the CFRunLoop. It can be
// used directly, it can be used as the driving force behind the similar
// Foundation NSRunLoop, and it can be used to implement higher-level event
// loops such as the NSApplication event loop.
//
// This file introduces a basic CFRunLoop-based implementation of the
// MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all
// of the machinery necessary to dispatch events to a delegate, but does not
// implement the specific run loop. Concrete subclasses must provide their
// own DoRun and Quit implementations.
//
// A concrete subclass that just runs a CFRunLoop loop is provided in
// MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop
// is provided.
//
// For the application's event loop, an implementation based on AppKit's
// NSApplication event system is provided in MessagePumpNSApplication.
//
// Typically, MessagePumpNSApplication only makes sense on a Cocoa
// application's main thread. If a CFRunLoop-based message pump is needed on
// any other thread, one of the other concrete subclasses is preferrable.
// MessagePumpMac::Create is defined, which returns a new NSApplication-based
// or NSRunLoop-based MessagePump subclass depending on which thread it is
// called on.
#ifndef BASE_MESSAGE_PUMP_MAC_H_
#define BASE_MESSAGE_PUMP_MAC_H_
#include "base/message_pump.h"
#include "base/basictypes.h"
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#if defined(__OBJC__)
@class NSAutoreleasePool;
#else // defined(__OBJC__)
class NSAutoreleasePool;
#endif // defined(__OBJC__)
namespace base {
class TimeTicks;
class MessagePumpCFRunLoopBase : public MessagePump {
// Needs access to CreateAutoreleasePool.
friend class MessagePumpScopedAutoreleasePool;
public:
MessagePumpCFRunLoopBase();
virtual ~MessagePumpCFRunLoopBase();
// Subclasses should implement the work they need to do in MessagePump::Run
// in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly.
// This arrangement is used because MessagePumpCFRunLoopBase needs to set
// up and tear down things before and after the "meat" of DoRun.
virtual void Run(Delegate* delegate);
virtual void DoRun(Delegate* delegate) = 0;
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
protected:
// Accessors for private data members to be used by subclasses.
CFRunLoopRef run_loop() const { return run_loop_; }
int nesting_level() const { return nesting_level_; }
int run_nesting_level() const { return run_nesting_level_; }
// Return an autorelease pool to wrap around any work being performed.
// In some cases, CreateAutoreleasePool may return nil intentionally to
// preventing an autorelease pool from being created, allowing any
// objects autoreleased by work to fall into the current autorelease pool.
virtual NSAutoreleasePool* CreateAutoreleasePool();
private:
// Timer callback scheduled by ScheduleDelayedWork. This does not do any
// work, but it signals delayed_work_source_ so that delayed work can be
// performed within the appropriate priority constraints.
static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info);
// Perform highest-priority work. This is associated with work_source_
// signalled by ScheduleWork. The static method calls the instance method;
// the instance method returns true if work was done.
static void RunWorkSource(void* info);
bool RunWork();
// Perform delayed-priority work. This is associated with
// delayed_work_source_ signalled by RunDelayedWorkTimer, and is responsible
// for calling ScheduleDelayedWork again if appropriate. The static method
// calls the instance method; the instance method returns true if more
// delayed work is available.
static void RunDelayedWorkSource(void* info);
bool RunDelayedWork();
// Perform idle-priority work. This is normally called by PreWaitObserver,
// but is also associated with idle_work_source_. When this function
// actually does perform idle work, it will resignal that source. The
// static method calls the instance method; the instance method returns
// true if idle work was done.
static void RunIdleWorkSource(void* info);
bool RunIdleWork();
// Perform work that may have been deferred because it was not runnable
// within a nested run loop. This is associated with
// nesting_deferred_work_source_ and is signalled by
// MaybeScheduleNestingDeferredWork when returning from a nested loop,
// so that an outer loop will be able to perform the necessary tasks if it
// permits nestable tasks.
static void RunNestingDeferredWorkSource(void* info);
bool RunNestingDeferredWork();
// Schedules possible nesting-deferred work to be processed before the run
// loop goes to sleep, exits, or begins processing sources at the top of its
// loop. If this function detects that a nested loop had run since the
// previous attempt to schedule nesting-deferred work, it will schedule a
// call to RunNestingDeferredWorkSource.
void MaybeScheduleNestingDeferredWork();
// Observer callback responsible for performing idle-priority work, before
// the run loop goes to sleep. Associated with idle_work_observer_.
static void PreWaitObserver(CFRunLoopObserverRef observer,
CFRunLoopActivity activity, void* info);
// Observer callback called before the run loop processes any sources.
// Associated with pre_source_observer_.
static void PreSourceObserver(CFRunLoopObserverRef observer,
CFRunLoopActivity activity, void* info);
// Observer callback called when the run loop starts and stops, at the
// beginning and end of calls to CFRunLoopRun. This is used to maintain
// nesting_level_. Associated with enter_exit_observer_.
static void EnterExitObserver(CFRunLoopObserverRef observer,
CFRunLoopActivity activity, void* info);
// Called by EnterExitObserver after performing maintenance on nesting_level_.
// This allows subclasses an opportunity to perform additional processing on
// the basis of run loops starting and stopping.
virtual void EnterExitRunLoop(CFRunLoopActivity activity);
// IOKit power state change notification callback, called when the system
// enters and leaves the sleep state.
static void PowerStateNotification(void* info, io_service_t service,
uint32_t message_type,
void* message_argument);
// The thread's run loop.
CFRunLoopRef run_loop_;
// The timer, sources, and observers are described above alongside their
// callbacks.
CFRunLoopTimerRef delayed_work_timer_;
CFRunLoopSourceRef work_source_;
CFRunLoopSourceRef delayed_work_source_;
CFRunLoopSourceRef idle_work_source_;
CFRunLoopSourceRef nesting_deferred_work_source_;
CFRunLoopObserverRef pre_wait_observer_;
CFRunLoopObserverRef pre_source_observer_;
CFRunLoopObserverRef enter_exit_observer_;
// Objects used for power state notification. See PowerStateNotification.
io_connect_t root_power_domain_;
IONotificationPortRef power_notification_port_;
io_object_t power_notification_object_;
// (weak) Delegate passed as an argument to the innermost Run call.
Delegate* delegate_;
// The time that delayed_work_timer_ is scheduled to fire. This is tracked
// independently of CFRunLoopTimerGetNextFireDate(delayed_work_timer_)
// to be able to reset the timer properly after waking from system sleep.
// See PowerStateNotification.
CFAbsoluteTime delayed_work_fire_time_;
// The recursion depth of the currently-executing CFRunLoopRun loop on the
// run loop's thread. 0 if no run loops are running inside of whatever scope
// the object was created in.
int nesting_level_;
// The recursion depth (calculated in the same way as nesting_level_) of the
// innermost executing CFRunLoopRun loop started by a call to Run.
int run_nesting_level_;
// The deepest (numerically highest) recursion depth encountered since the
// most recent attempt to run nesting-deferred work.
int deepest_nesting_level_;
// "Delegateless" work flags are set when work is ready to be performed but
// must wait until a delegate is available to process it. This can happen
// when a MessagePumpCFRunLoopBase is instantiated and work arrives without
// any call to Run on the stack. The Run method will check for delegateless
// work on entry and redispatch it as needed once a delegate is available.
bool delegateless_work_;
bool delegateless_delayed_work_;
bool delegateless_idle_work_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase);
};
class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase {
public:
MessagePumpCFRunLoop();
virtual void DoRun(Delegate* delegate);
virtual void Quit();
private:
virtual void EnterExitRunLoop(CFRunLoopActivity activity);
// True if Quit is called to stop the innermost MessagePump
// (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_)
// is running inside the MessagePump's innermost Run call.
bool quit_pending_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop);
};
class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase {
public:
MessagePumpNSRunLoop();
virtual ~MessagePumpNSRunLoop();
virtual void DoRun(Delegate* delegate);
virtual void Quit();
private:
// A source that doesn't do anything but provide something signalable
// attached to the run loop. This source will be signalled when Quit
// is called, to cause the loop to wake up so that it can stop.
CFRunLoopSourceRef quit_source_;
// False after Quit is called.
bool keep_running_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop);
};
class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
public:
MessagePumpNSApplication();
virtual void DoRun(Delegate* delegate);
virtual void Quit();
protected:
// Returns nil if NSApp is currently in the middle of calling -sendEvent.
virtual NSAutoreleasePool* CreateAutoreleasePool();
private:
// False after Quit is called.
bool keep_running_;
// True if DoRun is managing its own run loop as opposed to letting
// -[NSApplication run] handle it. The outermost run loop in the application
// is managed by -[NSApplication run], inner run loops are handled by a loop
// in DoRun.
bool running_own_loop_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication);
};
class MessagePumpMac {
public:
// Returns a new instance of MessagePumpNSApplication if called on the main
// thread. Otherwise, returns a new instance of MessagePumpNSRunLoop.
static MessagePump* Create();
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac);
};
} // namespace base
#endif // BASE_MESSAGE_PUMP_MAC_H_

View file

@ -0,0 +1,752 @@
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_pump_mac.h"
#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#include <IOKit/IOMessage.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include <limits>
#import "base/chrome_application_mac.h"
#include "base/logging.h"
#include "base/time.h"
namespace {
void NoOp(void* info) {
}
const CFTimeInterval kCFTimeIntervalMax =
std::numeric_limits<CFTimeInterval>::max();
} // namespace
namespace base {
// A scoper for autorelease pools created from message pump run loops.
// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
// case where an autorelease pool needs to be passed in.
class MessagePumpScopedAutoreleasePool {
public:
explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
pool_(pump->CreateAutoreleasePool()) {
}
~MessagePumpScopedAutoreleasePool() {
[pool_ drain];
}
private:
NSAutoreleasePool* pool_;
DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
};
// Must be called on the run loop thread.
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
: delegate_(NULL),
delayed_work_fire_time_(kCFTimeIntervalMax),
nesting_level_(0),
run_nesting_level_(0),
deepest_nesting_level_(0),
delegateless_work_(false),
delegateless_delayed_work_(false),
delegateless_idle_work_(false) {
run_loop_ = CFRunLoopGetCurrent();
CFRetain(run_loop_);
// Set a repeating timer with a preposterous firing time and interval. The
// timer will effectively never fire as-is. The firing time will be adjusted
// as needed when ScheduleDelayedWork is called.
CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
timer_context.info = this;
delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator
kCFTimeIntervalMax, // fire time
kCFTimeIntervalMax, // interval
0, // flags
0, // priority
RunDelayedWorkTimer,
&timer_context);
CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
source_context.info = this;
source_context.perform = RunWorkSource;
work_source_ = CFRunLoopSourceCreate(NULL, // allocator
1, // priority
&source_context);
CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes);
source_context.perform = RunDelayedWorkSource;
delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
2, // priority
&source_context);
CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
source_context.perform = RunIdleWorkSource;
idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
3, // priority
&source_context);
CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
source_context.perform = RunNestingDeferredWorkSource;
nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
0, // priority
&source_context);
CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_,
kCFRunLoopCommonModes);
CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
observer_context.info = this;
pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopBeforeWaiting,
true, // repeat
0, // priority
PreWaitObserver,
&observer_context);
CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes);
pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopBeforeSources,
true, // repeat
0, // priority
PreSourceObserver,
&observer_context);
CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes);
enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopEntry |
kCFRunLoopExit,
true, // repeat
0, // priority
EnterExitObserver,
&observer_context);
CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes);
root_power_domain_ = IORegisterForSystemPower(this,
&power_notification_port_,
PowerStateNotification,
&power_notification_object_);
if (root_power_domain_ != MACH_PORT_NULL) {
CFRunLoopAddSource(
run_loop_,
IONotificationPortGetRunLoopSource(power_notification_port_),
kCFRunLoopCommonModes);
}
}
// Ideally called on the run loop thread. If other run loops were running
// lower on the run loop thread's stack when this object was created, the
// same number of run loops must be running when this object is destroyed.
MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
if (root_power_domain_ != MACH_PORT_NULL) {
CFRunLoopRemoveSource(
run_loop_,
IONotificationPortGetRunLoopSource(power_notification_port_),
kCFRunLoopCommonModes);
IODeregisterForSystemPower(&power_notification_object_);
IOServiceClose(root_power_domain_);
IONotificationPortDestroy(power_notification_port_);
}
CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
kCFRunLoopCommonModes);
CFRelease(enter_exit_observer_);
CFRunLoopRemoveObserver(run_loop_, pre_source_observer_,
kCFRunLoopCommonModes);
CFRelease(pre_source_observer_);
CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_,
kCFRunLoopCommonModes);
CFRelease(pre_wait_observer_);
CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_,
kCFRunLoopCommonModes);
CFRelease(nesting_deferred_work_source_);
CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
CFRelease(idle_work_source_);
CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes);
CFRelease(delayed_work_source_);
CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
CFRelease(work_source_);
CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
CFRelease(delayed_work_timer_);
CFRelease(run_loop_);
}
// Must be called on the run loop thread.
void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
// nesting_level_ will be incremented in EnterExitRunLoop, so set
// run_nesting_level_ accordingly.
int last_run_nesting_level = run_nesting_level_;
run_nesting_level_ = nesting_level_ + 1;
Delegate* last_delegate = delegate_;
delegate_ = delegate;
if (delegate) {
// If any work showed up but could not be dispatched for want of a
// delegate, set it up for dispatch again now that a delegate is
// available.
if (delegateless_work_) {
CFRunLoopSourceSignal(work_source_);
delegateless_work_ = false;
}
if (delegateless_delayed_work_) {
CFRunLoopSourceSignal(delayed_work_source_);
delegateless_delayed_work_ = false;
}
if (delegateless_idle_work_) {
CFRunLoopSourceSignal(idle_work_source_);
delegateless_idle_work_ = false;
}
}
DoRun(delegate);
// Restore the previous state of the object.
delegate_ = last_delegate;
run_nesting_level_ = last_run_nesting_level;
}
// May be called on any thread.
void MessagePumpCFRunLoopBase::ScheduleWork() {
CFRunLoopSourceSignal(work_source_);
CFRunLoopWakeUp(run_loop_);
}
// Must be called on the run loop thread.
void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
const TimeTicks& delayed_work_time) {
TimeDelta delta = delayed_work_time - TimeTicks::Now();
delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
// The timer won't fire again until it's reset.
self->delayed_work_fire_time_ = kCFTimeIntervalMax;
// CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources.
// In order to establish the proper priority where delegate_->DoDelayedWork
// can only be called if delegate_->DoWork returns false, the timer used
// to schedule delayed work must signal a CFRunLoopSource set at a lower
// priority than the one used for delegate_->DoWork.
CFRunLoopSourceSignal(self->delayed_work_source_);
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
self->RunWork();
}
// Called by MessagePumpCFRunLoopBase::RunWorkSource.
bool MessagePumpCFRunLoopBase::RunWork() {
if (!delegate_) {
// This point can be reached with a NULL delegate_ if Run is not on the
// stack but foreign code is spinning the CFRunLoop. Arrange to come back
// here when a delegate is available.
delegateless_work_ = true;
return false;
}
// The NSApplication-based run loop only drains the autorelease pool at each
// UI event (NSEvent). The autorelease pool is not drained for each
// CFRunLoopSource target that's run. Use a local pool for any autoreleased
// objects if the app is not currently handling a UI event to ensure they're
// released promptly even in the absence of UI events.
MessagePumpScopedAutoreleasePool autorelease_pool(this);
// Call DoWork once, and if something was done, arrange to come back here
// again as long as the loop is still running.
bool did_work = delegate_->DoWork();
if (did_work) {
CFRunLoopSourceSignal(work_source_);
}
return did_work;
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::RunDelayedWorkSource(void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
self->RunDelayedWork();
}
// Called by MessagePumpCFRunLoopBase::RunDelayedWorkSource.
bool MessagePumpCFRunLoopBase::RunDelayedWork() {
if (!delegate_) {
// This point can be reached with a NULL delegate_ if Run is not on the
// stack but foreign code is spinning the CFRunLoop. Arrange to come back
// here when a delegate is available.
delegateless_delayed_work_ = true;
return false;
}
// The NSApplication-based run loop only drains the autorelease pool at each
// UI event (NSEvent). The autorelease pool is not drained for each
// CFRunLoopSource target that's run. Use a local pool for any autoreleased
// objects if the app is not currently handling a UI event to ensure they're
// released promptly even in the absence of UI events.
MessagePumpScopedAutoreleasePool autorelease_pool(this);
TimeTicks next_time;
delegate_->DoDelayedWork(&next_time);
bool more_work = !next_time.is_null();
if (more_work) {
TimeDelta delay = next_time - TimeTicks::Now();
if (delay > TimeDelta()) {
// There's more delayed work to be done in the future.
ScheduleDelayedWork(next_time);
} else {
// There's more delayed work to be done, and its time is in the past.
// Arrange to come back here directly as long as the loop is still
// running.
CFRunLoopSourceSignal(delayed_work_source_);
}
}
return more_work;
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
self->RunIdleWork();
}
// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
bool MessagePumpCFRunLoopBase::RunIdleWork() {
if (!delegate_) {
// This point can be reached with a NULL delegate_ if Run is not on the
// stack but foreign code is spinning the CFRunLoop. Arrange to come back
// here when a delegate is available.
delegateless_idle_work_ = true;
return false;
}
// The NSApplication-based run loop only drains the autorelease pool at each
// UI event (NSEvent). The autorelease pool is not drained for each
// CFRunLoopSource target that's run. Use a local pool for any autoreleased
// objects if the app is not currently handling a UI event to ensure they're
// released promptly even in the absence of UI events.
MessagePumpScopedAutoreleasePool autorelease_pool(this);
// Call DoIdleWork once, and if something was done, arrange to come back here
// again as long as the loop is still running.
bool did_work = delegate_->DoIdleWork();
if (did_work) {
CFRunLoopSourceSignal(idle_work_source_);
}
return did_work;
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
self->RunNestingDeferredWork();
}
// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
bool MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
if (!delegate_) {
// This point can be reached with a NULL delegate_ if Run is not on the
// stack but foreign code is spinning the CFRunLoop. There's no sense in
// attempting to do any work or signalling the work sources because
// without a delegate, work is not possible.
return false;
}
// Immediately try work in priority order.
if (!RunWork()) {
if (!RunDelayedWork()) {
if (!RunIdleWork()) {
return false;
}
} else {
// There was no work, and delayed work was done. Arrange for the loop
// to try non-nestable idle work on a subsequent pass.
CFRunLoopSourceSignal(idle_work_source_);
}
} else {
// Work was done. Arrange for the loop to try non-nestable delayed and
// idle work on a subsequent pass.
CFRunLoopSourceSignal(delayed_work_source_);
CFRunLoopSourceSignal(idle_work_source_);
}
return true;
}
// Called before the run loop goes to sleep or exits, or processes sources.
void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
// deepest_nesting_level_ is set as run loops are entered. If the deepest
// level encountered is deeper than the current level, a nested loop
// (relative to the current level) ran since the last time nesting-deferred
// work was scheduled. When that situation is encountered, schedule
// nesting-deferred work in case any work was deferred because nested work
// was disallowed.
if (deepest_nesting_level_ > nesting_level_) {
deepest_nesting_level_ = nesting_level_;
CFRunLoopSourceSignal(nesting_deferred_work_source_);
}
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
CFRunLoopActivity activity,
void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
// Attempt to do some idle work before going to sleep.
self->RunIdleWork();
// The run loop is about to go to sleep. If any of the work done since it
// started or woke up resulted in a nested run loop running,
// nesting-deferred work may have accumulated. Schedule it for processing
// if appropriate.
self->MaybeScheduleNestingDeferredWork();
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
CFRunLoopActivity activity,
void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
// The run loop has reached the top of the loop and is about to begin
// processing sources. If the last iteration of the loop at this nesting
// level did not sleep or exit, nesting-deferred work may have accumulated
// if a nested loop ran. Schedule nesting-deferred work for processing if
// appropriate.
self->MaybeScheduleNestingDeferredWork();
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
CFRunLoopActivity activity,
void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
switch (activity) {
case kCFRunLoopEntry:
++self->nesting_level_;
if (self->nesting_level_ > self->deepest_nesting_level_) {
self->deepest_nesting_level_ = self->nesting_level_;
}
break;
case kCFRunLoopExit:
// Not all run loops go to sleep. If a run loop is stopped before it
// goes to sleep due to a CFRunLoopStop call, or if the timeout passed
// to CFRunLoopRunInMode expires, the run loop may proceed directly from
// handling sources to exiting without any sleep. This most commonly
// occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
// to make a single pass through the loop and exit without sleep. Some
// native loops use CFRunLoop in this way. Because PreWaitObserver will
// not be called in these case, MaybeScheduleNestingDeferredWork needs
// to be called here, as the run loop exits.
//
// MaybeScheduleNestingDeferredWork consults self->nesting_level_
// to determine whether to schedule nesting-deferred work. It expects
// the nesting level to be set to the depth of the loop that is going
// to sleep or exiting. It must be called before decrementing the
// value so that the value still corresponds to the level of the exiting
// loop.
self->MaybeScheduleNestingDeferredWork();
--self->nesting_level_;
break;
default:
break;
}
self->EnterExitRunLoop(activity);
}
// Called from the run loop.
// static
void MessagePumpCFRunLoopBase::PowerStateNotification(void* info,
io_service_t service,
uint32_t message_type,
void* message_argument) {
// CFRunLoopTimer (NSTimer) is scheduled in terms of CFAbsoluteTime, which
// measures the number of seconds since 2001-01-01 00:00:00.0 Z. It is
// implemented in terms of kernel ticks, as in mach_absolute_time. While an
// offset and scale factor can be applied to convert between the two time
// bases at any time after boot, the kernel clock stops while the system is
// asleep, altering the offset. (The offset will also change when the
// real-time clock is adjusted.) CFRunLoopTimers are not readjusted to take
// this into account when the system wakes up, so any timers that were
// pending while the system was asleep will be delayed by the sleep
// duration.
//
// The MessagePump interface assumes that scheduled delayed work will be
// performed at the time ScheduleDelayedWork was asked to perform it. The
// delay caused by the CFRunLoopTimer not firing at the appropriate time
// results in a stall of queued delayed work when the system wakes up.
// With this limitation, scheduled work would not be performed until
// (system wake time + scheduled work time - system sleep time), while it
// would be expected to be performed at (scheduled work time).
//
// To work around this problem, when the system wakes up from sleep, if a
// delayed work timer is pending, it is rescheduled to fire at the original
// time that it was scheduled to fire.
//
// This mechanism is not resilient if the real-time clock does not maintain
// stable time while the system is sleeping, but it matches the behavior of
// the various other MessagePump implementations, and MessageLoop seems to
// be limited in the same way.
//
// References
// - Chris Kane, "NSTimer and deep sleep," cocoa-dev@lists.apple.com,
// http://lists.apple.com/archives/Cocoa-dev/2002/May/msg01547.html
// - Apple Technical Q&A QA1340, "Registering and unregistering for sleep
// and wake notifications,"
// http://developer.apple.com/mac/library/qa/qa2004/qa1340.html
// - Core Foundation source code, CF-550/CFRunLoop.c and CF-550/CFDate.c,
// http://www.opensource.apple.com/
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
switch (message_type) {
case kIOMessageSystemWillPowerOn:
if (self->delayed_work_fire_time_ != kCFTimeIntervalMax) {
CFRunLoopTimerSetNextFireDate(self->delayed_work_timer_,
self->delayed_work_fire_time_);
}
break;
case kIOMessageSystemWillSleep:
case kIOMessageCanSystemSleep:
// The system will wait for 30 seconds before entering sleep if neither
// IOAllowPowerChange nor IOCancelPowerChange are called. That would be
// pretty antisocial.
IOAllowPowerChange(self->root_power_domain_,
reinterpret_cast<long>(message_argument));
break;
default:
break;
}
}
// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
// implementation is a no-op.
void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
}
// Base version returns a standard NSAutoreleasePool.
NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
return [[NSAutoreleasePool alloc] init];
}
MessagePumpCFRunLoop::MessagePumpCFRunLoop()
: quit_pending_(false) {
}
// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were
// running lower on the run loop thread's stack when this object was created,
// the same number of CFRunLoopRun loops must be running for the outermost call
// to Run. Run/DoRun are reentrant after that point.
void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
// This is completely identical to calling CFRunLoopRun(), except autorelease
// pool management is introduced.
int result;
do {
MessagePumpScopedAutoreleasePool autorelease_pool(this);
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
kCFTimeIntervalMax,
false);
} while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
}
// Must be called on the run loop thread.
void MessagePumpCFRunLoop::Quit() {
// Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
if (nesting_level() == run_nesting_level()) {
// This object is running the innermost loop, just stop it.
CFRunLoopStop(run_loop());
} else {
// There's another loop running inside the loop managed by this object.
// In other words, someone else called CFRunLoopRunInMode on the same
// thread, deeper on the stack than the deepest Run call. Don't preempt
// other run loops, just mark this object to quit the innermost Run as
// soon as the other inner loops not managed by Run are done.
quit_pending_ = true;
}
}
// Called by MessagePumpCFRunLoopBase::EnterExitObserver.
void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
if (activity == kCFRunLoopExit &&
nesting_level() == run_nesting_level() &&
quit_pending_) {
// Quit was called while loops other than those managed by this object
// were running further inside a run loop managed by this object. Now
// that all unmanaged inner run loops are gone, stop the loop running
// just inside Run.
CFRunLoopStop(run_loop());
quit_pending_ = false;
}
}
MessagePumpNSRunLoop::MessagePumpNSRunLoop()
: keep_running_(true) {
CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
source_context.perform = NoOp;
quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
0, // priority
&source_context);
CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
}
MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
CFRelease(quit_source_);
}
void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
while (keep_running_) {
// NSRunLoop manages autorelease pools itself.
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
keep_running_ = true;
}
void MessagePumpNSRunLoop::Quit() {
keep_running_ = false;
CFRunLoopSourceSignal(quit_source_);
CFRunLoopWakeUp(run_loop());
}
MessagePumpNSApplication::MessagePumpNSApplication()
: keep_running_(true),
running_own_loop_(false) {
}
void MessagePumpNSApplication::DoRun(Delegate* delegate) {
bool last_running_own_loop_ = running_own_loop_;
// TODO(dmaclach): Get rid of this gratuitous sharedApplication.
// Tests should be setting up their applications on their own.
[CrApplication sharedApplication];
if (![NSApp isRunning]) {
running_own_loop_ = false;
// NSApplication manages autorelease pools itself when run this way.
[NSApp run];
} else {
running_own_loop_ = true;
NSDate* distant_future = [NSDate distantFuture];
while (keep_running_) {
MessagePumpScopedAutoreleasePool autorelease_pool(this);
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
untilDate:distant_future
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event) {
[NSApp sendEvent:event];
}
}
keep_running_ = true;
}
running_own_loop_ = last_running_own_loop_;
}
void MessagePumpNSApplication::Quit() {
if (!running_own_loop_) {
[NSApp stop:nil];
} else {
keep_running_ = false;
}
// Send a fake event to wake the loop up.
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0, 0)
modifierFlags:0
timestamp:0
windowNumber:0
context:NULL
subtype:0
data1:0
data2:0]
atStart:NO];
}
// Prevents an autorelease pool from being created if the app is in the midst of
// handling a UI event because various parts of AppKit depend on objects that
// are created while handling a UI event to be autoreleased in the event loop.
// An example of this is NSWindowController. When a window with a window
// controller is closed it goes through a stack like this:
// (Several stack frames elided for clarity)
//
// #0 [NSWindowController autorelease]
// #1 DoAClose
// #2 MessagePumpCFRunLoopBase::DoWork()
// #3 [NSRunLoop run]
// #4 [NSButton performClick:]
// #5 [NSWindow sendEvent:]
// #6 [NSApp sendEvent:]
// #7 [NSApp run]
//
// -performClick: spins a nested run loop. If the pool created in DoWork was a
// standard NSAutoreleasePool, it would release the objects that were
// autoreleased into it once DoWork released it. This would cause the window
// controller, which autoreleased itself in frame #0, to release itself, and
// possibly free itself. Unfortunately this window controller controls the
// window in frame #5. When the stack is unwound to frame #5, the window would
// no longer exists and crashes may occur. Apple gets around this by never
// releasing the pool it creates in frame #4, and letting frame #7 clean it up
// when it cleans up the pool that wraps frame #7. When an autorelease pool is
// released it releases all other pools that were created after it on the
// autorelease pool stack.
//
// CrApplication is responsible for setting handlingSendEvent to true just
// before it sends the event throught the event handling mechanism, and
// returning it to its previous value once the event has been sent.
NSAutoreleasePool* MessagePumpNSApplication::CreateAutoreleasePool() {
NSAutoreleasePool* pool = nil;
DCHECK([NSApp isKindOfClass:[CrApplication class]]);
if (![static_cast<CrApplication*>(NSApp) isHandlingSendEvent]) {
pool = MessagePumpCFRunLoopBase::CreateAutoreleasePool();
}
return pool;
}
// static
MessagePump* MessagePumpMac::Create() {
if ([NSThread isMainThread]) {
return new MessagePumpNSApplication;
}
return new MessagePumpNSRunLoop;
}
} // namespace base

View file

@ -0,0 +1,191 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <qabstracteventdispatcher.h>
#include <qevent.h>
#include <QCoreApplication>
#include <QThread>
#include <qtimer.h>
#include "base/message_pump_qt.h"
#include <fcntl.h>
#include <limits>
#include <math.h>
#include "base/eintr_wrapper.h"
#include "base/logging.h"
#include "base/platform_thread.h"
namespace {
// Cached QEvent user type, registered for our event system
static int sPokeEvent;
} // namespace
namespace base {
MessagePumpForUI::MessagePumpForUI()
: state_(NULL),
qt_pump(*this)
{
}
MessagePumpForUI::~MessagePumpForUI() {
}
MessagePumpQt::MessagePumpQt(MessagePumpForUI &aPump)
: pump(aPump), mTimer(new QTimer(this))
{
// Register our custom event type, to use in qApp event loop
sPokeEvent = QEvent::registerEventType();
connect(mTimer, SIGNAL(timeout()), this, SLOT(dispatchDelayed()));
mTimer->setSingleShot(true);
}
MessagePumpQt::~MessagePumpQt()
{
mTimer->stop();
delete mTimer;
}
bool
MessagePumpQt::event(QEvent *e)
{
if (e->type() == sPokeEvent) {
pump.HandleDispatch();
return true;
}
return false;
}
void
MessagePumpQt::scheduleDelayedIfNeeded(const TimeTicks& delayed_work_time)
{
if (delayed_work_time.is_null()) {
return;
}
if (mTimer->isActive()) {
mTimer->stop();
}
TimeDelta later = delayed_work_time - TimeTicks::Now();
// later.InMilliseconds() returns an int64_t, QTimer only accepts int's for start(),
// std::min only works on exact same types.
int laterMsecs = later.InMilliseconds() > std::numeric_limits<int>::max() ?
std::numeric_limits<int>::max() : later.InMilliseconds();
mTimer->start(laterMsecs > 0 ? laterMsecs : 0);
}
void
MessagePumpQt::dispatchDelayed()
{
pump.HandleDispatch();
}
void MessagePumpForUI::Run(Delegate* delegate) {
RunState state;
state.delegate = delegate;
state.should_quit = false;
state.run_depth = state_ ? state_->run_depth + 1 : 1;
// We really only do a single task for each iteration of the loop. If we
// have done something, assume there is likely something more to do. This
// will mean that we don't block on the message pump until there was nothing
// more to do. We also set this to true to make sure not to block on the
// first iteration of the loop, so RunAllPending() works correctly.
bool more_work_is_plausible = true;
RunState* previous_state = state_;
state_ = &state;
for(;;) {
QEventLoop::ProcessEventsFlags block = QEventLoop::AllEvents;
if (!more_work_is_plausible) {
block |= QEventLoop::WaitForMoreEvents;
}
QAbstractEventDispatcher* dispatcher =
QAbstractEventDispatcher::instance(QThread::currentThread());
// An assertion seems too much here, as during startup,
// the dispatcher might not be ready yet.
if (!dispatcher) {
return;
}
// processEvent's returns true if an event has been processed.
more_work_is_plausible = dispatcher->processEvents(block);
if (state_->should_quit) {
break;
}
more_work_is_plausible |= state_->delegate->DoWork();
if (state_->should_quit) {
break;
}
more_work_is_plausible |=
state_->delegate->DoDelayedWork(&delayed_work_time_);
if (state_->should_quit) {
break;
}
qt_pump.scheduleDelayedIfNeeded(delayed_work_time_);
if (more_work_is_plausible) {
continue;
}
more_work_is_plausible = state_->delegate->DoIdleWork();
if (state_->should_quit) {
break;
}
}
state_ = previous_state;
}
void MessagePumpForUI::HandleDispatch() {
if (state_->should_quit) {
return;
}
if (state_->delegate->DoWork()) {
// there might be more, see more_work_is_plausible
// variable above, that's why we ScheduleWork() to keep going.
ScheduleWork();
}
if (state_->should_quit) {
return;
}
state_->delegate->DoDelayedWork(&delayed_work_time_);
qt_pump.scheduleDelayedIfNeeded(delayed_work_time_);
}
void MessagePumpForUI::Quit() {
if (state_) {
state_->should_quit = true;
} else {
NOTREACHED() << "Quit called outside Run!";
}
}
void MessagePumpForUI::ScheduleWork() {
QCoreApplication::postEvent(&qt_pump,
new QEvent((QEvent::Type) sPokeEvent));
}
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
// On GLib implementation, a work source is defined which explicitly checks the
// time that has passed. Here, on Qt we can use a QTimer that enqueues our
// event signal in an event queue.
delayed_work_time_ = delayed_work_time;
qt_pump.scheduleDelayedIfNeeded(delayed_work_time_);
}
} // namespace base

View file

@ -0,0 +1,85 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MESSAGE_PUMP_QT_H_
#define BASE_MESSAGE_PUMP_QT_H_
#include <qobject.h>
#include "base/message_pump.h"
#include "base/time.h"
class QTimer;
namespace base {
class MessagePumpForUI;
class MessagePumpQt : public QObject {
Q_OBJECT
public:
MessagePumpQt(MessagePumpForUI &pump);
~MessagePumpQt();
virtual bool event (QEvent *e);
void scheduleDelayedIfNeeded(const TimeTicks& delayed_work_time);
public Q_SLOTS:
void dispatchDelayed();
private:
base::MessagePumpForUI &pump;
QTimer* mTimer;
};
// This class implements a MessagePump needed for TYPE_UI MessageLoops on
// OS_LINUX platforms using QApplication event loop
class MessagePumpForUI : public MessagePump {
public:
MessagePumpForUI();
~MessagePumpForUI();
virtual void Run(Delegate* delegate);
virtual void Quit();
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
// Internal methods used for processing the pump callbacks. They are
// public for simplicity but should not be used directly.
// HandleDispatch is called after the poll has completed.
void HandleDispatch();
private:
// We may make recursive calls to Run, so we save state that needs to be
// separate between them in this structure type.
struct RunState {
Delegate* delegate;
// Used to flag that the current Run() invocation should return ASAP.
bool should_quit;
// Used to count how many Run() invocations are on the stack.
int run_depth;
};
RunState* state_;
// This is the time when we need to do delayed work.
TimeTicks delayed_work_time_;
// MessagePump implementation for Qt based on the GLib implement.
// On Qt we use a QObject base class and the
// default qApp in order to process events through QEventLoop.
MessagePumpQt qt_pump;
DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
};
} // namespace base
#endif // BASE_MESSAGE_PUMP_QT_H_

View file

@ -0,0 +1,547 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/message_pump_win.h"
#include <math.h>
#include "base/message_loop.h"
#include "base/histogram.h"
#include "base/win_util.h"
#include "WinUtils.h"
using base::Time;
namespace base {
static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow";
// Message sent to get an additional time slice for pumping (processing) another
// task (a series of such messages creates a continuous task pump).
static const int kMsgHaveWork = WM_USER + 1;
//-----------------------------------------------------------------------------
// MessagePumpWin public:
void MessagePumpWin::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void MessagePumpWin::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void MessagePumpWin::WillProcessMessage(const MSG& msg) {
FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg));
}
void MessagePumpWin::DidProcessMessage(const MSG& msg) {
FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg));
}
void MessagePumpWin::RunWithDispatcher(
Delegate* delegate, Dispatcher* dispatcher) {
RunState s;
s.delegate = delegate;
s.dispatcher = dispatcher;
s.should_quit = false;
s.run_depth = state_ ? state_->run_depth + 1 : 1;
RunState* previous_state = state_;
state_ = &s;
DoRunLoop();
state_ = previous_state;
}
void MessagePumpWin::Quit() {
DCHECK(state_);
state_->should_quit = true;
}
//-----------------------------------------------------------------------------
// MessagePumpWin protected:
int MessagePumpWin::GetCurrentDelay() const {
if (delayed_work_time_.is_null())
return -1;
// Be careful here. TimeDelta has a precision of microseconds, but we want a
// value in milliseconds. If there are 5.5ms left, should the delay be 5 or
// 6? It should be 6 to avoid executing delayed work too early.
double timeout =
ceil((delayed_work_time_ - TimeTicks::Now()).InMillisecondsF());
// If this value is negative, then we need to run delayed work soon.
int delay = static_cast<int>(timeout);
if (delay < 0)
delay = 0;
return delay;
}
//-----------------------------------------------------------------------------
// MessagePumpForUI public:
MessagePumpForUI::MessagePumpForUI() {
InitMessageWnd();
}
MessagePumpForUI::~MessagePumpForUI() {
DestroyWindow(message_hwnd_);
UnregisterClass(kWndClass, GetModuleHandle(NULL));
}
void MessagePumpForUI::ScheduleWork() {
if (InterlockedExchange(&have_work_, 1))
return; // Someone else continued the pumping.
// Make sure the MessagePump does some work for us.
PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast<WPARAM>(this), 0);
// In order to wake up any cross-process COM calls which may currently be
// pending on the main thread, we also have to post a UI message.
PostMessage(message_hwnd_, WM_NULL, 0, 0);
}
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
//
// We would *like* to provide high resolution timers. Windows timers using
// SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup
// mechanism because the application can enter modal windows loops where it
// is not running our MessageLoop; the only way to have our timers fire in
// these cases is to post messages there.
//
// To provide sub-10ms timers, we process timers directly from our run loop.
// For the common case, timers will be processed there as the run loop does
// its normal work. However, we *also* set the system timer so that WM_TIMER
// events fire. This mops up the case of timers not being able to work in
// modal message loops. It is possible for the SetTimer to pop and have no
// pending timers, because they could have already been processed by the
// run loop itself.
//
// We use a single SetTimer corresponding to the timer that will expire
// soonest. As new timers are created and destroyed, we update SetTimer.
// Getting a spurrious SetTimer event firing is benign, as we'll just be
// processing an empty timer queue.
//
delayed_work_time_ = delayed_work_time;
int delay_msec = GetCurrentDelay();
DCHECK(delay_msec >= 0);
if (delay_msec < USER_TIMER_MINIMUM)
delay_msec = USER_TIMER_MINIMUM;
// Create a WM_TIMER event that will wake us up to check for any pending
// timers (in case we are running within a nested, external sub-pump).
SetTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this), delay_msec, NULL);
}
void MessagePumpForUI::PumpOutPendingPaintMessages() {
// If we are being called outside of the context of Run, then don't try to do
// any work.
if (!state_)
return;
// Create a mini-message-pump to force immediate processing of only Windows
// WM_PAINT messages. Don't provide an infinite loop, but do enough peeking
// to get the job done. Actual common max is 4 peeks, but we'll be a little
// safe here.
const int kMaxPeekCount = 20;
int peek_count;
for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
MSG msg;
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
break;
ProcessMessageHelper(msg);
if (state_->should_quit) // Handle WM_QUIT.
break;
}
// Histogram what was really being used, to help to adjust kMaxPeekCount.
DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count);
}
//-----------------------------------------------------------------------------
// MessagePumpForUI private:
// static
LRESULT CALLBACK MessagePumpForUI::WndProcThunk(
HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {
switch (message) {
case kMsgHaveWork:
reinterpret_cast<MessagePumpForUI*>(wparam)->HandleWorkMessage();
break;
case WM_TIMER:
reinterpret_cast<MessagePumpForUI*>(wparam)->HandleTimerMessage();
break;
}
return DefWindowProc(hwnd, message, wparam, lparam);
}
void MessagePumpForUI::DoRunLoop() {
// IF this was just a simple PeekMessage() loop (servicing all possible work
// queues), then Windows would try to achieve the following order according
// to MSDN documentation about PeekMessage with no filter):
// * Sent messages
// * Posted messages
// * Sent messages (again)
// * WM_PAINT messages
// * WM_TIMER messages
//
// Summary: none of the above classes is starved, and sent messages has twice
// the chance of being processed (i.e., reduced service time).
for (;;) {
// If we do any work, we may create more messages etc., and more work may
// possibly be waiting in another task group. When we (for example)
// ProcessNextWindowsMessage(), there is a good chance there are still more
// messages waiting. On the other hand, when any of these methods return
// having done no work, then it is pretty unlikely that calling them again
// quickly will find any work to do. Finally, if they all say they had no
// work, then it is a good time to consider sleeping (waiting) for more
// work.
bool more_work_is_plausible = ProcessNextWindowsMessage();
if (state_->should_quit)
break;
more_work_is_plausible |= state_->delegate->DoWork();
if (state_->should_quit)
break;
more_work_is_plausible |=
state_->delegate->DoDelayedWork(&delayed_work_time_);
// If we did not process any delayed work, then we can assume that our
// existing WM_TIMER if any will fire when delayed work should run. We
// don't want to disturb that timer if it is already in flight. However,
// if we did do all remaining delayed work, then lets kill the WM_TIMER.
if (more_work_is_plausible && delayed_work_time_.is_null())
KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
if (state_->should_quit)
break;
if (more_work_is_plausible)
continue;
more_work_is_plausible = state_->delegate->DoIdleWork();
if (state_->should_quit)
break;
if (more_work_is_plausible)
continue;
WaitForWork(); // Wait (sleep) until we have work to do again.
}
}
void MessagePumpForUI::InitMessageWnd() {
HINSTANCE hinst = GetModuleHandle(NULL);
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(wc);
wc.lpfnWndProc = WndProcThunk;
wc.hInstance = hinst;
wc.lpszClassName = kWndClass;
RegisterClassEx(&wc);
message_hwnd_ =
CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0);
DCHECK(message_hwnd_);
}
void MessagePumpForUI::WaitForWork() {
// Wait until a message is available, up to the time needed by the timer
// manager to fire the next set of timers.
int delay = GetCurrentDelay();
if (delay < 0) // Negative value means no timers waiting.
delay = INFINITE;
mozilla::widget::WinUtils::WaitForMessage(delay);
}
void MessagePumpForUI::HandleWorkMessage() {
// If we are being called outside of the context of Run, then don't try to do
// any work. This could correspond to a MessageBox call or something of that
// sort.
if (!state_) {
// Since we handled a kMsgHaveWork message, we must still update this flag.
InterlockedExchange(&have_work_, 0);
return;
}
// Let whatever would have run had we not been putting messages in the queue
// run now. This is an attempt to make our dummy message not starve other
// messages that may be in the Windows message queue.
ProcessPumpReplacementMessage();
// Now give the delegate a chance to do some work. He'll let us know if he
// needs to do more work.
if (state_->delegate->DoWork())
ScheduleWork();
}
void MessagePumpForUI::HandleTimerMessage() {
KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
// If we are being called outside of the context of Run, then don't do
// anything. This could correspond to a MessageBox call or something of
// that sort.
if (!state_)
return;
state_->delegate->DoDelayedWork(&delayed_work_time_);
if (!delayed_work_time_.is_null()) {
// A bit gratuitous to set delayed_work_time_ again, but oh well.
ScheduleDelayedWork(delayed_work_time_);
}
}
bool MessagePumpForUI::ProcessNextWindowsMessage() {
// If there are sent messages in the queue then PeekMessage internally
// dispatches the message and returns false. We return true in this
// case to ensure that the message loop peeks again instead of calling
// MsgWaitForMultipleObjectsEx again.
bool sent_messages_in_queue = false;
DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE);
if (HIWORD(queue_status) & QS_SENDMESSAGE)
sent_messages_in_queue = true;
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
return ProcessMessageHelper(msg);
return sent_messages_in_queue;
}
bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
if (WM_QUIT == msg.message) {
// Repost the QUIT message so that it will be retrieved by the primary
// GetMessage() loop.
state_->should_quit = true;
PostQuitMessage(static_cast<int>(msg.wParam));
return false;
}
// While running our main message pump, we discard kMsgHaveWork messages.
if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
return ProcessPumpReplacementMessage();
WillProcessMessage(msg);
if (state_->dispatcher) {
if (!state_->dispatcher->Dispatch(msg))
state_->should_quit = true;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
DidProcessMessage(msg);
return true;
}
bool MessagePumpForUI::ProcessPumpReplacementMessage() {
// When we encounter a kMsgHaveWork message, this method is called to peek
// and process a replacement message, such as a WM_PAINT or WM_TIMER. The
// goal is to make the kMsgHaveWork as non-intrusive as possible, even though
// a continuous stream of such messages are posted. This method carefully
// peeks a message while there is no chance for a kMsgHaveWork to be pending,
// then resets the have_work_ flag (allowing a replacement kMsgHaveWork to
// possibly be posted), and finally dispatches that peeked replacement. Note
// that the re-post of kMsgHaveWork may be asynchronous to this thread!!
MSG msg;
bool have_message = false;
if (MessageLoop::current()->os_modal_loop()) {
// We only peek out WM_PAINT and WM_TIMER here for reasons mentioned above.
have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
} else {
have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
if (have_message && msg.message == WM_NULL)
have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
}
DCHECK(!have_message || kMsgHaveWork != msg.message ||
msg.hwnd != message_hwnd_);
// Since we discarded a kMsgHaveWork message, we must update the flag.
int old_have_work = InterlockedExchange(&have_work_, 0);
DCHECK(old_have_work);
// We don't need a special time slice if we didn't have_message to process.
if (!have_message)
return false;
// Guarantee we'll get another time slice in the case where we go into native
// windows code. This ScheduleWork() may hurt performance a tiny bit when
// tasks appear very infrequently, but when the event queue is busy, the
// kMsgHaveWork events get (percentage wise) rarer and rarer.
ScheduleWork();
return ProcessMessageHelper(msg);
}
//-----------------------------------------------------------------------------
// MessagePumpForIO public:
MessagePumpForIO::MessagePumpForIO() {
port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1));
DCHECK(port_.IsValid());
}
void MessagePumpForIO::ScheduleWork() {
if (InterlockedExchange(&have_work_, 1))
return; // Someone else continued the pumping.
// Make sure the MessagePump does some work for us.
BOOL ret = PostQueuedCompletionStatus(port_, 0,
reinterpret_cast<ULONG_PTR>(this),
reinterpret_cast<OVERLAPPED*>(this));
DCHECK(ret);
}
void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
// We know that we can't be blocked right now since this method can only be
// called on the same thread as Run, so we only need to update our record of
// how long to sleep when we do sleep.
delayed_work_time_ = delayed_work_time;
}
void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle,
IOHandler* handler) {
ULONG_PTR key = reinterpret_cast<ULONG_PTR>(handler);
HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1);
DCHECK(port == port_.Get());
}
//-----------------------------------------------------------------------------
// MessagePumpForIO private:
void MessagePumpForIO::DoRunLoop() {
for (;;) {
// If we do any work, we may create more messages etc., and more work may
// possibly be waiting in another task group. When we (for example)
// WaitForIOCompletion(), there is a good chance there are still more
// messages waiting. On the other hand, when any of these methods return
// having done no work, then it is pretty unlikely that calling them
// again quickly will find any work to do. Finally, if they all say they
// had no work, then it is a good time to consider sleeping (waiting) for
// more work.
bool more_work_is_plausible = state_->delegate->DoWork();
if (state_->should_quit)
break;
more_work_is_plausible |= WaitForIOCompletion(0, NULL);
if (state_->should_quit)
break;
more_work_is_plausible |=
state_->delegate->DoDelayedWork(&delayed_work_time_);
if (state_->should_quit)
break;
if (more_work_is_plausible)
continue;
more_work_is_plausible = state_->delegate->DoIdleWork();
if (state_->should_quit)
break;
if (more_work_is_plausible)
continue;
WaitForWork(); // Wait (sleep) until we have work to do again.
}
}
// Wait until IO completes, up to the time needed by the timer manager to fire
// the next set of timers.
void MessagePumpForIO::WaitForWork() {
// We do not support nested IO message loops. This is to avoid messy
// recursion problems.
DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!";
int timeout = GetCurrentDelay();
if (timeout < 0) // Negative value means no timers waiting.
timeout = INFINITE;
WaitForIOCompletion(timeout, NULL);
}
bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
IOItem item;
if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) {
// We have to ask the system for another IO completion.
if (!GetIOItem(timeout, &item))
return false;
if (ProcessInternalIOItem(item))
return true;
}
if (item.context->handler) {
if (filter && item.handler != filter) {
// Save this item for later
completed_io_.push_back(item);
} else {
DCHECK(item.context->handler == item.handler);
item.handler->OnIOCompleted(item.context, item.bytes_transfered,
item.error);
}
} else {
// The handler must be gone by now, just cleanup the mess.
delete item.context;
}
return true;
}
// Asks the OS for another IO completion result.
bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) {
memset(item, 0, sizeof(*item));
ULONG_PTR key = 0;
OVERLAPPED* overlapped = NULL;
if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key,
&overlapped, timeout)) {
if (!overlapped)
return false; // Nothing in the queue.
item->error = GetLastError();
item->bytes_transfered = 0;
}
item->handler = reinterpret_cast<IOHandler*>(key);
item->context = reinterpret_cast<IOContext*>(overlapped);
return true;
}
bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) {
if (this == reinterpret_cast<MessagePumpForIO*>(item.context) &&
this == reinterpret_cast<MessagePumpForIO*>(item.handler)) {
// This is our internal completion.
DCHECK(!item.bytes_transfered);
InterlockedExchange(&have_work_, 0);
return true;
}
return false;
}
// Returns a completion item that was previously received.
bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) {
DCHECK(!completed_io_.empty());
for (std::list<IOItem>::iterator it = completed_io_.begin();
it != completed_io_.end(); ++it) {
if (!filter || it->handler == filter) {
*item = *it;
completed_io_.erase(it);
return true;
}
}
return false;
}
} // namespace base

View file

@ -0,0 +1,348 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_MESSAGE_PUMP_WIN_H_
#define BASE_MESSAGE_PUMP_WIN_H_
#include <windows.h>
#include <list>
#include "base/lock.h"
#include "base/message_pump.h"
#include "base/observer_list.h"
#include "base/scoped_handle.h"
#include "base/time.h"
namespace base {
// MessagePumpWin serves as the base for specialized versions of the MessagePump
// for Windows. It provides basic functionality like handling of observers and
// controlling the lifetime of the message pump.
class MessagePumpWin : public MessagePump {
public:
// An Observer is an object that receives global notifications from the
// MessageLoop.
//
// NOTE: An Observer implementation should be extremely fast!
//
class Observer {
public:
virtual ~Observer() {}
// This method is called before processing a message.
// The message may be undefined in which case msg.message is 0
virtual void WillProcessMessage(const MSG& msg) = 0;
// This method is called when control returns from processing a UI message.
// The message may be undefined in which case msg.message is 0
virtual void DidProcessMessage(const MSG& msg) = 0;
};
// Dispatcher is used during a nested invocation of Run to dispatch events.
// If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
// dispatch events (or invoke TranslateMessage), rather every message is
// passed to Dispatcher's Dispatch method for dispatch. It is up to the
// Dispatcher to dispatch, or not, the event.
//
// The nested loop is exited by either posting a quit, or returning false
// from Dispatch.
class Dispatcher {
public:
virtual ~Dispatcher() {}
// Dispatches the event. If true is returned processing continues as
// normal. If false is returned, the nested loop exits immediately.
virtual bool Dispatch(const MSG& msg) = 0;
};
MessagePumpWin() : have_work_(0), state_(NULL) {}
virtual ~MessagePumpWin() {}
// Add an Observer, which will start receiving notifications immediately.
void AddObserver(Observer* observer);
// Remove an Observer. It is safe to call this method while an Observer is
// receiving a notification callback.
void RemoveObserver(Observer* observer);
// Give a chance to code processing additional messages to notify the
// message loop observers that another message has been processed.
void WillProcessMessage(const MSG& msg);
void DidProcessMessage(const MSG& msg);
// Like MessagePump::Run, but MSG objects are routed through dispatcher.
void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher);
// MessagePump methods:
virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); }
virtual void Quit();
protected:
struct RunState {
Delegate* delegate;
Dispatcher* dispatcher;
// Used to flag that the current Run() invocation should return ASAP.
bool should_quit;
// Used to count how many Run() invocations are on the stack.
int run_depth;
};
virtual void DoRunLoop() = 0;
int GetCurrentDelay() const;
ObserverList<Observer> observers_;
// The time at which delayed work should run.
TimeTicks delayed_work_time_;
// A boolean value used to indicate if there is a kMsgDoWork message pending
// in the Windows Message queue. There is at most one such message, and it
// can drive execution of tasks when a native message pump is running.
LONG have_work_;
// State for the current invocation of Run.
RunState* state_;
};
//-----------------------------------------------------------------------------
// MessagePumpForUI extends MessagePumpWin with methods that are particular to a
// MessageLoop instantiated with TYPE_UI.
//
// MessagePumpForUI implements a "traditional" Windows message pump. It contains
// a nearly infinite loop that peeks out messages, and then dispatches them.
// Intermixed with those peeks are callouts to DoWork for pending tasks, and
// DoDelayedWork for pending timers. When there are no events to be serviced,
// this pump goes into a wait state. In most cases, this message pump handles
// all processing.
//
// However, when a task, or windows event, invokes on the stack a native dialog
// box or such, that window typically provides a bare bones (native?) message
// pump. That bare-bones message pump generally supports little more than a
// peek of the Windows message queue, followed by a dispatch of the peeked
// message. MessageLoop extends that bare-bones message pump to also service
// Tasks, at the cost of some complexity.
//
// The basic structure of the extension (refered to as a sub-pump) is that a
// special message, kMsgHaveWork, is repeatedly injected into the Windows
// Message queue. Each time the kMsgHaveWork message is peeked, checks are
// made for an extended set of events, including the availability of Tasks to
// run.
//
// After running a task, the special message kMsgHaveWork is again posted to
// the Windows Message queue, ensuring a future time slice for processing a
// future event. To prevent flooding the Windows Message queue, care is taken
// to be sure that at most one kMsgHaveWork message is EVER pending in the
// Window's Message queue.
//
// There are a few additional complexities in this system where, when there are
// no Tasks to run, this otherwise infinite stream of messages which drives the
// sub-pump is halted. The pump is automatically re-started when Tasks are
// queued.
//
// A second complexity is that the presence of this stream of posted tasks may
// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER.
// Such paint and timer events always give priority to a posted message, such as
// kMsgHaveWork messages. As a result, care is taken to do some peeking in
// between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork
// is peeked, and before a replacement kMsgHaveWork is posted).
//
// NOTE: Although it may seem odd that messages are used to start and stop this
// flow (as opposed to signaling objects, etc.), it should be understood that
// the native message pump will *only* respond to messages. As a result, it is
// an excellent choice. It is also helpful that the starter messages that are
// placed in the queue when new task arrive also awakens DoRunLoop.
//
class MessagePumpForUI : public MessagePumpWin {
public:
MessagePumpForUI();
virtual ~MessagePumpForUI();
// MessagePump methods:
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
// Applications can call this to encourage us to process all pending WM_PAINT
// messages. This method will process all paint messages the Windows Message
// queue can provide, up to some fixed number (to avoid any infinite loops).
void PumpOutPendingPaintMessages();
protected:
virtual void DoRunLoop();
bool ProcessNextWindowsMessage();
void InitMessageWnd();
void WaitForWork();
void HandleWorkMessage();
void HandleTimerMessage();
bool ProcessMessageHelper(const MSG& msg);
bool ProcessPumpReplacementMessage();
// A hidden message-only window.
HWND message_hwnd_;
private:
static LRESULT CALLBACK WndProcThunk(
HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
};
//-----------------------------------------------------------------------------
// MessagePumpForIO extends MessagePumpWin with methods that are particular to a
// MessageLoop instantiated with TYPE_IO. This version of MessagePump does not
// deal with Windows mesagges, and instead has a Run loop based on Completion
// Ports so it is better suited for IO operations.
//
class MessagePumpForIO : public MessagePumpWin {
public:
struct IOContext;
// Clients interested in receiving OS notifications when asynchronous IO
// operations complete should implement this interface and register themselves
// with the message pump.
//
// Typical use #1:
// // Use only when there are no user's buffers involved on the actual IO,
// // so that all the cleanup can be done by the message pump.
// class MyFile : public IOHandler {
// MyFile() {
// ...
// context_ = new IOContext;
// context_->handler = this;
// message_pump->RegisterIOHandler(file_, this);
// }
// ~MyFile() {
// if (pending_) {
// // By setting the handler to NULL, we're asking for this context
// // to be deleted when received, without calling back to us.
// context_->handler = NULL;
// } else {
// delete context_;
// }
// }
// virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered,
// DWORD error) {
// pending_ = false;
// }
// void DoSomeIo() {
// ...
// // The only buffer required for this operation is the overlapped
// // structure.
// ConnectNamedPipe(file_, &context_->overlapped);
// pending_ = true;
// }
// bool pending_;
// IOContext* context_;
// HANDLE file_;
// };
//
// Typical use #2:
// class MyFile : public IOHandler {
// MyFile() {
// ...
// message_pump->RegisterIOHandler(file_, this);
// }
// // Plus some code to make sure that this destructor is not called
// // while there are pending IO operations.
// ~MyFile() {
// }
// virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered,
// DWORD error) {
// ...
// delete context;
// }
// void DoSomeIo() {
// ...
// IOContext* context = new IOContext;
// // This is not used for anything. It just prevents the context from
// // being considered "abandoned".
// context->handler = this;
// ReadFile(file_, buffer, num_bytes, &read, &context->overlapped);
// }
// HANDLE file_;
// };
//
// Typical use #3:
// Same as the previous example, except that in order to deal with the
// requirement stated for the destructor, the class calls WaitForIOCompletion
// from the destructor to block until all IO finishes.
// ~MyFile() {
// while(pending_)
// message_pump->WaitForIOCompletion(INFINITE, this);
// }
//
class IOHandler {
public:
virtual ~IOHandler() {}
// This will be called once the pending IO operation associated with
// |context| completes. |error| is the Win32 error code of the IO operation
// (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero
// on error.
virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered,
DWORD error) = 0;
};
// The extended context that should be used as the base structure on every
// overlapped IO operation. |handler| must be set to the registered IOHandler
// for the given file when the operation is started, and it can be set to NULL
// before the operation completes to indicate that the handler should not be
// called anymore, and instead, the IOContext should be deleted when the OS
// notifies the completion of this operation. Please remember that any buffers
// involved with an IO operation should be around until the callback is
// received, so this technique can only be used for IO that do not involve
// additional buffers (other than the overlapped structure itself).
struct IOContext {
OVERLAPPED overlapped;
IOHandler* handler;
};
MessagePumpForIO();
virtual ~MessagePumpForIO() {}
// MessagePump methods:
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
// Register the handler to be used when asynchronous IO for the given file
// completes. The registration persists as long as |file_handle| is valid, so
// |handler| must be valid as long as there is pending IO for the given file.
void RegisterIOHandler(HANDLE file_handle, IOHandler* handler);
// Waits for the next IO completion that should be processed by |filter|, for
// up to |timeout| milliseconds. Return true if any IO operation completed,
// regardless of the involved handler, and false if the timeout expired. If
// the completion port received any message and the involved IO handler
// matches |filter|, the callback is called before returning from this code;
// if the handler is not the one that we are looking for, the callback will
// be postponed for another time, so reentrancy problems can be avoided.
// External use of this method should be reserved for the rare case when the
// caller is willing to allow pausing regular task dispatching on this thread.
bool WaitForIOCompletion(DWORD timeout, IOHandler* filter);
private:
struct IOItem {
IOHandler* handler;
IOContext* context;
DWORD bytes_transfered;
DWORD error;
};
virtual void DoRunLoop();
void WaitForWork();
bool MatchCompletedIOItem(IOHandler* filter, IOItem* item);
bool GetIOItem(DWORD timeout, IOItem* item);
bool ProcessInternalIOItem(const IOItem& item);
// The completion port associated with this thread.
ScopedHandle port_;
// This list will be empty almost always. It stores IO completions that have
// not been delivered yet because somebody was doing cleanup.
std::list<IOItem> completed_io_;
};
} // namespace base
#endif // BASE_MESSAGE_PUMP_WIN_H_

View file

@ -0,0 +1,137 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/object_watcher.h"
#include "base/logging.h"
namespace base {
//-----------------------------------------------------------------------------
class ObjectWatcher::Watch : public mozilla::Runnable {
public:
ObjectWatcher* watcher; // The associated ObjectWatcher instance
HANDLE object; // The object being watched
HANDLE wait_object; // Returned by RegisterWaitForSingleObject
MessageLoop* origin_loop; // Used to get back to the origin thread
Delegate* delegate; // Delegate to notify when signaled
bool did_signal; // DoneWaiting was called
NS_IMETHOD Run() override {
// The watcher may have already been torn down, in which case we need to
// just get out of dodge.
if (!watcher)
return NS_OK;
DCHECK(did_signal);
watcher->StopWatching();
delegate->OnObjectSignaled(object);
return NS_OK;
}
};
//-----------------------------------------------------------------------------
ObjectWatcher::ObjectWatcher() : watch_(nullptr) {
}
ObjectWatcher::~ObjectWatcher() {
StopWatching();
}
bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
if (watch_) {
NOTREACHED() << "Already watching an object";
return false;
}
RefPtr<Watch> watch = new Watch;
watch->watcher = this;
watch->object = object;
watch->origin_loop = MessageLoop::current();
watch->delegate = delegate;
watch->did_signal = false;
// Since our job is to just notice when an object is signaled and report the
// result back to this thread, we can just run on a Windows wait thread.
DWORD wait_flags = WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE;
if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting,
watch.get(), INFINITE, wait_flags)) {
NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError();
return false;
}
watch_ = watch.forget();
// We need to know if the current message loop is going away so we can
// prevent the wait thread from trying to access a dead message loop.
MessageLoop::current()->AddDestructionObserver(this);
return true;
}
bool ObjectWatcher::StopWatching() {
if (!watch_)
return false;
// Make sure ObjectWatcher is used in a single-threaded fashion.
DCHECK(watch_->origin_loop == MessageLoop::current());
// If DoneWaiting is in progress, we wait for it to finish. We know whether
// DoneWaiting happened or not by inspecting the did_signal flag.
if (!UnregisterWaitEx(watch_->wait_object, INVALID_HANDLE_VALUE)) {
NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError();
return false;
}
// Make sure that we see any mutation to did_signal. This should be a no-op
// since we expect that UnregisterWaitEx resulted in a memory barrier, but
// just to be sure, we're going to be explicit.
MemoryBarrier();
// If the watch has been posted, then we need to make sure it knows not to do
// anything once it is run.
watch_->watcher = NULL;
watch_ = nullptr;
MessageLoop::current()->RemoveDestructionObserver(this);
return true;
}
HANDLE ObjectWatcher::GetWatchedObject() {
if (!watch_)
return NULL;
return watch_->object;
}
// static
void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
DCHECK(!timed_out);
Watch* watch = static_cast<Watch*>(param);
RefPtr<Watch> addrefedWatch = watch;
// Record that we ran this function.
watch->did_signal = true;
// We rely on the locking in PostTask() to ensure that a memory barrier is
// provided, which in turn ensures our change to did_signal can be observed
// on the target thread.
watch->origin_loop->PostTask(addrefedWatch.forget());
}
void ObjectWatcher::WillDestroyCurrentMessageLoop() {
// Need to shutdown the watch so that we don't try to access the MessageLoop
// after this point.
StopWatching();
}
} // namespace base

View file

@ -0,0 +1,96 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_OBJECT_WATCHER_H_
#define BASE_OBJECT_WATCHER_H_
#include <windows.h>
#ifdef GetClassName
#undef GetClassName
#endif
#include "base/message_loop.h"
namespace base {
// A class that provides a means to asynchronously wait for a Windows object to
// become signaled. It is an abstraction around RegisterWaitForSingleObject
// that provides a notification callback, OnObjectSignaled, that runs back on
// the origin thread (i.e., the thread that called StartWatching).
//
// This class acts like a smart pointer such that when it goes out-of-scope,
// UnregisterWaitEx is automatically called, and any in-flight notification is
// suppressed.
//
// Typical usage:
//
// class MyClass : public base::ObjectWatcher::Delegate {
// public:
// void DoStuffWhenSignaled(HANDLE object) {
// watcher_.StartWatching(object, this);
// }
// virtual void OnObjectSignaled(HANDLE object) {
// // OK, time to do stuff!
// }
// private:
// base::ObjectWatcher watcher_;
// };
//
// In the above example, MyClass wants to "do stuff" when object becomes
// signaled. ObjectWatcher makes this task easy. When MyClass goes out of
// scope, the watcher_ will be destroyed, and there is no need to worry about
// OnObjectSignaled being called on a deleted MyClass pointer. Easy!
//
class ObjectWatcher : public MessageLoop::DestructionObserver {
public:
class Delegate {
public:
virtual ~Delegate() {}
// Called from the MessageLoop when a signaled object is detected. To
// continue watching the object, AddWatch must be called again.
virtual void OnObjectSignaled(HANDLE object) = 0;
};
ObjectWatcher();
~ObjectWatcher();
// When the object is signaled, the given delegate is notified on the thread
// where StartWatching is called. The ObjectWatcher is not responsible for
// deleting the delegate.
//
// Returns true if the watch was started. Otherwise, false is returned.
//
bool StartWatching(HANDLE object, Delegate* delegate);
// Stops watching. Does nothing if the watch has already completed. If the
// watch is still active, then it is canceled, and the associated delegate is
// not notified.
//
// Returns true if the watch was canceled. Otherwise, false is returned.
//
bool StopWatching();
// Returns the handle of the object being watched, or NULL if the object
// watcher is stopped.
HANDLE GetWatchedObject();
private:
// Called on a background thread when done waiting.
static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out);
// MessageLoop::DestructionObserver implementation:
virtual void WillDestroyCurrentMessageLoop();
// Internal state.
class Watch;
RefPtr<Watch> watch_;
DISALLOW_COPY_AND_ASSIGN(ObjectWatcher);
};
} // namespace base
#endif // BASE_OBJECT_WATCHER_H_

View file

@ -0,0 +1,186 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_OBSERVER_LIST_H__
#define BASE_OBSERVER_LIST_H__
#include <algorithm>
#include <limits>
#include <vector>
#include "base/basictypes.h"
#include "base/logging.h"
#if defined(ANDROID) && defined(_STLP_STD_NAME)
using _STLP_STD_NAME::find;
#endif
namespace base {
///////////////////////////////////////////////////////////////////////////////
//
// OVERVIEW:
//
// A container for a list of observers. Unlike a normal STL vector or list,
// this container can be modified during iteration without invalidating the
// iterator. So, it safely handles the case of an observer removing itself
// or other observers from the list while observers are being notified.
//
// TYPICAL USAGE:
//
// class MyWidget {
// public:
// ...
//
// class Observer {
// public:
// virtual void OnFoo(MyWidget* w) = 0;
// virtual void OnBar(MyWidget* w, int x, int y) = 0;
// };
//
// void AddObserver(Observer* obs) {
// observer_list_.AddObserver(obs);
// }
//
// void RemoveObserver(Observer* obs) {
// observer_list_.RemoveObserver(obs);
// }
//
// void NotifyFoo() {
// FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this));
// }
//
// void NotifyBar(int x, int y) {
// FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y));
// }
//
// private:
// ObserverList<Observer> observer_list_;
// };
//
//
///////////////////////////////////////////////////////////////////////////////
template <class ObserverType, bool check_empty = false>
class ObserverList {
public:
// Enumeration of which observers are notified.
enum NotificationType {
// Specifies that any observers added during notification are notified.
// This is the default type if non type is provided to the constructor.
NOTIFY_ALL,
// Specifies that observers added while sending out notification are not
// notified.
NOTIFY_EXISTING_ONLY
};
ObserverList() : notify_depth_(0), type_(NOTIFY_ALL) {}
explicit ObserverList(NotificationType type) : notify_depth_(0), type_(type) {}
~ObserverList() {
// When check_empty is true, assert that the list is empty on destruction.
if (check_empty) {
Compact();
DCHECK_EQ(observers_.size(), 0U);
}
}
// Add an observer to the list.
void AddObserver(ObserverType* obs) {
DCHECK(find(observers_.begin(), observers_.end(), obs) == observers_.end())
<< "Observers can only be added once!";
observers_.push_back(obs);
}
// Remove an observer from the list.
void RemoveObserver(ObserverType* obs) {
typename ListType::iterator it =
std::find(observers_.begin(), observers_.end(), obs);
if (it != observers_.end()) {
if (notify_depth_) {
*it = 0;
} else {
observers_.erase(it);
}
}
}
size_t size() const {
return observers_.size();
}
ObserverType* GetElementAt(int index) const {
return observers_[index];
}
// An iterator class that can be used to access the list of observers. See
// also the FOREACH_OBSERVER macro defined below.
class Iterator {
public:
explicit Iterator(const ObserverList<ObserverType>& list)
: list_(list),
index_(0),
max_index_(list.type_ == NOTIFY_ALL ?
std::numeric_limits<size_t>::max() :
list.observers_.size()) {
++list_.notify_depth_;
}
~Iterator() {
if (--list_.notify_depth_ == 0)
list_.Compact();
}
ObserverType* GetNext() {
ListType& observers = list_.observers_;
// Advance if the current element is null
size_t max_index = std::min(max_index_, observers.size());
while (index_ < max_index && !observers[index_])
++index_;
return index_ < max_index ? observers[index_++] : NULL;
}
private:
const ObserverList<ObserverType>& list_;
size_t index_;
size_t max_index_;
};
private:
typedef std::vector<ObserverType*> ListType;
void Compact() const {
typename ListType::iterator it = observers_.begin();
while (it != observers_.end()) {
if (*it) {
++it;
} else {
it = observers_.erase(it);
}
}
}
// These are marked mutable to facilitate having NotifyAll be const.
mutable ListType observers_;
mutable int notify_depth_;
NotificationType type_;
friend class ObserverList::Iterator;
DISALLOW_EVIL_CONSTRUCTORS(ObserverList);
};
} // namespace base
#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \
do { \
base::ObserverList<ObserverType>::Iterator it(observer_list); \
ObserverType* obs; \
while ((obs = it.GetNext()) != NULL) \
obs->func; \
} while (0)
#endif // BASE_OBSERVER_LIST_H__

View file

@ -0,0 +1,588 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/pickle.h"
#include "mozilla/Alignment.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/EndianUtils.h"
#include "mozilla/TypeTraits.h"
#include <stdlib.h>
#include <limits>
#include <string>
#include <algorithm>
#include "nsDebug.h"
//------------------------------------------------------------------------------
static_assert(MOZ_ALIGNOF(Pickle::memberAlignmentType) >= MOZ_ALIGNOF(uint32_t),
"Insufficient alignment");
static const uint32_t kHeaderSegmentCapacity = 64;
static const uint32_t kDefaultSegmentCapacity = 4096;
static const char kBytePaddingMarker = char(0xbf);
namespace {
// We want to copy data to our payload as efficiently as possible.
// memcpy fits the bill for copying, but not all compilers or
// architectures support inlining memcpy from void*, which has unknown
// static alignment. However, we know that all the members of our
// payload will be aligned on memberAlignmentType boundaries. We
// therefore use that knowledge to construct a copier that will copy
// efficiently (via standard C++ assignment mechanisms) if the datatype
// needs that alignment or less, and memcpy otherwise. (The compiler
// may still inline memcpy, of course.)
template<typename T, size_t size, bool hasSufficientAlignment>
struct Copier
{
static void Copy(T* dest, const char* iter) {
memcpy(dest, iter, sizeof(T));
}
};
// Copying 64-bit quantities happens often enough and can easily be made
// worthwhile on 32-bit platforms, so handle it specially. Only do it
// if 64-bit types aren't sufficiently aligned; the alignment
// requirements for them vary between 32-bit platforms.
#ifndef HAVE_64BIT_BUILD
template<typename T>
struct Copier<T, sizeof(uint64_t), false>
{
static void Copy(T* dest, const char* iter) {
#if MOZ_LITTLE_ENDIAN
static const int loIndex = 0, hiIndex = 1;
#else
static const int loIndex = 1, hiIndex = 0;
#endif
static_assert(MOZ_ALIGNOF(uint32_t*) == MOZ_ALIGNOF(void*),
"Pointers have different alignments");
const uint32_t* src = reinterpret_cast<const uint32_t*>(iter);
uint32_t* uint32dest = reinterpret_cast<uint32_t*>(dest);
uint32dest[loIndex] = src[loIndex];
uint32dest[hiIndex] = src[hiIndex];
}
};
#endif
template<typename T, size_t size>
struct Copier<T, size, true>
{
static void Copy(T* dest, const char* iter) {
*dest = *reinterpret_cast<const T*>(iter);
}
};
} // anonymous namespace
PickleIterator::PickleIterator(const Pickle& pickle)
: iter_(pickle.buffers_.Iter()) {
iter_.Advance(pickle.buffers_, pickle.header_size_);
}
template<typename T>
void
PickleIterator::CopyInto(T* dest) {
static_assert(mozilla::IsPod<T>::value, "Copied type must be a POD type");
Copier<T, sizeof(T), (MOZ_ALIGNOF(T) <= sizeof(Pickle::memberAlignmentType))>::Copy(dest, iter_.Data());
}
bool Pickle::IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const {
// Make sure we don't get into trouble where AlignInt(len) == 0.
MOZ_RELEASE_ASSERT(len < 64);
return iter.iter_.HasRoomFor(AlignInt(len));
}
void Pickle::UpdateIter(PickleIterator* iter, uint32_t bytes) const {
// Make sure we don't get into trouble where AlignInt(bytes) == 0.
MOZ_RELEASE_ASSERT(bytes < 64);
iter->iter_.Advance(buffers_, AlignInt(bytes));
}
// Payload is sizeof(Pickle::memberAlignmentType) aligned.
Pickle::Pickle(uint32_t header_size)
: buffers_(AlignInt(header_size), kHeaderSegmentCapacity, kDefaultSegmentCapacity),
header_(nullptr),
header_size_(AlignInt(header_size)) {
DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
DCHECK(header_size_ <= kHeaderSegmentCapacity);
header_ = reinterpret_cast<Header*>(buffers_.Start());
header_->payload_size = 0;
}
Pickle::Pickle(uint32_t header_size, const char* data, uint32_t length)
: buffers_(length, AlignCapacity(length), kDefaultSegmentCapacity),
header_(nullptr),
header_size_(AlignInt(header_size)) {
DCHECK(static_cast<memberAlignmentType>(header_size) >= sizeof(Header));
DCHECK(header_size <= kHeaderSegmentCapacity);
MOZ_RELEASE_ASSERT(header_size <= length);
header_ = reinterpret_cast<Header*>(buffers_.Start());
memcpy(header_, data, length);
}
Pickle::Pickle(Pickle&& other)
: buffers_(mozilla::Move(other.buffers_)),
header_(other.header_),
header_size_(other.header_size_) {
other.header_ = nullptr;
}
Pickle::~Pickle() {
}
Pickle& Pickle::operator=(Pickle&& other) {
BufferList tmp = mozilla::Move(other.buffers_);
other.buffers_ = mozilla::Move(buffers_);
buffers_ = mozilla::Move(tmp);
//std::swap(buffers_, other.buffers_);
std::swap(header_, other.header_);
std::swap(header_size_, other.header_size_);
return *this;
}
bool Pickle::ReadBool(PickleIterator* iter, bool* result) const {
DCHECK(iter);
int tmp;
if (!ReadInt(iter, &tmp))
return false;
DCHECK(0 == tmp || 1 == tmp);
*result = tmp ? true : false;
return true;
}
bool Pickle::ReadInt16(PickleIterator* iter, int16_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
bool Pickle::ReadUInt16(PickleIterator* iter, uint16_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
bool Pickle::ReadInt(PickleIterator* iter, int* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
// Always written as a 64-bit value since the size for this type can
// differ between architectures.
bool Pickle::ReadLong(PickleIterator* iter, long* result) const {
DCHECK(iter);
int64_t big_result = 0;
if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
iter->CopyInto(&big_result);
UpdateIter(iter, sizeof(big_result));
} else {
if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
return false;
}
}
DCHECK(big_result <= LONG_MAX && big_result >= LONG_MIN);
*result = static_cast<long>(big_result);
return true;
}
// Always written as a 64-bit value since the size for this type can
// differ between architectures.
bool Pickle::ReadULong(PickleIterator* iter, unsigned long* result) const {
DCHECK(iter);
uint64_t big_result = 0;
if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
iter->CopyInto(&big_result);
UpdateIter(iter, sizeof(big_result));
} else {
if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
return false;
}
}
DCHECK(big_result <= ULONG_MAX);
*result = static_cast<unsigned long>(big_result);
return true;
}
bool Pickle::ReadLength(PickleIterator* iter, int* result) const {
if (!ReadInt(iter, result))
return false;
return ((*result) >= 0);
}
// Always written as a 64-bit value since the size for this type can
// differ between architectures.
bool Pickle::ReadSize(PickleIterator* iter, size_t* result) const {
DCHECK(iter);
uint64_t big_result = 0;
if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
iter->CopyInto(&big_result);
UpdateIter(iter, sizeof(big_result));
} else {
if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
return false;
}
}
DCHECK(big_result <= std::numeric_limits<size_t>::max());
*result = static_cast<size_t>(big_result);
return true;
}
bool Pickle::ReadInt32(PickleIterator* iter, int32_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
bool Pickle::ReadUInt32(PickleIterator* iter, uint32_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
bool Pickle::ReadInt64(PickleIterator* iter, int64_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
bool Pickle::ReadUInt64(PickleIterator* iter, uint64_t* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
bool Pickle::ReadDouble(PickleIterator* iter, double* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
// Always written as a 64-bit value since the size for this type can
// differ between architectures.
bool Pickle::ReadIntPtr(PickleIterator* iter, intptr_t* result) const {
DCHECK(iter);
int64_t big_result = 0;
if (IteratorHasRoomFor(*iter, sizeof(big_result))) {
iter->CopyInto(&big_result);
UpdateIter(iter, sizeof(big_result));
} else {
if (!ReadBytesInto(iter, &big_result, sizeof(big_result))) {
return false;
}
}
DCHECK(big_result <= std::numeric_limits<intptr_t>::max() && big_result >= std::numeric_limits<intptr_t>::min());
*result = static_cast<intptr_t>(big_result);
return true;
}
bool Pickle::ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const {
DCHECK(iter);
if (!IteratorHasRoomFor(*iter, sizeof(*result)))
return ReadBytesInto(iter, result, sizeof(*result));
iter->CopyInto(result);
UpdateIter(iter, sizeof(*result));
return true;
}
bool Pickle::ReadString(PickleIterator* iter, std::string* result) const {
DCHECK(iter);
int len;
if (!ReadLength(iter, &len))
return false;
auto chars = mozilla::MakeUnique<char[]>(len);
if (!ReadBytesInto(iter, chars.get(), len)) {
return false;
}
result->assign(chars.get(), len);
return true;
}
bool Pickle::ReadWString(PickleIterator* iter, std::wstring* result) const {
DCHECK(iter);
int len;
if (!ReadLength(iter, &len))
return false;
// Avoid integer multiplication overflow.
if (len > INT_MAX / static_cast<int>(sizeof(wchar_t)))
return false;
auto chars = mozilla::MakeUnique<wchar_t[]>(len);
if (!ReadBytesInto(iter, chars.get(), len * sizeof(wchar_t))) {
return false;
}
result->assign(chars.get(), len);
return true;
}
bool Pickle::ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers,
uint32_t alignment) const
{
DCHECK(iter);
DCHECK(buffers);
DCHECK(alignment == 4 || alignment == 8);
DCHECK(intptr_t(header_) % alignment == 0);
if (AlignInt(length) < length) {
return false;
}
uint32_t padding_len = intptr_t(iter->iter_.Data()) % alignment;
if (!iter->iter_.AdvanceAcrossSegments(buffers_, padding_len)) {
return false;
}
bool success;
*buffers = const_cast<BufferList*>(&buffers_)->Extract(iter->iter_, length, &success);
if (!success) {
return false;
}
return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
}
bool Pickle::ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const {
if (AlignInt(length) < length) {
return false;
}
if (!buffers_.ReadBytes(iter->iter_, reinterpret_cast<char*>(data), length)) {
return false;
}
return iter->iter_.AdvanceAcrossSegments(buffers_, AlignInt(length) - length);
}
#ifdef MOZ_PICKLE_SENTINEL_CHECKING
bool Pickle::ReadSentinel(PickleIterator* iter, uint32_t sentinel) const {
uint32_t found;
if (!ReadUInt32(iter, &found)) {
return false;
}
return found == sentinel;
}
bool Pickle::WriteSentinel(uint32_t sentinel) {
return WriteUInt32(sentinel);
}
#endif
void Pickle::EndRead(PickleIterator& iter) const {
DCHECK(iter.iter_.Done());
}
void Pickle::BeginWrite(uint32_t length, uint32_t alignment) {
DCHECK(alignment % 4 == 0) << "Must be at least 32-bit aligned!";
// write at an alignment-aligned offset from the beginning of the header
uint32_t offset = AlignInt(header_->payload_size);
uint32_t padding = (header_size_ + offset) % alignment;
uint32_t new_size = offset + padding + AlignInt(length);
MOZ_RELEASE_ASSERT(new_size >= header_->payload_size);
DCHECK(intptr_t(header_) % alignment == 0);
#ifdef ARCH_CPU_64_BITS
DCHECK_LE(length, std::numeric_limits<uint32_t>::max());
#endif
if (padding) {
MOZ_RELEASE_ASSERT(padding <= 8);
static const char padding_data[8] = {
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
};
buffers_.WriteBytes(padding_data, padding);
}
DCHECK((header_size_ + header_->payload_size + padding) % alignment == 0);
header_->payload_size = new_size;
}
void Pickle::EndWrite(uint32_t length) {
// Zero-pad to keep tools like purify from complaining about uninitialized
// memory.
uint32_t padding = AlignInt(length) - length;
if (padding) {
MOZ_RELEASE_ASSERT(padding <= 4);
static const char padding_data[4] = {
kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker, kBytePaddingMarker,
};
buffers_.WriteBytes(padding_data, padding);
}
}
bool Pickle::WriteBytes(const void* data, uint32_t data_len, uint32_t alignment) {
DCHECK(alignment == 4 || alignment == 8);
DCHECK(intptr_t(header_) % alignment == 0);
BeginWrite(data_len, alignment);
buffers_.WriteBytes(reinterpret_cast<const char*>(data), data_len);
EndWrite(data_len);
return true;
}
bool Pickle::WriteString(const std::string& value) {
#ifdef MOZ_FAULTY
std::string v(value);
Singleton<mozilla::ipc::Faulty>::get()->FuzzString(v);
if (!WriteInt(static_cast<int>(v.size())))
return false;
return WriteBytes(v.data(), static_cast<int>(v.size()));
#else
if (!WriteInt(static_cast<int>(value.size())))
return false;
return WriteBytes(value.data(), static_cast<int>(value.size()));
#endif
}
bool Pickle::WriteWString(const std::wstring& value) {
#ifdef MOZ_FAULTY
std::wstring v(value);
Singleton<mozilla::ipc::Faulty>::get()->FuzzWString(v);
if (!WriteInt(static_cast<int>(v.size())))
return false;
return WriteBytes(v.data(),
static_cast<int>(v.size() * sizeof(wchar_t)));
#else
if (!WriteInt(static_cast<int>(value.size())))
return false;
return WriteBytes(value.data(),
static_cast<int>(value.size() * sizeof(wchar_t)));
#endif
}
bool Pickle::WriteData(const char* data, uint32_t length) {
#ifdef MOZ_FAULTY
std::string v(data, length);
Singleton<mozilla::ipc::Faulty>::get()->FuzzData(v, v.size());
return WriteInt(v.size()) && WriteBytes(v.data(), v.size());
#else
return WriteInt(length) && WriteBytes(data, length);
#endif
}
void Pickle::InputBytes(const char* data, uint32_t length) {
buffers_.WriteBytes(data, length);
}
int32_t* Pickle::GetInt32PtrForTest(uint32_t offset) {
size_t pos = buffers_.Size() - offset;
BufferList::IterImpl iter(buffers_);
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(buffers_, pos));
return reinterpret_cast<int32_t*>(iter.Data());
}
// static
uint32_t Pickle::MessageSize(uint32_t header_size,
const char* start,
const char* end) {
DCHECK(header_size == AlignInt(header_size));
DCHECK(header_size <= static_cast<memberAlignmentType>(kHeaderSegmentCapacity));
if (end < start)
return 0;
size_t length = static_cast<size_t>(end - start);
if (length < sizeof(Header))
return 0;
const Header* hdr = reinterpret_cast<const Header*>(start);
if (length < header_size)
return 0;
mozilla::CheckedInt<uint32_t> sum(header_size);
sum += hdr->payload_size;
if (!sum.isValid())
return 0;
return sum.value();
}

View file

@ -0,0 +1,326 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PICKLE_H__
#define BASE_PICKLE_H__
#include <string>
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/string16.h"
#include "mozilla/Attributes.h"
#include "mozilla/BufferList.h"
#include "mozilla/mozalloc.h"
#ifdef MOZ_FAULTY
#include "base/singleton.h"
#include "mozilla/ipc/Faulty.h"
#endif
#if !defined(RELEASE_OR_BETA) || defined(DEBUG)
#define MOZ_PICKLE_SENTINEL_CHECKING
#endif
class Pickle;
class PickleIterator {
public:
explicit PickleIterator(const Pickle& pickle);
private:
friend class Pickle;
mozilla::BufferList<InfallibleAllocPolicy>::IterImpl iter_;
template<typename T>
void CopyInto(T* dest);
};
// This class provides facilities for basic binary value packing and unpacking.
//
// The Pickle class supports appending primitive values (ints, strings, etc.)
// to a pickle instance. The Pickle instance grows its internal memory buffer
// dynamically to hold the sequence of primitive values. The internal memory
// buffer is exposed as the "data" of the Pickle. This "data" can be passed
// to a Pickle object to initialize it for reading.
//
// When reading from a Pickle object, it is important for the consumer to know
// what value types to read and in what order to read them as the Pickle does
// not keep track of the type of data written to it.
//
// The Pickle's data has a header which contains the size of the Pickle's
// payload. It can optionally support additional space in the header. That
// space is controlled by the header_size parameter passed to the Pickle
// constructor.
//
class Pickle {
public:
~Pickle();
Pickle() = delete;
// Initialize a Pickle object with the specified header size in bytes, which
// must be greater-than-or-equal-to sizeof(Pickle::Header). The header size
// will be rounded up to ensure that the header size is 32bit-aligned.
explicit Pickle(uint32_t header_size);
Pickle(uint32_t header_size, const char* data, uint32_t length);
Pickle(const Pickle& other) = delete;
Pickle(Pickle&& other);
// Performs a deep copy.
Pickle& operator=(const Pickle& other) = delete;
Pickle& operator=(Pickle&& other);
// Returns the size of the Pickle's data.
uint32_t size() const { return header_size_ + header_->payload_size; }
typedef mozilla::BufferList<InfallibleAllocPolicy> BufferList;
const BufferList& Buffers() const { return buffers_; }
uint32_t CurrentSize() const { return buffers_.Size(); }
// Methods for reading the payload of the Pickle. To read from the start of
// the Pickle, initialize *iter to NULL. If successful, these methods return
// true. Otherwise, false is returned to indicate that the result could not
// be extracted.
MOZ_MUST_USE bool ReadBool(PickleIterator* iter, bool* result) const;
MOZ_MUST_USE bool ReadInt16(PickleIterator* iter, int16_t* result) const;
MOZ_MUST_USE bool ReadUInt16(PickleIterator* iter, uint16_t* result) const;
MOZ_MUST_USE bool ReadShort(PickleIterator* iter, short* result) const;
MOZ_MUST_USE bool ReadInt(PickleIterator* iter, int* result) const;
MOZ_MUST_USE bool ReadLong(PickleIterator* iter, long* result) const;
MOZ_MUST_USE bool ReadULong(PickleIterator* iter, unsigned long* result) const;
MOZ_MUST_USE bool ReadSize(PickleIterator* iter, size_t* result) const;
MOZ_MUST_USE bool ReadInt32(PickleIterator* iter, int32_t* result) const;
MOZ_MUST_USE bool ReadUInt32(PickleIterator* iter, uint32_t* result) const;
MOZ_MUST_USE bool ReadInt64(PickleIterator* iter, int64_t* result) const;
MOZ_MUST_USE bool ReadUInt64(PickleIterator* iter, uint64_t* result) const;
MOZ_MUST_USE bool ReadDouble(PickleIterator* iter, double* result) const;
MOZ_MUST_USE bool ReadIntPtr(PickleIterator* iter, intptr_t* result) const;
MOZ_MUST_USE bool ReadUnsignedChar(PickleIterator* iter, unsigned char* result) const;
MOZ_MUST_USE bool ReadString(PickleIterator* iter, std::string* result) const;
MOZ_MUST_USE bool ReadWString(PickleIterator* iter, std::wstring* result) const;
MOZ_MUST_USE bool ReadBytesInto(PickleIterator* iter, void* data, uint32_t length) const;
MOZ_MUST_USE bool ExtractBuffers(PickleIterator* iter, size_t length, BufferList* buffers,
uint32_t alignment = sizeof(memberAlignmentType)) const;
// Safer version of ReadInt() checks for the result not being negative.
// Use it for reading the object sizes.
MOZ_MUST_USE bool ReadLength(PickleIterator* iter, int* result) const;
MOZ_MUST_USE bool ReadSentinel(PickleIterator* iter, uint32_t sentinel) const
#ifdef MOZ_PICKLE_SENTINEL_CHECKING
;
#else
{
return true;
}
#endif
void EndRead(PickleIterator& iter) const;
// Methods for adding to the payload of the Pickle. These values are
// appended to the end of the Pickle's payload. When reading values from a
// Pickle, it is important to read them in the order in which they were added
// to the Pickle.
bool WriteBool(bool value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzBool(&value);
#endif
return WriteInt(value ? 1 : 0);
}
bool WriteInt16(int16_t value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzInt16(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteUInt16(uint16_t value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzUInt16(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteInt(int value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzInt(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteLong(long value) {
// Always written as a 64-bit value since the size for this type can
// differ between architectures.
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzLong(&value);
#endif
return WriteInt64(int64_t(value));
}
bool WriteULong(unsigned long value) {
// Always written as a 64-bit value since the size for this type can
// differ between architectures.
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzULong(&value);
#endif
return WriteUInt64(uint64_t(value));
}
bool WriteSize(size_t value) {
// Always written as a 64-bit value since the size for this type can
// differ between architectures.
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzSize(&value);
#endif
return WriteUInt64(uint64_t(value));
}
bool WriteInt32(int32_t value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzInt(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteUInt32(uint32_t value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzUInt32(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteInt64(int64_t value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzInt64(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteUInt64(uint64_t value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzUInt64(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteDouble(double value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzDouble(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteIntPtr(intptr_t value) {
// Always written as a 64-bit value since the size for this type can
// differ between architectures.
return WriteInt64(int64_t(value));
}
bool WriteUnsignedChar(unsigned char value) {
#ifdef MOZ_FAULTY
Singleton<mozilla::ipc::Faulty>::get()->FuzzUChar(&value);
#endif
return WriteBytes(&value, sizeof(value));
}
bool WriteString(const std::string& value);
bool WriteWString(const std::wstring& value);
bool WriteData(const char* data, uint32_t length);
bool WriteBytes(const void* data, uint32_t data_len,
uint32_t alignment = sizeof(memberAlignmentType));
bool WriteSentinel(uint32_t sentinel)
#ifdef MOZ_PICKLE_SENTINEL_CHECKING
;
#else
{
return true;
}
#endif
int32_t* GetInt32PtrForTest(uint32_t offset);
void InputBytes(const char* data, uint32_t length);
// Payload follows after allocation of Header (header size is customizable).
struct Header {
uint32_t payload_size; // Specifies the size of the payload.
};
// Returns the header, cast to a user-specified type T. The type T must be a
// subclass of Header and its size must correspond to the header_size passed
// to the Pickle constructor.
template <class T>
T* headerT() {
DCHECK(sizeof(T) == header_size_);
return static_cast<T*>(header_);
}
template <class T>
const T* headerT() const {
DCHECK(sizeof(T) == header_size_);
return static_cast<const T*>(header_);
}
typedef uint32_t memberAlignmentType;
protected:
uint32_t payload_size() const { return header_->payload_size; }
// Resizes the buffer for use when writing the specified amount of data. The
// location that the data should be written at is returned, or NULL if there
// was an error. Call EndWrite with the returned offset and the given length
// to pad out for the next write.
void BeginWrite(uint32_t length, uint32_t alignment);
// Completes the write operation by padding the data with NULL bytes until it
// is padded. Should be paired with BeginWrite, but it does not necessarily
// have to be called after the data is written.
void EndWrite(uint32_t length);
// Round 'bytes' up to the next multiple of 'alignment'. 'alignment' must be
// a power of 2.
template<uint32_t alignment> struct ConstantAligner {
static uint32_t align(int bytes) {
static_assert((alignment & (alignment - 1)) == 0,
"alignment must be a power of two");
return (bytes + (alignment - 1)) & ~static_cast<uint32_t>(alignment - 1);
}
};
static uint32_t AlignInt(int bytes) {
return ConstantAligner<sizeof(memberAlignmentType)>::align(bytes);
}
static uint32_t AlignCapacity(int bytes) {
return ConstantAligner<kSegmentAlignment>::align(bytes);
}
// Returns true if the given iterator could point to data with the given
// length. If there is no room for the given data before the end of the
// payload, returns false.
bool IteratorHasRoomFor(const PickleIterator& iter, uint32_t len) const;
// Moves the iterator by the given number of bytes, making sure it is aligned.
// Pointer (iterator) is NOT aligned, but the change in the pointer
// is guaranteed to be a multiple of sizeof(memberAlignmentType).
void UpdateIter(PickleIterator* iter, uint32_t bytes) const;
// Figure out how big the message starting at range_start is. Returns 0 if
// there's no enough data to determine (i.e., if [range_start, range_end) does
// not contain enough of the message header to know the size).
static uint32_t MessageSize(uint32_t header_size,
const char* range_start,
const char* range_end);
// Segments capacities are aligned to 8 bytes to ensure that all reads/writes
// at 8-byte aligned offsets will be on 8-byte aligned pointers.
static const uint32_t kSegmentAlignment = 8;
private:
friend class PickleIterator;
BufferList buffers_;
Header* header_;
uint32_t header_size_;
};
#endif // BASE_PICKLE_H__

View file

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PLATFORM_FILE_H_
#define BASE_PLATFORM_FILE_H_
#include "build/build_config.h"
#if defined(OS_WIN)
#include <windows.h>
#endif
#include <string>
namespace base {
#if defined(OS_WIN)
typedef HANDLE PlatformFile;
const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
#elif defined(OS_POSIX)
typedef int PlatformFile;
const PlatformFile kInvalidPlatformFileValue = -1;
#endif
enum PlatformFileFlags {
PLATFORM_FILE_OPEN = 1,
PLATFORM_FILE_CREATE = 2,
PLATFORM_FILE_OPEN_ALWAYS = 4, // May create a new file.
PLATFORM_FILE_CREATE_ALWAYS = 8, // May overwrite an old file.
PLATFORM_FILE_READ = 16,
PLATFORM_FILE_WRITE = 32,
PLATFORM_FILE_EXCLUSIVE_READ = 64, // EXCLUSIVE is opposite of Windows SHARE
PLATFORM_FILE_EXCLUSIVE_WRITE = 128,
PLATFORM_FILE_ASYNC = 256
};
// Creates or open the given file. If PLATFORM_FILE_OPEN_ALWAYS is used, and
// |created| is provided, |created| will be set to true if the file was created
// or to false in case the file was just opened.
PlatformFile CreatePlatformFile(const std::wstring& name,
int flags,
bool* created);
} // namespace base
#endif // BASE_PLATFORM_FILE_H_

View file

@ -0,0 +1,70 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/platform_file.h"
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#ifdef ANDROID
#include <linux/stat.h>
#endif
#include "base/logging.h"
#include "base/string_util.h"
namespace base {
// TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here?
PlatformFile CreatePlatformFile(const std::wstring& name,
int flags,
bool* created) {
int open_flags = 0;
if (flags & PLATFORM_FILE_CREATE)
open_flags = O_CREAT | O_EXCL;
if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
DCHECK(!open_flags);
open_flags = O_CREAT | O_TRUNC;
}
if (!open_flags && !(flags & PLATFORM_FILE_OPEN) &&
!(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
NOTREACHED();
errno = ENOTSUP;
return kInvalidPlatformFileValue;
}
if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) {
open_flags |= O_RDWR;
} else if (flags & PLATFORM_FILE_WRITE) {
open_flags |= O_WRONLY;
} else if (!(flags & PLATFORM_FILE_READ)) {
NOTREACHED();
}
DCHECK(O_RDONLY == 0);
int descriptor = open(WideToUTF8(name).c_str(), open_flags,
S_IRUSR | S_IWUSR);
if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
if (descriptor > 0) {
if (created)
*created = false;
} else {
open_flags |= O_CREAT;
descriptor = open(WideToUTF8(name).c_str(), open_flags,
S_IRUSR | S_IWUSR);
if (created && descriptor > 0)
*created = true;
}
}
return descriptor;
}
} // namespace base

View file

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/platform_file.h"
#include "base/logging.h"
namespace base {
PlatformFile CreatePlatformFile(const std::wstring& name,
int flags,
bool* created) {
DWORD disposition = 0;
if (flags & PLATFORM_FILE_OPEN)
disposition = OPEN_EXISTING;
if (flags & PLATFORM_FILE_CREATE) {
DCHECK(!disposition);
disposition = CREATE_NEW;
}
if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
DCHECK(!disposition);
disposition = OPEN_ALWAYS;
}
if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
DCHECK(!disposition);
disposition = CREATE_ALWAYS;
}
if (!disposition) {
NOTREACHED();
return NULL;
}
DWORD access = (flags & PLATFORM_FILE_READ) ? GENERIC_READ : 0;
if (flags & PLATFORM_FILE_WRITE)
access |= GENERIC_WRITE;
DWORD sharing = (flags & PLATFORM_FILE_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
if (!(flags & PLATFORM_FILE_EXCLUSIVE_WRITE))
sharing |= FILE_SHARE_WRITE;
DWORD create_flags = 0;
if (flags & PLATFORM_FILE_ASYNC)
create_flags |= FILE_FLAG_OVERLAPPED;
HANDLE file = CreateFile(name.c_str(), access, sharing, NULL, disposition,
create_flags, NULL);
if ((flags & PLATFORM_FILE_OPEN_ALWAYS) && created &&
INVALID_HANDLE_VALUE != file) {
*created = (ERROR_ALREADY_EXISTS != GetLastError());
}
return file;
}
} // namespace disk_cache

View file

@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// WARNING: You should *NOT* be using this class directly. PlatformThread is
// the low-level platform-specific abstraction to the OS's threading interface.
// You should instead be using a message-loop driven Thread, see thread.h.
#ifndef BASE_PLATFORM_THREAD_H_
#define BASE_PLATFORM_THREAD_H_
#include "base/basictypes.h"
// PlatformThreadHandle should not be assumed to be a numeric type, since the
// standard intends to allow pthread_t to be a structure. This means you
// should not initialize it to a value, like 0. If it's a member variable, the
// constructor can safely "value initialize" using () in the initializer list.
#if defined(OS_WIN)
#include <windows.h>
typedef DWORD PlatformThreadId;
typedef void* PlatformThreadHandle; // HANDLE
#elif defined(OS_POSIX)
#include <pthread.h>
typedef pthread_t PlatformThreadHandle;
#if defined(OS_LINUX) || defined(OS_OPENBSD) || defined(__GLIBC__)
#include <unistd.h>
typedef pid_t PlatformThreadId;
#elif defined(OS_BSD)
#include <sys/types.h>
typedef lwpid_t PlatformThreadId;
#elif defined(OS_MACOSX)
#include <mach/mach.h>
typedef mach_port_t PlatformThreadId;
#endif
#endif
// A namespace for low-level thread functions.
class PlatformThread {
public:
// Gets the current thread id, which may be useful for logging purposes.
static PlatformThreadId CurrentId();
// Yield the current thread so another thread can be scheduled.
static void YieldCurrentThread();
// Sleeps for the specified duration (units are milliseconds).
static void Sleep(int duration_ms);
// Sets the thread name visible to a debugger. This has no effect otherwise.
static void SetName(const char* name);
// Implement this interface to run code on a background thread. Your
// ThreadMain method will be called on the newly created thread.
class Delegate {
public:
virtual ~Delegate() {}
virtual void ThreadMain() = 0;
};
// Creates a new thread. The |stack_size| parameter can be 0 to indicate
// that the default stack size should be used. Upon success,
// |*thread_handle| will be assigned a handle to the newly created thread,
// and |delegate|'s ThreadMain method will be executed on the newly created
// thread.
// NOTE: When you are done with the thread handle, you must call Join to
// release system resources associated with the thread. You must ensure that
// the Delegate object outlives the thread.
static bool Create(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle);
// CreateNonJoinable() does the same thing as Create() except the thread
// cannot be Join()'d. Therefore, it also does not output a
// PlatformThreadHandle.
static bool CreateNonJoinable(size_t stack_size, Delegate* delegate);
// Joins with a thread created via the Create function. This function blocks
// the caller until the designated thread exits. This will invalidate
// |thread_handle|.
static void Join(PlatformThreadHandle thread_handle);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
};
#endif // BASE_PLATFORM_THREAD_H_

View file

@ -0,0 +1,77 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/platform_thread.h"
#import <Foundation/Foundation.h>
#include <dlfcn.h>
#include "base/logging.h"
#include "base/scoped_nsautorelease_pool.h"
// A simple class that demonstrates our impressive ability to do nothing.
@interface NoOp : NSObject
// Does the deed. Or does it?
+ (void)noOp;
@end
@implementation NoOp
+ (void)noOp {
}
@end
namespace base {
// If Cocoa is to be used on more than one thread, it must know that the
// application is multithreaded. Since it's possible to enter Cocoa code
// from threads created by pthread_thread_create, Cocoa won't necessarily
// be aware that the application is multithreaded. Spawning an NSThread is
// enough to get Cocoa to set up for multithreaded operation, so this is done
// if necessary before pthread_thread_create spawns any threads.
//
// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html
void InitThreading() {
// this is called early in startup, before the event loop, so provide
// an autorelease pool to prevent leaks here
ScopedNSAutoreleasePool pool;
static BOOL multithreaded = [NSThread isMultiThreaded];
if (!multithreaded) {
[NSThread detachNewThreadSelector:@selector(noOp)
toTarget:[NoOp class]
withObject:nil];
multithreaded = YES;
DCHECK([NSThread isMultiThreaded]);
}
}
} // namespace base
// static
void PlatformThread::SetName(const char* name) {
// pthread_setname_np is only available in 10.6 or later, so test
// for it at runtime.
int (*dynamic_pthread_setname_np)(const char*);
*reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
dlsym(RTLD_DEFAULT, "pthread_setname_np");
if (!dynamic_pthread_setname_np)
return;
// Mac OS X does not expose the length limit of the name, so
// hardcode it.
const int kMaxNameLength = 63;
std::string shortened_name = std::string(name).substr(0, kMaxNameLength);
// pthread_setname() fails (harmlessly) in the sandbox, ignore when it does.
// See http://crbug.com/47058
// The name parameter is copied thus it's safe to release it after calling.
// Checked against the bionic implementation in bionic/libc/bionic/pthread.c
dynamic_pthread_setname_np(shortened_name.c_str());
}

View file

@ -0,0 +1,164 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/platform_thread.h"
#include <errno.h>
#include <sched.h>
#if defined(OS_MACOSX)
#include <mach/mach.h>
#elif defined(OS_NETBSD)
#include <lwp.h>
#elif defined(OS_LINUX)
#include <sys/syscall.h>
#include <sys/prctl.h>
#endif
#if !defined(OS_MACOSX)
#include <unistd.h>
#endif
#if defined(OS_BSD) && !defined(OS_NETBSD) && !defined(__GLIBC__)
#include <pthread_np.h>
#endif
#if defined(OS_MACOSX)
namespace base {
void InitThreading();
} // namespace
#endif
static void* ThreadFunc(void* closure) {
PlatformThread::Delegate* delegate =
static_cast<PlatformThread::Delegate*>(closure);
delegate->ThreadMain();
return NULL;
}
// static
PlatformThreadId PlatformThread::CurrentId() {
// Pthreads doesn't have the concept of a thread ID, so we have to reach down
// into the kernel.
#if defined(OS_MACOSX)
mach_port_t port = mach_thread_self();
mach_port_deallocate(mach_task_self(), port);
return port;
#elif defined(OS_LINUX)
#ifdef MOZ_WIDGET_GONK
return (intptr_t) (pthread_self());
#else
return syscall(__NR_gettid);
#endif
#elif defined(OS_OPENBSD) || defined(__GLIBC__)
return (intptr_t) (pthread_self());
#elif defined(OS_NETBSD)
return _lwp_self();
#elif defined(OS_DRAGONFLY)
return lwp_gettid();
#elif defined(OS_FREEBSD)
return pthread_getthreadid_np();
#endif
}
// static
void PlatformThread::YieldCurrentThread() {
sched_yield();
}
// static
void PlatformThread::Sleep(int duration_ms) {
struct timespec sleep_time, remaining;
// Contains the portion of duration_ms >= 1 sec.
sleep_time.tv_sec = duration_ms / 1000;
duration_ms -= sleep_time.tv_sec * 1000;
// Contains the portion of duration_ms < 1 sec.
sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds.
while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR)
sleep_time = remaining;
}
#ifndef OS_MACOSX
// Mac is implemented in platform_thread_mac.mm.
// static
void PlatformThread::SetName(const char* name) {
// On linux we can get the thread names to show up in the debugger by setting
// the process name for the LWP. We don't want to do this for the main
// thread because that would rename the process, causing tools like killall
// to stop working.
if (PlatformThread::CurrentId() == getpid())
return;
// http://0pointer.de/blog/projects/name-your-threads.html
// Set the name for the LWP (which gets truncated to 15 characters).
// Note that glibc also has a 'pthread_setname_np' api, but it may not be
// available everywhere and it's only benefit over using prctl directly is
// that it can set the name of threads other than the current thread.
#if defined(OS_LINUX)
prctl(PR_SET_NAME, reinterpret_cast<uintptr_t>(name), 0, 0, 0);
#elif defined(OS_NETBSD)
pthread_setname_np(pthread_self(), "%s", (void *)name);
#elif defined(OS_BSD) && !defined(__GLIBC__)
pthread_set_name_np(pthread_self(), name);
#else
#endif
}
#endif // !OS_MACOSX
namespace {
bool CreateThread(size_t stack_size, bool joinable,
PlatformThread::Delegate* delegate,
PlatformThreadHandle* thread_handle) {
#if defined(OS_MACOSX)
base::InitThreading();
#endif // OS_MACOSX
bool success = false;
pthread_attr_t attributes;
pthread_attr_init(&attributes);
// Pthreads are joinable by default, so only specify the detached attribute if
// the thread should be non-joinable.
if (!joinable) {
pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED);
}
if (stack_size > 0)
pthread_attr_setstacksize(&attributes, stack_size);
success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate);
pthread_attr_destroy(&attributes);
return success;
}
} // anonymous namespace
// static
bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle) {
return CreateThread(stack_size, true /* joinable thread */,
delegate, thread_handle);
}
// static
bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
PlatformThreadHandle unused;
bool result = CreateThread(stack_size, false /* non-joinable thread */,
delegate, &unused);
return result;
}
// static
void PlatformThread::Join(PlatformThreadHandle thread_handle) {
pthread_join(thread_handle, NULL);
}

View file

@ -0,0 +1,109 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/platform_thread.h"
#include "base/logging.h"
#include "base/win_util.h"
namespace {
// The information on how to set the thread name comes from
// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
const DWORD kVCThreadNameException = 0x406D1388;
typedef struct tagTHREADNAME_INFO {
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
DWORD __stdcall ThreadFunc(void* closure) {
PlatformThread::Delegate* delegate =
static_cast<PlatformThread::Delegate*>(closure);
delegate->ThreadMain();
return 0;
}
} // namespace
// static
PlatformThreadId PlatformThread::CurrentId() {
return GetCurrentThreadId();
}
// static
void PlatformThread::YieldCurrentThread() {
::Sleep(0);
}
// static
void PlatformThread::Sleep(int duration_ms) {
::Sleep(duration_ms);
}
// static
void PlatformThread::SetName(const char* name) {
#ifdef HAVE_SEH_EXCEPTIONS
// The debugger needs to be around to catch the name in the exception. If
// there isn't a debugger, we are just needlessly throwing an exception.
if (!::IsDebuggerPresent())
return;
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = CurrentId();
info.dwFlags = 0;
MOZ_SEH_TRY {
RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD),
reinterpret_cast<DWORD_PTR*>(&info));
} MOZ_SEH_EXCEPT(EXCEPTION_CONTINUE_EXECUTION) {
}
#endif
}
// static
bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle) {
unsigned int flags = 0;
if (stack_size > 0) {
flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
} else {
stack_size = 0;
}
// Using CreateThread here vs _beginthreadex makes thread creation a bit
// faster and doesn't require the loader lock to be available. Our code will
// have to work running on CreateThread() threads anyway, since we run code
// on the Windows thread pool, etc. For some background on the difference:
// http://www.microsoft.com/msj/1099/win32/win321099.aspx
*thread_handle = CreateThread(
NULL, stack_size, ThreadFunc, delegate, flags, NULL);
return *thread_handle != NULL;
}
// static
bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) {
PlatformThreadHandle thread_handle;
bool result = Create(stack_size, delegate, &thread_handle);
CloseHandle(thread_handle);
return result;
}
// static
void PlatformThread::Join(PlatformThreadHandle thread_handle) {
DCHECK(thread_handle);
// Wait for the thread to exit. It should already have terminated but make
// sure this assumption is valid.
DWORD result = WaitForSingleObject(thread_handle, INFINITE);
DCHECK_EQ(WAIT_OBJECT_0, result);
CloseHandle(thread_handle);
}

View file

@ -0,0 +1,65 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PORT_H_
#define BASE_PORT_H_
#include <stdarg.h>
#include "build/build_config.h"
#ifdef COMPILER_MSVC
#define GG_LONGLONG(x) x##I64
#define GG_ULONGLONG(x) x##UI64
#else
#define GG_LONGLONG(x) x##LL
#define GG_ULONGLONG(x) x##ULL
#endif
// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including <stdint.h>
// to get the INTn_C and UINTn_C macros for integer constants. It's difficult
// to guarantee any specific ordering of header includes, so it's difficult to
// guarantee that the INTn_C macros can be defined by including <stdint.h> at
// any specific point. Provide GG_INTn_C macros instead.
#define GG_INT8_C(x) (x)
#define GG_INT16_C(x) (x)
#define GG_INT32_C(x) (x)
#define GG_INT64_C(x) GG_LONGLONG(x)
#define GG_UINT8_C(x) (x ## U)
#define GG_UINT16_C(x) (x ## U)
#define GG_UINT32_C(x) (x ## U)
#define GG_UINT64_C(x) GG_ULONGLONG(x)
namespace base {
// It's possible for functions that use a va_list, such as StringPrintf, to
// invalidate the data in it upon use. The fix is to make a copy of the
// structure before using it and use that copy instead. va_copy is provided
// for this purpose. MSVC does not provide va_copy, so define an
// implementation here. It is not guaranteed that assignment is a copy, so the
// StringUtil.VariableArgsFunc unit test tests this capability.
// The C standard says that va_copy is a "macro", not a function. Trying to
// use va_list as ref args to a function, as above, breaks some machines.
# if defined(COMPILER_GCC)
# define base_va_copy(_a, _b) ::va_copy(_a, _b)
# elif defined(COMPILER_MSVC)
# define base_va_copy(_a, _b) (_a = _b)
# else
# error No va_copy for your compiler
# endif
} // namespace base
// Define an OS-neutral wrapper for shared library entry points
#if defined(OS_WIN)
#define API_CALL __stdcall
#elif defined(OS_LINUX) || defined(OS_MACOSX)
#define API_CALL
#endif
#endif // BASE_PORT_H_

View file

@ -0,0 +1,81 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_PROCESS_H_
#define BASE_PROCESS_H_
#include "base/basictypes.h"
#include <sys/types.h>
#ifdef OS_WIN
#include <windows.h>
#endif
namespace base {
// ProcessHandle is a platform specific type which represents the underlying OS
// handle to a process.
// ProcessId is a number which identifies the process in the OS.
#if defined(OS_WIN)
typedef HANDLE ProcessHandle;
typedef DWORD ProcessId;
#elif defined(OS_POSIX)
// On POSIX, our ProcessHandle will just be the PID.
typedef pid_t ProcessHandle;
typedef pid_t ProcessId;
#endif
class Process {
public:
Process() : process_(0), last_working_set_size_(0) {}
explicit Process(ProcessHandle aHandle) :
process_(aHandle), last_working_set_size_(0) {}
// A handle to the current process.
static Process Current();
// Get/Set the handle for this process. The handle will be 0 if the process
// is no longer running.
ProcessHandle handle() const { return process_; }
void set_handle(ProcessHandle aHandle) { process_ = aHandle; }
// Get the PID for this process.
ProcessId pid() const;
// Is the this process the current process.
bool is_current() const;
// Close the process handle. This will not terminate the process.
void Close();
// Terminates the process with extreme prejudice. The given result code will
// be the exit code of the process. If the process has already exited, this
// will do nothing.
void Terminate(int result_code);
// A process is backgrounded when it's priority is lower than normal.
// Return true if this process is backgrounded, false otherwise.
bool IsProcessBackgrounded() const;
// Set a prcess as backgrounded. If value is true, the priority
// of the process will be lowered. If value is false, the priority
// of the process will be made "normal" - equivalent to default
// process priority.
// Returns true if the priority was changed, false otherwise.
bool SetProcessBackgrounded(bool value);
// Releases as much of the working set back to the OS as possible.
// Returns true if successful, false otherwise.
bool EmptyWorkingSet();
private:
ProcessHandle process_;
size_t last_working_set_size_;
};
} // namespace base
#endif // BASE_PROCESS_H_

View file

@ -0,0 +1,62 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/process.h"
#include "base/process_util.h"
namespace base {
void Process::Close() {
process_ = 0;
// if the process wasn't termiated (so we waited) or the state
// wasn't already collected w/ a wait from process_utils, we're gonna
// end up w/ a zombie when it does finally exit.
}
void Process::Terminate(int result_code) {
// result_code isn't supportable.
if (!process_)
return;
// We don't wait here. It's the responsibility of other code to reap the
// child.
KillProcess(process_, result_code, false);
}
bool Process::IsProcessBackgrounded() const {
// http://code.google.com/p/chromium/issues/detail?id=8083
return false;
}
bool Process::SetProcessBackgrounded(bool value) {
// http://code.google.com/p/chromium/issues/detail?id=8083
// Just say we did it to keep renderer happy at the moment. Need to finish
// cleaning this up w/in higher layers since windows is probably the only
// one that can raise priorities w/o privileges.
return true;
}
bool Process::EmptyWorkingSet() {
// http://code.google.com/p/chromium/issues/detail?id=8083
return false;
}
ProcessId Process::pid() const {
if (process_ == 0)
return 0;
return GetProcId(process_);
}
bool Process::is_current() const {
return process_ == GetCurrentProcessHandle();
}
// static
Process Process::Current() {
return Process(GetCurrentProcessHandle());
}
} // namspace base

Some files were not shown because too many files have changed in this diff Show more