mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-02 13:27:33 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
41
ipc/app/Makefile.in
Normal file
41
ipc/app/Makefile.in
Normal 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 #}
|
||||
19
ipc/app/MozillaRuntimeMain.cpp
Normal file
19
ipc/app/MozillaRuntimeMain.cpp
Normal 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);
|
||||
}
|
||||
35
ipc/app/MozillaRuntimeMainAndroid.cpp
Normal file
35
ipc/app/MozillaRuntimeMainAndroid.cpp
Normal 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);
|
||||
}
|
||||
33
ipc/app/macbuild/Contents/Info.plist.in
Normal file
33
ipc/app/macbuild/Contents/Info.plist.in
Normal 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>
|
||||
1
ipc/app/macbuild/Contents/PkgInfo
Normal file
1
ipc/app/macbuild/Contents/PkgInfo
Normal file
|
|
@ -0,0 +1 @@
|
|||
APPL????
|
||||
|
|
@ -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
6
ipc/app/module.ver
Normal 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
114
ipc/app/moz.build
Normal 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
30
ipc/app/pie/moz.build
Normal 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']
|
||||
43
ipc/app/plugin-container.exe.manifest
Normal file
43
ipc/app/plugin-container.exe.manifest
Normal 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>
|
||||
17
ipc/chromium/atomics/moz.build
Normal file
17
ipc/chromium/atomics/moz.build
Normal 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',
|
||||
]
|
||||
69
ipc/chromium/chromium-config.mozbuild
Normal file
69
ipc/chromium/chromium-config.mozbuild
Normal 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
160
ipc/chromium/moz.build
Normal 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
27
ipc/chromium/src/LICENSE
Normal 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.
|
||||
74
ipc/chromium/src/base/at_exit.cc
Normal file
74
ipc/chromium/src/base/at_exit.cc
Normal 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
|
||||
75
ipc/chromium/src/base/at_exit.h
Normal file
75
ipc/chromium/src/base/at_exit.h
Normal 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_
|
||||
62
ipc/chromium/src/base/atomic_sequence_num.h
Normal file
62
ipc/chromium/src/base/atomic_sequence_num.h
Normal 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_
|
||||
155
ipc/chromium/src/base/atomicops.h
Normal file
155
ipc/chromium/src/base/atomicops.h
Normal 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_
|
||||
362
ipc/chromium/src/base/atomicops_internals_arm64_gcc.h
Normal file
362
ipc/chromium/src/base/atomicops_internals_arm64_gcc.h
Normal 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_
|
||||
126
ipc/chromium/src/base/atomicops_internals_arm_gcc.h
Normal file
126
ipc/chromium/src/base/atomicops_internals_arm_gcc.h
Normal 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_
|
||||
305
ipc/chromium/src/base/atomicops_internals_mips_gcc.h
Normal file
305
ipc/chromium/src/base/atomicops_internals_mips_gcc.h
Normal 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_
|
||||
15
ipc/chromium/src/base/atomicops_internals_mutex.cc
Normal file
15
ipc/chromium/src/base/atomicops_internals_mutex.cc
Normal 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
|
||||
251
ipc/chromium/src/base/atomicops_internals_mutex.h
Normal file
251
ipc/chromium/src/base/atomicops_internals_mutex.h
Normal 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
|
||||
134
ipc/chromium/src/base/atomicops_internals_ppc_gcc.h
Normal file
134
ipc/chromium/src/base/atomicops_internals_ppc_gcc.h
Normal 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_
|
||||
106
ipc/chromium/src/base/atomicops_internals_x86_gcc.cc
Normal file
106
ipc/chromium/src/base/atomicops_internals_x86_gcc.cc
Normal 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_
|
||||
261
ipc/chromium/src/base/atomicops_internals_x86_gcc.h
Normal file
261
ipc/chromium/src/base/atomicops_internals_x86_gcc.h
Normal 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_
|
||||
281
ipc/chromium/src/base/atomicops_internals_x86_macosx.h
Normal file
281
ipc/chromium/src/base/atomicops_internals_x86_macosx.h
Normal 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_
|
||||
178
ipc/chromium/src/base/atomicops_internals_x86_msvc.h
Normal file
178
ipc/chromium/src/base/atomicops_internals_x86_msvc.h
Normal 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_
|
||||
272
ipc/chromium/src/base/basictypes.h
Normal file
272
ipc/chromium/src/base/basictypes.h
Normal 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_
|
||||
62
ipc/chromium/src/base/chrome_application_mac.h
Normal file
62
ipc/chromium/src/base/chrome_application_mac.h
Normal 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_
|
||||
68
ipc/chromium/src/base/chrome_application_mac.mm
Normal file
68
ipc/chromium/src/base/chrome_application_mac.mm
Normal 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
|
||||
394
ipc/chromium/src/base/command_line.cc
Normal file
394
ipc/chromium/src/base/command_line.cc
Normal 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
|
||||
197
ipc/chromium/src/base/command_line.h
Normal file
197
ipc/chromium/src/base/command_line.h
Normal 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_
|
||||
79
ipc/chromium/src/base/compiler_specific.h
Normal file
79
ipc/chromium/src/base/compiler_specific.h
Normal 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_
|
||||
176
ipc/chromium/src/base/condition_variable.h
Normal file
176
ipc/chromium/src/base/condition_variable.h
Normal 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_
|
||||
91
ipc/chromium/src/base/condition_variable_posix.cc
Normal file
91
ipc/chromium/src/base/condition_variable_posix.cc
Normal 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);
|
||||
}
|
||||
448
ipc/chromium/src/base/condition_variable_win.cc
Normal file
448
ipc/chromium/src/base/condition_variable_win.cc
Normal 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.
|
||||
|
||||
*/
|
||||
56
ipc/chromium/src/base/cpu.cc
Normal file
56
ipc/chromium/src/base/cpu.cc
Normal 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
|
||||
44
ipc/chromium/src/base/cpu.h
Normal file
44
ipc/chromium/src/base/cpu.h
Normal 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_
|
||||
103
ipc/chromium/src/base/dir_reader_bsd.h
Normal file
103
ipc/chromium/src/base/dir_reader_bsd.h
Normal 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_
|
||||
33
ipc/chromium/src/base/dir_reader_fallback.h
Normal file
33
ipc/chromium/src/base/dir_reader_fallback.h
Normal 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_
|
||||
101
ipc/chromium/src/base/dir_reader_linux.h
Normal file
101
ipc/chromium/src/base/dir_reader_linux.h
Normal 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_
|
||||
43
ipc/chromium/src/base/dir_reader_posix.h
Normal file
43
ipc/chromium/src/base/dir_reader_posix.h
Normal 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_
|
||||
35
ipc/chromium/src/base/eintr_wrapper.h
Normal file
35
ipc/chromium/src/base/eintr_wrapper.h
Normal 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_
|
||||
38
ipc/chromium/src/base/file_descriptor_posix.h
Normal file
38
ipc/chromium/src/base/file_descriptor_posix.h
Normal 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_
|
||||
96
ipc/chromium/src/base/file_descriptor_shuffle.cc
Normal file
96
ipc/chromium/src/base/file_descriptor_shuffle.cc
Normal 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
|
||||
83
ipc/chromium/src/base/file_descriptor_shuffle.h
Normal file
83
ipc/chromium/src/base/file_descriptor_shuffle.h
Normal 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_
|
||||
332
ipc/chromium/src/base/file_path.cc
Normal file
332
ipc/chromium/src/base/file_path.cc
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
277
ipc/chromium/src/base/file_path.h
Normal file
277
ipc/chromium/src/base/file_path.h
Normal 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_
|
||||
244
ipc/chromium/src/base/file_util.cc
Normal file
244
ipc/chromium/src/base/file_util.cc
Normal 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
|
||||
229
ipc/chromium/src/base/file_util.h
Normal file
229
ipc/chromium/src/base/file_util.h
Normal 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_
|
||||
35
ipc/chromium/src/base/file_util_mac.mm
Normal file
35
ipc/chromium/src/base/file_util_mac.mm
Normal 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
|
||||
337
ipc/chromium/src/base/file_util_posix.cc
Normal file
337
ipc/chromium/src/base/file_util_posix.cc
Normal 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
|
||||
356
ipc/chromium/src/base/file_util_win.cc
Normal file
356
ipc/chromium/src/base/file_util_win.cc
Normal 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
|
||||
136
ipc/chromium/src/base/hash_tables.h
Normal file
136
ipc/chromium/src/base/hash_tables.h
Normal 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_
|
||||
1176
ipc/chromium/src/base/histogram.cc
Normal file
1176
ipc/chromium/src/base/histogram.cc
Normal file
File diff suppressed because it is too large
Load diff
780
ipc/chromium/src/base/histogram.h
Normal file
780
ipc/chromium/src/base/histogram.h
Normal 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_
|
||||
107
ipc/chromium/src/base/id_map.h
Normal file
107
ipc/chromium/src/base/id_map.h
Normal 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__
|
||||
176
ipc/chromium/src/base/linked_ptr.h
Normal file
176
ipc/chromium/src/base/linked_ptr.h
Normal 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_
|
||||
9
ipc/chromium/src/base/lock.cc
Normal file
9
ipc/chromium/src/base/lock.cc
Normal 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.
|
||||
77
ipc/chromium/src/base/lock.h
Normal file
77
ipc/chromium/src/base/lock.h
Normal 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_
|
||||
79
ipc/chromium/src/base/lock_impl.h
Normal file
79
ipc/chromium/src/base/lock_impl.h
Normal 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_
|
||||
50
ipc/chromium/src/base/lock_impl_posix.cc
Normal file
50
ipc/chromium/src/base/lock_impl_posix.cc
Normal 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);
|
||||
}
|
||||
75
ipc/chromium/src/base/lock_impl_win.cc
Normal file
75
ipc/chromium/src/base/lock_impl_win.cc
Normal 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
|
||||
99
ipc/chromium/src/base/logging.cc
Normal file
99
ipc/chromium/src/base/logging.cc
Normal 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;
|
||||
}
|
||||
130
ipc/chromium/src/base/logging.h
Normal file
130
ipc/chromium/src/base/logging.h
Normal 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_
|
||||
17
ipc/chromium/src/base/mac_util.h
Normal file
17
ipc/chromium/src/base/mac_util.h
Normal 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_
|
||||
34
ipc/chromium/src/base/mac_util.mm
Normal file
34
ipc/chromium/src/base/mac_util.mm
Normal 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
|
||||
579
ipc/chromium/src/base/message_loop.cc
Normal file
579
ipc/chromium/src/base/message_loop.cc
Normal 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
|
||||
558
ipc/chromium/src/base/message_loop.h
Normal file
558
ipc/chromium/src/base/message_loop.h
Normal 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_
|
||||
143
ipc/chromium/src/base/message_pump.h
Normal file
143
ipc/chromium/src/base/message_pump.h
Normal 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_
|
||||
125
ipc/chromium/src/base/message_pump_android.cc
Normal file
125
ipc/chromium/src/base/message_pump_android.cc
Normal 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
|
||||
77
ipc/chromium/src/base/message_pump_android.h
Normal file
77
ipc/chromium/src/base/message_pump_android.h
Normal 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_
|
||||
105
ipc/chromium/src/base/message_pump_default.cc
Normal file
105
ipc/chromium/src/base/message_pump_default.cc
Normal 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
|
||||
43
ipc/chromium/src/base/message_pump_default.h
Normal file
43
ipc/chromium/src/base/message_pump_default.h
Normal 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_
|
||||
341
ipc/chromium/src/base/message_pump_glib.cc
Normal file
341
ipc/chromium/src/base/message_pump_glib.cc
Normal 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
|
||||
147
ipc/chromium/src/base/message_pump_glib.h
Normal file
147
ipc/chromium/src/base/message_pump_glib.h
Normal 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_
|
||||
458
ipc/chromium/src/base/message_pump_libevent.cc
Normal file
458
ipc/chromium/src/base/message_pump_libevent.cc
Normal 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
|
||||
220
ipc/chromium/src/base/message_pump_libevent.h
Normal file
220
ipc/chromium/src/base/message_pump_libevent.h
Normal 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_
|
||||
279
ipc/chromium/src/base/message_pump_mac.h
Normal file
279
ipc/chromium/src/base/message_pump_mac.h
Normal 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_
|
||||
752
ipc/chromium/src/base/message_pump_mac.mm
Normal file
752
ipc/chromium/src/base/message_pump_mac.mm
Normal 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
|
||||
191
ipc/chromium/src/base/message_pump_qt.cc
Normal file
191
ipc/chromium/src/base/message_pump_qt.cc
Normal 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
|
||||
85
ipc/chromium/src/base/message_pump_qt.h
Normal file
85
ipc/chromium/src/base/message_pump_qt.h
Normal 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_
|
||||
547
ipc/chromium/src/base/message_pump_win.cc
Normal file
547
ipc/chromium/src/base/message_pump_win.cc
Normal 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
|
||||
348
ipc/chromium/src/base/message_pump_win.h
Normal file
348
ipc/chromium/src/base/message_pump_win.h
Normal 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_
|
||||
137
ipc/chromium/src/base/object_watcher.cc
Normal file
137
ipc/chromium/src/base/object_watcher.cc
Normal 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
|
||||
96
ipc/chromium/src/base/object_watcher.h
Normal file
96
ipc/chromium/src/base/object_watcher.h
Normal 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_
|
||||
186
ipc/chromium/src/base/observer_list.h
Normal file
186
ipc/chromium/src/base/observer_list.h
Normal 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__
|
||||
588
ipc/chromium/src/base/pickle.cc
Normal file
588
ipc/chromium/src/base/pickle.cc
Normal 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();
|
||||
}
|
||||
326
ipc/chromium/src/base/pickle.h
Normal file
326
ipc/chromium/src/base/pickle.h
Normal 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__
|
||||
48
ipc/chromium/src/base/platform_file.h
Normal file
48
ipc/chromium/src/base/platform_file.h
Normal 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_
|
||||
70
ipc/chromium/src/base/platform_file_posix.cc
Normal file
70
ipc/chromium/src/base/platform_file_posix.cc
Normal 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
|
||||
64
ipc/chromium/src/base/platform_file_win.cc
Normal file
64
ipc/chromium/src/base/platform_file_win.cc
Normal 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
|
||||
87
ipc/chromium/src/base/platform_thread.h
Normal file
87
ipc/chromium/src/base/platform_thread.h
Normal 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_
|
||||
77
ipc/chromium/src/base/platform_thread_mac.mm
Normal file
77
ipc/chromium/src/base/platform_thread_mac.mm
Normal 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());
|
||||
}
|
||||
|
||||
164
ipc/chromium/src/base/platform_thread_posix.cc
Normal file
164
ipc/chromium/src/base/platform_thread_posix.cc
Normal 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);
|
||||
}
|
||||
109
ipc/chromium/src/base/platform_thread_win.cc
Normal file
109
ipc/chromium/src/base/platform_thread_win.cc
Normal 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);
|
||||
}
|
||||
65
ipc/chromium/src/base/port.h
Normal file
65
ipc/chromium/src/base/port.h
Normal 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_
|
||||
81
ipc/chromium/src/base/process.h
Normal file
81
ipc/chromium/src/base/process.h
Normal 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_
|
||||
62
ipc/chromium/src/base/process_posix.cc
Normal file
62
ipc/chromium/src/base/process_posix.cc
Normal 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
Loading…
Add table
Add a link
Reference in a new issue