From a6109beea754f992b59be471f2056a913f095497 Mon Sep 17 00:00:00 2001 From: Zhangyi Chen Date: Mon, 18 Sep 2017 16:07:20 +0800 Subject: [PATCH 01/11] Fix compile issues of unit tests with gcc5.* on ubuntu 16.04 --- test/Makefile | 8 +------- test/sstream_workaround.h | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 test/sstream_workaround.h diff --git a/test/Makefile b/test/Makefile index c4004ad342..d5e558dc0f 100644 --- a/test/Makefile +++ b/test/Makefile @@ -3,7 +3,7 @@ NEED_GTEST=1 NEED_GMOCK=1 include ../config.mk CPPFLAGS+=-DBTHREAD_USE_FAST_PTHREAD_MUTEX -D__const__= -D_GNU_SOURCE -DUSE_SYMBOLIZE -DNO_TCMALLOC -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -CPPFLAGS+=-DUNIT_TEST -Dprivate=public -Dprotected=public -DBVAR_NOT_LINK_DEFAULT_VARIABLES +CPPFLAGS+=-DUNIT_TEST -Dprivate=public -Dprotected=public -DBVAR_NOT_LINK_DEFAULT_VARIABLES --include sstream_workaround.h CXXFLAGS+=$(CPPFLAGS) -g -pipe -Wall -W -fPIC -fstrict-aliasing -Wno-invalid-offsetof -Wno-unused-parameter -fno-omit-frame-pointer -std=c++0x #required by butil/crc32.cc to boost performance for 10x @@ -46,7 +46,6 @@ TEST_BUTIL_SOURCES = \ dir_reader_posix_unittest.cc \ file_path_unittest.cc \ file_unittest.cc \ - path_service_unittest.cc \ scoped_temp_dir_unittest.cc \ guid_unittest.cc \ hash_unittest.cc \ @@ -62,8 +61,6 @@ TEST_BUTIL_SOURCES = \ weak_ptr_unittest.cc \ observer_list_unittest.cc \ file_descriptor_shuffle_unittest.cc \ - process_metrics_unittest.cc \ - process_util_unittest.cc \ rand_util_unittest.cc \ safe_numerics_unittest.cc \ scoped_clear_errno_unittest.cc \ @@ -104,7 +101,6 @@ TEST_BUTIL_SOURCES = \ version_unittest.cc \ logging_unittest.cc \ tuple_unittest.cc \ - xdg_util_unittest.cc \ cacheline_unittest.cpp \ class_name_unittest.cpp \ endpoint_unittest.cpp \ @@ -124,11 +120,9 @@ TEST_BUTIL_SOURCES = \ flat_map_unittest.cpp \ crc32c_unittest.cc \ iobuf_unittest.cc \ - multiprocess_func_list.cc \ test_switches.cc \ test_timeouts.cc \ scoped_locale.cc \ - multiprocess_test.cc \ test_file_util.cc \ test_file_util_linux.cc \ butil_unittest_main.cpp diff --git a/test/sstream_workaround.h b/test/sstream_workaround.h new file mode 100644 index 0000000000..9840bf0a0d --- /dev/null +++ b/test/sstream_workaround.h @@ -0,0 +1,19 @@ +// Copyright (c) 2017 Baidu, Inc. +// Author: Zhangyi Chen (chenzhangyi01@baidu.com) + +#ifndef BUTIL_TEST_SSTREAM_WORKAROUND +#define BUTIL_TEST_SSTREAM_WORKAROUND + +// defining private as public makes it fail to compile sstream with gcc5.x like this: +// "error: ‘struct std::__cxx11::basic_stringbuf<_CharT, _Traits, _Alloc>:: +// __xfer_bufptrs’ redeclared with different access" + +#ifdef private +# undef private +# include +# define private public +#else +# include +#endif + +#endif // BUTIL_TEST_SSTREAM_WORKAROUND From a9cd5bb3cbcb26cc2ac7a48db3b82ebfc1dcd41d Mon Sep 17 00:00:00 2001 From: gejun Date: Mon, 18 Sep 2017 16:10:41 +0800 Subject: [PATCH 02/11] polish descriptions in benchmark.md --- docs/cn/benchmark.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/cn/benchmark.md b/docs/cn/benchmark.md index 0e37cd165a..7eb8e144fb 100644 --- a/docs/cn/benchmark.md +++ b/docs/cn/benchmark.md @@ -16,27 +16,27 @@ NOTE: following tests were done in 2015, which may not reflect latest status of ## UB -com组(INF前身)在08年开发的RPC框架,在百度产品线广泛使用,已被brpc代替。UB的每个请求独占一个连接(连接池),在大规模服务中每台机器都需要保持大量的连接,限制了其使用场景,像百度的分布式系统都不用UB。UB只支持nshead+mcpack协议,也没怎么考虑扩展性,所以增加新协议和新功能往往要调整大段代码,在实践中大部分人“知难而退”了。UB缺乏调试和运维接口,服务的运行状态对用户基本是黑盒,只能靠低效地打日志来追踪问题,服务出现问题时常要拉上维护者一起排查,效率很低。UB有多个变种: +百度在08年开发的RPC框架,在百度产品线广泛使用,已被brpc代替。UB的每个请求独占一个连接(连接池),在大规模服务中每台机器都需要保持大量的连接,限制了其使用场景,像百度的分布式系统没有用UB。UB只支持nshead+mcpack协议,也没怎么考虑扩展性,所以增加新协议和新功能往往要调整大段代码,在实践中大部分人“知难而退”了。UB缺乏调试和运维接口,服务的运行状态对用户基本是黑盒,只能靠低效地打日志来追踪问题,服务出现问题时常要拉上维护者一起排查,效率很低。UB有多个变种: -* ubrpc:INF在10年基于UB开发的RPC框架,用.idl文件(类似.proto)描述数据的schema,而不是手动打包。这个RPC有被使用,但不广泛。 +* ubrpc:百度在10年基于UB开发的RPC框架,用.idl文件(类似.proto)描述数据的schema,而不是手动打包。这个RPC有被使用,但不广泛。 -- nova_pbrpc:网盟在12年基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,协议是nshead + user's protobuf。 -- public_pbrpc:INF在13年初基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,但协议与nova_pbrpc不同,大致是nshead + meta protobuf。meta protobuf中有个string字段包含user's protobuf。由于用户数据要序列化两次,这个RPC的性能很差,没有被推广开来。 +- nova_pbrpc:百度网盟团队在12年基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,协议是nshead + user's protobuf。 +- public_pbrpc:百度在13年初基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,但协议与nova_pbrpc不同,大致是nshead + meta protobuf。meta protobuf中有个string字段包含user's protobuf。由于用户数据要序列化两次,这个RPC的性能很差,没有被推广开来。 -我们以在网盟广泛使用的nova_pbrpc为UB的代表。测试时其代码为r10500。早期的UB支持CPOOL和XPOOL,分别使用[select](http://linux.die.net/man/2/select)和[leader-follower模型](http://kircher-schwanninger.de/michael/publications/lf.pdf),后来提供了EPOOL,使用[epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)处理多路连接。鉴于产品线大都是用EPOOL模型,我们的UB配置也使用EPOOL。UB只支持[连接池](client.md#连接方式),结果用“**ubrpc_mc**"指代(mc代表"multiple +我们以在百度网盟团队广泛使用的nova_pbrpc为UB的代表。测试时其代码为r10500。早期的UB支持CPOOL和XPOOL,分别使用[select](http://linux.die.net/man/2/select)和[leader-follower模型](http://kircher-schwanninger.de/michael/publications/lf.pdf),后来提供了EPOOL,使用[epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)处理多路连接。鉴于产品线大都是用EPOOL模型,我们的UB配置也使用EPOOL。UB只支持[连接池](client.md#连接方式),结果用“**ubrpc_mc**"指代(mc代表"multiple connection")。虽然这个名称不太准确(见上文对ubrpc的介绍),但在本文的语境下,请默认ubrpc = UB。 ## hulu-pbrpc -INF在13年基于saber(kylin变种)和protobuf实现的RPC框架,hulu在实现上有较多问题:未封装的引用计数,混乱的生命周期,充斥的race conditions和ABA problems,运行质量不可靠,比如短链接从来没有能正常运行过。之后迅速被brpc代替,测试时其代码为`pbrpc_2-0-15-27959_PD_BL`。hulu-pbrpc只支持单连接,结果用“**hulu-pbrpc**"指代。 +百度在13年基于saber(kylin变种)和protobuf实现的RPC框架,hulu在多线程实现上有较多问题,已被brpc代替,测试时其代码为`pbrpc_2-0-15-27959_PD_BL`。hulu-pbrpc只支持单连接,结果用“**hulu-pbrpc**"指代。 ## brpc -INF在2014年底开发至今的rpc产品,支持百度内所有协议(不限于protobuf),并第一次统一了百度内主要分布式系统的RPC框架。测试时代码为r31906(开发请使用@ci-base保持更新)。brpc既支持单连接也支持连接池,前者的结果用"**baidu-rpc**"指代,后者用“**baidu-rpc_mc**"指代。 +INF在2014年底开发至今的rpc产品,支持百度内所有协议(不限于protobuf),并第一次统一了百度主要分布式系统和业务线的RPC框架。测试时代码为r31906。brpc既支持单连接也支持连接池,前者的结果用"**baidu-rpc**"指代,后者用“**baidu-rpc_mc**"指代。 ## sofa-pbrpc -大搜在13年基于boost::asio和protobuf实现的RPC框架,有多个版本,咨询相关同学后,确认ps/opensource下的和github上的较新,且会定期同步。故测试使用使用ps/opensource下的版本。测试时其代码为`sofa-pbrpc_1-0-2_BRANCH`。sofa-pbrpc只支持单连接,结果用“**sofa-pbrpc**”指代。 +百度大搜团队在13年基于boost::asio和protobuf实现的RPC框架,有多个版本,咨询相关同学后,确认ps/opensource下的和github上的较新,且会定期同步。故测试使用使用ps/opensource下的版本。测试时其代码为`sofa-pbrpc_1-0-2_BRANCH`。sofa-pbrpc只支持单连接,结果用“**sofa-pbrpc**”指代。 ## apache thrift From 1e2d716f5e4bc05edd0b1f827c3225e6dd39f21a Mon Sep 17 00:00:00 2001 From: Zhangyi Chen Date: Mon, 18 Sep 2017 16:40:51 +0800 Subject: [PATCH 03/11] Shrink butil --- .gitignore | 5 + Makefile | 25 - src/butil/base_paths.h | 55 -- src/butil/base_paths_android.h | 25 - src/butil/base_paths_mac.h | 24 - src/butil/base_paths_mac.mm | 114 --- src/butil/base_paths_posix.cc | 116 --- src/butil/base_paths_posix.h | 27 - src/butil/command_line.cc | 442 --------- src/butil/command_line.h | 194 ---- src/butil/file_util_posix.cc | 1 - src/butil/logging.cc | 1 - src/butil/memory/shared_memory.h | 325 ------- src/butil/memory/shared_memory_posix.cc | 479 ---------- src/butil/path_service.cc | 341 ------- src/butil/path_service.h | 92 -- src/butil/process/internal_linux.cc | 189 ---- src/butil/process/internal_linux.h | 90 -- src/butil/process/kill.cc | 26 - src/butil/process/kill.h | 162 ---- src/butil/process/kill_mac.cc | 172 ---- src/butil/process/kill_posix.cc | 482 ---------- src/butil/process/launch.cc | 52 -- src/butil/process/launch.h | 275 ------ src/butil/process/launch_mac.cc | 48 - src/butil/process/launch_posix.cc | 665 ------------- src/butil/process/memory.cc | 30 - src/butil/process/memory.h | 82 -- src/butil/process/memory_linux.cc | 213 ----- src/butil/process/memory_mac.mm | 751 --------------- src/butil/process/memory_stubs.cc | 19 - src/butil/process/process.h | 70 -- src/butil/process/process_handle.h | 94 -- src/butil/process/process_handle_freebsd.cc | 40 - src/butil/process/process_handle_linux.cc | 30 - src/butil/process/process_handle_mac.cc | 36 - src/butil/process/process_handle_openbsd.cc | 49 - src/butil/process/process_handle_posix.cc | 49 - src/butil/process/process_info.h | 25 - src/butil/process/process_info_linux.cc | 27 - src/butil/process/process_info_mac.cc | 31 - src/butil/process/process_iterator.cc | 65 -- src/butil/process/process_iterator.h | 185 ---- src/butil/process/process_iterator_freebsd.cc | 126 --- src/butil/process/process_iterator_linux.cc | 137 --- src/butil/process/process_iterator_mac.cc | 135 --- src/butil/process/process_iterator_openbsd.cc | 128 --- src/butil/process/process_linux.cc | 141 --- src/butil/process/process_metrics.cc | 45 - src/butil/process/process_metrics.h | 379 -------- src/butil/process/process_metrics_freebsd.cc | 122 --- src/butil/process/process_metrics_linux.cc | 812 ---------------- src/butil/process/process_metrics_mac.cc | 365 -------- src/butil/process/process_metrics_openbsd.cc | 161 ---- src/butil/process/process_metrics_posix.cc | 72 -- src/butil/process/process_posix.cc | 73 -- src/butil/sys_info.cc | 56 -- src/butil/sys_info.h | 141 --- src/butil/sys_info_freebsd.cc | 36 - src/butil/sys_info_internal.h | 34 - src/butil/sys_info_linux.cc | 100 -- src/butil/sys_info_mac.cc | 88 -- src/butil/sys_info_openbsd.cc | 75 -- src/butil/sys_info_posix.cc | 133 --- test/Makefile | 4 - test/butil_unittest_main.cpp | 11 - test/command_line_unittest.cc | 364 -------- test/endpoint_unittest.cpp | 2 + test/file_util_unittest.cc | 2 - test/multiprocess_func_list.cc | 57 -- test/multiprocess_test.cc | 59 -- test/multiprocess_test.h | 131 --- test/path_service_unittest.cc | 250 ----- test/process_metrics_unittest.cc | 368 -------- test/process_util_unittest.cc | 883 ------------------ test/singleton_unittest.cc | 1 - test/stack_trace_unittest.cc | 28 - test/test_file_util.cc | 23 - test/test_file_util.h | 91 -- test/test_timeouts.cc | 111 --- test/test_timeouts.h | 66 -- 81 files changed, 7 insertions(+), 12026 deletions(-) delete mode 100644 src/butil/base_paths.h delete mode 100644 src/butil/base_paths_android.h delete mode 100644 src/butil/base_paths_mac.h delete mode 100644 src/butil/base_paths_mac.mm delete mode 100644 src/butil/base_paths_posix.cc delete mode 100644 src/butil/base_paths_posix.h delete mode 100644 src/butil/command_line.cc delete mode 100644 src/butil/command_line.h delete mode 100644 src/butil/memory/shared_memory.h delete mode 100644 src/butil/memory/shared_memory_posix.cc delete mode 100644 src/butil/path_service.cc delete mode 100644 src/butil/path_service.h delete mode 100644 src/butil/process/internal_linux.cc delete mode 100644 src/butil/process/internal_linux.h delete mode 100644 src/butil/process/kill.cc delete mode 100644 src/butil/process/kill.h delete mode 100644 src/butil/process/kill_mac.cc delete mode 100644 src/butil/process/kill_posix.cc delete mode 100644 src/butil/process/launch.cc delete mode 100644 src/butil/process/launch.h delete mode 100644 src/butil/process/launch_mac.cc delete mode 100644 src/butil/process/launch_posix.cc delete mode 100644 src/butil/process/memory.cc delete mode 100644 src/butil/process/memory.h delete mode 100644 src/butil/process/memory_linux.cc delete mode 100644 src/butil/process/memory_mac.mm delete mode 100644 src/butil/process/memory_stubs.cc delete mode 100644 src/butil/process/process.h delete mode 100644 src/butil/process/process_handle.h delete mode 100644 src/butil/process/process_handle_freebsd.cc delete mode 100644 src/butil/process/process_handle_linux.cc delete mode 100644 src/butil/process/process_handle_mac.cc delete mode 100644 src/butil/process/process_handle_openbsd.cc delete mode 100644 src/butil/process/process_handle_posix.cc delete mode 100644 src/butil/process/process_info.h delete mode 100644 src/butil/process/process_info_linux.cc delete mode 100644 src/butil/process/process_info_mac.cc delete mode 100644 src/butil/process/process_iterator.cc delete mode 100644 src/butil/process/process_iterator.h delete mode 100644 src/butil/process/process_iterator_freebsd.cc delete mode 100644 src/butil/process/process_iterator_linux.cc delete mode 100644 src/butil/process/process_iterator_mac.cc delete mode 100644 src/butil/process/process_iterator_openbsd.cc delete mode 100644 src/butil/process/process_linux.cc delete mode 100644 src/butil/process/process_metrics.cc delete mode 100644 src/butil/process/process_metrics.h delete mode 100644 src/butil/process/process_metrics_freebsd.cc delete mode 100644 src/butil/process/process_metrics_linux.cc delete mode 100644 src/butil/process/process_metrics_mac.cc delete mode 100644 src/butil/process/process_metrics_openbsd.cc delete mode 100644 src/butil/process/process_metrics_posix.cc delete mode 100644 src/butil/process/process_posix.cc delete mode 100644 src/butil/sys_info.cc delete mode 100644 src/butil/sys_info.h delete mode 100644 src/butil/sys_info_freebsd.cc delete mode 100644 src/butil/sys_info_internal.h delete mode 100644 src/butil/sys_info_linux.cc delete mode 100644 src/butil/sys_info_mac.cc delete mode 100644 src/butil/sys_info_openbsd.cc delete mode 100644 src/butil/sys_info_posix.cc delete mode 100644 test/command_line_unittest.cc delete mode 100644 test/multiprocess_func_list.cc delete mode 100644 test/multiprocess_test.cc delete mode 100644 test/multiprocess_test.h delete mode 100644 test/path_service_unittest.cc delete mode 100644 test/process_metrics_unittest.cc delete mode 100644 test/process_util_unittest.cc delete mode 100644 test/test_file_util.cc delete mode 100644 test/test_file_util.h delete mode 100644 test/test_timeouts.cc delete mode 100644 test/test_timeouts.h diff --git a/.gitignore b/.gitignore index 1ba5f2c0f0..06ea21259b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,9 +9,14 @@ *.pb.cc *.pb.h *.prof +*.so /output /test/output #ignore hidden files .* *.swp + +#ignore auto-generated files +config.mk +src/butil/config.h diff --git a/Makefile b/Makefile index f5087e6112..ae56177a1a 100644 --- a/Makefile +++ b/Makefile @@ -52,15 +52,12 @@ BUTIL_SOURCES = \ src/butil/at_exit.cc \ src/butil/atomicops_internals_x86_gcc.cc \ src/butil/barrier_closure.cc \ - src/butil/base_paths.cc \ - src/butil/base_paths_posix.cc \ src/butil/base64.cc \ src/butil/base_switches.cc \ src/butil/big_endian.cc \ src/butil/bind_helpers.cc \ src/butil/callback_helpers.cc \ src/butil/callback_internal.cc \ - src/butil/command_line.cc \ src/butil/cpu.cc \ src/butil/debug/alias.cc \ src/butil/debug/asan_invalid_access.cc \ @@ -94,29 +91,10 @@ BUTIL_SOURCES = \ src/butil/memory/aligned_memory.cc \ src/butil/memory/ref_counted.cc \ src/butil/memory/ref_counted_memory.cc \ - src/butil/memory/shared_memory_posix.cc \ src/butil/memory/singleton.cc \ src/butil/memory/weak_ptr.cc \ - src/butil/nix/mime_util_xdg.cc \ - src/butil/nix/xdg_util.cc \ - src/butil/path_service.cc \ src/butil/posix/file_descriptor_shuffle.cc \ src/butil/posix/global_descriptors.cc \ - src/butil/process/internal_linux.cc \ - src/butil/process/kill.cc \ - src/butil/process/kill_posix.cc \ - src/butil/process/launch.cc \ - src/butil/process/launch_posix.cc \ - src/butil/process/process_handle_linux.cc \ - src/butil/process/process_handle_posix.cc \ - src/butil/process/process_info_linux.cc \ - src/butil/process/process_iterator.cc \ - src/butil/process/process_iterator_linux.cc \ - src/butil/process/process_linux.cc \ - src/butil/process/process_metrics.cc \ - src/butil/process/process_metrics_linux.cc \ - src/butil/process/process_metrics_posix.cc \ - src/butil/process/process_posix.cc \ src/butil/rand_util.cc \ src/butil/rand_util_posix.cc \ src/butil/fast_rand.cpp \ @@ -139,9 +117,6 @@ BUTIL_SOURCES = \ src/butil/synchronization/cancellation_flag.cc \ src/butil/synchronization/condition_variable_posix.cc \ src/butil/synchronization/waitable_event_posix.cc \ - src/butil/sys_info.cc \ - src/butil/sys_info_linux.cc \ - src/butil/sys_info_posix.cc \ src/butil/threading/non_thread_safe_impl.cc \ src/butil/threading/platform_thread_linux.cc \ src/butil/threading/platform_thread_posix.cc \ diff --git a/src/butil/base_paths.h b/src/butil/base_paths.h deleted file mode 100644 index a20d260f3f..0000000000 --- a/src/butil/base_paths.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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_BASE_PATHS_H_ -#define BASE_BASE_PATHS_H_ - -// This file declares path keys for the base module. These can be used with -// the PathService to access various special directories and files. - -#include "butil/build_config.h" - -#if defined(OS_WIN) -#include "butil/base_paths_win.h" -#elif defined(OS_MACOSX) -#include "butil/base_paths_mac.h" -#elif defined(OS_ANDROID) -#include "butil/base_paths_android.h" -#endif - -#if defined(OS_POSIX) -#include "butil/base_paths_posix.h" -#endif - -namespace butil { - -enum BasePathKey { - PATH_START = 0, - - DIR_CURRENT, // Current directory. - DIR_EXE, // Directory containing FILE_EXE. - DIR_MODULE, // Directory containing FILE_MODULE. - DIR_TEMP, // Temporary directory. - DIR_HOME, // User's root home directory. On Windows this will look - // like "C:\Users\you" (or on XP - // "C:\Document and Settings\you") which isn't necessarily - // a great place to put files. - FILE_EXE, // Path and filename of the current executable. - FILE_MODULE, // Path and filename of the module containing the code for - // the PathService (which could differ from FILE_EXE if the - // PathService were compiled into a shared object, for - // example). - DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful - // for tests that need to locate various resources. It - // should not be used outside of test code. - DIR_USER_DESKTOP, // The current user's Desktop. - - DIR_TEST_DATA, // Used only for testing. - - PATH_END -}; - -} // namespace butil - -#endif // BASE_BASE_PATHS_H_ diff --git a/src/butil/base_paths_android.h b/src/butil/base_paths_android.h deleted file mode 100644 index b2b54f695a..0000000000 --- a/src/butil/base_paths_android.h +++ /dev/null @@ -1,25 +0,0 @@ -// 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_BASE_PATHS_ANDROID_H_ -#define BASE_BASE_PATHS_ANDROID_H_ - -// This file declares Android-specific path keys for the base module. -// These can be used with the PathService to access various special -// directories and files. - -namespace butil { - -enum { - PATH_ANDROID_START = 300, - - DIR_ANDROID_APP_DATA, // Directory where to put Android app's data. - DIR_ANDROID_EXTERNAL_STORAGE, // Android external storage directory. - - PATH_ANDROID_END -}; - -} // namespace butil - -#endif // BASE_BASE_PATHS_ANDROID_H_ diff --git a/src/butil/base_paths_mac.h b/src/butil/base_paths_mac.h deleted file mode 100644 index 394936bca1..0000000000 --- a/src/butil/base_paths_mac.h +++ /dev/null @@ -1,24 +0,0 @@ -// 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_BASE_PATHS_MAC_H_ -#define BASE_BASE_PATHS_MAC_H_ - -// This file declares Mac-specific path keys for the base module. -// These can be used with the PathService to access various special -// directories and files. - -namespace butil { - -enum { - PATH_MAC_START = 200, - - DIR_APP_DATA, // ~/Library/Application Support - - PATH_MAC_END -}; - -} // namespace butil - -#endif // BASE_BASE_PATHS_MAC_H_ diff --git a/src/butil/base_paths_mac.mm b/src/butil/base_paths_mac.mm deleted file mode 100644 index 8db9ad8914..0000000000 --- a/src/butil/base_paths_mac.mm +++ /dev/null @@ -1,114 +0,0 @@ -// 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. - -// Defines base::PathProviderMac which replaces base::PathProviderPosix for Mac -// in base/path_service.cc. - -#include -#import -#include - -#include "base/base_paths.h" -#include "base/compiler_specific.h" -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/logging.h" -#include "base/mac/foundation_util.h" -#include "base/path_service.h" -#include "base/strings/string_util.h" -#include "base/build_config.h" - -namespace { - -void GetNSExecutablePath(base::FilePath* path) { - DCHECK(path); - // Executable path can have relative references ("..") depending on - // how the app was launched. - uint32_t executable_length = 0; - _NSGetExecutablePath(NULL, &executable_length); - DCHECK_GT(executable_length, 1u); - std::string executable_path; - int rv = _NSGetExecutablePath(WriteInto(&executable_path, executable_length), - &executable_length); - DCHECK_EQ(rv, 0); - - // _NSGetExecutablePath may return paths containing ./ or ../ which makes - // FilePath::DirName() work incorrectly, convert it to absolute path so that - // paths such as DIR_SOURCE_ROOT can work, since we expect absolute paths to - // be returned here. - *path = base::MakeAbsoluteFilePath(base::FilePath(executable_path)); -} - -// Returns true if the module for |address| is found. |path| will contain -// the path to the module. Note that |path| may not be absolute. -bool GetModulePathForAddress(base::FilePath* path, - const void* address) WARN_UNUSED_RESULT; - -bool GetModulePathForAddress(base::FilePath* path, const void* address) { - Dl_info info; - if (dladdr(address, &info) == 0) - return false; - *path = base::FilePath(info.dli_fname); - return true; -} - -} // namespace - -namespace base { - -bool PathProviderMac(int key, base::FilePath* result) { - switch (key) { - case base::FILE_EXE: - GetNSExecutablePath(result); - return true; - case base::FILE_MODULE: - return GetModulePathForAddress(result, - reinterpret_cast(&base::PathProviderMac)); - case base::DIR_APP_DATA: { - bool success = base::mac::GetUserDirectory(NSApplicationSupportDirectory, - result); -#if defined(OS_IOS) - // On IOS, this directory does not exist unless it is created explicitly. - if (success && !base::PathExists(*result)) - success = base::CreateDirectory(*result); -#endif // defined(OS_IOS) - return success; - } - case base::DIR_SOURCE_ROOT: - // Go through PathService to catch overrides. - if (!PathService::Get(base::FILE_EXE, result)) - return false; - - // Start with the executable's directory. - *result = result->DirName(); - -#if !defined(OS_IOS) - if (base::mac::AmIBundled()) { - // The bundled app executables (Chromium, TestShell, etc) live five - // levels down, eg: - // src/xcodebuild/{Debug|Release}/Chromium.app/Contents/MacOS/Chromium - *result = result->DirName().DirName().DirName().DirName().DirName(); - } else { - // Unit tests execute two levels deep from the source root, eg: - // src/xcodebuild/{Debug|Release}/base_unittests - *result = result->DirName().DirName(); - } -#endif - return true; - case base::DIR_USER_DESKTOP: -#if defined(OS_IOS) - // iOS does not have desktop directories. - NOTIMPLEMENTED(); - return false; -#else - return base::mac::GetUserDirectory(NSDesktopDirectory, result); -#endif - case base::DIR_CACHE: - return base::mac::GetUserDirectory(NSCachesDirectory, result); - default: - return false; - } -} - -} // namespace base diff --git a/src/butil/base_paths_posix.cc b/src/butil/base_paths_posix.cc deleted file mode 100644 index 87497198b8..0000000000 --- a/src/butil/base_paths_posix.cc +++ /dev/null @@ -1,116 +0,0 @@ -// 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. - -// Defines butil::PathProviderPosix, default path provider on POSIX OSes that -// don't have their own base_paths_OS.cc implementation (i.e. all but Mac and -// Android). - -#include -#include - -#include "butil/base_paths.h" -#include "butil/environment.h" -#include "butil/file_util.h" -#include "butil/files/file_path.h" -#include "butil/logging.h" -#include "butil/memory/scoped_ptr.h" -#include "butil/nix/xdg_util.h" -#include "butil/path_service.h" -#include "butil/process/process_metrics.h" -#include "butil/build_config.h" - -#if defined(OS_FREEBSD) -#include -#include -#elif defined(OS_SOLARIS) -#include -#endif - -namespace butil { - -bool PathProviderPosix(int key, FilePath* result) { - FilePath path; - switch (key) { - case butil::FILE_EXE: - case butil::FILE_MODULE: { // TODO(evanm): is this correct? -#if defined(OS_LINUX) - FilePath bin_dir; - if (!ReadSymbolicLink(FilePath(kProcSelfExe), &bin_dir)) { - NOTREACHED() << "Unable to resolve " << kProcSelfExe << "."; - return false; - } - *result = bin_dir; - return true; -#elif defined(OS_FREEBSD) - int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; - char bin_dir[PATH_MAX + 1]; - size_t length = sizeof(bin_dir); - // Upon return, |length| is the number of bytes written to |bin_dir| - // including the string terminator. - int error = sysctl(name, 4, bin_dir, &length, NULL, 0); - if (error < 0 || length <= 1) { - NOTREACHED() << "Unable to resolve path."; - return false; - } - *result = FilePath(FilePath::StringType(bin_dir, length - 1)); - return true; -#elif defined(OS_SOLARIS) - char bin_dir[PATH_MAX + 1]; - if (realpath(getexecname(), bin_dir) == NULL) { - NOTREACHED() << "Unable to resolve " << getexecname() << "."; - return false; - } - *result = FilePath(bin_dir); - return true; -#elif defined(OS_OPENBSD) - // There is currently no way to get the executable path on OpenBSD - char* cpath; - if ((cpath = getenv("CHROME_EXE_PATH")) != NULL) - *result = FilePath(cpath); - else - *result = FilePath("/usr/local/chrome/chrome"); - return true; -#endif - } - case butil::DIR_SOURCE_ROOT: { - // Allow passing this in the environment, for more flexibility in build - // tree configurations (sub-project builds, gyp --output_dir, etc.) - scoped_ptr env(butil::Environment::Create()); - std::string cr_source_root; - if (env->GetVar("CR_SOURCE_ROOT", &cr_source_root)) { - path = FilePath(cr_source_root); - if (butil::PathExists(path)) { - *result = path; - return true; - } else { - DLOG(WARNING) << "CR_SOURCE_ROOT is set, but it appears to not " - << "point to a directory."; - } - } - // On POSIX, unit tests execute two levels deep from the source root. - // For example: out/{Debug|Release}/net_unittest - if (PathService::Get(butil::DIR_EXE, &path)) { - *result = path.DirName().DirName(); - return true; - } - - DLOG(ERROR) << "Couldn't find your source root. " - << "Try running from your chromium/src directory."; - return false; - } - case butil::DIR_USER_DESKTOP: - *result = butil::nix::GetXDGUserDirectory("DESKTOP", "Desktop"); - return true; - case butil::DIR_CACHE: { - scoped_ptr env(butil::Environment::Create()); - FilePath cache_dir(butil::nix::GetXDGDirectory(env.get(), "XDG_CACHE_HOME", - ".cache")); - *result = cache_dir; - return true; - } - } - return false; -} - -} // namespace butil diff --git a/src/butil/base_paths_posix.h b/src/butil/base_paths_posix.h deleted file mode 100644 index 99ebb0756b..0000000000 --- a/src/butil/base_paths_posix.h +++ /dev/null @@ -1,27 +0,0 @@ -// 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_BASE_PATHS_POSIX_H_ -#define BASE_BASE_PATHS_POSIX_H_ - -// This file declares windows-specific path keys for the base module. -// These can be used with the PathService to access various special -// directories and files. - -namespace butil { - -enum { - PATH_POSIX_START = 400, - - DIR_CACHE, // Directory where to put cache data. Note this is - // *not* where the browser cache lives, but the - // browser cache can be a subdirectory. - // This is $XDG_CACHE_HOME on Linux and - // ~/Library/Caches on Mac. - PATH_POSIX_END -}; - -} // namespace butil - -#endif // BASE_BASE_PATHS_POSIX_H_ diff --git a/src/butil/command_line.cc b/src/butil/command_line.cc deleted file mode 100644 index d51ab3072d..0000000000 --- a/src/butil/command_line.cc +++ /dev/null @@ -1,442 +0,0 @@ -// 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. - -#include "butil/command_line.h" - -#include -#include - -#include "butil/basictypes.h" -#include "butil/files/file_path.h" -#include "butil/logging.h" -#include "butil/strings/string_split.h" -#include "butil/strings/string_util.h" -#include "butil/strings/utf_string_conversions.h" -#include "butil/build_config.h" - -#if defined(OS_WIN) -#include -#include -#endif - -namespace butil { - -CommandLine* CommandLine::current_process_commandline_ = NULL; - -namespace { - -const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--"); -const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("="); - -// Since we use a lazy match, make sure that longer versions (like "--") are -// listed before shorter versions (like "-") of similar prefixes. -#if defined(OS_WIN) -// By putting slash last, we can control whether it is treaded as a switch -// value by changing the value of switch_prefix_count to be one less than -// the array size. -const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; -#elif defined(OS_POSIX) -// Unixes don't use slash as a switch. -const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"}; -#endif -size_t switch_prefix_count = arraysize(kSwitchPrefixes); - -size_t GetSwitchPrefixLength(const CommandLine::StringType& string) { - for (size_t i = 0; i < switch_prefix_count; ++i) { - CommandLine::StringType prefix(kSwitchPrefixes[i]); - if (string.compare(0, prefix.length(), prefix) == 0) - return prefix.length(); - } - return 0; -} - -// Fills in |switch_string| and |switch_value| if |string| is a switch. -// This will preserve the input switch prefix in the output |switch_string|. -bool IsSwitch(const CommandLine::StringType& string, - CommandLine::StringType* switch_string, - CommandLine::StringType* switch_value) { - switch_string->clear(); - switch_value->clear(); - size_t prefix_length = GetSwitchPrefixLength(string); - if (prefix_length == 0 || prefix_length == string.length()) - return false; - - const size_t equals_position = string.find(kSwitchValueSeparator); - *switch_string = string.substr(0, equals_position); - if (equals_position != CommandLine::StringType::npos) - *switch_value = string.substr(equals_position + 1); - return true; -} - -// Append switches and arguments, keeping switches before arguments. -void AppendSwitchesAndArguments(CommandLine& command_line, - const CommandLine::StringVector& argv) { - bool parse_switches = true; - for (size_t i = 1; i < argv.size(); ++i) { - CommandLine::StringType arg = argv[i]; - TrimWhitespace(arg, TRIM_ALL, &arg); - - CommandLine::StringType switch_string; - CommandLine::StringType switch_value; - parse_switches &= (arg != kSwitchTerminator); - if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { -#if defined(OS_WIN) - command_line.AppendSwitchNative(UTF16ToASCII(switch_string), - switch_value); -#elif defined(OS_POSIX) - command_line.AppendSwitchNative(switch_string, switch_value); -#endif - } else { - command_line.AppendArgNative(arg); - } - } -} - -// Lowercase switches for backwards compatiblity *on Windows*. -std::string LowerASCIIOnWindows(const std::string& string) { -#if defined(OS_WIN) - return StringToLowerASCII(string); -#elif defined(OS_POSIX) - return string; -#endif -} - - -#if defined(OS_WIN) -// Quote a string as necessary for CommandLineToArgvW compatiblity *on Windows*. -std::wstring QuoteForCommandLineToArgvW(const std::wstring& arg) { - // We follow the quoting rules of CommandLineToArgvW. - // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx - if (arg.find_first_of(L" \\\"") == std::wstring::npos) { - // No quoting necessary. - return arg; - } - - std::wstring out; - out.push_back(L'"'); - for (size_t i = 0; i < arg.size(); ++i) { - if (arg[i] == '\\') { - // Find the extent of this run of backslashes. - size_t start = i, end = start + 1; - for (; end < arg.size() && arg[end] == '\\'; ++end) - /* empty */; - size_t backslash_count = end - start; - - // Backslashes are escapes only if the run is followed by a double quote. - // Since we also will end the string with a double quote, we escape for - // either a double quote or the end of the string. - if (end == arg.size() || arg[end] == '"') { - // To quote, we need to output 2x as many backslashes. - backslash_count *= 2; - } - for (size_t j = 0; j < backslash_count; ++j) - out.push_back('\\'); - - // Advance i to one before the end to balance i++ in loop. - i = end - 1; - } else if (arg[i] == '"') { - out.push_back('\\'); - out.push_back('"'); - } else { - out.push_back(arg[i]); - } - } - out.push_back('"'); - - return out; -} -#endif - -} // namespace - -CommandLine::CommandLine(NoProgram no_program) - : argv_(1), - begin_args_(1) { -} - -CommandLine::CommandLine(const FilePath& program) - : argv_(1), - begin_args_(1) { - SetProgram(program); -} - -CommandLine::CommandLine(int argc, const CommandLine::CharType* const* argv) - : argv_(1), - begin_args_(1) { - InitFromArgv(argc, argv); -} - -CommandLine::CommandLine(const StringVector& argv) - : argv_(1), - begin_args_(1) { - InitFromArgv(argv); -} - -CommandLine::~CommandLine() { -} - -#if defined(OS_WIN) -// static -void CommandLine::set_slash_is_not_a_switch() { - // The last switch prefix should be slash, so adjust the size to skip it. - DCHECK(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/") == 0); - switch_prefix_count = arraysize(kSwitchPrefixes) - 1; -} -#endif - -// static -bool CommandLine::Init(int argc, const char* const* argv) { - if (current_process_commandline_) { - // If this is intentional, Reset() must be called first. If we are using - // the shared build mode, we have to share a single object across multiple - // shared libraries. - return false; - } - - current_process_commandline_ = new CommandLine(NO_PROGRAM); -#if defined(OS_WIN) - current_process_commandline_->ParseFromString(::GetCommandLineW()); -#elif defined(OS_POSIX) - current_process_commandline_->InitFromArgv(argc, argv); -#endif - - return true; -} - -// static -void CommandLine::Reset() { - DCHECK(current_process_commandline_); - delete current_process_commandline_; - current_process_commandline_ = NULL; -} - -// static -CommandLine* CommandLine::ForCurrentProcess() { - DCHECK(current_process_commandline_); - return current_process_commandline_; -} - -// static -bool CommandLine::InitializedForCurrentProcess() { - return !!current_process_commandline_; -} - -#if defined(OS_WIN) -// static -CommandLine CommandLine::FromString(const std::wstring& command_line) { - CommandLine cmd(NO_PROGRAM); - cmd.ParseFromString(command_line); - return cmd; -} -#endif - -void CommandLine::InitFromArgv(int argc, - const CommandLine::CharType* const* argv) { - StringVector new_argv; - for (int i = 0; i < argc; ++i) - new_argv.push_back(argv[i]); - InitFromArgv(new_argv); -} - -void CommandLine::InitFromArgv(const StringVector& argv) { - argv_ = StringVector(1); - switches_.clear(); - begin_args_ = 1; - SetProgram(argv.empty() ? FilePath() : FilePath(argv[0])); - AppendSwitchesAndArguments(*this, argv); -} - -CommandLine::StringType CommandLine::GetCommandLineString() const { - StringType string(argv_[0]); -#if defined(OS_WIN) - string = QuoteForCommandLineToArgvW(string); -#endif - StringType params(GetArgumentsString()); - if (!params.empty()) { - string.append(StringType(FILE_PATH_LITERAL(" "))); - string.append(params); - } - return string; -} - -CommandLine::StringType CommandLine::GetArgumentsString() const { - StringType params; - // Append switches and arguments. - bool parse_switches = true; - for (size_t i = 1; i < argv_.size(); ++i) { - StringType arg = argv_[i]; - StringType switch_string; - StringType switch_value; - parse_switches &= arg != kSwitchTerminator; - if (i > 1) - params.append(StringType(FILE_PATH_LITERAL(" "))); - if (parse_switches && IsSwitch(arg, &switch_string, &switch_value)) { - params.append(switch_string); - if (!switch_value.empty()) { -#if defined(OS_WIN) - switch_value = QuoteForCommandLineToArgvW(switch_value); -#endif - params.append(kSwitchValueSeparator + switch_value); - } - } - else { -#if defined(OS_WIN) - arg = QuoteForCommandLineToArgvW(arg); -#endif - params.append(arg); - } - } - return params; -} - -FilePath CommandLine::GetProgram() const { - return FilePath(argv_[0]); -} - -void CommandLine::SetProgram(const FilePath& program) { - TrimWhitespace(program.value(), TRIM_ALL, &argv_[0]); -} - -bool CommandLine::HasSwitch(const std::string& switch_string) const { - return switches_.find(LowerASCIIOnWindows(switch_string)) != switches_.end(); -} - -std::string CommandLine::GetSwitchValueASCII( - const std::string& switch_string) const { - StringType value = GetSwitchValueNative(switch_string); - if (!IsStringASCII(value)) { - DLOG(WARNING) << "Value of switch (" << switch_string << ") must be ASCII."; - return std::string(); - } -#if defined(OS_WIN) - return UTF16ToASCII(value); -#else - return value; -#endif -} - -FilePath CommandLine::GetSwitchValuePath( - const std::string& switch_string) const { - return FilePath(GetSwitchValueNative(switch_string)); -} - -CommandLine::StringType CommandLine::GetSwitchValueNative( - const std::string& switch_string) const { - SwitchMap::const_iterator result = - switches_.find(LowerASCIIOnWindows(switch_string)); - return result == switches_.end() ? StringType() : result->second; -} - -void CommandLine::AppendSwitch(const std::string& switch_string) { - AppendSwitchNative(switch_string, StringType()); -} - -void CommandLine::AppendSwitchPath(const std::string& switch_string, - const FilePath& path) { - AppendSwitchNative(switch_string, path.value()); -} - -void CommandLine::AppendSwitchNative(const std::string& switch_string, - const CommandLine::StringType& value) { - std::string switch_key(LowerASCIIOnWindows(switch_string)); -#if defined(OS_WIN) - StringType combined_switch_string(ASCIIToWide(switch_key)); -#elif defined(OS_POSIX) - StringType combined_switch_string(switch_string); -#endif - size_t prefix_length = GetSwitchPrefixLength(combined_switch_string); - switches_[switch_key.substr(prefix_length)] = value; - // Preserve existing switch prefixes in |argv_|; only append one if necessary. - if (prefix_length == 0) - combined_switch_string = kSwitchPrefixes[0] + combined_switch_string; - if (!value.empty()) - combined_switch_string += kSwitchValueSeparator + value; - // Append the switch and update the switches/arguments divider |begin_args_|. - argv_.insert(argv_.begin() + begin_args_++, combined_switch_string); -} - -void CommandLine::AppendSwitchASCII(const std::string& switch_string, - const std::string& value_string) { -#if defined(OS_WIN) - AppendSwitchNative(switch_string, ASCIIToWide(value_string)); -#elif defined(OS_POSIX) - AppendSwitchNative(switch_string, value_string); -#endif -} - -void CommandLine::CopySwitchesFrom(const CommandLine& source, - const char* const switches[], - size_t count) { - for (size_t i = 0; i < count; ++i) { - if (source.HasSwitch(switches[i])) - AppendSwitchNative(switches[i], source.GetSwitchValueNative(switches[i])); - } -} - -CommandLine::StringVector CommandLine::GetArgs() const { - // Gather all arguments after the last switch (may include kSwitchTerminator). - StringVector args(argv_.begin() + begin_args_, argv_.end()); - // Erase only the first kSwitchTerminator (maybe "--" is a legitimate page?) - StringVector::iterator switch_terminator = - std::find(args.begin(), args.end(), kSwitchTerminator); - if (switch_terminator != args.end()) - args.erase(switch_terminator); - return args; -} - -void CommandLine::AppendArg(const std::string& value) { -#if defined(OS_WIN) - DCHECK(IsStringUTF8(value)); - AppendArgNative(UTF8ToWide(value)); -#elif defined(OS_POSIX) - AppendArgNative(value); -#endif -} - -void CommandLine::AppendArgPath(const FilePath& path) { - AppendArgNative(path.value()); -} - -void CommandLine::AppendArgNative(const CommandLine::StringType& value) { - argv_.push_back(value); -} - -void CommandLine::AppendArguments(const CommandLine& other, - bool include_program) { - if (include_program) - SetProgram(other.GetProgram()); - AppendSwitchesAndArguments(*this, other.argv()); -} - -void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) { - if (wrapper.empty()) - return; - // The wrapper may have embedded arguments (like "gdb --args"). In this case, - // we don't pretend to do anything fancy, we just split on spaces. - StringVector wrapper_argv; - SplitString(wrapper, FILE_PATH_LITERAL(' '), &wrapper_argv); - // Prepend the wrapper and update the switches/arguments |begin_args_|. - argv_.insert(argv_.begin(), wrapper_argv.begin(), wrapper_argv.end()); - begin_args_ += wrapper_argv.size(); -} - -#if defined(OS_WIN) -void CommandLine::ParseFromString(const std::wstring& command_line) { - std::wstring command_line_string; - 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); - - DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: " - << UTF16ToUTF8(command_line); - InitFromArgv(num_args, args); - LocalFree(args); -} -#endif - -} // namespace butil diff --git a/src/butil/command_line.h b/src/butil/command_line.h deleted file mode 100644 index cb4be0ee37..0000000000 --- a/src/butil/command_line.h +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This class works with command lines: building and parsing. -// Arguments with prefixes ('--', '-', and on Windows, '/') are switches. -// Switches will precede all other arguments without switch prefixes. -// Switches can optionally have values, delimited by '=', e.g., "-switch=value". -// An argument of "--" will terminate switch parsing during initialization, -// interpreting subsequent tokens as non-switch arguments, regardless of prefix. - -// There is a singleton read-only CommandLine that represents the command line -// that the current process was started with. It must be initialized in main(). - -#ifndef BASE_COMMAND_LINE_H_ -#define BASE_COMMAND_LINE_H_ - -#include -#include -#include -#include - -#include "butil/base_export.h" -#include "butil/build_config.h" - -namespace butil { - -class FilePath; - -class BASE_EXPORT CommandLine { - public: -#if defined(OS_WIN) - // The native command line string type. - typedef std::wstring StringType; -#elif defined(OS_POSIX) - typedef std::string StringType; -#endif - - typedef StringType::value_type CharType; - typedef std::vector StringVector; - typedef std::map SwitchMap; - - // A constructor for CommandLines that only carry switches and arguments. - enum NoProgram { NO_PROGRAM }; - explicit CommandLine(NoProgram no_program); - - // Construct a new command line with |program| as argv[0]. - explicit CommandLine(const FilePath& program); - - // Construct a new command line from an argument list. - CommandLine(int argc, const CharType* const* argv); - explicit CommandLine(const StringVector& argv); - - ~CommandLine(); - -#if defined(OS_WIN) - // By default this class will treat command-line arguments beginning with - // slashes as switches on Windows, but not other platforms. - // - // If this behavior is inappropriate for your application, you can call this - // function BEFORE initializing the current process' global command line - // object and the behavior will be the same as Posix systems (only hyphens - // begin switches, everything else will be an arg). - static void set_slash_is_not_a_switch(); -#endif - - // Initialize the current process CommandLine singleton. On Windows, ignores - // its arguments (we instead parse GetCommandLineW() directly) because we - // don't trust the CRT's parsing of the command line, but it still must be - // called to set up the command line. Returns false if initialization has - // already occurred, and true otherwise. Only the caller receiving a 'true' - // return value should take responsibility for calling Reset. - static bool 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, as in main(), Reset() is not necessary. - static void Reset(); - - // Get the singleton CommandLine representing the current process's - // command line. Note: returned value is mutable, but not thread safe; - // only mutate if you know what you're doing! - static CommandLine* ForCurrentProcess(); - - // Returns true if the CommandLine has been initialized for the given process. - static bool InitializedForCurrentProcess(); - -#if defined(OS_WIN) - static CommandLine FromString(const std::wstring& command_line); -#endif - - // Initialize from an argv vector. - void InitFromArgv(int argc, const CharType* const* argv); - void InitFromArgv(const StringVector& argv); - - // Constructs and returns the represented command line string. - // CAUTION! This should be avoided on POSIX because quoting behavior is - // unclear. - StringType GetCommandLineString() const; - - // Constructs and returns the represented arguments string. - // CAUTION! This should be avoided on POSIX because quoting behavior is - // unclear. - StringType GetArgumentsString() const; - - // Returns the original command line string as a vector of strings. - const StringVector& argv() const { return argv_; } - - // Get and Set the program part of the command line string (the first item). - FilePath GetProgram() const; - void SetProgram(const FilePath& program); - - // Returns true if this command line contains the given switch. - // (Switch names are case-insensitive). - bool HasSwitch(const std::string& switch_string) const; - - // Returns the value associated with the given switch. If the switch has no - // value or isn't present, this method returns the empty string. - std::string GetSwitchValueASCII(const std::string& switch_string) const; - FilePath GetSwitchValuePath(const std::string& switch_string) const; - StringType GetSwitchValueNative(const std::string& switch_string) const; - - // Get a copy of all switches, along with their values. - const SwitchMap& GetSwitches() const { return switches_; } - - // Append a switch [with optional value] to the command line. - // Note: Switches will precede arguments regardless of appending order. - void AppendSwitch(const std::string& switch_string); - void AppendSwitchPath(const std::string& switch_string, - const FilePath& path); - void AppendSwitchNative(const std::string& switch_string, - const StringType& value); - void AppendSwitchASCII(const std::string& switch_string, - const std::string& value); - - // Copy a set of switches (and any values) from another command line. - // Commonly used when launching a subprocess. - void CopySwitchesFrom(const CommandLine& source, - const char* const switches[], - size_t count); - - // Get the remaining arguments to the command. - StringVector GetArgs() const; - - // Append an argument to the command line. Note that the argument is quoted - // properly such that it is interpreted as one argument to the target command. - // AppendArg is primarily for ASCII; non-ASCII input is interpreted as UTF-8. - // Note: Switches will precede arguments regardless of appending order. - void AppendArg(const std::string& value); - void AppendArgPath(const FilePath& value); - void AppendArgNative(const StringType& value); - - // Append the switches and arguments from another command line to this one. - // If |include_program| is true, include |other|'s program as well. - void AppendArguments(const CommandLine& other, bool include_program); - - // Insert a command before the current command. - // Common for debuggers, like "valgrind" or "gdb --args". - void PrependWrapper(const StringType& wrapper); - -#if defined(OS_WIN) - // Initialize by parsing the given command line string. - // The program name is assumed to be the first item in the string. - void ParseFromString(const std::wstring& command_line); -#endif - - private: - // Disallow default constructor; a program name must be explicitly specified. - CommandLine(); - // Allow the copy constructor. A common pattern is to copy of the current - // process's command line and then add some flags to it. For example: - // CommandLine cl(*CommandLine::ForCurrentProcess()); - // cl.AppendSwitch(...); - - // The singleton CommandLine representing the current process's command line. - static CommandLine* current_process_commandline_; - - // The argv array: { program, [(--|-|/)switch[=value]]*, [--], [argument]* } - StringVector argv_; - - // Parsed-out switch keys and values. - SwitchMap switches_; - - // The index after the program and switches, any arguments start here. - size_t begin_args_; -}; - -} // namespace butil - -// TODO(brettw) remove once all callers specify the namespace properly. -using butil::CommandLine; - -#endif // BASE_COMMAND_LINE_H_ diff --git a/src/butil/file_util_posix.cc b/src/butil/file_util_posix.cc index e33014da84..8d9faf1107 100644 --- a/src/butil/file_util_posix.cc +++ b/src/butil/file_util_posix.cc @@ -43,7 +43,6 @@ #include "butil/strings/stringprintf.h" #include "butil/strings/sys_string_conversions.h" #include "butil/strings/utf_string_conversions.h" -#include "butil/sys_info.h" #include "butil/threading/thread_restrictions.h" #include "butil/time/time.h" diff --git a/src/butil/logging.cc b/src/butil/logging.cc index 903c27158c..17cd30b48c 100644 --- a/src/butil/logging.cc +++ b/src/butil/logging.cc @@ -60,7 +60,6 @@ typedef pthread_mutex_t* MutexHandle; #include #include "butil/file_util.h" -#include "butil/command_line.h" #include "butil/debug/alias.h" #include "butil/debug/debugger.h" #include "butil/debug/stack_trace.h" diff --git a/src/butil/memory/shared_memory.h b/src/butil/memory/shared_memory.h deleted file mode 100644 index e9af0bccda..0000000000 --- a/src/butil/memory/shared_memory.h +++ /dev/null @@ -1,325 +0,0 @@ -// 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_MEMORY_SHARED_MEMORY_H_ -#define BASE_MEMORY_SHARED_MEMORY_H_ - -#include "butil/build_config.h" - -#include - -#if defined(OS_POSIX) -#include -#include -#include -#endif - -#include "butil/base_export.h" -#include "butil/basictypes.h" -#include "butil/process/process_handle.h" - -#if defined(OS_POSIX) -#include "butil/file_descriptor_posix.h" -#include "butil/file_util.h" -#include "butil/files/scoped_file.h" -#endif - -namespace butil { - -class FilePath; - -// SharedMemoryHandle is a platform specific type which represents -// the underlying OS handle to a shared memory segment. -#if defined(OS_WIN) -typedef HANDLE SharedMemoryHandle; -#elif defined(OS_POSIX) -// A SharedMemoryId is sufficient to identify a given shared memory segment on a -// system, but insufficient to map it. -typedef FileDescriptor SharedMemoryHandle; -typedef ino_t SharedMemoryId; -#endif - -// Options for creating a shared memory object. -struct SharedMemoryCreateOptions { - SharedMemoryCreateOptions() - : name_deprecated(NULL), - size(0), - open_existing_deprecated(false), - executable(false), - share_read_only(false) {} - - // DEPRECATED (crbug.com/345734): - // If NULL, the object is anonymous. This pointer is owned by the caller - // and must live through the call to Create(). - const std::string* name_deprecated; - - // Size of the shared memory object to be created. - // When opening an existing object, this has no effect. - size_t size; - - // DEPRECATED (crbug.com/345734): - // If true, and the shared memory already exists, Create() will open the - // existing shared memory and ignore the size parameter. If false, - // shared memory must not exist. This flag is meaningless unless - // name_deprecated is non-NULL. - bool open_existing_deprecated; - - // If true, mappings might need to be made executable later. - bool executable; - - // If true, the file can be shared read-only to a process. - bool share_read_only; -}; - -// Platform abstraction for shared memory. Provides a C++ wrapper -// around the OS primitive for a memory mapped file. -class BASE_EXPORT SharedMemory { - public: - SharedMemory(); - -#if defined(OS_WIN) - // Similar to the default constructor, except that this allows for - // calling LockDeprecated() to acquire the named mutex before either Create or - // Open are called on Windows. - explicit SharedMemory(const std::wstring& name); -#endif - - // Create a new SharedMemory object from an existing, open - // shared memory file. - // - // WARNING: This does not reduce the OS-level permissions on the handle; it - // only affects how the SharedMemory will be mmapped. Use - // ShareReadOnlyToProcess to drop permissions. TODO(jln,jyasskin): DCHECK - // that |read_only| matches the permissions of the handle. - SharedMemory(SharedMemoryHandle handle, bool read_only); - - // Create a new SharedMemory object from an existing, open - // shared memory file that was created by a remote process and not shared - // to the current process. - SharedMemory(SharedMemoryHandle handle, bool read_only, - ProcessHandle process); - - // Closes any open files. - ~SharedMemory(); - - // Return true iff the given handle is valid (i.e. not the distingished - // invalid value; NULL for a HANDLE and -1 for a file descriptor) - static bool IsHandleValid(const SharedMemoryHandle& handle); - - // Returns invalid handle (see comment above for exact definition). - static SharedMemoryHandle NULLHandle(); - - // Closes a shared memory handle. - static void CloseHandle(const SharedMemoryHandle& handle); - - // Returns the maximum number of handles that can be open at once per process. - static size_t GetHandleLimit(); - - // Creates a shared memory object as described by the options struct. - // Returns true on success and false on failure. - bool Create(const SharedMemoryCreateOptions& options); - - // Creates and maps an anonymous shared memory segment of size size. - // Returns true on success and false on failure. - bool CreateAndMapAnonymous(size_t size); - - // Creates an anonymous shared memory segment of size size. - // Returns true on success and false on failure. - bool CreateAnonymous(size_t size) { - SharedMemoryCreateOptions options; - options.size = size; - return Create(options); - } - - // DEPRECATED (crbug.com/345734): - // Creates or opens a shared memory segment based on a name. - // If open_existing is true, and the shared memory already exists, - // opens the existing shared memory and ignores the size parameter. - // If open_existing is false, shared memory must not exist. - // size is the size of the block to be created. - // Returns true on success, false on failure. - bool CreateNamedDeprecated( - const std::string& name, bool open_existing, size_t size) { - SharedMemoryCreateOptions options; - options.name_deprecated = &name; - options.open_existing_deprecated = open_existing; - options.size = size; - return Create(options); - } - - // Deletes resources associated with a shared memory segment based on name. - // Not all platforms require this call. - bool Delete(const std::string& name); - - // Opens a shared memory segment based on a name. - // If read_only is true, opens for read-only access. - // Returns true on success, false on failure. - bool Open(const std::string& name, bool read_only); - - // Maps the shared memory into the caller's address space. - // Returns true on success, false otherwise. The memory address - // is accessed via the memory() accessor. The mapped address is guaranteed to - // have an alignment of at least MAP_MINIMUM_ALIGNMENT. This method will fail - // if this object is currently mapped. - bool Map(size_t bytes) { - return MapAt(0, bytes); - } - - // Same as above, but with |offset| to specify from begining of the shared - // memory block to map. - // |offset| must be alignent to value of |SysInfo::VMAllocationGranularity()|. - bool MapAt(off_t offset, size_t bytes); - enum { MAP_MINIMUM_ALIGNMENT = 32 }; - - // Unmaps the shared memory from the caller's address space. - // Returns true if successful; returns false on error or if the - // memory is not mapped. - bool Unmap(); - - // The size requested when the map is first created. - size_t requested_size() const { return requested_size_; } - - // The actual size of the mapped memory (may be larger than requested). - size_t mapped_size() const { return mapped_size_; } - - // Gets a pointer to the opened memory space if it has been - // Mapped via Map(). Returns NULL if it is not mapped. - void *memory() const { return memory_; } - - // Returns the underlying OS handle for this segment. - // Use of this handle for anything other than an opaque - // identifier is not portable. - SharedMemoryHandle handle() const; - -#if defined(OS_POSIX) && !defined(OS_NACL) - // Returns a unique identifier for this shared memory segment. Inode numbers - // are technically only unique to a single filesystem. However, we always - // allocate shared memory backing files from the same directory, so will end - // up on the same filesystem. - SharedMemoryId id() const { return inode_; } -#endif - - // Closes the open shared memory segment. - // It is safe to call Close repeatedly. - void Close(); - - // Shares the shared memory to another process. Attempts to create a - // platform-specific new_handle which can be used in a remote process to read - // the shared memory file. new_handle is an output parameter to receive the - // handle for use in the remote process. - // - // |*this| must have been initialized using one of the Create*() or Open() - // methods with share_read_only=true. If it was constructed from a - // SharedMemoryHandle, this call will CHECK-fail. - // - // Returns true on success, false otherwise. - bool ShareReadOnlyToProcess(ProcessHandle process, - SharedMemoryHandle* new_handle) { - return ShareToProcessCommon(process, new_handle, false, SHARE_READONLY); - } - - // Logically equivalent to: - // bool ok = ShareReadOnlyToProcess(process, new_handle); - // Close(); - // return ok; - // Note that the memory is unmapped by calling this method, regardless of the - // return value. - bool GiveReadOnlyToProcess(ProcessHandle process, - SharedMemoryHandle* new_handle) { - return ShareToProcessCommon(process, new_handle, true, SHARE_READONLY); - } - - // Shares the shared memory to another process. Attempts - // to create a platform-specific new_handle which can be - // used in a remote process to access the shared memory - // file. new_handle is an output parameter to receive - // the handle for use in the remote process. - // Returns true on success, false otherwise. - bool ShareToProcess(ProcessHandle process, - SharedMemoryHandle* new_handle) { - return ShareToProcessCommon(process, new_handle, false, SHARE_CURRENT_MODE); - } - - // Logically equivalent to: - // bool ok = ShareToProcess(process, new_handle); - // Close(); - // return ok; - // Note that the memory is unmapped by calling this method, regardless of the - // return value. - bool GiveToProcess(ProcessHandle process, - SharedMemoryHandle* new_handle) { - return ShareToProcessCommon(process, new_handle, true, SHARE_CURRENT_MODE); - } - - // DEPRECATED (crbug.com/345734): - // Locks the shared memory. - // - // WARNING: on POSIX the memory locking primitive only works across - // processes, not across threads. The LockDeprecated method is not currently - // used in inner loops, so we protect against multiple threads in a - // critical section using a class global lock. - void LockDeprecated(); - - // DEPRECATED (crbug.com/345734): - // Releases the shared memory lock. - void UnlockDeprecated(); - - private: -#if defined(OS_POSIX) && !defined(OS_NACL) -#if !defined(OS_ANDROID) - bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly); - bool FilePathForMemoryName(const std::string& mem_name, FilePath* path); -#endif - void LockOrUnlockCommon(int function); -#endif // defined(OS_POSIX) && !defined(OS_NACL) - enum ShareMode { - SHARE_READONLY, - SHARE_CURRENT_MODE, - }; - bool ShareToProcessCommon(ProcessHandle process, - SharedMemoryHandle* new_handle, - bool close_self, - ShareMode); - -#if defined(OS_WIN) - std::wstring name_; - HANDLE mapped_file_; -#elif defined(OS_POSIX) - int mapped_file_; - int readonly_mapped_file_; - ino_t inode_; -#endif - size_t mapped_size_; - void* memory_; - bool read_only_; - size_t requested_size_; -#if !defined(OS_POSIX) - HANDLE lock_; -#endif - - DISALLOW_COPY_AND_ASSIGN(SharedMemory); -}; - -// DEPRECATED (crbug.com/345734): -// A helper class that acquires the shared memory lock while -// the SharedMemoryAutoLockDeprecated is in scope. -class SharedMemoryAutoLockDeprecated { - public: - explicit SharedMemoryAutoLockDeprecated(SharedMemory* shared_memory) - : shared_memory_(shared_memory) { - shared_memory_->LockDeprecated(); - } - - ~SharedMemoryAutoLockDeprecated() { - shared_memory_->UnlockDeprecated(); - } - - private: - SharedMemory* shared_memory_; - DISALLOW_COPY_AND_ASSIGN(SharedMemoryAutoLockDeprecated); -}; - -} // namespace butil - -#endif // BASE_MEMORY_SHARED_MEMORY_H_ diff --git a/src/butil/memory/shared_memory_posix.cc b/src/butil/memory/shared_memory_posix.cc deleted file mode 100644 index 70c0cd5218..0000000000 --- a/src/butil/memory/shared_memory_posix.cc +++ /dev/null @@ -1,479 +0,0 @@ -// 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. - -#include "butil/memory/shared_memory.h" - -#include -#include -#include -#include -#include -#include - -#include "butil/file_util.h" -#include "butil/files/scoped_file.h" -#include "butil/lazy_instance.h" -#include "butil/logging.h" -#include "butil/process/process_metrics.h" -#include "butil/safe_strerror_posix.h" -#include "butil/strings/utf_string_conversions.h" -#include "butil/synchronization/lock.h" -#include "butil/threading/platform_thread.h" -#include "butil/threading/thread_restrictions.h" - -#if defined(OS_MACOSX) -#include "butil/mac/foundation_util.h" -#endif // OS_MACOSX - -#if defined(OS_ANDROID) -#include "butil/os_compat_android.h" -#include "third_party/ashmem/ashmem.h" -#endif - -namespace butil { - -namespace { - -LazyInstance::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER; - -} - -SharedMemory::SharedMemory() - : mapped_file_(-1), - readonly_mapped_file_(-1), - inode_(0), - mapped_size_(0), - memory_(NULL), - read_only_(false), - requested_size_(0) { -} - -SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) - : mapped_file_(handle.fd), - readonly_mapped_file_(-1), - inode_(0), - mapped_size_(0), - memory_(NULL), - read_only_(read_only), - requested_size_(0) { - struct stat st; - if (fstat(handle.fd, &st) == 0) { - // If fstat fails, then the file descriptor is invalid and we'll learn this - // fact when Map() fails. - inode_ = st.st_ino; - } -} - -SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, - ProcessHandle process) - : mapped_file_(handle.fd), - readonly_mapped_file_(-1), - inode_(0), - mapped_size_(0), - memory_(NULL), - read_only_(read_only), - requested_size_(0) { - // We don't handle this case yet (note the ignored parameter); let's die if - // someone comes calling. - NOTREACHED(); -} - -SharedMemory::~SharedMemory() { - Close(); -} - -// static -bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { - return handle.fd >= 0; -} - -// static -SharedMemoryHandle SharedMemory::NULLHandle() { - return SharedMemoryHandle(); -} - -// static -void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { - DCHECK_GE(handle.fd, 0); - if (close(handle.fd) < 0) - DPLOG(ERROR) << "close"; -} - -// static -size_t SharedMemory::GetHandleLimit() { - return butil::GetMaxFds(); -} - -bool SharedMemory::CreateAndMapAnonymous(size_t size) { - return CreateAnonymous(size) && Map(size); -} - -#if !defined(OS_ANDROID) -// Chromium mostly only uses the unique/private shmem as specified by -// "name == L"". The exception is in the StatsTable. -// TODO(jrg): there is no way to "clean up" all unused named shmem if -// we restart from a crash. (That isn't a new problem, but it is a problem.) -// In case we want to delete it later, it may be useful to save the value -// of mem_filename after FilePathForMemoryName(). -bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { - DCHECK_EQ(-1, mapped_file_); - if (options.size == 0) return false; - - if (options.size > static_cast(std::numeric_limits::max())) - return false; - - // This function theoretically can block on the disk, but realistically - // the temporary files we create will just go into the buffer cache - // and be deleted before they ever make it out to disk. - butil::ThreadRestrictions::ScopedAllowIO allow_io; - - ScopedFILE fp; - bool fix_size = true; - ScopedFD readonly_fd; - - FilePath path; - if (options.name_deprecated == NULL || options.name_deprecated->empty()) { - // It doesn't make sense to have a open-existing private piece of shmem - DCHECK(!options.open_existing_deprecated); - // Q: Why not use the shm_open() etc. APIs? - // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU - FilePath directory; - if (GetShmemTempDir(options.executable, &directory)) - fp.reset(CreateAndOpenTemporaryFileInDir(directory, &path)); - - if (fp) { - if (options.share_read_only) { - // Also open as readonly so that we can ShareReadOnlyToProcess. - readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); - if (!readonly_fd.is_valid()) { - DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; - fp.reset(); - return false; - } - } - // Deleting the file prevents anyone else from mapping it in (making it - // private), and prevents the need for cleanup (once the last fd is - // closed, it is truly freed). - if (unlink(path.value().c_str())) - PLOG(WARNING) << "unlink"; - } - } else { - if (!FilePathForMemoryName(*options.name_deprecated, &path)) - return false; - - // Make sure that the file is opened without any permission - // to other users on the system. - const mode_t kOwnerOnly = S_IRUSR | S_IWUSR; - - // First, try to create the file. - int fd = HANDLE_EINTR( - open(path.value().c_str(), O_RDWR | O_CREAT | O_EXCL, kOwnerOnly)); - if (fd == -1 && options.open_existing_deprecated) { - // If this doesn't work, try and open an existing file in append mode. - // Opening an existing file in a world writable directory has two main - // security implications: - // - Attackers could plant a file under their control, so ownership of - // the file is checked below. - // - Attackers could plant a symbolic link so that an unexpected file - // is opened, so O_NOFOLLOW is passed to open(). - fd = HANDLE_EINTR( - open(path.value().c_str(), O_RDWR | O_APPEND | O_NOFOLLOW)); - - // Check that the current user owns the file. - // If uid != euid, then a more complex permission model is used and this - // API is not appropriate. - const uid_t real_uid = getuid(); - const uid_t effective_uid = geteuid(); - struct stat sb; - if (fd >= 0 && - (fstat(fd, &sb) != 0 || sb.st_uid != real_uid || - sb.st_uid != effective_uid)) { - LOG(ERROR) << - "Invalid owner when opening existing shared memory file."; - close(fd); - return false; - } - - // An existing file was opened, so its size should not be fixed. - fix_size = false; - } - - if (options.share_read_only) { - // Also open as readonly so that we can ShareReadOnlyToProcess. - readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); - if (!readonly_fd.is_valid()) { - DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; - close(fd); - fd = -1; - return false; - } - } - if (fd >= 0) { - // "a+" is always appropriate: if it's a new file, a+ is similar to w+. - fp.reset(fdopen(fd, "a+")); - } - } - if (fp && fix_size) { - // Get current size. - struct stat stat; - if (fstat(fileno(fp.get()), &stat) != 0) - return false; - const size_t current_size = stat.st_size; - if (current_size != options.size) { - if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0) - return false; - } - requested_size_ = options.size; - } - if (fp == NULL) { -#if !defined(OS_MACOSX) - PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; - FilePath dir = path.DirName(); - if (access(dir.value().c_str(), W_OK | X_OK) < 0) { - PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); - if (dir.value() == "/dev/shm") { - LOG(FATAL) << "This is frequently caused by incorrect permissions on " - << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; - } - } -#else - PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed"; -#endif - return false; - } - - return PrepareMapFile(fp.Pass(), readonly_fd.Pass()); -} - -// Our current implementation of shmem is with mmap()ing of files. -// These files need to be deleted explicitly. -// In practice this call is only needed for unit tests. -bool SharedMemory::Delete(const std::string& name) { - FilePath path; - if (!FilePathForMemoryName(name, &path)) - return false; - - if (PathExists(path)) - return butil::DeleteFile(path, false); - - // Doesn't exist, so success. - return true; -} - -bool SharedMemory::Open(const std::string& name, bool read_only) { - FilePath path; - if (!FilePathForMemoryName(name, &path)) - return false; - - read_only_ = read_only; - - const char *mode = read_only ? "r" : "r+"; - ScopedFILE fp(butil::OpenFile(path, mode)); - ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); - if (!readonly_fd.is_valid()) { - DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; - return false; - } - return PrepareMapFile(fp.Pass(), readonly_fd.Pass()); -} -#endif // !defined(OS_ANDROID) - -bool SharedMemory::MapAt(off_t offset, size_t bytes) { - if (mapped_file_ == -1) - return false; - - if (bytes > static_cast(std::numeric_limits::max())) - return false; - - if (memory_) - return false; - -#if defined(OS_ANDROID) - // On Android, Map can be called with a size and offset of zero to use the - // ashmem-determined size. - if (bytes == 0) { - DCHECK_EQ(0, offset); - int ashmem_bytes = ashmem_get_size_region(mapped_file_); - if (ashmem_bytes < 0) - return false; - bytes = ashmem_bytes; - } -#endif - - memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), - MAP_SHARED, mapped_file_, offset); - - bool mmap_succeeded = memory_ != (void*)-1 && memory_ != NULL; - if (mmap_succeeded) { - mapped_size_ = bytes; - DCHECK_EQ(0U, reinterpret_cast(memory_) & - (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); - } else { - memory_ = NULL; - } - - return mmap_succeeded; -} - -bool SharedMemory::Unmap() { - if (memory_ == NULL) - return false; - - munmap(memory_, mapped_size_); - memory_ = NULL; - mapped_size_ = 0; - return true; -} - -SharedMemoryHandle SharedMemory::handle() const { - return FileDescriptor(mapped_file_, false); -} - -void SharedMemory::Close() { - Unmap(); - - if (mapped_file_ > 0) { - if (close(mapped_file_) < 0) - PLOG(ERROR) << "close"; - mapped_file_ = -1; - } - if (readonly_mapped_file_ > 0) { - if (close(readonly_mapped_file_) < 0) - PLOG(ERROR) << "close"; - readonly_mapped_file_ = -1; - } -} - -void SharedMemory::LockDeprecated() { - g_thread_lock_.Get().Acquire(); - LockOrUnlockCommon(F_LOCK); -} - -void SharedMemory::UnlockDeprecated() { - LockOrUnlockCommon(F_ULOCK); - g_thread_lock_.Get().Release(); -} - -#if !defined(OS_ANDROID) -bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) { - DCHECK_EQ(-1, mapped_file_); - DCHECK_EQ(-1, readonly_mapped_file_); - if (fp == NULL) - return false; - - // This function theoretically can block on the disk, but realistically - // the temporary files we create will just go into the buffer cache - // and be deleted before they ever make it out to disk. - butil::ThreadRestrictions::ScopedAllowIO allow_io; - - struct stat st; - memset(&st, 0, sizeof(st)); - if (fstat(fileno(fp.get()), &st)) - NOTREACHED(); - if (readonly_fd.is_valid()) { - struct stat readonly_st; - memset(&readonly_st, 0, sizeof(readonly_st)); - if (fstat(readonly_fd.get(), &readonly_st)) - NOTREACHED(); - if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { - LOG(ERROR) << "writable and read-only inodes don't match; bailing"; - return false; - } - } - - mapped_file_ = dup(fileno(fp.get())); - if (mapped_file_ == -1) { - if (errno == EMFILE) { - LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; - return false; - } else { - NOTREACHED() << "Call to dup failed, errno=" << errno; - } - } - inode_ = st.st_ino; - readonly_mapped_file_ = readonly_fd.release(); - - return true; -} - -// For the given shmem named |mem_name|, return a filename to mmap() -// (and possibly create). Modifies |filename|. Return false on -// error, or true of we are happy. -bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, - FilePath* path) { - // mem_name will be used for a filename; make sure it doesn't - // contain anything which will confuse us. - DCHECK_EQ(std::string::npos, mem_name.find('/')); - DCHECK_EQ(std::string::npos, mem_name.find('\0')); - - FilePath temp_dir; - if (!GetShmemTempDir(false, &temp_dir)) - return false; - -#if !defined(OS_MACOSX) -#if defined(GOOGLE_CHROME_BUILD) - std::string name_base = std::string("com.google.Chrome"); -#else - std::string name_base = std::string("org.chromium.Chromium"); -#endif -#else // OS_MACOSX - std::string name_base = std::string(butil::mac::BaseBundleID()); -#endif // OS_MACOSX - *path = temp_dir.AppendASCII(name_base + ".shmem." + mem_name); - return true; -} -#endif // !defined(OS_ANDROID) - -void SharedMemory::LockOrUnlockCommon(int function) { - DCHECK_GE(mapped_file_, 0); - while (lockf(mapped_file_, function, 0) < 0) { - if (errno == EINTR) { - continue; - } else if (errno == ENOLCK) { - // temporary kernel resource exaustion - butil::PlatformThread::Sleep(butil::TimeDelta::FromMilliseconds(500)); - continue; - } else { - NOTREACHED() << "lockf() failed." - << " function:" << function - << " fd:" << mapped_file_ - << " errno:" << errno - << " msg:" << safe_strerror(errno); - } - } -} - -bool SharedMemory::ShareToProcessCommon(ProcessHandle process, - SharedMemoryHandle* new_handle, - bool close_self, - ShareMode share_mode) { - int handle_to_dup = -1; - switch(share_mode) { - case SHARE_CURRENT_MODE: - handle_to_dup = mapped_file_; - break; - case SHARE_READONLY: - // We could imagine re-opening the file from /dev/fd, but that can't make - // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10 - CHECK(readonly_mapped_file_ >= 0); - handle_to_dup = readonly_mapped_file_; - break; - } - - const int new_fd = dup(handle_to_dup); - if (new_fd < 0) { - DPLOG(ERROR) << "dup() failed."; - return false; - } - - new_handle->fd = new_fd; - new_handle->auto_close = true; - - if (close_self) - Close(); - - return true; -} - -} // namespace butil diff --git a/src/butil/path_service.cc b/src/butil/path_service.cc deleted file mode 100644 index 43a43cfdb8..0000000000 --- a/src/butil/path_service.cc +++ /dev/null @@ -1,341 +0,0 @@ -// 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. - -#include "butil/path_service.h" - -#if defined(OS_WIN) -#include -#include -#include -#endif - -#include "butil/containers/hash_tables.h" -#include "butil/file_util.h" -#include "butil/files/file_path.h" -#include "butil/lazy_instance.h" -#include "butil/logging.h" -#include "butil/synchronization/lock.h" - -using butil::FilePath; -using butil::MakeAbsoluteFilePath; - -namespace butil { - bool PathProvider(int key, FilePath* result); -#if defined(OS_WIN) - bool PathProviderWin(int key, FilePath* result); -#elif defined(OS_MACOSX) - bool PathProviderMac(int key, FilePath* result); -#elif defined(OS_ANDROID) - bool PathProviderAndroid(int key, FilePath* result); -#elif defined(OS_POSIX) - // PathProviderPosix is the default path provider on POSIX OSes other than - // Mac and Android. - bool PathProviderPosix(int key, FilePath* result); -#endif -} - -namespace { - -typedef butil::hash_map PathMap; - -// We keep a linked list of providers. In a debug build we ensure that no two -// providers claim overlapping keys. -struct Provider { - PathService::ProviderFunc func; - struct Provider* next; -#ifndef NDEBUG - int key_start; - int key_end; -#endif - bool is_static; -}; - -Provider base_provider = { - butil::PathProvider, - NULL, -#ifndef NDEBUG - butil::PATH_START, - butil::PATH_END, -#endif - true -}; - -#if defined(OS_WIN) -Provider base_provider_win = { - butil::PathProviderWin, - &base_provider, -#ifndef NDEBUG - butil::PATH_WIN_START, - butil::PATH_WIN_END, -#endif - true -}; -#endif - -#if defined(OS_MACOSX) -Provider base_provider_mac = { - butil::PathProviderMac, - &base_provider, -#ifndef NDEBUG - butil::PATH_MAC_START, - butil::PATH_MAC_END, -#endif - true -}; -#endif - -#if defined(OS_ANDROID) -Provider base_provider_android = { - butil::PathProviderAndroid, - &base_provider, -#ifndef NDEBUG - butil::PATH_ANDROID_START, - butil::PATH_ANDROID_END, -#endif - true -}; -#endif - -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) -Provider base_provider_posix = { - butil::PathProviderPosix, - &base_provider, -#ifndef NDEBUG - butil::PATH_POSIX_START, - butil::PATH_POSIX_END, -#endif - true -}; -#endif - - -struct PathData { - butil::Lock lock; - PathMap cache; // Cache mappings from path key to path value. - PathMap overrides; // Track path overrides. - Provider* providers; // Linked list of path service providers. - bool cache_disabled; // Don't use cache if true; - - PathData() : cache_disabled(false) { -#if defined(OS_WIN) - providers = &base_provider_win; -#elif defined(OS_MACOSX) - providers = &base_provider_mac; -#elif defined(OS_ANDROID) - providers = &base_provider_android; -#elif defined(OS_POSIX) - providers = &base_provider_posix; -#endif - } - - ~PathData() { - Provider* p = providers; - while (p) { - Provider* next = p->next; - if (!p->is_static) - delete p; - p = next; - } - } -}; - -static butil::LazyInstance g_path_data = LAZY_INSTANCE_INITIALIZER; - -static PathData* GetPathData() { - return g_path_data.Pointer(); -} - -// Tries to find |key| in the cache. |path_data| should be locked by the caller! -bool LockedGetFromCache(int key, const PathData* path_data, FilePath* result) { - if (path_data->cache_disabled) - return false; - // check for a cached version - PathMap::const_iterator it = path_data->cache.find(key); - if (it != path_data->cache.end()) { - *result = it->second; - return true; - } - return false; -} - -// Tries to find |key| in the overrides map. |path_data| should be locked by the -// caller! -bool LockedGetFromOverrides(int key, PathData* path_data, FilePath* result) { - // check for an overridden version. - PathMap::const_iterator it = path_data->overrides.find(key); - if (it != path_data->overrides.end()) { - if (!path_data->cache_disabled) - path_data->cache[key] = it->second; - *result = it->second; - return true; - } - return false; -} - -} // namespace - -// TODO(brettw): this function does not handle long paths (filename > MAX_PATH) -// characters). This isn't supported very well by Windows right now, so it is -// moot, but we should keep this in mind for the future. -// static -bool PathService::Get(int key, FilePath* result) { - PathData* path_data = GetPathData(); - DCHECK(path_data); - DCHECK(result); - DCHECK_GE(key, butil::DIR_CURRENT); - - // special case the current directory because it can never be cached - if (key == butil::DIR_CURRENT) - return butil::GetCurrentDirectory(result); - - Provider* provider = NULL; - { - butil::AutoLock scoped_lock(path_data->lock); - if (LockedGetFromCache(key, path_data, result)) - return true; - - if (LockedGetFromOverrides(key, path_data, result)) - return true; - - // Get the beginning of the list while it is still locked. - provider = path_data->providers; - } - - FilePath path; - - // Iterating does not need the lock because only the list head might be - // modified on another thread. - while (provider) { - if (provider->func(key, &path)) - break; - DCHECK(path.empty()) << "provider should not have modified path"; - provider = provider->next; - } - - if (path.empty()) - return false; - - if (path.ReferencesParent()) { - // Make sure path service never returns a path with ".." in it. - path = MakeAbsoluteFilePath(path); - if (path.empty()) - return false; - } - *result = path; - - butil::AutoLock scoped_lock(path_data->lock); - if (!path_data->cache_disabled) - path_data->cache[key] = path; - - return true; -} - -// static -bool PathService::Override(int key, const FilePath& path) { - // Just call the full function with true for the value of |create|, and - // assume that |path| may not be absolute yet. - return OverrideAndCreateIfNeeded(key, path, false, true); -} - -// static -bool PathService::OverrideAndCreateIfNeeded(int key, - const FilePath& path, - bool is_absolute, - bool create) { - PathData* path_data = GetPathData(); - DCHECK(path_data); - DCHECK_GT(key, butil::DIR_CURRENT) << "invalid path key"; - - FilePath file_path = path; - - // For some locations this will fail if called from inside the sandbox there- - // fore we protect this call with a flag. - if (create) { - // Make sure the directory exists. We need to do this before we translate - // this to the absolute path because on POSIX, MakeAbsoluteFilePath fails - // if called on a non-existent path. - if (!butil::PathExists(file_path) && - !butil::CreateDirectory(file_path)) - return false; - } - - // We need to have an absolute path. - if (!is_absolute) { - file_path = MakeAbsoluteFilePath(file_path); - if (file_path.empty()) - return false; - } - DCHECK(file_path.IsAbsolute()); - - butil::AutoLock scoped_lock(path_data->lock); - - // Clear the cache now. Some of its entries could have depended - // on the value we are overriding, and are now out of sync with reality. - path_data->cache.clear(); - - path_data->overrides[key] = file_path; - - return true; -} - -// static -bool PathService::RemoveOverride(int key) { - PathData* path_data = GetPathData(); - DCHECK(path_data); - - butil::AutoLock scoped_lock(path_data->lock); - - if (path_data->overrides.find(key) == path_data->overrides.end()) - return false; - - // Clear the cache now. Some of its entries could have depended on the value - // we are going to remove, and are now out of sync. - path_data->cache.clear(); - - path_data->overrides.erase(key); - - return true; -} - -// static -void PathService::RegisterProvider(ProviderFunc func, int key_start, - int key_end) { - PathData* path_data = GetPathData(); - DCHECK(path_data); - DCHECK_GT(key_end, key_start); - - Provider* p; - - p = new Provider; - p->is_static = false; - p->func = func; -#ifndef NDEBUG - p->key_start = key_start; - p->key_end = key_end; -#endif - - butil::AutoLock scoped_lock(path_data->lock); - -#ifndef NDEBUG - Provider *iter = path_data->providers; - while (iter) { - DCHECK(key_start >= iter->key_end || key_end <= iter->key_start) << - "path provider collision"; - iter = iter->next; - } -#endif - - p->next = path_data->providers; - path_data->providers = p; -} - -// static -void PathService::DisableCache() { - PathData* path_data = GetPathData(); - DCHECK(path_data); - - butil::AutoLock scoped_lock(path_data->lock); - path_data->cache.clear(); - path_data->cache_disabled = true; -} diff --git a/src/butil/path_service.h b/src/butil/path_service.h deleted file mode 100644 index 1855805f7a..0000000000 --- a/src/butil/path_service.h +++ /dev/null @@ -1,92 +0,0 @@ -// 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_PATH_SERVICE_H_ -#define BASE_PATH_SERVICE_H_ - -#include - -#include "butil/base_export.h" -#include "butil/base_paths.h" -#include "butil/gtest_prod_util.h" -#include "butil/build_config.h" - -namespace butil { -class FilePath; -class ScopedPathOverride; -} // namespace - -// The path service is a global table mapping keys to file system paths. It is -// OK to use this service from multiple threads. -// -class BASE_EXPORT PathService { - public: - // Retrieves a path to a special directory or file and places it into the - // string pointed to by 'path'. If you ask for a directory it is guaranteed - // to NOT have a path separator at the end. For example, "c:\windows\temp" - // Directories are also guaranteed to exist when this function succeeds. - // - // Returns true if the directory or file was successfully retrieved. On - // failure, 'path' will not be changed. - static bool Get(int key, butil::FilePath* path); - - // Overrides the path to a special directory or file. This cannot be used to - // change the value of DIR_CURRENT, but that should be obvious. Also, if the - // path specifies a directory that does not exist, the directory will be - // created by this method. This method returns true if successful. - // - // If the given path is relative, then it will be resolved against - // DIR_CURRENT. - // - // WARNING: Consumers of PathService::Get may expect paths to be constant - // over the lifetime of the app, so this method should be used with caution. - // - // Unit tests generally should use ScopedPathOverride instead. Overrides from - // one test should not carry over to another. - static bool Override(int key, const butil::FilePath& path); - - // This function does the same as PathService::Override but it takes extra - // parameters: - // - |is_absolute| indicates that |path| has already been expanded into an - // absolute path, otherwise MakeAbsoluteFilePath() will be used. This is - // useful to override paths that may not exist yet, since MakeAbsoluteFilePath - // fails for those. Note that MakeAbsoluteFilePath also expands symbolic - // links, even if path.IsAbsolute() is already true. - // - |create| guides whether the directory to be overriden must - // be created in case it doesn't exist already. - static bool OverrideAndCreateIfNeeded(int key, - const butil::FilePath& path, - bool is_absolute, - bool create); - - // To extend the set of supported keys, you can register a path provider, - // which is just a function mirroring PathService::Get. The ProviderFunc - // returns false if it cannot provide a non-empty path for the given key. - // Otherwise, true is returned. - // - // WARNING: This function could be called on any thread from which the - // PathService is used, so a the ProviderFunc MUST BE THREADSAFE. - // - typedef bool (*ProviderFunc)(int, butil::FilePath*); - - // Call to register a path provider. You must specify the range "[key_start, - // key_end)" of supported path keys. - static void RegisterProvider(ProviderFunc provider, - int key_start, - int key_end); - - // Disable internal cache. - static void DisableCache(); - - private: - friend class butil::ScopedPathOverride; - FRIEND_TEST_ALL_PREFIXES(PathServiceTest, RemoveOverride); - - // Removes an override for a special directory or file. Returns true if there - // was an override to remove or false if none was present. - // NOTE: This function is intended to be used by tests only! - static bool RemoveOverride(int key); -}; - -#endif // BASE_PATH_SERVICE_H_ diff --git a/src/butil/process/internal_linux.cc b/src/butil/process/internal_linux.cc deleted file mode 100644 index 6666548782..0000000000 --- a/src/butil/process/internal_linux.cc +++ /dev/null @@ -1,189 +0,0 @@ -// 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. - -#include "butil/process/internal_linux.h" - -#include - -#include -#include -#include - -#include "butil/file_util.h" -#include "butil/logging.h" -#include "butil/strings/string_number_conversions.h" -#include "butil/strings/string_split.h" -#include "butil/strings/string_util.h" -#include "butil/threading/thread_restrictions.h" -#include "butil/time/time.h" - -namespace butil { -namespace internal { - -const char kProcDir[] = "/proc"; - -const char kStatFile[] = "stat"; - -butil::FilePath GetProcPidDir(pid_t pid) { - return butil::FilePath(kProcDir).Append(IntToString(pid)); -} - -pid_t ProcDirSlotToPid(const char* d_name) { - int i; - for (i = 0; i < NAME_MAX && d_name[i]; ++i) { - if (!IsAsciiDigit(d_name[i])) { - return 0; - } - } - if (i == NAME_MAX) - return 0; - - // Read the process's command line. - pid_t pid; - std::string pid_string(d_name); - if (!StringToInt(pid_string, &pid)) { - NOTREACHED(); - return 0; - } - return pid; -} - -bool ReadProcFile(const FilePath& file, std::string* buffer) { - buffer->clear(); - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - - if (!ReadFileToString(file, buffer)) { - DLOG(WARNING) << "Failed to read " << file.MaybeAsASCII(); - return false; - } - return !buffer->empty(); -} - -bool ReadProcStats(pid_t pid, std::string* buffer) { - FilePath stat_file = internal::GetProcPidDir(pid).Append(kStatFile); - return ReadProcFile(stat_file, buffer); -} - -bool ParseProcStats(const std::string& stats_data, - std::vector* proc_stats) { - // |stats_data| may be empty if the process disappeared somehow. - // e.g. http://crbug.com/145811 - if (stats_data.empty()) - return false; - - // The stat file is formatted as: - // pid (process name) data1 data2 .... dataN - // Look for the closing paren by scanning backwards, to avoid being fooled by - // processes with ')' in the name. - size_t open_parens_idx = stats_data.find(" ("); - size_t close_parens_idx = stats_data.rfind(") "); - if (open_parens_idx == std::string::npos || - close_parens_idx == std::string::npos || - open_parens_idx > close_parens_idx) { - DLOG(WARNING) << "Failed to find matched parens in '" << stats_data << "'"; - NOTREACHED(); - return false; - } - open_parens_idx++; - - proc_stats->clear(); - // PID. - proc_stats->push_back(stats_data.substr(0, open_parens_idx)); - // Process name without parentheses. - proc_stats->push_back( - stats_data.substr(open_parens_idx + 1, - close_parens_idx - (open_parens_idx + 1))); - - // Split the rest. - std::vector other_stats; - SplitString(stats_data.substr(close_parens_idx + 2), ' ', &other_stats); - for (size_t i = 0; i < other_stats.size(); ++i) - proc_stats->push_back(other_stats[i]); - return true; -} - -typedef std::map ProcStatMap; -void ParseProcStat(const std::string& contents, ProcStatMap* output) { - typedef std::pair StringPair; - std::vector key_value_pairs; - SplitStringIntoKeyValuePairs(contents, ' ', '\n', &key_value_pairs); - for (size_t i = 0; i < key_value_pairs.size(); ++i) { - const StringPair& key_value_pair = key_value_pairs[i]; - output->insert(key_value_pair); - } -} - -int64_t GetProcStatsFieldAsInt64(const std::vector& proc_stats, - ProcStatsFields field_num) { - DCHECK_GE(field_num, VM_PPID); - CHECK_LT(static_cast(field_num), proc_stats.size()); - - int64_t value; - return StringToInt64(proc_stats[field_num], &value) ? value : 0; -} - -size_t GetProcStatsFieldAsSizeT(const std::vector& proc_stats, - ProcStatsFields field_num) { - DCHECK_GE(field_num, VM_PPID); - CHECK_LT(static_cast(field_num), proc_stats.size()); - - size_t value; - return StringToSizeT(proc_stats[field_num], &value) ? value : 0; -} - -int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) { - std::string stats_data; - if (!ReadProcStats(pid, &stats_data)) - return 0; - std::vector proc_stats; - if (!ParseProcStats(stats_data, &proc_stats)) - return 0; - return GetProcStatsFieldAsInt64(proc_stats, field_num); -} - -size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, - ProcStatsFields field_num) { - std::string stats_data; - if (!ReadProcStats(pid, &stats_data)) - return 0; - std::vector proc_stats; - if (!ParseProcStats(stats_data, &proc_stats)) - return 0; - return GetProcStatsFieldAsSizeT(proc_stats, field_num); -} - -Time GetBootTime() { - FilePath path("/proc/stat"); - std::string contents; - if (!ReadProcFile(path, &contents)) - return Time(); - ProcStatMap proc_stat; - ParseProcStat(contents, &proc_stat); - ProcStatMap::const_iterator btime_it = proc_stat.find("btime"); - if (btime_it == proc_stat.end()) - return Time(); - int btime; - if (!StringToInt(btime_it->second, &btime)) - return Time(); - return Time::FromTimeT(btime); -} - -TimeDelta ClockTicksToTimeDelta(int clock_ticks) { - // This queries the /proc-specific scaling factor which is - // conceptually the system hertz. To dump this value on another - // system, try - // od -t dL /proc/self/auxv - // and look for the number after 17 in the output; mine is - // 0000040 17 100 3 134512692 - // which means the answer is 100. - // It may be the case that this value is always 100. - static const int kHertz = sysconf(_SC_CLK_TCK); - - return TimeDelta::FromMicroseconds( - Time::kMicrosecondsPerSecond * clock_ticks / kHertz); -} - -} // namespace internal -} // namespace butil diff --git a/src/butil/process/internal_linux.h b/src/butil/process/internal_linux.h deleted file mode 100644 index 0dabf27e22..0000000000 --- a/src/butil/process/internal_linux.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 internal routines that are called by other files in -// butil/process/. - -#ifndef BASE_PROCESS_LINUX_INTERNAL_H_ -#define BASE_PROCESS_LINUX_INTERNAL_H_ - -#include - -#include "butil/files/file_path.h" - -namespace butil { - -class Time; -class TimeDelta; - -namespace internal { - -// "/proc" -extern const char kProcDir[]; - -// "stat" -extern const char kStatFile[]; - -// Returns a FilePath to "/proc/pid". -butil::FilePath GetProcPidDir(pid_t pid); - -// Take a /proc directory entry named |d_name|, and if it is the directory for -// a process, convert it to a pid_t. -// Returns 0 on failure. -// e.g. /proc/self/ will return 0, whereas /proc/1234 will return 1234. -pid_t ProcDirSlotToPid(const char* d_name); - -// Reads /proc//stat into |buffer|. Returns true if the file can be read -// and is non-empty. -bool ReadProcStats(pid_t pid, std::string* buffer); - -// Takes |stats_data| and populates |proc_stats| with the values split by -// spaces. Taking into account the 2nd field may, in itself, contain spaces. -// Returns true if successful. -bool ParseProcStats(const std::string& stats_data, - std::vector* proc_stats); - -// Fields from /proc//stat, 0-based. See man 5 proc. -// If the ordering ever changes, carefully review functions that use these -// values. -enum ProcStatsFields { - VM_COMM = 1, // Filename of executable, without parentheses. - VM_STATE = 2, // Letter indicating the state of the process. - VM_PPID = 3, // PID of the parent. - VM_PGRP = 4, // Process group id. - VM_UTIME = 13, // Time scheduled in user mode in clock ticks. - VM_STIME = 14, // Time scheduled in kernel mode in clock ticks. - VM_NUMTHREADS = 19, // Number of threads. - VM_STARTTIME = 21, // The time the process started in clock ticks. - VM_VSIZE = 22, // Virtual memory size in bytes. - VM_RSS = 23, // Resident Set Size in pages. -}; - -// Reads the |field_num|th field from |proc_stats|. Returns 0 on failure. -// This version does not handle the first 3 values, since the first value is -// simply |pid|, and the next two values are strings. -int64_t GetProcStatsFieldAsInt64(const std::vector& proc_stats, - ProcStatsFields field_num); - -// Same as GetProcStatsFieldAsInt64(), but for size_t values. -size_t GetProcStatsFieldAsSizeT(const std::vector& proc_stats, - ProcStatsFields field_num); - -// Convenience wrapper around GetProcStatsFieldAsInt64(), ParseProcStats() and -// ReadProcStats(). See GetProcStatsFieldAsInt64() for details. -int64_t ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num); - -// Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values. -size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid, - ProcStatsFields field_num); - -// Returns the time that the OS started. Clock ticks are relative to this. -Time GetBootTime(); - -// Converts Linux clock ticks to a wall time delta. -TimeDelta ClockTicksToTimeDelta(int clock_ticks); - -} // namespace internal -} // namespace butil - -#endif // BASE_PROCESS_LINUX_INTERNAL_H_ diff --git a/src/butil/process/kill.cc b/src/butil/process/kill.cc deleted file mode 100644 index 69995dbf09..0000000000 --- a/src/butil/process/kill.cc +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/kill.h" - -#include "butil/process/process_iterator.h" - -namespace butil { - -bool KillProcesses(const FilePath::StringType& executable_name, - int exit_code, - const ProcessFilter* filter) { - bool result = true; - NamedProcessIterator iter(executable_name, filter); - while (const ProcessEntry* entry = iter.NextProcessEntry()) { -#if defined(OS_WIN) - result &= KillProcessById(entry->pid(), exit_code, true); -#else - result &= KillProcess(entry->pid(), exit_code, true); -#endif - } - return result; -} - -} // namespace butil diff --git a/src/butil/process/kill.h b/src/butil/process/kill.h deleted file mode 100644 index fa3aff1a0e..0000000000 --- a/src/butil/process/kill.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 routines to kill processes and get the exit code and -// termination status. - -#ifndef BASE_PROCESS_KILL_H_ -#define BASE_PROCESS_KILL_H_ - -#include "butil/files/file_path.h" -#include "butil/process/process_handle.h" -#include "butil/time/time.h" - -namespace butil { - -class ProcessFilter; - -// Return status values from GetTerminationStatus. Don't use these as -// exit code arguments to KillProcess*(), use platform/application -// specific values instead. -enum TerminationStatus { - TERMINATION_STATUS_NORMAL_TERMINATION, // zero exit status - TERMINATION_STATUS_ABNORMAL_TERMINATION, // non-zero exit status - TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill - TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault - TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet -#if defined(OS_ANDROID) - // On Android processes are spawned from the system Zygote and we do not get - // the termination status. We can't know if the termination was a crash or an - // oom kill for sure, but we can use status of the strong process bindings as - // a hint. - TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill -#endif - TERMINATION_STATUS_MAX_ENUM -}; - -// Attempts to kill all the processes on the current machine that were launched -// from the given executable name, ending them with the given exit code. If -// filter is non-null, then only processes selected by the filter are killed. -// Returns true if all processes were able to be killed off, false if at least -// one couldn't be killed. -BASE_EXPORT bool KillProcesses(const FilePath::StringType& executable_name, - int exit_code, - const ProcessFilter* filter); - -// Attempts to kill the process identified by the given process -// entry structure, giving it the specified exit code. If |wait| is true, wait -// for the process to be actually terminated before returning. -// Returns true if this is successful, false otherwise. -BASE_EXPORT bool KillProcess(ProcessHandle process, int exit_code, bool wait); - -#if defined(OS_POSIX) -// Attempts to kill the process group identified by |process_group_id|. Returns -// true on success. -BASE_EXPORT bool KillProcessGroup(ProcessHandle process_group_id); -#endif // defined(OS_POSIX) - -#if defined(OS_WIN) -BASE_EXPORT bool KillProcessById(ProcessId process_id, - int exit_code, - bool wait); -#endif // defined(OS_WIN) - -// Get the termination status of the process by interpreting the -// circumstances of the child process' death. |exit_code| is set to -// the status returned by waitpid() on POSIX, and from -// GetExitCodeProcess() on Windows. |exit_code| may be NULL if the -// caller is not interested in it. Note that on Linux, this function -// will only return a useful result the first time it is called after -// the child exits (because it will reap the child and the information -// will no longer be available). -BASE_EXPORT TerminationStatus GetTerminationStatus(ProcessHandle handle, - int* exit_code); - -#if defined(OS_POSIX) -// Send a kill signal to the process and then wait for the process to exit -// and get the termination status. -// -// This is used in situations where it is believed that the process is dead -// or dying (because communication with the child process has been cut). -// In order to avoid erroneously returning that the process is still running -// because the kernel is still cleaning it up, this will wait for the process -// to terminate. In order to avoid the risk of hanging while waiting for the -// process to terminate, send a SIGKILL to the process before waiting for the -// termination status. -// -// Note that it is not an option to call WaitForExitCode and then -// GetTerminationStatus as the child will be reaped when WaitForExitCode -// returns, and this information will be lost. -// -BASE_EXPORT TerminationStatus GetKnownDeadTerminationStatus( - ProcessHandle handle, int* exit_code); -#endif // defined(OS_POSIX) - -// Waits for process to exit. On POSIX systems, if the process hasn't been -// signaled then puts the exit code in |exit_code|; otherwise it's considered -// a failure. On Windows |exit_code| is always filled. Returns true on success, -// and closes |handle| in any case. -BASE_EXPORT bool WaitForExitCode(ProcessHandle handle, int* exit_code); - -// Waits for process to exit. If it did exit within |timeout_milliseconds|, -// then puts the exit code in |exit_code|, and returns true. -// In POSIX systems, if the process has been signaled then |exit_code| is set -// to -1. Returns false on failure (the caller is then responsible for closing -// |handle|). -// The caller is always responsible for closing the |handle|. -BASE_EXPORT bool WaitForExitCodeWithTimeout(ProcessHandle handle, - int* exit_code, - butil::TimeDelta timeout); - -// Wait for all the processes based on the named executable to exit. If filter -// is non-null, then only processes selected by the filter are waited on. -// Returns after all processes have exited or wait_milliseconds have expired. -// Returns true if all the processes exited, false otherwise. -BASE_EXPORT bool WaitForProcessesToExit( - const FilePath::StringType& executable_name, - butil::TimeDelta wait, - const ProcessFilter* filter); - -// Wait for a single process to exit. Return true if it exited cleanly within -// the given time limit. On Linux |handle| must be a child process, however -// on Mac and Windows it can be any process. -BASE_EXPORT bool WaitForSingleProcess(ProcessHandle handle, - butil::TimeDelta wait); - -// Waits a certain amount of time (can be 0) for all the processes with a given -// executable name to exit, then kills off any of them that are still around. -// If filter is non-null, then only processes selected by the filter are waited -// on. Killed processes are ended with the given exit code. Returns false if -// any processes needed to be killed, true if they all exited cleanly within -// the wait_milliseconds delay. -BASE_EXPORT bool CleanupProcesses(const FilePath::StringType& executable_name, - butil::TimeDelta wait, - int exit_code, - const ProcessFilter* filter); - -// This method ensures that the specified process eventually terminates, and -// then it closes the given process handle. -// -// It assumes that the process has already been signalled to exit, and it -// begins by waiting a small amount of time for it to exit. If the process -// does not appear to have exited, then this function starts to become -// aggressive about ensuring that the process terminates. -// -// On Linux this method does not block the calling thread. -// On OS X this method may block for up to 2 seconds. -// -// NOTE: The process handle must have been opened with the PROCESS_TERMINATE -// and SYNCHRONIZE permissions. -// -BASE_EXPORT void EnsureProcessTerminated(ProcessHandle process_handle); - -#if defined(OS_POSIX) && !defined(OS_MACOSX) -// The nicer version of EnsureProcessTerminated() that is patient and will -// wait for |process_handle| to finish and then reap it. -BASE_EXPORT void EnsureProcessGetsReaped(ProcessHandle process_handle); -#endif - -} // namespace butil - -#endif // BASE_PROCESS_KILL_H_ diff --git a/src/butil/process/kill_mac.cc b/src/butil/process/kill_mac.cc deleted file mode 100644 index d50a229109..0000000000 --- a/src/butil/process/kill_mac.cc +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/kill.h" - -#include -#include -#include -#include - -#include "butil/file_util.h" -#include "butil/files/scoped_file.h" -#include "butil/logging.h" -#include "butil/posix/eintr_wrapper.h" - -namespace butil { - -namespace { - -const int kWaitBeforeKillSeconds = 2; - -// Reap |child| process. This call blocks until completion. -void BlockingReap(pid_t child) { - const pid_t result = HANDLE_EINTR(waitpid(child, NULL, 0)); - if (result == -1) { - DPLOG(ERROR) << "waitpid(" << child << ", NULL, 0)"; - } -} - -// Waits for |timeout| seconds for the given |child| to exit and reap it. If -// the child doesn't exit within the time specified, kills it. -// -// This function takes two approaches: first, it tries to use kqueue to -// observe when the process exits. kevent can monitor a kqueue with a -// timeout, so this method is preferred to wait for a specified period of -// time. Once the kqueue indicates the process has exited, waitpid will reap -// the exited child. If the kqueue doesn't provide an exit event notification, -// before the timeout expires, or if the kqueue fails or misbehaves, the -// process will be mercilessly killed and reaped. -// -// A child process passed to this function may be in one of several states: -// running, terminated and not yet reaped, and (apparently, and unfortunately) -// terminated and already reaped. Normally, a process will at least have been -// asked to exit before this function is called, but this is not required. -// If a process is terminating and unreaped, there may be a window between the -// time that kqueue will no longer recognize it and when it becomes an actual -// zombie that a non-blocking (WNOHANG) waitpid can reap. This condition is -// detected when kqueue indicates that the process is not running and a -// non-blocking waitpid fails to reap the process but indicates that it is -// still running. In this event, a blocking attempt to reap the process -// collects the known-dying child, preventing zombies from congregating. -// -// In the event that the kqueue misbehaves entirely, as it might under a -// EMFILE condition ("too many open files", or out of file descriptors), this -// function will forcibly kill and reap the child without delay. This -// eliminates another potential zombie vector. (If you're out of file -// descriptors, you're probably deep into something else, but that doesn't -// mean that zombies be allowed to kick you while you're down.) -// -// The fact that this function seemingly can be called to wait on a child -// that's not only already terminated but already reaped is a bit of a -// problem: a reaped child's pid can be reclaimed and may refer to a distinct -// process in that case. The fact that this function can seemingly be called -// to wait on a process that's not even a child is also a problem: kqueue will -// work in that case, but waitpid won't, and killing a non-child might not be -// the best approach. -void WaitForChildToDie(pid_t child, int timeout) { - DCHECK(child > 0); - DCHECK(timeout > 0); - - // DON'T ADD ANY EARLY RETURNS TO THIS FUNCTION without ensuring that - // |child| has been reaped. Specifically, even if a kqueue, kevent, or other - // call fails, this function should fall back to the last resort of trying - // to kill and reap the process. Not observing this rule will resurrect - // zombies. - - int result; - - ScopedFD kq(HANDLE_EINTR(kqueue())); - if (!kq.is_valid()) { - DPLOG(ERROR) << "kqueue()"; - } else { - struct kevent change = {0}; - EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); - - if (result == -1) { - if (errno != ESRCH) { - DPLOG(ERROR) << "kevent (setup " << child << ")"; - } else { - // At this point, one of the following has occurred: - // 1. The process has died but has not yet been reaped. - // 2. The process has died and has already been reaped. - // 3. The process is in the process of dying. It's no longer - // kqueueable, but it may not be waitable yet either. Mark calls - // this case the "zombie death race". - - result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG)); - - if (result != 0) { - // A positive result indicates case 1. waitpid succeeded and reaped - // the child. A result of -1 indicates case 2. The child has already - // been reaped. In both of these cases, no further action is - // necessary. - return; - } - - // |result| is 0, indicating case 3. The process will be waitable in - // short order. Fall back out of the kqueue code to kill it (for good - // measure) and reap it. - } - } else { - // Keep track of the elapsed time to be able to restart kevent if it's - // interrupted. - TimeDelta remaining_delta = TimeDelta::FromSeconds(timeout); - TimeTicks deadline = TimeTicks::Now() + remaining_delta; - result = -1; - struct kevent event = {0}; - while (remaining_delta.InMilliseconds() > 0) { - const struct timespec remaining_timespec = remaining_delta.ToTimeSpec(); - result = kevent(kq.get(), NULL, 0, &event, 1, &remaining_timespec); - if (result == -1 && errno == EINTR) { - remaining_delta = deadline - TimeTicks::Now(); - result = 0; - } else { - break; - } - } - - if (result == -1) { - DPLOG(ERROR) << "kevent (wait " << child << ")"; - } else if (result > 1) { - DLOG(ERROR) << "kevent (wait " << child << "): unexpected result " - << result; - } else if (result == 1) { - if ((event.fflags & NOTE_EXIT) && - (event.ident == static_cast(child))) { - // The process is dead or dying. This won't block for long, if at - // all. - BlockingReap(child); - return; - } else { - DLOG(ERROR) << "kevent (wait " << child - << "): unexpected event: fflags=" << event.fflags - << ", ident=" << event.ident; - } - } - } - } - - // The child is still alive, or is very freshly dead. Be sure by sending it - // a signal. This is safe even if it's freshly dead, because it will be a - // zombie (or on the way to zombiedom) and kill will return 0 even if the - // signal is not delivered to a live process. - result = kill(child, SIGKILL); - if (result == -1) { - DPLOG(ERROR) << "kill(" << child << ", SIGKILL)"; - } else { - // The child is definitely on the way out now. BlockingReap won't need to - // wait for long, if at all. - BlockingReap(child); - } -} - -} // namespace - -void EnsureProcessTerminated(ProcessHandle process) { - WaitForChildToDie(process, kWaitBeforeKillSeconds); -} - -} // namespace butil diff --git a/src/butil/process/kill_posix.cc b/src/butil/process/kill_posix.cc deleted file mode 100644 index b12676636c..0000000000 --- a/src/butil/process/kill_posix.cc +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/kill.h" - -#include -#include -#include -#include - -#include "butil/file_util.h" -#include "butil/files/scoped_file.h" -#include "butil/logging.h" -#include "butil/posix/eintr_wrapper.h" -#include "butil/process/process_iterator.h" -#include "butil/synchronization/waitable_event.h" -#include "butil/third_party/dynamic_annotations/dynamic_annotations.h" -#include "butil/threading/platform_thread.h" - -namespace butil { - -namespace { - -bool WaitpidWithTimeout(ProcessHandle handle, - int* status, - butil::TimeDelta wait) { - // This POSIX version of this function only guarantees that we wait no less - // than |wait| for the process to exit. The child process may - // exit sometime before the timeout has ended but we may still block for up - // to 256 milliseconds after the fact. - // - // waitpid() has no direct support on POSIX for specifying a timeout, you can - // either ask it to block indefinitely or return immediately (WNOHANG). - // When a child process terminates a SIGCHLD signal is sent to the parent. - // Catching this signal would involve installing a signal handler which may - // affect other parts of the application and would be difficult to debug. - // - // Our strategy is to call waitpid() once up front to check if the process - // has already exited, otherwise to loop for |wait|, sleeping for - // at most 256 milliseconds each time using usleep() and then calling - // waitpid(). The amount of time we sleep starts out at 1 milliseconds, and - // we double it every 4 sleep cycles. - // - // usleep() is speced to exit if a signal is received for which a handler - // has been installed. This means that when a SIGCHLD is sent, it will exit - // depending on behavior external to this function. - // - // This function is used primarily for unit tests, if we want to use it in - // the application itself it would probably be best to examine other routes. - - if (wait.InMilliseconds() == butil::kNoTimeout) { - return HANDLE_EINTR(waitpid(handle, status, 0)) > 0; - } - - pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); - static const int64_t kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds. - int64_t max_sleep_time_usecs = 1 << 10; // ~1 milliseconds. - int64_t double_sleep_time = 0; - - // If the process hasn't exited yet, then sleep and try again. - TimeTicks wakeup_time = TimeTicks::Now() + wait; - while (ret_pid == 0) { - TimeTicks now = TimeTicks::Now(); - if (now > wakeup_time) - break; - // Guaranteed to be non-negative! - int64_t sleep_time_usecs = (wakeup_time - now).InMicroseconds(); - // Sleep for a bit while we wait for the process to finish. - if (sleep_time_usecs > max_sleep_time_usecs) - sleep_time_usecs = max_sleep_time_usecs; - - // usleep() will return 0 and set errno to EINTR on receipt of a signal - // such as SIGCHLD. - usleep(sleep_time_usecs); - ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG)); - - if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) && - (double_sleep_time++ % 4 == 0)) { - max_sleep_time_usecs *= 2; - } - } - - return ret_pid > 0; -} - -TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, - bool can_block, - int* exit_code) { - int status = 0; - const pid_t result = HANDLE_EINTR(waitpid(handle, &status, - can_block ? 0 : WNOHANG)); - if (result == -1) { - DPLOG(ERROR) << "waitpid(" << handle << ")"; - if (exit_code) - *exit_code = 0; - return TERMINATION_STATUS_NORMAL_TERMINATION; - } else if (result == 0) { - // the child hasn't exited yet. - if (exit_code) - *exit_code = 0; - return TERMINATION_STATUS_STILL_RUNNING; - } - - if (exit_code) - *exit_code = status; - - if (WIFSIGNALED(status)) { - switch (WTERMSIG(status)) { - case SIGABRT: - case SIGBUS: - case SIGFPE: - case SIGILL: - case SIGSEGV: - return TERMINATION_STATUS_PROCESS_CRASHED; - case SIGINT: - case SIGKILL: - case SIGTERM: - return TERMINATION_STATUS_PROCESS_WAS_KILLED; - default: - break; - } - } - - if (WIFEXITED(status) && WEXITSTATUS(status) != 0) - return TERMINATION_STATUS_ABNORMAL_TERMINATION; - - return TERMINATION_STATUS_NORMAL_TERMINATION; -} - -} // namespace - -// Attempts to kill the process identified by the given process -// entry structure. Ignores specified exit_code; posix can't force that. -// Returns true if this is successful, false otherwise. -bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) { - DCHECK_GT(process_id, 1) << " tried to kill invalid process_id"; - if (process_id <= 1) - return false; - bool result = kill(process_id, SIGTERM) == 0; - if (result && wait) { - int tries = 60; - - if (RunningOnValgrind()) { - // Wait for some extra time when running under Valgrind since the child - // processes may take some time doing leak checking. - tries *= 2; - } - - unsigned sleep_ms = 4; - - // The process may not end immediately due to pending I/O - bool exited = false; - while (tries-- > 0) { - pid_t pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG)); - if (pid == process_id) { - exited = true; - break; - } - if (pid == -1) { - if (errno == ECHILD) { - // The wait may fail with ECHILD if another process also waited for - // the same pid, causing the process state to get cleaned up. - exited = true; - break; - } - DPLOG(ERROR) << "Error waiting for process " << process_id; - } - - usleep(sleep_ms * 1000); - const unsigned kMaxSleepMs = 1000; - if (sleep_ms < kMaxSleepMs) - sleep_ms *= 2; - } - - // If we're waiting and the child hasn't died by now, force it - // with a SIGKILL. - if (!exited) - result = kill(process_id, SIGKILL) == 0; - } - - if (!result) - DPLOG(ERROR) << "Unable to terminate process " << process_id; - - return result; -} - -bool KillProcessGroup(ProcessHandle process_group_id) { - bool result = kill(-1 * process_group_id, SIGKILL) == 0; - if (!result) - DPLOG(ERROR) << "Unable to terminate process group " << process_group_id; - return result; -} - -TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { - return GetTerminationStatusImpl(handle, false /* can_block */, exit_code); -} - -TerminationStatus GetKnownDeadTerminationStatus(ProcessHandle handle, - int* exit_code) { - bool result = kill(handle, SIGKILL) == 0; - - if (!result) - DPLOG(ERROR) << "Unable to terminate process " << handle; - - return GetTerminationStatusImpl(handle, true /* can_block */, exit_code); -} - -bool WaitForExitCode(ProcessHandle handle, int* exit_code) { - int status; - if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) { - NOTREACHED(); - return false; - } - - if (WIFEXITED(status)) { - *exit_code = WEXITSTATUS(status); - return true; - } - - // If it didn't exit cleanly, it must have been signaled. - DCHECK(WIFSIGNALED(status)); - return false; -} - -bool WaitForExitCodeWithTimeout(ProcessHandle handle, - int* exit_code, - butil::TimeDelta timeout) { - int status; - if (!WaitpidWithTimeout(handle, &status, timeout)) - return false; - if (WIFSIGNALED(status)) { - *exit_code = -1; - return true; - } - if (WIFEXITED(status)) { - *exit_code = WEXITSTATUS(status); - return true; - } - return false; -} - -bool WaitForProcessesToExit(const FilePath::StringType& executable_name, - butil::TimeDelta wait, - const ProcessFilter* filter) { - bool result = false; - - // TODO(port): This is inefficient, but works if there are multiple procs. - // TODO(port): use waitpid to avoid leaving zombies around - - butil::TimeTicks end_time = butil::TimeTicks::Now() + wait; - do { - NamedProcessIterator iter(executable_name, filter); - if (!iter.NextProcessEntry()) { - result = true; - break; - } - butil::PlatformThread::Sleep(butil::TimeDelta::FromMilliseconds(100)); - } while ((end_time - butil::TimeTicks::Now()) > butil::TimeDelta()); - - return result; -} - -#if defined(OS_MACOSX) -// Using kqueue on Mac so that we can wait on non-child processes. -// We can't use kqueues on child processes because we need to reap -// our own children using wait. -static bool WaitForSingleNonChildProcess(ProcessHandle handle, - butil::TimeDelta wait) { - DCHECK_GT(handle, 0); - DCHECK(wait.InMilliseconds() == butil::kNoTimeout || wait > butil::TimeDelta()); - - ScopedFD kq(kqueue()); - if (!kq.is_valid()) { - DPLOG(ERROR) << "kqueue"; - return false; - } - - struct kevent change = {0}; - EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); - int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL)); - if (result == -1) { - if (errno == ESRCH) { - // If the process wasn't found, it must be dead. - return true; - } - - DPLOG(ERROR) << "kevent (setup " << handle << ")"; - return false; - } - - // Keep track of the elapsed time to be able to restart kevent if it's - // interrupted. - bool wait_forever = wait.InMilliseconds() == butil::kNoTimeout; - butil::TimeDelta remaining_delta; - butil::TimeTicks deadline; - if (!wait_forever) { - remaining_delta = wait; - deadline = butil::TimeTicks::Now() + remaining_delta; - } - - result = -1; - struct kevent event = {0}; - - while (wait_forever || remaining_delta > butil::TimeDelta()) { - struct timespec remaining_timespec; - struct timespec* remaining_timespec_ptr; - if (wait_forever) { - remaining_timespec_ptr = NULL; - } else { - remaining_timespec = remaining_delta.ToTimeSpec(); - remaining_timespec_ptr = &remaining_timespec; - } - - result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr); - - if (result == -1 && errno == EINTR) { - if (!wait_forever) { - remaining_delta = deadline - butil::TimeTicks::Now(); - } - result = 0; - } else { - break; - } - } - - if (result < 0) { - DPLOG(ERROR) << "kevent (wait " << handle << ")"; - return false; - } else if (result > 1) { - DLOG(ERROR) << "kevent (wait " << handle << "): unexpected result " - << result; - return false; - } else if (result == 0) { - // Timed out. - return false; - } - - DCHECK_EQ(result, 1); - - if (event.filter != EVFILT_PROC || - (event.fflags & NOTE_EXIT) == 0 || - event.ident != static_cast(handle)) { - DLOG(ERROR) << "kevent (wait " << handle - << "): unexpected event: filter=" << event.filter - << ", fflags=" << event.fflags - << ", ident=" << event.ident; - return false; - } - - return true; -} -#endif // OS_MACOSX - -bool WaitForSingleProcess(ProcessHandle handle, butil::TimeDelta wait) { - ProcessHandle parent_pid = GetParentProcessId(handle); - ProcessHandle our_pid = Process::Current().handle(); - if (parent_pid != our_pid) { -#if defined(OS_MACOSX) - // On Mac we can wait on non child processes. - return WaitForSingleNonChildProcess(handle, wait); -#else - // Currently on Linux we can't handle non child processes. - NOTIMPLEMENTED(); -#endif // OS_MACOSX - } - - int status; - if (!WaitpidWithTimeout(handle, &status, wait)) - return false; - return WIFEXITED(status); -} - -bool CleanupProcesses(const FilePath::StringType& executable_name, - butil::TimeDelta wait, - int exit_code, - const ProcessFilter* filter) { - bool exited_cleanly = WaitForProcessesToExit(executable_name, wait, filter); - if (!exited_cleanly) - KillProcesses(executable_name, exit_code, filter); - return exited_cleanly; -} - -#if !defined(OS_MACOSX) - -namespace { - -// Return true if the given child is dead. This will also reap the process. -// Doesn't block. -static bool IsChildDead(pid_t child) { - const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG)); - if (result == -1) { - DPLOG(ERROR) << "waitpid(" << child << ")"; - NOTREACHED(); - } else if (result > 0) { - // The child has died. - return true; - } - - return false; -} - -// A thread class which waits for the given child to exit and reaps it. -// If the child doesn't exit within a couple of seconds, kill it. -class BackgroundReaper : public PlatformThread::Delegate { - public: - BackgroundReaper(pid_t child, unsigned timeout) - : child_(child), - timeout_(timeout) { - } - - // Overridden from PlatformThread::Delegate: - virtual void ThreadMain() OVERRIDE { - WaitForChildToDie(); - delete this; - } - - void WaitForChildToDie() { - // Wait forever case. - if (timeout_ == 0) { - pid_t r = HANDLE_EINTR(waitpid(child_, NULL, 0)); - if (r != child_) { - DPLOG(ERROR) << "While waiting for " << child_ - << " to terminate, we got the following result: " << r; - } - return; - } - - // There's no good way to wait for a specific child to exit in a timed - // fashion. (No kqueue on Linux), so we just loop and sleep. - - // Wait for 2 * timeout_ 500 milliseconds intervals. - for (unsigned i = 0; i < 2 * timeout_; ++i) { - PlatformThread::Sleep(TimeDelta::FromMilliseconds(500)); - if (IsChildDead(child_)) - return; - } - - if (kill(child_, SIGKILL) == 0) { - // SIGKILL is uncatchable. Since the signal was delivered, we can - // just wait for the process to die now in a blocking manner. - if (HANDLE_EINTR(waitpid(child_, NULL, 0)) < 0) - DPLOG(WARNING) << "waitpid"; - } else { - DLOG(ERROR) << "While waiting for " << child_ << " to terminate we" - << " failed to deliver a SIGKILL signal (" << errno << ")."; - } - } - - private: - const pid_t child_; - // Number of seconds to wait, if 0 then wait forever and do not attempt to - // kill |child_|. - const unsigned timeout_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundReaper); -}; - -} // namespace - -void EnsureProcessTerminated(ProcessHandle process) { - // If the child is already dead, then there's nothing to do. - if (IsChildDead(process)) - return; - - const unsigned timeout = 2; // seconds - BackgroundReaper* reaper = new BackgroundReaper(process, timeout); - PlatformThread::CreateNonJoinable(0, reaper); -} - -void EnsureProcessGetsReaped(ProcessHandle process) { - // If the child is already dead, then there's nothing to do. - if (IsChildDead(process)) - return; - - BackgroundReaper* reaper = new BackgroundReaper(process, 0); - PlatformThread::CreateNonJoinable(0, reaper); -} - -#endif // !defined(OS_MACOSX) - -} // namespace butil diff --git a/src/butil/process/launch.cc b/src/butil/process/launch.cc deleted file mode 100644 index 78896362bc..0000000000 --- a/src/butil/process/launch.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/launch.h" - -namespace butil { - -LaunchOptions::LaunchOptions() - : wait(false), -#if defined(OS_WIN) - start_hidden(false), - handles_to_inherit(NULL), - inherit_handles(false), - as_user(NULL), - empty_desktop_name(false), - job_handle(NULL), - stdin_handle(NULL), - stdout_handle(NULL), - stderr_handle(NULL), - force_breakaway_from_job_(false) -#else - clear_environ(false), - fds_to_remap(NULL), - maximize_rlimits(NULL), - new_process_group(false) -#if defined(OS_LINUX) - , clone_flags(0) - , allow_new_privs(false) -#endif // OS_LINUX -#if defined(OS_CHROMEOS) - , ctrl_terminal_fd(-1) -#endif // OS_CHROMEOS -#endif // !defined(OS_WIN) - { -} - -LaunchOptions::~LaunchOptions() { -} - -LaunchOptions LaunchOptionsForTest() { - LaunchOptions options; -#if defined(OS_LINUX) - // To prevent accidental privilege sharing to an untrusted child, processes - // are started with PR_SET_NO_NEW_PRIVS. Do not set that here, since this - // new child will be used for testing only. - options.allow_new_privs = true; -#endif - return options; -} - -} // namespace butil diff --git a/src/butil/process/launch.h b/src/butil/process/launch.h deleted file mode 100644 index e84d196e33..0000000000 --- a/src/butil/process/launch.h +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2013 The Chromium 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 functions for launching subprocesses. - -#ifndef BASE_PROCESS_LAUNCH_H_ -#define BASE_PROCESS_LAUNCH_H_ - -#include -#include -#include - -#include "butil/base_export.h" -#include "butil/basictypes.h" -#include "butil/environment.h" -#include "butil/process/process_handle.h" -#include "butil/strings/string_piece.h" - -#if defined(OS_POSIX) -#include "butil/posix/file_descriptor_shuffle.h" -#elif defined(OS_WIN) -#include -#include "butil/win/scoped_handle.h" -#endif - -namespace butil { - -class CommandLine; - -#if defined(OS_WIN) -typedef std::vector HandlesToInheritVector; -#endif -// TODO(viettrungluu): Only define this on POSIX? -typedef std::vector > FileHandleMappingVector; - -// Options for launching a subprocess that are passed to LaunchProcess(). -// The default constructor constructs the object with default options. -struct BASE_EXPORT LaunchOptions { - LaunchOptions(); - ~LaunchOptions(); - - // If true, wait for the process to complete. - bool wait; - -#if defined(OS_WIN) - bool start_hidden; - - // If non-null, inherit exactly the list of handles in this vector (these - // handles must be inheritable). This is only supported on Vista and higher. - HandlesToInheritVector* handles_to_inherit; - - // If true, the new process inherits handles from the parent. In production - // code this flag should be used only when running short-lived, trusted - // binaries, because open handles from other libraries and subsystems will - // leak to the child process, causing errors such as open socket hangs. - // Note: If |handles_to_inherit| is non-null, this flag is ignored and only - // those handles will be inherited (on Vista and higher). - bool inherit_handles; - - // If non-null, runs as if the user represented by the token had launched it. - // Whether the application is visible on the interactive desktop depends on - // the token belonging to an interactive logon session. - // - // To avoid hard to diagnose problems, when specified this loads the - // environment variables associated with the user and if this operation fails - // the entire call fails as well. - UserTokenHandle as_user; - - // If true, use an empty string for the desktop name. - bool empty_desktop_name; - - // If non-null, launches the application in that job object. The process will - // be terminated immediately and LaunchProcess() will fail if assignment to - // the job object fails. - HANDLE job_handle; - - // Handles for the redirection of stdin, stdout and stderr. The handles must - // be inheritable. Caller should either set all three of them or none (i.e. - // there is no way to redirect stderr without redirecting stdin). The - // |inherit_handles| flag must be set to true when redirecting stdio stream. - HANDLE stdin_handle; - HANDLE stdout_handle; - HANDLE stderr_handle; - - // If set to true, ensures that the child process is launched with the - // CREATE_BREAKAWAY_FROM_JOB flag which allows it to breakout of the parent - // job if any. - bool force_breakaway_from_job_; -#else - // Set/unset environment variables. These are applied on top of the parent - // process environment. Empty (the default) means to inherit the same - // environment. See AlterEnvironment(). - EnvironmentMap environ; - - // Clear the environment for the new process before processing changes from - // |environ|. - bool clear_environ; - - // If non-null, remap file descriptors according to the mapping of - // src fd->dest fd to propagate FDs into the child process. - // This pointer is owned by the caller and must live through the - // call to LaunchProcess(). - const FileHandleMappingVector* fds_to_remap; - - // Each element is an RLIMIT_* constant that should be raised to its - // rlim_max. This pointer is owned by the caller and must live through - // the call to LaunchProcess(). - const std::vector* maximize_rlimits; - - // If true, start the process in a new process group, instead of - // inheriting the parent's process group. The pgid of the child process - // will be the same as its pid. - bool new_process_group; - -#if defined(OS_LINUX) - // If non-zero, start the process using clone(), using flags as provided. - int clone_flags; - - // By default, child processes will have the PR_SET_NO_NEW_PRIVS bit set. If - // true, then this bit will not be set in the new child process. - bool allow_new_privs; -#endif // defined(OS_LINUX) - -#if defined(OS_CHROMEOS) - // If non-negative, the specified file descriptor will be set as the launched - // process' controlling terminal. - int ctrl_terminal_fd; -#endif // defined(OS_CHROMEOS) - -#if defined(OS_MACOSX) - // If this name is non-empty, the new child, after fork() but before exec(), - // will look up this server name in the bootstrap namespace. The resulting - // service port will be replaced as the bootstrap port in the child. Because - // the process's IPC space is cleared on exec(), any rights to the old - // bootstrap port will not be transferred to the new process. - std::string replacement_bootstrap_name; -#endif - -#endif // !defined(OS_WIN) -}; - -// Launch a process via the command line |cmdline|. -// See the documentation of LaunchOptions for details on |options|. -// -// Returns true upon success. -// -// Upon success, if |process_handle| is non-null, it will be filled in with the -// handle of the launched process. NOTE: In this case, the caller is -// responsible for closing the handle so that it doesn't leak! -// Otherwise, the process handle will be implicitly closed. -// -// Unix-specific notes: -// - All file descriptors open in the parent process will be closed in the -// child process except for any preserved by options::fds_to_remap, and -// stdin, stdout, and stderr. If not remapped by options::fds_to_remap, -// stdin is reopened as /dev/null, and the child is allowed to inherit its -// parent's stdout and stderr. -// - If the first argument on the command line does not contain a slash, -// PATH will be searched. (See man execvp.) -BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline, - const LaunchOptions& options, - ProcessHandle* process_handle); - -#if defined(OS_WIN) -// Windows-specific LaunchProcess that takes the command line as a -// string. Useful for situations where you need to control the -// command line arguments directly, but prefer the CommandLine version -// if launching Chrome itself. -// -// The first command line argument should be the path to the process, -// and don't forget to quote it. -// -// Example (including literal quotes) -// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\" -BASE_EXPORT bool LaunchProcess(const string16& cmdline, - const LaunchOptions& options, - win::ScopedHandle* process_handle); - -// Launches a process with elevated privileges. This does not behave exactly -// like LaunchProcess as it uses ShellExecuteEx instead of CreateProcess to -// create the process. This means the process will have elevated privileges -// and thus some common operations like OpenProcess will fail. The process will -// be available through the |process_handle| argument. Currently the only -// supported LaunchOptions are |start_hidden| and |wait|. -BASE_EXPORT bool LaunchElevatedProcess(const CommandLine& cmdline, - const LaunchOptions& options, - ProcessHandle* process_handle); - -#elif defined(OS_POSIX) -// A POSIX-specific version of LaunchProcess that takes an argv array -// instead of a CommandLine. Useful for situations where you need to -// control the command line arguments directly, but prefer the -// CommandLine version if launching Chrome itself. -BASE_EXPORT bool LaunchProcess(const std::vector& argv, - const LaunchOptions& options, - ProcessHandle* process_handle); - -// Close all file descriptors, except those which are a destination in the -// given multimap. Only call this function in a child process where you know -// that there aren't any other threads. -BASE_EXPORT void CloseSuperfluousFds(const InjectiveMultimap& saved_map); -#endif // defined(OS_POSIX) - -#if defined(OS_WIN) -// Set |job_object|'s JOBOBJECT_EXTENDED_LIMIT_INFORMATION -// BasicLimitInformation.LimitFlags to |limit_flags|. -BASE_EXPORT bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags); - -// Output multi-process printf, cout, cerr, etc to the cmd.exe console that ran -// chrome. This is not thread-safe: only call from main thread. -BASE_EXPORT void RouteStdioToConsole(); -#endif // defined(OS_WIN) - -// Executes the application specified by |cl| and wait for it to exit. Stores -// the output (stdout) in |output|. Redirects stderr to /dev/null. Returns true -// on success (application launched and exited cleanly, with exit code -// indicating success). -BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output); - -#if defined(OS_WIN) -// A Windows-specific version of GetAppOutput that takes a command line string -// instead of a CommandLine object. Useful for situations where you need to -// control the command line arguments directly. -BASE_EXPORT bool GetAppOutput(const StringPiece16& cl, std::string* output); -#endif - -#if defined(OS_POSIX) -// A POSIX-specific version of GetAppOutput that takes an argv array -// instead of a CommandLine. Useful for situations where you need to -// control the command line arguments directly. -BASE_EXPORT bool GetAppOutput(const std::vector& argv, - std::string* output); - -// A restricted version of |GetAppOutput()| which (a) clears the environment, -// and (b) stores at most |max_output| bytes; also, it doesn't search the path -// for the command. -BASE_EXPORT bool GetAppOutputRestricted(const CommandLine& cl, - std::string* output, size_t max_output); - -// A version of |GetAppOutput()| which also returns the exit code of the -// executed command. Returns true if the application runs and exits cleanly. If -// this is the case the exit code of the application is available in -// |*exit_code|. -BASE_EXPORT bool GetAppOutputWithExitCode(const CommandLine& cl, - std::string* output, int* exit_code); -#endif // defined(OS_POSIX) - -// If supported on the platform, and the user has sufficent rights, increase -// the current process's scheduling priority to a high priority. -BASE_EXPORT void RaiseProcessToHighPriority(); - -#if defined(OS_MACOSX) -// Restore the default exception handler, setting it to Apple Crash Reporter -// (ReportCrash). When forking and execing a new process, the child will -// inherit the parent's exception ports, which may be set to the Breakpad -// instance running inside the parent. The parent's Breakpad instance should -// not handle the child's exceptions. Calling RestoreDefaultExceptionHandler -// in the child after forking will restore the standard exception handler. -// See http://crbug.com/20371/ for more details. -void RestoreDefaultExceptionHandler(); - -// Look up the bootstrap server named |replacement_bootstrap_name| via the -// current |bootstrap_port|. Then replace the task's bootstrap port with the -// received right. -void ReplaceBootstrapPort(const std::string& replacement_bootstrap_name); -#endif // defined(OS_MACOSX) - -// Creates a LaunchOptions object suitable for launching processes in a test -// binary. This should not be called in production/released code. -BASE_EXPORT LaunchOptions LaunchOptionsForTest(); - -} // namespace butil - -#endif // BASE_PROCESS_LAUNCH_H_ diff --git a/src/butil/process/launch_mac.cc b/src/butil/process/launch_mac.cc deleted file mode 100644 index 1d0b56c0f1..0000000000 --- a/src/butil/process/launch_mac.cc +++ /dev/null @@ -1,48 +0,0 @@ -// 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. - -#include "butil/process/launch.h" - -#include -#include - -#include "butil/logging.h" - -namespace butil { - -void RestoreDefaultExceptionHandler() { - // This function is tailored to remove the Breakpad exception handler. - // exception_mask matches s_exception_mask in - // breakpad/src/client/mac/handler/exception_handler.cc - const exception_mask_t exception_mask = EXC_MASK_BAD_ACCESS | - EXC_MASK_BAD_INSTRUCTION | - EXC_MASK_ARITHMETIC | - EXC_MASK_BREAKPOINT; - - // Setting the exception port to MACH_PORT_NULL may not be entirely - // kosher to restore the default exception handler, but in practice, - // it results in the exception port being set to Apple Crash Reporter, - // the desired behavior. - task_set_exception_ports(mach_task_self(), exception_mask, MACH_PORT_NULL, - EXCEPTION_DEFAULT, THREAD_STATE_NONE); -} - -void ReplaceBootstrapPort(const std::string& new_bootstrap_name) { - // This function is called between fork() and exec(), so it should take care - // to run properly in that situation. - - mach_port_t port = MACH_PORT_NULL; - kern_return_t kr = bootstrap_look_up(bootstrap_port, - new_bootstrap_name.c_str(), &port); - if (kr != KERN_SUCCESS) { - RAW_LOG(FATAL, "Failed to look up replacement bootstrap port."); - } - - kr = task_set_bootstrap_port(mach_task_self(), port); - if (kr != KERN_SUCCESS) { - RAW_LOG(FATAL, "Failed to replace bootstrap port."); - } -} - -} // namespace butil diff --git a/src/butil/process/launch_posix.cc b/src/butil/process/launch_posix.cc deleted file mode 100644 index bed2ddb313..0000000000 --- a/src/butil/process/launch_posix.cc +++ /dev/null @@ -1,665 +0,0 @@ -// 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. - -#include "butil/process/launch.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "butil/allocator/type_profiler_control.h" -#include "butil/command_line.h" -#include "butil/compiler_specific.h" -#include "butil/debug/debugger.h" -#include "butil/debug/stack_trace.h" -#include "butil/file_util.h" -#include "butil/files/dir_reader_posix.h" -#include "butil/files/scoped_file.h" -#include "butil/logging.h" -#include "butil/memory/scoped_ptr.h" -#include "butil/posix/eintr_wrapper.h" -#include "butil/process/kill.h" -#include "butil/process/process_metrics.h" -#include "butil/strings/stringprintf.h" -#include "butil/synchronization/waitable_event.h" -#include "butil/third_party/dynamic_annotations/dynamic_annotations.h" -#include "butil/threading/platform_thread.h" -#include "butil/threading/thread_restrictions.h" - -#if defined(OS_LINUX) -#include -#endif - -#if defined(OS_CHROMEOS) -#include -#endif - -#if defined(OS_FREEBSD) -#include -#include -#endif - -#if defined(OS_MACOSX) -#include -#include -#else -extern char** environ; -#endif - -namespace butil { - -namespace { - -// Get the process's "environment" (i.e. the thing that setenv/getenv -// work with). -char** GetEnvironment() { -#if defined(OS_MACOSX) - return *_NSGetEnviron(); -#else - return environ; -#endif -} - -// Set the process's "environment" (i.e. the thing that setenv/getenv -// work with). -void SetEnvironment(char** env) { -#if defined(OS_MACOSX) - *_NSGetEnviron() = env; -#else - environ = env; -#endif -} - -// Set the calling thread's signal mask to new_sigmask and return -// the previous signal mask. -sigset_t SetSignalMask(const sigset_t& new_sigmask) { - sigset_t old_sigmask; -#if defined(OS_ANDROID) - // POSIX says pthread_sigmask() must be used in multi-threaded processes, - // but Android's pthread_sigmask() was broken until 4.1: - // https://code.google.com/p/android/issues/detail?id=15337 - // http://stackoverflow.com/questions/13777109/pthread-sigmask-on-android-not-working - RAW_CHECK(sigprocmask(SIG_SETMASK, &new_sigmask, &old_sigmask) == 0, ""); -#else - RAW_CHECK(pthread_sigmask(SIG_SETMASK, &new_sigmask, &old_sigmask) == 0, ""); -#endif - return old_sigmask; -} - -#if !defined(OS_LINUX) || \ - (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__)) -void ResetChildSignalHandlersToDefaults() { - // The previous signal handlers are likely to be meaningless in the child's - // context so we reset them to the defaults for now. http://crbug.com/44953 - // These signal handlers are set up at least in browser_main_posix.cc: - // BrowserMainPartsPosix::PreEarlyInitialization and stack_trace_posix.cc: - // EnableInProcessStackDumping. - signal(SIGHUP, SIG_DFL); - signal(SIGINT, SIG_DFL); - signal(SIGILL, SIG_DFL); - signal(SIGABRT, SIG_DFL); - signal(SIGFPE, SIG_DFL); - signal(SIGBUS, SIG_DFL); - signal(SIGSEGV, SIG_DFL); - signal(SIGSYS, SIG_DFL); - signal(SIGTERM, SIG_DFL); -} - -#else - -// TODO(jln): remove the Linux special case once kernels are fixed. - -// Internally the kernel makes sigset_t an array of long large enough to have -// one bit per signal. -typedef uint64_t kernel_sigset_t; - -// This is what struct sigaction looks like to the kernel at least on X86 and -// ARM. MIPS, for instance, is very different. -struct kernel_sigaction { - void* k_sa_handler; // For this usage it only needs to be a generic pointer. - unsigned long k_sa_flags; - void* k_sa_restorer; // For this usage it only needs to be a generic pointer. - kernel_sigset_t k_sa_mask; -}; - -// glibc's sigaction() will prevent access to sa_restorer, so we need to roll -// our own. -int sys_rt_sigaction(int sig, const struct kernel_sigaction* act, - struct kernel_sigaction* oact) { - return syscall(SYS_rt_sigaction, sig, act, oact, sizeof(kernel_sigset_t)); -} - -// This function is intended to be used in between fork() and execve() and will -// reset all signal handlers to the default. -// The motivation for going through all of them is that sa_restorer can leak -// from parents and help defeat ASLR on buggy kernels. We reset it to NULL. -// See crbug.com/177956. -void ResetChildSignalHandlersToDefaults(void) { - for (int signum = 1; ; ++signum) { - struct kernel_sigaction act; - memset(&act, 0, sizeof(act)); - int sigaction_get_ret = sys_rt_sigaction(signum, NULL, &act); - if (sigaction_get_ret && errno == EINVAL) { -#if !defined(NDEBUG) - // Linux supports 32 real-time signals from 33 to 64. - // If the number of signals in the Linux kernel changes, someone should - // look at this code. - const int kNumberOfSignals = 64; - RAW_CHECK(signum == kNumberOfSignals + 1, ""); -#endif // !defined(NDEBUG) - break; - } - // All other failures are fatal. - if (sigaction_get_ret) { - RAW_LOG(FATAL, "sigaction (get) failed."); - } - - // The kernel won't allow to re-set SIGKILL or SIGSTOP. - if (signum != SIGSTOP && signum != SIGKILL) { - act.k_sa_handler = (void*)SIG_DFL; - act.k_sa_restorer = NULL; - if (sys_rt_sigaction(signum, &act, NULL)) { - RAW_LOG(FATAL, "sigaction (set) failed."); - } - } -#if !defined(NDEBUG) - // Now ask the kernel again and check that no restorer will leak. - if (sys_rt_sigaction(signum, NULL, &act) || act.k_sa_restorer) { - RAW_LOG(FATAL, "Cound not fix sa_restorer."); - } -#endif // !defined(NDEBUG) - } -} -#endif // !defined(OS_LINUX) || - // (!defined(__i386__) && !defined(__x86_64__) && !defined(__arm__)) - -} // anonymous namespace - -// Functor for |ScopedDIR| (below). -struct ScopedDIRClose { - inline void operator()(DIR* x) const { - if (x) - closedir(x); - } -}; - -// Automatically closes |DIR*|s. -typedef scoped_ptr ScopedDIR; - -#if defined(OS_LINUX) -static const char kFDDir[] = "/proc/self/fd"; -#elif defined(OS_MACOSX) -static const char kFDDir[] = "/dev/fd"; -#elif defined(OS_SOLARIS) -static const char kFDDir[] = "/dev/fd"; -#elif defined(OS_FREEBSD) -static const char kFDDir[] = "/dev/fd"; -#elif defined(OS_OPENBSD) -static const char kFDDir[] = "/dev/fd"; -#elif defined(OS_ANDROID) -static const char kFDDir[] = "/proc/self/fd"; -#endif - -void CloseSuperfluousFds(const butil::InjectiveMultimap& saved_mapping) { - // DANGER: no calls to malloc or locks are allowed from now on: - // http://crbug.com/36678 - - // Get the maximum number of FDs possible. - size_t max_fds = GetMaxFds(); - - DirReaderPosix fd_dir(kFDDir); - if (!fd_dir.IsValid()) { - // Fallback case: Try every possible fd. - for (size_t i = 0; i < max_fds; ++i) { - const int fd = static_cast(i); - if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) - continue; - // Cannot use STL iterators here, since debug iterators use locks. - size_t j; - for (j = 0; j < saved_mapping.size(); j++) { - if (fd == saved_mapping[j].dest) - break; - } - if (j < saved_mapping.size()) - continue; - - // Since we're just trying to close anything we can find, - // ignore any error return values of close(). - close(fd); - } - return; - } - - const int dir_fd = fd_dir.fd(); - - for ( ; fd_dir.Next(); ) { - // Skip . and .. entries. - if (fd_dir.name()[0] == '.') - continue; - - char *endptr; - errno = 0; - const long int fd = strtol(fd_dir.name(), &endptr, 10); - if (fd_dir.name()[0] == 0 || *endptr || fd < 0 || errno) - continue; - if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO) - continue; - // Cannot use STL iterators here, since debug iterators use locks. - size_t i; - for (i = 0; i < saved_mapping.size(); i++) { - if (fd == saved_mapping[i].dest) - break; - } - if (i < saved_mapping.size()) - continue; - if (fd == dir_fd) - continue; - - // When running under Valgrind, Valgrind opens several FDs for its - // own use and will complain if we try to close them. All of - // these FDs are >= |max_fds|, so we can check against that here - // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758 - if (fd < static_cast(max_fds)) { - int ret = IGNORE_EINTR(close(fd)); - DPCHECK(ret == 0); - } - } -} - -bool LaunchProcess(const std::vector& argv, - const LaunchOptions& options, - ProcessHandle* process_handle) { - size_t fd_shuffle_size = 0; - if (options.fds_to_remap) { - fd_shuffle_size = options.fds_to_remap->size(); - } - - InjectiveMultimap fd_shuffle1; - InjectiveMultimap fd_shuffle2; - fd_shuffle1.reserve(fd_shuffle_size); - fd_shuffle2.reserve(fd_shuffle_size); - - scoped_ptr argv_cstr(new char*[argv.size() + 1]); - scoped_ptr new_environ; - char* const empty_environ = NULL; - char* const* old_environ = GetEnvironment(); - if (options.clear_environ) - old_environ = &empty_environ; - if (!options.environ.empty()) - new_environ = AlterEnvironment(old_environ, options.environ); - - sigset_t full_sigset; - sigfillset(&full_sigset); - const sigset_t orig_sigmask = SetSignalMask(full_sigset); - - pid_t pid; -#if defined(OS_LINUX) - if (options.clone_flags) { - // Signal handling in this function assumes the creation of a new - // process, so we check that a thread is not being created by mistake - // and that signal handling follows the process-creation rules. - RAW_CHECK( - !(options.clone_flags & (CLONE_SIGHAND | CLONE_THREAD | CLONE_VM)), ""); - pid = syscall(__NR_clone, options.clone_flags, 0, 0, 0); - } else -#endif - { - pid = fork(); - } - - // Always restore the original signal mask in the parent. - if (pid != 0) { - SetSignalMask(orig_sigmask); - } - - if (pid < 0) { - DPLOG(ERROR) << "fork"; - return false; - } else if (pid == 0) { - // Child process - - // DANGER: no calls to malloc or locks are allowed from now on: - // http://crbug.com/36678 - - // DANGER: fork() rule: in the child, if you don't end up doing exec*(), - // you call _exit() instead of exit(). This is because _exit() does not - // call any previously-registered (in the parent) exit handlers, which - // might do things like block waiting for threads that don't even exist - // in the child. - - // If a child process uses the readline library, the process block forever. - // In BSD like OSes including OS X it is safe to assign /dev/null as stdin. - // See http://crbug.com/56596. - butil::ScopedFD null_fd(HANDLE_EINTR(open("/dev/null", O_RDONLY))); - if (!null_fd.is_valid()) { - RAW_LOG(ERROR, "Failed to open /dev/null"); - _exit(127); - } - - int new_fd = HANDLE_EINTR(dup2(null_fd.get(), STDIN_FILENO)); - if (new_fd != STDIN_FILENO) { - RAW_LOG(ERROR, "Failed to dup /dev/null for stdin"); - _exit(127); - } - - if (options.new_process_group) { - // Instead of inheriting the process group ID of the parent, the child - // starts off a new process group with pgid equal to its process ID. - if (setpgid(0, 0) < 0) { - RAW_LOG(ERROR, "setpgid failed"); - _exit(127); - } - } - - // Stop type-profiler. - // The profiler should be stopped between fork and exec since it inserts - // locks at new/delete expressions. See http://crbug.com/36678. - butil::type_profiler::Controller::Stop(); - - if (options.maximize_rlimits) { - // Some resource limits need to be maximal in this child. - for (size_t i = 0; i < options.maximize_rlimits->size(); ++i) { - const int resource = (*options.maximize_rlimits)[i]; - struct rlimit limit; - if (getrlimit(resource, &limit) < 0) { - RAW_LOG(WARNING, "getrlimit failed"); - } else if (limit.rlim_cur < limit.rlim_max) { - limit.rlim_cur = limit.rlim_max; - if (setrlimit(resource, &limit) < 0) { - RAW_LOG(WARNING, "setrlimit failed"); - } - } - } - } - -#if defined(OS_MACOSX) - RestoreDefaultExceptionHandler(); - if (!options.replacement_bootstrap_name.empty()) - ReplaceBootstrapPort(options.replacement_bootstrap_name); -#endif // defined(OS_MACOSX) - - ResetChildSignalHandlersToDefaults(); - SetSignalMask(orig_sigmask); - -#if 0 - // When debugging it can be helpful to check that we really aren't making - // any hidden calls to malloc. - void *malloc_thunk = - reinterpret_cast(reinterpret_cast(malloc) & ~4095); - mprotect(malloc_thunk, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); - memset(reinterpret_cast(malloc), 0xff, 8); -#endif // 0 - -#if defined(OS_CHROMEOS) - if (options.ctrl_terminal_fd >= 0) { - // Set process' controlling terminal. - if (HANDLE_EINTR(setsid()) != -1) { - if (HANDLE_EINTR( - ioctl(options.ctrl_terminal_fd, TIOCSCTTY, NULL)) == -1) { - RAW_LOG(WARNING, "ioctl(TIOCSCTTY), ctrl terminal not set"); - } - } else { - RAW_LOG(WARNING, "setsid failed, ctrl terminal not set"); - } - } -#endif // defined(OS_CHROMEOS) - - if (options.fds_to_remap) { - // Cannot use STL iterators here, since debug iterators use locks. - for (size_t i = 0; i < options.fds_to_remap->size(); ++i) { - const FileHandleMappingVector::value_type& value = - (*options.fds_to_remap)[i]; - fd_shuffle1.push_back(InjectionArc(value.first, value.second, false)); - fd_shuffle2.push_back(InjectionArc(value.first, value.second, false)); - } - } - - if (!options.environ.empty() || options.clear_environ) - SetEnvironment(new_environ.get()); - - // fd_shuffle1 is mutated by this call because it cannot malloc. - if (!ShuffleFileDescriptors(&fd_shuffle1)) - _exit(127); - - CloseSuperfluousFds(fd_shuffle2); - - // Set NO_NEW_PRIVS by default. Since NO_NEW_PRIVS only exists in kernel - // 3.5+, do not check the return value of prctl here. -#if defined(OS_LINUX) -#ifndef PR_SET_NO_NEW_PRIVS -#define PR_SET_NO_NEW_PRIVS 38 -#endif - if (!options.allow_new_privs) { - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) && errno != EINVAL) { - // Only log if the error is not EINVAL (i.e. not supported). - RAW_LOG(FATAL, "prctl(PR_SET_NO_NEW_PRIVS) failed"); - } - } -#endif - - for (size_t i = 0; i < argv.size(); i++) - argv_cstr[i] = const_cast(argv[i].c_str()); - argv_cstr[argv.size()] = NULL; - execvp(argv_cstr[0], argv_cstr.get()); - - RAW_LOG(ERROR, "LaunchProcess: failed to execvp:"); - RAW_LOG(ERROR, argv_cstr[0]); - _exit(127); - } else { - // Parent process - if (options.wait) { - // While this isn't strictly disk IO, waiting for another process to - // finish is the sort of thing ThreadRestrictions is trying to prevent. - butil::ThreadRestrictions::AssertIOAllowed(); - pid_t ret = HANDLE_EINTR(waitpid(pid, 0, 0)); - DPCHECK(ret > 0); - } - - if (process_handle) - *process_handle = pid; - } - - return true; -} - - -bool LaunchProcess(const CommandLine& cmdline, - const LaunchOptions& options, - ProcessHandle* process_handle) { - return LaunchProcess(cmdline.argv(), options, process_handle); -} - -void RaiseProcessToHighPriority() { - // On POSIX, we don't actually do anything here. We could try to nice() or - // setpriority() or sched_getscheduler, but these all require extra rights. -} - -// Return value used by GetAppOutputInternal to encapsulate the various exit -// scenarios from the function. -enum GetAppOutputInternalResult { - EXECUTE_FAILURE, - EXECUTE_SUCCESS, - GOT_MAX_OUTPUT, -}; - -// Executes the application specified by |argv| and wait for it to exit. Stores -// the output (stdout) in |output|. If |do_search_path| is set, it searches the -// path for the application; in that case, |envp| must be null, and it will use -// the current environment. If |do_search_path| is false, |argv[0]| should fully -// specify the path of the application, and |envp| will be used as the -// environment. Redirects stderr to /dev/null. -// If we successfully start the application and get all requested output, we -// return GOT_MAX_OUTPUT, or if there is a problem starting or exiting -// the application we return RUN_FAILURE. Otherwise we return EXECUTE_SUCCESS. -// The GOT_MAX_OUTPUT return value exists so a caller that asks for limited -// output can treat this as a success, despite having an exit code of SIG_PIPE -// due to us closing the output pipe. -// In the case of EXECUTE_SUCCESS, the application exit code will be returned -// in |*exit_code|, which should be checked to determine if the application -// ran successfully. -static GetAppOutputInternalResult GetAppOutputInternal( - const std::vector& argv, - char* const envp[], - std::string* output, - size_t max_output, - bool do_search_path, - int* exit_code) { - // Doing a blocking wait for another command to finish counts as IO. - butil::ThreadRestrictions::AssertIOAllowed(); - // exit_code must be supplied so calling function can determine success. - DCHECK(exit_code); - *exit_code = EXIT_FAILURE; - - int pipe_fd[2]; - pid_t pid; - InjectiveMultimap fd_shuffle1, fd_shuffle2; - scoped_ptr argv_cstr(new char*[argv.size() + 1]); - - fd_shuffle1.reserve(3); - fd_shuffle2.reserve(3); - - // Either |do_search_path| should be false or |envp| should be null, but not - // both. - DCHECK(!do_search_path ^ !envp); - - if (pipe(pipe_fd) < 0) - return EXECUTE_FAILURE; - - switch (pid = fork()) { - case -1: // error - close(pipe_fd[0]); - close(pipe_fd[1]); - return EXECUTE_FAILURE; - case 0: // child - { - // DANGER: no calls to malloc or locks are allowed from now on: - // http://crbug.com/36678 - -#if defined(OS_MACOSX) - RestoreDefaultExceptionHandler(); -#endif - - // Obscure fork() rule: in the child, if you don't end up doing exec*(), - // you call _exit() instead of exit(). This is because _exit() does not - // call any previously-registered (in the parent) exit handlers, which - // might do things like block waiting for threads that don't even exist - // in the child. - int dev_null = open("/dev/null", O_WRONLY); - if (dev_null < 0) - _exit(127); - - // Stop type-profiler. - // The profiler should be stopped between fork and exec since it inserts - // locks at new/delete expressions. See http://crbug.com/36678. - butil::type_profiler::Controller::Stop(); - - fd_shuffle1.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true)); - fd_shuffle1.push_back(InjectionArc(dev_null, STDERR_FILENO, true)); - fd_shuffle1.push_back(InjectionArc(dev_null, STDIN_FILENO, true)); - // Adding another element here? Remeber to increase the argument to - // reserve(), above. - - for (size_t i = 0; i < fd_shuffle1.size(); ++i) - fd_shuffle2.push_back(fd_shuffle1[i]); - - if (!ShuffleFileDescriptors(&fd_shuffle1)) - _exit(127); - - CloseSuperfluousFds(fd_shuffle2); - - for (size_t i = 0; i < argv.size(); i++) - argv_cstr[i] = const_cast(argv[i].c_str()); - argv_cstr[argv.size()] = NULL; - if (do_search_path) - execvp(argv_cstr[0], argv_cstr.get()); - else - execve(argv_cstr[0], argv_cstr.get(), envp); - _exit(127); - } - default: // parent - { - // Close our writing end of pipe now. Otherwise later read would not - // be able to detect end of child's output (in theory we could still - // write to the pipe). - close(pipe_fd[1]); - - output->clear(); - char buffer[256]; - size_t output_buf_left = max_output; - ssize_t bytes_read = 1; // A lie to properly handle |max_output == 0| - // case in the logic below. - - while (output_buf_left > 0) { - bytes_read = HANDLE_EINTR(read(pipe_fd[0], buffer, - std::min(output_buf_left, sizeof(buffer)))); - if (bytes_read <= 0) - break; - output->append(buffer, bytes_read); - output_buf_left -= static_cast(bytes_read); - } - close(pipe_fd[0]); - - // Always wait for exit code (even if we know we'll declare - // GOT_MAX_OUTPUT). - bool success = WaitForExitCode(pid, exit_code); - - // If we stopped because we read as much as we wanted, we return - // GOT_MAX_OUTPUT (because the child may exit due to |SIGPIPE|). - if (!output_buf_left && bytes_read > 0) - return GOT_MAX_OUTPUT; - else if (success) - return EXECUTE_SUCCESS; - return EXECUTE_FAILURE; - } - } -} - -bool GetAppOutput(const CommandLine& cl, std::string* output) { - return GetAppOutput(cl.argv(), output); -} - -bool GetAppOutput(const std::vector& argv, std::string* output) { - // Run |execve()| with the current environment and store "unlimited" data. - int exit_code; - GetAppOutputInternalResult result = GetAppOutputInternal( - argv, NULL, output, std::numeric_limits::max(), true, - &exit_code); - return result == EXECUTE_SUCCESS && exit_code == EXIT_SUCCESS; -} - -// TODO(viettrungluu): Conceivably, we should have a timeout as well, so we -// don't hang if what we're calling hangs. -bool GetAppOutputRestricted(const CommandLine& cl, - std::string* output, size_t max_output) { - // Run |execve()| with the empty environment. - char* const empty_environ = NULL; - int exit_code; - GetAppOutputInternalResult result = GetAppOutputInternal( - cl.argv(), &empty_environ, output, max_output, false, &exit_code); - return result == GOT_MAX_OUTPUT || (result == EXECUTE_SUCCESS && - exit_code == EXIT_SUCCESS); -} - -bool GetAppOutputWithExitCode(const CommandLine& cl, - std::string* output, - int* exit_code) { - // Run |execve()| with the current environment and store "unlimited" data. - GetAppOutputInternalResult result = GetAppOutputInternal( - cl.argv(), NULL, output, std::numeric_limits::max(), true, - exit_code); - return result == EXECUTE_SUCCESS; -} - -} // namespace butil diff --git a/src/butil/process/memory.cc b/src/butil/process/memory.cc deleted file mode 100644 index dbae72ba15..0000000000 --- a/src/butil/process/memory.cc +++ /dev/null @@ -1,30 +0,0 @@ -// 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. - -#include "butil/process/memory.h" - -namespace butil { - -// Defined in memory_mac.mm for Mac. -#if !defined(OS_MACOSX) - -bool UncheckedCalloc(size_t num_items, size_t size, void** result) { - const size_t alloc_size = num_items * size; - - // Overflow check - if (size && ((alloc_size / size) != num_items)) { - *result = NULL; - return false; - } - - if (!UncheckedMalloc(alloc_size, result)) - return false; - - memset(*result, 0, alloc_size); - return true; -} - -#endif - -} diff --git a/src/butil/process/memory.h b/src/butil/process/memory.h deleted file mode 100644 index eb95bd059c..0000000000 --- a/src/butil/process/memory.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2013 The Chromium 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_MEMORY_H_ -#define BASE_PROCESS_MEMORY_H_ - -#include "butil/base_export.h" -#include "butil/basictypes.h" -#include "butil/process/process_handle.h" -#include "butil/build_config.h" - -#if defined(OS_WIN) -#include -#endif - -#ifdef PVALLOC_AVAILABLE -// Build config explicitly tells us whether or not pvalloc is available. -#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC) -#define PVALLOC_AVAILABLE 1 -#else -#define PVALLOC_AVAILABLE 0 -#endif - -namespace butil { - -// Enables low fragmentation heap (LFH) for every heaps of this process. This -// won't have any effect on heaps created after this function call. It will not -// modify data allocated in the heaps before calling this function. So it is -// better to call this function early in initialization and again before -// entering the main loop. -// Note: Returns true on Windows 2000 without doing anything. -BASE_EXPORT bool EnableLowFragmentationHeap(); - -// Enables 'terminate on heap corruption' flag. Helps protect against heap -// overflow. Has no effect if the OS doesn't provide the necessary facility. -BASE_EXPORT void EnableTerminationOnHeapCorruption(); - -// Turns on process termination if memory runs out. -BASE_EXPORT void EnableTerminationOnOutOfMemory(); - -#if defined(OS_WIN) -// Returns the module handle to which an address belongs. The reference count -// of the module is not incremented. -BASE_EXPORT HMODULE GetModuleFromAddress(void* address); -#endif - -#if defined(OS_LINUX) || defined(OS_ANDROID) -BASE_EXPORT extern size_t g_oom_size; - -// The maximum allowed value for the OOM score. -const int kMaxOomScore = 1000; - -// This adjusts /proc//oom_score_adj so the Linux OOM killer will -// prefer to kill certain process types over others. The range for the -// adjustment is [-1000, 1000], with [0, 1000] being user accessible. -// If the Linux system doesn't support the newer oom_score_adj range -// of [0, 1000], then we revert to using the older oom_adj, and -// translate the given value into [0, 15]. Some aliasing of values -// may occur in that case, of course. -BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score); -#endif - -// Special allocator functions for callers that want to check for OOM. -// These will not abort if the allocation fails even if -// EnableTerminationOnOutOfMemory has been called. -// This can be useful for huge and/or unpredictable size memory allocations. -// Please only use this if you really handle the case when the allocation -// fails. Doing otherwise would risk security. -// These functions may still crash on OOM when running under memory tools, -// specifically ASan and other sanitizers. -// Return value tells whether the allocation succeeded. If it fails |result| is -// set to NULL, otherwise it holds the memory address. -BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size, - void** result); -BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items, - size_t size, - void** result); - -} // namespace butil - -#endif // BASE_PROCESS_MEMORY_H_ diff --git a/src/butil/process/memory_linux.cc b/src/butil/process/memory_linux.cc deleted file mode 100644 index ddc12dd65f..0000000000 --- a/src/butil/process/memory_linux.cc +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/memory.h" - -#include - -#include "butil/file_util.h" -#include "butil/files/file_path.h" -#include "butil/logging.h" -#include "butil/process/internal_linux.h" -#include "butil/strings/string_number_conversions.h" - -#if defined(USE_TCMALLOC) -// Used by UncheckedMalloc. If tcmalloc is linked to the executable -// this will be replaced by a strong symbol that actually implement -// the semantics and don't call new handler in case the allocation fails. -extern "C" { - -__attribute__((weak, visibility("default"))) -void* tc_malloc_skip_new_handler_weak(size_t size); - -void* tc_malloc_skip_new_handler_weak(size_t size) { - return malloc(size); -} - -} -#endif - -namespace butil { - -size_t g_oom_size = 0U; - -namespace { - -#if !defined(OS_ANDROID) -void OnNoMemorySize(size_t size) { - g_oom_size = size; - - if (size != 0) - LOG(FATAL) << "Out of memory, size = " << size; - LOG(FATAL) << "Out of memory."; -} - -void OnNoMemory() { - OnNoMemorySize(0); -} -#endif // !defined(OS_ANDROID) - -} // namespace - -#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) && \ - !defined(THREAD_SANITIZER) && !defined(LEAK_SANITIZER) - -#if defined(LIBC_GLIBC) && !defined(USE_TCMALLOC) - -extern "C" { -void* __libc_malloc(size_t size); -void* __libc_realloc(void* ptr, size_t size); -void* __libc_calloc(size_t nmemb, size_t size); -void* __libc_valloc(size_t size); -#if PVALLOC_AVAILABLE == 1 -void* __libc_pvalloc(size_t size); -#endif -void* __libc_memalign(size_t alignment, size_t size); - -// Overriding the system memory allocation functions: -// -// For security reasons, we want malloc failures to be fatal. Too much code -// doesn't check for a NULL return value from malloc and unconditionally uses -// the resulting pointer. If the first offset that they try to access is -// attacker controlled, then the attacker can direct the code to access any -// part of memory. -// -// Thus, we define all the standard malloc functions here and mark them as -// visibility 'default'. This means that they replace the malloc functions for -// all Chromium code and also for all code in shared libraries. There are tests -// for this in process_util_unittest.cc. -// -// If we are using tcmalloc, then the problem is moot since tcmalloc handles -// this for us. Thus this code is in a !defined(USE_TCMALLOC) block. -// -// If we are testing the binary with AddressSanitizer, we should not -// redefine malloc and let AddressSanitizer do it instead. -// -// We call the real libc functions in this code by using __libc_malloc etc. -// Previously we tried using dlsym(RTLD_NEXT, ...) but that failed depending on -// the link order. Since ld.so needs calloc during symbol resolution, it -// defines its own versions of several of these functions in dl-minimal.c. -// Depending on the runtime library order, dlsym ended up giving us those -// functions and bad things happened. See crbug.com/31809 -// -// This means that any code which calls __libc_* gets the raw libc versions of -// these functions. - -#define DIE_ON_OOM_1(function_name) \ - void* function_name(size_t) __attribute__ ((visibility("default"))); \ - \ - void* function_name(size_t size) { \ - void* ret = __libc_##function_name(size); \ - if (ret == NULL && size != 0) \ - OnNoMemorySize(size); \ - return ret; \ - } - -#define DIE_ON_OOM_2(function_name, arg1_type) \ - void* function_name(arg1_type, size_t) \ - __attribute__ ((visibility("default"))); \ - \ - void* function_name(arg1_type arg1, size_t size) { \ - void* ret = __libc_##function_name(arg1, size); \ - if (ret == NULL && size != 0) \ - OnNoMemorySize(size); \ - return ret; \ - } - -DIE_ON_OOM_1(malloc) -DIE_ON_OOM_1(valloc) -#if PVALLOC_AVAILABLE == 1 -DIE_ON_OOM_1(pvalloc) -#endif - -DIE_ON_OOM_2(calloc, size_t) -DIE_ON_OOM_2(realloc, void*) -DIE_ON_OOM_2(memalign, size_t) - -// posix_memalign has a unique signature and doesn't have a __libc_ variant. -int posix_memalign(void** ptr, size_t alignment, size_t size) - __attribute__ ((visibility("default"))); - -int posix_memalign(void** ptr, size_t alignment, size_t size) { - // This will use the safe version of memalign, above. - *ptr = memalign(alignment, size); - return 0; -} - -} // extern C - -#else - -// TODO(mostynb@opera.com): dlsym dance - -#endif // LIBC_GLIBC && !USE_TCMALLOC - -#endif // !*_SANITIZER - -void EnableTerminationOnHeapCorruption() { - // On Linux, there nothing to do AFAIK. -} - -void EnableTerminationOnOutOfMemory() { -#if defined(OS_ANDROID) - // Android doesn't support setting a new handler. - DLOG(WARNING) << "Not feasible."; -#else - // Set the new-out of memory handler. - std::set_new_handler(&OnNoMemory); - // If we're using glibc's allocator, the above functions will override - // malloc and friends and make them die on out of memory. -#endif -} - -// NOTE: This is not the only version of this function in the source: -// the setuid sandbox (in process_util_linux.c, in the sandbox source) -// also has its own C version. -bool AdjustOOMScore(ProcessId process, int score) { - if (score < 0 || score > kMaxOomScore) - return false; - - FilePath oom_path(internal::GetProcPidDir(process)); - - // Attempt to write the newer oom_score_adj file first. - FilePath oom_file = oom_path.AppendASCII("oom_score_adj"); - if (PathExists(oom_file)) { - std::string score_str = IntToString(score); - DVLOG(1) << "Adjusting oom_score_adj of " << process << " to " - << score_str; - int score_len = static_cast(score_str.length()); - return (score_len == WriteFile(oom_file, score_str.c_str(), score_len)); - } - - // If the oom_score_adj file doesn't exist, then we write the old - // style file and translate the oom_adj score to the range 0-15. - oom_file = oom_path.AppendASCII("oom_adj"); - if (PathExists(oom_file)) { - // Max score for the old oom_adj range. Used for conversion of new - // values to old values. - const int kMaxOldOomScore = 15; - - int converted_score = score * kMaxOldOomScore / kMaxOomScore; - std::string score_str = IntToString(converted_score); - DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str; - int score_len = static_cast(score_str.length()); - return (score_len == WriteFile(oom_file, score_str.c_str(), score_len)); - } - - return false; -} - -bool UncheckedMalloc(size_t size, void** result) { -#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) || \ - (!defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)) - *result = malloc(size); -#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC) - *result = __libc_malloc(size); -#elif defined(USE_TCMALLOC) - *result = tc_malloc_skip_new_handler_weak(size); -#endif - return *result != NULL; -} - -} // namespace butil diff --git a/src/butil/process/memory_mac.mm b/src/butil/process/memory_mac.mm deleted file mode 100644 index 575e886a17..0000000000 --- a/src/butil/process/memory_mac.mm +++ /dev/null @@ -1,751 +0,0 @@ -// Copyright (c) 2013 The Chromium 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/memory.h" - -#include -#include -#include -#include -#include -#import - -#include - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/mac/mac_util.h" -#include "base/mac/mach_logging.h" -#include "base/scoped_clear_errno.h" -#include "third_party/apple_apsl/CFBase.h" -#include "third_party/apple_apsl/malloc.h" - -#if ARCH_CPU_32_BITS -#include -#include - -#include "base/threading/thread_local.h" -#include "third_party/mach_override/mach_override.h" -#endif // ARCH_CPU_32_BITS - -namespace base { - -// These are helpers for EnableTerminationOnHeapCorruption, which is a no-op -// on 64 bit Macs. -#if ARCH_CPU_32_BITS -namespace { - -// Finds the library path for malloc() and thus the libC part of libSystem, -// which in Lion is in a separate image. -const char* LookUpLibCPath() { - const void* addr = reinterpret_cast(&malloc); - - Dl_info info; - if (dladdr(addr, &info)) - return info.dli_fname; - - DLOG(WARNING) << "Could not find image path for malloc()"; - return NULL; -} - -typedef void(*malloc_error_break_t)(void); -malloc_error_break_t g_original_malloc_error_break = NULL; - -// Returns the function pointer for malloc_error_break. This symbol is declared -// as __private_extern__ and cannot be dlsym()ed. Instead, use nlist() to -// get it. -malloc_error_break_t LookUpMallocErrorBreak() { - const char* lib_c_path = LookUpLibCPath(); - if (!lib_c_path) - return NULL; - - // Only need to look up two symbols, but nlist() requires a NULL-terminated - // array and takes no count. - struct nlist nl[3]; - bzero(&nl, sizeof(nl)); - - // The symbol to find. - nl[0].n_un.n_name = const_cast("_malloc_error_break"); - - // A reference symbol by which the address of the desired symbol will be - // calculated. - nl[1].n_un.n_name = const_cast("_malloc"); - - int rv = nlist(lib_c_path, nl); - if (rv != 0 || nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) { - return NULL; - } - - // nlist() returns addresses as offsets in the image, not the instruction - // pointer in memory. Use the known in-memory address of malloc() - // to compute the offset for malloc_error_break(). - uintptr_t reference_addr = reinterpret_cast(&malloc); - reference_addr -= nl[1].n_value; - reference_addr += nl[0].n_value; - - return reinterpret_cast(reference_addr); -} - -// Combines ThreadLocalBoolean with AutoReset. It would be convenient -// to compose ThreadLocalPointer with base::AutoReset, but that -// would require allocating some storage for the bool. -class ThreadLocalBooleanAutoReset { - public: - ThreadLocalBooleanAutoReset(ThreadLocalBoolean* tlb, bool new_value) - : scoped_tlb_(tlb), - original_value_(tlb->Get()) { - scoped_tlb_->Set(new_value); - } - ~ThreadLocalBooleanAutoReset() { - scoped_tlb_->Set(original_value_); - } - - private: - ThreadLocalBoolean* scoped_tlb_; - bool original_value_; - - DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanAutoReset); -}; - -base::LazyInstance::Leaky - g_unchecked_alloc = LAZY_INSTANCE_INITIALIZER; - -// NOTE(shess): This is called when the malloc library noticed that the heap -// is fubar. Avoid calls which will re-enter the malloc library. -void CrMallocErrorBreak() { - g_original_malloc_error_break(); - - // Out of memory is certainly not heap corruption, and not necessarily - // something for which the process should be terminated. Leave that decision - // to the OOM killer. - if (errno == ENOMEM) - return; - - // The malloc library attempts to log to ASL (syslog) before calling this - // code, which fails accessing a Unix-domain socket when sandboxed. The - // failed socket results in writing to a -1 fd, leaving EBADF in errno. If - // UncheckedMalloc() is on the stack, for large allocations (15k and up) only - // an OOM failure leads here. Smaller allocations could also arrive here due - // to freelist corruption, but there is no way to distinguish that from OOM at - // this point. - // - // NOTE(shess): I hypothesize that EPERM case in 10.9 is the same root cause - // as EBADF. Unfortunately, 10.9's opensource releases don't include malloc - // source code at this time. - // - if ((errno == EBADF || errno == EPERM) && g_unchecked_alloc.Get().Get()) - return; - - // A unit test checks this error message, so it needs to be in release builds. - char buf[1024] = - "Terminating process due to a potential for future heap corruption: " - "errno="; - char errnobuf[] = { - '0' + ((errno / 100) % 10), - '0' + ((errno / 10) % 10), - '0' + (errno % 10), - '\000' - }; - COMPILE_ASSERT(ELAST <= 999, errno_too_large_to_encode); - strlcat(buf, errnobuf, sizeof(buf)); - RAW_LOG(ERROR, buf); - - // Crash by writing to NULL+errno to allow analyzing errno from - // crash dump info (setting a breakpad key would re-enter the malloc - // library). Max documented errno in intro(2) is actually 102, but - // it really just needs to be "small" to stay on the right vm page. - const int kMaxErrno = 256; - char* volatile death_ptr = NULL; - death_ptr += std::min(errno, kMaxErrno); - *death_ptr = '!'; -} - -} // namespace -#endif // ARCH_CPU_32_BITS - -void EnableTerminationOnHeapCorruption() { -#if defined(ADDRESS_SANITIZER) || ARCH_CPU_64_BITS - // AddressSanitizer handles heap corruption, and on 64 bit Macs, the malloc - // system automatically abort()s on heap corruption. - return; -#else - // Only override once, otherwise CrMallocErrorBreak() will recurse - // to itself. - if (g_original_malloc_error_break) - return; - - malloc_error_break_t malloc_error_break = LookUpMallocErrorBreak(); - if (!malloc_error_break) { - DLOG(WARNING) << "Could not find malloc_error_break"; - return; - } - - mach_error_t err = mach_override_ptr( - (void*)malloc_error_break, - (void*)&CrMallocErrorBreak, - (void**)&g_original_malloc_error_break); - - if (err != err_none) - DLOG(WARNING) << "Could not override malloc_error_break; error = " << err; -#endif // defined(ADDRESS_SANITIZER) || ARCH_CPU_64_BITS -} - -// ------------------------------------------------------------------------ - -namespace { - -bool g_oom_killer_enabled; - -// Starting with Mac OS X 10.7, the zone allocators set up by the system are -// read-only, to prevent them from being overwritten in an attack. However, -// blindly unprotecting and reprotecting the zone allocators fails with -// GuardMalloc because GuardMalloc sets up its zone allocator using a block of -// memory in its bss. Explicit saving/restoring of the protection is required. -// -// This function takes a pointer to a malloc zone, de-protects it if necessary, -// and returns (in the out parameters) a region of memory (if any) to be -// re-protected when modifications are complete. This approach assumes that -// there is no contention for the protection of this memory. -void DeprotectMallocZone(ChromeMallocZone* default_zone, - mach_vm_address_t* reprotection_start, - mach_vm_size_t* reprotection_length, - vm_prot_t* reprotection_value) { - mach_port_t unused; - *reprotection_start = reinterpret_cast(default_zone); - struct vm_region_basic_info_64 info; - mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; - kern_return_t result = - mach_vm_region(mach_task_self(), - reprotection_start, - reprotection_length, - VM_REGION_BASIC_INFO_64, - reinterpret_cast(&info), - &count, - &unused); - MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_region"; - - // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but - // balance it with a deallocate in case this ever changes. See 10.9.2 - // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. - mach_port_deallocate(mach_task_self(), unused); - - // Does the region fully enclose the zone pointers? Possibly unwarranted - // simplification used: using the size of a full version 8 malloc zone rather - // than the actual smaller size if the passed-in zone is not version 8. - CHECK(*reprotection_start <= - reinterpret_cast(default_zone)); - mach_vm_size_t zone_offset = reinterpret_cast(default_zone) - - reinterpret_cast(*reprotection_start); - CHECK(zone_offset + sizeof(ChromeMallocZone) <= *reprotection_length); - - if (info.protection & VM_PROT_WRITE) { - // No change needed; the zone is already writable. - *reprotection_start = 0; - *reprotection_length = 0; - *reprotection_value = VM_PROT_NONE; - } else { - *reprotection_value = info.protection; - result = mach_vm_protect(mach_task_self(), - *reprotection_start, - *reprotection_length, - false, - info.protection | VM_PROT_WRITE); - MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; - } -} - -// === C malloc/calloc/valloc/realloc/posix_memalign === - -typedef void* (*malloc_type)(struct _malloc_zone_t* zone, - size_t size); -typedef void* (*calloc_type)(struct _malloc_zone_t* zone, - size_t num_items, - size_t size); -typedef void* (*valloc_type)(struct _malloc_zone_t* zone, - size_t size); -typedef void (*free_type)(struct _malloc_zone_t* zone, - void* ptr); -typedef void* (*realloc_type)(struct _malloc_zone_t* zone, - void* ptr, - size_t size); -typedef void* (*memalign_type)(struct _malloc_zone_t* zone, - size_t alignment, - size_t size); - -malloc_type g_old_malloc; -calloc_type g_old_calloc; -valloc_type g_old_valloc; -free_type g_old_free; -realloc_type g_old_realloc; -memalign_type g_old_memalign; - -malloc_type g_old_malloc_purgeable; -calloc_type g_old_calloc_purgeable; -valloc_type g_old_valloc_purgeable; -free_type g_old_free_purgeable; -realloc_type g_old_realloc_purgeable; -memalign_type g_old_memalign_purgeable; - -void* oom_killer_malloc(struct _malloc_zone_t* zone, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_malloc(zone, size); - if (!result && size) - debug::BreakDebugger(); - return result; -} - -void* oom_killer_calloc(struct _malloc_zone_t* zone, - size_t num_items, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_calloc(zone, num_items, size); - if (!result && num_items && size) - debug::BreakDebugger(); - return result; -} - -void* oom_killer_valloc(struct _malloc_zone_t* zone, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_valloc(zone, size); - if (!result && size) - debug::BreakDebugger(); - return result; -} - -void oom_killer_free(struct _malloc_zone_t* zone, - void* ptr) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - g_old_free(zone, ptr); -} - -void* oom_killer_realloc(struct _malloc_zone_t* zone, - void* ptr, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_realloc(zone, ptr, size); - if (!result && size) - debug::BreakDebugger(); - return result; -} - -void* oom_killer_memalign(struct _malloc_zone_t* zone, - size_t alignment, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_memalign(zone, alignment, size); - // Only die if posix_memalign would have returned ENOMEM, since there are - // other reasons why NULL might be returned (see - // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ). - if (!result && size && alignment >= sizeof(void*) - && (alignment & (alignment - 1)) == 0) { - debug::BreakDebugger(); - } - return result; -} - -void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_malloc_purgeable(zone, size); - if (!result && size) - debug::BreakDebugger(); - return result; -} - -void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone, - size_t num_items, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_calloc_purgeable(zone, num_items, size); - if (!result && num_items && size) - debug::BreakDebugger(); - return result; -} - -void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_valloc_purgeable(zone, size); - if (!result && size) - debug::BreakDebugger(); - return result; -} - -void oom_killer_free_purgeable(struct _malloc_zone_t* zone, - void* ptr) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - g_old_free_purgeable(zone, ptr); -} - -void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone, - void* ptr, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_realloc_purgeable(zone, ptr, size); - if (!result && size) - debug::BreakDebugger(); - return result; -} - -void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone, - size_t alignment, - size_t size) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; -#endif // ARCH_CPU_32_BITS - void* result = g_old_memalign_purgeable(zone, alignment, size); - // Only die if posix_memalign would have returned ENOMEM, since there are - // other reasons why NULL might be returned (see - // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ). - if (!result && size && alignment >= sizeof(void*) - && (alignment & (alignment - 1)) == 0) { - debug::BreakDebugger(); - } - return result; -} - -// === C++ operator new === - -void oom_killer_new() { - debug::BreakDebugger(); -} - -// === Core Foundation CFAllocators === - -bool CanGetContextForCFAllocator() { - return !base::mac::IsOSLaterThanYosemite_DontCallThis(); -} - -CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) { - if (base::mac::IsOSSnowLeopard()) { - ChromeCFAllocatorLeopards* our_allocator = - const_cast( - reinterpret_cast(allocator)); - return &our_allocator->_context; - } else if (base::mac::IsOSLion() || - base::mac::IsOSMountainLion() || - base::mac::IsOSMavericks() || - base::mac::IsOSYosemite()) { - ChromeCFAllocatorLions* our_allocator = - const_cast( - reinterpret_cast(allocator)); - return &our_allocator->_context; - } else { - return NULL; - } -} - -CFAllocatorAllocateCallBack g_old_cfallocator_system_default; -CFAllocatorAllocateCallBack g_old_cfallocator_malloc; -CFAllocatorAllocateCallBack g_old_cfallocator_malloc_zone; - -void* oom_killer_cfallocator_system_default(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_system_default(alloc_size, hint, info); - if (!result) - debug::BreakDebugger(); - return result; -} - -void* oom_killer_cfallocator_malloc(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_malloc(alloc_size, hint, info); - if (!result) - debug::BreakDebugger(); - return result; -} - -void* oom_killer_cfallocator_malloc_zone(CFIndex alloc_size, - CFOptionFlags hint, - void* info) { - void* result = g_old_cfallocator_malloc_zone(alloc_size, hint, info); - if (!result) - debug::BreakDebugger(); - return result; -} - -// === Cocoa NSObject allocation === - -typedef id (*allocWithZone_t)(id, SEL, NSZone*); -allocWithZone_t g_old_allocWithZone; - -id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone) -{ - id result = g_old_allocWithZone(self, _cmd, zone); - if (!result) - debug::BreakDebugger(); - return result; -} - -} // namespace - -bool UncheckedMalloc(size_t size, void** result) { - if (g_old_malloc) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; - ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); -#endif // ARCH_CPU_32_BITS - *result = g_old_malloc(malloc_default_zone(), size); - } else { - *result = malloc(size); - } - - return *result != NULL; -} - -bool UncheckedCalloc(size_t num_items, size_t size, void** result) { - if (g_old_calloc) { -#if ARCH_CPU_32_BITS - ScopedClearErrno clear_errno; - ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true); -#endif // ARCH_CPU_32_BITS - *result = g_old_calloc(malloc_default_zone(), num_items, size); - } else { - *result = calloc(num_items, size); - } - - return *result != NULL; -} - -void* UncheckedMalloc(size_t size) { - void* address; - return UncheckedMalloc(size, &address) ? address : NULL; -} - -void* UncheckedCalloc(size_t num_items, size_t size) { - void* address; - return UncheckedCalloc(num_items, size, &address) ? address : NULL; -} - -void EnableTerminationOnOutOfMemory() { - if (g_oom_killer_enabled) - return; - - g_oom_killer_enabled = true; - - // === C malloc/calloc/valloc/realloc/posix_memalign === - - // This approach is not perfect, as requests for amounts of memory larger than - // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will - // still fail with a NULL rather than dying (see - // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c for details). - // Unfortunately, it's the best we can do. Also note that this does not affect - // allocations from non-default zones. - - CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc && - !g_old_memalign) << "Old allocators unexpectedly non-null"; - - CHECK(!g_old_malloc_purgeable && !g_old_calloc_purgeable && - !g_old_valloc_purgeable && !g_old_realloc_purgeable && - !g_old_memalign_purgeable) << "Old allocators unexpectedly non-null"; - -#if !defined(ADDRESS_SANITIZER) - // Don't do anything special on OOM for the malloc zones replaced by - // AddressSanitizer, as modifying or protecting them may not work correctly. - - ChromeMallocZone* default_zone = - reinterpret_cast(malloc_default_zone()); - ChromeMallocZone* purgeable_zone = - reinterpret_cast(malloc_default_purgeable_zone()); - - mach_vm_address_t default_reprotection_start = 0; - mach_vm_size_t default_reprotection_length = 0; - vm_prot_t default_reprotection_value = VM_PROT_NONE; - DeprotectMallocZone(default_zone, - &default_reprotection_start, - &default_reprotection_length, - &default_reprotection_value); - - mach_vm_address_t purgeable_reprotection_start = 0; - mach_vm_size_t purgeable_reprotection_length = 0; - vm_prot_t purgeable_reprotection_value = VM_PROT_NONE; - if (purgeable_zone) { - DeprotectMallocZone(purgeable_zone, - &purgeable_reprotection_start, - &purgeable_reprotection_length, - &purgeable_reprotection_value); - } - - // Default zone - - g_old_malloc = default_zone->malloc; - g_old_calloc = default_zone->calloc; - g_old_valloc = default_zone->valloc; - g_old_free = default_zone->free; - g_old_realloc = default_zone->realloc; - CHECK(g_old_malloc && g_old_calloc && g_old_valloc && g_old_free && - g_old_realloc) - << "Failed to get system allocation functions."; - - default_zone->malloc = oom_killer_malloc; - default_zone->calloc = oom_killer_calloc; - default_zone->valloc = oom_killer_valloc; - default_zone->free = oom_killer_free; - default_zone->realloc = oom_killer_realloc; - - if (default_zone->version >= 5) { - g_old_memalign = default_zone->memalign; - if (g_old_memalign) - default_zone->memalign = oom_killer_memalign; - } - - // Purgeable zone (if it exists) - - if (purgeable_zone) { - g_old_malloc_purgeable = purgeable_zone->malloc; - g_old_calloc_purgeable = purgeable_zone->calloc; - g_old_valloc_purgeable = purgeable_zone->valloc; - g_old_free_purgeable = purgeable_zone->free; - g_old_realloc_purgeable = purgeable_zone->realloc; - CHECK(g_old_malloc_purgeable && g_old_calloc_purgeable && - g_old_valloc_purgeable && g_old_free_purgeable && - g_old_realloc_purgeable) - << "Failed to get system allocation functions."; - - purgeable_zone->malloc = oom_killer_malloc_purgeable; - purgeable_zone->calloc = oom_killer_calloc_purgeable; - purgeable_zone->valloc = oom_killer_valloc_purgeable; - purgeable_zone->free = oom_killer_free_purgeable; - purgeable_zone->realloc = oom_killer_realloc_purgeable; - - if (purgeable_zone->version >= 5) { - g_old_memalign_purgeable = purgeable_zone->memalign; - if (g_old_memalign_purgeable) - purgeable_zone->memalign = oom_killer_memalign_purgeable; - } - } - - // Restore protection if it was active. - - if (default_reprotection_start) { - kern_return_t result = mach_vm_protect(mach_task_self(), - default_reprotection_start, - default_reprotection_length, - false, - default_reprotection_value); - MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; - } - - if (purgeable_reprotection_start) { - kern_return_t result = mach_vm_protect(mach_task_self(), - purgeable_reprotection_start, - purgeable_reprotection_length, - false, - purgeable_reprotection_value); - MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect"; - } -#endif - - // === C malloc_zone_batch_malloc === - - // batch_malloc is omitted because the default malloc zone's implementation - // only supports batch_malloc for "tiny" allocations from the free list. It - // will fail for allocations larger than "tiny", and will only allocate as - // many blocks as it's able to from the free list. These factors mean that it - // can return less than the requested memory even in a non-out-of-memory - // situation. There's no good way to detect whether a batch_malloc failure is - // due to these other factors, or due to genuine memory or address space - // exhaustion. The fact that it only allocates space from the "tiny" free list - // means that it's likely that a failure will not be due to memory exhaustion. - // Similarly, these constraints on batch_malloc mean that callers must always - // be expecting to receive less memory than was requested, even in situations - // where memory pressure is not a concern. Finally, the only public interface - // to batch_malloc is malloc_zone_batch_malloc, which is specific to the - // system's malloc implementation. It's unlikely that anyone's even heard of - // it. - - // === C++ operator new === - - // Yes, operator new does call through to malloc, but this will catch failures - // that our imperfect handling of malloc cannot. - - std::set_new_handler(oom_killer_new); - -#ifndef ADDRESS_SANITIZER - // === Core Foundation CFAllocators === - - // This will not catch allocation done by custom allocators, but will catch - // all allocation done by system-provided ones. - - CHECK(!g_old_cfallocator_system_default && !g_old_cfallocator_malloc && - !g_old_cfallocator_malloc_zone) - << "Old allocators unexpectedly non-null"; - - bool cf_allocator_internals_known = CanGetContextForCFAllocator(); - - if (cf_allocator_internals_known) { - CFAllocatorContext* context = - ContextForCFAllocator(kCFAllocatorSystemDefault); - CHECK(context) << "Failed to get context for kCFAllocatorSystemDefault."; - g_old_cfallocator_system_default = context->allocate; - CHECK(g_old_cfallocator_system_default) - << "Failed to get kCFAllocatorSystemDefault allocation function."; - context->allocate = oom_killer_cfallocator_system_default; - - context = ContextForCFAllocator(kCFAllocatorMalloc); - CHECK(context) << "Failed to get context for kCFAllocatorMalloc."; - g_old_cfallocator_malloc = context->allocate; - CHECK(g_old_cfallocator_malloc) - << "Failed to get kCFAllocatorMalloc allocation function."; - context->allocate = oom_killer_cfallocator_malloc; - - context = ContextForCFAllocator(kCFAllocatorMallocZone); - CHECK(context) << "Failed to get context for kCFAllocatorMallocZone."; - g_old_cfallocator_malloc_zone = context->allocate; - CHECK(g_old_cfallocator_malloc_zone) - << "Failed to get kCFAllocatorMallocZone allocation function."; - context->allocate = oom_killer_cfallocator_malloc_zone; - } else { - DLOG(WARNING) << "Internals of CFAllocator not known; out-of-memory " - "failures via CFAllocator will not result in termination. " - "http://crbug.com/45650"; - } -#endif - - // === Cocoa NSObject allocation === - - // Note that both +[NSObject new] and +[NSObject alloc] call through to - // +[NSObject allocWithZone:]. - - CHECK(!g_old_allocWithZone) - << "Old allocator unexpectedly non-null"; - - Class nsobject_class = [NSObject class]; - Method orig_method = class_getClassMethod(nsobject_class, - @selector(allocWithZone:)); - g_old_allocWithZone = reinterpret_cast( - method_getImplementation(orig_method)); - CHECK(g_old_allocWithZone) - << "Failed to get allocWithZone allocation function."; - method_setImplementation(orig_method, - reinterpret_cast(oom_killer_allocWithZone)); -} - -} // namespace base diff --git a/src/butil/process/memory_stubs.cc b/src/butil/process/memory_stubs.cc deleted file mode 100644 index 24ad6ed66a..0000000000 --- a/src/butil/process/memory_stubs.cc +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/memory.h" - -namespace butil { - -void EnableTerminationOnOutOfMemory() { -} - -void EnableTerminationOnHeapCorruption() { -} - -bool AdjustOOMScore(ProcessId process, int score) { - return false; -} - -} // namespace butil diff --git a/src/butil/process/process.h b/src/butil/process/process.h deleted file mode 100644 index b6acf4bb9c..0000000000 --- a/src/butil/process/process.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_PROCESS_PROCESS_PROCESS_H_ -#define BASE_PROCESS_PROCESS_PROCESS_H_ - -#include "butil/base_export.h" -#include "butil/basictypes.h" -#include "butil/process/process_handle.h" -#include "butil/build_config.h" - -namespace butil { - -class BASE_EXPORT Process { - public: - Process() : process_(kNullProcessHandle) { - } - - explicit Process(ProcessHandle handle) : process_(handle) { - } - - // A handle to the current process. - static Process Current(); - - static bool CanBackgroundProcesses(); - - // 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 handle) { - process_ = handle; - } - - // 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 process 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); - - // Returns an integer representing the priority of a process. The meaning - // of this value is OS dependent. - int GetPriority() const; - - private: - ProcessHandle process_; -}; - -} // namespace butil - -#endif // BASE_PROCESS_PROCESS_PROCESS_H_ diff --git a/src/butil/process/process_handle.h b/src/butil/process/process_handle.h deleted file mode 100644 index 8a23fef4a1..0000000000 --- a/src/butil/process/process_handle.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2013 The Chromium 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_PROCESS_HANDLE_H_ -#define BASE_PROCESS_PROCESS_HANDLE_H_ - -#include "butil/base_export.h" -#include "butil/basictypes.h" -#include "butil/files/file_path.h" -#include "butil/build_config.h" - -#include -#if defined(OS_WIN) -#include -#endif - -namespace butil { - -// 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; -typedef HANDLE UserTokenHandle; -const ProcessHandle kNullProcessHandle = NULL; -const ProcessId kNullProcessId = 0; -#elif defined(OS_POSIX) -// On POSIX, our ProcessHandle will just be the PID. -typedef pid_t ProcessHandle; -typedef pid_t ProcessId; -const ProcessHandle kNullProcessHandle = 0; -const ProcessId kNullProcessId = 0; -#endif // defined(OS_WIN) - -// Returns the id of the current process. -BASE_EXPORT ProcessId GetCurrentProcId(); - -// Returns the ProcessHandle of the current process. -BASE_EXPORT ProcessHandle GetCurrentProcessHandle(); - -// Converts a PID to a process handle. This handle must be closed by -// CloseProcessHandle when you are done with it. Returns true on success. -BASE_EXPORT bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle); - -// Converts a PID to a process handle. On Windows the handle is opened -// with more access rights and must only be used by trusted code. -// You have to close returned handle using CloseProcessHandle. Returns true -// on success. -// TODO(sanjeevr): Replace all calls to OpenPrivilegedProcessHandle with the -// more specific OpenProcessHandleWithAccess method and delete this. -BASE_EXPORT bool OpenPrivilegedProcessHandle(ProcessId pid, - ProcessHandle* handle); - -// Converts a PID to a process handle using the desired access flags. Use a -// combination of the kProcessAccess* flags defined above for |access_flags|. -BASE_EXPORT bool OpenProcessHandleWithAccess(ProcessId pid, - uint32_t access_flags, - ProcessHandle* handle); - -// Closes the process handle opened by OpenProcessHandle. -BASE_EXPORT void CloseProcessHandle(ProcessHandle process); - -// Returns the unique ID for the specified process. This is functionally the -// same as Windows' GetProcessId(), but works on versions of Windows before -// Win XP SP1 as well. -BASE_EXPORT ProcessId GetProcId(ProcessHandle process); - -#if defined(OS_WIN) -enum IntegrityLevel { - INTEGRITY_UNKNOWN, - LOW_INTEGRITY, - MEDIUM_INTEGRITY, - HIGH_INTEGRITY, -}; -// Determine the integrity level of the specified process. Returns false -// if the system does not support integrity levels (pre-Vista) or in the case -// of an underlying system failure. -BASE_EXPORT bool GetProcessIntegrityLevel(ProcessHandle process, - IntegrityLevel* level); -#endif - -#if defined(OS_POSIX) -// Returns the path to the executable of the given process. -BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process); - -// Returns the ID for the parent of the given process. -BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process); -#endif - -} // namespace butil - -#endif // BASE_PROCESS_PROCESS_HANDLE_H_ diff --git a/src/butil/process/process_handle_freebsd.cc b/src/butil/process/process_handle_freebsd.cc deleted file mode 100644 index e760ea9e4c..0000000000 --- a/src/butil/process/process_handle_freebsd.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "butil/process/process_handle.h" - -#include -#include -#include -#include - -namespace butil { - -ProcessId GetParentProcessId(ProcessHandle process) { - struct kinfo_proc info; - size_t length; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process }; - - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) - return -1; - - return info.ki_ppid; -} - -FilePath GetProcessExecutablePath(ProcessHandle process) { - char pathname[PATH_MAX]; - size_t length; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, process }; - - length = sizeof(pathname); - - if (sysctl(mib, arraysize(mib), pathname, &length, NULL, 0) < 0 || - length == 0) { - return FilePath(); - } - - return FilePath(std::string(pathname)); -} - -} // namespace butil diff --git a/src/butil/process/process_handle_linux.cc b/src/butil/process/process_handle_linux.cc deleted file mode 100644 index c26ca40886..0000000000 --- a/src/butil/process/process_handle_linux.cc +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_handle.h" - -#include "butil/file_util.h" -#include "butil/process/internal_linux.h" - -namespace butil { - -ProcessId GetParentProcessId(ProcessHandle process) { - ProcessId pid = - internal::ReadProcStatsAndGetFieldAsInt64(process, internal::VM_PPID); - if (pid) - return pid; - return -1; -} - -FilePath GetProcessExecutablePath(ProcessHandle process) { - FilePath stat_file = internal::GetProcPidDir(process).Append("exe"); - FilePath exe_name; - if (!ReadSymbolicLink(stat_file, &exe_name)) { - // No such process. Happens frequently in e.g. TerminateAllChromeProcesses - return FilePath(); - } - return exe_name; -} - -} // namespace butil diff --git a/src/butil/process/process_handle_mac.cc b/src/butil/process/process_handle_mac.cc deleted file mode 100644 index 99df61f19d..0000000000 --- a/src/butil/process/process_handle_mac.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_handle.h" - -#include -#include -#include - -#include "butil/logging.h" - -namespace butil { - -ProcessId GetParentProcessId(ProcessHandle process) { - struct kinfo_proc info; - size_t length = sizeof(struct kinfo_proc); - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process }; - if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) { - DPLOG(ERROR) << "sysctl"; - return -1; - } - if (length == 0) - return -1; - return info.kp_eproc.e_ppid; -} - -FilePath GetProcessExecutablePath(ProcessHandle process) { - char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; - if (!proc_pidpath(process, pathbuf, sizeof(pathbuf))) - return FilePath(); - - return FilePath(pathbuf); -} - -} // namespace butil diff --git a/src/butil/process/process_handle_openbsd.cc b/src/butil/process/process_handle_openbsd.cc deleted file mode 100644 index 97063ec86a..0000000000 --- a/src/butil/process/process_handle_openbsd.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "butil/process/process_handle.h" - -#include -#include -#include - -namespace butil { - -ProcessId GetParentProcessId(ProcessHandle process) { - struct kinfo_proc info; - size_t length; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process, - sizeof(struct kinfo_proc), 0 }; - - if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) - return -1; - - mib[5] = (length / sizeof(struct kinfo_proc)); - - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) - return -1; - - return info.p_ppid; -} - -FilePath GetProcessExecutablePath(ProcessHandle process) { - struct kinfo_proc kp; - size_t len; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process, - sizeof(struct kinfo_proc), 0 }; - - if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) == -1) - return FilePath(); - mib[5] = (len / sizeof(struct kinfo_proc)); - if (sysctl(mib, arraysize(mib), &kp, &len, NULL, 0) < 0) - return FilePath(); - if ((kp.p_flag & P_SYSTEM) != 0) - return FilePath(); - if (strcmp(kp.p_comm, "chrome") == 0) - return FilePath(kp.p_comm); - - return FilePath(); -} - -} // namespace butil diff --git a/src/butil/process/process_handle_posix.cc b/src/butil/process/process_handle_posix.cc deleted file mode 100644 index 32db46017c..0000000000 --- a/src/butil/process/process_handle_posix.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_handle.h" - -#include - -namespace butil { - -ProcessId GetCurrentProcId() { - return getpid(); -} - -ProcessHandle GetCurrentProcessHandle() { - return GetCurrentProcId(); -} - -bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { - // On Posix platforms, process handles are the same as PIDs, so we - // don't need to do anything. - *handle = pid; - return true; -} - -bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { - // On POSIX permissions are checked for each operation on process, - // not when opening a "handle". - return OpenProcessHandle(pid, handle); -} - -bool OpenProcessHandleWithAccess(ProcessId pid, - uint32_t access_flags, - ProcessHandle* handle) { - // On POSIX permissions are checked for each operation on process, - // not when opening a "handle". - return OpenProcessHandle(pid, handle); -} - -void CloseProcessHandle(ProcessHandle process) { - // See OpenProcessHandle, nothing to do. - return; -} - -ProcessId GetProcId(ProcessHandle process) { - return process; -} - -} // namespace butil diff --git a/src/butil/process/process_info.h b/src/butil/process/process_info.h deleted file mode 100644 index 42add3d1a8..0000000000 --- a/src/butil/process/process_info.h +++ /dev/null @@ -1,25 +0,0 @@ -// 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_PROCESS_PROCESS_PROCESS_INFO_H_ -#define BASE_PROCESS_PROCESS_PROCESS_INFO_H_ - -#include "butil/base_export.h" -#include "butil/basictypes.h" - -namespace butil { - -class Time; - -// Vends information about the current process. -class BASE_EXPORT CurrentProcessInfo { - public: - // Returns the time at which the process was launched. May be empty if an - // error occurred retrieving the information. - static const Time CreationTime(); -}; - -} // namespace butil - -#endif // BASE_PROCESS_PROCESS_PROCESS_INFO_H_ diff --git a/src/butil/process/process_info_linux.cc b/src/butil/process/process_info_linux.cc deleted file mode 100644 index 5feca3246f..0000000000 --- a/src/butil/process/process_info_linux.cc +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2013 The Chromium 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 "butil/process/process_info.h" - -#include "butil/basictypes.h" -#include "butil/logging.h" -#include "butil/process/internal_linux.h" -#include "butil/process/process_handle.h" -#include "butil/time/time.h" - -namespace butil { - -//static -const Time CurrentProcessInfo::CreationTime() { - ProcessHandle pid = GetCurrentProcessHandle(); - int64_t start_ticks = - internal::ReadProcStatsAndGetFieldAsInt64(pid, internal::VM_STARTTIME); - DCHECK(start_ticks); - TimeDelta start_offset = internal::ClockTicksToTimeDelta(start_ticks); - Time boot_time = internal::GetBootTime(); - DCHECK(!boot_time.is_null()); - return Time(boot_time + start_offset); -} - -} // namespace butil diff --git a/src/butil/process/process_info_mac.cc b/src/butil/process/process_info_mac.cc deleted file mode 100644 index d9f6331e2e..0000000000 --- a/src/butil/process/process_info_mac.cc +++ /dev/null @@ -1,31 +0,0 @@ -// 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. - -#include "butil/process/process_info.h" - -#include -#include -#include - -#include "butil/basictypes.h" -#include "butil/memory/scoped_ptr.h" -#include "butil/time/time.h" - -namespace butil { - -//static -const Time CurrentProcessInfo::CreationTime() { - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; - size_t len = 0; - if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) - return Time(); - - scoped_ptr - proc(static_cast(malloc(len))); - if (sysctl(mib, arraysize(mib), proc.get(), &len, NULL, 0) < 0) - return Time(); - return Time::FromTimeVal(proc->kp_proc.p_un.__p_starttime); -} - -} // namespace butil diff --git a/src/butil/process/process_iterator.cc b/src/butil/process/process_iterator.cc deleted file mode 100644 index 8f113d3e67..0000000000 --- a/src/butil/process/process_iterator.cc +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_iterator.h" - -namespace butil { - -#if defined(OS_POSIX) -ProcessEntry::ProcessEntry() : pid_(0), ppid_(0), gid_(0) {} -ProcessEntry::~ProcessEntry() {} -#endif - -const ProcessEntry* ProcessIterator::NextProcessEntry() { - bool result = false; - do { - result = CheckForNextProcess(); - } while (result && !IncludeEntry()); - if (result) - return &entry_; - return NULL; -} - -ProcessIterator::ProcessEntries ProcessIterator::Snapshot() { - ProcessEntries found; - while (const ProcessEntry* process_entry = NextProcessEntry()) { - found.push_back(*process_entry); - } - return found; -} - -bool ProcessIterator::IncludeEntry() { - return !filter_ || filter_->Includes(entry_); -} - -NamedProcessIterator::NamedProcessIterator( - const FilePath::StringType& executable_name, - const ProcessFilter* filter) : ProcessIterator(filter), - executable_name_(executable_name) { -#if defined(OS_ANDROID) - // On Android, the process name contains only the last 15 characters, which - // is in file /proc//stat, the string between open parenthesis and close - // parenthesis. Please See ProcessIterator::CheckForNextProcess for details. - // Now if the length of input process name is greater than 15, only save the - // last 15 characters. - if (executable_name_.size() > 15) { - executable_name_ = FilePath::StringType(executable_name_, - executable_name_.size() - 15, 15); - } -#endif -} - -NamedProcessIterator::~NamedProcessIterator() { -} - -int GetProcessCount(const FilePath::StringType& executable_name, - const ProcessFilter* filter) { - int count = 0; - NamedProcessIterator iter(executable_name, filter); - while (iter.NextProcessEntry()) - ++count; - return count; -} - -} // namespace butil diff --git a/src/butil/process/process_iterator.h b/src/butil/process/process_iterator.h deleted file mode 100644 index b12f6d8ec1..0000000000 --- a/src/butil/process/process_iterator.h +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 methods to iterate over processes on the system. - -#ifndef BASE_PROCESS_PROCESS_ITERATOR_H_ -#define BASE_PROCESS_PROCESS_ITERATOR_H_ - -#include -#include -#include - -#include "butil/base_export.h" -#include "butil/basictypes.h" -#include "butil/files/file_path.h" -#include "butil/process/process.h" -#include "butil/build_config.h" - -#if defined(OS_WIN) -#include -#include -#elif defined(OS_MACOSX) || defined(OS_OPENBSD) -#include -#elif defined(OS_FREEBSD) -#include -#elif defined(OS_POSIX) -#include -#endif - -namespace butil { - -#if defined(OS_WIN) -struct ProcessEntry : public PROCESSENTRY32 { - ProcessId pid() const { return th32ProcessID; } - ProcessId parent_pid() const { return th32ParentProcessID; } - const wchar_t* exe_file() const { return szExeFile; } -}; - -// Process access masks. These constants provide platform-independent -// definitions for the standard Windows access masks. -// See http://msdn.microsoft.com/en-us/library/ms684880(VS.85).aspx for -// the specific semantics of each mask value. -const uint32_t kProcessAccessTerminate = PROCESS_TERMINATE; -const uint32_t kProcessAccessCreateThread = PROCESS_CREATE_THREAD; -const uint32_t kProcessAccessSetSessionId = PROCESS_SET_SESSIONID; -const uint32_t kProcessAccessVMOperation = PROCESS_VM_OPERATION; -const uint32_t kProcessAccessVMRead = PROCESS_VM_READ; -const uint32_t kProcessAccessVMWrite = PROCESS_VM_WRITE; -const uint32_t kProcessAccessDuplicateHandle = PROCESS_DUP_HANDLE; -const uint32_t kProcessAccessCreateProcess = PROCESS_CREATE_PROCESS; -const uint32_t kProcessAccessSetQuota = PROCESS_SET_QUOTA; -const uint32_t kProcessAccessSetInformation = PROCESS_SET_INFORMATION; -const uint32_t kProcessAccessQueryInformation = PROCESS_QUERY_INFORMATION; -const uint32_t kProcessAccessSuspendResume = PROCESS_SUSPEND_RESUME; -const uint32_t kProcessAccessQueryLimitedInfomation = - PROCESS_QUERY_LIMITED_INFORMATION; -const uint32_t kProcessAccessWaitForTermination = SYNCHRONIZE; -#elif defined(OS_POSIX) -struct BASE_EXPORT ProcessEntry { - ProcessEntry(); - ~ProcessEntry(); - - ProcessId pid() const { return pid_; } - ProcessId parent_pid() const { return ppid_; } - ProcessId gid() const { return gid_; } - const char* exe_file() const { return exe_file_.c_str(); } - const std::vector& cmd_line_args() const { - return cmd_line_args_; - } - - ProcessId pid_; - ProcessId ppid_; - ProcessId gid_; - std::string exe_file_; - std::vector cmd_line_args_; -}; - -// Process access masks. They are not used on Posix because access checking -// does not happen during handle creation. -const uint32_t kProcessAccessTerminate = 0; -const uint32_t kProcessAccessCreateThread = 0; -const uint32_t kProcessAccessSetSessionId = 0; -const uint32_t kProcessAccessVMOperation = 0; -const uint32_t kProcessAccessVMRead = 0; -const uint32_t kProcessAccessVMWrite = 0; -const uint32_t kProcessAccessDuplicateHandle = 0; -const uint32_t kProcessAccessCreateProcess = 0; -const uint32_t kProcessAccessSetQuota = 0; -const uint32_t kProcessAccessSetInformation = 0; -const uint32_t kProcessAccessQueryInformation = 0; -const uint32_t kProcessAccessSuspendResume = 0; -const uint32_t kProcessAccessQueryLimitedInfomation = 0; -const uint32_t kProcessAccessWaitForTermination = 0; -#endif // defined(OS_POSIX) - -// Used to filter processes by process ID. -class ProcessFilter { - public: - // Returns true to indicate set-inclusion and false otherwise. This method - // should not have side-effects and should be idempotent. - virtual bool Includes(const ProcessEntry& entry) const = 0; - - protected: - virtual ~ProcessFilter() {} -}; - -// This class provides a way to iterate through a list of processes on the -// current machine with a specified filter. -// To use, create an instance and then call NextProcessEntry() until it returns -// false. -class BASE_EXPORT ProcessIterator { - public: - typedef std::list ProcessEntries; - - explicit ProcessIterator(const ProcessFilter* filter); - virtual ~ProcessIterator(); - - // If there's another process that matches the given executable name, - // returns a const pointer to the corresponding PROCESSENTRY32. - // If there are no more matching processes, returns NULL. - // The returned pointer will remain valid until NextProcessEntry() - // is called again or this NamedProcessIterator goes out of scope. - const ProcessEntry* NextProcessEntry(); - - // Takes a snapshot of all the ProcessEntry found. - ProcessEntries Snapshot(); - - protected: - virtual bool IncludeEntry(); - const ProcessEntry& entry() { return entry_; } - - private: - // Determines whether there's another process (regardless of executable) - // left in the list of all processes. Returns true and sets entry_ to - // that process's info if there is one, false otherwise. - bool CheckForNextProcess(); - - // Initializes a PROCESSENTRY32 data structure so that it's ready for - // use with Process32First/Process32Next. - void InitProcessEntry(ProcessEntry* entry); - -#if defined(OS_WIN) - HANDLE snapshot_; - bool started_iteration_; -#elif defined(OS_MACOSX) || defined(OS_BSD) - std::vector kinfo_procs_; - size_t index_of_kinfo_proc_; -#elif defined(OS_POSIX) - DIR* procfs_dir_; -#endif - ProcessEntry entry_; - const ProcessFilter* filter_; - - DISALLOW_COPY_AND_ASSIGN(ProcessIterator); -}; - -// This class provides a way to iterate through the list of processes -// on the current machine that were started from the given executable -// name. To use, create an instance and then call NextProcessEntry() -// until it returns false. -class BASE_EXPORT NamedProcessIterator : public ProcessIterator { - public: - NamedProcessIterator(const FilePath::StringType& executable_name, - const ProcessFilter* filter); - virtual ~NamedProcessIterator(); - - protected: - virtual bool IncludeEntry() OVERRIDE; - - private: - FilePath::StringType executable_name_; - - DISALLOW_COPY_AND_ASSIGN(NamedProcessIterator); -}; - -// Returns the number of processes on the machine that are running from the -// given executable name. If filter is non-null, then only processes selected -// by the filter will be counted. -BASE_EXPORT int GetProcessCount(const FilePath::StringType& executable_name, - const ProcessFilter* filter); - -} // namespace butil - -#endif // BASE_PROCESS_PROCESS_ITERATOR_H_ diff --git a/src/butil/process/process_iterator_freebsd.cc b/src/butil/process/process_iterator_freebsd.cc deleted file mode 100644 index d083cfab2f..0000000000 --- a/src/butil/process/process_iterator_freebsd.cc +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_iterator.h" - -#include -#include -#include - -#include "butil/logging.h" -#include "butil/strings/string_util.h" - -namespace butil { - -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : index_of_kinfo_proc_(), - filter_(filter) { - - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid() }; - - bool done = false; - int try_num = 1; - const int max_tries = 10; - - do { - size_t len = 0; - if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { - LOG(ERROR) << "failed to get the size needed for the process list"; - kinfo_procs_.resize(0); - done = true; - } else { - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - // Leave some spare room for process table growth (more could show up - // between when we check and now) - num_of_kinfo_proc += 16; - kinfo_procs_.resize(num_of_kinfo_proc); - len = num_of_kinfo_proc * sizeof(struct kinfo_proc); - if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) <0) { - // If we get a mem error, it just means we need a bigger buffer, so - // loop around again. Anything else is a real error and give up. - if (errno != ENOMEM) { - LOG(ERROR) << "failed to get the process list"; - kinfo_procs_.resize(0); - done = true; - } - } else { - // Got the list, just make sure we're sized exactly right - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - kinfo_procs_.resize(num_of_kinfo_proc); - done = true; - } - } - } while (!done && (try_num++ < max_tries)); - - if (!done) { - LOG(ERROR) << "failed to collect the process list in a few tries"; - kinfo_procs_.resize(0); - } -} - -ProcessIterator::~ProcessIterator() { -} - -bool ProcessIterator::CheckForNextProcess() { - std::string data; - - for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { - size_t length; - struct kinfo_proc kinfo = kinfo_procs_[index_of_kinfo_proc_]; - int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.ki_pid }; - - if ((kinfo.ki_pid > 0) && (kinfo.ki_stat == SZOMB)) - continue; - - length = 0; - if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) { - LOG(ERROR) << "failed to figure out the buffer size for a command line"; - continue; - } - - data.resize(length); - - if (sysctl(mib, arraysize(mib), &data[0], &length, NULL, 0) < 0) { - LOG(ERROR) << "failed to fetch a commandline"; - continue; - } - - std::string delimiters; - delimiters.push_back('\0'); - Tokenize(data, delimiters, &entry_.cmd_line_args_); - - size_t exec_name_end = data.find('\0'); - if (exec_name_end == std::string::npos) { - LOG(ERROR) << "command line data didn't match expected format"; - continue; - } - - entry_.pid_ = kinfo.ki_pid; - entry_.ppid_ = kinfo.ki_ppid; - entry_.gid_ = kinfo.ki_pgid; - - size_t last_slash = data.rfind('/', exec_name_end); - if (last_slash == std::string::npos) { - entry_.exe_file_.assign(data, 0, exec_name_end); - } else { - entry_.exe_file_.assign(data, last_slash + 1, - exec_name_end - last_slash - 1); - } - - // Start w/ the next entry next time through - ++index_of_kinfo_proc_; - - return true; - } - return false; -} - -bool NamedProcessIterator::IncludeEntry() { - if (executable_name_ != entry().exe_file()) - return false; - - return ProcessIterator::IncludeEntry(); -} - -} // namespace butil diff --git a/src/butil/process/process_iterator_linux.cc b/src/butil/process/process_iterator_linux.cc deleted file mode 100644 index 85100d3d09..0000000000 --- a/src/butil/process/process_iterator_linux.cc +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_iterator.h" - -#include "butil/file_util.h" -#include "butil/logging.h" -#include "butil/process/internal_linux.h" -#include "butil/strings/string_util.h" -#include "butil/threading/thread_restrictions.h" - -namespace butil { - -namespace { - -// Reads the |field_num|th field from |proc_stats|. -// Returns an empty string on failure. -// This version only handles VM_COMM and VM_STATE, which are the only fields -// that are strings. -std::string GetProcStatsFieldAsString( - const std::vector& proc_stats, - internal::ProcStatsFields field_num) { - if (field_num < internal::VM_COMM || field_num > internal::VM_STATE) { - NOTREACHED(); - return std::string(); - } - - if (proc_stats.size() > static_cast(field_num)) - return proc_stats[field_num]; - - NOTREACHED(); - return 0; -} - -// Reads /proc//cmdline and populates |proc_cmd_line_args| with the command -// line arguments. Returns true if successful. -// Note: /proc//cmdline contains command line arguments separated by single -// null characters. We tokenize it into a vector of strings using '\0' as a -// delimiter. -bool GetProcCmdline(pid_t pid, std::vector* proc_cmd_line_args) { - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - - FilePath cmd_line_file = internal::GetProcPidDir(pid).Append("cmdline"); - std::string cmd_line; - if (!ReadFileToString(cmd_line_file, &cmd_line)) - return false; - std::string delimiters; - delimiters.push_back('\0'); - Tokenize(cmd_line, delimiters, proc_cmd_line_args); - return true; -} - -} // namespace - -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : filter_(filter) { - procfs_dir_ = opendir(internal::kProcDir); -} - -ProcessIterator::~ProcessIterator() { - if (procfs_dir_) { - closedir(procfs_dir_); - procfs_dir_ = NULL; - } -} - -bool ProcessIterator::CheckForNextProcess() { - // TODO(port): skip processes owned by different UID - - pid_t pid = kNullProcessId; - std::vector cmd_line_args; - std::string stats_data; - std::vector proc_stats; - - // Arbitrarily guess that there will never be more than 200 non-process - // files in /proc. Hardy has 53 and Lucid has 61. - int skipped = 0; - const int kSkipLimit = 200; - while (skipped < kSkipLimit) { - dirent* slot = readdir(procfs_dir_); - // all done looking through /proc? - if (!slot) - return false; - - // If not a process, keep looking for one. - pid = internal::ProcDirSlotToPid(slot->d_name); - if (!pid) { - skipped++; - continue; - } - - if (!GetProcCmdline(pid, &cmd_line_args)) - continue; - - if (!internal::ReadProcStats(pid, &stats_data)) - continue; - if (!internal::ParseProcStats(stats_data, &proc_stats)) - continue; - - std::string runstate = - GetProcStatsFieldAsString(proc_stats, internal::VM_STATE); - if (runstate.size() != 1) { - NOTREACHED(); - continue; - } - - // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? - // Allowed values: D R S T Z - if (runstate[0] != 'Z') - break; - - // Nope, it's a zombie; somebody isn't cleaning up after their children. - // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) - // There could be a lot of zombies, can't really decrement i here. - } - if (skipped >= kSkipLimit) { - NOTREACHED(); - return false; - } - - entry_.pid_ = pid; - entry_.ppid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PPID); - entry_.gid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PGRP); - entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end()); - entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value(); - return true; -} - -bool NamedProcessIterator::IncludeEntry() { - if (executable_name_ != entry().exe_file()) - return false; - return ProcessIterator::IncludeEntry(); -} - -} // namespace butil diff --git a/src/butil/process/process_iterator_mac.cc b/src/butil/process/process_iterator_mac.cc deleted file mode 100644 index 543b9ccebf..0000000000 --- a/src/butil/process/process_iterator_mac.cc +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_iterator.h" - -#include -#include -#include -#include - -#include "butil/logging.h" -#include "butil/strings/string_util.h" - -namespace butil { - -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : index_of_kinfo_proc_(0), - filter_(filter) { - // Get a snapshot of all of my processes (yes, as we loop it can go stale, but - // but trying to find where we were in a constantly changing list is basically - // impossible. - - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() }; - - // Since more processes could start between when we get the size and when - // we get the list, we do a loop to keep trying until we get it. - bool done = false; - int try_num = 1; - const int max_tries = 10; - do { - // Get the size of the buffer - size_t len = 0; - if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { - DLOG(ERROR) << "failed to get the size needed for the process list"; - kinfo_procs_.resize(0); - done = true; - } else { - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - // Leave some spare room for process table growth (more could show up - // between when we check and now) - num_of_kinfo_proc += 16; - kinfo_procs_.resize(num_of_kinfo_proc); - len = num_of_kinfo_proc * sizeof(struct kinfo_proc); - // Load the list of processes - if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { - // If we get a mem error, it just means we need a bigger buffer, so - // loop around again. Anything else is a real error and give up. - if (errno != ENOMEM) { - DLOG(ERROR) << "failed to get the process list"; - kinfo_procs_.resize(0); - done = true; - } - } else { - // Got the list, just make sure we're sized exactly right - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - kinfo_procs_.resize(num_of_kinfo_proc); - done = true; - } - } - } while (!done && (try_num++ < max_tries)); - - if (!done) { - DLOG(ERROR) << "failed to collect the process list in a few tries"; - kinfo_procs_.resize(0); - } -} - -ProcessIterator::~ProcessIterator() { -} - -bool ProcessIterator::CheckForNextProcess() { - std::string data; - for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { - kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_]; - - // Skip processes just awaiting collection - if ((kinfo.kp_proc.p_pid > 0) && (kinfo.kp_proc.p_stat == SZOMB)) - continue; - - int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo.kp_proc.p_pid }; - - // Find out what size buffer we need. - size_t data_len = 0; - if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { - DVPLOG(1) << "failed to figure out the buffer size for a commandline"; - continue; - } - - data.resize(data_len); - if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { - DVPLOG(1) << "failed to fetch a commandline"; - continue; - } - - // |data| contains all the command line parameters of the process, separated - // by blocks of one or more null characters. We tokenize |data| into a - // vector of strings using '\0' as a delimiter and populate - // |entry_.cmd_line_args_|. - std::string delimiters; - delimiters.push_back('\0'); - Tokenize(data, delimiters, &entry_.cmd_line_args_); - - // |data| starts with the full executable path followed by a null character. - // We search for the first instance of '\0' and extract everything before it - // to populate |entry_.exe_file_|. - size_t exec_name_end = data.find('\0'); - if (exec_name_end == std::string::npos) { - DLOG(ERROR) << "command line data didn't match expected format"; - continue; - } - - entry_.pid_ = kinfo.kp_proc.p_pid; - entry_.ppid_ = kinfo.kp_eproc.e_ppid; - entry_.gid_ = kinfo.kp_eproc.e_pgid; - size_t last_slash = data.rfind('/', exec_name_end); - if (last_slash == std::string::npos) - entry_.exe_file_.assign(data, 0, exec_name_end); - else - entry_.exe_file_.assign(data, last_slash + 1, - exec_name_end - last_slash - 1); - // Start w/ the next entry next time through - ++index_of_kinfo_proc_; - // Done - return true; - } - return false; -} - -bool NamedProcessIterator::IncludeEntry() { - return (executable_name_ == entry().exe_file() && - ProcessIterator::IncludeEntry()); -} - -} // namespace butil diff --git a/src/butil/process/process_iterator_openbsd.cc b/src/butil/process/process_iterator_openbsd.cc deleted file mode 100644 index 943ef04f64..0000000000 --- a/src/butil/process/process_iterator_openbsd.cc +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_iterator.h" - -#include -#include - -#include "butil/logging.h" -#include "butil/strings/string_util.h" - -namespace butil { - -ProcessIterator::ProcessIterator(const ProcessFilter* filter) - : index_of_kinfo_proc_(), - filter_(filter) { - - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, getuid(), - sizeof(struct kinfo_proc), 0 }; - - bool done = false; - int try_num = 1; - const int max_tries = 10; - - do { - size_t len = 0; - if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { - DLOG(ERROR) << "failed to get the size needed for the process list"; - kinfo_procs_.resize(0); - done = true; - } else { - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - // Leave some spare room for process table growth (more could show up - // between when we check and now) - num_of_kinfo_proc += 16; - kinfo_procs_.resize(num_of_kinfo_proc); - len = num_of_kinfo_proc * sizeof(struct kinfo_proc); - if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { - // If we get a mem error, it just means we need a bigger buffer, so - // loop around again. Anything else is a real error and give up. - if (errno != ENOMEM) { - DLOG(ERROR) << "failed to get the process list"; - kinfo_procs_.resize(0); - done = true; - } - } else { - // Got the list, just make sure we're sized exactly right - size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); - kinfo_procs_.resize(num_of_kinfo_proc); - done = true; - } - } - } while (!done && (try_num++ < max_tries)); - - if (!done) { - DLOG(ERROR) << "failed to collect the process list in a few tries"; - kinfo_procs_.resize(0); - } -} - -ProcessIterator::~ProcessIterator() { -} - -bool ProcessIterator::CheckForNextProcess() { - std::string data; - for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { - kinfo_proc& kinfo = kinfo_procs_[index_of_kinfo_proc_]; - - // Skip processes just awaiting collection - if ((kinfo.p_pid > 0) && (kinfo.p_stat == SZOMB)) - continue; - - int mib[] = { CTL_KERN, KERN_PROC_ARGS, kinfo.p_pid }; - - // Find out what size buffer we need. - size_t data_len = 0; - if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { - DVPLOG(1) << "failed to figure out the buffer size for a commandline"; - continue; - } - - data.resize(data_len); - if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { - DVPLOG(1) << "failed to fetch a commandline"; - continue; - } - - // |data| contains all the command line parameters of the process, separated - // by blocks of one or more null characters. We tokenize |data| into a - // vector of strings using '\0' as a delimiter and populate - // |entry_.cmd_line_args_|. - std::string delimiters; - delimiters.push_back('\0'); - Tokenize(data, delimiters, &entry_.cmd_line_args_); - - // |data| starts with the full executable path followed by a null character. - // We search for the first instance of '\0' and extract everything before it - // to populate |entry_.exe_file_|. - size_t exec_name_end = data.find('\0'); - if (exec_name_end == std::string::npos) { - DLOG(ERROR) << "command line data didn't match expected format"; - continue; - } - - entry_.pid_ = kinfo.p_pid; - entry_.ppid_ = kinfo.p_ppid; - entry_.gid_ = kinfo.p__pgid; - size_t last_slash = data.rfind('/', exec_name_end); - if (last_slash == std::string::npos) - entry_.exe_file_.assign(data, 0, exec_name_end); - else - entry_.exe_file_.assign(data, last_slash + 1, - exec_name_end - last_slash - 1); - // Start w/ the next entry next time through - ++index_of_kinfo_proc_; - // Done - return true; - } - return false; -} - -bool NamedProcessIterator::IncludeEntry() { - return (executable_name_ == entry().exe_file() && - ProcessIterator::IncludeEntry()); -} - -} // namespace butil diff --git a/src/butil/process/process_linux.cc b/src/butil/process/process_linux.cc deleted file mode 100644 index 926942e78e..0000000000 --- a/src/butil/process/process_linux.cc +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "butil/process/process.h" - -#include -#include - -#include "butil/file_util.h" -#include "butil/lazy_instance.h" -#include "butil/logging.h" -#include "butil/strings/string_split.h" -#include "butil/strings/stringprintf.h" -#include "butil/synchronization/lock.h" - -namespace butil { - -namespace { -const int kForegroundPriority = 0; - -#if defined(OS_CHROMEOS) -// We are more aggressive in our lowering of background process priority -// for chromeos as we have much more control over other processes running -// on the machine. -// -// TODO(davemoore) Refactor this by adding support for higher levels to set -// the foregrounding / backgrounding process so we don't have to keep -// chrome / chromeos specific logic here. -const int kBackgroundPriority = 19; -const char kControlPath[] = "/sys/fs/cgroup/cpu%s/cgroup.procs"; -const char kForeground[] = "/chrome_renderers/foreground"; -const char kBackground[] = "/chrome_renderers/background"; -const char kProcPath[] = "/proc/%d/cgroup"; - -struct CGroups { - // Check for cgroups files. ChromeOS supports these by default. It creates - // a cgroup mount in /sys/fs/cgroup and then configures two cpu task groups, - // one contains at most a single foreground renderer and the other contains - // all background renderers. This allows us to limit the impact of background - // renderers on foreground ones to a greater level than simple renicing. - bool enabled; - butil::FilePath foreground_file; - butil::FilePath background_file; - - CGroups() { - foreground_file = - butil::FilePath(butil::StringPrintf(kControlPath, kForeground)); - background_file = - butil::FilePath(butil::StringPrintf(kControlPath, kBackground)); - butil::FileSystemType foreground_type; - butil::FileSystemType background_type; - enabled = - butil::GetFileSystemType(foreground_file, &foreground_type) && - butil::GetFileSystemType(background_file, &background_type) && - foreground_type == FILE_SYSTEM_CGROUP && - background_type == FILE_SYSTEM_CGROUP; - } -}; - -butil::LazyInstance cgroups = LAZY_INSTANCE_INITIALIZER; -#else -const int kBackgroundPriority = 5; -#endif -} - -bool Process::IsProcessBackgrounded() const { - DCHECK(process_); - -#if defined(OS_CHROMEOS) - if (cgroups.Get().enabled) { - std::string proc; - if (butil::ReadFileToString( - butil::FilePath(StringPrintf(kProcPath, process_)), - &proc)) { - std::vector proc_parts; - butil::SplitString(proc, ':', &proc_parts); - DCHECK(proc_parts.size() == 3); - bool ret = proc_parts[2] == std::string(kBackground); - return ret; - } else { - return false; - } - } -#endif - return GetPriority() == kBackgroundPriority; -} - -bool Process::SetProcessBackgrounded(bool background) { - DCHECK(process_); - -#if defined(OS_CHROMEOS) - if (cgroups.Get().enabled) { - std::string pid = StringPrintf("%d", process_); - const butil::FilePath file = - background ? - cgroups.Get().background_file : cgroups.Get().foreground_file; - return butil::WriteFile(file, pid.c_str(), pid.size()) > 0; - } -#endif // OS_CHROMEOS - - if (!CanBackgroundProcesses()) - return false; - - int priority = background ? kBackgroundPriority : kForegroundPriority; - int result = setpriority(PRIO_PROCESS, process_, priority); - DPCHECK(result == 0); - return result == 0; -} - -struct CheckForNicePermission { - CheckForNicePermission() : can_reraise_priority(false) { - // NOTE(gejun): Older linux does not support RLIMIT_NICE (not defined - // in bits/resource.h) -#if defined(RLIMIT_NICE) - // We won't be able to raise the priority if we don't have the right rlimit. - // The limit may be adjusted in /etc/security/limits.conf for PAM systems. - struct rlimit rlim; - if ((getrlimit(RLIMIT_NICE, &rlim) == 0) && - (20 - kForegroundPriority) <= static_cast(rlim.rlim_cur)) { - can_reraise_priority = true; - } -#endif - }; - - bool can_reraise_priority; -}; - -// static -bool Process::CanBackgroundProcesses() { -#if defined(OS_CHROMEOS) - if (cgroups.Get().enabled) - return true; -#endif - - static LazyInstance check_for_nice_permission = - LAZY_INSTANCE_INITIALIZER; - return check_for_nice_permission.Get().can_reraise_priority; -} - -} // namespace butil diff --git a/src/butil/process/process_metrics.cc b/src/butil/process/process_metrics.cc deleted file mode 100644 index 49d11b51df..0000000000 --- a/src/butil/process/process_metrics.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2013 The Chromium 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 "butil/process/process_metrics.h" - -#include "butil/logging.h" - -namespace butil { - -SystemMetrics::SystemMetrics() { - committed_memory_ = 0; -} - -SystemMetrics SystemMetrics::Sample() { - SystemMetrics system_metrics; - - system_metrics.committed_memory_ = GetSystemCommitCharge(); -#if defined(OS_LINUX) || defined(OS_ANDROID) - GetSystemMemoryInfo(&system_metrics.memory_info_); - GetSystemDiskInfo(&system_metrics.disk_info_); -#endif -#if defined(OS_CHROMEOS) - GetSwapInfo(&system_metrics.swap_info_); -#endif - - return system_metrics; -} - -double ProcessMetrics::GetPlatformIndependentCPUUsage() { -#if defined(OS_WIN) - return GetCPUUsage() * processor_count_; -#else - return GetCPUUsage(); -#endif -} - -#if !defined(OS_MACOSX) -int ProcessMetrics::GetIdleWakeupsPerSecond() { - NOTIMPLEMENTED(); // http://crbug.com/20488 - return 0; -} -#endif // !defined(OS_MACOSX) - -} // namespace butil diff --git a/src/butil/process/process_metrics.h b/src/butil/process/process_metrics.h deleted file mode 100644 index 5e79ec07c5..0000000000 --- a/src/butil/process/process_metrics.h +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 routines for gathering resource statistics for processes -// running on the system. - -#ifndef BASE_PROCESS_PROCESS_METRICS_H_ -#define BASE_PROCESS_PROCESS_METRICS_H_ - -#include - -#include "butil/base_export.h" -#include "butil/basictypes.h" -#include "butil/gtest_prod_util.h" -#include "butil/process/process_handle.h" -#include "butil/time/time.h" - -#if defined(OS_MACOSX) -#include -#endif - -namespace butil { - -#if defined(OS_WIN) -struct IoCounters : public IO_COUNTERS { -}; -#elif defined(OS_POSIX) -struct IoCounters { - uint64_t ReadOperationCount; - uint64_t WriteOperationCount; - uint64_t OtherOperationCount; - uint64_t ReadTransferCount; - uint64_t WriteTransferCount; - uint64_t OtherTransferCount; -}; -#endif - -// Working Set (resident) memory usage broken down by -// -// On Windows: -// priv (private): These pages (kbytes) cannot be shared with any other process. -// shareable: These pages (kbytes) can be shared with other processes under -// the right circumstances. -// shared : These pages (kbytes) are currently shared with at least one -// other process. -// -// On Linux: -// priv: Pages mapped only by this process. -// shared: PSS or 0 if the kernel doesn't support this. -// shareable: 0 - -// On ChromeOS: -// priv: Pages mapped only by this process. -// shared: PSS or 0 if the kernel doesn't support this. -// shareable: 0 -// swapped Pages swapped out to zram. -// -// On OS X: TODO(thakis): Revise. -// priv: Memory. -// shared: 0 -// shareable: 0 -// -struct WorkingSetKBytes { - WorkingSetKBytes() : priv(0), shareable(0), shared(0) {} - size_t priv; - size_t shareable; - size_t shared; -#if defined(OS_CHROMEOS) - size_t swapped; -#endif -}; - -// Committed (resident + paged) memory usage broken down by -// private: These pages cannot be shared with any other process. -// mapped: These pages are mapped into the view of a section (backed by -// pagefile.sys) -// image: These pages are mapped into the view of an image section (backed by -// file system) -struct CommittedKBytes { - CommittedKBytes() : priv(0), mapped(0), image(0) {} - size_t priv; - size_t mapped; - size_t image; -}; - -// Free memory (Megabytes marked as free) in the 2G process address space. -// total : total amount in megabytes marked as free. Maximum value is 2048. -// largest : size of the largest contiguous amount of memory found. It is -// always smaller or equal to FreeMBytes::total. -// largest_ptr: starting address of the largest memory block. -struct FreeMBytes { - size_t total; - size_t largest; - void* largest_ptr; -}; - -// Convert a POSIX timeval to microseconds. -BASE_EXPORT int64_t TimeValToMicroseconds(const struct timeval& tv); - -// Provides performance metrics for a specified process (CPU usage, memory and -// IO counters). To use it, invoke CreateProcessMetrics() to get an instance -// for a specific process, then access the information with the different get -// methods. -class BASE_EXPORT ProcessMetrics { - public: - ~ProcessMetrics(); - - // Creates a ProcessMetrics for the specified process. - // The caller owns the returned object. -#if !defined(OS_MACOSX) || defined(OS_IOS) - static ProcessMetrics* CreateProcessMetrics(ProcessHandle process); -#else - class PortProvider { - public: - virtual ~PortProvider() {} - - // Should return the mach task for |process| if possible, or else - // |MACH_PORT_NULL|. Only processes that this returns tasks for will have - // metrics on OS X (except for the current process, which always gets - // metrics). - virtual mach_port_t TaskForPid(ProcessHandle process) const = 0; - }; - - // The port provider needs to outlive the ProcessMetrics object returned by - // this function. If NULL is passed as provider, the returned object - // only returns valid metrics if |process| is the current process. - static ProcessMetrics* CreateProcessMetrics(ProcessHandle process, - PortProvider* port_provider); -#endif // !defined(OS_MACOSX) || defined(OS_IOS) - - // Returns the current space allocated for the pagefile, in bytes (these pages - // may or may not be in memory). On Linux, this returns the total virtual - // memory size. - size_t GetPagefileUsage() const; - // Returns the peak space allocated for the pagefile, in bytes. - size_t GetPeakPagefileUsage() const; - // Returns the current working set size, in bytes. On Linux, this returns - // the resident set size. - size_t GetWorkingSetSize() const; - // Returns the peak working set size, in bytes. - size_t GetPeakWorkingSetSize() const; - // Returns private and sharedusage, in bytes. Private bytes is the amount of - // memory currently allocated to a process that cannot be shared. Returns - // false on platform specific error conditions. Note: |private_bytes| - // returns 0 on unsupported OSes: prior to XP SP2. - bool GetMemoryBytes(size_t* private_bytes, - size_t* shared_bytes); - // Fills a CommittedKBytes with both resident and paged - // memory usage as per definition of CommittedBytes. - void GetCommittedKBytes(CommittedKBytes* usage) const; - // Fills a WorkingSetKBytes containing resident private and shared memory - // usage in bytes, as per definition of WorkingSetBytes. - bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const; - - // Computes the current process available memory for allocation. - // It does a linear scan of the address space querying each memory region - // for its free (unallocated) status. It is useful for estimating the memory - // load and fragmentation. - bool CalculateFreeMemory(FreeMBytes* free) const; - - // Returns the CPU usage in percent since the last time this method or - // GetPlatformIndependentCPUUsage() was called. The first time this method - // is called it returns 0 and will return the actual CPU info on subsequent - // calls. On Windows, the CPU usage value is for all CPUs. So if you have - // 2 CPUs and your process is using all the cycles of 1 CPU and not the other - // CPU, this method returns 50. - double GetCPUUsage(); - - // Returns the number of average idle cpu wakeups per second since the last - // call. - int GetIdleWakeupsPerSecond(); - - // Same as GetCPUUsage(), but will return consistent values on all platforms - // (cancelling the Windows exception mentioned above) by returning a value in - // the range of 0 to (100 * numCPUCores) everywhere. - double GetPlatformIndependentCPUUsage(); - - // Retrieves accounting information for all I/O operations performed by the - // process. - // If IO information is retrieved successfully, the function returns true - // and fills in the IO_COUNTERS passed in. The function returns false - // otherwise. - bool GetIOCounters(IoCounters* io_counters) const; - - private: -#if !defined(OS_MACOSX) || defined(OS_IOS) - explicit ProcessMetrics(ProcessHandle process); -#else - ProcessMetrics(ProcessHandle process, PortProvider* port_provider); -#endif // !defined(OS_MACOSX) || defined(OS_IOS) - -#if defined(OS_LINUX) || defined(OS_ANDROID) - bool GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) const; -#endif - -#if defined(OS_CHROMEOS) - bool GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) const; -#endif - - ProcessHandle process_; - - int processor_count_; - - // Used to store the previous times and CPU usage counts so we can - // compute the CPU usage between calls. - TimeTicks last_cpu_time_; - int64_t last_system_time_; - - // Same thing for idle wakeups. - TimeTicks last_idle_wakeups_time_; - int64_t last_absolute_idle_wakeups_; - -#if !defined(OS_IOS) -#if defined(OS_MACOSX) - // Queries the port provider if it's set. - mach_port_t TaskForPid(ProcessHandle process) const; - - PortProvider* port_provider_; -#elif defined(OS_POSIX) - // Jiffie count at the last_cpu_time_ we updated. - int last_cpu_; -#endif // defined(OS_POSIX) -#endif // !defined(OS_IOS) - - DISALLOW_COPY_AND_ASSIGN(ProcessMetrics); -}; - -// Returns the memory committed by the system in KBytes. -// Returns 0 if it can't compute the commit charge. -BASE_EXPORT size_t GetSystemCommitCharge(); - -#if defined(OS_POSIX) -// Returns the maximum number of file descriptors that can be open by a process -// at once. If the number is unavailable, a conservative best guess is returned. -size_t GetMaxFds(); - -// Sets the file descriptor soft limit to |max_descriptors| or the OS hard -// limit, whichever is lower. -BASE_EXPORT void SetFdLimit(unsigned int max_descriptors); -#endif // defined(OS_POSIX) - -#if defined(OS_LINUX) || defined(OS_ANDROID) -// Parse the data found in /proc//stat and return the sum of the -// CPU-related ticks. Returns -1 on parse error. -// Exposed for testing. -BASE_EXPORT int ParseProcStatCPU(const std::string& input); - -// Get the number of threads of |process| as available in /proc//stat. -// This should be used with care as no synchronization with running threads is -// done. This is mostly useful to guarantee being single-threaded. -// Returns 0 on failure. -BASE_EXPORT int GetNumberOfThreads(ProcessHandle process); - -// /proc/self/exe refers to the current executable. -BASE_EXPORT extern const char kProcSelfExe[]; - -// Data from /proc/meminfo about system-wide memory consumption. -// Values are in KB. -struct BASE_EXPORT SystemMemoryInfoKB { - SystemMemoryInfoKB(); - - int total; - int free; - int buffers; - int cached; - int active_anon; - int inactive_anon; - int active_file; - int inactive_file; - int swap_total; - int swap_free; - int dirty; - - // vmstats data. - int pswpin; - int pswpout; - int pgmajfault; - -#ifdef OS_CHROMEOS - int shmem; - int slab; - // Gem data will be -1 if not supported. - int gem_objects; - long long gem_size; -#endif -}; - -// Parses a string containing the contents of /proc/meminfo -// returns true on success or false for a parsing error -BASE_EXPORT bool ParseProcMeminfo(const std::string& input, - SystemMemoryInfoKB* meminfo); - -// Parses a string containing the contents of /proc/vmstat -// returns true on success or false for a parsing error -BASE_EXPORT bool ParseProcVmstat(const std::string& input, - SystemMemoryInfoKB* meminfo); - -// Retrieves data from /proc/meminfo and /proc/vmstat -// about system-wide memory consumption. -// Fills in the provided |meminfo| structure. Returns true on success. -// Exposed for memory debugging widget. -BASE_EXPORT bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo); - -// Data from /proc/diskstats about system-wide disk I/O. -struct BASE_EXPORT SystemDiskInfo { - SystemDiskInfo(); - - uint64_t reads; - uint64_t reads_merged; - uint64_t sectors_read; - uint64_t read_time; - uint64_t writes; - uint64_t writes_merged; - uint64_t sectors_written; - uint64_t write_time; - uint64_t io; - uint64_t io_time; - uint64_t weighted_io_time; -}; - -// Checks whether the candidate string is a valid disk name, [sh]d[a-z]+ -// for a generic disk or mmcblk[0-9]+ for the MMC case. -// Names of disk partitions (e.g. sda1) are not valid. -BASE_EXPORT bool IsValidDiskName(const std::string& candidate); - -// Retrieves data from /proc/diskstats about system-wide disk I/O. -// Fills in the provided |diskinfo| structure. Returns true on success. -BASE_EXPORT bool GetSystemDiskInfo(SystemDiskInfo* diskinfo); -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - -#if defined(OS_CHROMEOS) -// Data from files in directory /sys/block/zram0 about ZRAM usage. -struct BASE_EXPORT SwapInfo { - SwapInfo() - : num_reads(0), - num_writes(0), - compr_data_size(0), - orig_data_size(0), - mem_used_total(0) { - } - - uint64_t num_reads; - uint64_t num_writes; - uint64_t compr_data_size; - uint64_t orig_data_size; - uint64_t mem_used_total; -}; - -// In ChromeOS, reads files from /sys/block/zram0 that contain ZRAM usage data. -// Fills in the provided |swap_data| structure. -BASE_EXPORT void GetSwapInfo(SwapInfo* swap_info); -#endif // defined(OS_CHROMEOS) - -// Collects and holds performance metrics for system memory and disk. -// Provides functionality to retrieve the data on various platforms and -// to serialize the stored data. -class SystemMetrics { - public: - SystemMetrics(); - - static SystemMetrics Sample(); - - private: - FRIEND_TEST_ALL_PREFIXES(SystemMetricsTest, SystemMetrics); - - size_t committed_memory_; -#if defined(OS_LINUX) || defined(OS_ANDROID) - SystemMemoryInfoKB memory_info_; - SystemDiskInfo disk_info_; -#endif -#if defined(OS_CHROMEOS) - SwapInfo swap_info_; -#endif -}; - -} // namespace butil - -#endif // BASE_PROCESS_PROCESS_METRICS_H_ diff --git a/src/butil/process/process_metrics_freebsd.cc b/src/butil/process/process_metrics_freebsd.cc deleted file mode 100644 index 236c5973a5..0000000000 --- a/src/butil/process/process_metrics_freebsd.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_metrics.h" - -#include -#include -#include - -#include "butil/sys_info.h" - -namespace butil { - -ProcessMetrics::ProcessMetrics(ProcessHandle process) - : process_(process), - last_system_time_(0), - last_cpu_(0) { - processor_count_ = butil::SysInfo::NumberOfProcessors(); -} - -// static -ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { - return new ProcessMetrics(process); -} - -size_t ProcessMetrics::GetPagefileUsage() const { - struct kinfo_proc info; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ }; - size_t length = sizeof(info); - - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) - return 0; - - return info.ki_size; -} - -size_t ProcessMetrics::GetPeakPagefileUsage() const { - return 0; -} - -size_t ProcessMetrics::GetWorkingSetSize() const { - struct kinfo_proc info; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ }; - size_t length = sizeof(info); - - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) - return 0; - - return info.ki_rssize * getpagesize(); -} - -size_t ProcessMetrics::GetPeakWorkingSetSize() const { - return 0; -} - -bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, - size_t* shared_bytes) { - WorkingSetKBytes ws_usage; - if (!GetWorkingSetKBytes(&ws_usage)) - return false; - - if (private_bytes) - *private_bytes = ws_usage.priv << 10; - - if (shared_bytes) - *shared_bytes = ws_usage.shared * 1024; - - return true; -} - -bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { -// TODO(bapt) be sure we can't be precise - size_t priv = GetWorkingSetSize(); - if (!priv) - return false; - ws_usage->priv = priv / 1024; - ws_usage->shareable = 0; - ws_usage->shared = 0; - - return true; -} - -double ProcessMetrics::GetCPUUsage() { - struct kinfo_proc info; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ }; - size_t length = sizeof(info); - - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) - return 0; - - return (info.ki_pctcpu / FSCALE) * 100.0; -} - -bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { - return false; -} - -size_t GetSystemCommitCharge() { - int mib[2], pagesize; - unsigned long mem_total, mem_free, mem_inactive; - size_t length = sizeof(mem_total); - - if (sysctl(mib, arraysize(mib), &mem_total, &length, NULL, 0) < 0) - return 0; - - length = sizeof(mem_free); - if (sysctlbyname("vm.stats.vm.v_free_count", &mem_free, &length, NULL, 0) < 0) - return 0; - - length = sizeof(mem_inactive); - if (sysctlbyname("vm.stats.vm.v_inactive_count", &mem_inactive, &length, - NULL, 0) < 0) { - return 0; - } - - pagesize = getpagesize(); - - return mem_total - (mem_free*pagesize) - (mem_inactive*pagesize); -} - -} // namespace butil diff --git a/src/butil/process/process_metrics_linux.cc b/src/butil/process/process_metrics_linux.cc deleted file mode 100644 index a20e30b56d..0000000000 --- a/src/butil/process/process_metrics_linux.cc +++ /dev/null @@ -1,812 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_metrics.h" - -#include -#include -#include -#include -#include -#include - -#include "butil/file_util.h" -#include "butil/logging.h" -#include "butil/process/internal_linux.h" -#include "butil/strings/string_number_conversions.h" -#include "butil/strings/string_split.h" -#include "butil/strings/string_tokenizer.h" -#include "butil/strings/string_util.h" -#include "butil/sys_info.h" -#include "butil/threading/thread_restrictions.h" - -namespace butil { - -namespace { - -enum ParsingState { - KEY_NAME, - KEY_VALUE -}; - -#ifdef OS_CHROMEOS -// Read a file with a single number string and return the number as a uint64_t. -static uint64_t ReadFileToUint64(const butil::FilePath file) { - std::string file_as_string; - if (!ReadFileToString(file, &file_as_string)) - return 0; - butil::TrimWhitespaceASCII(file_as_string, butil::TRIM_ALL, &file_as_string); - uint64_t file_as_uint64 = 0; - if (!butil::StringToUint64(file_as_string, &file_as_uint64)) - return 0; - return file_as_uint64; -} -#endif - -// Read /proc//status and returns the value for |field|, or 0 on failure. -// Only works for fields in the form of "Field: value kB". -size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) { - FilePath stat_file = internal::GetProcPidDir(pid).Append("status"); - std::string status; - { - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - if (!ReadFileToString(stat_file, &status)) - return 0; - } - - StringTokenizer tokenizer(status, ":\n"); - ParsingState state = KEY_NAME; - StringPiece last_key_name; - while (tokenizer.GetNext()) { - switch (state) { - case KEY_NAME: - last_key_name = tokenizer.token_piece(); - state = KEY_VALUE; - break; - case KEY_VALUE: - DCHECK(!last_key_name.empty()); - if (last_key_name == field) { - std::string value_str; - tokenizer.token_piece().CopyToString(&value_str); - std::string value_str_trimmed; - butil::TrimWhitespaceASCII(value_str, butil::TRIM_ALL, - &value_str_trimmed); - std::vector split_value_str; - SplitString(value_str_trimmed, ' ', &split_value_str); - if (split_value_str.size() != 2 || split_value_str[1] != "kB") { - NOTREACHED(); - return 0; - } - size_t value; - if (!StringToSizeT(split_value_str[0], &value)) { - NOTREACHED(); - return 0; - } - return value; - } - state = KEY_NAME; - break; - } - } - NOTREACHED(); - return 0; -} - -// Get the total CPU of a single process. Return value is number of jiffies -// on success or -1 on error. -int GetProcessCPU(pid_t pid) { - // Use /proc//task to find all threads and parse their /stat file. - FilePath task_path = internal::GetProcPidDir(pid).Append("task"); - - DIR* dir = opendir(task_path.value().c_str()); - if (!dir) { - DPLOG(ERROR) << "opendir(" << task_path.value() << ")"; - return -1; - } - - int total_cpu = 0; - while (struct dirent* ent = readdir(dir)) { - pid_t tid = internal::ProcDirSlotToPid(ent->d_name); - if (!tid) - continue; - - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - - std::string stat; - FilePath stat_path = - task_path.Append(ent->d_name).Append(internal::kStatFile); - if (ReadFileToString(stat_path, &stat)) { - int cpu = ParseProcStatCPU(stat); - if (cpu > 0) - total_cpu += cpu; - } - } - closedir(dir); - - return total_cpu; -} - -} // namespace - -// static -ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { - return new ProcessMetrics(process); -} - -// On linux, we return vsize. -size_t ProcessMetrics::GetPagefileUsage() const { - return internal::ReadProcStatsAndGetFieldAsSizeT(process_, - internal::VM_VSIZE); -} - -// On linux, we return the high water mark of vsize. -size_t ProcessMetrics::GetPeakPagefileUsage() const { - return ReadProcStatusAndGetFieldAsSizeT(process_, "VmPeak") * 1024; -} - -// On linux, we return RSS. -size_t ProcessMetrics::GetWorkingSetSize() const { - return internal::ReadProcStatsAndGetFieldAsSizeT(process_, internal::VM_RSS) * - getpagesize(); -} - -// On linux, we return the high water mark of RSS. -size_t ProcessMetrics::GetPeakWorkingSetSize() const { - return ReadProcStatusAndGetFieldAsSizeT(process_, "VmHWM") * 1024; -} - -bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, - size_t* shared_bytes) { - WorkingSetKBytes ws_usage; - if (!GetWorkingSetKBytes(&ws_usage)) - return false; - - if (private_bytes) - *private_bytes = ws_usage.priv * 1024; - - if (shared_bytes) - *shared_bytes = ws_usage.shared * 1024; - - return true; -} - -bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { -#if defined(OS_CHROMEOS) - if (GetWorkingSetKBytesTotmaps(ws_usage)) - return true; -#endif - return GetWorkingSetKBytesStatm(ws_usage); -} - -double ProcessMetrics::GetCPUUsage() { - TimeTicks time = TimeTicks::Now(); - - if (last_cpu_ == 0) { - // First call, just set the last values. - last_cpu_time_ = time; - last_cpu_ = GetProcessCPU(process_); - return 0; - } - - int64_t time_delta = (time - last_cpu_time_).InMicroseconds(); - DCHECK_NE(time_delta, 0); - if (time_delta == 0) - return 0; - - int cpu = GetProcessCPU(process_); - - // We have the number of jiffies in the time period. Convert to percentage. - // Note this means we will go *over* 100 in the case where multiple threads - // are together adding to more than one CPU's worth. - TimeDelta cpu_time = internal::ClockTicksToTimeDelta(cpu); - TimeDelta last_cpu_time = internal::ClockTicksToTimeDelta(last_cpu_); - int percentage = (int)(100 * (cpu_time - last_cpu_time).InSecondsF() / - TimeDelta::FromMicroseconds(time_delta).InSecondsF()); - - last_cpu_time_ = time; - last_cpu_ = cpu; - - return percentage; -} - -// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING -// in your kernel configuration. -bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - - std::string proc_io_contents; - FilePath io_file = internal::GetProcPidDir(process_).Append("io"); - if (!ReadFileToString(io_file, &proc_io_contents)) - return false; - - (*io_counters).OtherOperationCount = 0; - (*io_counters).OtherTransferCount = 0; - - StringTokenizer tokenizer(proc_io_contents, ": \n"); - ParsingState state = KEY_NAME; - StringPiece last_key_name; - while (tokenizer.GetNext()) { - switch (state) { - case KEY_NAME: - last_key_name = tokenizer.token_piece(); - state = KEY_VALUE; - break; - case KEY_VALUE: - DCHECK(!last_key_name.empty()); - if (last_key_name == "syscr") { - StringToInt64(tokenizer.token_piece(), - reinterpret_cast(&(*io_counters).ReadOperationCount)); - } else if (last_key_name == "syscw") { - StringToInt64(tokenizer.token_piece(), - reinterpret_cast(&(*io_counters).WriteOperationCount)); - } else if (last_key_name == "rchar") { - StringToInt64(tokenizer.token_piece(), - reinterpret_cast(&(*io_counters).ReadTransferCount)); - } else if (last_key_name == "wchar") { - StringToInt64(tokenizer.token_piece(), - reinterpret_cast(&(*io_counters).WriteTransferCount)); - } - state = KEY_NAME; - break; - } - } - return true; -} - -ProcessMetrics::ProcessMetrics(ProcessHandle process) - : process_(process), - last_system_time_(0), - last_cpu_(0) { - processor_count_ = butil::SysInfo::NumberOfProcessors(); -} - -#if defined(OS_CHROMEOS) -// Private, Shared and Proportional working set sizes are obtained from -// /proc//totmaps -bool ProcessMetrics::GetWorkingSetKBytesTotmaps(WorkingSetKBytes *ws_usage) - const { - // The format of /proc//totmaps is: - // - // Rss: 6120 kB - // Pss: 3335 kB - // Shared_Clean: 1008 kB - // Shared_Dirty: 4012 kB - // Private_Clean: 4 kB - // Private_Dirty: 1096 kB - // Referenced: XXX kB - // Anonymous: XXX kB - // AnonHugePages: XXX kB - // Swap: XXX kB - // Locked: XXX kB - const size_t kPssIndex = (1 * 3) + 1; - const size_t kPrivate_CleanIndex = (4 * 3) + 1; - const size_t kPrivate_DirtyIndex = (5 * 3) + 1; - const size_t kSwapIndex = (9 * 3) + 1; - - std::string totmaps_data; - { - FilePath totmaps_file = internal::GetProcPidDir(process_).Append("totmaps"); - ThreadRestrictions::ScopedAllowIO allow_io; - bool ret = ReadFileToString(totmaps_file, &totmaps_data); - if (!ret || totmaps_data.length() == 0) - return false; - } - - std::vector totmaps_fields; - SplitStringAlongWhitespace(totmaps_data, &totmaps_fields); - - DCHECK_EQ("Pss:", totmaps_fields[kPssIndex-1]); - DCHECK_EQ("Private_Clean:", totmaps_fields[kPrivate_CleanIndex - 1]); - DCHECK_EQ("Private_Dirty:", totmaps_fields[kPrivate_DirtyIndex - 1]); - DCHECK_EQ("Swap:", totmaps_fields[kSwapIndex-1]); - - int pss = 0; - int private_clean = 0; - int private_dirty = 0; - int swap = 0; - bool ret = true; - ret &= StringToInt(totmaps_fields[kPssIndex], &pss); - ret &= StringToInt(totmaps_fields[kPrivate_CleanIndex], &private_clean); - ret &= StringToInt(totmaps_fields[kPrivate_DirtyIndex], &private_dirty); - ret &= StringToInt(totmaps_fields[kSwapIndex], &swap); - - // On ChromeOS swap is to zram. We count this as private / shared, as - // increased swap decreases available RAM to user processes, which would - // otherwise create surprising results. - ws_usage->priv = private_clean + private_dirty + swap; - ws_usage->shared = pss + swap; - ws_usage->shareable = 0; - ws_usage->swapped = swap; - return ret; -} -#endif - -// Private and Shared working set sizes are obtained from /proc//statm. -bool ProcessMetrics::GetWorkingSetKBytesStatm(WorkingSetKBytes* ws_usage) - const { - // Use statm instead of smaps because smaps is: - // a) Large and slow to parse. - // b) Unavailable in the SUID sandbox. - - // First we need to get the page size, since everything is measured in pages. - // For details, see: man 5 proc. - const int page_size_kb = getpagesize() / 1024; - if (page_size_kb <= 0) - return false; - - std::string statm; - { - FilePath statm_file = internal::GetProcPidDir(process_).Append("statm"); - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - bool ret = ReadFileToString(statm_file, &statm); - if (!ret || statm.length() == 0) - return false; - } - - std::vector statm_vec; - SplitString(statm, ' ', &statm_vec); - if (statm_vec.size() != 7) - return false; // Not the format we expect. - - int statm_rss, statm_shared; - bool ret = true; - ret &= StringToInt(statm_vec[1], &statm_rss); - ret &= StringToInt(statm_vec[2], &statm_shared); - - ws_usage->priv = (statm_rss - statm_shared) * page_size_kb; - ws_usage->shared = statm_shared * page_size_kb; - - // Sharable is not calculated, as it does not provide interesting data. - ws_usage->shareable = 0; - -#if defined(OS_CHROMEOS) - // Can't get swapped memory from statm. - ws_usage->swapped = 0; -#endif - - return ret; -} - -size_t GetSystemCommitCharge() { - SystemMemoryInfoKB meminfo; - if (!GetSystemMemoryInfo(&meminfo)) - return 0; - return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached; -} - -// Exposed for testing. -int ParseProcStatCPU(const std::string& input) { - std::vector proc_stats; - if (!internal::ParseProcStats(input, &proc_stats)) - return -1; - - if (proc_stats.size() <= internal::VM_STIME) - return -1; - int utime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME); - int stime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME); - return utime + stime; -} - -const char kProcSelfExe[] = "/proc/self/exe"; - -int GetNumberOfThreads(ProcessHandle process) { - return internal::ReadProcStatsAndGetFieldAsInt64(process, - internal::VM_NUMTHREADS); -} - -namespace { - -// The format of /proc/diskstats is: -// Device major number -// Device minor number -// Device name -// Field 1 -- # of reads completed -// This is the total number of reads completed successfully. -// Field 2 -- # of reads merged, field 6 -- # of writes merged -// Reads and writes which are adjacent to each other may be merged for -// efficiency. Thus two 4K reads may become one 8K read before it is -// ultimately handed to the disk, and so it will be counted (and queued) -// as only one I/O. This field lets you know how often this was done. -// Field 3 -- # of sectors read -// This is the total number of sectors read successfully. -// Field 4 -- # of milliseconds spent reading -// This is the total number of milliseconds spent by all reads (as -// measured from __make_request() to end_that_request_last()). -// Field 5 -- # of writes completed -// This is the total number of writes completed successfully. -// Field 6 -- # of writes merged -// See the description of field 2. -// Field 7 -- # of sectors written -// This is the total number of sectors written successfully. -// Field 8 -- # of milliseconds spent writing -// This is the total number of milliseconds spent by all writes (as -// measured from __make_request() to end_that_request_last()). -// Field 9 -- # of I/Os currently in progress -// The only field that should go to zero. Incremented as requests are -// given to appropriate struct request_queue and decremented as they -// finish. -// Field 10 -- # of milliseconds spent doing I/Os -// This field increases so long as field 9 is nonzero. -// Field 11 -- weighted # of milliseconds spent doing I/Os -// This field is incremented at each I/O start, I/O completion, I/O -// merge, or read of these stats by the number of I/Os in progress -// (field 9) times the number of milliseconds spent doing I/O since the -// last update of this field. This can provide an easy measure of both -// I/O completion time and the backlog that may be accumulating. - -const size_t kDiskDriveName = 2; -const size_t kDiskReads = 3; -const size_t kDiskReadsMerged = 4; -const size_t kDiskSectorsRead = 5; -const size_t kDiskReadTime = 6; -const size_t kDiskWrites = 7; -const size_t kDiskWritesMerged = 8; -const size_t kDiskSectorsWritten = 9; -const size_t kDiskWriteTime = 10; -const size_t kDiskIO = 11; -const size_t kDiskIOTime = 12; -const size_t kDiskWeightedIOTime = 13; - -} // namespace - -SystemMemoryInfoKB::SystemMemoryInfoKB() { - total = 0; - free = 0; - buffers = 0; - cached = 0; - active_anon = 0; - inactive_anon = 0; - active_file = 0; - inactive_file = 0; - swap_total = 0; - swap_free = 0; - dirty = 0; - - pswpin = 0; - pswpout = 0; - pgmajfault = 0; - -#ifdef OS_CHROMEOS - shmem = 0; - slab = 0; - gem_objects = -1; - gem_size = -1; -#endif -} - -// exposed for testing -bool ParseProcMeminfo(const std::string& meminfo_data, - SystemMemoryInfoKB* meminfo) { - // The format of /proc/meminfo is: - // - // MemTotal: 8235324 kB - // MemFree: 1628304 kB - // Buffers: 429596 kB - // Cached: 4728232 kB - // ... - // There is no guarantee on the ordering or position - // though it doesn't appear to change very often - - // As a basic sanity check, let's make sure we at least get non-zero - // MemTotal value - meminfo->total = 0; - - std::vector meminfo_lines; - Tokenize(meminfo_data, "\n", &meminfo_lines); - for (std::vector::iterator it = meminfo_lines.begin(); - it != meminfo_lines.end(); ++it) { - std::vector tokens; - SplitStringAlongWhitespace(*it, &tokens); - // HugePages_* only has a number and no suffix so we can't rely on - // there being exactly 3 tokens. - if (tokens.size() > 1) { - if (tokens[0] == "MemTotal:") { - StringToInt(tokens[1], &meminfo->total); - continue; - } if (tokens[0] == "MemFree:") { - StringToInt(tokens[1], &meminfo->free); - continue; - } if (tokens[0] == "Buffers:") { - StringToInt(tokens[1], &meminfo->buffers); - continue; - } if (tokens[0] == "Cached:") { - StringToInt(tokens[1], &meminfo->cached); - continue; - } if (tokens[0] == "Active(anon):") { - StringToInt(tokens[1], &meminfo->active_anon); - continue; - } if (tokens[0] == "Inactive(anon):") { - StringToInt(tokens[1], &meminfo->inactive_anon); - continue; - } if (tokens[0] == "Active(file):") { - StringToInt(tokens[1], &meminfo->active_file); - continue; - } if (tokens[0] == "Inactive(file):") { - StringToInt(tokens[1], &meminfo->inactive_file); - continue; - } if (tokens[0] == "SwapTotal:") { - StringToInt(tokens[1], &meminfo->swap_total); - continue; - } if (tokens[0] == "SwapFree:") { - StringToInt(tokens[1], &meminfo->swap_free); - continue; - } if (tokens[0] == "Dirty:") { - StringToInt(tokens[1], &meminfo->dirty); - continue; -#if defined(OS_CHROMEOS) - // Chrome OS has a tweaked kernel that allows us to query Shmem, which is - // usually video memory otherwise invisible to the OS. - } if (tokens[0] == "Shmem:") { - StringToInt(tokens[1], &meminfo->shmem); - continue; - } if (tokens[0] == "Slab:") { - StringToInt(tokens[1], &meminfo->slab); - continue; -#endif - } - } else - DLOG(WARNING) << "meminfo: tokens: " << tokens.size() - << " malformed line: " << *it; - } - - // Make sure we got a valid MemTotal. - if (!meminfo->total) - return false; - - return true; -} - -// exposed for testing -bool ParseProcVmstat(const std::string& vmstat_data, - SystemMemoryInfoKB* meminfo) { - // The format of /proc/vmstat is: - // - // nr_free_pages 299878 - // nr_inactive_anon 239863 - // nr_active_anon 1318966 - // nr_inactive_file 2015629 - // ... - // - // We iterate through the whole file because the position of the - // fields are dependent on the kernel version and configuration. - - std::vector vmstat_lines; - Tokenize(vmstat_data, "\n", &vmstat_lines); - for (std::vector::iterator it = vmstat_lines.begin(); - it != vmstat_lines.end(); ++it) { - std::vector tokens; - SplitString(*it, ' ', &tokens); - if (tokens.size() == 2) { - if (tokens[0] == "pswpin") { - StringToInt(tokens[1], &meminfo->pswpin); - continue; - } if (tokens[0] == "pswpout") { - StringToInt(tokens[1], &meminfo->pswpout); - continue; - } if (tokens[0] == "pgmajfault") - StringToInt(tokens[1], &meminfo->pgmajfault); - } - } - - return true; -} - -bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) { - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - - // Used memory is: total - free - buffers - caches - FilePath meminfo_file("/proc/meminfo"); - std::string meminfo_data; - if (!ReadFileToString(meminfo_file, &meminfo_data)) { - DLOG(WARNING) << "Failed to open " << meminfo_file.value(); - return false; - } - - if (!ParseProcMeminfo(meminfo_data, meminfo)) { - DLOG(WARNING) << "Failed to parse " << meminfo_file.value(); - return false; - } - -#if defined(OS_CHROMEOS) - // Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a - // bind mount into /sys/kernel/debug and synchronously reading the in-memory - // files in /sys is fast. -#if defined(ARCH_CPU_ARM_FAMILY) - FilePath geminfo_file("/var/run/debugfs_gpu/exynos_gem_objects"); -#else - FilePath geminfo_file("/var/run/debugfs_gpu/i915_gem_objects"); -#endif - std::string geminfo_data; - meminfo->gem_objects = -1; - meminfo->gem_size = -1; - if (ReadFileToString(geminfo_file, &geminfo_data)) { - int gem_objects = -1; - long long gem_size = -1; - int num_res = sscanf(geminfo_data.c_str(), - "%d objects, %lld bytes", - &gem_objects, &gem_size); - if (num_res == 2) { - meminfo->gem_objects = gem_objects; - meminfo->gem_size = gem_size; - } - } - -#if defined(ARCH_CPU_ARM_FAMILY) - // Incorporate Mali graphics memory if present. - FilePath mali_memory_file("/sys/class/misc/mali0/device/memory"); - std::string mali_memory_data; - if (ReadFileToString(mali_memory_file, &mali_memory_data)) { - long long mali_size = -1; - int num_res = sscanf(mali_memory_data.c_str(), "%lld bytes", &mali_size); - if (num_res == 1) - meminfo->gem_size += mali_size; - } -#endif // defined(ARCH_CPU_ARM_FAMILY) -#endif // defined(OS_CHROMEOS) - - FilePath vmstat_file("/proc/vmstat"); - std::string vmstat_data; - if (!ReadFileToString(vmstat_file, &vmstat_data)) { - DLOG(WARNING) << "Failed to open " << vmstat_file.value(); - return false; - } - if (!ParseProcVmstat(vmstat_data, meminfo)) { - DLOG(WARNING) << "Failed to parse " << vmstat_file.value(); - return false; - } - - return true; -} - -SystemDiskInfo::SystemDiskInfo() { - reads = 0; - reads_merged = 0; - sectors_read = 0; - read_time = 0; - writes = 0; - writes_merged = 0; - sectors_written = 0; - write_time = 0; - io = 0; - io_time = 0; - weighted_io_time = 0; -} - -bool IsValidDiskName(const std::string& candidate) { - if (candidate.length() < 3) - return false; - if (candidate.substr(0,2) == "sd" || candidate.substr(0,2) == "hd") { - // [sh]d[a-z]+ case - for (size_t i = 2; i < candidate.length(); i++) { - if (!islower(candidate[i])) - return false; - } - } else { - if (candidate.length() < 7) { - return false; - } - if (candidate.substr(0,6) == "mmcblk") { - // mmcblk[0-9]+ case - for (size_t i = 6; i < candidate.length(); i++) { - if (!isdigit(candidate[i])) - return false; - } - } else { - return false; - } - } - - return true; -} - -bool GetSystemDiskInfo(SystemDiskInfo* diskinfo) { - // Synchronously reading files in /proc is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - - FilePath diskinfo_file("/proc/diskstats"); - std::string diskinfo_data; - if (!ReadFileToString(diskinfo_file, &diskinfo_data)) { - DLOG(WARNING) << "Failed to open " << diskinfo_file.value(); - return false; - } - - std::vector diskinfo_lines; - size_t line_count = Tokenize(diskinfo_data, "\n", &diskinfo_lines); - if (line_count == 0) { - DLOG(WARNING) << "No lines found"; - return false; - } - - diskinfo->reads = 0; - diskinfo->reads_merged = 0; - diskinfo->sectors_read = 0; - diskinfo->read_time = 0; - diskinfo->writes = 0; - diskinfo->writes_merged = 0; - diskinfo->sectors_written = 0; - diskinfo->write_time = 0; - diskinfo->io = 0; - diskinfo->io_time = 0; - diskinfo->weighted_io_time = 0; - - uint64_t reads = 0; - uint64_t reads_merged = 0; - uint64_t sectors_read = 0; - uint64_t read_time = 0; - uint64_t writes = 0; - uint64_t writes_merged = 0; - uint64_t sectors_written = 0; - uint64_t write_time = 0; - uint64_t io = 0; - uint64_t io_time = 0; - uint64_t weighted_io_time = 0; - - for (size_t i = 0; i < line_count; i++) { - std::vector disk_fields; - SplitStringAlongWhitespace(diskinfo_lines[i], &disk_fields); - - // Fields may have overflowed and reset to zero. - if (IsValidDiskName(disk_fields[kDiskDriveName])) { - StringToUint64(disk_fields[kDiskReads], &reads); - StringToUint64(disk_fields[kDiskReadsMerged], &reads_merged); - StringToUint64(disk_fields[kDiskSectorsRead], §ors_read); - StringToUint64(disk_fields[kDiskReadTime], &read_time); - StringToUint64(disk_fields[kDiskWrites], &writes); - StringToUint64(disk_fields[kDiskWritesMerged], &writes_merged); - StringToUint64(disk_fields[kDiskSectorsWritten], §ors_written); - StringToUint64(disk_fields[kDiskWriteTime], &write_time); - StringToUint64(disk_fields[kDiskIO], &io); - StringToUint64(disk_fields[kDiskIOTime], &io_time); - StringToUint64(disk_fields[kDiskWeightedIOTime], &weighted_io_time); - - diskinfo->reads += reads; - diskinfo->reads_merged += reads_merged; - diskinfo->sectors_read += sectors_read; - diskinfo->read_time += read_time; - diskinfo->writes += writes; - diskinfo->writes_merged += writes_merged; - diskinfo->sectors_written += sectors_written; - diskinfo->write_time += write_time; - diskinfo->io += io; - diskinfo->io_time += io_time; - diskinfo->weighted_io_time += weighted_io_time; - } - } - - return true; -} - -#if defined(OS_CHROMEOS) -void GetSwapInfo(SwapInfo* swap_info) { - // Synchronously reading files in /sys/block/zram0 is safe. - ThreadRestrictions::ScopedAllowIO allow_io; - - butil::FilePath zram_path("/sys/block/zram0"); - uint64_t orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size")); - if (orig_data_size <= 4096) { - // A single page is compressed at startup, and has a high compression - // ratio. We ignore this as it doesn't indicate any real swapping. - swap_info->orig_data_size = 0; - swap_info->num_reads = 0; - swap_info->num_writes = 0; - swap_info->compr_data_size = 0; - swap_info->mem_used_total = 0; - return; - } - swap_info->orig_data_size = orig_data_size; - swap_info->num_reads = ReadFileToUint64(zram_path.Append("num_reads")); - swap_info->num_writes = ReadFileToUint64(zram_path.Append("num_writes")); - swap_info->compr_data_size = - ReadFileToUint64(zram_path.Append("compr_data_size")); - swap_info->mem_used_total = - ReadFileToUint64(zram_path.Append("mem_used_total")); -} -#endif // defined(OS_CHROMEOS) - -} // namespace butil diff --git a/src/butil/process/process_metrics_mac.cc b/src/butil/process/process_metrics_mac.cc deleted file mode 100644 index ea0d035f79..0000000000 --- a/src/butil/process/process_metrics_mac.cc +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_metrics.h" - -#include -#include -#include -#include - -#include "butil/containers/hash_tables.h" -#include "butil/logging.h" -#include "butil/mac/mach_logging.h" -#include "butil/mac/scoped_mach_port.h" -#include "butil/sys_info.h" - -#if !defined(TASK_POWER_INFO) -// Doesn't exist in the 10.6 or 10.7 SDKs. -#define TASK_POWER_INFO 21 -struct task_power_info { - uint64_t total_user; - uint64_t total_system; - uint64_t task_interrupt_wakeups; - uint64_t task_platform_idle_wakeups; - uint64_t task_timer_wakeups_bin_1; - uint64_t task_timer_wakeups_bin_2; -}; -typedef struct task_power_info task_power_info_data_t; -typedef struct task_power_info *task_power_info_t; -#define TASK_POWER_INFO_COUNT ((mach_msg_type_number_t) \ - (sizeof (task_power_info_data_t) / sizeof (natural_t))) -#endif - -namespace butil { - -namespace { - -bool GetTaskInfo(mach_port_t task, task_basic_info_64* task_info_data) { - if (task == MACH_PORT_NULL) - return false; - mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; - kern_return_t kr = task_info(task, - TASK_BASIC_INFO_64, - reinterpret_cast(task_info_data), - &count); - // Most likely cause for failure: |task| is a zombie. - return kr == KERN_SUCCESS; -} - -bool GetCPUTypeForProcess(pid_t pid, cpu_type_t* cpu_type) { - size_t len = sizeof(*cpu_type); - int result = sysctlbyname("sysctl.proc_cputype", - cpu_type, - &len, - NULL, - 0); - if (result != 0) { - DPLOG(ERROR) << "sysctlbyname(""sysctl.proc_cputype"")"; - return false; - } - - return true; -} - -bool IsAddressInSharedRegion(mach_vm_address_t addr, cpu_type_t type) { - if (type == CPU_TYPE_I386) { - return addr >= SHARED_REGION_BASE_I386 && - addr < (SHARED_REGION_BASE_I386 + SHARED_REGION_SIZE_I386); - } else if (type == CPU_TYPE_X86_64) { - return addr >= SHARED_REGION_BASE_X86_64 && - addr < (SHARED_REGION_BASE_X86_64 + SHARED_REGION_SIZE_X86_64); - } else { - return false; - } -} - -} // namespace - -// Getting a mach task from a pid for another process requires permissions in -// general, so there doesn't really seem to be a way to do these (and spinning -// up ps to fetch each stats seems dangerous to put in a base api for anyone to -// call). Child processes ipc their port, so return something if available, -// otherwise return 0. - -// static -ProcessMetrics* ProcessMetrics::CreateProcessMetrics( - ProcessHandle process, - ProcessMetrics::PortProvider* port_provider) { - return new ProcessMetrics(process, port_provider); -} - -size_t ProcessMetrics::GetPagefileUsage() const { - task_basic_info_64 task_info_data; - if (!GetTaskInfo(TaskForPid(process_), &task_info_data)) - return 0; - return task_info_data.virtual_size; -} - -size_t ProcessMetrics::GetPeakPagefileUsage() const { - return 0; -} - -size_t ProcessMetrics::GetWorkingSetSize() const { - task_basic_info_64 task_info_data; - if (!GetTaskInfo(TaskForPid(process_), &task_info_data)) - return 0; - return task_info_data.resident_size; -} - -size_t ProcessMetrics::GetPeakWorkingSetSize() const { - return 0; -} - -// This is a rough approximation of the algorithm that libtop uses. -// private_bytes is the size of private resident memory. -// shared_bytes is the size of shared resident memory. -bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, - size_t* shared_bytes) { - size_t private_pages_count = 0; - size_t shared_pages_count = 0; - - if (!private_bytes && !shared_bytes) - return true; - - mach_port_t task = TaskForPid(process_); - if (task == MACH_PORT_NULL) { - DLOG(ERROR) << "Invalid process"; - return false; - } - - cpu_type_t cpu_type; - if (!GetCPUTypeForProcess(process_, &cpu_type)) - return false; - - // The same region can be referenced multiple times. To avoid double counting - // we need to keep track of which regions we've already counted. - butil::hash_set seen_objects; - - // We iterate through each VM region in the task's address map. For shared - // memory we add up all the pages that are marked as shared. Like libtop we - // try to avoid counting pages that are also referenced by other tasks. Since - // we don't have access to the VM regions of other tasks the only hint we have - // is if the address is in the shared region area. - // - // Private memory is much simpler. We simply count the pages that are marked - // as private or copy on write (COW). - // - // See libtop_update_vm_regions in - // http://www.opensource.apple.com/source/top/top-67/libtop.c - mach_vm_size_t size = 0; - for (mach_vm_address_t address = MACH_VM_MIN_ADDRESS;; address += size) { - vm_region_top_info_data_t info; - mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT; - mach_port_t object_name; - kern_return_t kr = mach_vm_region(task, - &address, - &size, - VM_REGION_TOP_INFO, - reinterpret_cast(&info), - &info_count, - &object_name); - if (kr == KERN_INVALID_ADDRESS) { - // We're at the end of the address space. - break; - } else if (kr != KERN_SUCCESS) { - MACH_DLOG(ERROR, kr) << "mach_vm_region"; - return false; - } - - // The kernel always returns a null object for VM_REGION_TOP_INFO, but - // balance it with a deallocate in case this ever changes. See 10.9.2 - // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region. - mach_port_deallocate(mach_task_self(), object_name); - - if (IsAddressInSharedRegion(address, cpu_type) && - info.share_mode != SM_PRIVATE) - continue; - - if (info.share_mode == SM_COW && info.ref_count == 1) - info.share_mode = SM_PRIVATE; - - switch (info.share_mode) { - case SM_PRIVATE: - private_pages_count += info.private_pages_resident; - private_pages_count += info.shared_pages_resident; - break; - case SM_COW: - private_pages_count += info.private_pages_resident; - // Fall through - case SM_SHARED: - if (seen_objects.count(info.obj_id) == 0) { - // Only count the first reference to this region. - seen_objects.insert(info.obj_id); - shared_pages_count += info.shared_pages_resident; - } - break; - default: - break; - } - } - - if (private_bytes) - *private_bytes = private_pages_count * PAGE_SIZE; - if (shared_bytes) - *shared_bytes = shared_pages_count * PAGE_SIZE; - - return true; -} - -void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const { -} - -bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { - size_t priv = GetWorkingSetSize(); - if (!priv) - return false; - ws_usage->priv = priv / 1024; - ws_usage->shareable = 0; - ws_usage->shared = 0; - return true; -} - -#define TIME_VALUE_TO_TIMEVAL(a, r) do { \ - (r)->tv_sec = (a)->seconds; \ - (r)->tv_usec = (a)->microseconds; \ -} while (0) - -double ProcessMetrics::GetCPUUsage() { - mach_port_t task = TaskForPid(process_); - if (task == MACH_PORT_NULL) - return 0; - - // Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage() - // in libtop.c), but this is more concise and gives the same results: - task_thread_times_info thread_info_data; - mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT; - kern_return_t kr = task_info(task, - TASK_THREAD_TIMES_INFO, - reinterpret_cast(&thread_info_data), - &thread_info_count); - if (kr != KERN_SUCCESS) { - // Most likely cause: |task| is a zombie. - return 0; - } - - task_basic_info_64 task_info_data; - if (!GetTaskInfo(task, &task_info_data)) - return 0; - - /* Set total_time. */ - // thread info contains live time... - struct timeval user_timeval, system_timeval, task_timeval; - TIME_VALUE_TO_TIMEVAL(&thread_info_data.user_time, &user_timeval); - TIME_VALUE_TO_TIMEVAL(&thread_info_data.system_time, &system_timeval); - timeradd(&user_timeval, &system_timeval, &task_timeval); - - // ... task info contains terminated time. - TIME_VALUE_TO_TIMEVAL(&task_info_data.user_time, &user_timeval); - TIME_VALUE_TO_TIMEVAL(&task_info_data.system_time, &system_timeval); - timeradd(&user_timeval, &task_timeval, &task_timeval); - timeradd(&system_timeval, &task_timeval, &task_timeval); - - TimeTicks time = TimeTicks::Now(); - int64_t task_time = TimeValToMicroseconds(task_timeval); - - if (last_system_time_ == 0) { - // First call, just set the last values. - last_cpu_time_ = time; - last_system_time_ = task_time; - return 0; - } - - int64_t system_time_delta = task_time - last_system_time_; - int64_t time_delta = (time - last_cpu_time_).InMicroseconds(); - DCHECK_NE(0U, time_delta); - if (time_delta == 0) - return 0; - - last_cpu_time_ = time; - last_system_time_ = task_time; - - return static_cast(system_time_delta * 100.0) / time_delta; -} - -int ProcessMetrics::GetIdleWakeupsPerSecond() { - mach_port_t task = TaskForPid(process_); - if (task == MACH_PORT_NULL) - return 0; - - task_power_info power_info_data; - mach_msg_type_number_t power_info_count = TASK_POWER_INFO_COUNT; - kern_return_t kr = task_info(task, - TASK_POWER_INFO, - reinterpret_cast(&power_info_data), - &power_info_count); - if (kr != KERN_SUCCESS) { - // Most likely cause: |task| is a zombie, or this is on a pre-10.8.4 system - // where TASK_POWER_INFO isn't supported yet. - return 0; - } - uint64_t absolute_idle_wakeups = power_info_data.task_platform_idle_wakeups; - - TimeTicks time = TimeTicks::Now(); - - if (last_absolute_idle_wakeups_ == 0) { - // First call, just set the last values. - last_idle_wakeups_time_ = time; - last_absolute_idle_wakeups_ = absolute_idle_wakeups; - return 0; - } - - int64_t wakeups_delta = absolute_idle_wakeups - last_absolute_idle_wakeups_; - int64_t time_delta = (time - last_idle_wakeups_time_).InMicroseconds(); - DCHECK_NE(0U, time_delta); - if (time_delta == 0) - return 0; - - last_idle_wakeups_time_ = time; - last_absolute_idle_wakeups_ = absolute_idle_wakeups; - - // Round to average wakeups per second. - const int kMicrosecondsPerSecond = 1000 * 1000; - return (wakeups_delta * kMicrosecondsPerSecond + time_delta/2) / time_delta; -} - -bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { - return false; -} - -ProcessMetrics::ProcessMetrics(ProcessHandle process, - ProcessMetrics::PortProvider* port_provider) - : process_(process), - last_system_time_(0), - last_absolute_idle_wakeups_(0), - port_provider_(port_provider) { - processor_count_ = SysInfo::NumberOfProcessors(); -} - -mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const { - mach_port_t task = MACH_PORT_NULL; - if (port_provider_) - task = port_provider_->TaskForPid(process_); - if (task == MACH_PORT_NULL && process_ == getpid()) - task = mach_task_self(); - return task; -} - -// Bytes committed by the system. -size_t GetSystemCommitCharge() { - butil::mac::ScopedMachSendRight host(mach_host_self()); - mach_msg_type_number_t count = HOST_VM_INFO_COUNT; - vm_statistics_data_t data; - kern_return_t kr = host_statistics(host, HOST_VM_INFO, - reinterpret_cast(&data), - &count); - if (kr != KERN_SUCCESS) { - MACH_DLOG(WARNING, kr) << "host_statistics"; - return 0; - } - - return (data.active_count * PAGE_SIZE) / 1024; -} - -} // namespace butil diff --git a/src/butil/process/process_metrics_openbsd.cc b/src/butil/process/process_metrics_openbsd.cc deleted file mode 100644 index 2f95df3942..0000000000 --- a/src/butil/process/process_metrics_openbsd.cc +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_metrics.h" - -#include -#include - -namespace butil { - -// static -ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { - return new ProcessMetrics(process); -} - -size_t ProcessMetrics::GetPagefileUsage() const { - struct kinfo_proc info; - size_t length; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_, - sizeof(struct kinfo_proc), 0 }; - - if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) - return -1; - - mib[5] = (length / sizeof(struct kinfo_proc)); - - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) - return -1; - - return (info.p_vm_tsize + info.p_vm_dsize + info.p_vm_ssize); -} - -size_t ProcessMetrics::GetPeakPagefileUsage() const { - return 0; -} - -size_t ProcessMetrics::GetWorkingSetSize() const { - struct kinfo_proc info; - size_t length; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_, - sizeof(struct kinfo_proc), 0 }; - - if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) - return -1; - - mib[5] = (length / sizeof(struct kinfo_proc)); - - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) - return -1; - - return info.p_vm_rssize * getpagesize(); -} - -size_t ProcessMetrics::GetPeakWorkingSetSize() const { - return 0; -} - -bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes, - size_t* shared_bytes) { - WorkingSetKBytes ws_usage; - - if (!GetWorkingSetKBytes(&ws_usage)) - return false; - - if (private_bytes) - *private_bytes = ws_usage.priv << 10; - - if (shared_bytes) - *shared_bytes = ws_usage.shared * 1024; - - return true; -} - -bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { - // TODO(bapt): be sure we can't be precise - size_t priv = GetWorkingSetSize(); - if (!priv) - return false; - ws_usage->priv = priv / 1024; - ws_usage->shareable = 0; - ws_usage->shared = 0; - - return true; -} - -bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { - return false; -} - -static int GetProcessCPU(pid_t pid) { - struct kinfo_proc info; - size_t length; - int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid, - sizeof(struct kinfo_proc), 0 }; - - if (sysctl(mib, arraysize(mib), NULL, &length, NULL, 0) < 0) - return -1; - - mib[5] = (length / sizeof(struct kinfo_proc)); - - if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0) - return 0; - - return info.p_pctcpu; -} - -double ProcessMetrics::GetCPUUsage() { - TimeTicks time = TimeTicks::Now(); - - if (last_cpu_ == 0) { - // First call, just set the last values. - last_cpu_time_ = time; - last_cpu_ = GetProcessCPU(process_); - return 0; - } - - int64_t time_delta = (time - last_cpu_time_).InMicroseconds(); - DCHECK_NE(time_delta, 0); - - if (time_delta == 0) - return 0; - - int cpu = GetProcessCPU(process_); - - last_cpu_time_ = time; - last_cpu_ = cpu; - - double percentage = static_cast((cpu * 100.0) / FSCALE); - - return percentage; -} - -ProcessMetrics::ProcessMetrics(ProcessHandle process) - : process_(process), - last_system_time_(0), - last_cpu_(0) { - - processor_count_ = butil::SysInfo::NumberOfProcessors(); -} - -size_t GetSystemCommitCharge() { - int mib[] = { CTL_VM, VM_METER }; - int pagesize; - struct vmtotal vmtotal; - unsigned long mem_total, mem_free, mem_inactive; - size_t len = sizeof(vmtotal); - - if (sysctl(mib, arraysize(mib), &vmtotal, &len, NULL, 0) < 0) - return 0; - - mem_total = vmtotal.t_vm; - mem_free = vmtotal.t_free; - mem_inactive = vmtotal.t_vm - vmtotal.t_avm; - - pagesize = getpagesize(); - - return mem_total - (mem_free*pagesize) - (mem_inactive*pagesize); -} - -} // namespace butil diff --git a/src/butil/process/process_metrics_posix.cc b/src/butil/process/process_metrics_posix.cc deleted file mode 100644 index bcd9386f8c..0000000000 --- a/src/butil/process/process_metrics_posix.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "butil/process/process_metrics.h" - -#include -#include - -#include "butil/logging.h" - -namespace butil { - -int64_t TimeValToMicroseconds(const struct timeval& tv) { - static const int kMicrosecondsPerSecond = 1000000; - int64_t ret = tv.tv_sec; // Avoid (int * int) integer overflow. - ret *= kMicrosecondsPerSecond; - ret += tv.tv_usec; - return ret; -} - -ProcessMetrics::~ProcessMetrics() { } - -#if defined(OS_LINUX) -static const rlim_t kSystemDefaultMaxFds = 8192; -#elif defined(OS_MACOSX) -static const rlim_t kSystemDefaultMaxFds = 256; -#elif defined(OS_SOLARIS) -static const rlim_t kSystemDefaultMaxFds = 8192; -#elif defined(OS_FREEBSD) -static const rlim_t kSystemDefaultMaxFds = 8192; -#elif defined(OS_OPENBSD) -static const rlim_t kSystemDefaultMaxFds = 256; -#elif defined(OS_ANDROID) -static const rlim_t kSystemDefaultMaxFds = 1024; -#endif - -size_t GetMaxFds() { - rlim_t max_fds; - struct rlimit nofile; - if (getrlimit(RLIMIT_NOFILE, &nofile)) { - // getrlimit failed. Take a best guess. - max_fds = kSystemDefaultMaxFds; - RAW_LOG(ERROR, "getrlimit(RLIMIT_NOFILE) failed"); - } else { - max_fds = nofile.rlim_cur; - } - - if (max_fds > INT_MAX) - max_fds = INT_MAX; - - return static_cast(max_fds); -} - - -void SetFdLimit(unsigned int max_descriptors) { - struct rlimit limits; - if (getrlimit(RLIMIT_NOFILE, &limits) == 0) { - unsigned int new_limit = max_descriptors; - if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) { - new_limit = limits.rlim_max; - } - limits.rlim_cur = new_limit; - if (setrlimit(RLIMIT_NOFILE, &limits) != 0) { - PLOG(INFO) << "Failed to set file descriptor limit"; - } - } else { - PLOG(INFO) << "Failed to get file descriptor limit"; - } -} - -} // namespace butil diff --git a/src/butil/process/process_posix.cc b/src/butil/process/process_posix.cc deleted file mode 100644 index 512829e71b..0000000000 --- a/src/butil/process/process_posix.cc +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "butil/process/process.h" - -#include -#include -#include - -#include "butil/logging.h" -#include "butil/process/kill.h" - -namespace butil { - -// static -Process Process::Current() { - return Process(GetCurrentProcessHandle()); -} - -ProcessId Process::pid() const { - if (process_ == 0) - return 0; - - return GetProcId(process_); -} - -bool Process::is_current() const { - return process_ == GetCurrentProcessHandle(); -} - -void Process::Close() { - process_ = 0; - // if the process wasn't terminated (so we waited) or the state - // 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); -} - -#if !defined(OS_LINUX) -bool Process::IsProcessBackgrounded() const { - // See SetProcessBackgrounded(). - return false; -} - -bool Process::SetProcessBackgrounded(bool value) { - // POSIX only allows lowering the priority of a process, so if we - // were to lower it we wouldn't be able to raise it back to its initial - // priority. - return false; -} - -// static -bool Process::CanBackgroundProcesses() { - return false; -} - -#endif - -int Process::GetPriority() const { - DCHECK(process_); - return getpriority(PRIO_PROCESS, process_); -} - -} // namespace butil diff --git a/src/butil/sys_info.cc b/src/butil/sys_info.cc deleted file mode 100644 index eb63ba1c26..0000000000 --- a/src/butil/sys_info.cc +++ /dev/null @@ -1,56 +0,0 @@ -// 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. - -#include "butil/sys_info.h" - -#include "butil/base_switches.h" -#include "butil/command_line.h" -#include "butil/lazy_instance.h" -#include "butil/strings/string_number_conversions.h" -#include "butil/sys_info_internal.h" -#include "butil/time/time.h" - -namespace butil { - -#if !defined(OS_ANDROID) - -static const int kLowMemoryDeviceThresholdMB = 512; - -bool DetectLowEndDevice() { - CommandLine* command_line = CommandLine::ForCurrentProcess(); - int int_value = 0; - if (command_line->HasSwitch(switches::kLowEndDeviceMode)) { - std::string string_value = - command_line->GetSwitchValueASCII(switches::kLowEndDeviceMode); - StringToInt(string_value, &int_value); - } - if (int_value == 1) - return true; - if (int_value != 2) - return false; - - int ram_size_mb = SysInfo::AmountOfPhysicalMemoryMB(); - return (ram_size_mb > 0 && ram_size_mb < kLowMemoryDeviceThresholdMB); -} - -static LazyInstance< - internal::LazySysInfoValue >::Leaky - g_lazy_low_end_device = LAZY_INSTANCE_INITIALIZER; - -// static -bool SysInfo::IsLowEndDevice() { - return g_lazy_low_end_device.Get().value(); -} -#endif - -// static -int64_t SysInfo::Uptime() { - // This code relies on an implementation detail of TimeTicks::Now() - that - // its return value happens to coincide with the system uptime value in - // microseconds, on Win/Mac/iOS/Linux/ChromeOS and Android. - int64_t uptime_in_microseconds = TimeTicks::Now().ToInternalValue(); - return uptime_in_microseconds / 1000; -} - -} // namespace butil diff --git a/src/butil/sys_info.h b/src/butil/sys_info.h deleted file mode 100644 index 06bd972395..0000000000 --- a/src/butil/sys_info.h +++ /dev/null @@ -1,141 +0,0 @@ -// 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_SYS_INFO_H_ -#define BASE_SYS_INFO_H_ - -#include -#include - -#include "butil/base_export.h" -#include "butil/basictypes.h" -#include "butil/files/file_path.h" -#include "butil/time/time.h" -#include "butil/build_config.h" - -namespace butil { - -class BASE_EXPORT SysInfo { - public: - // Return the number of logical processors/cores on the current machine. - static int NumberOfProcessors(); - - // Return the number of bytes of physical memory on the current machine. - static int64_t AmountOfPhysicalMemory(); - - // Return the number of bytes of current available physical memory on the - // machine. - static int64_t AmountOfAvailablePhysicalMemory(); - - // Return the number of bytes of virtual memory of this process. A return - // value of zero means that there is no limit on the available virtual - // memory. - static int64_t AmountOfVirtualMemory(); - - // Return the number of megabytes of physical memory on the current machine. - static int AmountOfPhysicalMemoryMB() { - return static_cast(AmountOfPhysicalMemory() / 1024 / 1024); - } - - // Return the number of megabytes of available virtual memory, or zero if it - // is unlimited. - static int AmountOfVirtualMemoryMB() { - return static_cast(AmountOfVirtualMemory() / 1024 / 1024); - } - - // Return the available disk space in bytes on the volume containing |path|, - // or -1 on failure. - static int64_t AmountOfFreeDiskSpace(const FilePath& path); - - // Returns system uptime in milliseconds. - static int64_t Uptime(); - - // Returns the name of the host operating system. - static std::string OperatingSystemName(); - - // Returns the version of the host operating system. - static std::string OperatingSystemVersion(); - - // Retrieves detailed numeric values for the OS version. - // TODO(port): Implement a Linux version of this method and enable the - // corresponding unit test. - // DON'T USE THIS ON THE MAC OR WINDOWS to determine the current OS release - // for OS version-specific feature checks and workarounds. If you must use - // an OS version check instead of a feature check, use the butil::mac::IsOS* - // family from butil/mac/mac_util.h, or butil::win::GetVersion from - // butil/win/windows_version.h. - static void OperatingSystemVersionNumbers(int32_t* major_version, - int32_t* minor_version, - int32_t* bugfix_version); - - // Returns the architecture of the running operating system. - // Exact return value may differ across platforms. - // e.g. a 32-bit x86 kernel on a 64-bit capable CPU will return "x86", - // whereas a x86-64 kernel on the same CPU will return "x86_64" - static std::string OperatingSystemArchitecture(); - - // Avoid using this. Use butil/cpu.h to get information about the CPU instead. - // http://crbug.com/148884 - // Returns the CPU model name of the system. If it can not be figured out, - // an empty string is returned. - static std::string CPUModelName(); - - // Return the smallest amount of memory (in bytes) which the VM system will - // allocate. - static size_t VMAllocationGranularity(); - -#if defined(OS_POSIX) && !defined(OS_MACOSX) - // Returns the maximum SysV shared memory segment size, or zero if there is no - // limit. - static size_t MaxSharedMemorySize(); -#endif // defined(OS_POSIX) && !defined(OS_MACOSX) - -#if defined(OS_CHROMEOS) - typedef std::map LsbReleaseMap; - - // Returns the contents of /etc/lsb-release as a map. - static const LsbReleaseMap& GetLsbReleaseMap(); - - // If |key| is present in the LsbReleaseMap, sets |value| and returns true. - static bool GetLsbReleaseValue(const std::string& key, std::string* value); - - // Convenience function for GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD",...). - // Returns "unknown" if CHROMEOS_RELEASE_BOARD is not set. - static std::string GetLsbReleaseBoard(); - - // Returns the creation time of /etc/lsb-release. (Used to get the date and - // time of the Chrome OS build). - static Time GetLsbReleaseTime(); - - // Returns true when actually running in a Chrome OS environment. - static bool IsRunningOnChromeOS(); - - // Test method to force re-parsing of lsb-release. - static void SetChromeOSVersionInfoForTest(const std::string& lsb_release, - const Time& lsb_release_time); -#endif // defined(OS_CHROMEOS) - -#if defined(OS_ANDROID) - // Returns the Android build's codename. - static std::string GetAndroidBuildCodename(); - - // Returns the Android build ID. - static std::string GetAndroidBuildID(); - - // Returns the device's name. - static std::string GetDeviceName(); - - static int DalvikHeapSizeMB(); - static int DalvikHeapGrowthLimitMB(); -#endif // defined(OS_ANDROID) - - // Returns true if this is a low-end device. - // Low-end device refers to devices having less than 512M memory in the - // current implementation. - static bool IsLowEndDevice(); -}; - -} // namespace butil - -#endif // BASE_SYS_INFO_H_ diff --git a/src/butil/sys_info_freebsd.cc b/src/butil/sys_info_freebsd.cc deleted file mode 100644 index cd548da60d..0000000000 --- a/src/butil/sys_info_freebsd.cc +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "butil/sys_info.h" - -#include - -#include "butil/logging.h" - -namespace butil { - -int64_t SysInfo::AmountOfPhysicalMemory() { - int pages, page_size; - size_t size = sizeof(pages); - sysctlbyname("vm.stats.vm.v_page_count", &pages, &size, NULL, 0); - sysctlbyname("vm.stats.vm.v_page_size", &page_size, &size, NULL, 0); - if (pages == -1 || page_size == -1) { - NOTREACHED(); - return 0; - } - return static_cast(pages) * page_size; -} - -// static -size_t SysInfo::MaxSharedMemorySize() { - size_t limit; - size_t size = sizeof(limit); - if (sysctlbyname("kern.ipc.shmmax", &limit, &size, NULL, 0) < 0) { - NOTREACHED(); - return 0; - } - return limit; -} - -} // namespace butil diff --git a/src/butil/sys_info_internal.h b/src/butil/sys_info_internal.h deleted file mode 100644 index 078a4db168..0000000000 --- a/src/butil/sys_info_internal.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_SYS_INFO_INTERNAL_H_ -#define BASE_SYS_INFO_INTERNAL_H_ - -#include "butil/basictypes.h" - -namespace butil { - -namespace internal { - -template -class LazySysInfoValue { - public: - LazySysInfoValue() - : value_(F()) { } - - ~LazySysInfoValue() { } - - T value() { return value_; } - - private: - const T value_; - - DISALLOW_COPY_AND_ASSIGN(LazySysInfoValue); -}; - -} // namespace internal - -} // namespace butil - -#endif // BASE_SYS_INFO_INTERNAL_H_ diff --git a/src/butil/sys_info_linux.cc b/src/butil/sys_info_linux.cc deleted file mode 100644 index b18dfcd04d..0000000000 --- a/src/butil/sys_info_linux.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "butil/sys_info.h" - -#include - -#include "butil/file_util.h" -#include "butil/lazy_instance.h" -#include "butil/logging.h" -#include "butil/strings/string_number_conversions.h" -#include "butil/sys_info_internal.h" - -namespace { - -int64_t AmountOfMemory(int pages_name) { - long pages = sysconf(pages_name); - long page_size = sysconf(_SC_PAGESIZE); - if (pages == -1 || page_size == -1) { - NOTREACHED(); - return 0; - } - return static_cast(pages) * page_size; -} - -int64_t AmountOfPhysicalMemory() { - return AmountOfMemory(_SC_PHYS_PAGES); -} - -size_t MaxSharedMemorySize() { - std::string contents; - butil::ReadFileToString(butil::FilePath("/proc/sys/kernel/shmmax"), &contents); - DCHECK(!contents.empty()); - if (!contents.empty() && contents[contents.length() - 1] == '\n') { - contents.erase(contents.length() - 1); - } - - int64_t limit; - if (!butil::StringToInt64(contents, &limit)) { - limit = 0; - } - if (limit < 0 || - static_cast(limit) > std::numeric_limits::max()) { - limit = 0; - } - DCHECK(limit > 0); - return static_cast(limit); -} - -butil::LazyInstance< - butil::internal::LazySysInfoValue >::Leaky - g_lazy_physical_memory = LAZY_INSTANCE_INITIALIZER; -butil::LazyInstance< - butil::internal::LazySysInfoValue >::Leaky - g_lazy_max_shared_memory = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -namespace butil { - -// static -int64_t SysInfo::AmountOfAvailablePhysicalMemory() { - return AmountOfMemory(_SC_AVPHYS_PAGES); -} - -// static -int64_t SysInfo::AmountOfPhysicalMemory() { - return g_lazy_physical_memory.Get().value(); -} - -// static -size_t SysInfo::MaxSharedMemorySize() { - return g_lazy_max_shared_memory.Get().value(); -} - -// static -std::string SysInfo::CPUModelName() { -#if defined(OS_CHROMEOS) && defined(ARCH_CPU_ARMEL) - const char kCpuModelPrefix[] = "Hardware"; -#else - const char kCpuModelPrefix[] = "model name"; -#endif - std::string contents; - ReadFileToString(FilePath("/proc/cpuinfo"), &contents); - DCHECK(!contents.empty()); - if (!contents.empty()) { - std::istringstream iss(contents); - std::string line; - while (std::getline(iss, line)) { - if (line.compare(0, strlen(kCpuModelPrefix), kCpuModelPrefix) == 0) { - size_t pos = line.find(": "); - return line.substr(pos + 2); - } - } - } - return std::string(); -} - -} // namespace butil diff --git a/src/butil/sys_info_mac.cc b/src/butil/sys_info_mac.cc deleted file mode 100644 index 440e432197..0000000000 --- a/src/butil/sys_info_mac.cc +++ /dev/null @@ -1,88 +0,0 @@ -// 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. - -#include "butil/sys_info.h" - -#include -#include -#include -#include -#include -#include - -#include "butil/logging.h" -#include "butil/mac/scoped_mach_port.h" -#include "butil/strings/stringprintf.h" - -namespace butil { - -// static -std::string SysInfo::OperatingSystemName() { - return "Mac OS X"; -} - -// static -std::string SysInfo::OperatingSystemVersion() { - int32_t major, minor, bugfix; - OperatingSystemVersionNumbers(&major, &minor, &bugfix); - return butil::StringPrintf("%d.%d.%d", major, minor, bugfix); -} - -// static -void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version, - int32_t* minor_version, - int32_t* bugfix_version) { - Gestalt(gestaltSystemVersionMajor, - reinterpret_cast(major_version)); - Gestalt(gestaltSystemVersionMinor, - reinterpret_cast(minor_version)); - Gestalt(gestaltSystemVersionBugFix, - reinterpret_cast(bugfix_version)); -} - -// static -int64_t SysInfo::AmountOfPhysicalMemory() { - struct host_basic_info hostinfo; - mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; - butil::mac::ScopedMachSendRight host(mach_host_self()); - int result = host_info(host, - HOST_BASIC_INFO, - reinterpret_cast(&hostinfo), - &count); - if (result != KERN_SUCCESS) { - NOTREACHED(); - return 0; - } - DCHECK_EQ(HOST_BASIC_INFO_COUNT, count); - return static_cast(hostinfo.max_mem); -} - -// static -int64_t SysInfo::AmountOfAvailablePhysicalMemory() { - butil::mac::ScopedMachSendRight host(mach_host_self()); - vm_statistics_data_t vm_info; - mach_msg_type_number_t count = HOST_VM_INFO_COUNT; - - if (host_statistics(host.get(), - HOST_VM_INFO, - reinterpret_cast(&vm_info), - &count) != KERN_SUCCESS) { - NOTREACHED(); - return 0; - } - - return static_cast( - vm_info.free_count - vm_info.speculative_count) * PAGE_SIZE; -} - -// static -std::string SysInfo::CPUModelName() { - char name[256]; - size_t len = arraysize(name); - if (sysctlbyname("machdep.cpu.brand_string", &name, &len, NULL, 0) == 0) - return name; - return std::string(); -} - -} // namespace butil diff --git a/src/butil/sys_info_openbsd.cc b/src/butil/sys_info_openbsd.cc deleted file mode 100644 index d7111d7a98..0000000000 --- a/src/butil/sys_info_openbsd.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "butil/sys_info.h" - -#include -#include -#include - -#include "butil/logging.h" - -namespace { - -int64_t AmountOfMemory(int pages_name) { - long pages = sysconf(pages_name); - long page_size = sysconf(_SC_PAGESIZE); - if (pages == -1 || page_size == -1) { - NOTREACHED(); - return 0; - } - return static_cast(pages) * page_size; -} - -} // namespace - -namespace butil { - -// static -int SysInfo::NumberOfProcessors() { - int mib[] = { CTL_HW, HW_NCPU }; - int ncpu; - size_t size = sizeof(ncpu); - if (sysctl(mib, arraysize(mib), &ncpu, &size, NULL, 0) < 0) { - NOTREACHED(); - return 1; - } - return ncpu; -} - -// static -int64_t SysInfo::AmountOfPhysicalMemory() { - return AmountOfMemory(_SC_PHYS_PAGES); -} - -// static -int64_t SysInfo::AmountOfAvailablePhysicalMemory() { - return AmountOfMemory(_SC_AVPHYS_PAGES); -} - -// static -size_t SysInfo::MaxSharedMemorySize() { - int mib[] = { CTL_KERN, KERN_SHMINFO, KERN_SHMINFO_SHMMAX }; - size_t limit; - size_t size = sizeof(limit); - if (sysctl(mib, arraysize(mib), &limit, &size, NULL, 0) < 0) { - NOTREACHED(); - return 0; - } - return limit; -} - -// static -std::string SysInfo::CPUModelName() { - int mib[] = { CTL_HW, HW_MODEL }; - char name[256]; - size_t len = arraysize(name); - if (sysctl(mib, arraysize(mib), name, &len, NULL, 0) < 0) { - NOTREACHED(); - return std::string(); - } - return name; -} - -} // namespace butil diff --git a/src/butil/sys_info_posix.cc b/src/butil/sys_info_posix.cc deleted file mode 100644 index 0a634b46ee..0000000000 --- a/src/butil/sys_info_posix.cc +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "butil/sys_info.h" - -#include -#include -#include -#include -#include -#include - -#include "butil/basictypes.h" -#include "butil/file_util.h" -#include "butil/lazy_instance.h" -#include "butil/logging.h" -#include "butil/strings/utf_string_conversions.h" -#include "butil/sys_info_internal.h" -#include "butil/threading/thread_restrictions.h" - -#if defined(OS_ANDROID) -#include -#define statvfs statfs // Android uses a statvfs-like statfs struct and call. -#else -#include -#endif - -namespace { - -#if !defined(OS_OPENBSD) -int NumberOfProcessors() { - // It seems that sysconf returns the number of "logical" processors on both - // Mac and Linux. So we get the number of "online logical" processors. - long res = sysconf(_SC_NPROCESSORS_ONLN); - if (res == -1) { - NOTREACHED(); - return 1; - } - - return static_cast(res); -} - -butil::LazyInstance< - butil::internal::LazySysInfoValue >::Leaky - g_lazy_number_of_processors = LAZY_INSTANCE_INITIALIZER; -#endif - -int64_t AmountOfVirtualMemory() { - struct rlimit limit; - int result = getrlimit(RLIMIT_DATA, &limit); - if (result != 0) { - NOTREACHED(); - return 0; - } - return limit.rlim_cur == RLIM_INFINITY ? 0 : limit.rlim_cur; -} - -butil::LazyInstance< - butil::internal::LazySysInfoValue >::Leaky - g_lazy_virtual_memory = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -namespace butil { - -#if !defined(OS_OPENBSD) -int SysInfo::NumberOfProcessors() { - return g_lazy_number_of_processors.Get().value(); -} -#endif - -// static -int64_t SysInfo::AmountOfVirtualMemory() { - return g_lazy_virtual_memory.Get().value(); -} - -// static -int64_t SysInfo::AmountOfFreeDiskSpace(const FilePath& path) { - butil::ThreadRestrictions::AssertIOAllowed(); - - struct statvfs stats; - if (HANDLE_EINTR(statvfs(path.value().c_str(), &stats)) != 0) - return -1; - return static_cast(stats.f_bavail) * stats.f_frsize; -} - -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) -// static -std::string SysInfo::OperatingSystemName() { - struct utsname info; - if (uname(&info) < 0) { - NOTREACHED(); - return std::string(); - } - return std::string(info.sysname); -} -#endif - -#if !defined(OS_MACOSX) && !defined(OS_ANDROID) -// static -std::string SysInfo::OperatingSystemVersion() { - struct utsname info; - if (uname(&info) < 0) { - NOTREACHED(); - return std::string(); - } - return std::string(info.release); -} -#endif - -// static -std::string SysInfo::OperatingSystemArchitecture() { - struct utsname info; - if (uname(&info) < 0) { - NOTREACHED(); - return std::string(); - } - std::string arch(info.machine); - if (arch == "i386" || arch == "i486" || arch == "i586" || arch == "i686") { - arch = "x86"; - } else if (arch == "amd64") { - arch = "x86_64"; - } - return arch; -} - -// static -size_t SysInfo::VMAllocationGranularity() { - return getpagesize(); -} - -} // namespace butil diff --git a/test/Makefile b/test/Makefile index d5e558dc0f..cc9412ee1b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -30,7 +30,6 @@ TEST_BUTIL_SOURCES = \ callback_list_unittest.cc \ callback_unittest.cc \ cancelable_callback_unittest.cc \ - command_line_unittest.cc \ hash_tables_unittest.cc \ linked_list_unittest.cc \ mru_cache_unittest.cc \ @@ -85,7 +84,6 @@ TEST_BUTIL_SOURCES = \ condition_variable_unittest.cc \ lock_unittest.cc \ waitable_event_unittest.cc \ - sys_info_unittest.cc \ type_traits_unittest.cc \ non_thread_safe_unittest.cc \ platform_thread_unittest.cc \ @@ -121,9 +119,7 @@ TEST_BUTIL_SOURCES = \ crc32c_unittest.cc \ iobuf_unittest.cc \ test_switches.cc \ - test_timeouts.cc \ scoped_locale.cc \ - test_file_util.cc \ test_file_util_linux.cc \ butil_unittest_main.cpp diff --git a/test/butil_unittest_main.cpp b/test/butil_unittest_main.cpp index 2b9f09a561..e3606b5cf5 100644 --- a/test/butil_unittest_main.cpp +++ b/test/butil_unittest_main.cpp @@ -3,10 +3,8 @@ #include #include #include -#include "butil/command_line.h" #include "butil/base_switches.h" #include "butil/at_exit.h" -#include "test/test_timeouts.h" #include "test/multiprocess_func_list.h" // Disable coredumps by default to avoid generating a lot of coredumps @@ -15,17 +13,8 @@ DEFINE_bool(disable_coredump, true, "Never core dump"); int main(int argc, char** argv) { butil::AtExitManager at_exit; - butil::CommandLine::Init(argc, argv); - TestTimeouts::Initialize(); testing::InitGoogleTest(&argc, argv); - std::string client_func = - butil::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kTestChildProcess); - // Check to see if we are being run as a client process. - if (!client_func.empty()) { - return multi_process_function_list::InvokeChildProcessTest(client_func); - } google::ParseCommandLineFlags(&argc, &argv, true); if (FLAGS_disable_coredump) { rlimit core_limit; diff --git a/test/command_line_unittest.cc b/test/command_line_unittest.cc deleted file mode 100644 index cd71820b0c..0000000000 --- a/test/command_line_unittest.cc +++ /dev/null @@ -1,364 +0,0 @@ -// 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. - -#include -#include - -#include "butil/basictypes.h" -#include "butil/command_line.h" -#include "butil/files/file_path.h" -#include "butil/strings/utf_string_conversions.h" -#include - -using butil::FilePath; - -// To test Windows quoting behavior, we use a string that has some backslashes -// and quotes. -// Consider the command-line argument: q\"bs1\bs2\\bs3q\\\" -// Here it is with C-style escapes. -static const CommandLine::StringType kTrickyQuoted = - FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\""); -// It should be parsed by Windows as: q"bs1\bs2\\bs3q\" -// Here that is with C-style escapes. -static const CommandLine::StringType kTricky = - FILE_PATH_LITERAL("q\"bs1\\bs2\\\\bs3q\\\""); - -TEST(CommandLineTest, CommandLineConstructor) { - const CommandLine::CharType* argv[] = { - FILE_PATH_LITERAL("program"), - FILE_PATH_LITERAL("--foo="), - FILE_PATH_LITERAL("-bAr"), - FILE_PATH_LITERAL("-spaetzel=pierogi"), - FILE_PATH_LITERAL("-baz"), - FILE_PATH_LITERAL("flim"), - FILE_PATH_LITERAL("--other-switches=--dog=canine --cat=feline"), - FILE_PATH_LITERAL("-spaetzle=Crepe"), - FILE_PATH_LITERAL("-=loosevalue"), - FILE_PATH_LITERAL("-"), - FILE_PATH_LITERAL("FLAN"), - FILE_PATH_LITERAL("a"), - FILE_PATH_LITERAL("--input-translation=45--output-rotation"), - FILE_PATH_LITERAL("--"), - FILE_PATH_LITERAL("--"), - FILE_PATH_LITERAL("--not-a-switch"), - FILE_PATH_LITERAL("\"in the time of submarines...\""), - FILE_PATH_LITERAL("unquoted arg-with-space")}; - CommandLine cl(arraysize(argv), argv); - - EXPECT_FALSE(cl.GetCommandLineString().empty()); - EXPECT_FALSE(cl.HasSwitch("cruller")); - EXPECT_FALSE(cl.HasSwitch("flim")); - EXPECT_FALSE(cl.HasSwitch("program")); - EXPECT_FALSE(cl.HasSwitch("dog")); - EXPECT_FALSE(cl.HasSwitch("cat")); - EXPECT_FALSE(cl.HasSwitch("output-rotation")); - EXPECT_FALSE(cl.HasSwitch("not-a-switch")); - EXPECT_FALSE(cl.HasSwitch("--")); - - EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), - cl.GetProgram().value()); - - EXPECT_TRUE(cl.HasSwitch("foo")); - EXPECT_TRUE(cl.HasSwitch("bAr")); - EXPECT_TRUE(cl.HasSwitch("baz")); - EXPECT_TRUE(cl.HasSwitch("spaetzle")); -#if defined(OS_WIN) - EXPECT_TRUE(cl.HasSwitch("SPAETZLE")); -#endif - EXPECT_TRUE(cl.HasSwitch("other-switches")); - EXPECT_TRUE(cl.HasSwitch("input-translation")); - - EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); - EXPECT_EQ("", cl.GetSwitchValueASCII("Foo")); - EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); - EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); - EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( - "other-switches")); - EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); - - const CommandLine::StringVector& args = cl.GetArgs(); - ASSERT_EQ(8U, args.size()); - - std::vector::const_iterator iter = args.begin(); - EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("a"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter); - ++iter; - EXPECT_TRUE(iter == args.end()); -} - -TEST(CommandLineTest, CommandLineFromString) { -#if defined(OS_WIN) - CommandLine cl = CommandLine::FromString( - L"program --foo= -bAr /Spaetzel=pierogi /Baz flim " - L"--other-switches=\"--dog=canine --cat=feline\" " - L"-spaetzle=Crepe -=loosevalue FLAN " - L"--input-translation=\"45\"--output-rotation " - L"--quotes=" + kTrickyQuoted + L" " - L"-- -- --not-a-switch " - L"\"in the time of submarines...\""); - - EXPECT_FALSE(cl.GetCommandLineString().empty()); - EXPECT_FALSE(cl.HasSwitch("cruller")); - EXPECT_FALSE(cl.HasSwitch("flim")); - EXPECT_FALSE(cl.HasSwitch("program")); - EXPECT_FALSE(cl.HasSwitch("dog")); - EXPECT_FALSE(cl.HasSwitch("cat")); - EXPECT_FALSE(cl.HasSwitch("output-rotation")); - EXPECT_FALSE(cl.HasSwitch("not-a-switch")); - EXPECT_FALSE(cl.HasSwitch("--")); - - EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), - cl.GetProgram().value()); - - EXPECT_TRUE(cl.HasSwitch("foo")); - EXPECT_TRUE(cl.HasSwitch("bar")); - EXPECT_TRUE(cl.HasSwitch("baz")); - EXPECT_TRUE(cl.HasSwitch("spaetzle")); - EXPECT_TRUE(cl.HasSwitch("SPAETZLE")); - EXPECT_TRUE(cl.HasSwitch("other-switches")); - EXPECT_TRUE(cl.HasSwitch("input-translation")); - EXPECT_TRUE(cl.HasSwitch("quotes")); - - EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); - EXPECT_EQ("", cl.GetSwitchValueASCII("Foo")); - EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); - EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); - EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( - "other-switches")); - EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); - EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes")); - - const CommandLine::StringVector& args = cl.GetArgs(); - ASSERT_EQ(5U, args.size()); - - std::vector::const_iterator iter = args.begin(); - EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); - ++iter; - EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter); - ++iter; - EXPECT_TRUE(iter == args.end()); - - // Check that a generated string produces an equivalent command line. - CommandLine cl_duplicate = CommandLine::FromString(cl.GetCommandLineString()); - EXPECT_EQ(cl.GetCommandLineString(), cl_duplicate.GetCommandLineString()); -#endif -} - -// Tests behavior with an empty input string. -TEST(CommandLineTest, EmptyString) { -#if defined(OS_WIN) - CommandLine cl_from_string = CommandLine::FromString(L""); - EXPECT_TRUE(cl_from_string.GetCommandLineString().empty()); - EXPECT_TRUE(cl_from_string.GetProgram().empty()); - EXPECT_EQ(1U, cl_from_string.argv().size()); - EXPECT_TRUE(cl_from_string.GetArgs().empty()); -#endif - CommandLine cl_from_argv(0, NULL); - EXPECT_TRUE(cl_from_argv.GetCommandLineString().empty()); - EXPECT_TRUE(cl_from_argv.GetProgram().empty()); - EXPECT_EQ(1U, cl_from_argv.argv().size()); - EXPECT_TRUE(cl_from_argv.GetArgs().empty()); -} - -TEST(CommandLineTest, GetArgumentsString) { - static const FilePath::CharType kPath1[] = - FILE_PATH_LITERAL("C:\\Some File\\With Spaces.ggg"); - static const FilePath::CharType kPath2[] = - FILE_PATH_LITERAL("C:\\no\\spaces.ggg"); - - static const char kFirstArgName[] = "first-arg"; - static const char kSecondArgName[] = "arg2"; - static const char kThirdArgName[] = "arg with space"; - static const char kFourthArgName[] = "nospace"; - - CommandLine cl(CommandLine::NO_PROGRAM); - cl.AppendSwitchPath(kFirstArgName, FilePath(kPath1)); - cl.AppendSwitchPath(kSecondArgName, FilePath(kPath2)); - cl.AppendArg(kThirdArgName); - cl.AppendArg(kFourthArgName); - -#if defined(OS_WIN) - CommandLine::StringType expected_first_arg( - butil::UTF8ToUTF16(kFirstArgName)); - CommandLine::StringType expected_second_arg( - butil::UTF8ToUTF16(kSecondArgName)); - CommandLine::StringType expected_third_arg( - butil::UTF8ToUTF16(kThirdArgName)); - CommandLine::StringType expected_fourth_arg( - butil::UTF8ToUTF16(kFourthArgName)); -#elif defined(OS_POSIX) - CommandLine::StringType expected_first_arg(kFirstArgName); - CommandLine::StringType expected_second_arg(kSecondArgName); - CommandLine::StringType expected_third_arg(kThirdArgName); - CommandLine::StringType expected_fourth_arg(kFourthArgName); -#endif - -#if defined(OS_WIN) -#define QUOTE_ON_WIN FILE_PATH_LITERAL("\"") -#else -#define QUOTE_ON_WIN FILE_PATH_LITERAL("") -#endif // OS_WIN - - CommandLine::StringType expected_str; - expected_str.append(FILE_PATH_LITERAL("--")) - .append(expected_first_arg) - .append(FILE_PATH_LITERAL("=")) - .append(QUOTE_ON_WIN) - .append(kPath1) - .append(QUOTE_ON_WIN) - .append(FILE_PATH_LITERAL(" ")) - .append(FILE_PATH_LITERAL("--")) - .append(expected_second_arg) - .append(FILE_PATH_LITERAL("=")) - .append(QUOTE_ON_WIN) - .append(kPath2) - .append(QUOTE_ON_WIN) - .append(FILE_PATH_LITERAL(" ")) - .append(QUOTE_ON_WIN) - .append(expected_third_arg) - .append(QUOTE_ON_WIN) - .append(FILE_PATH_LITERAL(" ")) - .append(expected_fourth_arg); - EXPECT_EQ(expected_str, cl.GetArgumentsString()); -} - -// Test methods for appending switches to a command line. -TEST(CommandLineTest, AppendSwitches) { - std::string switch1 = "switch1"; - std::string switch2 = "switch2"; - std::string value2 = "value"; - std::string switch3 = "switch3"; - std::string value3 = "a value with spaces"; - std::string switch4 = "switch4"; - std::string value4 = "\"a value with quotes\""; - std::string switch5 = "quotes"; - CommandLine::StringType value5 = kTricky; - - CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); - - cl.AppendSwitch(switch1); - cl.AppendSwitchASCII(switch2, value2); - cl.AppendSwitchASCII(switch3, value3); - cl.AppendSwitchASCII(switch4, value4); - cl.AppendSwitchNative(switch5, value5); - - EXPECT_TRUE(cl.HasSwitch(switch1)); - EXPECT_TRUE(cl.HasSwitch(switch2)); - EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2)); - EXPECT_TRUE(cl.HasSwitch(switch3)); - EXPECT_EQ(value3, cl.GetSwitchValueASCII(switch3)); - EXPECT_TRUE(cl.HasSwitch(switch4)); - EXPECT_EQ(value4, cl.GetSwitchValueASCII(switch4)); - EXPECT_TRUE(cl.HasSwitch(switch5)); - EXPECT_EQ(value5, cl.GetSwitchValueNative(switch5)); - -#if defined(OS_WIN) - EXPECT_EQ(L"Program " - L"--switch1 " - L"--switch2=value " - L"--switch3=\"a value with spaces\" " - L"--switch4=\"\\\"a value with quotes\\\"\" " - L"--quotes=\"" + kTrickyQuoted + L"\"", - cl.GetCommandLineString()); -#endif -} - -TEST(CommandLineTest, AppendSwitchesDashDash) { - const CommandLine::CharType* raw_argv[] = { FILE_PATH_LITERAL("prog"), - FILE_PATH_LITERAL("--"), - FILE_PATH_LITERAL("--arg1") }; - CommandLine cl(arraysize(raw_argv), raw_argv); - - cl.AppendSwitch("switch1"); - cl.AppendSwitchASCII("switch2", "foo"); - - cl.AppendArg("--arg2"); - - EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"), - cl.GetCommandLineString()); - CommandLine::StringVector cl_argv = cl.argv(); - EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]); - EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]); - EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]); - EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]); - EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]); - EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]); -} - -// Tests that when AppendArguments is called that the program is set correctly -// on the target CommandLine object and the switches from the source -// CommandLine are added to the target. -TEST(CommandLineTest, AppendArguments) { - CommandLine cl1(FilePath(FILE_PATH_LITERAL("Program"))); - cl1.AppendSwitch("switch1"); - cl1.AppendSwitchASCII("switch2", "foo"); - - CommandLine cl2(CommandLine::NO_PROGRAM); - cl2.AppendArguments(cl1, true); - EXPECT_EQ(cl1.GetProgram().value(), cl2.GetProgram().value()); - EXPECT_EQ(cl1.GetCommandLineString(), cl2.GetCommandLineString()); - - CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1"))); - c1.AppendSwitch("switch1"); - CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2"))); - c2.AppendSwitch("switch2"); - - c1.AppendArguments(c2, true); - EXPECT_EQ(c1.GetProgram().value(), c2.GetProgram().value()); - EXPECT_TRUE(c1.HasSwitch("switch1")); - EXPECT_TRUE(c1.HasSwitch("switch2")); -} - -#if defined(OS_WIN) -// Make sure that the command line string program paths are quoted as necessary. -// This only makes sense on Windows and the test is basically here to guard -// against regressions. -TEST(CommandLineTest, ProgramQuotes) { - // Check that quotes are not added for paths without spaces. - const FilePath kProgram(L"Program"); - CommandLine cl_program(kProgram); - EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value()); - EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString()); - - const FilePath kProgramPath(L"Program Path"); - - // Check that quotes are not returned from GetProgram(). - CommandLine cl_program_path(kProgramPath); - EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value()); - - // Check that quotes are added to command line string paths containing spaces. - CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString()); - CommandLine::StringType program_string(cl_program_path.GetProgram().value()); - EXPECT_EQ('"', cmd_string[0]); - EXPECT_EQ(program_string, cmd_string.substr(1, program_string.length())); - EXPECT_EQ('"', cmd_string[program_string.length() + 1]); -} -#endif - -// Calling Init multiple times should not modify the previous CommandLine. -TEST(CommandLineTest, Init) { - CommandLine* initial = CommandLine::ForCurrentProcess(); - EXPECT_FALSE(CommandLine::Init(0, NULL)); - CommandLine* current = CommandLine::ForCurrentProcess(); - EXPECT_EQ(initial, current); -} diff --git a/test/endpoint_unittest.cpp b/test/endpoint_unittest.cpp index 7dcce99434..cbb49e58d2 100644 --- a/test/endpoint_unittest.cpp +++ b/test/endpoint_unittest.cpp @@ -81,10 +81,12 @@ TEST(EndPointTest, endpoint) { ASSERT_EQ(0, hostname2endpoint("localhost:65535", &p5)) << berror(); ASSERT_EQ(0, hostname2endpoint("localhost:0", &p5)); +#ifdef BAIDU_INTERNAL butil::EndPoint p6; ASSERT_EQ(0, hostname2endpoint("tc-cm-et21.tc: 289 ", &p6)); ASSERT_STREQ("10.23.249.73", butil::ip2str(p6.ip).c_str()); ASSERT_EQ(289, p6.port); +#endif } TEST(EndPointTest, hash_table) { diff --git a/test/file_util_unittest.cc b/test/file_util_unittest.cc index 2b313f677c..a0b855166d 100644 --- a/test/file_util_unittest.cc +++ b/test/file_util_unittest.cc @@ -23,13 +23,11 @@ #include #include -#include "butil/base_paths.h" #include "butil/file_util.h" #include "butil/files/file_enumerator.h" #include "butil/files/file_path.h" #include "butil/files/scoped_file.h" #include "butil/files/scoped_temp_dir.h" -#include "butil/path_service.h" #include "butil/strings/utf_string_conversions.h" #include "test/test_file_util.h" #include "butil/threading/platform_thread.h" diff --git a/test/multiprocess_func_list.cc b/test/multiprocess_func_list.cc deleted file mode 100644 index 49ae07dd3e..0000000000 --- a/test/multiprocess_func_list.cc +++ /dev/null @@ -1,57 +0,0 @@ -// 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. - -#include "multiprocess_func_list.h" - -#include - -// Helper functions to maintain mapping of "test name"->test func. -// The information is accessed via a global map. -namespace multi_process_function_list { - -namespace { - -struct ProcessFunctions { - ProcessFunctions() : main(NULL), setup(NULL) {} - ProcessFunctions(TestMainFunctionPtr main, SetupFunctionPtr setup) - : main(main), - setup(setup) { - } - TestMainFunctionPtr main; - SetupFunctionPtr setup; -}; - -typedef std::map MultiProcessTestMap; - -// Retrieve a reference to the global 'func name' -> func ptr map. -MultiProcessTestMap& GetMultiprocessFuncMap() { - static MultiProcessTestMap test_name_to_func_ptr_map; - return test_name_to_func_ptr_map; -} - -} // namespace - -AppendMultiProcessTest::AppendMultiProcessTest( - std::string test_name, - TestMainFunctionPtr main_func_ptr, - SetupFunctionPtr setup_func_ptr) { - GetMultiprocessFuncMap()[test_name] = - ProcessFunctions(main_func_ptr, setup_func_ptr); -} - -int InvokeChildProcessTest(std::string test_name) { - MultiProcessTestMap& func_lookup_table = GetMultiprocessFuncMap(); - MultiProcessTestMap::iterator it = func_lookup_table.find(test_name); - if (it != func_lookup_table.end()) { - const ProcessFunctions& process_functions = it->second; - if (process_functions.setup) - (*process_functions.setup)(); - if (process_functions.main) - return (*process_functions.main)(); - } - - return -1; -} - -} // namespace multi_process_function_list diff --git a/test/multiprocess_test.cc b/test/multiprocess_test.cc deleted file mode 100644 index d50531c19e..0000000000 --- a/test/multiprocess_test.cc +++ /dev/null @@ -1,59 +0,0 @@ -// 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. - -#include "test/multiprocess_test.h" - -#include "butil/base_switches.h" -#include "butil/command_line.h" - -namespace butil { - -#if !defined(OS_ANDROID) -ProcessHandle SpawnMultiProcessTestChild( - const std::string& procname, - const CommandLine& base_command_line, - const LaunchOptions& options) { - CommandLine command_line(base_command_line); - // TODO(viettrungluu): See comment above |MakeCmdLine()| in the header file. - // This is a temporary hack, since |MakeCmdLine()| has to provide a full - // command line. - if (!command_line.HasSwitch(switches::kTestChildProcess)) - command_line.AppendSwitchASCII(switches::kTestChildProcess, procname); - - ProcessHandle handle = kNullProcessHandle; - LaunchProcess(command_line, options, &handle); - return handle; -} -#endif // !defined(OS_ANDROID) - -CommandLine GetMultiProcessTestChildBaseCommandLine() { - return *CommandLine::ForCurrentProcess(); -} - -// MultiProcessTest ------------------------------------------------------------ - -MultiProcessTest::MultiProcessTest() { -} - -ProcessHandle MultiProcessTest::SpawnChild(const std::string& procname) { - LaunchOptions options; -#if defined(OS_WIN) - options.start_hidden = true; -#endif - return SpawnChildWithOptions(procname, options); -} - -ProcessHandle MultiProcessTest::SpawnChildWithOptions( - const std::string& procname, - const LaunchOptions& options) { - return SpawnMultiProcessTestChild(procname, MakeCmdLine(procname), options); -} - -CommandLine MultiProcessTest::MakeCmdLine(const std::string& procname) { - CommandLine command_line = GetMultiProcessTestChildBaseCommandLine(); - command_line.AppendSwitchASCII(switches::kTestChildProcess, procname); - return command_line; -} - -} // namespace butil diff --git a/test/multiprocess_test.h b/test/multiprocess_test.h deleted file mode 100644 index cab2f9f2e3..0000000000 --- a/test/multiprocess_test.h +++ /dev/null @@ -1,131 +0,0 @@ -// 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_TEST_MULTIPROCESS_TEST_H_ -#define BASE_TEST_MULTIPROCESS_TEST_H_ - -#include -#include -#include "butil/basictypes.h" -#include "butil/process/launch.h" -#include "butil/process/process_handle.h" -#include "butil/build_config.h" - -namespace butil { - -class CommandLine; - -// Helpers to spawn a child for a multiprocess test and execute a designated -// function. Use these when you already have another base class for your test -// fixture, but you want (some) of your tests to be multiprocess (otherwise you -// may just want to derive your fixture from |MultiProcessTest|, below). -// -// Use these helpers as follows: -// -// TEST_F(MyTest, ATest) { -// CommandLine command_line( -// butil::GetMultiProcessTestChildBaseCommandLine()); -// // Maybe add our own switches to |command_line|.... -// -// LaunchOptions options; -// // Maybe set some options (e.g., |start_hidden| on Windows).... -// -// // Start a child process and run |a_test_func|. -// butil::ProcessHandle test_child_handle = -// butil::SpawnMultiProcessTestChild("a_test_func", command_line, -// options); -// -// // Do stuff involving |test_child_handle| and the child process.... -// -// int rv = -1; -// ASSERT_TRUE(butil::WaitForExitCodeWithTimeout( -// test_child_handle, &rv, TestTimeouts::action_timeout())); -// butil::CloseProcessHandle(test_child_handle); -// EXPECT_EQ(0, rv); -// } -// -// // Note: |MULTIPROCESS_TEST_MAIN()| is defined in -// // test/multi_process_function_list.h. -// MULTIPROCESS_TEST_MAIN(a_test_func) { -// // Code here runs in a child process.... -// return 0; -// } - -// Spawns a child process and executes the function |procname| declared using -// |MULTIPROCESS_TEST_MAIN()| or |MULTIPROCESS_TEST_MAIN_WITH_SETUP()|. -// |command_line| should be as provided by -// |GetMultiProcessTestChildBaseCommandLine()| (below), possibly with arguments -// added. Note: On Windows, you probably want to set |options.start_hidden|. -ProcessHandle SpawnMultiProcessTestChild( - const std::string& procname, - const CommandLine& command_line, - const LaunchOptions& options); - -// Gets the base command line for |SpawnMultiProcessTestChild()|. To this, you -// may add any flags needed for your child process. -CommandLine GetMultiProcessTestChildBaseCommandLine(); - -// MultiProcessTest ------------------------------------------------------------ - -// A MultiProcessTest is a test class which makes it easier to -// write a test which requires code running out of process. -// -// To create a multiprocess test simply follow these steps: -// -// 1) Derive your test from MultiProcessTest. Example: -// -// class MyTest : public MultiProcessTest { -// }; -// -// TEST_F(MyTest, TestCaseName) { -// ... -// } -// -// 2) Create a mainline function for the child processes and include -// test/multiprocess_func_list.h. -// See the declaration of the MULTIPROCESS_TEST_MAIN macro -// in that file for an example. -// 3) Call SpawnChild("foo"), where "foo" is the name of -// the function you wish to run in the child processes. -// That's it! -class MultiProcessTest : public testing::Test { - public: - MultiProcessTest(); - - protected: - // Run a child process. - // 'procname' is the name of a function which the child will - // execute. It must be exported from this library in order to - // run. - // - // Example signature: - // extern "C" int __declspec(dllexport) FooBar() { - // // do client work here - // } - // - // Returns the handle to the child, or NULL on failure - ProcessHandle SpawnChild(const std::string& procname); - - // Run a child process using the given launch options. - // - // Note: On Windows, you probably want to set |options.start_hidden|. - ProcessHandle SpawnChildWithOptions(const std::string& procname, - const LaunchOptions& options); - - // Set up the command line used to spawn the child process. - // Override this to add things to the command line (calling this first in the - // override). - // Note that currently some tests rely on this providing a full command line, - // which they then use directly with |LaunchProcess()|. - // TODO(viettrungluu): Remove this and add a virtual - // |ModifyChildCommandLine()|; make the two divergent uses more sane. - virtual CommandLine MakeCmdLine(const std::string& procname); - - private: - DISALLOW_COPY_AND_ASSIGN(MultiProcessTest); -}; - -} // namespace butil - -#endif // BASE_TEST_MULTIPROCESS_TEST_H_ diff --git a/test/path_service_unittest.cc b/test/path_service_unittest.cc deleted file mode 100644 index f287dd36de..0000000000 --- a/test/path_service_unittest.cc +++ /dev/null @@ -1,250 +0,0 @@ -// 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. - -#include "butil/path_service.h" - -#include "butil/basictypes.h" -#include "butil/file_util.h" -#include "butil/files/file_path.h" -#include "butil/files/scoped_temp_dir.h" -#include "butil/strings/string_util.h" -#include "butil/build_config.h" -#include -#include -#include - -#if defined(OS_WIN) -#include -#include "butil/win/windows_version.h" -// userenv.dll is required for GetDefaultUserProfileDirectory(). -#pragma comment(lib, "userenv.lib") -#endif - -namespace { - -// Returns true if PathService::Get returns true and sets the path parameter -// to non-empty for the given PathService::DirType enumeration value. -bool ALLOW_UNUSED ReturnsValidPath(int dir_type) { - butil::FilePath path; - bool result = PathService::Get(dir_type, &path); - - // Some paths might not exist on some platforms in which case confirming - // |result| is true and !path.empty() is the best we can do. - bool check_path_exists = true; -#if defined(OS_POSIX) - // If chromium has never been started on this account, the cache path may not - // exist. - if (dir_type == butil::DIR_CACHE) - check_path_exists = false; -#endif -#if defined(OS_LINUX) - // On the linux try-bots: a path is returned (e.g. /home/chrome-bot/Desktop), - // but it doesn't exist. - if (dir_type == butil::DIR_USER_DESKTOP) - check_path_exists = false; -#endif -#if defined(OS_WIN) - if (dir_type == butil::DIR_DEFAULT_USER_QUICK_LAUNCH) { - // On Windows XP, the Quick Launch folder for the "Default User" doesn't - // exist by default. At least confirm that the path returned begins with the - // Default User's profile path. - if (butil::win::GetVersion() < butil::win::VERSION_VISTA) { - wchar_t default_profile_path[MAX_PATH]; - DWORD size = arraysize(default_profile_path); - return (result && - ::GetDefaultUserProfileDirectory(default_profile_path, &size) && - StartsWith(path.value(), default_profile_path, false)); - } - } else if (dir_type == butil::DIR_TASKBAR_PINS) { - // There is no pinned-to-taskbar shortcuts prior to Win7. - if (butil::win::GetVersion() < butil::win::VERSION_WIN7) - check_path_exists = false; - } -#endif -#if defined(OS_MACOSX) - if (dir_type != butil::DIR_EXE && dir_type != butil::DIR_MODULE && - dir_type != butil::FILE_EXE && dir_type != butil::FILE_MODULE) { - if (path.ReferencesParent()) - return false; - } -#else - if (path.ReferencesParent()) - return false; -#endif - return result && !path.empty() && (!check_path_exists || - butil::PathExists(path)); -} - -#if defined(OS_WIN) -// Function to test any directory keys that are not supported on some versions -// of Windows. Checks that the function fails and that the returned path is -// empty. -bool ReturnsInvalidPath(int dir_type) { - butil::FilePath path; - bool result = PathService::Get(dir_type, &path); - return !result && path.empty(); -} -#endif - -} // namespace - -// On the Mac this winds up using some autoreleased objects, so we need to -// be a testing::Test. -typedef testing::Test PathServiceTest; - -// FIXME(gejun): Fail due to -// [0710/142222:FATAL:path_service.cc(212)] Check failed: path.empty(). provider should not have modified path -#if defined(FixedPathServiceTestGet) -// Test that all PathService::Get calls return a value and a true result -// in the development environment. (This test was created because a few -// later changes to Get broke the semantics of the function and yielded the -// correct value while returning false.) -TEST_F(PathServiceTest, Get) { - for (int key = butil::PATH_START + 1; key < butil::PATH_END; ++key) { -#if defined(OS_ANDROID) - if (key == butil::FILE_MODULE || key == butil::DIR_USER_DESKTOP || - key == butil::DIR_HOME) - continue; // Android doesn't implement these. -#elif defined(OS_IOS) - if (key == butil::DIR_USER_DESKTOP) - continue; // iOS doesn't implement DIR_USER_DESKTOP; -#endif - EXPECT_PRED1(ReturnsValidPath, key); - } -#if defined(OS_WIN) - for (int key = butil::PATH_WIN_START + 1; key < butil::PATH_WIN_END; ++key) { - bool valid = true; - switch(key) { - case butil::DIR_LOCAL_APP_DATA_LOW: - // DIR_LOCAL_APP_DATA_LOW is not supported prior Vista and is expected - // to fail. - valid = butil::win::GetVersion() >= butil::win::VERSION_VISTA; - break; - case butil::DIR_APP_SHORTCUTS: - // DIR_APP_SHORTCUTS is not supported prior Windows 8 and is expected to - // fail. - valid = butil::win::GetVersion() >= butil::win::VERSION_WIN8; - break; - } - - if (valid) - EXPECT_TRUE(ReturnsValidPath(key)) << key; - else - EXPECT_TRUE(ReturnsInvalidPath(key)) << key; - } -#elif defined(OS_MACOSX) - for (int key = butil::PATH_MAC_START + 1; key < butil::PATH_MAC_END; ++key) { - EXPECT_PRED1(ReturnsValidPath, key); - } -#elif defined(OS_ANDROID) - for (int key = butil::PATH_ANDROID_START + 1; key < butil::PATH_ANDROID_END; - ++key) { - EXPECT_PRED1(ReturnsValidPath, key); - } -#elif defined(OS_POSIX) - for (int key = butil::PATH_POSIX_START + 1; key < butil::PATH_POSIX_END; - ++key) { - EXPECT_PRED1(ReturnsValidPath, key); - } -#endif -} -#endif - -// Test that all versions of the Override function of PathService do what they -// are supposed to do. -TEST_F(PathServiceTest, Override) { - int my_special_key = 666; - butil::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - butil::FilePath fake_cache_dir(temp_dir.path().AppendASCII("cache")); - // PathService::Override should always create the path provided if it doesn't - // exist. - EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir)); - EXPECT_TRUE(butil::PathExists(fake_cache_dir)); - - butil::FilePath fake_cache_dir2(temp_dir.path().AppendASCII("cache2")); - // PathService::OverrideAndCreateIfNeeded should obey the |create| parameter. - PathService::OverrideAndCreateIfNeeded(my_special_key, - fake_cache_dir2, - false, - false); - EXPECT_FALSE(butil::PathExists(fake_cache_dir2)); - EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key, - fake_cache_dir2, - false, - true)); - EXPECT_TRUE(butil::PathExists(fake_cache_dir2)); - -#if defined(OS_POSIX) - butil::FilePath non_existent( - butil::MakeAbsoluteFilePath(temp_dir.path()).AppendASCII("non_existent")); - EXPECT_TRUE(non_existent.IsAbsolute()); - EXPECT_FALSE(butil::PathExists(non_existent)); -#if !defined(OS_ANDROID) - // This fails because MakeAbsoluteFilePath fails for non-existent files. - // Earlier versions of Bionic libc don't fail for non-existent files, so - // skip this check on Android. - EXPECT_FALSE(PathService::OverrideAndCreateIfNeeded(my_special_key, - non_existent, - false, - false)); -#endif - // This works because indicating that |non_existent| is absolute skips the - // internal MakeAbsoluteFilePath call. - EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key, - non_existent, - true, - false)); - // Check that the path has been overridden and no directory was created. - EXPECT_FALSE(butil::PathExists(non_existent)); - butil::FilePath path; - EXPECT_TRUE(PathService::Get(my_special_key, &path)); - EXPECT_EQ(non_existent, path); -#endif -} - -// Check if multiple overrides can co-exist. -TEST_F(PathServiceTest, OverrideMultiple) { - int my_special_key = 666; - butil::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - butil::FilePath fake_cache_dir1(temp_dir.path().AppendASCII("1")); - EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir1)); - EXPECT_TRUE(butil::PathExists(fake_cache_dir1)); - ASSERT_EQ(1, butil::WriteFile(fake_cache_dir1.AppendASCII("t1"), ".", 1)); - - butil::FilePath fake_cache_dir2(temp_dir.path().AppendASCII("2")); - EXPECT_TRUE(PathService::Override(my_special_key + 1, fake_cache_dir2)); - EXPECT_TRUE(butil::PathExists(fake_cache_dir2)); - ASSERT_EQ(1, butil::WriteFile(fake_cache_dir2.AppendASCII("t2"), ".", 1)); - - butil::FilePath result; - EXPECT_TRUE(PathService::Get(my_special_key, &result)); - // Override might have changed the path representation but our test file - // should be still there. - EXPECT_TRUE(butil::PathExists(result.AppendASCII("t1"))); - EXPECT_TRUE(PathService::Get(my_special_key + 1, &result)); - EXPECT_TRUE(butil::PathExists(result.AppendASCII("t2"))); -} - -TEST_F(PathServiceTest, RemoveOverride) { - // Before we start the test we have to call RemoveOverride at least once to - // clear any overrides that might have been left from other tests. - PathService::RemoveOverride(butil::DIR_TEMP); - - butil::FilePath original_user_data_dir; - EXPECT_TRUE(PathService::Get(butil::DIR_TEMP, &original_user_data_dir)); - EXPECT_FALSE(PathService::RemoveOverride(butil::DIR_TEMP)); - - butil::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - EXPECT_TRUE(PathService::Override(butil::DIR_TEMP, temp_dir.path())); - butil::FilePath new_user_data_dir; - EXPECT_TRUE(PathService::Get(butil::DIR_TEMP, &new_user_data_dir)); - EXPECT_NE(original_user_data_dir, new_user_data_dir); - - EXPECT_TRUE(PathService::RemoveOverride(butil::DIR_TEMP)); - EXPECT_TRUE(PathService::Get(butil::DIR_TEMP, &new_user_data_dir)); - EXPECT_EQ(original_user_data_dir, new_user_data_dir); -} diff --git a/test/process_metrics_unittest.cc b/test/process_metrics_unittest.cc deleted file mode 100644 index 906f61f170..0000000000 --- a/test/process_metrics_unittest.cc +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2013 The Chromium 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 "butil/process/process_metrics.h" - -#include -#include - -#include - - -namespace butil { -namespace debug { - -// Tests for SystemMetrics. -// Exists as a class so it can be a friend of SystemMetrics. -class SystemMetricsTest : public testing::Test { - public: - SystemMetricsTest() {} - - private: - DISALLOW_COPY_AND_ASSIGN(SystemMetricsTest); -}; - -///////////////////////////////////////////////////////////////////////////// - -#if defined(OS_LINUX) || defined(OS_ANDROID) -TEST_F(SystemMetricsTest, IsValidDiskName) { - std::string invalid_input1 = ""; - std::string invalid_input2 = "s"; - std::string invalid_input3 = "sdz+"; - std::string invalid_input4 = "hda0"; - std::string invalid_input5 = "mmcbl"; - std::string invalid_input6 = "mmcblka"; - std::string invalid_input7 = "mmcblkb"; - std::string invalid_input8 = "mmmblk0"; - - EXPECT_FALSE(IsValidDiskName(invalid_input1)); - EXPECT_FALSE(IsValidDiskName(invalid_input2)); - EXPECT_FALSE(IsValidDiskName(invalid_input3)); - EXPECT_FALSE(IsValidDiskName(invalid_input4)); - EXPECT_FALSE(IsValidDiskName(invalid_input5)); - EXPECT_FALSE(IsValidDiskName(invalid_input6)); - EXPECT_FALSE(IsValidDiskName(invalid_input7)); - EXPECT_FALSE(IsValidDiskName(invalid_input8)); - - std::string valid_input1 = "sda"; - std::string valid_input2 = "sdaaaa"; - std::string valid_input3 = "hdz"; - std::string valid_input4 = "mmcblk0"; - std::string valid_input5 = "mmcblk999"; - - EXPECT_TRUE(IsValidDiskName(valid_input1)); - EXPECT_TRUE(IsValidDiskName(valid_input2)); - EXPECT_TRUE(IsValidDiskName(valid_input3)); - EXPECT_TRUE(IsValidDiskName(valid_input4)); - EXPECT_TRUE(IsValidDiskName(valid_input5)); -} - -TEST_F(SystemMetricsTest, ParseMeminfo) { - struct SystemMemoryInfoKB meminfo; - std::string invalid_input1 = "abc"; - std::string invalid_input2 = "MemTotal:"; - // Partial file with no MemTotal - std::string invalid_input3 = - "MemFree: 3913968 kB\n" - "Buffers: 2348340 kB\n" - "Cached: 49071596 kB\n" - "SwapCached: 12 kB\n" - "Active: 36393900 kB\n" - "Inactive: 21221496 kB\n" - "Active(anon): 5674352 kB\n" - "Inactive(anon): 633992 kB\n"; - EXPECT_FALSE(ParseProcMeminfo(invalid_input1, &meminfo)); - EXPECT_FALSE(ParseProcMeminfo(invalid_input2, &meminfo)); - EXPECT_FALSE(ParseProcMeminfo(invalid_input3, &meminfo)); - - std::string valid_input1 = - "MemTotal: 3981504 kB\n" - "MemFree: 140764 kB\n" - "Buffers: 116480 kB\n" - "Cached: 406160 kB\n" - "SwapCached: 21304 kB\n" - "Active: 3152040 kB\n" - "Inactive: 472856 kB\n" - "Active(anon): 2972352 kB\n" - "Inactive(anon): 270108 kB\n" - "Active(file): 179688 kB\n" - "Inactive(file): 202748 kB\n" - "Unevictable: 0 kB\n" - "Mlocked: 0 kB\n" - "SwapTotal: 5832280 kB\n" - "SwapFree: 3672368 kB\n" - "Dirty: 184 kB\n" - "Writeback: 0 kB\n" - "AnonPages: 3101224 kB\n" - "Mapped: 142296 kB\n" - "Shmem: 140204 kB\n" - "Slab: 54212 kB\n" - "SReclaimable: 30936 kB\n" - "SUnreclaim: 23276 kB\n" - "KernelStack: 2464 kB\n" - "PageTables: 24812 kB\n" - "NFS_Unstable: 0 kB\n" - "Bounce: 0 kB\n" - "WritebackTmp: 0 kB\n" - "CommitLimit: 7823032 kB\n" - "Committed_AS: 7973536 kB\n" - "VmallocTotal: 34359738367 kB\n" - "VmallocUsed: 375940 kB\n" - "VmallocChunk: 34359361127 kB\n" - "DirectMap4k: 72448 kB\n" - "DirectMap2M: 4061184 kB\n"; - // output from a much older kernel where the Active and Inactive aren't - // broken down into anon and file and Huge Pages are enabled - std::string valid_input2 = - "MemTotal: 255908 kB\n" - "MemFree: 69936 kB\n" - "Buffers: 15812 kB\n" - "Cached: 115124 kB\n" - "SwapCached: 0 kB\n" - "Active: 92700 kB\n" - "Inactive: 63792 kB\n" - "HighTotal: 0 kB\n" - "HighFree: 0 kB\n" - "LowTotal: 255908 kB\n" - "LowFree: 69936 kB\n" - "SwapTotal: 524280 kB\n" - "SwapFree: 524200 kB\n" - "Dirty: 4 kB\n" - "Writeback: 0 kB\n" - "Mapped: 42236 kB\n" - "Slab: 25912 kB\n" - "Committed_AS: 118680 kB\n" - "PageTables: 1236 kB\n" - "VmallocTotal: 3874808 kB\n" - "VmallocUsed: 1416 kB\n" - "VmallocChunk: 3872908 kB\n" - "HugePages_Total: 0\n" - "HugePages_Free: 0\n" - "Hugepagesize: 4096 kB\n"; - - EXPECT_TRUE(ParseProcMeminfo(valid_input1, &meminfo)); - EXPECT_TRUE(meminfo.total == 3981504); - EXPECT_TRUE(meminfo.free == 140764); - EXPECT_TRUE(meminfo.buffers == 116480); - EXPECT_TRUE(meminfo.cached == 406160); - EXPECT_TRUE(meminfo.active_anon == 2972352); - EXPECT_TRUE(meminfo.active_file == 179688); - EXPECT_TRUE(meminfo.inactive_anon == 270108); - EXPECT_TRUE(meminfo.inactive_file == 202748); - EXPECT_TRUE(meminfo.swap_total == 5832280); - EXPECT_TRUE(meminfo.swap_free == 3672368); - EXPECT_TRUE(meminfo.dirty == 184); -#if defined(OS_CHROMEOS) - EXPECT_TRUE(meminfo.shmem == 140204); - EXPECT_TRUE(meminfo.slab == 54212); -#endif - EXPECT_TRUE(ParseProcMeminfo(valid_input2, &meminfo)); - EXPECT_TRUE(meminfo.total == 255908); - EXPECT_TRUE(meminfo.free == 69936); - EXPECT_TRUE(meminfo.buffers == 15812); - EXPECT_TRUE(meminfo.cached == 115124); - EXPECT_TRUE(meminfo.swap_total == 524280); - EXPECT_TRUE(meminfo.swap_free == 524200); - EXPECT_TRUE(meminfo.dirty == 4); -} - -TEST_F(SystemMetricsTest, ParseVmstat) { - struct SystemMemoryInfoKB meminfo; - // part of vmstat from a 3.2 kernel with numa enabled - std::string valid_input1 = - "nr_free_pages 905104\n" - "nr_inactive_anon 142478" - "nr_active_anon 1520046\n" - "nr_inactive_file 4481001\n" - "nr_active_file 8313439\n" - "nr_unevictable 5044\n" - "nr_mlock 5044\n" - "nr_anon_pages 1633780\n" - "nr_mapped 104742\n" - "nr_file_pages 12828218\n" - "nr_dirty 245\n" - "nr_writeback 0\n" - "nr_slab_reclaimable 831609\n" - "nr_slab_unreclaimable 41164\n" - "nr_page_table_pages 31470\n" - "nr_kernel_stack 1735\n" - "nr_unstable 0\n" - "nr_bounce 0\n" - "nr_vmscan_write 406\n" - "nr_vmscan_immediate_reclaim 281\n" - "nr_writeback_temp 0\n" - "nr_isolated_anon 0\n" - "nr_isolated_file 0\n" - "nr_shmem 28820\n" - "nr_dirtied 84674644\n" - "nr_written 75307109\n" - "nr_anon_transparent_hugepages 0\n" - "nr_dirty_threshold 1536206\n" - "nr_dirty_background_threshold 768103\n" - "pgpgin 30777108\n" - "pgpgout 319023278\n" - "pswpin 179\n" - "pswpout 406\n" - "pgalloc_dma 0\n" - "pgalloc_dma32 20833399\n" - "pgalloc_normal 1622609290\n" - "pgalloc_movable 0\n" - "pgfree 1644355583\n" - "pgactivate 75391882\n" - "pgdeactivate 4121019\n" - "pgfault 2542879679\n" - "pgmajfault 487192\n"; - std::string valid_input2 = - "nr_free_pages 180125\n" - "nr_inactive_anon 51\n" - "nr_active_anon 38832\n" - "nr_inactive_file 50171\n" - "nr_active_file 47510\n" - "nr_unevictable 0\n" - "nr_mlock 0\n" - "nr_anon_pages 38825\n" - "nr_mapped 24043\n" - "nr_file_pages 97733\n" - "nr_dirty 0\n" - "nr_writeback 0\n" - "nr_slab_reclaimable 4032\n" - "nr_slab_unreclaimable 2848\n" - "nr_page_table_pages 1505\n" - "nr_kernel_stack 626\n" - "nr_unstable 0\n" - "nr_bounce 0\n" - "nr_vmscan_write 0\n" - "nr_vmscan_immediate_reclaim 0\n" - "nr_writeback_temp 0\n" - "nr_isolated_anon 0\n" - "nr_isolated_file 0\n" - "nr_shmem 58\n" - "nr_dirtied 435358\n" - "nr_written 401258\n" - "nr_anon_transparent_hugepages 0\n" - "nr_dirty_threshold 18566\n" - "nr_dirty_background_threshold 4641\n" - "pgpgin 299464\n" - "pgpgout 2437788\n" - "pswpin 12\n" - "pswpout 901\n" - "pgalloc_normal 144213030\n" - "pgalloc_high 164501274\n" - "pgalloc_movable 0\n" - "pgfree 308894908\n" - "pgactivate 239320\n" - "pgdeactivate 1\n" - "pgfault 716044601\n" - "pgmajfault 2023\n" - "pgrefill_normal 0\n" - "pgrefill_high 0\n" - "pgrefill_movable 0\n"; - EXPECT_TRUE(ParseProcVmstat(valid_input1, &meminfo)); - EXPECT_TRUE(meminfo.pswpin == 179); - EXPECT_TRUE(meminfo.pswpout == 406); - EXPECT_TRUE(meminfo.pgmajfault == 487192); - EXPECT_TRUE(ParseProcVmstat(valid_input2, &meminfo)); - EXPECT_TRUE(meminfo.pswpin == 12); - EXPECT_TRUE(meminfo.pswpout == 901); - EXPECT_TRUE(meminfo.pgmajfault == 2023); -} -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - -#if defined(OS_LINUX) || defined(OS_ANDROID) -TEST(SystemMetrics2Test, GetSystemMemoryInfo) { - butil::SystemMemoryInfoKB info; - EXPECT_TRUE(butil::GetSystemMemoryInfo(&info)); - - // Ensure each field received a value. - EXPECT_GT(info.total, 0); - EXPECT_GT(info.free, 0); - EXPECT_GT(info.buffers, 0); - EXPECT_GT(info.cached, 0); - EXPECT_GT(info.active_anon, 0); - EXPECT_GT(info.inactive_anon, 0); - EXPECT_GT(info.active_file, 0); - EXPECT_GT(info.inactive_file, 0); - - // All the values should be less than the total amount of memory. - EXPECT_LT(info.free, info.total); - EXPECT_LT(info.buffers, info.total); - EXPECT_LT(info.cached, info.total); - EXPECT_LT(info.active_anon, info.total); - EXPECT_LT(info.inactive_anon, info.total); - EXPECT_LT(info.active_file, info.total); - EXPECT_LT(info.inactive_file, info.total); - -#if defined(OS_CHROMEOS) - // Chrome OS exposes shmem. - EXPECT_GT(info.shmem, 0); - EXPECT_LT(info.shmem, info.total); - // Chrome unit tests are not run on actual Chrome OS hardware, so gem_objects - // and gem_size cannot be tested here. -#endif -} -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - -#if defined(OS_WIN) -// TODO(estade): if possible, port this test. -TEST(ProcessMetricsTest, CalcFreeMemory) { - scoped_ptr metrics( - butil::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess())); - ASSERT_TRUE(NULL != metrics.get()); - - bool using_tcmalloc = false; - - // Typical values here is ~1900 for total and ~1000 for largest. Obviously - // it depends in what other tests have done to this process. - butil::FreeMBytes free_mem1 = {0}; - EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1)); - EXPECT_LT(10u, free_mem1.total); - EXPECT_LT(10u, free_mem1.largest); - EXPECT_GT(2048u, free_mem1.total); - EXPECT_GT(2048u, free_mem1.largest); - EXPECT_GE(free_mem1.total, free_mem1.largest); - EXPECT_TRUE(NULL != free_mem1.largest_ptr); - - // Allocate 20M and check again. It should have gone down. - const int kAllocMB = 20; - scoped_ptr alloc(new char[kAllocMB * 1024 * 1024]); - size_t expected_total = free_mem1.total - kAllocMB; - size_t expected_largest = free_mem1.largest; - - butil::FreeMBytes free_mem2 = {0}; - EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2)); - EXPECT_GE(free_mem2.total, free_mem2.largest); - // This test is flaky when using tcmalloc, because tcmalloc - // allocation strategy sometimes results in less than the - // full drop of 20Mb of free memory. - if (!using_tcmalloc) - EXPECT_GE(expected_total, free_mem2.total); - EXPECT_GE(expected_largest, free_mem2.largest); - EXPECT_TRUE(NULL != free_mem2.largest_ptr); -} -#endif // defined(OS_WIN) - -#if defined(OS_LINUX) || defined(OS_ANDROID) -TEST(ProcessMetricsTest, ParseProcStatCPU) { - // /proc/self/stat for a process running "top". - const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 " - "4202496 471 0 0 0 " - "12 16 0 0 " // <- These are the goods. - "20 0 1 0 121946157 15077376 314 18446744073709551615 4194304 " - "4246868 140733983044336 18446744073709551615 140244213071219 " - "0 0 0 138047495 0 0 0 17 1 0 0 0 0 0"; - EXPECT_EQ(12 + 16, butil::ParseProcStatCPU(kTopStat)); - - // cat /proc/self/stat on a random other machine I have. - const char kSelfStat[] = "5364 (cat) R 5354 5364 5354 34819 5364 " - "0 142 0 0 0 " - "0 0 0 0 " // <- No CPU, apparently. - "16 0 1 0 1676099790 2957312 114 4294967295 134512640 134528148 " - "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0"; - - EXPECT_EQ(0, butil::ParseProcStatCPU(kSelfStat)); -} -#endif // defined(OS_LINUX) || defined(OS_ANDROID) - -} // namespace debug -} // namespace butil diff --git a/test/process_util_unittest.cc b/test/process_util_unittest.cc deleted file mode 100644 index eddaaedd62..0000000000 --- a/test/process_util_unittest.cc +++ /dev/null @@ -1,883 +0,0 @@ -// 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. - -#define _CRT_SECURE_NO_WARNINGS - -#include - -#include "butil/command_line.h" -#include "butil/debug/alias.h" -#include "butil/debug/stack_trace.h" -#include "butil/files/file_path.h" -#include "butil/logging.h" -#include "butil/memory/scoped_ptr.h" -#include "butil/path_service.h" -#include "butil/posix/eintr_wrapper.h" -#include "butil/process/kill.h" -#include "butil/process/launch.h" -#include "butil/process/memory.h" -#include "butil/process/process.h" -#include "butil/process/process_metrics.h" -#include "butil/strings/string_number_conversions.h" -#include "butil/strings/utf_string_conversions.h" -#include "butil/synchronization/waitable_event.h" -#include "test/multiprocess_test.h" -#include "test/test_timeouts.h" -#include "butil/third_party/dynamic_annotations/dynamic_annotations.h" -#include "butil/threading/platform_thread.h" -#include -#include "test/multiprocess_func_list.h" - -#if defined(OS_LINUX) -#include -#include -#endif -#if defined(OS_POSIX) -#include -#include -#include -#include -#include -#include -#include -#endif -#if defined(OS_WIN) -#include -#include "butil/win/windows_version.h" -#endif -#if defined(OS_MACOSX) -#include -#include -#endif - -using butil::FilePath; - -namespace { - -#if defined(OS_ANDROID) -const char kShellPath[] = "/system/bin/sh"; -const char kPosixShell[] = "sh"; -#else -const char kShellPath[] = "/bin/sh"; -const char kPosixShell[] = "bash"; -#endif - -const char kSignalFileSlow[] = "SlowChildProcess.die"; -const char kSignalFileKill[] = "KilledChildProcess.die"; - -#if defined(OS_WIN) -const int kExpectedStillRunningExitCode = 0x102; -const int kExpectedKilledExitCode = 1; -#else -const int kExpectedStillRunningExitCode = 0; -#endif - -// Sleeps until file filename is created. -void WaitToDie(const char* filename) { - FILE* fp; - do { - butil::PlatformThread::Sleep(butil::TimeDelta::FromMilliseconds(10)); - fp = fopen(filename, "r"); - } while (!fp); - fclose(fp); -} - -// Signals children they should die now. -void SignalChildren(const char* filename) { - FILE* fp = fopen(filename, "w"); - fclose(fp); -} - -// Using a pipe to the child to wait for an event was considered, but -// there were cases in the past where pipes caused problems (other -// libraries closing the fds, child deadlocking). This is a simple -// case, so it's not worth the risk. Using wait loops is discouraged -// in most instances. -butil::TerminationStatus WaitForChildTermination(butil::ProcessHandle handle, - int* exit_code) { - // Now we wait until the result is something other than STILL_RUNNING. - butil::TerminationStatus status = butil::TERMINATION_STATUS_STILL_RUNNING; - const butil::TimeDelta kInterval = butil::TimeDelta::FromMilliseconds(20); - butil::TimeDelta waited; - do { - status = butil::GetTerminationStatus(handle, exit_code); - butil::PlatformThread::Sleep(kInterval); - waited += kInterval; - } while (status == butil::TERMINATION_STATUS_STILL_RUNNING && -// Waiting for more time for process termination on android devices. -#if defined(OS_ANDROID) - waited < TestTimeouts::large_test_timeout()); -#else - waited < TestTimeouts::action_max_timeout()); -#endif - - return status; -} - -} // namespace - -class ProcessUtilTest : public butil::MultiProcessTest { - public: -#if defined(OS_POSIX) - // Spawn a child process that counts how many file descriptors are open. - int CountOpenFDsInChild(); -#endif - // Converts the filename to a platform specific filepath. - // On Android files can not be created in arbitrary directories. - static std::string GetSignalFilePath(const char* filename); -}; - -std::string ProcessUtilTest::GetSignalFilePath(const char* filename) { -#if !defined(OS_ANDROID) - return filename; -#else - FilePath tmp_dir; - PathService::Get(butil::DIR_CACHE, &tmp_dir); - tmp_dir = tmp_dir.Append(filename); - return tmp_dir.value(); -#endif -} - -MULTIPROCESS_TEST_MAIN(SimpleChildProcess) { - return 0; -} - -// TODO(viettrungluu): This should be in a "MultiProcessTestTest". -TEST_F(ProcessUtilTest, SpawnChild) { - butil::ProcessHandle handle = SpawnChild("SimpleChildProcess"); - ASSERT_NE(butil::kNullProcessHandle, handle); - EXPECT_TRUE(butil::WaitForSingleProcess( - handle, TestTimeouts::action_max_timeout())); - butil::CloseProcessHandle(handle); -} - -MULTIPROCESS_TEST_MAIN(SlowChildProcess) { - WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileSlow).c_str()); - return 0; -} - -TEST_F(ProcessUtilTest, KillSlowChild) { - const std::string signal_file = - ProcessUtilTest::GetSignalFilePath(kSignalFileSlow); - remove(signal_file.c_str()); - butil::ProcessHandle handle = SpawnChild("SlowChildProcess"); - ASSERT_NE(butil::kNullProcessHandle, handle); - SignalChildren(signal_file.c_str()); - EXPECT_TRUE(butil::WaitForSingleProcess( - handle, TestTimeouts::action_max_timeout())); - butil::CloseProcessHandle(handle); - remove(signal_file.c_str()); -} - -// Times out on Linux and Win, flakes on other platforms, http://crbug.com/95058 -TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) { - const std::string signal_file = - ProcessUtilTest::GetSignalFilePath(kSignalFileSlow); - remove(signal_file.c_str()); - butil::ProcessHandle handle = SpawnChild("SlowChildProcess"); - ASSERT_NE(butil::kNullProcessHandle, handle); - - int exit_code = 42; - EXPECT_EQ(butil::TERMINATION_STATUS_STILL_RUNNING, - butil::GetTerminationStatus(handle, &exit_code)); - EXPECT_EQ(kExpectedStillRunningExitCode, exit_code); - - SignalChildren(signal_file.c_str()); - exit_code = 42; - butil::TerminationStatus status = - WaitForChildTermination(handle, &exit_code); - EXPECT_EQ(butil::TERMINATION_STATUS_NORMAL_TERMINATION, status); - EXPECT_EQ(0, exit_code); - butil::CloseProcessHandle(handle); - remove(signal_file.c_str()); -} - -#if defined(OS_WIN) -// TODO(cpu): figure out how to test this in other platforms. -TEST_F(ProcessUtilTest, GetProcId) { - butil::ProcessId id1 = butil::GetProcId(GetCurrentProcess()); - EXPECT_NE(0ul, id1); - butil::ProcessHandle handle = SpawnChild("SimpleChildProcess"); - ASSERT_NE(butil::kNullProcessHandle, handle); - butil::ProcessId id2 = butil::GetProcId(handle); - EXPECT_NE(0ul, id2); - EXPECT_NE(id1, id2); - butil::CloseProcessHandle(handle); -} -#endif - -#if !defined(OS_MACOSX) -// This test is disabled on Mac, since it's flaky due to ReportCrash -// taking a variable amount of time to parse and load the debug and -// symbol data for this unit test's executable before firing the -// signal handler. -// -// TODO(gspencer): turn this test process into a very small program -// with no symbols (instead of using the multiprocess testing -// framework) to reduce the ReportCrash overhead. -const char kSignalFileCrash[] = "CrashingChildProcess.die"; - -MULTIPROCESS_TEST_MAIN(CrashingChildProcess) { - WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileCrash).c_str()); -#if defined(OS_POSIX) - // Have to disable to signal handler for segv so we can get a crash - // instead of an abnormal termination through the crash dump handler. - ::signal(SIGSEGV, SIG_DFL); -#endif - // Make this process have a segmentation fault. - volatile int* oops = NULL; - *oops = 0xDEAD; - return 1; -} - -// This test intentionally crashes, so we don't need to run it under -// AddressSanitizer. -#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN) -#define MAYBE_GetTerminationStatusCrash DISABLED_GetTerminationStatusCrash -#else -#define MAYBE_GetTerminationStatusCrash GetTerminationStatusCrash -#endif -TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) { - const std::string signal_file = - ProcessUtilTest::GetSignalFilePath(kSignalFileCrash); - remove(signal_file.c_str()); - butil::ProcessHandle handle = SpawnChild("CrashingChildProcess"); - ASSERT_NE(butil::kNullProcessHandle, handle); - - int exit_code = 42; - EXPECT_EQ(butil::TERMINATION_STATUS_STILL_RUNNING, - butil::GetTerminationStatus(handle, &exit_code)); - EXPECT_EQ(kExpectedStillRunningExitCode, exit_code); - - SignalChildren(signal_file.c_str()); - exit_code = 42; - butil::TerminationStatus status = - WaitForChildTermination(handle, &exit_code); - EXPECT_EQ(butil::TERMINATION_STATUS_PROCESS_CRASHED, status); - -#if defined(OS_WIN) - EXPECT_EQ(0xc0000005, exit_code); -#elif defined(OS_POSIX) - int signaled = WIFSIGNALED(exit_code); - EXPECT_NE(0, signaled); - int signal = WTERMSIG(exit_code); - EXPECT_EQ(SIGSEGV, signal); -#endif - butil::CloseProcessHandle(handle); - - // Reset signal handlers back to "normal". - butil::debug::EnableInProcessStackDumping(); - remove(signal_file.c_str()); -} -#endif // !defined(OS_MACOSX) - -MULTIPROCESS_TEST_MAIN(KilledChildProcess) { - WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileKill).c_str()); -#if defined(OS_WIN) - // Kill ourselves. - HANDLE handle = ::OpenProcess(PROCESS_ALL_ACCESS, 0, ::GetCurrentProcessId()); - ::TerminateProcess(handle, kExpectedKilledExitCode); -#elif defined(OS_POSIX) - // Send a SIGKILL to this process, just like the OOM killer would. - ::kill(getpid(), SIGKILL); -#endif - return 1; -} - -TEST_F(ProcessUtilTest, GetTerminationStatusKill) { - const std::string signal_file = - ProcessUtilTest::GetSignalFilePath(kSignalFileKill); - remove(signal_file.c_str()); - butil::ProcessHandle handle = SpawnChild("KilledChildProcess"); - ASSERT_NE(butil::kNullProcessHandle, handle); - - int exit_code = 42; - EXPECT_EQ(butil::TERMINATION_STATUS_STILL_RUNNING, - butil::GetTerminationStatus(handle, &exit_code)); - EXPECT_EQ(kExpectedStillRunningExitCode, exit_code); - - SignalChildren(signal_file.c_str()); - exit_code = 42; - butil::TerminationStatus status = - WaitForChildTermination(handle, &exit_code); - EXPECT_EQ(butil::TERMINATION_STATUS_PROCESS_WAS_KILLED, status); -#if defined(OS_WIN) - EXPECT_EQ(kExpectedKilledExitCode, exit_code); -#elif defined(OS_POSIX) - int signaled = WIFSIGNALED(exit_code); - EXPECT_NE(0, signaled); - int signal = WTERMSIG(exit_code); - EXPECT_EQ(SIGKILL, signal); -#endif - butil::CloseProcessHandle(handle); - remove(signal_file.c_str()); -} - -// Ensure that the priority of a process is restored correctly after -// backgrounding and restoring. -// Note: a platform may not be willing or able to lower the priority of -// a process. The calls to SetProcessBackground should be noops then. -TEST_F(ProcessUtilTest, SetProcessBackgrounded) { - butil::ProcessHandle handle = SpawnChild("SimpleChildProcess"); - butil::Process process(handle); - int old_priority = process.GetPriority(); -#if defined(OS_WIN) - EXPECT_TRUE(process.SetProcessBackgrounded(true)); - EXPECT_TRUE(process.IsProcessBackgrounded()); - EXPECT_TRUE(process.SetProcessBackgrounded(false)); - EXPECT_FALSE(process.IsProcessBackgrounded()); -#else - process.SetProcessBackgrounded(true); - process.SetProcessBackgrounded(false); -#endif - int new_priority = process.GetPriority(); - EXPECT_EQ(old_priority, new_priority); -} - -// Same as SetProcessBackgrounded but to this very process. It uses -// a different code path at least for Windows. -TEST_F(ProcessUtilTest, SetProcessBackgroundedSelf) { - butil::Process process(butil::Process::Current().handle()); - int old_priority = process.GetPriority(); -#if defined(OS_WIN) - EXPECT_TRUE(process.SetProcessBackgrounded(true)); - EXPECT_TRUE(process.IsProcessBackgrounded()); - EXPECT_TRUE(process.SetProcessBackgrounded(false)); - EXPECT_FALSE(process.IsProcessBackgrounded()); -#else - process.SetProcessBackgrounded(true); - process.SetProcessBackgrounded(false); -#endif - int new_priority = process.GetPriority(); - EXPECT_EQ(old_priority, new_priority); -} - -#if defined(OS_WIN) -// TODO(estade): if possible, port this test. -TEST_F(ProcessUtilTest, GetAppOutput) { - // Let's create a decently long message. - std::string message; - for (int i = 0; i < 1025; i++) { // 1025 so it does not end on a kilo-byte - // boundary. - message += "Hello!"; - } - // cmd.exe's echo always adds a \r\n to its output. - std::string expected(message); - expected += "\r\n"; - - FilePath cmd(L"cmd.exe"); - CommandLine cmd_line(cmd); - cmd_line.AppendArg("/c"); - cmd_line.AppendArg("echo " + message + ""); - std::string output; - ASSERT_TRUE(butil::GetAppOutput(cmd_line, &output)); - EXPECT_EQ(expected, output); - - // Let's make sure stderr is ignored. - CommandLine other_cmd_line(cmd); - other_cmd_line.AppendArg("/c"); - // http://msdn.microsoft.com/library/cc772622.aspx - cmd_line.AppendArg("echo " + message + " >&2"); - output.clear(); - ASSERT_TRUE(butil::GetAppOutput(other_cmd_line, &output)); - EXPECT_EQ("", output); -} - -// TODO(estade): if possible, port this test. -TEST_F(ProcessUtilTest, LaunchAsUser) { - butil::UserTokenHandle token; - ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)); - butil::LaunchOptions options; - options.as_user = token; - EXPECT_TRUE(butil::LaunchProcess(MakeCmdLine("SimpleChildProcess"), options, - NULL)); -} - -static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle"; - -MULTIPROCESS_TEST_MAIN(TriggerEventChildProcess) { - std::string handle_value_string = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - kEventToTriggerHandleSwitch); - CHECK(!handle_value_string.empty()); - - uint64_t handle_value_uint64; - CHECK(butil::StringToUint64(handle_value_string, &handle_value_uint64)); - // Give ownership of the handle to |event|. - butil::WaitableEvent event(reinterpret_cast(handle_value_uint64)); - - event.Signal(); - - return 0; -} - -TEST_F(ProcessUtilTest, InheritSpecifiedHandles) { - // Manually create the event, so that it can be inheritable. - SECURITY_ATTRIBUTES security_attributes = {}; - security_attributes.nLength = static_cast(sizeof(security_attributes)); - security_attributes.lpSecurityDescriptor = NULL; - security_attributes.bInheritHandle = true; - - // Takes ownership of the event handle. - butil::WaitableEvent event( - CreateEvent(&security_attributes, true, false, NULL)); - butil::HandlesToInheritVector handles_to_inherit; - handles_to_inherit.push_back(event.handle()); - butil::LaunchOptions options; - options.handles_to_inherit = &handles_to_inherit; - - CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess"); - cmd_line.AppendSwitchASCII(kEventToTriggerHandleSwitch, - butil::Uint64ToString(reinterpret_cast(event.handle()))); - - // This functionality actually requires Vista or later. Make sure that it - // fails properly on XP. - if (butil::win::GetVersion() < butil::win::VERSION_VISTA) { - EXPECT_FALSE(butil::LaunchProcess(cmd_line, options, NULL)); - return; - } - - // Launch the process and wait for it to trigger the event. - ASSERT_TRUE(butil::LaunchProcess(cmd_line, options, NULL)); - EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout())); -} -#endif // defined(OS_WIN) - -#if defined(OS_POSIX) - -namespace { - -// Returns the maximum number of files that a process can have open. -// Returns 0 on error. -int GetMaxFilesOpenInProcess() { - struct rlimit rlim; - if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { - return 0; - } - - // rlim_t is a uint64_t - clip to maxint. We do this since FD #s are ints - // which are all 32 bits on the supported platforms. - rlim_t max_int = static_cast(std::numeric_limits::max()); - if (rlim.rlim_cur > max_int) { - return max_int; - } - - return rlim.rlim_cur; -} - -const int kChildPipe = 20; // FD # for write end of pipe in child process. - -} // namespace - -MULTIPROCESS_TEST_MAIN(ProcessUtilsLeakFDChildProcess) { - // This child process counts the number of open FDs, it then writes that - // number out to a pipe connected to the parent. - int num_open_files = 0; - int write_pipe = kChildPipe; - int max_files = GetMaxFilesOpenInProcess(); - for (int i = STDERR_FILENO + 1; i < max_files; i++) { - if (i != kChildPipe) { - int fd; - if ((fd = HANDLE_EINTR(dup(i))) != -1) { - close(fd); - num_open_files += 1; - } - } - } - - int written = HANDLE_EINTR(write(write_pipe, &num_open_files, - sizeof(num_open_files))); - DCHECK_EQ(static_cast(written), sizeof(num_open_files)); - int ret = IGNORE_EINTR(close(write_pipe)); - DPCHECK(ret == 0); - - return 0; -} - -int ProcessUtilTest::CountOpenFDsInChild() { - int fds[2]; - if (pipe(fds) < 0) - NOTREACHED(); - - butil::FileHandleMappingVector fd_mapping_vec; - fd_mapping_vec.push_back(std::pair(fds[1], kChildPipe)); - butil::LaunchOptions options; - options.fds_to_remap = &fd_mapping_vec; - butil::ProcessHandle handle = - SpawnChildWithOptions("ProcessUtilsLeakFDChildProcess", options); - CHECK(handle); - int ret = IGNORE_EINTR(close(fds[1])); - DPCHECK(ret == 0); - - // Read number of open files in client process from pipe; - int num_open_files = -1; - ssize_t bytes_read = - HANDLE_EINTR(read(fds[0], &num_open_files, sizeof(num_open_files))); - CHECK_EQ(bytes_read, static_cast(sizeof(num_open_files))); - -#if defined(THREAD_SANITIZER) - // Compiler-based ThreadSanitizer makes this test slow. - CHECK(butil::WaitForSingleProcess(handle, butil::TimeDelta::FromSeconds(3))); -#else - CHECK(butil::WaitForSingleProcess(handle, butil::TimeDelta::FromSeconds(1))); -#endif - butil::CloseProcessHandle(handle); - ret = IGNORE_EINTR(close(fds[0])); - DPCHECK(ret == 0); - - return num_open_files; -} - -#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) -// ProcessUtilTest.FDRemapping is flaky when ran under xvfb-run on Precise. -// The problem is 100% reproducible with both ASan and TSan. -// See http://crbug.com/136720. -#define MAYBE_FDRemapping DISABLED_FDRemapping -#else -#define MAYBE_FDRemapping FDRemapping -#endif -TEST_F(ProcessUtilTest, MAYBE_FDRemapping) { - int fds_before = CountOpenFDsInChild(); - - // open some dummy fds to make sure they don't propagate over to the - // child process. - int dev_null = open("/dev/null", O_RDONLY); - int sockets[2]; - socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); - - int fds_after = CountOpenFDsInChild(); - - ASSERT_EQ(fds_after, fds_before); - - int ret; - ret = IGNORE_EINTR(close(sockets[0])); - DPCHECK(ret == 0); - ret = IGNORE_EINTR(close(sockets[1])); - DPCHECK(ret == 0); - ret = IGNORE_EINTR(close(dev_null)); - DPCHECK(ret == 0); -} - -namespace { - -std::string TestLaunchProcess(const std::vector& args, - const butil::EnvironmentMap& env_changes, - const bool clear_environ, - const int clone_flags) { - butil::FileHandleMappingVector fds_to_remap; - - int fds[2]; - PCHECK(pipe(fds) == 0); - - fds_to_remap.push_back(std::make_pair(fds[1], 1)); - butil::LaunchOptions options; - options.wait = true; - options.environ = env_changes; - options.clear_environ = clear_environ; - options.fds_to_remap = &fds_to_remap; -#if defined(OS_LINUX) - options.clone_flags = clone_flags; -#else - CHECK_EQ(0, clone_flags); -#endif // OS_LINUX - EXPECT_TRUE(butil::LaunchProcess(args, options, NULL)); - PCHECK(IGNORE_EINTR(close(fds[1])) == 0); - - char buf[512]; - const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf))); - - PCHECK(IGNORE_EINTR(close(fds[0])) == 0); - - return std::string(buf, n); -} - -const char kLargeString[] = - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789" - "0123456789012345678901234567890123456789012345678901234567890123456789"; - -} // namespace - -TEST_F(ProcessUtilTest, LaunchProcess) { - butil::EnvironmentMap env_changes; - std::vector echo_base_test; - echo_base_test.push_back(kPosixShell); - echo_base_test.push_back("-c"); - echo_base_test.push_back("echo $BASE_TEST"); - - std::vector print_env; - print_env.push_back("/usr/bin/env"); - const int no_clone_flags = 0; - const bool no_clear_environ = false; - - const char kBaseTest[] = "BASE_TEST"; - - env_changes[kBaseTest] = "bar"; - EXPECT_EQ("bar\n", - TestLaunchProcess( - echo_base_test, env_changes, no_clear_environ, no_clone_flags)); - env_changes.clear(); - - EXPECT_EQ(0, setenv(kBaseTest, "testing", 1 /* override */)); - EXPECT_EQ("testing\n", - TestLaunchProcess( - echo_base_test, env_changes, no_clear_environ, no_clone_flags)); - - env_changes[kBaseTest] = std::string(); - EXPECT_EQ("\n", - TestLaunchProcess( - echo_base_test, env_changes, no_clear_environ, no_clone_flags)); - - env_changes[kBaseTest] = "foo"; - EXPECT_EQ("foo\n", - TestLaunchProcess( - echo_base_test, env_changes, no_clear_environ, no_clone_flags)); - - env_changes.clear(); - EXPECT_EQ(0, setenv(kBaseTest, kLargeString, 1 /* override */)); - EXPECT_EQ(std::string(kLargeString) + "\n", - TestLaunchProcess( - echo_base_test, env_changes, no_clear_environ, no_clone_flags)); - - env_changes[kBaseTest] = "wibble"; - EXPECT_EQ("wibble\n", - TestLaunchProcess( - echo_base_test, env_changes, no_clear_environ, no_clone_flags)); - -#if defined(OS_LINUX) - // Test a non-trival value for clone_flags. - // Don't test on Valgrind as it has limited support for clone(). - if (!RunningOnValgrind()) { - EXPECT_EQ( - "wibble\n", - TestLaunchProcess( - echo_base_test, env_changes, no_clear_environ, CLONE_FS | SIGCHLD)); - } - - EXPECT_EQ( - "BASE_TEST=wibble\n", - TestLaunchProcess( - print_env, env_changes, true /* clear_environ */, no_clone_flags)); - env_changes.clear(); - EXPECT_EQ( - "", - TestLaunchProcess( - print_env, env_changes, true /* clear_environ */, no_clone_flags)); -#endif -} - -TEST_F(ProcessUtilTest, GetAppOutput) { - std::string output; - -#if defined(OS_ANDROID) - std::vector argv; - argv.push_back("sh"); // Instead of /bin/sh, force path search to find it. - argv.push_back("-c"); - - argv.push_back("exit 0"); - EXPECT_TRUE(butil::GetAppOutput(CommandLine(argv), &output)); - EXPECT_STREQ("", output.c_str()); - - argv[2] = "exit 1"; - EXPECT_FALSE(butil::GetAppOutput(CommandLine(argv), &output)); - EXPECT_STREQ("", output.c_str()); - - argv[2] = "echo foobar42"; - EXPECT_TRUE(butil::GetAppOutput(CommandLine(argv), &output)); - EXPECT_STREQ("foobar42\n", output.c_str()); -#else - EXPECT_TRUE(butil::GetAppOutput(CommandLine(FilePath("true")), &output)); - EXPECT_STREQ("", output.c_str()); - - EXPECT_FALSE(butil::GetAppOutput(CommandLine(FilePath("false")), &output)); - - std::vector argv; - argv.push_back("/bin/echo"); - argv.push_back("-n"); - argv.push_back("foobar42"); - EXPECT_TRUE(butil::GetAppOutput(CommandLine(argv), &output)); - EXPECT_STREQ("foobar42", output.c_str()); -#endif // defined(OS_ANDROID) -} - -// Flakes on Android, crbug.com/375840 -#if defined(OS_ANDROID) -#define MAYBE_GetAppOutputRestricted DISABLED_GetAppOutputRestricted -#else -#define MAYBE_GetAppOutputRestricted GetAppOutputRestricted -#endif -TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestricted) { - // Unfortunately, since we can't rely on the path, we need to know where - // everything is. So let's use /bin/sh, which is on every POSIX system, and - // its built-ins. - std::vector argv; - argv.push_back(std::string(kShellPath)); // argv[0] - argv.push_back("-c"); // argv[1] - - // On success, should set |output|. We use |/bin/sh -c 'exit 0'| instead of - // |true| since the location of the latter may be |/bin| or |/usr/bin| (and we - // need absolute paths). - argv.push_back("exit 0"); // argv[2]; equivalent to "true" - std::string output = "abc"; - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 100)); - EXPECT_STREQ("", output.c_str()); - - argv[2] = "exit 1"; // equivalent to "false" - output = "before"; - EXPECT_FALSE(butil::GetAppOutputRestricted(CommandLine(argv), - &output, 100)); - EXPECT_STREQ("", output.c_str()); - - // Amount of output exactly equal to space allowed. - argv[2] = "echo 123456789"; // (the sh built-in doesn't take "-n") - output.clear(); - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 10)); - EXPECT_STREQ("123456789\n", output.c_str()); - - // Amount of output greater than space allowed. - output.clear(); - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 5)); - EXPECT_STREQ("12345", output.c_str()); - - // Amount of output less than space allowed. - output.clear(); - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 15)); - EXPECT_STREQ("123456789\n", output.c_str()); - - // Zero space allowed. - output = "abc"; - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 0)); - EXPECT_STREQ("", output.c_str()); -} - -#if !defined(OS_MACOSX) && !defined(OS_OPENBSD) -// TODO(benwells): GetAppOutputRestricted should terminate applications -// with SIGPIPE when we have enough output. http://crbug.com/88502 -TEST_F(ProcessUtilTest, GetAppOutputRestrictedSIGPIPE) { - std::vector argv; - std::string output; - - argv.push_back(std::string(kShellPath)); // argv[0] - argv.push_back("-c"); -#if defined(OS_ANDROID) - argv.push_back("while echo 12345678901234567890; do :; done"); - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 10)); - EXPECT_STREQ("1234567890", output.c_str()); -#else - argv.push_back("yes"); - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 10)); - EXPECT_STREQ("y\ny\ny\ny\ny\n", output.c_str()); -#endif -} -#endif - -#if defined(ADDRESS_SANITIZER) && defined(OS_MACOSX) && \ - defined(ARCH_CPU_64_BITS) -// Times out under AddressSanitizer on 64-bit OS X, see -// http://crbug.com/298197. -#define MAYBE_GetAppOutputRestrictedNoZombies \ - DISABLED_GetAppOutputRestrictedNoZombies -#else -#define MAYBE_GetAppOutputRestrictedNoZombies GetAppOutputRestrictedNoZombies -#endif -TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestrictedNoZombies) { - std::vector argv; - - argv.push_back(std::string(kShellPath)); // argv[0] - argv.push_back("-c"); // argv[1] - argv.push_back("echo 123456789012345678901234567890"); // argv[2] - - // Run |GetAppOutputRestricted()| 300 (> default per-user processes on Mac OS - // 10.5) times with an output buffer big enough to capture all output. - for (int i = 0; i < 300; i++) { - std::string output; - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 100)); - EXPECT_STREQ("123456789012345678901234567890\n", output.c_str()); - } - - // Ditto, but with an output buffer too small to capture all output. - for (int i = 0; i < 300; i++) { - std::string output; - EXPECT_TRUE(butil::GetAppOutputRestricted(CommandLine(argv), &output, 10)); - EXPECT_STREQ("1234567890", output.c_str()); - } -} - -TEST_F(ProcessUtilTest, GetAppOutputWithExitCode) { - // Test getting output from a successful application. - std::vector argv; - std::string output; - int exit_code; - argv.push_back(std::string(kShellPath)); // argv[0] - argv.push_back("-c"); // argv[1] - argv.push_back("echo foo"); // argv[2]; - EXPECT_TRUE(butil::GetAppOutputWithExitCode(CommandLine(argv), &output, - &exit_code)); - EXPECT_STREQ("foo\n", output.c_str()); - EXPECT_EQ(exit_code, 0); - - // Test getting output from an application which fails with a specific exit - // code. - output.clear(); - argv[2] = "echo foo; exit 2"; - EXPECT_TRUE(butil::GetAppOutputWithExitCode(CommandLine(argv), &output, - &exit_code)); - EXPECT_STREQ("foo\n", output.c_str()); - EXPECT_EQ(exit_code, 2); -} - -TEST_F(ProcessUtilTest, GetParentProcessId) { - butil::ProcessId ppid = butil::GetParentProcessId(butil::GetCurrentProcId()); - EXPECT_EQ(ppid, getppid()); -} - -// TODO(port): port those unit tests. -bool IsProcessDead(butil::ProcessHandle child) { - // waitpid() will actually reap the process which is exactly NOT what we - // want to test for. The good thing is that if it can't find the process - // we'll get a nice value for errno which we can test for. - const pid_t result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG)); - return result == -1 && errno == ECHILD; -} - -TEST_F(ProcessUtilTest, DelayedTermination) { - butil::ProcessHandle child_process = SpawnChild("process_util_test_never_die"); - ASSERT_TRUE(child_process); - butil::EnsureProcessTerminated(child_process); - butil::WaitForSingleProcess(child_process, butil::TimeDelta::FromSeconds(5)); - - // Check that process was really killed. - EXPECT_TRUE(IsProcessDead(child_process)); - butil::CloseProcessHandle(child_process); -} - -MULTIPROCESS_TEST_MAIN(process_util_test_never_die) { - while (1) { - sleep(500); - } - return 0; -} - -TEST_F(ProcessUtilTest, ImmediateTermination) { - butil::ProcessHandle child_process = - SpawnChild("process_util_test_die_immediately"); - ASSERT_TRUE(child_process); - // Give it time to die. - sleep(2); - butil::EnsureProcessTerminated(child_process); - - // Check that process was really killed. - EXPECT_TRUE(IsProcessDead(child_process)); - butil::CloseProcessHandle(child_process); -} - -MULTIPROCESS_TEST_MAIN(process_util_test_die_immediately) { - return 0; -} - -#endif // defined(OS_POSIX) diff --git a/test/singleton_unittest.cc b/test/singleton_unittest.cc index d65de8338e..9881165878 100644 --- a/test/singleton_unittest.cc +++ b/test/singleton_unittest.cc @@ -4,7 +4,6 @@ #include "butil/at_exit.h" #include "butil/memory/singleton.h" -#include "butil/path_service.h" #include namespace { diff --git a/test/stack_trace_unittest.cc b/test/stack_trace_unittest.cc index 1c249abfa3..85ce63f38c 100644 --- a/test/stack_trace_unittest.cc +++ b/test/stack_trace_unittest.cc @@ -8,24 +8,13 @@ #include "butil/debug/stack_trace.h" #include "butil/logging.h" -#include "butil/process/kill.h" -#include "butil/process/process_handle.h" #include "test/test_timeouts.h" #include -#include "test/multiprocess_func_list.h" - -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS) -#include "test/multiprocess_test.h" -#endif namespace butil { namespace debug { -#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_IOS) -typedef MultiProcessTest StackTraceTest; -#else typedef testing::Test StackTraceTest; -#endif // Note: On Linux, this test currently only fully works on Debug builds. // See comments in the #ifdef soup if you intend to change this. @@ -134,23 +123,6 @@ TEST_F(StackTraceTest, DebugPrintBacktrace) { #endif // !defined(__UCLIBC__) #if defined(OS_POSIX) && !defined(OS_ANDROID) -#if !defined(OS_IOS) -MULTIPROCESS_TEST_MAIN(MismatchedMallocChildProcess) { - char* pointer = new char[10]; - delete pointer; - return 2; -} - -// Regression test for StackDumpingSignalHandler async-signal unsafety. -// Combined with tcmalloc's debugallocation, that signal handler -// and e.g. mismatched new[]/delete would cause a hang because -// of re-entering malloc. -TEST_F(StackTraceTest, AsyncSignalUnsafeSignalHandlerHang) { - ProcessHandle child = SpawnChild("MismatchedMallocChildProcess"); - ASSERT_NE(kNullProcessHandle, child); - ASSERT_TRUE(WaitForSingleProcess(child, TestTimeouts::action_timeout())); -} -#endif // !defined(OS_IOS) namespace { diff --git a/test/test_file_util.cc b/test/test_file_util.cc deleted file mode 100644 index ec9dfee564..0000000000 --- a/test/test_file_util.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2013 The Chromium 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 "test/test_file_util.h" - -#include "test/test_timeouts.h" -#include "butil/threading/platform_thread.h" - -namespace butil { - -bool EvictFileFromSystemCacheWithRetry(const FilePath& path) { - const int kCycles = 10; - const TimeDelta kDelay = TestTimeouts::action_timeout() / kCycles; - for (int i = 0; i < kCycles; i++) { - if (EvictFileFromSystemCache(path)) - return true; - PlatformThread::Sleep(kDelay); - } - return false; -} - -} // namespace butil diff --git a/test/test_file_util.h b/test/test_file_util.h deleted file mode 100644 index 560f0710e1..0000000000 --- a/test/test_file_util.h +++ /dev/null @@ -1,91 +0,0 @@ -// 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_TEST_TEST_FILE_UTIL_H_ -#define BASE_TEST_TEST_FILE_UTIL_H_ - -// File utility functions used only by tests. - -#include - -#include "butil/compiler_specific.h" -#include "butil/files/file_path.h" - -#if defined(OS_ANDROID) -#include -#include "butil/basictypes.h" -#endif - -namespace butil { - -class FilePath; - -// Clear a specific file from the system cache like EvictFileFromSystemCache, -// but on failure it will sleep and retry. On the Windows buildbots, eviction -// can fail if the file is marked in use, and this will throw off timings that -// rely on uncached files. -bool EvictFileFromSystemCacheWithRetry(const FilePath& file); - -// Wrapper over butil::Delete. On Windows repeatedly invokes Delete in case -// of failure to workaround Windows file locking semantics. Returns true on -// success. -bool DieFileDie(const FilePath& file, bool recurse); - -// Clear a specific file from the system cache. After this call, trying -// to access this file will result in a cold load from the hard drive. -bool EvictFileFromSystemCache(const FilePath& file); - -#if defined(OS_WIN) -// Returns true if the volume supports Alternate Data Streams. -bool VolumeSupportsADS(const FilePath& path); - -// Returns true if the ZoneIdentifier is correctly set to "Internet" (3). -// Note that this function must be called from the same process as -// the one that set the zone identifier. I.e. don't use it in UI/automation -// based tests. -bool HasInternetZoneIdentifier(const FilePath& full_path); -#endif // defined(OS_WIN) - -} // namespace butil - -// TODO(brettw) move all of this to namespace butil. -namespace file_util { - -// In general it's not reliable to convert a FilePath to a wstring and we use -// string16 elsewhere for Unicode strings, but in tests it is frequently -// convenient to be able to compare paths to literals like L"foobar". -std::wstring FilePathAsWString(const butil::FilePath& path); -butil::FilePath WStringAsFilePath(const std::wstring& path); - -// For testing, make the file unreadable or unwritable. -// In POSIX, this does not apply to the root user. -bool MakeFileUnreadable(const butil::FilePath& path) WARN_UNUSED_RESULT; -bool MakeFileUnwritable(const butil::FilePath& path) WARN_UNUSED_RESULT; - -#if defined(OS_ANDROID) -// Register the ContentUriTestUrils JNI bindings. -bool RegisterContentUriTestUtils(JNIEnv* env); - -// Insert an image file into the MediaStore, and retrieve the content URI for -// testing purpose. -butil::FilePath InsertImageIntoMediaStore(const butil::FilePath& path); -#endif // defined(OS_ANDROID) - -// Saves the current permissions for a path, and restores it on destruction. -class PermissionRestorer { - public: - explicit PermissionRestorer(const butil::FilePath& path); - ~PermissionRestorer(); - - private: - const butil::FilePath path_; - void* info_; // The opaque stored permission information. - size_t length_; // The length of the stored permission information. - - DISALLOW_COPY_AND_ASSIGN(PermissionRestorer); -}; - -} // namespace file_util - -#endif // BASE_TEST_TEST_FILE_UTIL_H_ diff --git a/test/test_timeouts.cc b/test/test_timeouts.cc deleted file mode 100644 index 043f08f5e1..0000000000 --- a/test/test_timeouts.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "test/test_timeouts.h" - -#include - -#include "butil/command_line.h" -#include "butil/debug/debugger.h" -#include "butil/logging.h" -#include "butil/strings/string_number_conversions.h" -#include "test/test_switches.h" - -namespace { - -// ASan/TSan/MSan instrument each memory access. This may slow the execution -// down significantly. -#if defined(MEMORY_SANITIZER) -static const int kTimeoutMultiplier = 3; -#elif defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \ - defined(SYZYASAN) -static const int kTimeoutMultiplier = 2; -#else -static const int kTimeoutMultiplier = 1; -#endif - -const int kAlmostInfiniteTimeoutMs = 100000000; - -// Sets value to the greatest of: -// 1) value's current value multiplied by kTimeoutMultiplier (assuming -// InitializeTimeout is called only once per value). -// 2) min_value. -// 3) the numerical value given by switch_name on the command line multiplied -// by kTimeoutMultiplier. -void InitializeTimeout(const char* switch_name, int min_value, int* value) { - DCHECK(value); - if (CommandLine::ForCurrentProcess()->HasSwitch(switch_name)) { - std::string string_value( - CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switch_name)); - int timeout; - butil::StringToInt(string_value, &timeout); - *value = std::max(*value, timeout); - } - *value *= kTimeoutMultiplier; - *value = std::max(*value, min_value); -} - -// Sets value to the greatest of: -// 1) value's current value multiplied by kTimeoutMultiplier. -// 2) 0 -// 3) the numerical value given by switch_name on the command line multiplied -// by kTimeoutMultiplier. -void InitializeTimeout(const char* switch_name, int* value) { - InitializeTimeout(switch_name, 0, value); -} - -} // namespace - -// static -bool TestTimeouts::initialized_ = false; - -// The timeout values should increase in the order they appear in this block. -// static -int TestTimeouts::tiny_timeout_ms_ = 100; -int TestTimeouts::action_timeout_ms_ = 10000; -#ifndef NDEBUG -int TestTimeouts::action_max_timeout_ms_ = 45000; -#else -int TestTimeouts::action_max_timeout_ms_ = 30000; -#endif // NDEBUG -int TestTimeouts::large_test_timeout_ms_ = 10 * 60 * 1000; - -int TestTimeouts::test_launcher_timeout_ms_ = 45000; - -// static -void TestTimeouts::Initialize() { - if (initialized_) { - NOTREACHED(); - return; - } - initialized_ = true; - - if (butil::debug::BeingDebugged()) { - fprintf(stdout, - "Detected presence of a debugger, running without test timeouts.\n"); - } - - // Note that these timeouts MUST be initialized in the correct order as - // per the CHECKS below. - InitializeTimeout(switches::kTestTinyTimeout, &tiny_timeout_ms_); - InitializeTimeout(switches::kUiTestActionTimeout, - butil::debug::BeingDebugged() ? kAlmostInfiniteTimeoutMs - : tiny_timeout_ms_, - &action_timeout_ms_); - InitializeTimeout(switches::kUiTestActionMaxTimeout, action_timeout_ms_, - &action_max_timeout_ms_); - InitializeTimeout(switches::kTestLargeTimeout, action_max_timeout_ms_, - &large_test_timeout_ms_); - - // Test launcher timeout is independent from anything above action timeout. - InitializeTimeout(switches::kTestLauncherTimeout, action_timeout_ms_, - &test_launcher_timeout_ms_); - - // The timeout values should be increasing in the right order. - CHECK(tiny_timeout_ms_ <= action_timeout_ms_); - CHECK(action_timeout_ms_ <= action_max_timeout_ms_); - CHECK(action_max_timeout_ms_ <= large_test_timeout_ms_); - - CHECK(action_timeout_ms_ <= test_launcher_timeout_ms_); -} diff --git a/test/test_timeouts.h b/test/test_timeouts.h deleted file mode 100644 index 87bf3f7a71..0000000000 --- a/test/test_timeouts.h +++ /dev/null @@ -1,66 +0,0 @@ -// 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_TEST_TEST_TIMEOUTS_H_ -#define BASE_TEST_TEST_TIMEOUTS_H_ - -#include "butil/basictypes.h" -#include "butil/logging.h" -#include "butil/time/time.h" - -// Returns common timeouts to use in tests. Makes it possible to adjust -// the timeouts for different environments (like Valgrind). -class TestTimeouts { - public: - // Initializes the timeouts. Non thread-safe. Should be called exactly once - // by the test suite. - static void Initialize(); - - // Timeout for actions that are expected to finish "almost instantly". - static butil::TimeDelta tiny_timeout() { - DCHECK(initialized_); - return butil::TimeDelta::FromMilliseconds(tiny_timeout_ms_); - } - - // Timeout to wait for something to happen. If you are not sure - // which timeout to use, this is the one you want. - static butil::TimeDelta action_timeout() { - DCHECK(initialized_); - return butil::TimeDelta::FromMilliseconds(action_timeout_ms_); - } - - // Timeout longer than the above, but still suitable to use - // multiple times in a single test. Use if the timeout above - // is not sufficient. - static butil::TimeDelta action_max_timeout() { - DCHECK(initialized_); - return butil::TimeDelta::FromMilliseconds(action_max_timeout_ms_); - } - - // Timeout for a large test that may take a few minutes to run. - static butil::TimeDelta large_test_timeout() { - DCHECK(initialized_); - return butil::TimeDelta::FromMilliseconds(large_test_timeout_ms_); - } - - // Timeout for a single test launched used built-in test launcher. - // Do not use outside of the test launcher. - static butil::TimeDelta test_launcher_timeout() { - DCHECK(initialized_); - return butil::TimeDelta::FromMilliseconds(test_launcher_timeout_ms_); - } - - private: - static bool initialized_; - - static int tiny_timeout_ms_; - static int action_timeout_ms_; - static int action_max_timeout_ms_; - static int large_test_timeout_ms_; - static int test_launcher_timeout_ms_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(TestTimeouts); -}; - -#endif // BASE_TEST_TEST_TIMEOUTS_H_ From c4f27454a6c7ee9e76cbe00850e7a3279aa2cc49 Mon Sep 17 00:00:00 2001 From: Zhangyi Chen Date: Mon, 18 Sep 2017 17:24:33 +0800 Subject: [PATCH 04/11] Remove butil/nix, butil/third_party/xdg_mime/ and related stuff --- Makefile | 9 - src/butil/nix/mime_util_xdg.cc | 39 - src/butil/nix/mime_util_xdg.h | 31 - src/butil/nix/xdg_util.cc | 120 -- src/butil/nix/xdg_util.h | 74 -- src/butil/third_party/xdg_mime/LICENSE | 168 --- src/butil/third_party/xdg_mime/README | 8 - .../third_party/xdg_mime/README.chromium | 11 - src/butil/third_party/xdg_mime/compile.patch | 17 - src/butil/third_party/xdg_mime/xdgmime.c | 934 -------------- src/butil/third_party/xdg_mime/xdgmime.h | 133 -- src/butil/third_party/xdg_mime/xdgmimealias.c | 183 --- src/butil/third_party/xdg_mime/xdgmimealias.h | 51 - src/butil/third_party/xdg_mime/xdgmimecache.c | 1075 ----------------- src/butil/third_party/xdg_mime/xdgmimecache.h | 81 -- src/butil/third_party/xdg_mime/xdgmimeglob.c | 691 ----------- src/butil/third_party/xdg_mime/xdgmimeglob.h | 70 -- src/butil/third_party/xdg_mime/xdgmimeicon.c | 182 --- src/butil/third_party/xdg_mime/xdgmimeicon.h | 50 - src/butil/third_party/xdg_mime/xdgmimeint.c | 206 ---- src/butil/third_party/xdg_mime/xdgmimeint.h | 78 -- src/butil/third_party/xdg_mime/xdgmimemagic.c | 813 ------------- src/butil/third_party/xdg_mime/xdgmimemagic.h | 57 - .../third_party/xdg_mime/xdgmimeparent.c | 218 ---- .../third_party/xdg_mime/xdgmimeparent.h | 51 - src/butil/third_party/xdg_user_dirs/LICENSE | 21 - .../third_party/xdg_user_dirs/README.chromium | 7 - .../xdg_user_dirs/xdg_user_dir_lookup.cc | 232 ---- .../xdg_user_dirs/xdg_user_dir_lookup.h | 33 - test/file_util_unittest.cc | 1 - test/stack_trace_unittest.cc | 1 - 31 files changed, 5645 deletions(-) delete mode 100644 src/butil/nix/mime_util_xdg.cc delete mode 100644 src/butil/nix/mime_util_xdg.h delete mode 100644 src/butil/nix/xdg_util.cc delete mode 100644 src/butil/nix/xdg_util.h delete mode 100644 src/butil/third_party/xdg_mime/LICENSE delete mode 100644 src/butil/third_party/xdg_mime/README delete mode 100644 src/butil/third_party/xdg_mime/README.chromium delete mode 100644 src/butil/third_party/xdg_mime/compile.patch delete mode 100644 src/butil/third_party/xdg_mime/xdgmime.c delete mode 100644 src/butil/third_party/xdg_mime/xdgmime.h delete mode 100644 src/butil/third_party/xdg_mime/xdgmimealias.c delete mode 100644 src/butil/third_party/xdg_mime/xdgmimealias.h delete mode 100644 src/butil/third_party/xdg_mime/xdgmimecache.c delete mode 100644 src/butil/third_party/xdg_mime/xdgmimecache.h delete mode 100644 src/butil/third_party/xdg_mime/xdgmimeglob.c delete mode 100644 src/butil/third_party/xdg_mime/xdgmimeglob.h delete mode 100644 src/butil/third_party/xdg_mime/xdgmimeicon.c delete mode 100644 src/butil/third_party/xdg_mime/xdgmimeicon.h delete mode 100644 src/butil/third_party/xdg_mime/xdgmimeint.c delete mode 100644 src/butil/third_party/xdg_mime/xdgmimeint.h delete mode 100644 src/butil/third_party/xdg_mime/xdgmimemagic.c delete mode 100644 src/butil/third_party/xdg_mime/xdgmimemagic.h delete mode 100644 src/butil/third_party/xdg_mime/xdgmimeparent.c delete mode 100644 src/butil/third_party/xdg_mime/xdgmimeparent.h delete mode 100644 src/butil/third_party/xdg_user_dirs/LICENSE delete mode 100644 src/butil/third_party/xdg_user_dirs/README.chromium delete mode 100644 src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc delete mode 100644 src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.h diff --git a/Makefile b/Makefile index ae56177a1a..2cbc83ef8a 100644 --- a/Makefile +++ b/Makefile @@ -34,15 +34,6 @@ BUTIL_SOURCES = \ src/butil/third_party/nspr/prtime.cc \ src/butil/third_party/symbolize/demangle.cc \ src/butil/third_party/symbolize/symbolize.cc \ - src/butil/third_party/xdg_mime/xdgmime.c \ - src/butil/third_party/xdg_mime/xdgmimealias.c \ - src/butil/third_party/xdg_mime/xdgmimecache.c \ - src/butil/third_party/xdg_mime/xdgmimeglob.c \ - src/butil/third_party/xdg_mime/xdgmimeicon.c \ - src/butil/third_party/xdg_mime/xdgmimeint.c \ - src/butil/third_party/xdg_mime/xdgmimemagic.c \ - src/butil/third_party/xdg_mime/xdgmimeparent.c \ - src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc \ src/butil/third_party/snappy/snappy-sinksource.cc \ src/butil/third_party/snappy/snappy-stubs-internal.cc \ src/butil/third_party/snappy/snappy.cc \ diff --git a/src/butil/nix/mime_util_xdg.cc b/src/butil/nix/mime_util_xdg.cc deleted file mode 100644 index bfdfd57b1b..0000000000 --- a/src/butil/nix/mime_util_xdg.cc +++ /dev/null @@ -1,39 +0,0 @@ -// 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. - -#include "butil/nix/mime_util_xdg.h" - -#include "butil/files/file_path.h" -#include "butil/lazy_instance.h" -#include "butil/synchronization/lock.h" -#include "butil/third_party/xdg_mime/xdgmime.h" -#include "butil/threading/thread_restrictions.h" - -namespace butil { -namespace nix { - -namespace { - -// None of the XDG stuff is thread-safe, so serialize all access under -// this lock. -LazyInstance::Leaky g_mime_util_xdg_lock = LAZY_INSTANCE_INITIALIZER; - -} // namespace - -std::string GetFileMimeType(const FilePath& filepath) { - if (filepath.empty()) - return std::string(); - ThreadRestrictions::AssertIOAllowed(); - AutoLock scoped_lock(g_mime_util_xdg_lock.Get()); - return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str()); -} - -std::string GetDataMimeType(const std::string& data) { - ThreadRestrictions::AssertIOAllowed(); - AutoLock scoped_lock(g_mime_util_xdg_lock.Get()); - return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL); -} - -} // namespace nix -} // namespace butil diff --git a/src/butil/nix/mime_util_xdg.h b/src/butil/nix/mime_util_xdg.h deleted file mode 100644 index 8901f1817d..0000000000 --- a/src/butil/nix/mime_util_xdg.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_NIX_MIME_UTIL_XDG_H_ -#define BASE_NIX_MIME_UTIL_XDG_H_ - -#include - -#include "butil/base_export.h" -#include "butil/build_config.h" - -namespace butil { - -class FilePath; - -namespace nix { - -// Gets the mime type for a file based on its filename. The file path does not -// have to exist. Please note because it doesn't touch the disk, this does not -// work for directories. -// If the mime type is unknown, this will return application/octet-stream. -BASE_EXPORT std::string GetFileMimeType(const FilePath& filepath); - -// Get the mime type for a byte vector. -BASE_EXPORT std::string GetDataMimeType(const std::string& data); - -} // namespace nix -} // namespace butil - -#endif // BASE_NIX_MIME_UTIL_XDG_H_ diff --git a/src/butil/nix/xdg_util.cc b/src/butil/nix/xdg_util.cc deleted file mode 100644 index 7327404dad..0000000000 --- a/src/butil/nix/xdg_util.cc +++ /dev/null @@ -1,120 +0,0 @@ -// 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. - -#include "butil/nix/xdg_util.h" - -#include - -#include "butil/base_paths.h" -#include "butil/environment.h" -#include "butil/file_util.h" -#include "butil/files/file_path.h" -#include "butil/path_service.h" -#include "butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.h" - -namespace { - -// The KDE session version environment variable used in KDE 4. -const char kKDE4SessionEnvVar[] = "KDE_SESSION_VERSION"; - -} // namespace - -namespace butil { -namespace nix { - -const char kDotConfigDir[] = ".config"; -const char kXdgConfigHomeEnvVar[] = "XDG_CONFIG_HOME"; - -FilePath GetXDGDirectory(Environment* env, const char* env_name, - const char* fallback_dir) { - FilePath path; - std::string env_value; - if (env->GetVar(env_name, &env_value) && !env_value.empty()) { - path = FilePath(env_value); - } else { - PathService::Get(butil::DIR_HOME, &path); - path = path.Append(fallback_dir); - } - return path.StripTrailingSeparators(); -} - -FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) { - FilePath path; - char* xdg_dir = xdg_user_dir_lookup(dir_name); - if (xdg_dir) { - path = FilePath(xdg_dir); - free(xdg_dir); - } else { - PathService::Get(butil::DIR_HOME, &path); - path = path.Append(fallback_dir); - } - return path.StripTrailingSeparators(); -} - -DesktopEnvironment GetDesktopEnvironment(Environment* env) { - // XDG_CURRENT_DESKTOP is the newest standard circa 2012. - std::string xdg_current_desktop; - if (env->GetVar("XDG_CURRENT_DESKTOP", &xdg_current_desktop)) { - // Not all desktop environments set this env var as of this writing. - if (xdg_current_desktop == "Unity") - return DESKTOP_ENVIRONMENT_UNITY; - else if (xdg_current_desktop == "GNOME") - return DESKTOP_ENVIRONMENT_GNOME; - } - - // DESKTOP_SESSION was what everyone used in 2010. - std::string desktop_session; - if (env->GetVar("DESKTOP_SESSION", &desktop_session)) { - if (desktop_session == "gnome") { - return DESKTOP_ENVIRONMENT_GNOME; - } else if (desktop_session == "kde4") { - return DESKTOP_ENVIRONMENT_KDE4; - } else if (desktop_session == "kde") { - // This may mean KDE4 on newer systems, so we have to check. - if (env->HasVar(kKDE4SessionEnvVar)) - return DESKTOP_ENVIRONMENT_KDE4; - return DESKTOP_ENVIRONMENT_KDE3; - } else if (desktop_session.find("xfce") != std::string::npos || - desktop_session == "xubuntu") { - return DESKTOP_ENVIRONMENT_XFCE; - } - } - - // Fall back on some older environment variables. - // Useful particularly in the DESKTOP_SESSION=default case. - if (env->HasVar("GNOME_DESKTOP_SESSION_ID")) { - return DESKTOP_ENVIRONMENT_GNOME; - } else if (env->HasVar("KDE_FULL_SESSION")) { - if (env->HasVar(kKDE4SessionEnvVar)) - return DESKTOP_ENVIRONMENT_KDE4; - return DESKTOP_ENVIRONMENT_KDE3; - } - - return DESKTOP_ENVIRONMENT_OTHER; -} - -const char* GetDesktopEnvironmentName(DesktopEnvironment env) { - switch (env) { - case DESKTOP_ENVIRONMENT_OTHER: - return NULL; - case DESKTOP_ENVIRONMENT_GNOME: - return "GNOME"; - case DESKTOP_ENVIRONMENT_KDE3: - return "KDE3"; - case DESKTOP_ENVIRONMENT_KDE4: - return "KDE4"; - case DESKTOP_ENVIRONMENT_UNITY: - return "UNITY"; - case DESKTOP_ENVIRONMENT_XFCE: - return "XFCE"; - } - return NULL; -} - -const char* GetDesktopEnvironmentName(Environment* env) { - return GetDesktopEnvironmentName(GetDesktopEnvironment(env)); -} - -} // namespace nix -} // namespace butil diff --git a/src/butil/nix/xdg_util.h b/src/butil/nix/xdg_util.h deleted file mode 100644 index c1caa7a325..0000000000 --- a/src/butil/nix/xdg_util.h +++ /dev/null @@ -1,74 +0,0 @@ -// 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_NIX_XDG_UTIL_H_ -#define BASE_NIX_XDG_UTIL_H_ - -// XDG refers to http://en.wikipedia.org/wiki/Freedesktop.org . -// This file contains utilities found across free desktop environments. -// -// TODO(brettw) this file should be in app/x11, but is currently used by -// net. We should have a net API to allow the embedder to specify the behavior -// that it uses XDG for, and then move this file. - -#include "butil/base_export.h" - -#ifdef nix -#error asdf -#endif - -namespace butil { - -class Environment; -class FilePath; - -namespace nix { - -// The default XDG config directory name. -BASE_EXPORT extern const char kDotConfigDir[]; - -// The XDG config directory environment variable. -BASE_EXPORT extern const char kXdgConfigHomeEnvVar[]; - -// Utility function for getting XDG directories. -// |env_name| is the name of an environment variable that we want to use to get -// a directory path. |fallback_dir| is the directory relative to $HOME that we -// use if |env_name| cannot be found or is empty. |fallback_dir| may be NULL. -// Examples of |env_name| are XDG_CONFIG_HOME and XDG_DATA_HOME. -BASE_EXPORT FilePath GetXDGDirectory(Environment* env, const char* env_name, - const char* fallback_dir); - -// Wrapper around xdg_user_dir_lookup() from src/butil/third_party/xdg-user-dirs -// This looks up "well known" user directories like the desktop and music -// folder. Examples of |dir_name| are DESKTOP and MUSIC. -BASE_EXPORT FilePath GetXDGUserDirectory(const char* dir_name, - const char* fallback_dir); - -enum DesktopEnvironment { - DESKTOP_ENVIRONMENT_OTHER, - DESKTOP_ENVIRONMENT_GNOME, - // KDE3 and KDE4 are sufficiently different that we count - // them as two different desktop environments here. - DESKTOP_ENVIRONMENT_KDE3, - DESKTOP_ENVIRONMENT_KDE4, - DESKTOP_ENVIRONMENT_UNITY, - DESKTOP_ENVIRONMENT_XFCE, -}; - -// Return an entry from the DesktopEnvironment enum with a best guess -// of which desktop environment we're using. We use this to know when -// to attempt to use preferences from the desktop environment -- -// proxy settings, password manager, etc. -BASE_EXPORT DesktopEnvironment GetDesktopEnvironment(Environment* env); - -// Return a string representation of the given desktop environment. -// May return NULL in the case of DESKTOP_ENVIRONMENT_OTHER. -BASE_EXPORT const char* GetDesktopEnvironmentName(DesktopEnvironment env); -// Convenience wrapper that calls GetDesktopEnvironment() first. -BASE_EXPORT const char* GetDesktopEnvironmentName(Environment* env); - -} // namespace nix -} // namespace butil - -#endif // BASE_NIX_XDG_UTIL_H_ diff --git a/src/butil/third_party/xdg_mime/LICENSE b/src/butil/third_party/xdg_mime/LICENSE deleted file mode 100644 index 55fedcff5a..0000000000 --- a/src/butil/third_party/xdg_mime/LICENSE +++ /dev/null @@ -1,168 +0,0 @@ -Licensed under the Academic Free License version 2.0 (below) -Or under the following terms: - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the -Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. - - --------------------------------------------------------------------------------- -Academic Free License v. 2.0 --------------------------------------------------------------------------------- - -This Academic Free License (the "License") applies to any original work of -authorship (the "Original Work") whose owner (the "Licensor") has placed the -following notice immediately following the copyright notice for the Original -Work: - -Licensed under the Academic Free License version 2.0 -1) Grant of Copyright License. Licensor hereby grants You a world-wide, -royalty-free, non-exclusive, perpetual, sublicenseable license to do the -following: - -a) to reproduce the Original Work in copies; -b) to prepare derivative works ("Derivative Works") based upon the Original - Work; -c) to distribute copies of the Original Work and Derivative Works to the - public; -d) to perform the Original Work publicly; and -e) to display the Original Work publicly. - -2) Grant of Patent License. Licensor hereby grants You a world-wide, -royalty-free, non-exclusive, perpetual, sublicenseable license, under patent -claims owned or controlled by the Licensor that are embodied in the Original -Work as furnished by the Licensor, to make, use, sell and offer for sale the -Original Work and Derivative Works. - -3) Grant of Source Code License. The term "Source Code" means the preferred -form of the Original Work for making modifications to it and all available -documentation describing how to modify the Original Work. Licensor hereby -agrees to provide a machine-readable copy of the Source Code of the Original -Work along with each copy of the Original Work that Licensor distributes. -Licensor reserves the right to satisfy this obligation by placing a -machine-readable copy of the Source Code in an information repository -reasonably calculated to permit inexpensive and convenient access by You for as -long as Licensor continues to distribute the Original Work, and by publishing -the address of that information repository in a notice immediately following -the copyright notice that applies to the Original Work. - -4) Exclusions From License Grant. Neither the names of Licensor, nor the names -of any contributors to the Original Work, nor any of their trademarks or -service marks, may be used to endorse or promote products derived from this -Original Work without express prior written permission of the Licensor. Nothing -in this License shall be deemed to grant any rights to trademarks, copyrights, -patents, trade secrets or any other intellectual property of Licensor except as -expressly stated herein. No patent license is granted to make, use, sell or -offer to sell embodiments of any patent claims other than the licensed claims -defined in Section 2. No right is granted to the trademarks of Licensor even if -such marks are included in the Original Work. Nothing in this License shall be -interpreted to prohibit Licensor from licensing under different terms from this -License any Original Work that Licensor otherwise would have a right to -license. - -5) This section intentionally omitted. - -6) Attribution Rights. You must retain, in the Source Code of any Derivative -Works that You create, all copyright, patent or trademark notices from the -Source Code of the Original Work, as well as any notices of licensing and any -descriptive text identified therein as an "Attribution Notice." You must cause -the Source Code for any Derivative Works that You create to carry a prominent -Attribution Notice reasonably calculated to inform recipients that You have -modified the Original Work. - -7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that -the copyright in and to the Original Work and the patent rights granted herein -by Licensor are owned by the Licensor or are sublicensed to You under the terms -of this License with the permission of the contributor(s) of those copyrights -and patent rights. Except as expressly stated in the immediately proceeding -sentence, the Original Work is provided under this License on an "AS IS" BASIS -and WITHOUT WARRANTY, either express or implied, including, without limitation, -the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. -This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No -license to Original Work is granted hereunder except under this disclaimer. - -8) Limitation of Liability. Under no circumstances and under no legal theory, -whether in tort (including negligence), contract, or otherwise, shall the -Licensor be liable to any person for any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License -or the use of the Original Work including, without limitation, damages for loss -of goodwill, work stoppage, computer failure or malfunction, or any and all -other commercial damages or losses. This limitation of liability shall not -apply to liability for death or personal injury resulting from Licensor's -negligence to the extent applicable law prohibits such limitation. Some -jurisdictions do not allow the exclusion or limitation of incidental or -consequential damages, so this exclusion and limitation may not apply to You. - -9) Acceptance and Termination. If You distribute copies of the Original Work or -a Derivative Work, You must make a reasonable effort under the circumstances to -obtain the express assent of recipients to the terms of this License. Nothing -else but this License (or another written agreement between Licensor and You) -grants You permission to create Derivative Works based upon the Original Work -or to exercise any of the rights granted in Section 1 herein, and any attempt -to do so except under the terms of this License (or another written agreement -between Licensor and You) is expressly prohibited by U.S. copyright law, the -equivalent laws of other countries, and by international treaty. Therefore, by -exercising any of the rights granted to You in Section 1 herein, You indicate -Your acceptance of this License and all of its terms and conditions. - -10) Termination for Patent Action. This License shall terminate automatically -and You may no longer exercise any of the rights granted to You by this License -as of the date You commence an action, including a cross-claim or counterclaim, -for patent infringement (i) against Licensor with respect to a patent -applicable to software or (ii) against any entity with respect to a patent -applicable to the Original Work (but excluding combinations of the Original -Work with other software or hardware). - -11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this -License may be brought only in the courts of a jurisdiction wherein the -Licensor resides or in which Licensor conducts its primary business, and under -the laws of that jurisdiction excluding its conflict-of-law provisions. The -application of the United Nations Convention on Contracts for the International -Sale of Goods is expressly excluded. Any use of the Original Work outside the -scope of this License or after its termination shall be subject to the -requirements and penalties of the U.S. Copyright Act, 17 U.S.C. 101 et seq., -the equivalent laws of other countries, and international treaty. This section -shall survive the termination of this License. - -12) Attorneys Fees. In any action to enforce the terms of this License or -seeking damages relating thereto, the prevailing party shall be entitled to -recover its costs and expenses, including, without limitation, reasonable -attorneys' fees and costs incurred in connection with such action, including -any appeal of such action. This section shall survive the termination of this -License. - -13) Miscellaneous. This License represents the complete agreement concerning -the subject matter hereof. If any provision of this License is held to be -unenforceable, such provision shall be reformed only to the extent necessary to -make it enforceable. - -14) Definition of "You" in This License. "You" throughout this License, whether -in upper or lower case, means an individual or a legal entity exercising rights -under, and complying with all of the terms of, this License. For legal -entities, "You" includes any entity that controls, is controlled by, or is -under common control with you. For purposes of this definition, "control" means -(i) the power, direct or indirect, to cause the direction or management of such -entity, whether by contract or otherwise, or (ii) ownership of fifty percent -(50%) or more of the outstanding shares, or (iii) beneficial ownership of such -entity. - -15) Right to Use. You may use the Original Work in all ways not otherwise -restricted or conditioned by this License or by law, and Licensor promises not -to interfere with or be responsible for such uses by You. - -This license is Copyright (C) 2003 Lawrence E. Rosen. All rights reserved. -Permission is hereby granted to copy and distribute this license without -modification. This license may not be modified without the express written -permission of its copyright owner. diff --git a/src/butil/third_party/xdg_mime/README b/src/butil/third_party/xdg_mime/README deleted file mode 100644 index e7f3f6859d..0000000000 --- a/src/butil/third_party/xdg_mime/README +++ /dev/null @@ -1,8 +0,0 @@ -This module is a simple module that parses the proposed MIME spec listed -at http://freedesktop.org/. It is currently targetted at version 0.12. -There are no formal releases planned for this module, and it is not -intended to be installed at this time. Rather, it is meant to be used -by other libraries or applications to add support for the MIME system. - -It is dual-licensed under the terms of the GNU Lesser General Public -License, and the Academic Free License, version 2.0. diff --git a/src/butil/third_party/xdg_mime/README.chromium b/src/butil/third_party/xdg_mime/README.chromium deleted file mode 100644 index 29d239a4ce..0000000000 --- a/src/butil/third_party/xdg_mime/README.chromium +++ /dev/null @@ -1,11 +0,0 @@ -Name: xdg-mime -URL: http://freedesktop.org -License: Academic Free License version 2.0 or LGPL v2 - -The code in this directory is synced from: -git://anongit.freedesktop.org/xdg/xdgmime -@ 2cdd8d36d7930d5a594587286cb1949ff62f7027 on 2012/08/06. - -In addition, we have the following patch(es): -- compile.patch: small tweaks to make the code compile. -- Added a LICENSE file. diff --git a/src/butil/third_party/xdg_mime/compile.patch b/src/butil/third_party/xdg_mime/compile.patch deleted file mode 100644 index cd055edc03..0000000000 --- a/src/butil/third_party/xdg_mime/compile.patch +++ /dev/null @@ -1,17 +0,0 @@ ---- a/xdgmimecache.c -+++ b/xdgmimecache.c -@@ -40,6 +40,8 @@ - - #include /* for ntohl/ntohs */ - -+#define HAVE_MMAP 1 -+ - #ifdef HAVE_MMAP - #include - #else -@@ -1000,5 +1002,3 @@ - dump_glob_node (cache, offset + 20 * j, 0); - } - } -- -- diff --git a/src/butil/third_party/xdg_mime/xdgmime.c b/src/butil/third_party/xdg_mime/xdgmime.c deleted file mode 100644 index c7b16bbca7..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmime.c +++ /dev/null @@ -1,934 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmime.c: XDG Mime Spec mime resolver. Based on version 0.11 of the spec. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2003,2004 Red Hat, Inc. - * Copyright (C) 2003,2004 Jonathan Blandford - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "xdgmime.h" -#include "xdgmimeint.h" -#include "xdgmimeglob.h" -#include "xdgmimemagic.h" -#include "xdgmimealias.h" -#include "xdgmimeicon.h" -#include "xdgmimeparent.h" -#include "xdgmimecache.h" -#include -#include -#include -#include -#include -#include -#include - -typedef struct XdgDirTimeList XdgDirTimeList; -typedef struct XdgCallbackList XdgCallbackList; - -static int need_reread = TRUE; -static time_t last_stat_time = 0; - -static XdgGlobHash *global_hash = NULL; -static XdgMimeMagic *global_magic = NULL; -static XdgAliasList *alias_list = NULL; -static XdgParentList *parent_list = NULL; -static XdgDirTimeList *dir_time_list = NULL; -static XdgCallbackList *callback_list = NULL; -static XdgIconList *icon_list = NULL; -static XdgIconList *generic_icon_list = NULL; - -XdgMimeCache **_caches = NULL; -static int n_caches = 0; - -const char xdg_mime_type_unknown[] = "application/octet-stream"; -const char xdg_mime_type_empty[] = "application/x-zerosize"; -const char xdg_mime_type_textplain[] = "text/plain"; - - -enum -{ - XDG_CHECKED_UNCHECKED, - XDG_CHECKED_VALID, - XDG_CHECKED_INVALID -}; - -struct XdgDirTimeList -{ - time_t mtime; - char *directory_name; - int checked; - XdgDirTimeList *next; -}; - -struct XdgCallbackList -{ - XdgCallbackList *next; - XdgCallbackList *prev; - int callback_id; - XdgMimeCallback callback; - void *data; - XdgMimeDestroy destroy; -}; - -/* Function called by xdg_run_command_on_dirs. If it returns TRUE, further - * directories aren't looked at */ -typedef int (*XdgDirectoryFunc) (const char *directory, - void *user_data); - -static void -xdg_dir_time_list_add (char *file_name, - time_t mtime) -{ - XdgDirTimeList *list; - - for (list = dir_time_list; list; list = list->next) - { - if (strcmp (list->directory_name, file_name) == 0) - { - free (file_name); - return; - } - } - - list = calloc (1, sizeof (XdgDirTimeList)); - list->checked = XDG_CHECKED_UNCHECKED; - list->directory_name = file_name; - list->mtime = mtime; - list->next = dir_time_list; - dir_time_list = list; -} - -static void -xdg_dir_time_list_free (XdgDirTimeList *list) -{ - XdgDirTimeList *next; - - while (list) - { - next = list->next; - free (list->directory_name); - free (list); - list = next; - } -} - -static int -xdg_mime_init_from_directory (const char *directory) -{ - char *file_name; - struct stat st; - - assert (directory != NULL); - - file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); - if (stat (file_name, &st) == 0) - { - XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name); - - if (cache != NULL) - { - xdg_dir_time_list_add (file_name, st.st_mtime); - - _caches = realloc (_caches, sizeof (XdgMimeCache *) * (n_caches + 2)); - _caches[n_caches] = cache; - _caches[n_caches + 1] = NULL; - n_caches++; - - return FALSE; - } - } - free (file_name); - - file_name = malloc (strlen (directory) + strlen ("/mime/globs2") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/globs2"); - if (stat (file_name, &st) == 0) - { - _xdg_mime_glob_read_from_file (global_hash, file_name, TRUE); - xdg_dir_time_list_add (file_name, st.st_mtime); - } - else - { - free (file_name); - file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/globs"); - if (stat (file_name, &st) == 0) - { - _xdg_mime_glob_read_from_file (global_hash, file_name, FALSE); - xdg_dir_time_list_add (file_name, st.st_mtime); - } - else - { - free (file_name); - } - } - - file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/magic"); - if (stat (file_name, &st) == 0) - { - _xdg_mime_magic_read_from_file (global_magic, file_name); - xdg_dir_time_list_add (file_name, st.st_mtime); - } - else - { - free (file_name); - } - - file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/aliases"); - _xdg_mime_alias_read_from_file (alias_list, file_name); - free (file_name); - - file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/subclasses"); - _xdg_mime_parent_read_from_file (parent_list, file_name); - free (file_name); - - file_name = malloc (strlen (directory) + strlen ("/mime/icons") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/icons"); - _xdg_mime_icon_read_from_file (icon_list, file_name); - free (file_name); - - file_name = malloc (strlen (directory) + strlen ("/mime/generic-icons") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/generic-icons"); - _xdg_mime_icon_read_from_file (generic_icon_list, file_name); - free (file_name); - - return FALSE; /* Keep processing */ -} - -/* Runs a command on all the directories in the search path */ -static void -xdg_run_command_on_dirs (XdgDirectoryFunc func, - void *user_data) -{ - const char *xdg_data_home; - const char *xdg_data_dirs; - const char *ptr; - - xdg_data_home = getenv ("XDG_DATA_HOME"); - if (xdg_data_home) - { - if ((func) (xdg_data_home, user_data)) - return; - } - else - { - const char *home; - - home = getenv ("HOME"); - if (home != NULL) - { - char *guessed_xdg_home; - int stop_processing; - - guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1); - strcpy (guessed_xdg_home, home); - strcat (guessed_xdg_home, "/.local/share/"); - stop_processing = (func) (guessed_xdg_home, user_data); - free (guessed_xdg_home); - - if (stop_processing) - return; - } - } - - xdg_data_dirs = getenv ("XDG_DATA_DIRS"); - if (xdg_data_dirs == NULL) - xdg_data_dirs = "/usr/local/share/:/usr/share/"; - - ptr = xdg_data_dirs; - - while (*ptr != '\000') - { - const char *end_ptr; - char *dir; - int len; - int stop_processing; - - end_ptr = ptr; - while (*end_ptr != ':' && *end_ptr != '\000') - end_ptr ++; - - if (end_ptr == ptr) - { - ptr++; - continue; - } - - if (*end_ptr == ':') - len = end_ptr - ptr; - else - len = end_ptr - ptr + 1; - dir = malloc (len + 1); - strncpy (dir, ptr, len); - dir[len] = '\0'; - stop_processing = (func) (dir, user_data); - free (dir); - - if (stop_processing) - return; - - ptr = end_ptr; - } -} - -/* Checks file_path to make sure it has the same mtime as last time it was - * checked. If it has a different mtime, or if the file doesn't exist, it - * returns FALSE. - * - * FIXME: This doesn't protect against permission changes. - */ -static int -xdg_check_file (const char *file_path, - int *exists) -{ - struct stat st; - - /* If the file exists */ - if (stat (file_path, &st) == 0) - { - XdgDirTimeList *list; - - if (exists) - *exists = TRUE; - - for (list = dir_time_list; list; list = list->next) - { - if (! strcmp (list->directory_name, file_path)) - { - if (st.st_mtime == list->mtime) - list->checked = XDG_CHECKED_VALID; - else - list->checked = XDG_CHECKED_INVALID; - - return (list->checked != XDG_CHECKED_VALID); - } - } - return TRUE; - } - - if (exists) - *exists = FALSE; - - return FALSE; -} - -static int -xdg_check_dir (const char *directory, - int *invalid_dir_list) -{ - int invalid, exists; - char *file_name; - - assert (directory != NULL); - - /* Check the mime.cache file */ - file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache"); - invalid = xdg_check_file (file_name, &exists); - free (file_name); - if (invalid) - { - *invalid_dir_list = TRUE; - return TRUE; - } - else if (exists) - { - return FALSE; - } - - /* Check the globs file */ - file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/globs"); - invalid = xdg_check_file (file_name, NULL); - free (file_name); - if (invalid) - { - *invalid_dir_list = TRUE; - return TRUE; - } - - /* Check the magic file */ - file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1); - strcpy (file_name, directory); strcat (file_name, "/mime/magic"); - invalid = xdg_check_file (file_name, NULL); - free (file_name); - if (invalid) - { - *invalid_dir_list = TRUE; - return TRUE; - } - - return FALSE; /* Keep processing */ -} - -/* Walks through all the mime files stat()ing them to see if they've changed. - * Returns TRUE if they have. */ -static int -xdg_check_dirs (void) -{ - XdgDirTimeList *list; - int invalid_dir_list = FALSE; - - for (list = dir_time_list; list; list = list->next) - list->checked = XDG_CHECKED_UNCHECKED; - - xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir, - &invalid_dir_list); - - if (invalid_dir_list) - return TRUE; - - for (list = dir_time_list; list; list = list->next) - { - if (list->checked != XDG_CHECKED_VALID) - return TRUE; - } - - return FALSE; -} - -/* We want to avoid stat()ing on every single mime call, so we only look for - * newer files every 5 seconds. This will return TRUE if we need to reread the - * mime data from disk. - */ -static int -xdg_check_time_and_dirs (void) -{ - struct timeval tv; - time_t current_time; - int retval = FALSE; - - gettimeofday (&tv, NULL); - current_time = tv.tv_sec; - - if (current_time >= last_stat_time + 5) - { - retval = xdg_check_dirs (); - last_stat_time = current_time; - } - - return retval; -} - -/* Called in every public function. It reloads the hash function if need be. - */ -static void -xdg_mime_init (void) -{ - if (xdg_check_time_and_dirs ()) - { - xdg_mime_shutdown (); - } - - if (need_reread) - { - global_hash = _xdg_glob_hash_new (); - global_magic = _xdg_mime_magic_new (); - alias_list = _xdg_mime_alias_list_new (); - parent_list = _xdg_mime_parent_list_new (); - icon_list = _xdg_mime_icon_list_new (); - generic_icon_list = _xdg_mime_icon_list_new (); - - xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory, - NULL); - - need_reread = FALSE; - } -} - -const char * -xdg_mime_get_mime_type_for_data (const void *data, - size_t len, - int *result_prio) -{ - const char *mime_type; - - if (len == 0) - { - *result_prio = 100; - return XDG_MIME_TYPE_EMPTY; - } - - xdg_mime_init (); - - if (_caches) - mime_type = _xdg_mime_cache_get_mime_type_for_data (data, len, result_prio); - else - mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, result_prio, NULL, 0); - - if (mime_type) - return mime_type; - - return _xdg_binary_or_text_fallback(data, len); -} - -const char * -xdg_mime_get_mime_type_for_file (const char *file_name, - struct stat *statbuf) -{ - const char *mime_type; - /* currently, only a few globs occur twice, and none - * more often, so 5 seems plenty. - */ - const char *mime_types[5]; - FILE *file; - unsigned char *data; - int max_extent; - int bytes_read; - struct stat buf; - const char *base_name; - int n; - - if (file_name == NULL) - return NULL; - if (! _xdg_utf8_validate (file_name)) - return NULL; - - xdg_mime_init (); - - if (_caches) - return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf); - - base_name = _xdg_get_base_name (file_name); - n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5); - - if (n == 1) - return mime_types[0]; - - if (!statbuf) - { - if (stat (file_name, &buf) != 0) - return XDG_MIME_TYPE_UNKNOWN; - - statbuf = &buf; - } - - if (!S_ISREG (statbuf->st_mode)) - return XDG_MIME_TYPE_UNKNOWN; - - /* FIXME: Need to make sure that max_extent isn't totally broken. This could - * be large and need getting from a stream instead of just reading it all - * in. */ - max_extent = _xdg_mime_magic_get_buffer_extents (global_magic); - data = malloc (max_extent); - if (data == NULL) - return XDG_MIME_TYPE_UNKNOWN; - - file = fopen (file_name, "r"); - if (file == NULL) - { - free (data); - return XDG_MIME_TYPE_UNKNOWN; - } - - bytes_read = fread (data, 1, max_extent, file); - if (ferror (file)) - { - free (data); - fclose (file); - return XDG_MIME_TYPE_UNKNOWN; - } - - mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL, - mime_types, n); - - free (data); - fclose (file); - - if (mime_type) - return mime_type; - - return _xdg_binary_or_text_fallback(data, bytes_read); -} - -const char * -xdg_mime_get_mime_type_from_file_name (const char *file_name) -{ - const char *mime_type; - - xdg_mime_init (); - - if (_caches) - return _xdg_mime_cache_get_mime_type_from_file_name (file_name); - - if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1)) - return mime_type; - else - return XDG_MIME_TYPE_UNKNOWN; -} - -int -xdg_mime_get_mime_types_from_file_name (const char *file_name, - const char *mime_types[], - int n_mime_types) -{ - xdg_mime_init (); - - if (_caches) - return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types, n_mime_types); - - return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types); -} - -int -xdg_mime_is_valid_mime_type (const char *mime_type) -{ - /* FIXME: We should make this a better test - */ - return _xdg_utf8_validate (mime_type); -} - -void -xdg_mime_shutdown (void) -{ - XdgCallbackList *list; - - /* FIXME: Need to make this (and the whole library) thread safe */ - if (dir_time_list) - { - xdg_dir_time_list_free (dir_time_list); - dir_time_list = NULL; - } - - if (global_hash) - { - _xdg_glob_hash_free (global_hash); - global_hash = NULL; - } - if (global_magic) - { - _xdg_mime_magic_free (global_magic); - global_magic = NULL; - } - - if (alias_list) - { - _xdg_mime_alias_list_free (alias_list); - alias_list = NULL; - } - - if (parent_list) - { - _xdg_mime_parent_list_free (parent_list); - parent_list = NULL; - } - - if (icon_list) - { - _xdg_mime_icon_list_free (icon_list); - icon_list = NULL; - } - - if (generic_icon_list) - { - _xdg_mime_icon_list_free (generic_icon_list); - generic_icon_list = NULL; - } - - if (_caches) - { - int i; - - for (i = 0; i < n_caches; i++) - _xdg_mime_cache_unref (_caches[i]); - free (_caches); - _caches = NULL; - n_caches = 0; - } - - for (list = callback_list; list; list = list->next) - (list->callback) (list->data); - - need_reread = TRUE; -} - -int -xdg_mime_get_max_buffer_extents (void) -{ - xdg_mime_init (); - - if (_caches) - return _xdg_mime_cache_get_max_buffer_extents (); - - return _xdg_mime_magic_get_buffer_extents (global_magic); -} - -const char * -_xdg_mime_unalias_mime_type (const char *mime_type) -{ - const char *lookup; - - if (_caches) - return _xdg_mime_cache_unalias_mime_type (mime_type); - - if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL) - return lookup; - - return mime_type; -} - -const char * -xdg_mime_unalias_mime_type (const char *mime_type) -{ - xdg_mime_init (); - - return _xdg_mime_unalias_mime_type (mime_type); -} - -int -_xdg_mime_mime_type_equal (const char *mime_a, - const char *mime_b) -{ - const char *unalias_a, *unalias_b; - - unalias_a = _xdg_mime_unalias_mime_type (mime_a); - unalias_b = _xdg_mime_unalias_mime_type (mime_b); - - if (strcmp (unalias_a, unalias_b) == 0) - return 1; - - return 0; -} - -int -xdg_mime_mime_type_equal (const char *mime_a, - const char *mime_b) -{ - xdg_mime_init (); - - return _xdg_mime_mime_type_equal (mime_a, mime_b); -} - -int -xdg_mime_media_type_equal (const char *mime_a, - const char *mime_b) -{ - char *sep; - - sep = strchr (mime_a, '/'); - - if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0) - return 1; - - return 0; -} - -#if 1 -static int -xdg_mime_is_super_type (const char *mime) -{ - int length; - const char *type; - - length = strlen (mime); - type = &(mime[length - 2]); - - if (strcmp (type, "/*") == 0) - return 1; - - return 0; -} -#endif - -int -_xdg_mime_mime_type_subclass (const char *mime, - const char *base) -{ - const char *umime, *ubase; - const char **parents; - - if (_caches) - return _xdg_mime_cache_mime_type_subclass (mime, base); - - umime = _xdg_mime_unalias_mime_type (mime); - ubase = _xdg_mime_unalias_mime_type (base); - - if (strcmp (umime, ubase) == 0) - return 1; - -#if 1 - /* Handle supertypes */ - if (xdg_mime_is_super_type (ubase) && - xdg_mime_media_type_equal (umime, ubase)) - return 1; -#endif - - /* Handle special cases text/plain and application/octet-stream */ - if (strcmp (ubase, "text/plain") == 0 && - strncmp (umime, "text/", 5) == 0) - return 1; - - if (strcmp (ubase, "application/octet-stream") == 0) - return 1; - - parents = _xdg_mime_parent_list_lookup (parent_list, umime); - for (; parents && *parents; parents++) - { - if (_xdg_mime_mime_type_subclass (*parents, ubase)) - return 1; - } - - return 0; -} - -int -xdg_mime_mime_type_subclass (const char *mime, - const char *base) -{ - xdg_mime_init (); - - return _xdg_mime_mime_type_subclass (mime, base); -} - -char ** -xdg_mime_list_mime_parents (const char *mime) -{ - const char **parents; - char **result; - int i, n; - - if (_caches) - return _xdg_mime_cache_list_mime_parents (mime); - - parents = xdg_mime_get_mime_parents (mime); - - if (!parents) - return NULL; - - for (i = 0; parents[i]; i++) ; - - n = (i + 1) * sizeof (char *); - result = (char **) malloc (n); - memcpy (result, parents, n); - - return result; -} - -const char ** -xdg_mime_get_mime_parents (const char *mime) -{ - const char *umime; - - xdg_mime_init (); - - umime = _xdg_mime_unalias_mime_type (mime); - - return _xdg_mime_parent_list_lookup (parent_list, umime); -} - -void -xdg_mime_dump (void) -{ - xdg_mime_init(); - - printf ("*** ALIASES ***\n\n"); - _xdg_mime_alias_list_dump (alias_list); - printf ("\n*** PARENTS ***\n\n"); - _xdg_mime_parent_list_dump (parent_list); - printf ("\n*** CACHE ***\n\n"); - _xdg_glob_hash_dump (global_hash); - printf ("\n*** GLOBS ***\n\n"); - _xdg_glob_hash_dump (global_hash); - printf ("\n*** GLOBS REVERSE TREE ***\n\n"); - _xdg_mime_cache_glob_dump (); -} - - -/* Registers a function to be called every time the mime database reloads its files - */ -int -xdg_mime_register_reload_callback (XdgMimeCallback callback, - void *data, - XdgMimeDestroy destroy) -{ - XdgCallbackList *list_el; - static int callback_id = 1; - - /* Make a new list element */ - list_el = calloc (1, sizeof (XdgCallbackList)); - list_el->callback_id = callback_id; - list_el->callback = callback; - list_el->data = data; - list_el->destroy = destroy; - list_el->next = callback_list; - if (list_el->next) - list_el->next->prev = list_el; - - callback_list = list_el; - callback_id ++; - - return callback_id - 1; -} - -void -xdg_mime_remove_callback (int callback_id) -{ - XdgCallbackList *list; - - for (list = callback_list; list; list = list->next) - { - if (list->callback_id == callback_id) - { - if (list->next) - list->next = list->prev; - - if (list->prev) - list->prev->next = list->next; - else - callback_list = list->next; - - /* invoke the destroy handler */ - (list->destroy) (list->data); - free (list); - return; - } - } -} - -const char * -xdg_mime_get_icon (const char *mime) -{ - xdg_mime_init (); - - if (_caches) - return _xdg_mime_cache_get_icon (mime); - - return _xdg_mime_icon_list_lookup (icon_list, mime); -} - -const char * -xdg_mime_get_generic_icon (const char *mime) -{ - xdg_mime_init (); - - if (_caches) - return _xdg_mime_cache_get_generic_icon (mime); - - return _xdg_mime_icon_list_lookup (generic_icon_list, mime); -} diff --git a/src/butil/third_party/xdg_mime/xdgmime.h b/src/butil/third_party/xdg_mime/xdgmime.h deleted file mode 100644 index 6a34edfc38..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmime.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmime.h: XDG Mime Spec mime resolver. Based on version 0.11 of the spec. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2003 Red Hat, Inc. - * Copyright (C) 2003 Jonathan Blandford - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - - -#ifndef __XDG_MIME_H__ -#define __XDG_MIME_H__ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#ifdef XDG_PREFIX -#define XDG_ENTRY(func) _XDG_ENTRY2(XDG_PREFIX,func) -#define _XDG_ENTRY2(prefix,func) _XDG_ENTRY3(prefix,func) -#define _XDG_ENTRY3(prefix,func) prefix##_##func - -#define XDG_RESERVED_ENTRY(func) _XDG_RESERVED_ENTRY2(XDG_PREFIX,func) -#define _XDG_RESERVED_ENTRY2(prefix,func) _XDG_RESERVED_ENTRY3(prefix,func) -#define _XDG_RESERVED_ENTRY3(prefix,func) _##prefix##_##func -#endif - -typedef void (*XdgMimeCallback) (void *user_data); -typedef void (*XdgMimeDestroy) (void *user_data); - - -#ifdef XDG_PREFIX -#define xdg_mime_get_mime_type_for_data XDG_ENTRY(get_mime_type_for_data) -#define xdg_mime_get_mime_type_for_file XDG_ENTRY(get_mime_type_for_file) -#define xdg_mime_get_mime_type_from_file_name XDG_ENTRY(get_mime_type_from_file_name) -#define xdg_mime_get_mime_types_from_file_name XDG_ENTRY(get_mime_types_from_file_name) -#define xdg_mime_is_valid_mime_type XDG_ENTRY(is_valid_mime_type) -#define xdg_mime_mime_type_equal XDG_ENTRY(mime_type_equal) -#define xdg_mime_media_type_equal XDG_ENTRY(media_type_equal) -#define xdg_mime_mime_type_subclass XDG_ENTRY(mime_type_subclass) -#define xdg_mime_get_mime_parents XDG_ENTRY(get_mime_parents) -#define xdg_mime_list_mime_parents XDG_ENTRY(list_mime_parents) -#define xdg_mime_unalias_mime_type XDG_ENTRY(unalias_mime_type) -#define xdg_mime_get_max_buffer_extents XDG_ENTRY(get_max_buffer_extents) -#define xdg_mime_shutdown XDG_ENTRY(shutdown) -#define xdg_mime_dump XDG_ENTRY(dump) -#define xdg_mime_register_reload_callback XDG_ENTRY(register_reload_callback) -#define xdg_mime_remove_callback XDG_ENTRY(remove_callback) -#define xdg_mime_type_unknown XDG_ENTRY(type_unknown) -#define xdg_mime_type_empty XDG_ENTRY(type_empty) -#define xdg_mime_type_textplain XDG_ENTRY(type_textplain) -#define xdg_mime_get_icon XDG_ENTRY(get_icon) -#define xdg_mime_get_generic_icon XDG_ENTRY(get_generic_icon) - -#define _xdg_mime_mime_type_equal XDG_RESERVED_ENTRY(mime_type_equal) -#define _xdg_mime_mime_type_subclass XDG_RESERVED_ENTRY(mime_type_subclass) -#define _xdg_mime_unalias_mime_type XDG_RESERVED_ENTRY(unalias_mime_type) -#endif - -extern const char xdg_mime_type_unknown[]; -extern const char xdg_mime_type_empty[]; -extern const char xdg_mime_type_textplain[]; -#define XDG_MIME_TYPE_UNKNOWN xdg_mime_type_unknown -#define XDG_MIME_TYPE_EMPTY xdg_mime_type_empty -#define XDG_MIME_TYPE_TEXTPLAIN xdg_mime_type_textplain - -const char *xdg_mime_get_mime_type_for_data (const void *data, - size_t len, - int *result_prio); -const char *xdg_mime_get_mime_type_for_file (const char *file_name, - struct stat *statbuf); -const char *xdg_mime_get_mime_type_from_file_name (const char *file_name); -int xdg_mime_get_mime_types_from_file_name(const char *file_name, - const char *mime_types[], - int n_mime_types); -int xdg_mime_is_valid_mime_type (const char *mime_type); -int xdg_mime_mime_type_equal (const char *mime_a, - const char *mime_b); -int xdg_mime_media_type_equal (const char *mime_a, - const char *mime_b); -int xdg_mime_mime_type_subclass (const char *mime_a, - const char *mime_b); - /* xdg_mime_get_mime_parents() is deprecated since it does - * not work correctly with caches. Use xdg_mime_list_parents() - * instead, but notice that that function expects you to free - * the array it returns. - */ -const char **xdg_mime_get_mime_parents (const char *mime); -char ** xdg_mime_list_mime_parents (const char *mime); -const char *xdg_mime_unalias_mime_type (const char *mime); -const char *xdg_mime_get_icon (const char *mime); -const char *xdg_mime_get_generic_icon (const char *mime); -int xdg_mime_get_max_buffer_extents (void); -void xdg_mime_shutdown (void); -void xdg_mime_dump (void); -int xdg_mime_register_reload_callback (XdgMimeCallback callback, - void *data, - XdgMimeDestroy destroy); -void xdg_mime_remove_callback (int callback_id); - - /* Private versions of functions that don't call xdg_mime_init () */ -int _xdg_mime_mime_type_equal (const char *mime_a, - const char *mime_b); -int _xdg_mime_mime_type_subclass (const char *mime, - const char *base); -const char *_xdg_mime_unalias_mime_type (const char *mime); - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif /* __XDG_MIME_H__ */ diff --git a/src/butil/third_party/xdg_mime/xdgmimealias.c b/src/butil/third_party/xdg_mime/xdgmimealias.c deleted file mode 100644 index 95beff88bd..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimealias.c +++ /dev/null @@ -1,183 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimealias.c: Private file. Datastructure for storing the aliases. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2004 Red Hat, Inc. - * Copyright (C) 2004 Matthias Clasen - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "xdgmimealias.h" -#include "xdgmimeint.h" -#include -#include -#include -#include -#include - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -typedef struct XdgAlias XdgAlias; - -struct XdgAlias -{ - char *alias; - char *mime_type; -}; - -struct XdgAliasList -{ - struct XdgAlias *aliases; - int n_aliases; -}; - -XdgAliasList * -_xdg_mime_alias_list_new (void) -{ - XdgAliasList *list; - - list = malloc (sizeof (XdgAliasList)); - - list->aliases = NULL; - list->n_aliases = 0; - - return list; -} - -void -_xdg_mime_alias_list_free (XdgAliasList *list) -{ - int i; - - if (list->aliases) - { - for (i = 0; i < list->n_aliases; i++) - { - free (list->aliases[i].alias); - free (list->aliases[i].mime_type); - } - free (list->aliases); - } - free (list); -} - -static int -alias_entry_cmp (const void *v1, const void *v2) -{ - return strcmp (((XdgAlias *)v1)->alias, ((XdgAlias *)v2)->alias); -} - -const char * -_xdg_mime_alias_list_lookup (XdgAliasList *list, - const char *alias) -{ - XdgAlias *entry; - XdgAlias key; - - if (list->n_aliases > 0) - { - key.alias = (char *)alias; - key.mime_type = NULL; - - entry = bsearch (&key, list->aliases, list->n_aliases, - sizeof (XdgAlias), alias_entry_cmp); - if (entry) - return entry->mime_type; - } - - return NULL; -} - -void -_xdg_mime_alias_read_from_file (XdgAliasList *list, - const char *file_name) -{ - FILE *file; - char line[255]; - int alloc; - - file = fopen (file_name, "r"); - - if (file == NULL) - return; - - /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. - * Blah */ - alloc = list->n_aliases + 16; - list->aliases = realloc (list->aliases, alloc * sizeof (XdgAlias)); - while (fgets (line, 255, file) != NULL) - { - char *sep; - if (line[0] == '#') - continue; - - sep = strchr (line, ' '); - if (sep == NULL) - continue; - *(sep++) = '\000'; - sep[strlen (sep) -1] = '\000'; - if (list->n_aliases == alloc) - { - alloc <<= 1; - list->aliases = realloc (list->aliases, - alloc * sizeof (XdgAlias)); - } - list->aliases[list->n_aliases].alias = strdup (line); - list->aliases[list->n_aliases].mime_type = strdup (sep); - list->n_aliases++; - } - list->aliases = realloc (list->aliases, - list->n_aliases * sizeof (XdgAlias)); - - fclose (file); - - if (list->n_aliases > 1) - qsort (list->aliases, list->n_aliases, - sizeof (XdgAlias), alias_entry_cmp); -} - - -void -_xdg_mime_alias_list_dump (XdgAliasList *list) -{ - int i; - - if (list->aliases) - { - for (i = 0; i < list->n_aliases; i++) - { - printf ("%s %s\n", - list->aliases[i].alias, - list->aliases[i].mime_type); - } - } -} - diff --git a/src/butil/third_party/xdg_mime/xdgmimealias.h b/src/butil/third_party/xdg_mime/xdgmimealias.h deleted file mode 100644 index 3c28012dc3..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimealias.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimealias.h: Private file. Datastructure for storing the aliases. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2004 Red Hat, Inc. - * Copyright (C) 200 Matthias Clasen - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __XDG_MIME_ALIAS_H__ -#define __XDG_MIME_ALIAS_H__ - -#include "xdgmime.h" - -typedef struct XdgAliasList XdgAliasList; - -#ifdef XDG_PREFIX -#define _xdg_mime_alias_read_from_file XDG_RESERVED_ENTRY(alias_read_from_file) -#define _xdg_mime_alias_list_new XDG_RESERVED_ENTRY(alias_list_new) -#define _xdg_mime_alias_list_free XDG_RESERVED_ENTRY(alias_list_free) -#define _xdg_mime_alias_list_lookup XDG_RESERVED_ENTRY(alias_list_lookup) -#define _xdg_mime_alias_list_dump XDG_RESERVED_ENTRY(alias_list_dump) -#endif - -void _xdg_mime_alias_read_from_file (XdgAliasList *list, - const char *file_name); -XdgAliasList *_xdg_mime_alias_list_new (void); -void _xdg_mime_alias_list_free (XdgAliasList *list); -const char *_xdg_mime_alias_list_lookup (XdgAliasList *list, - const char *alias); -void _xdg_mime_alias_list_dump (XdgAliasList *list); - -#endif /* __XDG_MIME_ALIAS_H__ */ diff --git a/src/butil/third_party/xdg_mime/xdgmimecache.c b/src/butil/third_party/xdg_mime/xdgmimecache.c deleted file mode 100644 index 2e3a72a9a5..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimecache.c +++ /dev/null @@ -1,1075 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimealias.c: Private file. mmappable caches for mime data - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2005 Matthias Clasen - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#include -#include -#include -#include - -#include /* for ntohl/ntohs */ - -#define HAVE_MMAP 1 - -#ifdef HAVE_MMAP -#include -#else -#warning Building xdgmime without MMAP support. Binary "mime.cache" files will not be used. -#endif - -#include -#include - -#include "xdgmimecache.h" -#include "xdgmimeint.h" - -#ifndef MAX -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#endif - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -#ifndef _O_BINARY -#define _O_BINARY 0 -#endif - -#ifndef MAP_FAILED -#define MAP_FAILED ((void *) -1) -#endif - -#define MAJOR_VERSION 1 -#define MINOR_VERSION_MIN 1 -#define MINOR_VERSION_MAX 2 - -struct _XdgMimeCache -{ - int ref_count; - int minor; - - size_t size; - char *buffer; -}; - -#define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset)))) -#define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset)))) - -XdgMimeCache * -_xdg_mime_cache_ref (XdgMimeCache *cache) -{ - cache->ref_count++; - return cache; -} - -void -_xdg_mime_cache_unref (XdgMimeCache *cache) -{ - cache->ref_count--; - - if (cache->ref_count == 0) - { -#ifdef HAVE_MMAP - munmap (cache->buffer, cache->size); -#endif - free (cache); - } -} - -XdgMimeCache * -_xdg_mime_cache_new_from_file (const char *file_name) -{ - XdgMimeCache *cache = NULL; - -#ifdef HAVE_MMAP - int fd = -1; - struct stat st; - char *buffer = NULL; - int minor; - - /* Open the file and map it into memory */ - fd = open (file_name, O_RDONLY|_O_BINARY, 0); - - if (fd < 0) - return NULL; - - if (fstat (fd, &st) < 0 || st.st_size < 4) - goto done; - - buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - - if (buffer == MAP_FAILED) - goto done; - - minor = GET_UINT16 (buffer, 2); - /* Verify version */ - if (GET_UINT16 (buffer, 0) != MAJOR_VERSION || - (minor < MINOR_VERSION_MIN || - minor > MINOR_VERSION_MAX)) - { - munmap (buffer, st.st_size); - - goto done; - } - - cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache)); - cache->minor = minor; - cache->ref_count = 1; - cache->buffer = buffer; - cache->size = st.st_size; - - done: - if (fd != -1) - close (fd); - -#endif /* HAVE_MMAP */ - - return cache; -} - -static int -cache_magic_matchlet_compare_to_data (XdgMimeCache *cache, - xdg_uint32_t offset, - const void *data, - size_t len) -{ - xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset); - xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4); - xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12); - xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16); - xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20); - - size_t i, j; - - for (i = range_start; i < range_start + range_length; i++) - { - int valid_matchlet = TRUE; - - if (i + data_length > len) - return FALSE; - - if (mask_offset) - { - for (j = 0; j < data_length; j++) - { - if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) != - ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j])) - { - valid_matchlet = FALSE; - break; - } - } - } - else - { - valid_matchlet = memcmp(cache->buffer + data_offset, data + i, data_length) == 0; - } - - if (valid_matchlet) - return TRUE; - } - - return FALSE; -} - -static int -cache_magic_matchlet_compare (XdgMimeCache *cache, - xdg_uint32_t offset, - const void *data, - size_t len) -{ - xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24); - xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28); - - size_t i; - - if (cache_magic_matchlet_compare_to_data (cache, offset, data, len)) - { - if (n_children == 0) - return TRUE; - - for (i = 0; i < n_children; i++) - { - if (cache_magic_matchlet_compare (cache, child_offset + 32 * i, - data, len)) - return TRUE; - } - } - - return FALSE; -} - -static const char * -cache_magic_compare_to_data (XdgMimeCache *cache, - xdg_uint32_t offset, - const void *data, - size_t len, - int *prio) -{ - xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset); - xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4); - xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8); - xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12); - - size_t i; - - for (i = 0; i < n_matchlets; i++) - { - if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32, - data, len)) - { - *prio = priority; - - return cache->buffer + mimetype_offset; - } - } - - return NULL; -} - -static const char * -cache_magic_lookup_data (XdgMimeCache *cache, - const void *data, - size_t len, - int *prio, - const char *mime_types[], - int n_mime_types) -{ - xdg_uint32_t list_offset; - xdg_uint32_t n_entries; - xdg_uint32_t offset; - - size_t j; - int n; - - *prio = 0; - - list_offset = GET_UINT32 (cache->buffer, 24); - n_entries = GET_UINT32 (cache->buffer, list_offset); - offset = GET_UINT32 (cache->buffer, list_offset + 8); - - for (j = 0; j < n_entries; j++) - { - const char *match; - - match = cache_magic_compare_to_data (cache, offset + 16 * j, - data, len, prio); - if (match) - return match; - else - { - xdg_uint32_t mimetype_offset; - const char *non_match; - - mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4); - non_match = cache->buffer + mimetype_offset; - - for (n = 0; n < n_mime_types; n++) - { - if (mime_types[n] && - _xdg_mime_mime_type_equal (mime_types[n], non_match)) - mime_types[n] = NULL; - } - } - } - - return NULL; -} - -static const char * -cache_alias_lookup (const char *alias) -{ - const char *ptr; - int i, min, max, mid, cmp; - - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4); - xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); - xdg_uint32_t offset; - - min = 0; - max = n_entries - 1; - while (max >= min) - { - mid = (min + max) / 2; - - offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid); - ptr = cache->buffer + offset; - cmp = strcmp (ptr, alias); - - if (cmp < 0) - min = mid + 1; - else if (cmp > 0) - max = mid - 1; - else - { - offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4); - return cache->buffer + offset; - } - } - } - - return NULL; -} - -typedef struct { - const char *mime; - int weight; -} MimeWeight; - -static int -cache_glob_lookup_literal (const char *file_name, - const char *mime_types[], - int n_mime_types, - int case_sensitive_check) -{ - const char *ptr; - int i, min, max, mid, cmp; - - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12); - xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); - xdg_uint32_t offset; - - min = 0; - max = n_entries - 1; - while (max >= min) - { - mid = (min + max) / 2; - - offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid); - ptr = cache->buffer + offset; - cmp = strcmp (ptr, file_name); - - if (cmp < 0) - min = mid + 1; - else if (cmp > 0) - max = mid - 1; - else - { - int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 8); - int case_sensitive = weight & 0x100; - weight = weight & 0xff; - - if (case_sensitive_check || !case_sensitive) - { - offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * mid + 4); - mime_types[0] = (const char *)(cache->buffer + offset); - - return 1; - } - return 0; - } - } - } - - return 0; -} - -static int -cache_glob_lookup_fnmatch (const char *file_name, - MimeWeight mime_types[], - int n_mime_types, - int case_sensitive_check) -{ - const char *mime_type; - const char *ptr; - - size_t i, j; - int n; - - n = 0; - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - - xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20); - xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); - - for (j = 0; j < n_entries && n < n_mime_types; j++) - { - xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j); - xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 4); - int weight = GET_UINT32 (cache->buffer, list_offset + 4 + 12 * j + 8); - int case_sensitive = weight & 0x100; - weight = weight & 0xff; - ptr = cache->buffer + offset; - mime_type = cache->buffer + mimetype_offset; - if (case_sensitive_check || !case_sensitive) - { - /* FIXME: Not UTF-8 safe */ - if (fnmatch (ptr, file_name, 0) == 0) - { - mime_types[n].mime = mime_type; - mime_types[n].weight = weight; - n++; - } - } - } - - if (n > 0) - return n; - } - - return 0; -} - -static int -cache_glob_node_lookup_suffix (XdgMimeCache *cache, - xdg_uint32_t n_entries, - xdg_uint32_t offset, - const char *file_name, - int len, - int case_sensitive_check, - MimeWeight mime_types[], - int n_mime_types) -{ - xdg_unichar_t character; - xdg_unichar_t match_char; - xdg_uint32_t mimetype_offset; - xdg_uint32_t n_children; - xdg_uint32_t child_offset; - int weight; - int case_sensitive; - - int min, max, mid, n; - xdg_uint32_t i; - - character = file_name[len - 1]; - - assert (character != 0); - - min = 0; - max = n_entries - 1; - while (max >= min) - { - mid = (min + max) / 2; - match_char = GET_UINT32 (cache->buffer, offset + 12 * mid); - if (match_char < character) - min = mid + 1; - else if (match_char > character) - max = mid - 1; - else - { - len--; - n = 0; - n_children = GET_UINT32 (cache->buffer, offset + 12 * mid + 4); - child_offset = GET_UINT32 (cache->buffer, offset + 12 * mid + 8); - - if (len > 0) - { - n = cache_glob_node_lookup_suffix (cache, - n_children, child_offset, - file_name, len, - case_sensitive_check, - mime_types, - n_mime_types); - } - if (n == 0) - { - i = 0; - while (n < n_mime_types && i < n_children) - { - match_char = GET_UINT32 (cache->buffer, child_offset + 12 * i); - if (match_char != 0) - break; - - mimetype_offset = GET_UINT32 (cache->buffer, child_offset + 12 * i + 4); - weight = GET_UINT32 (cache->buffer, child_offset + 12 * i + 8); - case_sensitive = weight & 0x100; - weight = weight & 0xff; - - if (case_sensitive_check || !case_sensitive) - { - mime_types[n].mime = cache->buffer + mimetype_offset; - mime_types[n].weight = weight; - n++; - } - i++; - } - } - return n; - } - } - return 0; -} - -static int -cache_glob_lookup_suffix (const char *file_name, - int len, - int ignore_case, - MimeWeight mime_types[], - int n_mime_types) -{ - int i, n; - - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - - xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16); - xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); - xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4); - - n = cache_glob_node_lookup_suffix (cache, - n_entries, offset, - file_name, len, - ignore_case, - mime_types, - n_mime_types); - if (n > 0) - return n; - } - - return 0; -} - -static int compare_mime_weight (const void *a, const void *b) -{ - const MimeWeight *aa = (const MimeWeight *)a; - const MimeWeight *bb = (const MimeWeight *)b; - - return bb->weight - aa->weight; -} - -#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z') -static char * -ascii_tolower (const char *str) -{ - char *p, *lower; - - lower = strdup (str); - p = lower; - while (*p != 0) - { - char c = *p; - *p++ = ISUPPER (c) ? c - 'A' + 'a' : c; - } - return lower; -} - -static int -cache_glob_lookup_file_name (const char *file_name, - const char *mime_types[], - int n_mime_types) -{ - int n; - MimeWeight mimes[10]; - int n_mimes = 10; - int i; - int len; - char *lower_case; - - assert (file_name != NULL && n_mime_types > 0); - - /* First, check the literals */ - - lower_case = ascii_tolower (file_name); - - n = cache_glob_lookup_literal (lower_case, mime_types, n_mime_types, FALSE); - if (n > 0) - { - free (lower_case); - return n; - } - - n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types, TRUE); - if (n > 0) - { - free (lower_case); - return n; - } - - len = strlen (file_name); - n = cache_glob_lookup_suffix (lower_case, len, FALSE, mimes, n_mimes); - if (n == 0) - n = cache_glob_lookup_suffix (file_name, len, TRUE, mimes, n_mimes); - - /* Last, try fnmatch */ - if (n == 0) - n = cache_glob_lookup_fnmatch (lower_case, mimes, n_mimes, FALSE); - if (n == 0) - n = cache_glob_lookup_fnmatch (file_name, mimes, n_mimes, TRUE); - - free (lower_case); - - qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight); - - if (n_mime_types < n) - n = n_mime_types; - - for (i = 0; i < n; i++) - mime_types[i] = mimes[i].mime; - - return n; -} - -int -_xdg_mime_cache_get_max_buffer_extents (void) -{ - xdg_uint32_t offset; - xdg_uint32_t max_extent; - int i; - - max_extent = 0; - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - - offset = GET_UINT32 (cache->buffer, 24); - max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4)); - } - - return max_extent; -} - -static const char * -cache_get_mime_type_for_data (const void *data, - size_t len, - int *result_prio, - const char *mime_types[], - int n_mime_types) -{ - const char *mime_type; - int i, n, priority; - - priority = 0; - mime_type = NULL; - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - - int prio; - const char *match; - - match = cache_magic_lookup_data (cache, data, len, &prio, - mime_types, n_mime_types); - if (prio > priority) - { - priority = prio; - mime_type = match; - } - } - - if (result_prio) - *result_prio = priority; - - if (priority > 0) - { - /* Pick glob-result R where mime_type inherits from R */ - for (n = 0; n < n_mime_types; n++) - { - if (mime_types[n] && _xdg_mime_cache_mime_type_subclass(mime_types[n], mime_type)) - return mime_types[n]; - } - - /* Return magic match */ - return mime_type; - } - - /* Pick first glob result, as fallback */ - for (n = 0; n < n_mime_types; n++) - { - if (mime_types[n]) - return mime_types[n]; - } - - return NULL; -} - -const char * -_xdg_mime_cache_get_mime_type_for_data (const void *data, - size_t len, - int *result_prio) -{ - return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0); -} - -const char * -_xdg_mime_cache_get_mime_type_for_file (const char *file_name, - struct stat *statbuf) -{ - const char *mime_type; - const char *mime_types[10]; - FILE *file; - unsigned char *data; - int max_extent; - int bytes_read; - struct stat buf; - const char *base_name; - int n; - - if (file_name == NULL) - return NULL; - - if (! _xdg_utf8_validate (file_name)) - return NULL; - - base_name = _xdg_get_base_name (file_name); - n = cache_glob_lookup_file_name (base_name, mime_types, 10); - - if (n == 1) - return mime_types[0]; - - if (!statbuf) - { - if (stat (file_name, &buf) != 0) - return XDG_MIME_TYPE_UNKNOWN; - - statbuf = &buf; - } - - if (statbuf->st_size == 0) - return XDG_MIME_TYPE_EMPTY; - - if (!S_ISREG (statbuf->st_mode)) - return XDG_MIME_TYPE_UNKNOWN; - - /* FIXME: Need to make sure that max_extent isn't totally broken. This could - * be large and need getting from a stream instead of just reading it all - * in. */ - max_extent = _xdg_mime_cache_get_max_buffer_extents (); - data = malloc (max_extent); - if (data == NULL) - return XDG_MIME_TYPE_UNKNOWN; - - file = fopen (file_name, "r"); - if (file == NULL) - { - free (data); - return XDG_MIME_TYPE_UNKNOWN; - } - - bytes_read = fread (data, 1, max_extent, file); - if (ferror (file)) - { - free (data); - fclose (file); - return XDG_MIME_TYPE_UNKNOWN; - } - - mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL, - mime_types, n); - - if (!mime_type) - mime_type = _xdg_binary_or_text_fallback(data, bytes_read); - - free (data); - fclose (file); - - return mime_type; -} - -const char * -_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name) -{ - const char *mime_type; - - if (cache_glob_lookup_file_name (file_name, &mime_type, 1)) - return mime_type; - else - return XDG_MIME_TYPE_UNKNOWN; -} - -int -_xdg_mime_cache_get_mime_types_from_file_name (const char *file_name, - const char *mime_types[], - int n_mime_types) -{ - return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types); -} - -#if 1 -static int -is_super_type (const char *mime) -{ - int length; - const char *type; - - length = strlen (mime); - type = &(mime[length - 2]); - - if (strcmp (type, "/*") == 0) - return 1; - - return 0; -} -#endif - -int -_xdg_mime_cache_mime_type_subclass (const char *mime, - const char *base) -{ - const char *umime, *ubase; - - int i, min, max, med, cmp; - xdg_uint32_t j; - - umime = _xdg_mime_cache_unalias_mime_type (mime); - ubase = _xdg_mime_cache_unalias_mime_type (base); - - if (strcmp (umime, ubase) == 0) - return 1; - - /* We really want to handle text/ * in GtkFileFilter, so we just - * turn on the supertype matching - */ -#if 1 - /* Handle supertypes */ - if (is_super_type (ubase) && - xdg_mime_media_type_equal (umime, ubase)) - return 1; -#endif - - /* Handle special cases text/plain and application/octet-stream */ - if (strcmp (ubase, "text/plain") == 0 && - strncmp (umime, "text/", 5) == 0) - return 1; - - if (strcmp (ubase, "application/octet-stream") == 0) - return 1; - - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - - xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8); - xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); - xdg_uint32_t offset, n_parents, parent_offset; - - min = 0; - max = n_entries - 1; - while (max >= min) - { - med = (min + max)/2; - - offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med); - cmp = strcmp (cache->buffer + offset, umime); - if (cmp < 0) - min = med + 1; - else if (cmp > 0) - max = med - 1; - else - { - offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4); - n_parents = GET_UINT32 (cache->buffer, offset); - - for (j = 0; j < n_parents; j++) - { - parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j); - if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase)) - return 1; - } - - break; - } - } - } - - return 0; -} - -const char * -_xdg_mime_cache_unalias_mime_type (const char *mime) -{ - const char *lookup; - - lookup = cache_alias_lookup (mime); - - if (lookup) - return lookup; - - return mime; -} - -char ** -_xdg_mime_cache_list_mime_parents (const char *mime) -{ - int i, l, p; - xdg_uint32_t j, k; - char *all_parents[128]; /* we'll stop at 128 */ - char **result; - - mime = xdg_mime_unalias_mime_type (mime); - - p = 0; - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - - xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8); - xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); - - for (j = 0; j < n_entries; j++) - { - xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j); - xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4); - - if (strcmp (cache->buffer + mimetype_offset, mime) == 0) - { - xdg_uint32_t parent_mime_offset; - xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset); - - for (k = 0; k < n_parents && p < 127; k++) - { - parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k); - - /* Don't add same parent multiple times. - * This can happen for instance if the same type is listed in multiple directories - */ - for (l = 0; l < p; l++) - { - if (strcmp (all_parents[l], cache->buffer + parent_mime_offset) == 0) - break; - } - - if (l == p) - all_parents[p++] = cache->buffer + parent_mime_offset; - } - - break; - } - } - } - all_parents[p++] = NULL; - - result = (char **) malloc (p * sizeof (char *)); - memcpy (result, all_parents, p * sizeof (char *)); - - return result; -} - -static const char * -cache_lookup_icon (const char *mime, int header) -{ - const char *ptr; - int i, min, max, mid, cmp; - - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, header); - xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset); - xdg_uint32_t offset; - - min = 0; - max = n_entries - 1; - while (max >= min) - { - mid = (min + max) / 2; - - offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid); - ptr = cache->buffer + offset; - cmp = strcmp (ptr, mime); - - if (cmp < 0) - min = mid + 1; - else if (cmp > 0) - max = mid - 1; - else - { - offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4); - return cache->buffer + offset; - } - } - } - - return NULL; -} - -const char * -_xdg_mime_cache_get_generic_icon (const char *mime) -{ - return cache_lookup_icon (mime, 36); -} - -const char * -_xdg_mime_cache_get_icon (const char *mime) -{ - return cache_lookup_icon (mime, 32); -} - -static void -dump_glob_node (XdgMimeCache *cache, - xdg_uint32_t offset, - int depth) -{ - xdg_unichar_t character; - xdg_uint32_t mime_offset; - xdg_uint32_t n_children; - xdg_uint32_t child_offset; - int i; - - character = GET_UINT32 (cache->buffer, offset); - mime_offset = GET_UINT32 (cache->buffer, offset + 4); - n_children = GET_UINT32 (cache->buffer, offset + 8); - child_offset = GET_UINT32 (cache->buffer, offset + 12); - for (i = 0; i < depth; i++) - printf (" "); - printf ("%c", character); - if (mime_offset) - printf (" - %s", cache->buffer + mime_offset); - printf ("\n"); - if (child_offset) - { - for (i = 0; i < (int)n_children; i++) - dump_glob_node (cache, child_offset + 20 * i, depth + 1); - } -} - -void -_xdg_mime_cache_glob_dump (void) -{ - int i; - xdg_uint32_t j; - for (i = 0; _caches[i]; i++) - { - XdgMimeCache *cache = _caches[i]; - xdg_uint32_t list_offset; - xdg_uint32_t n_entries; - xdg_uint32_t offset; - list_offset = GET_UINT32 (cache->buffer, 16); - n_entries = GET_UINT32 (cache->buffer, list_offset); - offset = GET_UINT32 (cache->buffer, list_offset + 4); - for (j = 0; j < n_entries; j++) - dump_glob_node (cache, offset + 20 * j, 0); - } -} diff --git a/src/butil/third_party/xdg_mime/xdgmimecache.h b/src/butil/third_party/xdg_mime/xdgmimecache.h deleted file mode 100644 index 27f42d0ca1..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimecache.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimecache.h: Private file. Datastructure for mmapped caches. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2005 Matthias Clasen - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __XDG_MIME_CACHE_H__ -#define __XDG_MIME_CACHE_H__ - -#include "xdgmime.h" - -typedef struct _XdgMimeCache XdgMimeCache; - -#ifdef XDG_PREFIX -#define _xdg_mime_cache_new_from_file XDG_RESERVED_ENTRY(cache_new_from_file) -#define _xdg_mime_cache_ref XDG_RESERVED_ENTRY(cache_ref) -#define _xdg_mime_cache_unref XDG_RESERVED_ENTRY(cache_unref) -#define _xdg_mime_cache_get_max_buffer_extents XDG_RESERVED_ENTRY(cache_get_max_buffer_extents) -#define _xdg_mime_cache_get_mime_type_for_data XDG_RESERVED_ENTRY(cache_get_mime_type_for_data) -#define _xdg_mime_cache_get_mime_type_for_file XDG_RESERVED_ENTRY(cache_get_mime_type_for_file) -#define _xdg_mime_cache_get_mime_type_from_file_name XDG_RESERVED_ENTRY(cache_get_mime_type_from_file_name) -#define _xdg_mime_cache_get_mime_types_from_file_name XDG_RESERVED_ENTRY(cache_get_mime_types_from_file_name) -#define _xdg_mime_cache_list_mime_parents XDG_RESERVED_ENTRY(cache_list_mime_parents) -#define _xdg_mime_cache_mime_type_subclass XDG_RESERVED_ENTRY(cache_mime_type_subclass) -#define _xdg_mime_cache_unalias_mime_type XDG_RESERVED_ENTRY(cache_unalias_mime_type) -#define _xdg_mime_cache_get_icon XDG_RESERVED_ENTRY(cache_get_icon) -#define _xdg_mime_cache_get_generic_icon XDG_RESERVED_ENTRY(cache_get_generic_icon) -#define _xdg_mime_cache_glob_dump XDG_RESERVED_ENTRY(cache_glob_dump) -#endif - -extern XdgMimeCache **_caches; - -XdgMimeCache *_xdg_mime_cache_new_from_file (const char *file_name); -XdgMimeCache *_xdg_mime_cache_ref (XdgMimeCache *cache); -void _xdg_mime_cache_unref (XdgMimeCache *cache); - - -const char *_xdg_mime_cache_get_mime_type_for_data (const void *data, - size_t len, - int *result_prio); -const char *_xdg_mime_cache_get_mime_type_for_file (const char *file_name, - struct stat *statbuf); -int _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name, - const char *mime_types[], - int n_mime_types); -const char *_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name); -int _xdg_mime_cache_is_valid_mime_type (const char *mime_type); -int _xdg_mime_cache_mime_type_equal (const char *mime_a, - const char *mime_b); -int _xdg_mime_cache_media_type_equal (const char *mime_a, - const char *mime_b); -int _xdg_mime_cache_mime_type_subclass (const char *mime_a, - const char *mime_b); -char **_xdg_mime_cache_list_mime_parents (const char *mime); -const char *_xdg_mime_cache_unalias_mime_type (const char *mime); -int _xdg_mime_cache_get_max_buffer_extents (void); -const char *_xdg_mime_cache_get_icon (const char *mime); -const char *_xdg_mime_cache_get_generic_icon (const char *mime); -void _xdg_mime_cache_glob_dump (void); - -#endif /* __XDG_MIME_CACHE_H__ */ diff --git a/src/butil/third_party/xdg_mime/xdgmimeglob.c b/src/butil/third_party/xdg_mime/xdgmimeglob.c deleted file mode 100644 index f8434bcc56..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimeglob.c +++ /dev/null @@ -1,691 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimeglob.c: Private file. Datastructure for storing the globs. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2003 Red Hat, Inc. - * Copyright (C) 2003 Jonathan Blandford - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "xdgmimeglob.h" -#include "xdgmimeint.h" -#include -#include -#include -#include -#include - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -typedef struct XdgGlobHashNode XdgGlobHashNode; -typedef struct XdgGlobList XdgGlobList; - -struct XdgGlobHashNode -{ - xdg_unichar_t character; - const char *mime_type; - int weight; - int case_sensitive; - XdgGlobHashNode *next; - XdgGlobHashNode *child; -}; -struct XdgGlobList -{ - const char *data; - const char *mime_type; - int weight; - int case_sensitive; - XdgGlobList *next; -}; - -struct XdgGlobHash -{ - XdgGlobList *literal_list; - XdgGlobHashNode *simple_node; - XdgGlobList *full_list; -}; - - -/* XdgGlobList - */ -static XdgGlobList * -_xdg_glob_list_new (void) -{ - XdgGlobList *new_element; - - new_element = calloc (1, sizeof (XdgGlobList)); - - return new_element; -} - -/* Frees glob_list and all of it's children */ -static void -_xdg_glob_list_free (XdgGlobList *glob_list) -{ - XdgGlobList *ptr, *next; - - ptr = glob_list; - - while (ptr != NULL) - { - next = ptr->next; - - if (ptr->data) - free ((void *) ptr->data); - if (ptr->mime_type) - free ((void *) ptr->mime_type); - free (ptr); - - ptr = next; - } -} - -static XdgGlobList * -_xdg_glob_list_append (XdgGlobList *glob_list, - void *data, - const char *mime_type, - int weight, - int case_sensitive) -{ - XdgGlobList *new_element; - XdgGlobList *tmp_element; - - tmp_element = glob_list; - while (tmp_element != NULL) - { - if (strcmp (tmp_element->data, data) == 0 && - strcmp (tmp_element->mime_type, mime_type) == 0) - return glob_list; - - tmp_element = tmp_element->next; - } - - new_element = _xdg_glob_list_new (); - new_element->data = data; - new_element->mime_type = mime_type; - new_element->weight = weight; - new_element->case_sensitive = case_sensitive; - if (glob_list == NULL) - return new_element; - - tmp_element = glob_list; - while (tmp_element->next != NULL) - tmp_element = tmp_element->next; - - tmp_element->next = new_element; - - return glob_list; -} - -/* XdgGlobHashNode - */ - -static XdgGlobHashNode * -_xdg_glob_hash_node_new (void) -{ - XdgGlobHashNode *glob_hash_node; - - glob_hash_node = calloc (1, sizeof (XdgGlobHashNode)); - - return glob_hash_node; -} - -static void -_xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node, - int depth) -{ - int i; - for (i = 0; i < depth; i++) - printf (" "); - - printf ("%c", (char)glob_hash_node->character); - if (glob_hash_node->mime_type) - printf (" - %s %d\n", glob_hash_node->mime_type, glob_hash_node->weight); - else - printf ("\n"); - if (glob_hash_node->child) - _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1); - if (glob_hash_node->next) - _xdg_glob_hash_node_dump (glob_hash_node->next, depth); -} - -static XdgGlobHashNode * -_xdg_glob_hash_insert_ucs4 (XdgGlobHashNode *glob_hash_node, - xdg_unichar_t *text, - const char *mime_type, - int weight, - int case_sensitive) -{ - XdgGlobHashNode *node; - xdg_unichar_t character; - - character = text[0]; - - if ((glob_hash_node == NULL) || - (character < glob_hash_node->character)) - { - node = _xdg_glob_hash_node_new (); - node->character = character; - node->next = glob_hash_node; - glob_hash_node = node; - } - else if (character == glob_hash_node->character) - { - node = glob_hash_node; - } - else - { - XdgGlobHashNode *prev_node; - int found_node = FALSE; - - /* Look for the first character of text in glob_hash_node, and insert it if we - * have to.*/ - prev_node = glob_hash_node; - node = prev_node->next; - - while (node != NULL) - { - if (character < node->character) - { - node = _xdg_glob_hash_node_new (); - node->character = character; - node->next = prev_node->next; - prev_node->next = node; - - found_node = TRUE; - break; - } - else if (character == node->character) - { - found_node = TRUE; - break; - } - prev_node = node; - node = node->next; - } - - if (! found_node) - { - node = _xdg_glob_hash_node_new (); - node->character = character; - node->next = prev_node->next; - prev_node->next = node; - } - } - - text++; - if (*text == 0) - { - if (node->mime_type) - { - if (strcmp (node->mime_type, mime_type) != 0) - { - XdgGlobHashNode *child; - int found_node = FALSE; - - child = node->child; - while (child && child->character == 0) - { - if (strcmp (child->mime_type, mime_type) == 0) - { - found_node = TRUE; - break; - } - child = child->next; - } - - if (!found_node) - { - child = _xdg_glob_hash_node_new (); - child->character = 0; - child->mime_type = strdup (mime_type); - child->weight = weight; - child->case_sensitive = case_sensitive; - child->child = NULL; - child->next = node->child; - node->child = child; - } - } - } - else - { - node->mime_type = strdup (mime_type); - node->weight = weight; - node->case_sensitive = case_sensitive; - } - } - else - { - node->child = _xdg_glob_hash_insert_ucs4 (node->child, text, mime_type, weight, case_sensitive); - } - return glob_hash_node; -} - -/* glob must be valid UTF-8 */ -static XdgGlobHashNode * -_xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node, - const char *text, - const char *mime_type, - int weight, - int case_sensitive) -{ - XdgGlobHashNode *node; - xdg_unichar_t *unitext; - int len; - - unitext = _xdg_convert_to_ucs4 (text, &len); - _xdg_reverse_ucs4 (unitext, len); - node = _xdg_glob_hash_insert_ucs4 (glob_hash_node, unitext, mime_type, weight, case_sensitive); - free (unitext); - return node; -} - -typedef struct { - const char *mime; - int weight; -} MimeWeight; - -static int -_xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node, - const char *file_name, - int len, - int case_sensitive_check, - MimeWeight mime_types[], - int n_mime_types) -{ - int n; - XdgGlobHashNode *node; - xdg_unichar_t character; - - if (glob_hash_node == NULL) - return 0; - - character = file_name[len - 1]; - - for (node = glob_hash_node; node && character >= node->character; node = node->next) - { - if (character == node->character) - { - len--; - n = 0; - if (len > 0) - { - n = _xdg_glob_hash_node_lookup_file_name (node->child, - file_name, - len, - case_sensitive_check, - mime_types, - n_mime_types); - } - if (n == 0) - { - if (node->mime_type && - (case_sensitive_check || - !node->case_sensitive)) - { - mime_types[n].mime = node->mime_type; - mime_types[n].weight = node->weight; - n++; - } - node = node->child; - while (n < n_mime_types && node && node->character == 0) - { - if (node->mime_type && - (case_sensitive_check || - !node->case_sensitive)) - { - mime_types[n].mime = node->mime_type; - mime_types[n].weight = node->weight; - n++; - } - node = node->next; - } - } - return n; - } - } - - return 0; -} - -static int compare_mime_weight (const void *a, const void *b) -{ - const MimeWeight *aa = (const MimeWeight *)a; - const MimeWeight *bb = (const MimeWeight *)b; - - return bb->weight - aa->weight; -} - -#define ISUPPER(c) ((c) >= 'A' && (c) <= 'Z') -static char * -ascii_tolower (const char *str) -{ - char *p, *lower; - - lower = strdup (str); - p = lower; - while (*p != 0) - { - char c = *p; - *p++ = ISUPPER (c) ? c - 'A' + 'a' : c; - } - return lower; -} - -int -_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash, - const char *file_name, - const char *mime_types[], - int n_mime_types) -{ - XdgGlobList *list; - int i, n; - MimeWeight mimes[10]; - int n_mimes = 10; - int len; - char *lower_case; - - /* First, check the literals */ - - assert (file_name != NULL && n_mime_types > 0); - - n = 0; - - lower_case = ascii_tolower (file_name); - - for (list = glob_hash->literal_list; list; list = list->next) - { - if (strcmp ((const char *)list->data, file_name) == 0) - { - mime_types[0] = list->mime_type; - free (lower_case); - return 1; - } - } - - for (list = glob_hash->literal_list; list; list = list->next) - { - if (!list->case_sensitive && - strcmp ((const char *)list->data, lower_case) == 0) - { - mime_types[0] = list->mime_type; - free (lower_case); - return 1; - } - } - - - len = strlen (file_name); - n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, lower_case, len, FALSE, - mimes, n_mimes); - if (n == 0) - n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, file_name, len, TRUE, - mimes, n_mimes); - - if (n == 0) - { - for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next) - { - if (fnmatch ((const char *)list->data, file_name, 0) == 0) - { - mimes[n].mime = list->mime_type; - mimes[n].weight = list->weight; - n++; - } - } - } - free (lower_case); - - qsort (mimes, n, sizeof (MimeWeight), compare_mime_weight); - - if (n_mime_types < n) - n = n_mime_types; - - for (i = 0; i < n; i++) - mime_types[i] = mimes[i].mime; - - return n; -} - - - -/* XdgGlobHash - */ - -XdgGlobHash * -_xdg_glob_hash_new (void) -{ - XdgGlobHash *glob_hash; - - glob_hash = calloc (1, sizeof (XdgGlobHash)); - - return glob_hash; -} - - -static void -_xdg_glob_hash_free_nodes (XdgGlobHashNode *node) -{ - if (node) - { - if (node->child) - _xdg_glob_hash_free_nodes (node->child); - if (node->next) - _xdg_glob_hash_free_nodes (node->next); - if (node->mime_type) - free ((void *) node->mime_type); - free (node); - } -} - -void -_xdg_glob_hash_free (XdgGlobHash *glob_hash) -{ - _xdg_glob_list_free (glob_hash->literal_list); - _xdg_glob_list_free (glob_hash->full_list); - _xdg_glob_hash_free_nodes (glob_hash->simple_node); - free (glob_hash); -} - -XdgGlobType -_xdg_glob_determine_type (const char *glob) -{ - const char *ptr; - int maybe_in_simple_glob = FALSE; - int first_char = TRUE; - - ptr = glob; - - while (*ptr != '\0') - { - if (*ptr == '*' && first_char) - maybe_in_simple_glob = TRUE; - else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*') - return XDG_GLOB_FULL; - - first_char = FALSE; - ptr = _xdg_utf8_next_char (ptr); - } - if (maybe_in_simple_glob) - return XDG_GLOB_SIMPLE; - else - return XDG_GLOB_LITERAL; -} - -/* glob must be valid UTF-8 */ -void -_xdg_glob_hash_append_glob (XdgGlobHash *glob_hash, - const char *glob, - const char *mime_type, - int weight, - int case_sensitive) -{ - XdgGlobType type; - - assert (glob_hash != NULL); - assert (glob != NULL); - - type = _xdg_glob_determine_type (glob); - - switch (type) - { - case XDG_GLOB_LITERAL: - glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type), weight, case_sensitive); - break; - case XDG_GLOB_SIMPLE: - glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type, weight, case_sensitive); - break; - case XDG_GLOB_FULL: - glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type), weight, case_sensitive); - break; - } -} - -void -_xdg_glob_hash_dump (XdgGlobHash *glob_hash) -{ - XdgGlobList *list; - printf ("LITERAL STRINGS\n"); - if (!glob_hash || glob_hash->literal_list == NULL) - { - printf (" None\n"); - } - else - { - for (list = glob_hash->literal_list; list; list = list->next) - printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight); - } - printf ("\nSIMPLE GLOBS\n"); - if (!glob_hash || glob_hash->simple_node == NULL) - { - printf (" None\n"); - } - else - { - _xdg_glob_hash_node_dump (glob_hash->simple_node, 4); - } - - printf ("\nFULL GLOBS\n"); - if (!glob_hash || glob_hash->full_list == NULL) - { - printf (" None\n"); - } - else - { - for (list = glob_hash->full_list; list; list = list->next) - printf (" %s - %s %d\n", (char *)list->data, list->mime_type, list->weight); - } -} - - -void -_xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash, - const char *file_name, - int version_two) -{ - FILE *glob_file; - char line[255]; - char *p; - - glob_file = fopen (file_name, "r"); - - if (glob_file == NULL) - return; - - /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. - * Blah */ - while (fgets (line, 255, glob_file) != NULL) - { - char *colon; - char *mimetype, *glob, *end; - int weight; - int case_sensitive; - - if (line[0] == '#' || line[0] == 0) - continue; - - end = line + strlen(line) - 1; - if (*end == '\n') - *end = 0; - - p = line; - if (version_two) - { - colon = strchr (p, ':'); - if (colon == NULL) - continue; - *colon = 0; - weight = atoi (p); - p = colon + 1; - } - else - weight = 50; - - colon = strchr (p, ':'); - if (colon == NULL) - continue; - *colon = 0; - - mimetype = p; - p = colon + 1; - glob = p; - case_sensitive = FALSE; - - colon = strchr (p, ':'); - if (version_two && colon != NULL) - { - char *flag; - - /* We got flags */ - *colon = 0; - p = colon + 1; - - /* Flags end at next colon */ - colon = strchr (p, ':'); - if (colon != NULL) - *colon = 0; - - flag = strstr (p, "cs"); - if (flag != NULL && - /* Start or after comma */ - (flag == p || - flag[-1] == ',') && - /* ends with comma or end of string */ - (flag[2] == 0 || - flag[2] == ',')) - case_sensitive = TRUE; - } - - _xdg_glob_hash_append_glob (glob_hash, glob, mimetype, weight, case_sensitive); - } - - fclose (glob_file); -} diff --git a/src/butil/third_party/xdg_mime/xdgmimeglob.h b/src/butil/third_party/xdg_mime/xdgmimeglob.h deleted file mode 100644 index 00182920d4..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimeglob.h +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimeglob.h: Private file. Datastructure for storing the globs. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2003 Red Hat, Inc. - * Copyright (C) 2003 Jonathan Blandford - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __XDG_MIME_GLOB_H__ -#define __XDG_MIME_GLOB_H__ - -#include "xdgmime.h" - -typedef struct XdgGlobHash XdgGlobHash; - -typedef enum -{ - XDG_GLOB_LITERAL, /* Makefile */ - XDG_GLOB_SIMPLE, /* *.gif */ - XDG_GLOB_FULL /* x*.[ch] */ -} XdgGlobType; - - -#ifdef XDG_PREFIX -#define _xdg_mime_glob_read_from_file XDG_RESERVED_ENTRY(glob_read_from_file) -#define _xdg_glob_hash_new XDG_RESERVED_ENTRY(hash_new) -#define _xdg_glob_hash_free XDG_RESERVED_ENTRY(hash_free) -#define _xdg_glob_hash_lookup_file_name XDG_RESERVED_ENTRY(hash_lookup_file_name) -#define _xdg_glob_hash_append_glob XDG_RESERVED_ENTRY(hash_append_glob) -#define _xdg_glob_determine_type XDG_RESERVED_ENTRY(determine_type) -#define _xdg_glob_hash_dump XDG_RESERVED_ENTRY(hash_dump) -#endif - -void _xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash, - const char *file_name, - int version_two); -XdgGlobHash *_xdg_glob_hash_new (void); -void _xdg_glob_hash_free (XdgGlobHash *glob_hash); -int _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash, - const char *text, - const char *mime_types[], - int n_mime_types); -void _xdg_glob_hash_append_glob (XdgGlobHash *glob_hash, - const char *glob, - const char *mime_type, - int weight, - int case_sensitive); -XdgGlobType _xdg_glob_determine_type (const char *glob); -void _xdg_glob_hash_dump (XdgGlobHash *glob_hash); - -#endif /* __XDG_MIME_GLOB_H__ */ diff --git a/src/butil/third_party/xdg_mime/xdgmimeicon.c b/src/butil/third_party/xdg_mime/xdgmimeicon.c deleted file mode 100644 index 88d3f82dca..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimeicon.c +++ /dev/null @@ -1,182 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimeicon.c: Private file. Datastructure for storing the aliases. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2008 Red Hat, Inc. - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "xdgmimeicon.h" -#include "xdgmimeint.h" -#include -#include -#include -#include -#include - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -typedef struct XdgIcon XdgIcon; - -struct XdgIcon -{ - char *mime_type; - char *icon_name; -}; - -struct XdgIconList -{ - struct XdgIcon *icons; - int n_icons; -}; - -XdgIconList * -_xdg_mime_icon_list_new (void) -{ - XdgIconList *list; - - list = malloc (sizeof (XdgIconList)); - - list->icons = NULL; - list->n_icons = 0; - - return list; -} - -void -_xdg_mime_icon_list_free (XdgIconList *list) -{ - int i; - - if (list->icons) - { - for (i = 0; i < list->n_icons; i++) - { - free (list->icons[i].mime_type); - free (list->icons[i].icon_name); - } - free (list->icons); - } - free (list); -} - -static int -icon_entry_cmp (const void *v1, const void *v2) -{ - return strcmp (((XdgIcon *)v1)->mime_type, ((XdgIcon *)v2)->mime_type); -} - -const char * -_xdg_mime_icon_list_lookup (XdgIconList *list, - const char *mime_type) -{ - XdgIcon *entry; - XdgIcon key; - - if (list->n_icons > 0) - { - key.mime_type = (char *)mime_type; - key.icon_name = NULL; - - entry = bsearch (&key, list->icons, list->n_icons, - sizeof (XdgIcon), icon_entry_cmp); - if (entry) - return entry->icon_name; - } - - return NULL; -} - -void -_xdg_mime_icon_read_from_file (XdgIconList *list, - const char *file_name) -{ - FILE *file; - char line[255]; - int alloc; - - file = fopen (file_name, "r"); - - if (file == NULL) - return; - - /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. - * Blah */ - alloc = list->n_icons + 16; - list->icons = realloc (list->icons, alloc * sizeof (XdgIcon)); - while (fgets (line, 255, file) != NULL) - { - char *sep; - if (line[0] == '#') - continue; - - sep = strchr (line, ':'); - if (sep == NULL) - continue; - *(sep++) = '\000'; - sep[strlen (sep) -1] = '\000'; - if (list->n_icons == alloc) - { - alloc <<= 1; - list->icons = realloc (list->icons, - alloc * sizeof (XdgIcon)); - } - list->icons[list->n_icons].mime_type = strdup (line); - list->icons[list->n_icons].icon_name = strdup (sep); - list->n_icons++; - } - list->icons = realloc (list->icons, - list->n_icons * sizeof (XdgIcon)); - - fclose (file); - - if (list->n_icons > 1) - qsort (list->icons, list->n_icons, - sizeof (XdgIcon), icon_entry_cmp); -} - - -void -_xdg_mime_icon_list_dump (XdgIconList *list) -{ - int i; - - if (list->icons) - { - for (i = 0; i < list->n_icons; i++) - { - printf ("%s %s\n", - list->icons[i].mime_type, - list->icons[i].icon_name); - } - } -} - diff --git a/src/butil/third_party/xdg_mime/xdgmimeicon.h b/src/butil/third_party/xdg_mime/xdgmimeicon.h deleted file mode 100644 index b5f25835db..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimeicon.h +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimeicon.h: Private file. Datastructure for storing the aliases. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2008 Red Hat, Inc. - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __XDG_MIME_ICON_H__ -#define __XDG_MIME_ICON_H__ - -#include "xdgmime.h" - -typedef struct XdgIconList XdgIconList; - -#ifdef XDG_PREFIX -#define _xdg_mime_icon_read_from_file XDG_ENTRY(icon_read_from_file) -#define _xdg_mime_icon_list_new XDG_ENTRY(icon_list_new) -#define _xdg_mime_icon_list_free XDG_ENTRY(icon_list_free) -#define _xdg_mime_icon_list_lookup XDG_ENTRY(icon_list_lookup) -#define _xdg_mime_icon_list_dump XDG_ENTRY(icon_list_dump) -#endif - -void _xdg_mime_icon_read_from_file (XdgIconList *list, - const char *file_name); -XdgIconList *_xdg_mime_icon_list_new (void); -void _xdg_mime_icon_list_free (XdgIconList *list); -const char *_xdg_mime_icon_list_lookup (XdgIconList *list, - const char *mime); -void _xdg_mime_icon_list_dump (XdgIconList *list); - -#endif /* __XDG_MIME_ICON_H__ */ diff --git a/src/butil/third_party/xdg_mime/xdgmimeint.c b/src/butil/third_party/xdg_mime/xdgmimeint.c deleted file mode 100644 index 53647949c7..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimeint.c +++ /dev/null @@ -1,206 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimeint.c: Internal defines and functions. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2003 Red Hat, Inc. - * Copyright (C) 2003 Jonathan Blandford - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "xdgmimeint.h" -#include -#include - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -static const char _xdg_utf8_skip_data[256] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 -}; - -const char * const _xdg_utf8_skip = _xdg_utf8_skip_data; - - - -/* Returns the number of unprocessed characters. */ -xdg_unichar_t -_xdg_utf8_to_ucs4(const char *source) -{ - xdg_unichar_t ucs32; - if( ! ( *source & 0x80 ) ) - { - ucs32 = *source; - } - else - { - int bytelength = 0; - xdg_unichar_t result; - if ( ! (*source & 0x40) ) - { - ucs32 = *source; - } - else - { - if ( ! (*source & 0x20) ) - { - result = *source++ & 0x1F; - bytelength = 2; - } - else if ( ! (*source & 0x10) ) - { - result = *source++ & 0x0F; - bytelength = 3; - } - else if ( ! (*source & 0x08) ) - { - result = *source++ & 0x07; - bytelength = 4; - } - else if ( ! (*source & 0x04) ) - { - result = *source++ & 0x03; - bytelength = 5; - } - else if ( ! (*source & 0x02) ) - { - result = *source++ & 0x01; - bytelength = 6; - } - else - { - result = *source++; - bytelength = 1; - } - - for ( bytelength --; bytelength > 0; bytelength -- ) - { - result <<= 6; - result |= *source++ & 0x3F; - } - ucs32 = result; - } - } - return ucs32; -} - - -/* hullo. this is great code. don't rewrite it */ - -xdg_unichar_t -_xdg_ucs4_to_lower (xdg_unichar_t source) -{ - /* FIXME: Do a real to_upper sometime */ - /* CaseFolding-3.2.0.txt has a table of rules. */ - if ((source & 0xFF) == source) - return (xdg_unichar_t) tolower ((unsigned char) source); - return source; -} - -int -_xdg_utf8_validate (const char *source) -{ - /* FIXME: actually write */ - return TRUE; -} - -const char * -_xdg_get_base_name (const char *file_name) -{ - const char *base_name; - - if (file_name == NULL) - return NULL; - - base_name = strrchr (file_name, '/'); - - if (base_name == NULL) - return file_name; - else - return base_name + 1; -} - -xdg_unichar_t * -_xdg_convert_to_ucs4 (const char *source, int *len) -{ - xdg_unichar_t *out; - int i; - const char *p; - - out = malloc (sizeof (xdg_unichar_t) * (strlen (source) + 1)); - - p = source; - i = 0; - while (*p) - { - out[i++] = _xdg_utf8_to_ucs4 (p); - p = _xdg_utf8_next_char (p); - } - out[i] = 0; - *len = i; - - return out; -} - -void -_xdg_reverse_ucs4 (xdg_unichar_t *source, int len) -{ - xdg_unichar_t c; - int i; - - for (i = 0; i < len - i - 1; i++) - { - c = source[i]; - source[i] = source[len - i - 1]; - source[len - i - 1] = c; - } -} - -const char * -_xdg_binary_or_text_fallback(const void *data, size_t len) -{ - unsigned char *chardata; - size_t i; - - chardata = (unsigned char *) data; - for (i = 0; i < 32 && i < len; ++i) - { - if (chardata[i] < 32 && chardata[i] != 9 && chardata[i] != 10 && chardata[i] != 13) - return XDG_MIME_TYPE_UNKNOWN; /* binary data */ - } - - return XDG_MIME_TYPE_TEXTPLAIN; -} diff --git a/src/butil/third_party/xdg_mime/xdgmimeint.h b/src/butil/third_party/xdg_mime/xdgmimeint.h deleted file mode 100644 index 9e8b2cb85a..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimeint.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimeint.h: Internal defines and functions. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2003 Red Hat, Inc. - * Copyright (C) 2003 Jonathan Blandford - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __XDG_MIME_INT_H__ -#define __XDG_MIME_INT_H__ - -#include "xdgmime.h" - - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -/* FIXME: Needs to be configure check */ -typedef unsigned int xdg_unichar_t; -typedef unsigned char xdg_uchar8_t; -typedef unsigned short xdg_uint16_t; -typedef unsigned int xdg_uint32_t; - -#ifdef XDG_PREFIX -#define _xdg_utf8_skip XDG_RESERVED_ENTRY(utf8_skip) -#define _xdg_utf8_to_ucs4 XDG_RESERVED_ENTRY(utf8_to_ucs4) -#define _xdg_ucs4_to_lower XDG_RESERVED_ENTRY(ucs4_to_lower) -#define _xdg_utf8_validate XDG_RESERVED_ENTRY(utf8_validate) -#define _xdg_get_base_name XDG_RESERVED_ENTRY(get_base_name) -#define _xdg_convert_to_ucs4 XDG_RESERVED_ENTRY(convert_to_ucs4) -#define _xdg_reverse_ucs4 XDG_RESERVED_ENTRY(reverse_ucs4) -#endif - -#define SWAP_BE16_TO_LE16(val) (xdg_uint16_t)(((xdg_uint16_t)(val) << 8)|((xdg_uint16_t)(val) >> 8)) - -#define SWAP_BE32_TO_LE32(val) (xdg_uint32_t)((((xdg_uint32_t)(val) & 0xFF000000U) >> 24) | \ - (((xdg_uint32_t)(val) & 0x00FF0000U) >> 8) | \ - (((xdg_uint32_t)(val) & 0x0000FF00U) << 8) | \ - (((xdg_uint32_t)(val) & 0x000000FFU) << 24)) -/* UTF-8 utils - */ -extern const char *const _xdg_utf8_skip; -#define _xdg_utf8_next_char(p) (char *)((p) + _xdg_utf8_skip[*(unsigned char *)(p)]) -#define _xdg_utf8_char_size(p) (int) (_xdg_utf8_skip[*(unsigned char *)(p)]) - -xdg_unichar_t _xdg_utf8_to_ucs4 (const char *source); -xdg_unichar_t _xdg_ucs4_to_lower (xdg_unichar_t source); -int _xdg_utf8_validate (const char *source); -xdg_unichar_t *_xdg_convert_to_ucs4 (const char *source, int *len); -void _xdg_reverse_ucs4 (xdg_unichar_t *source, int len); -const char *_xdg_get_base_name (const char *file_name); -const char *_xdg_binary_or_text_fallback(const void *data, size_t len); - -#endif /* __XDG_MIME_INT_H__ */ diff --git a/src/butil/third_party/xdg_mime/xdgmimemagic.c b/src/butil/third_party/xdg_mime/xdgmimemagic.c deleted file mode 100644 index fa3da861e8..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimemagic.c +++ /dev/null @@ -1,813 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimemagic.: Private file. Datastructure for storing magic files. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2003 Red Hat, Inc. - * Copyright (C) 2003 Jonathan Blandford - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include "xdgmimemagic.h" -#include "xdgmimeint.h" -#include -#include -#include -#include -#include -#include - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -#if !defined getc_unlocked && !defined HAVE_GETC_UNLOCKED -# define getc_unlocked(fp) getc (fp) -#endif - -typedef struct XdgMimeMagicMatch XdgMimeMagicMatch; -typedef struct XdgMimeMagicMatchlet XdgMimeMagicMatchlet; - -typedef enum -{ - XDG_MIME_MAGIC_SECTION, - XDG_MIME_MAGIC_MAGIC, - XDG_MIME_MAGIC_ERROR, - XDG_MIME_MAGIC_EOF -} XdgMimeMagicState; - -struct XdgMimeMagicMatch -{ - const char *mime_type; - int priority; - XdgMimeMagicMatchlet *matchlet; - XdgMimeMagicMatch *next; -}; - - -struct XdgMimeMagicMatchlet -{ - int indent; - int offset; - unsigned int value_length; - unsigned char *value; - unsigned char *mask; - unsigned int range_length; - unsigned int word_size; - XdgMimeMagicMatchlet *next; -}; - - -struct XdgMimeMagic -{ - XdgMimeMagicMatch *match_list; - int max_extent; -}; - -static XdgMimeMagicMatch * -_xdg_mime_magic_match_new (void) -{ - return calloc (1, sizeof (XdgMimeMagicMatch)); -} - - -static XdgMimeMagicMatchlet * -_xdg_mime_magic_matchlet_new (void) -{ - XdgMimeMagicMatchlet *matchlet; - - matchlet = malloc (sizeof (XdgMimeMagicMatchlet)); - - matchlet->indent = 0; - matchlet->offset = 0; - matchlet->value_length = 0; - matchlet->value = NULL; - matchlet->mask = NULL; - matchlet->range_length = 1; - matchlet->word_size = 1; - matchlet->next = NULL; - - return matchlet; -} - - -static void -_xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet) -{ - if (mime_magic_matchlet) - { - if (mime_magic_matchlet->next) - _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next); - if (mime_magic_matchlet->value) - free (mime_magic_matchlet->value); - if (mime_magic_matchlet->mask) - free (mime_magic_matchlet->mask); - free (mime_magic_matchlet); - } -} - - -/* Frees mime_magic_match and the remainder of its list - */ -static void -_xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match) -{ - XdgMimeMagicMatch *ptr, *next; - - ptr = mime_magic_match; - while (ptr) - { - next = ptr->next; - - if (ptr->mime_type) - free ((void *) ptr->mime_type); - if (ptr->matchlet) - _xdg_mime_magic_matchlet_free (ptr->matchlet); - free (ptr); - - ptr = next; - } -} - -/* Reads in a hunk of data until a newline character or a '\000' is hit. The - * returned string is null terminated, and doesn't include the newline. - */ -static unsigned char * -_xdg_mime_magic_read_to_newline (FILE *magic_file, - int *end_of_file) -{ - unsigned char *retval; - int c; - int len, pos; - - len = 128; - pos = 0; - retval = malloc (len); - *end_of_file = FALSE; - - while (TRUE) - { - c = getc_unlocked (magic_file); - if (c == EOF) - { - *end_of_file = TRUE; - break; - } - if (c == '\n' || c == '\000') - break; - retval[pos++] = (unsigned char) c; - if (pos % 128 == 127) - { - len = len + 128; - retval = realloc (retval, len); - } - } - - retval[pos] = '\000'; - return retval; -} - -/* Returns the number read from the file, or -1 if no number could be read. - */ -static int -_xdg_mime_magic_read_a_number (FILE *magic_file, - int *end_of_file) -{ - /* LONG_MAX is about 20 characters on my system */ -#define MAX_NUMBER_SIZE 30 - char number_string[MAX_NUMBER_SIZE + 1]; - int pos = 0; - int c; - long retval = -1; - - while (TRUE) - { - c = getc_unlocked (magic_file); - - if (c == EOF) - { - *end_of_file = TRUE; - break; - } - if (! isdigit (c)) - { - ungetc (c, magic_file); - break; - } - number_string[pos] = (char) c; - pos++; - if (pos == MAX_NUMBER_SIZE) - break; - } - if (pos > 0) - { - number_string[pos] = '\000'; - errno = 0; - retval = strtol (number_string, NULL, 10); - - if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0)) - return -1; - } - - return retval; -} - -/* Headers are of the format: - * [:] - */ -static XdgMimeMagicState -_xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match) -{ - int c; - char *buffer; - char *end_ptr; - int end_of_file = 0; - - assert (magic_file != NULL); - assert (match != NULL); - - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - if (c != '[') - return XDG_MIME_MAGIC_ERROR; - - match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); - if (end_of_file) - return XDG_MIME_MAGIC_EOF; - if (match->priority == -1) - return XDG_MIME_MAGIC_ERROR; - - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - if (c != ':') - return XDG_MIME_MAGIC_ERROR; - - buffer = (char *)_xdg_mime_magic_read_to_newline (magic_file, &end_of_file); - if (end_of_file) - return XDG_MIME_MAGIC_EOF; - - end_ptr = buffer; - while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n') - end_ptr++; - if (*end_ptr != ']') - { - free (buffer); - return XDG_MIME_MAGIC_ERROR; - } - *end_ptr = '\000'; - - match->mime_type = strdup (buffer); - free (buffer); - - return XDG_MIME_MAGIC_MAGIC; -} - -static XdgMimeMagicState -_xdg_mime_magic_parse_error (FILE *magic_file) -{ - int c; - - while (1) - { - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - if (c == '\n') - return XDG_MIME_MAGIC_SECTION; - } -} - -/* Headers are of the format: - * [ indent ] ">" start-offset "=" value - * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n" - */ -static XdgMimeMagicState -_xdg_mime_magic_parse_magic_line (FILE *magic_file, - XdgMimeMagicMatch *match) -{ - XdgMimeMagicMatchlet *matchlet; - int c; - int end_of_file; - int indent = 0; - size_t bytes_read; - - assert (magic_file != NULL); - - /* Sniff the buffer to make sure it's a valid line */ - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - else if (c == '[') - { - ungetc (c, magic_file); - return XDG_MIME_MAGIC_SECTION; - } - else if (c == '\n') - return XDG_MIME_MAGIC_MAGIC; - - /* At this point, it must be a digit or a '>' */ - end_of_file = FALSE; - if (isdigit (c)) - { - ungetc (c, magic_file); - indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); - if (end_of_file) - return XDG_MIME_MAGIC_EOF; - if (indent == -1) - return XDG_MIME_MAGIC_ERROR; - c = getc_unlocked (magic_file); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - } - - if (c != '>') - return XDG_MIME_MAGIC_ERROR; - - matchlet = _xdg_mime_magic_matchlet_new (); - matchlet->indent = indent; - matchlet->offset = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); - if (end_of_file) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - if (matchlet->offset == -1) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - c = getc_unlocked (magic_file); - if (c == EOF) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - else if (c != '=') - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - - /* Next two bytes determine how long the value is */ - matchlet->value_length = 0; - c = getc_unlocked (magic_file); - if (c == EOF) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - matchlet->value_length = c & 0xFF; - matchlet->value_length = matchlet->value_length << 8; - - c = getc_unlocked (magic_file); - if (c == EOF) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - matchlet->value_length = matchlet->value_length + (c & 0xFF); - - matchlet->value = malloc (matchlet->value_length); - - /* OOM */ - if (matchlet->value == NULL) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file); - if (bytes_read != matchlet->value_length) - { - _xdg_mime_magic_matchlet_free (matchlet); - if (feof (magic_file)) - return XDG_MIME_MAGIC_EOF; - else - return XDG_MIME_MAGIC_ERROR; - } - - c = getc_unlocked (magic_file); - if (c == '&') - { - matchlet->mask = malloc (matchlet->value_length); - /* OOM */ - if (matchlet->mask == NULL) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file); - if (bytes_read != matchlet->value_length) - { - _xdg_mime_magic_matchlet_free (matchlet); - if (feof (magic_file)) - return XDG_MIME_MAGIC_EOF; - else - return XDG_MIME_MAGIC_ERROR; - } - c = getc_unlocked (magic_file); - } - - if (c == '~') - { - matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); - if (end_of_file) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - if (matchlet->word_size != 0 && - matchlet->word_size != 1 && - matchlet->word_size != 2 && - matchlet->word_size != 4) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - c = getc_unlocked (magic_file); - } - - if (c == '+') - { - matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file); - if (end_of_file) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_EOF; - } - if (matchlet->range_length == (unsigned int)-1) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - c = getc_unlocked (magic_file); - } - - - if (c == '\n') - { - /* We clean up the matchlet, byte swapping if needed */ - if (matchlet->word_size > 1) - { - size_t i; - if (matchlet->value_length % matchlet->word_size != 0) - { - _xdg_mime_magic_matchlet_free (matchlet); - return XDG_MIME_MAGIC_ERROR; - } - /* FIXME: need to get this defined in a style file */ -#if LITTLE_ENDIAN - for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size) - { - if (matchlet->word_size == 2) - *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i))); - else if (matchlet->word_size == 4) - *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i))); - if (matchlet->mask) - { - if (matchlet->word_size == 2) - *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i))); - else if (matchlet->word_size == 4) - *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i))); - - } - } -#endif - } - - matchlet->next = match->matchlet; - match->matchlet = matchlet; - - - return XDG_MIME_MAGIC_MAGIC; - } - - _xdg_mime_magic_matchlet_free (matchlet); - if (c == EOF) - return XDG_MIME_MAGIC_EOF; - - return XDG_MIME_MAGIC_ERROR; -} - -static int -_xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet, - const void *data, - size_t len) -{ - size_t i, j; - for (i = matchlet->offset; i < matchlet->offset + matchlet->range_length; i++) - { - int valid_matchlet = TRUE; - - if (i + matchlet->value_length > len) - return FALSE; - - if (matchlet->mask) - { - for (j = 0; j < matchlet->value_length; j++) - { - if ((matchlet->value[j] & matchlet->mask[j]) != - ((((unsigned char *) data)[j + i]) & matchlet->mask[j])) - { - valid_matchlet = FALSE; - break; - } - } - } - else - { - for (j = 0; j < matchlet->value_length; j++) - { - if (matchlet->value[j] != ((unsigned char *) data)[j + i]) - { - valid_matchlet = FALSE; - break; - } - } - } - if (valid_matchlet) - return TRUE; - } - return FALSE; -} - -static int -_xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet, - const void *data, - size_t len, - int indent) -{ - while ((matchlet != NULL) && (matchlet->indent == indent)) - { - if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len)) - { - if ((matchlet->next == NULL) || - (matchlet->next->indent <= indent)) - return TRUE; - - if (_xdg_mime_magic_matchlet_compare_level (matchlet->next, - data, - len, - indent + 1)) - return TRUE; - } - - do - { - matchlet = matchlet->next; - } - while (matchlet && matchlet->indent > indent); - } - - return FALSE; -} - -static int -_xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match, - const void *data, - size_t len) -{ - return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0); -} - -static void -_xdg_mime_magic_insert_match (XdgMimeMagic *mime_magic, - XdgMimeMagicMatch *match) -{ - XdgMimeMagicMatch *list; - - if (mime_magic->match_list == NULL) - { - mime_magic->match_list = match; - return; - } - - if (match->priority > mime_magic->match_list->priority) - { - match->next = mime_magic->match_list; - mime_magic->match_list = match; - return; - } - - list = mime_magic->match_list; - while (list->next != NULL) - { - if (list->next->priority < match->priority) - { - match->next = list->next; - list->next = match; - return; - } - list = list->next; - } - list->next = match; - match->next = NULL; -} - -XdgMimeMagic * -_xdg_mime_magic_new (void) -{ - return calloc (1, sizeof (XdgMimeMagic)); -} - -void -_xdg_mime_magic_free (XdgMimeMagic *mime_magic) -{ - if (mime_magic) { - _xdg_mime_magic_match_free (mime_magic->match_list); - free (mime_magic); - } -} - -int -_xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic) -{ - return mime_magic->max_extent; -} - -const char * -_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic, - const void *data, - size_t len, - int *result_prio, - const char *mime_types[], - int n_mime_types) -{ - XdgMimeMagicMatch *match; - const char *mime_type; - int n; - int prio; - - prio = 0; - mime_type = NULL; - for (match = mime_magic->match_list; match; match = match->next) - { - if (_xdg_mime_magic_match_compare_to_data (match, data, len)) - { - prio = match->priority; - mime_type = match->mime_type; - break; - } - else - { - for (n = 0; n < n_mime_types; n++) - { - if (mime_types[n] && - _xdg_mime_mime_type_equal (mime_types[n], match->mime_type)) - mime_types[n] = NULL; - } - } - } - - if (mime_type == NULL) - { - for (n = 0; n < n_mime_types; n++) - { - if (mime_types[n]) - mime_type = mime_types[n]; - } - } - - if (result_prio) - *result_prio = prio; - - return mime_type; -} - -static void -_xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic) -{ - XdgMimeMagicMatch *match; - int max_extent = 0; - - for (match = mime_magic->match_list; match; match = match->next) - { - XdgMimeMagicMatchlet *matchlet; - - for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next) - { - int extent; - - extent = matchlet->value_length + matchlet->offset + matchlet->range_length; - if (max_extent < extent) - max_extent = extent; - } - } - - mime_magic->max_extent = max_extent; -} - -static XdgMimeMagicMatchlet * -_xdg_mime_magic_matchlet_mirror (XdgMimeMagicMatchlet *matchlets) -{ - XdgMimeMagicMatchlet *new_list; - XdgMimeMagicMatchlet *tmp; - - if ((matchlets == NULL) || (matchlets->next == NULL)) - return matchlets; - - new_list = NULL; - tmp = matchlets; - while (tmp != NULL) - { - XdgMimeMagicMatchlet *matchlet; - - matchlet = tmp; - tmp = tmp->next; - matchlet->next = new_list; - new_list = matchlet; - } - - return new_list; - -} - -static void -_xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic, - FILE *magic_file) -{ - XdgMimeMagicState state; - XdgMimeMagicMatch *match = NULL; /* Quiet compiler */ - - state = XDG_MIME_MAGIC_SECTION; - - while (state != XDG_MIME_MAGIC_EOF) - { - switch (state) - { - case XDG_MIME_MAGIC_SECTION: - match = _xdg_mime_magic_match_new (); - state = _xdg_mime_magic_parse_header (magic_file, match); - if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR) - _xdg_mime_magic_match_free (match); - break; - case XDG_MIME_MAGIC_MAGIC: - state = _xdg_mime_magic_parse_magic_line (magic_file, match); - if (state == XDG_MIME_MAGIC_SECTION || - (state == XDG_MIME_MAGIC_EOF && match->mime_type)) - { - match->matchlet = _xdg_mime_magic_matchlet_mirror (match->matchlet); - _xdg_mime_magic_insert_match (mime_magic, match); - } - else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR) - _xdg_mime_magic_match_free (match); - break; - case XDG_MIME_MAGIC_ERROR: - state = _xdg_mime_magic_parse_error (magic_file); - break; - case XDG_MIME_MAGIC_EOF: - default: - /* Make the compiler happy */ - assert (0); - } - } - _xdg_mime_update_mime_magic_extents (mime_magic); -} - -void -_xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic, - const char *file_name) -{ - FILE *magic_file; - char header[12]; - - magic_file = fopen (file_name, "r"); - - if (magic_file == NULL) - return; - - if (fread (header, 1, 12, magic_file) == 12) - { - if (memcmp ("MIME-Magic\0\n", header, 12) == 0) - _xdg_mime_magic_read_magic_file (mime_magic, magic_file); - } - - fclose (magic_file); -} diff --git a/src/butil/third_party/xdg_mime/xdgmimemagic.h b/src/butil/third_party/xdg_mime/xdgmimemagic.h deleted file mode 100644 index 35c8039c8e..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimemagic.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimemagic.h: Private file. Datastructure for storing the magic files. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2003 Red Hat, Inc. - * Copyright (C) 2003 Jonathan Blandford - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __XDG_MIME_MAGIC_H__ -#define __XDG_MIME_MAGIC_H__ - -#include -#include "xdgmime.h" -typedef struct XdgMimeMagic XdgMimeMagic; - -#ifdef XDG_PREFIX -#define _xdg_mime_glob_read_from_file XDG_RESERVED_ENTRY(glob_read_from_file) -#define _xdg_mime_magic_new XDG_RESERVED_ENTRY(magic_new) -#define _xdg_mime_magic_read_from_file XDG_RESERVED_ENTRY(magic_read_from_file) -#define _xdg_mime_magic_free XDG_RESERVED_ENTRY(magic_free) -#define _xdg_mime_magic_get_buffer_extents XDG_RESERVED_ENTRY(magic_get_buffer_extents) -#define _xdg_mime_magic_lookup_data XDG_RESERVED_ENTRY(magic_lookup_data) -#endif - - -XdgMimeMagic *_xdg_mime_magic_new (void); -void _xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic, - const char *file_name); -void _xdg_mime_magic_free (XdgMimeMagic *mime_magic); -int _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic); -const char *_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic, - const void *data, - size_t len, - int *result_prio, - const char *mime_types[], - int n_mime_types); - -#endif /* __XDG_MIME_MAGIC_H__ */ diff --git a/src/butil/third_party/xdg_mime/xdgmimeparent.c b/src/butil/third_party/xdg_mime/xdgmimeparent.c deleted file mode 100644 index 754a34bcbe..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimeparent.c +++ /dev/null @@ -1,218 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimealias.c: Private file. Datastructure for storing the hierarchy. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2004 Red Hat, Inc. - * Copyright (C) 2004 Matthias Clasen - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "xdgmimeparent.h" -#include "xdgmimeint.h" -#include -#include -#include -#include -#include - -#ifndef FALSE -#define FALSE (0) -#endif - -#ifndef TRUE -#define TRUE (!FALSE) -#endif - -typedef struct XdgMimeParents XdgMimeParents; - -struct XdgMimeParents -{ - char *mime; - char **parents; - int n_parents; -}; - -struct XdgParentList -{ - struct XdgMimeParents *parents; - int n_mimes; -}; - -XdgParentList * -_xdg_mime_parent_list_new (void) -{ - XdgParentList *list; - - list = malloc (sizeof (XdgParentList)); - - list->parents = NULL; - list->n_mimes = 0; - - return list; -} - -void -_xdg_mime_parent_list_free (XdgParentList *list) -{ - int i; - char **p; - - if (list->parents) - { - for (i = 0; i < list->n_mimes; i++) - { - for (p = list->parents[i].parents; *p; p++) - free (*p); - - free (list->parents[i].parents); - free (list->parents[i].mime); - } - free (list->parents); - } - free (list); -} - -static int -parent_entry_cmp (const void *v1, const void *v2) -{ - return strcmp (((XdgMimeParents *)v1)->mime, ((XdgMimeParents *)v2)->mime); -} - -const char ** -_xdg_mime_parent_list_lookup (XdgParentList *list, - const char *mime) -{ - XdgMimeParents *entry; - XdgMimeParents key; - - if (list->n_mimes > 0) - { - key.mime = (char *)mime; - key.parents = NULL; - - entry = bsearch (&key, list->parents, list->n_mimes, - sizeof (XdgMimeParents), &parent_entry_cmp); - if (entry) - return (const char **)entry->parents; - } - - return NULL; -} - -void -_xdg_mime_parent_read_from_file (XdgParentList *list, - const char *file_name) -{ - FILE *file; - char line[255]; - int i, alloc; - XdgMimeParents *entry; - - file = fopen (file_name, "r"); - - if (file == NULL) - return; - - /* FIXME: Not UTF-8 safe. Doesn't work if lines are greater than 255 chars. - * Blah */ - alloc = list->n_mimes + 16; - list->parents = realloc (list->parents, alloc * sizeof (XdgMimeParents)); - while (fgets (line, 255, file) != NULL) - { - char *sep; - if (line[0] == '#') - continue; - - sep = strchr (line, ' '); - if (sep == NULL) - continue; - *(sep++) = '\000'; - sep[strlen (sep) -1] = '\000'; - entry = NULL; - for (i = 0; i < list->n_mimes; i++) - { - if (strcmp (list->parents[i].mime, line) == 0) - { - entry = &(list->parents[i]); - break; - } - } - - if (!entry) - { - if (list->n_mimes == alloc) - { - alloc <<= 1; - list->parents = realloc (list->parents, - alloc * sizeof (XdgMimeParents)); - } - list->parents[list->n_mimes].mime = strdup (line); - list->parents[list->n_mimes].parents = NULL; - entry = &(list->parents[list->n_mimes]); - list->n_mimes++; - } - - if (!entry->parents) - { - entry->n_parents = 1; - entry->parents = malloc ((entry->n_parents + 1) * sizeof (char *)); - } - else - { - entry->n_parents += 1; - entry->parents = realloc (entry->parents, - (entry->n_parents + 2) * sizeof (char *)); - } - entry->parents[entry->n_parents - 1] = strdup (sep); - entry->parents[entry->n_parents] = NULL; - } - - list->parents = realloc (list->parents, - list->n_mimes * sizeof (XdgMimeParents)); - - fclose (file); - - if (list->n_mimes > 1) - qsort (list->parents, list->n_mimes, - sizeof (XdgMimeParents), &parent_entry_cmp); -} - - -void -_xdg_mime_parent_list_dump (XdgParentList *list) -{ - int i; - char **p; - - if (list->parents) - { - for (i = 0; i < list->n_mimes; i++) - { - for (p = list->parents[i].parents; *p; p++) - printf ("%s %s\n", list->parents[i].mime, *p); - } - } -} - diff --git a/src/butil/third_party/xdg_mime/xdgmimeparent.h b/src/butil/third_party/xdg_mime/xdgmimeparent.h deleted file mode 100644 index b564f4127f..0000000000 --- a/src/butil/third_party/xdg_mime/xdgmimeparent.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ -/* xdgmimeparent.h: Private file. Datastructure for storing the hierarchy. - * - * More info can be found at http://www.freedesktop.org/standards/ - * - * Copyright (C) 2004 Red Hat, Inc. - * Copyright (C) 200 Matthias Clasen - * - * Licensed under the Academic Free License version 2.0 - * Or under the following terms: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __XDG_MIME_PARENT_H__ -#define __XDG_MIME_PARENT_H__ - -#include "xdgmime.h" - -typedef struct XdgParentList XdgParentList; - -#ifdef XDG_PREFIX -#define _xdg_mime_parent_read_from_file XDG_RESERVED_ENTRY(parent_read_from_file) -#define _xdg_mime_parent_list_new XDG_RESERVED_ENTRY(parent_list_new) -#define _xdg_mime_parent_list_free XDG_RESERVED_ENTRY(parent_list_free) -#define _xdg_mime_parent_list_lookup XDG_RESERVED_ENTRY(parent_list_lookup) -#define _xdg_mime_parent_list_dump XDG_RESERVED_ENTRY(parent_list_dump) -#endif - -void _xdg_mime_parent_read_from_file (XdgParentList *list, - const char *file_name); -XdgParentList *_xdg_mime_parent_list_new (void); -void _xdg_mime_parent_list_free (XdgParentList *list); -const char **_xdg_mime_parent_list_lookup (XdgParentList *list, - const char *mime); -void _xdg_mime_parent_list_dump (XdgParentList *list); - -#endif /* __XDG_MIME_PARENT_H__ */ diff --git a/src/butil/third_party/xdg_user_dirs/LICENSE b/src/butil/third_party/xdg_user_dirs/LICENSE deleted file mode 100644 index 540e8031c7..0000000000 --- a/src/butil/third_party/xdg_user_dirs/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - Copyright (c) 2007 Red Hat, inc - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. diff --git a/src/butil/third_party/xdg_user_dirs/README.chromium b/src/butil/third_party/xdg_user_dirs/README.chromium deleted file mode 100644 index b4cf6d7400..0000000000 --- a/src/butil/third_party/xdg_user_dirs/README.chromium +++ /dev/null @@ -1,7 +0,0 @@ -Name: xdg-user-dirs -URL: http://www.freedesktop.org/wiki/Software/xdg-user-dirs -License: MIT - -This directory include xdg-user-dir-lookup.c renamed as xdg_user_dir_lookup.cc -from xdg-user-dirs 0.10. We made xdg_user_dir_lookup() non-static and added a -xdg_user_dir_lookup.h. diff --git a/src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc b/src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc deleted file mode 100644 index 343f70cb42..0000000000 --- a/src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.cc +++ /dev/null @@ -1,232 +0,0 @@ -/* - This file is not licenced under the GPL like the rest of the code. - Its is under the MIT license, to encourage reuse by cut-and-paste. - - Copyright (c) 2007 Red Hat, inc - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -#include -#include -#include - -/** - * xdg_user_dir_lookup_with_fallback: - * @type: a string specifying the type of directory - * @fallback: value to use if the directory isn't specified by the user - * @returns: a newly allocated absolute pathname - * - * Looks up a XDG user directory of the specified type. - * Example of types are "DESKTOP" and "DOWNLOAD". - * - * In case the user hasn't specified any directory for the specified - * type the value returned is @fallback. - * - * The return value is newly allocated and must be freed with - * free(). The return value is never NULL if @fallback != NULL, unless - * out of memory. - **/ -static char * -xdg_user_dir_lookup_with_fallback (const char *type, const char *fallback) -{ - FILE *file; - char *home_dir, *config_home, *config_file; - char buffer[512]; - char *user_dir; - char *p, *d; - int len; - int relative; - - home_dir = getenv ("HOME"); - - if (home_dir == NULL) - goto error; - - config_home = getenv ("XDG_CONFIG_HOME"); - if (config_home == NULL || config_home[0] == 0) - { - config_file = (char*) malloc (strlen (home_dir) + strlen ("/.config/user-dirs.dirs") + 1); - if (config_file == NULL) - goto error; - - strcpy (config_file, home_dir); - strcat (config_file, "/.config/user-dirs.dirs"); - } - else - { - config_file = (char*) malloc (strlen (config_home) + strlen ("/user-dirs.dirs") + 1); - if (config_file == NULL) - goto error; - - strcpy (config_file, config_home); - strcat (config_file, "/user-dirs.dirs"); - } - - file = fopen (config_file, "r"); - free (config_file); - if (file == NULL) - goto error; - - user_dir = NULL; - while (fgets (buffer, sizeof (buffer), file)) - { - /* Remove newline at end */ - len = strlen (buffer); - if (len > 0 && buffer[len-1] == '\n') - buffer[len-1] = 0; - - p = buffer; - while (*p == ' ' || *p == '\t') - p++; - - if (strncmp (p, "XDG_", 4) != 0) - continue; - p += 4; - if (strncmp (p, type, strlen (type)) != 0) - continue; - p += strlen (type); - if (strncmp (p, "_DIR", 4) != 0) - continue; - p += 4; - - while (*p == ' ' || *p == '\t') - p++; - - if (*p != '=') - continue; - p++; - - while (*p == ' ' || *p == '\t') - p++; - - if (*p != '"') - continue; - p++; - - relative = 0; - if (strncmp (p, "$HOME/", 6) == 0) - { - p += 6; - relative = 1; - } - else if (*p != '/') - continue; - - if (relative) - { - user_dir = (char*) malloc (strlen (home_dir) + 1 + strlen (p) + 1); - if (user_dir == NULL) - goto error2; - - strcpy (user_dir, home_dir); - strcat (user_dir, "/"); - } - else - { - user_dir = (char*) malloc (strlen (p) + 1); - if (user_dir == NULL) - goto error2; - - *user_dir = 0; - } - - d = user_dir + strlen (user_dir); - while (*p && *p != '"') - { - if ((*p == '\\') && (*(p+1) != 0)) - p++; - *d++ = *p++; - } - *d = 0; - } -error2: - fclose (file); - - if (user_dir) - return user_dir; - - error: - if (fallback) - return strdup (fallback); - return NULL; -} - -/** - * xdg_user_dir_lookup: - * @type: a string specifying the type of directory - * @returns: a newly allocated absolute pathname - * - * Looks up a XDG user directory of the specified type. - * Example of types are "DESKTOP" and "DOWNLOAD". - * - * The return value is always != NULL (unless out of memory), - * and if a directory - * for the type is not specified by the user the default - * is the home directory. Except for DESKTOP which defaults - * to ~/Desktop. - * - * The return value is newly allocated and must be freed with - * free(). - **/ -char * -xdg_user_dir_lookup (const char *type) -{ - char *dir, *home_dir, *user_dir; - - dir = xdg_user_dir_lookup_with_fallback (type, NULL); - if (dir != NULL) - return dir; - - home_dir = getenv ("HOME"); - - if (home_dir == NULL) - return strdup ("/tmp"); - - /* Special case desktop for historical compatibility */ - if (strcmp (type, "DESKTOP") == 0) - { - user_dir = (char*) malloc (strlen (home_dir) + strlen ("/Desktop") + 1); - if (user_dir == NULL) - return NULL; - - strcpy (user_dir, home_dir); - strcat (user_dir, "/Desktop"); - return user_dir; - } - - return strdup (home_dir); -} - -#ifdef STANDALONE_XDG_USER_DIR_LOOKUP -int -main (int argc, char *argv[]) -{ - if (argc != 2) - { - fprintf (stderr, "Usage %s \n", argv[0]); - exit (1); - } - - printf ("%s\n", xdg_user_dir_lookup (argv[1])); - return 0; -} -#endif diff --git a/src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.h b/src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.h deleted file mode 100644 index 9e81e1b530..0000000000 --- a/src/butil/third_party/xdg_user_dirs/xdg_user_dir_lookup.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - This file is not licenced under the GPL like the rest of the code. - Its is under the MIT license, to encourage reuse by cut-and-paste. - - Copyright (c) 2007 Red Hat, inc - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. -*/ - -#ifndef CHROME_THIRD_PARTY_XDG_USER_DIRS_XDG_USER_DIR_LOOKUP_H_ -#define CHROME_THIRD_PARTY_XDG_USER_DIRS_XDG_USER_DIR_LOOKUP_H_ - -char* xdg_user_dir_lookup(const char *type); - -#endif // CHROME_THIRD_PARTY_XDG_USER_DIRS_XDG_USER_DIR_LOOKUP_H_ diff --git a/test/file_util_unittest.cc b/test/file_util_unittest.cc index a0b855166d..9abfd9310c 100644 --- a/test/file_util_unittest.cc +++ b/test/file_util_unittest.cc @@ -29,7 +29,6 @@ #include "butil/files/scoped_file.h" #include "butil/files/scoped_temp_dir.h" #include "butil/strings/utf_string_conversions.h" -#include "test/test_file_util.h" #include "butil/threading/platform_thread.h" #include #include diff --git a/test/stack_trace_unittest.cc b/test/stack_trace_unittest.cc index 85ce63f38c..1b96161001 100644 --- a/test/stack_trace_unittest.cc +++ b/test/stack_trace_unittest.cc @@ -8,7 +8,6 @@ #include "butil/debug/stack_trace.h" #include "butil/logging.h" -#include "test/test_timeouts.h" #include namespace butil { From e8ca7be300b43c74dd3c78e186690eea975f3d45 Mon Sep 17 00:00:00 2001 From: zhujiashun Date: Mon, 18 Sep 2017 17:37:45 +0800 Subject: [PATCH 05/11] add en/new_protocol.md --- docs/cn/new_protocol.md | 2 +- docs/en/new_protocol.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 docs/en/new_protocol.md diff --git a/docs/cn/new_protocol.md b/docs/cn/new_protocol.md index 3832c03ed8..cb61705e52 100644 --- a/docs/cn/new_protocol.md +++ b/docs/cn/new_protocol.md @@ -2,7 +2,7 @@ brpc server在同端口支持所有的协议,大部分时候这对部署和运维更加方便。由于不同协议的格式大相径庭,严格地来说,同端口很难无二义地支持所有协议。出于解耦和可扩展性的考虑,也不太可能集中式地构建一个针对所有协议的分类器。我们的做法就是把协议归三类后逐个尝试: -- 第一类协议:标记或特殊字符在最前面,比如[baidu_std](baidu_std.md),hulu_pbrpc的前4个字符分别分别是PRPC和HULU,解析代码只需要检查前4个字节就可以知道协议是否匹配,最先尝试这类协议。这些协议在同一个连接上也可以共存。 +- 第一类协议:标记或特殊字符在最前面,比如[baidu_std](baidu_std.md),hulu_pbrpc的前4个字符分别是PRPC和HULU,解析代码只需要检查前4个字节就可以知道协议是否匹配,最先尝试这类协议。这些协议在同一个连接上也可以共存。 - 第二类协议:有较为复杂的语法,没有固定的协议标记或特殊字符,可能在解析一段输入后才能判断是否匹配,目前此类协议只有http。 - 第三类协议:协议标记或特殊字符在中间,比如nshead的magic_num在第25-28字节。由于之前的字段均为二进制,难以判断正确性,在没有读取完28字节前,我们无法判定消息是不是nshead格式的,所以处理起来很麻烦,若其解析排在http之前,那么<=28字节的http消息便可能无法被解析,因为程序以为是“还未完整的nshead消息”。 diff --git a/docs/en/new_protocol.md b/docs/en/new_protocol.md new file mode 100644 index 0000000000..221f6986c3 --- /dev/null +++ b/docs/en/new_protocol.md @@ -0,0 +1,12 @@ +# Multi-protocol support in server side + +brpc server supports all protocols in the same port, and it makes deployment and maintenance more convenient in most of the time. Since the format of different protocols is very different, it is hard to support all protocols in the same port unambiguously. In consider of decoupling and extensibility, it is also hard to build a multiplexer for all protocols. Thus our way is to classify all protocols into three categories and try one by one: + +- First-class protocol: Special characters are marked in front of the protocol data, for example, the data of protocol [baidu_std](baidu_std.md) and hulu_pbrpc begins with 'PRPC' and 'HULU' respectively. Parser just check first four characters to know whether the protocol is matched. This class of protocol is checked first and all these protocols can share one TCP connection. +- Second-class protocol: Some complex protocols without special marked characters can only be detected after several input data are parsed. Currently only HTTP is classified into this category. +- Third-class protocol: Special characters are in the middle of the protocol data, such as the magic number of nshead protocol is the 25th-28th characters. It is complex to handle this case because without reading first 28 bytes, we cannot determine whether the protocol is nshead. If it is tried before http, http messages less than 28 bytes may not be parsed, since the parser consider it as an uncomplete nshead message. + +Considering that there will be only one protocol in most connections, we record the result of last selection so that it will be tried first when further data comes. It reduces the overhead of matching protocols to nearly zero for long connections. Although the process of matching protocols will be run everytime for short connections, the bottleneck of short connections is not in here and this method is still fast enough. If there are lots of new protocols added into brpc in the future, we may consider some heuristic methods to match protocols. + +# Multi-protocol support in client side + From f3b388a8133750f55a474d5aeb200f9bafee0a64 Mon Sep 17 00:00:00 2001 From: Zhangyi Chen Date: Mon, 18 Sep 2017 17:55:32 +0800 Subject: [PATCH 06/11] Remove butil/allocator/ --- Makefile | 1 - src/butil/allocator/type_profiler_control.cc | 38 -------------------- src/butil/allocator/type_profiler_control.h | 31 ---------------- 3 files changed, 70 deletions(-) delete mode 100644 src/butil/allocator/type_profiler_control.cc delete mode 100644 src/butil/allocator/type_profiler_control.h diff --git a/Makefile b/Makefile index 2cbc83ef8a..76c545250e 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,6 @@ BUTIL_SOURCES = \ src/butil/third_party/snappy/snappy-stubs-internal.cc \ src/butil/third_party/snappy/snappy.cc \ src/butil/third_party/murmurhash3/murmurhash3.cpp \ - src/butil/allocator/type_profiler_control.cc \ src/butil/arena.cpp \ src/butil/at_exit.cc \ src/butil/atomicops_internals_x86_gcc.cc \ diff --git a/src/butil/allocator/type_profiler_control.cc b/src/butil/allocator/type_profiler_control.cc deleted file mode 100644 index 4365b22bab..0000000000 --- a/src/butil/allocator/type_profiler_control.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 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. - -#include "butil/allocator/type_profiler_control.h" - -namespace butil { -namespace type_profiler { - -namespace { - -#if defined(TYPE_PROFILING) -const bool kTypeProfilingEnabled = true; -#else -const bool kTypeProfilingEnabled = false; -#endif - -bool g_enable_intercept = kTypeProfilingEnabled; - -} // namespace - -// static -void Controller::Stop() { - g_enable_intercept = false; -} - -// static -bool Controller::IsProfiling() { - return kTypeProfilingEnabled && g_enable_intercept; -} - -// static -void Controller::Restart() { - g_enable_intercept = kTypeProfilingEnabled; -} - -} // namespace type_profiler -} // namespace butil diff --git a/src/butil/allocator/type_profiler_control.h b/src/butil/allocator/type_profiler_control.h deleted file mode 100644 index 14c8c62864..0000000000 --- a/src/butil/allocator/type_profiler_control.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 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_ALLOCATOR_TYPE_PROFILER_CONTROL_H_ -#define BASE_ALLOCATOR_TYPE_PROFILER_CONTROL_H_ - -#include "butil/gtest_prod_util.h" - -namespace butil { -namespace type_profiler { - -class Controller { - public: - static void Stop(); - static bool IsProfiling(); - - private: - FRIEND_TEST_ALL_PREFIXES(TypeProfilerTest, - TestProfileNewWithoutProfiledDelete); - - // It must be used only from allowed unit tests. The following is only - // allowed for use in unit tests. Profiling should never be restarted in - // regular use. - static void Restart(); -}; - -} // namespace type_profiler -} // namespace butil - -#endif // BASE_ALLOCATOR_TYPE_PROFILER_CONTROL_H_ From bea6f4c199a762deee389f22f98120513c23e963 Mon Sep 17 00:00:00 2001 From: jiangrujie Date: Mon, 18 Sep 2017 19:14:48 +0800 Subject: [PATCH 07/11] Add documentation in english --- docs/cn/http_client.md | 15 +-- docs/cn/streaming_log.md | 74 ++-------- docs/en/http_client.md | 231 ++++++++++++++++++++++++++++++++ docs/en/iobuf.md | 97 ++++++++++++++ docs/en/streaming_log.md | 283 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 632 insertions(+), 68 deletions(-) create mode 100644 docs/en/http_client.md create mode 100644 docs/en/iobuf.md create mode 100644 docs/en/streaming_log.md diff --git a/docs/cn/http_client.md b/docs/cn/http_client.md index bbe34d5a4f..b689ca5216 100644 --- a/docs/cn/http_client.md +++ b/docs/cn/http_client.md @@ -83,7 +83,7 @@ URL的一般形式如下图: 确实,在简单使用场景下,这两者有所重复,但在复杂场景中,两者差别很大,比如: -- 访问挂在bns下的多个http server。此时Channel.Init传入的是bns节点名称,对uri()的赋值则是包含Host的完整URL(比如"www.foo.com/index.html?name=value"),BNS下所有的http server都会看到"Host: www.foo.com";uri()也可以是只包含路径的URL,比如"/index.html?name=value",框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。 +- 访问挂在bns下的多个http server。此时Channel.Init传入的是bns节点名称,对uri()的赋值则是包含Host的完整URL(比如"www.foo.com/index.html?name=value"),BNS下所有的http server都会看到"Host: [www.foo.com](http://www.foo.com/)";uri()也可以是只包含路径的URL,比如"/index.html?name=value",框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。 - 通过http proxy访问目标server。此时Channel.Init传入的是proxy server的地址,但uri()填入的是目标server的URL。 # 常见设置 @@ -132,7 +132,7 @@ os.move_to(cntl->request_attachment()); Notes on http header: -- 根据 HTTP 协议[规定](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2), header 的 field_name部分不区分大小写。在r33861前,field_name都转为了小写,在r33861后,大小写能保持不变(仍然支持大小写不敏感)。 +- 根据 HTTP 协议[规定](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2), header 的 field_name部分不区分大小写。brpc对于field_name大小写保持不变,且仍然支持大小写不敏感。 - 如果 HTTP 头中出现了相同的 field_name, 根据协议[规定](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2),value将被合并到一起, 中间用逗号(,) 分隔, 具体value如何理解,需要用户自己确定. - query之间用"&"分隔, key和value之间用"="分隔, value可以省略,比如key1=value1&key2&key3=value3中key2是合理的query,值为空字符串。 @@ -144,12 +144,11 @@ Notes on http header: 当Server返回的http status code不是2xx时,该次http访问即视为失败,client端会设置对应的ErrorCode: -- 在r31923前,1xx和3xx错误对应EPROTONOSUPPORT,4xx错误对应EREQUEST,其余错误对应EINTERNAL。http body不会置入`cntl->response_attachment()`。 -- 在r31923后,所有错误被统一为EHTTP。如果用户发现`cntl->ErrorCode()`为EHTTP,那么可以检查`cntl->http_response().status_code()`以获得具体的http错误。同时http body会置入`cntl->response_attachment()`,用户可以把代表错误的html或json传递回来。 +- 所有错误被统一为EHTTP。如果用户发现`cntl->ErrorCode()`为EHTTP,那么可以检查`cntl->http_response().status_code()`以获得具体的http错误。同时http body会置入`cntl->response_attachment()`,用户可以把代表错误的html或json传递回来。 # 压缩request body -在r33877后,调用Controller::set_request_compress_type(brpc::COMPRESS_TYPE_GZIP)可将http body用gzip压缩,并设置"Content-Encoding"为"gzip"。 +调用Controller::set_request_compress_type(brpc::COMPRESS_TYPE_GZIP)可将http body用gzip压缩,并设置"Content-Encoding"为"gzip"。 # 解压response body @@ -172,9 +171,9 @@ if (encoding != NULL && *encoding == "gzip") { # 持续下载 -r33796前brpc client在下载一个超长的body时,需要一直等待直到body完整才会视作RPC结束,这个过程中超长body都会存在内存中,如果body是无限长的(比如直播用的flv文件),那么内存会持续增长,直到超时。换句话说,r33796前的brpc client不适合下载大文件。 +通常下载一个超长的body时,需要一直等待直到body完整才会视作RPC结束,这个过程中超长body都会存在内存中,如果body是无限长的(比如直播用的flv文件),那么内存会持续增长,直到超时。这样的http client不适合下载大文件。 -r33796后brpc client支持在读取完body前就结束RPC,让用户在RPC结束后再读取持续增长的body。注意这个功能不等同于“支持http chunked mode”,brpc的http实现一直支持解析chunked mode,这里的问题是如何让用户处理超长或无限长的body,和body是否以chunked mode传输无关。 +brpc client支持在读取完body前就结束RPC,让用户在RPC结束后再读取持续增长的body。注意这个功能不等同于“支持http chunked mode”,brpc的http实现一直支持解析chunked mode,这里的问题是如何让用户处理超长或无限长的body,和body是否以chunked mode传输无关。 使用方法如下: @@ -215,4 +214,4 @@ r33796后brpc client支持在读取完body前就结束RPC,让用户在RPC结 # 访问带认证的Server -根据Server的认证方式生成对应的auth_data,并设置为http header "Authorization"的值。比如用的是curl,那就加上选项`-H "Authorization : "。`查询[giano文档](http://doc.noah.baidu.com/new/baas/base_tool.md)了解如何在Shell中生成auth_data。 +根据Server的认证方式生成对应的auth_data,并设置为http header "Authorization"的值。比如用的是curl,那就加上选项`-H "Authorization : "。` diff --git a/docs/cn/streaming_log.md b/docs/cn/streaming_log.md index a65dd3be34..b951b438ab 100644 --- a/docs/cn/streaming_log.md +++ b/docs/cn/streaming_log.md @@ -16,52 +16,12 @@ PLOG(FATAL) << "Fail to call function setting errno"; VLOG(1) << "verbose log tier 1"; CHECK_GT(1, 2) << "1 can't be greater than 2"; -// public/common >= r32401支持限制打印频率。 LOG_EVERY_SECOND(INFO) << "High-frequent logs"; LOG_EVERY_N(ERROR, 10) << "High-frequent logs"; LOG_FIRST_N(INFO, 20) << "Logs that prints for at most 20 times"; LOG_ONCE(WARNING) << "Logs that only prints once"; ``` -## 配置comlog - -```c++ -// logging默认重定向至comlog,要配置comlog的话,要额外include comlog_sink.h -#include - -// 从./conf/log.conf读取comlog的配置。SetupFromConfig是我们提供的封装函数,不用像com_loadlog那样区分path和file。 -if (logging::ComlogSink::GetInstance()->SetupFromConfig("conf/log.conf") != 0) { - LOG(ERROR) << "Fail to setup comlog from conf/log.conf"; - return -1; -} - -OR - -// 直接调用com_loadlog从./conf/log.conf读取comlog的配置。 -if (com_loadlog("./conf", "log.conf") != 0) { - LOG(ERROR) << "Fail to com_loadlog"; - return -1; -} - -OR - -// 把日志打入./my_log/.log中,comlog选项取默认值。 -logging::ComlogSinkOptions options; -options.log_dir = "my_log"; -if (logging::ComlogSink::GetInstance()->Setup(&options) != 0) { - LOG(ERROR) << "Fail to setup comlog from options"; - return -1; -} - -OR - -// 把日志打入./log/.log中,comlog选项取默认值。 -if (logging::ComlogSink::GetInstance()->Setup(NULL) != 0) { - LOG(ERROR) << "Fail to setup comlog by default options"; - return -1; -} -``` - # DESCRIPTION 流式日志是打印复杂对象或模板对象的不二之选。大部分业务对象都很复杂,如果用printf形式的函数打印,你需要先把对象转成string,才能以%s输出。但string组合起来既不方便(比如没法append数字),还得分配大量的临时内存(string导致的)。C++中解决这个问题的方法便是“把日志流式地送入std::ostream对象”。比如为了打印对象A,那么我们得实现如下的函数: @@ -122,17 +82,17 @@ LOG(WARNING) << "Unusual thing happened ..." << ...; LOG(TRACE) << "Something just took place..." << ...; ``` -streaming log的日志等级是comlog和glog的合集,具体的来说,下表是日志等级的映射关系: +streaming log的日志等级与glog映射关系如下: -| streaming log | comlog | glog | 使用场景 | -| ------------- | ---------------------------- | -------------------- | ---------------------------------------- | -| FATAL | COMLOG_FATAL | FATAL (coredump) | 致命错误。但由于百度内大部分FATAL实际上非致命,所以streaming log的FATAL默认不像glog那样直接coredump,除非打开了[-crash_on_fatal_log](http://brpc.baidu.com:8765/flags/crash_on_fatal_log) | -| ERROR | COMLOG_FATAL | ERROR | 不致命的错误。 | -| WARNING | COMLOG_WARNING | WARNING | 不常见的分支。 | -| NOTICE | COMLOG_NOTICE | - | 一般来说你不应该使用NOTICE,它用于打印重要的业务日志,若要使用务必和检索端同学确认。glog没有NOTICE。 | -| INFO, TRACE | COMLOG_TRACE | INFO | 打印重要的副作用。比如打开关闭了某某资源之类的。 | -| VLOG(n) | COMLOG_TRACE | INFO | 打印分层的详细日志。 | -| DEBUG | COMLOG_TRACEVLOG(1) (NDEBUG) | INFOVLOG(1) (NDEBUG) | 仅为代码兼容性,基本没有用。若要使日志仅在未定义NDEBUG时才打印,用DLOG/DPLOG/DVLOG等即可。 | +| streaming log | glog | 使用场景 | +| ------------- | -------------------- | ---------------------------------------- | +| FATAL | FATAL (coredump) | 致命错误。但由于百度内大部分FATAL实际上非致命,所以streaming log的FATAL默认不像glog那样直接coredump,除非打开了[-crash_on_fatal_log](http://brpc.baidu.com:8765/flags/crash_on_fatal_log) | +| ERROR | ERROR | 不致命的错误。 | +| WARNING | WARNING | 不常见的分支。 | +| NOTICE | - | 一般来说你不应该使用NOTICE,它用于打印重要的业务日志,若要使用务必和检索端同学确认。glog没有NOTICE。 | +| INFO, TRACE | INFO | 打印重要的副作用。比如打开关闭了某某资源之类的。 | +| VLOG(n) | INFO | 打印分层的详细日志。 | +| DEBUG | INFOVLOG(1) (NDEBUG) | 仅为代码兼容性,基本没有用。若要使日志仅在未定义NDEBUG时才打印,用DLOG/DPLOG/DVLOG等即可。 | ## PLOG @@ -148,7 +108,7 @@ if (fd < 0) { ## noflush -如果你暂时不希望刷入comlog,加上noflush。这一般会用在打印循环中: +如果你暂时不希望刷到屏幕,加上noflush。这一般会用在打印循环中: ```c++ LOG(TRACE) << "Items:" << noflush; @@ -158,7 +118,7 @@ for (iterator it = items.begin(); it != items.end(); ++it) { LOG(TRACE); ``` -前两次TRACE日志都没有刷到comlog,而是还记录在thread-local缓冲中,第三次TRACE日志则把缓冲都刷入了comlog。如果items里面有三个元素,不加noflush的打印结果可能是这样的: +前两次TRACE日志都没有刷到屏幕,而是还记录在thread-local缓冲中,第三次TRACE日志则把缓冲都刷入了屏幕。如果items里面有三个元素,不加noflush的打印结果可能是这样的: ``` TRACE: ... Items: @@ -173,7 +133,7 @@ TRACE: ... item3 TRACE: ... Items: item1 item2 item3 ``` -r34694前noflush和调用处的pthread绑定,如果在noflush后发送了RPC(可能跨越pthread),那么日志输出可能不符合预期。r34694后noflush支持bthread,可以实现类似于UB的pushnotice的效果,即检索线程一路打印都暂不刷出(加上noflush),直到最后检索结束时再一次性刷出。注意,如果检索过程是异步的,就不应该使用noflush,因为异步显然会跨越bthread,使noflush仍然失效。 +noflush支持bthread,可以实现类似于UB的pushnotice的效果,即检索线程一路打印都暂不刷出(加上noflush),直到最后检索结束时再一次性刷出。注意,如果检索过程是异步的,就不应该使用noflush,因为异步显然会跨越bthread,使noflush仍然失效。 ## LOG_IF @@ -299,7 +259,7 @@ CHECK(x > y); // Check failed: x > y. ## LogSink -streaming log通过logging::SetLogSink修改日志刷入的目标,默认是屏幕。用户可以继承LogSink,实现自己的日志打印逻辑。我们默认提供了两个LogSink实现: +streaming log通过logging::SetLogSink修改日志刷入的目标,默认是屏幕。用户可以继承LogSink,实现自己的日志打印逻辑。我们默认提供了个LogSink实现: ### StringSink @@ -316,9 +276,3 @@ TEST_F(StreamingLogTest, log_at) { ::logging::SetLogSink(old_sink); } ``` - -### ComlogSink - -定义在butil/comlog_sink.h中,把日志打印入comlog,主要用于线上系统,用法见[SYNOPSIS](#SYNOPSIS)一段。 - -使用ComlogSink的streaming log可以和com_writelog, ul_writelog混用。你并不需要把程序中所有日志都换成streaming log。 diff --git a/docs/en/http_client.md b/docs/en/http_client.md new file mode 100644 index 0000000000..9abeda967e --- /dev/null +++ b/docs/en/http_client.md @@ -0,0 +1,231 @@ +Examples for Http Client: [example/http_c++](https://github.com/brpc/brpc/blob/master/example/http_c++/http_client.cpp) + +# Create Channel + +In order to use`brpc::Channel` to access the HTTP service, `ChannelOptions.protocol` must be specified as `PROTOCOL_HTTP`. + +After setting the HTTP protocol, the first parameter of `Channel::Init` can be any valid URL. *Note*: We only use the host and port part inside the URL here in order to save the user from additional parsing work. Other parts of the URL in `Channel::Init` will be discarded. + +```c++ +brpc::ChannelOptions options; +options.protocol = brpc::PROTOCOL_HTTP; +if (channel.Init("www.baidu.com" /*any url*/, &options) != 0) { + LOG(ERROR) << "Fail to initialize channel"; + return -1; +} +``` + +http channel also support BNS address. + +# GET + +```c++ +brpc::Controller cntl; +cntl.http_request().uri() = "www.baidu.com/index.html"; // Request URL +channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/); +``` + +HTTP has nothing to do with protobuf, so every parameters of `CallMethod` are NULL except `Controller` and `done`, which can be used to issue RPC asynchronously. + +`cntl.response_attachment ()` is the response body whose type is `butil :: IOBuf`. Note that converting `IOBuf` to `std :: string` using `to_string()` needs to allocate memory and copy all the content. As a result, if performance comes first, you should use `IOBuf` directly rather than continuous memory. + +# POST + +The default HTTP Method is GET. You can set the method to POST if needed, and you should append the POST data into `request_attachment()`, which ([butil::IOBuf](https://github.com/brpc/brpc/blob/master/src/butil/iobuf.h)) supports `std :: string` or `char *` + +```c++ +brpc::Controller cntl; +cntl.http_request().uri() = "..."; // Request URL +cntl.http_request().set_method(brpc::HTTP_METHOD_POST); +cntl.request_attachment().append("{\"message\":\"hello world!\"}"); +channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/); +``` + +If you need a lot print, we suggest using `butil::IOBufBuilder`, which has the same interface as `std::ostringstream`. It's much simpler and more efficient to print lots of objects using `butil::IOBufBuilder`. + +```c++ +brpc::Controller cntl; +cntl.http_request().uri() = "..."; // Request URL +cntl.http_request().set_method(brpc::HTTP_METHOD_POST); +butil::IOBufBuilder os; +os << "A lot of printing" << printable_objects << ...; +os.move_to(cntl.request_attachment()); +channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/); +``` + +# URL + +Below is the normal form of an URL: + +``` +// URI scheme : http://en.wikipedia.org/wiki/URI_scheme +// +// foo://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose +// \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/ +// | | | | | | | | +// | userinfo host port | | query fragment +// | \________________________________/\_____________|____|/ \__/ \__/ +// schema | | | | | | +// authority | | | | | +// path | | interpretable as keys +// | | +// \_______________________________________________|____|/ \____/ \_____/ +// | | | | | +// hierarchical part | | interpretable as values +// | | +// interpretable as filename | +// | +// | +// interpretable as extension +``` + +Here's the question, why to pass URL parameter twice (via `set_uri`) instead of using the URL inside `Channel::Init()` ? + +For most simple cases, it's a repeat work. But in complex scenes, they are very different in: + +- Access multiple servers under a BNS node. At this time `Channel::Init` accepts the BNS node name, the value of `set_uri()` is the whole URL including Host (such as `www.foo.com/index.html?name=value`). As a result, all servers under BNS will see `Host: www.foo.com`. `set_uri()` also takes URL with the path only, such as `/index.html?name=value`. RPC framework will automatically fill the `Host` header using of the target server's ip and port. For example, http server at 10.46.188.39: 8989 will see `Host: 10.46.188.39: 8989`. +- Access the target server via http proxy. At this point `Channel::Init` takes the address of the proxy server, while `set_uri()` takes the URL of the target server. + +# Basic Usage + +We use `http request` as example (which is the same to `http response`). Here's some basic operations: + +Access an HTTP header named `Foo` + +```c++ +const std::string* value = cntl->http_request().GetHeader("Foo"); // NULL when not exist +``` + +Set an HTTP header named `Foo` + +```c++ +cntl->http_request().SetHeader("Foo", "value"); +``` + +Access a query named `Foo` + +```c++ +const std::string* value = cntl->http_request().uri().GetQuery("Foo"); // NULL when not exist +``` + +Set a query named `Foo` + +```c++ +cntl->http_request().uri().SetQuery("Foo", "value"); +``` + +Set HTTP method + +```c++ +cntl->http_request().set_method(brpc::HTTP_METHOD_POST); +``` + +Set the URL + +```c++ +cntl->http_request().uri() = "http://www.baidu.com"; +``` + +Set the `content-type` + +```c++ +cntl->http_request().set_content_type("text/plain"); +``` + +Access HTTP body + +```c++ +butil::IOBuf& buf = cntl->request_attachment(); +std::string str = cntl->request_attachment().to_string(); // trigger copy underlying +``` + +Set HTTP body + +```c++ +cntl->request_attachment().append("...."); +butil::IOBufBuilder os; os << "...."; +os.move_to(cntl->request_attachment()); +``` + +Notes on http header: + +- The field_name of the header is case-insensitive according to [standard](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2). The framework supports that while leaving the case unchanged. +- If we have multiple headers with the same field_name, according to [standard](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2), values will be merged together separating by comma (,). Users should figure out how to use this value according to own needs. +- Queries are separated by "&", while key and value are partitioned by "=". Value may be omitted. For example, `key1=value1&key2&key3=value3` is a valid query string, and the value for `key2` is an empty string. + +# Debug for HTTP client + +Turn on [-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose) so that the framework will print each request and response in stderr. Note that this should only be used for test and debug rather than online cases. + +# Error Handle for HTTP + +When server returns a non-2xx HTTP status code, the HTTP request is considered to be failed and sets the corresponding ErrorCode: + +- All errors are unified as `EHTTP`. If you find `cntl->ErrorCode()` as `EHTTP`, you can check `cntl-> http_response().status_code()` to get a more specific HTTP error. In the meanwhile, HTTP body will be placed inside `cntl->response_attachment()`, you can check for error body such as html or json there. + +# Compress Request Body + +Call `Controller::set_request_compress_type(brpc::COMPRESS_TYPE_GZIP)` and then the framework will use gzip to compress HTTP body and set `Content-Encoding` to gzip. + +# Decompress Response Body + +For generality, brpc will not decompress response body automatically. You can do it yourself as the code won't be complicate: + +```c++ +#include +... +const std::string* encoding = cntl->http_response().GetHeader("Content-Encoding"); +if (encoding != NULL && *encoding == "gzip") { + butil::IOBuf uncompressed; + if (!brpc::policy::GzipDecompress(cntl->response_attachment(), &uncompressed)) { + LOG(ERROR) << "Fail to un-gzip response body"; + return; + } + cntl->response_attachment().swap(uncompressed); +} +// Now cntl->response_attachment() contains the decompressed data +``` + +# Continuous Download + +When downloading a large file, normally the client needs to wait until the whole file has been loaded into its memory to finish this RPC. In order to leverage the problem of memory growth and RPC resourses, in brpc the client can end its RPC first and then continuously read the rest of the file. Note that it's not HTTP chunked mode as brpc always supports for parsing chunked mode body. This is the solution to allow user the deal with super large body. + +Basic usage: + +1. Implement ProgressiveReader: + + ```c++ + #include + ... + class ProgressiveReader { + public: + // Called when one part was read. + // Error returned is treated as *permenant* and the socket where the + // data was read will be closed. + // A temporary error may be handled by blocking this function, which + // may block the HTTP parsing on the socket. + virtual butil::Status OnReadOnePart(const void* data, size_t length) = 0; + + // Called when there's nothing to read anymore. The `status' is a hint for + // why this method is called. + // - status.ok(): the message is complete and successfully consumed. + // - otherwise: socket was broken or OnReadOnePart() failed. + // This method will be called once and only once. No other methods will + // be called after. User can release the memory of this object inside. + virtual void OnEndOfMessage(const butil::Status& status) = 0; + }; + ``` + + `OnReadOnePart` is called each time data is read. `OnEndOfMessage` is called each time data has finished or connection has broken. Please refer to comments before implementing. + +2. Set `cntl.response_will_be_read_progressively();` before RPC so that brpc knows to end RPC after reading the header part. + +3. Call `cntl.ReadProgressiveAttachmentBy(new MyProgressiveReader);` after RPC so that you can use your own implemented object `MyProgressiveReader` . You may delete this object inside `OnEndOfMessage`. + +# Continuous Upload + +Currently the POST data should be intact so that we do not support large POST body. + +# Access Server with Authentication + +Generate `auth_data` according to the server's authentication method and then set it into header `Authorization`. This is the same as using curl to add option `-H "Authorization : "`. \ No newline at end of file diff --git a/docs/en/iobuf.md b/docs/en/iobuf.md new file mode 100644 index 0000000000..1992c1c421 --- /dev/null +++ b/docs/en/iobuf.md @@ -0,0 +1,97 @@ +brpc uses [butil::IOBuf](https://github.com/brpc/brpc/blob/master/src/butil/iobuf.h) as data structure for attachment storage and HTTP body. It is a non-contiguous zero copy buffer, which has been proved in other projects as excellent performance. The interface of `IOBuf` is similar to `std::string`, but not the same. + +If you used the `BufHandle` in Kylin before, you should notice the difference in convenience of `IOBuf`: the former hardly had any encapsulation, leaving the internal structure directly in front of the user. The user must carefully handle the reference count, which is very error prone, leading to lots of bugs. + +# What IOBuf can: + +- Default constructor doesn't involve copying. +- Explicit copy doesn't change source IOBuf. Only copy the management structure of IOBuf instead of the data. +- Append another IOBuf without copy. +- Append string involves copy. +- Read from/Write into fd. +- Convert to protobuf and vice versa. +- IOBufBuilder可以把IOBuf当std::ostream用。 + +# What IOBuf can't: + +- Used as general storage structure. IOBuf should not keep a long life cycle to prevent multiple memory blocks (8K each) being locked by one IOBuf object. + +# Slice + +Slice 16 bytes from IOBuf: + +```c++ +source_buf.cut(&heading_iobuf, 16); // cut all bytes of source_buf when its length < 16 +``` + +Remove 16 bytes: + +```c++ +source_buf.pop_front(16); // Empty source_buf when its length < 16 +``` + +# Concatenate + +Append to another IOBuf: + +```c++ +buf.append(another_buf); // no data copy +``` + +Append std::string + +```c++ +buf.append(str); // copy data of str into buf +``` + +# Parse + +Parse protobuf from IOBuf + +```c++ +IOBufAsZeroCopyInputStream wrapper(&iobuf); +pb_message.ParseFromZeroCopyStream(&wrapper); +``` + +Parse IOBuf as user-defined structure + +```c++ +IOBufAsZeroCopyInputStream wrapper(&iobuf); +CodedInputStream coded_stream(&wrapper); +coded_stream.ReadLittleEndian32(&value); +... +``` + +# Serialize + +Serialize protobuf into IOBuf + +```c++ +IOBufAsZeroCopyOutputStream wrapper(&iobuf); +pb_message.SerializeToZeroCopyStream(&wrapper); +``` + +Append printable data into IOBuf + +```c++ +IOBufBuilder os; +os << "anything can be sent to std::ostream"; +os.buf(); // IOBuf +``` + +# Print + +```c++ +std::cout << iobuf; +std::string str = iobuf.to_string(); +``` + +# Performance + +IOBuf has excellent performance in general aspects: + +| Action | Throughput | QPS | +| ---------------------------------------- | ----------- | ------- | +| Read from file -> Slice 12+16 bytes -> Copy -> Merge into another buffer ->Write to /dev/null | 240.423MB/s | 8586535 | +| Read from file -> Slice 12+128 bytes -> Copy-> Merge into another buffer ->Write to /dev/null | 790.022MB/s | 5643014 | +| Read from file -> Slice 12+1024 bytes -> Copy-> Merge into another buffer ->Write to /dev/null | 1519.99MB/s | 1467171 | \ No newline at end of file diff --git a/docs/en/streaming_log.md b/docs/en/streaming_log.md new file mode 100644 index 0000000000..0412118df7 --- /dev/null +++ b/docs/en/streaming_log.md @@ -0,0 +1,283 @@ +# Name + +streaming_log - Print log to std::ostreams + +# SYNOPSIS + +```c++ +#include + +LOG(FATAL) << "Fatal error occurred! contexts=" << ...; +LOG(WARNING) << "Unusual thing happened ..." << ...; +LOG(TRACE) << "Something just took place..." << ...; +LOG(TRACE) << "Items:" << noflush; +LOG_IF(NOTICE, n > 10) << "This log will only be printed when n > 10"; +PLOG(FATAL) << "Fail to call function setting errno"; +VLOG(1) << "verbose log tier 1"; +CHECK_GT(1, 2) << "1 can't be greater than 2"; + +LOG_EVERY_SECOND(INFO) << "High-frequent logs"; +LOG_EVERY_N(ERROR, 10) << "High-frequent logs"; +LOG_FIRST_N(INFO, 20) << "Logs that prints for at most 20 times"; +LOG_ONCE(WARNING) << "Logs that only prints once"; +``` + +# DESCRIPTION + +Streaming log is the best choice for printing complex objects or template objects. As most objects are complicate, user needs to convert all the fields to string first in order to use `printf` with `%s`. However it's very inconvenient (can't append numbers) and needs lots of temporary memory (caused by string). The solution in C++ is to send the log as a stream to the `std::ostream` object. For example, in order to print object A, we need to implement the following interface: + +```c++ +std::ostream& operator<<(std::ostream& os, const A& a); +``` + +The signature of the function means to print object `a` to `os` and then return `os`. The return value of `os` enables us to combine binary operator `<<` (left-combine). As a result, `os << a << b << c;` means `operator<<(operator<<(operator<<(os, a), b), c);`. Apparently `operator<<` needs a returning reference to complete this process, which is also called chaining. In languages that don't support operator overloading, you will see a more tedious form, such as `os.print(a).print(b).print(c)`. + +You should also use chaining in your own implementation of `operator<<`. In fact, printing a complex object is like DFS a tree: Call `operator<<` on each child node, and then each child node invokes the function on the grandchild node, and so forth. For example, object A has two member variables: B and C. Printing A becomes the process of putting B and C ostream: + +```c++ +struct A { + B b; + C c; +}; +std::ostream& operator<<(std::ostream& os, const A& a) { + return os << "A{b=" << a.b << ", c=" << a.c << "}"; +} +``` + +Data structure of B and C along with the print function: + +```c++ +struct B { + int value; +}; +std::ostream& operator<<(std::ostream& os, const B& b) { + return os << "B{value=" << b.value << "}"; +} + +struct C { + string name; +}; +std::ostream& operator<<(std::ostream& os, const C& c) { + return os << "C{name=" << c.name << "}"; +} +``` + +Finally the result of printing object A is: + +``` +A{b=B{value=10}, c=C{name=tom}} +``` + +This way we don't need to allocate temporary memory since objects are directly passed into the ostream object. Of course, the memory management of ostream itself is another topic. + +OK, now we connect the whole printing process by ostream. The most common ostream objects are `std::cout` and `std::cerr`, so objects implement the above function can be directly sent to `std::cout` and `std::cerr`. In other words, if a log stream also inherits ostream, then these objects can be written into log. Streaming log is such a log stream that inherits `std::ostream` to send the object into the log. In the current implementation, the logs are recorded in a thread-local buffer, which will be flushed into screen or ` logging::LogSink` after a complete log record. Of course, the implementation is thread safe. + +## LOG + +If you have ever used glog before, you should find it easy to start. The log macro is the same as glog. For example, to print a FATAL log (Note that there is no `std::endl`): + +```c++ +LOG(FATAL) << "Fatal error occurred! contexts=" << ...; +LOG(WARNING) << "Unusual thing happened ..." << ...; +LOG(TRACE) << "Something just took place..." << ...; +``` + +The log level of streaming log in accordance with glog: + +| streaming log | glog | Use Cases | +| ------------- | -------------------- | ---------------------------------------- | +| FATAL | FATAL (coredump) | Fatal error. Since most fatal log inside baidu is not fatal actually, it won't trigger coredump directly as glog, unless you turn on [-crash_on_fatal_log](http://brpc.baidu.com:8765/flags/crash_on_fatal_log) | +| ERROR | ERROR | Non-fatal error. | +| WARNING | WARNING | Unusual branches | +| NOTICE | - | Generally you should not use NOTICE as it's intended for important business logs. Make sure to check with other developers. glog doesn't have NOTICE. | +| INFO, TRACE | INFO | Important side effects such as open/close some resources. | +| VLOG(n) | INFO | Detailed log that support multiple layers. | +| DEBUG | INFOVLOG(1) (NDEBUG) | Just for compatibility. Print logs only when `NDEBUG` is not defined. See DLOG/DPLOG/DVLOG for more reference. | + +## PLOG + +The difference of PLOG and LOG is that it will append error information at the end of log. It's kind of like `%m` in `printf`. Under POSIX environment, the error code is `errno`。 + +```c++ +int fd = open("foo.conf", O_RDONLY); // foo.conf does not exist, errno was set to ENOENT +if (fd < 0) { + PLOG(FATAL) << "Fail to open foo.conf"; // "Fail to open foo.conf: No such file or directory" + return -1; +} +``` + +## noflush + +If you don't want to flush the log at once, append `noflush`. It's commonly used inside a loop: + +```c++ +LOG(TRACE) << "Items:" << noflush; +for (iterator it = items.begin(); it != items.end(); ++it) { + LOG(TRACE) << ' ' << *it << noflush; +} +LOG(TRACE); +``` + +The first two LOG(TRACE) doesn't flush the log to the screen. They are recorded inside the thread-local buffer. The third LOG(TRACE) flush all logs into the screen. If there are 3 elements inside items and we don't append `noflush`, the result would be: + +``` +TRACE: ... Items: +TRACE: ... item1 +TRACE: ... item2 +TRACE: ... item3 +``` + +After we add `noflush`: + +``` +TRACE: ... Items: item1 item2 item3 +``` + +The `noflush` feature also support bthread so that we can push lots of logs from the server's bthreads without actually print them (using `noflush`), and flush the whole log at the end of RPC. Note that you should not use `noflush` when implementing an asynchronous method since it will change the underlying bthread, leaving `noflush` out of function. + +## LOG_IF + +`LOG_IF(log_level, condition)` prints only when condition is true. It's the same as `if (condition) { LOG() << ...; }` with shorter code: + +```c++ +LOG_IF(NOTICE, n > 10) << "This log will only be printed when n > 10"; +``` + +## XXX_EVERY_SECOND + +XXX represents for LOG, LOG_IF, PLOG, SYSLOG, VLOG, DLOG, and so on. These logging macros print log at most once per second. You can use these to check running status inside hotspot area. The first call to this macro prints the log immediately, and costs additional 30ns (caused by gettimeofday) compared to normal LOG. + +```c++ +LOG_EVERY_SECOND(INFO) << "High-frequent logs"; +``` + +## XXX_EVERY_N + +XXX represents for LOG, LOG_IF, PLOG, SYSLOG, VLOG, DLOG, and so on. These logging macros print log every N times. You can use these to check running status inside hotspot area. The first call to this macro prints the log immediately, and costs an additional atomic operation (relaxed order) compared to normal LOG. This macro is thread safe which means counting from multiple threads is also accurate while glog is not. + +```c++ +LOG_EVERY_N(ERROR, 10) << "High-frequent logs"; +``` + +## XXX_FIRST_N + +XXX represents for LOG, LOG_IF, PLOG, SYSLOG, VLOG, DLOG, and so on. These logging macros print log at most N times. It costs an additional atomic operation (relaxed order) compared to normal LOG before N, and zero cost after. + +```c++ +LOG_FIRST_N(ERROR, 20) << "Logs that prints for at most 20 times"; +``` + +## XXX_ONCE + +XX represents for LOG, LOG_IF, PLOG, SYSLOG, VLOG, DLOG, and so on. These logging macros print log at most once. It's the same as `XXX_FIRST_N(..., 1)` + +```c++ +LOG_ONCE(ERROR) << "Logs that only prints once"; +``` + +## VLOG + +VLOG(verbose_level) is detail log that support multiple layers. It uses 2 gflags: *--verbose* and *--verbose_module* to control the logging layer you want (Note that glog uses *--v* and *--vmodule*). The log will be printed only when `--verbose` >= `verbose_level`: + +```c++ +VLOG(0) << "verbose log tier 0"; +VLOG(1) << "verbose log tier 1"; +VLOG(2) << "verbose log tier 2"; +``` + +When `--verbose=1`, the first 2 log will be printed while the last won't. Module means a file or file path without the extension name, and value of `--verbose_module` will overwrite `--verbose`. For example: + +```bash +--verbose=1 --verbose_module="channel=2,server=3" # print VLOG of those with verbose value: + # channel.cpp <= 2 + # server.cpp <= 3 + # other files <= 1 +--verbose=1 --verbose_module="src/brpc/channel=2,server=3" + # For files with same names, add paths +``` + +You can set `--verbose` and `--verbose_module` through `google::SetCommandLineOption` dynamically. + +VLOG has another form VLOG2, which allows user to specify virtual path: + +```c++ +// public/foo/bar.cpp +VLOG2("a/b/c", 2) << "being filtered by a/b/c rather than public/foo/bar"; +``` + +> VLOG and VLOG2 also have corresponding VLOG_IF and VLOG2_IF. + +## DLOG + +All log macros have debug versions, starting with D, such as DLOG, DVLOG. When NDEBUG is defined, these logs will not be printed. + +**Do not put important side effects inside the log streams beginning with D.** + +*No printing* means that even the parameters are not evaluated. If your parameters have side effects, they won't happend when NDEBUG is defined. For example, `DLOG(FATAL) << foo();` where foo is a function or it changes a dictionary, anyway, it's essential. However, it won't be evaluated when NDEBUG is defined. + +## CHECK + +Another import variation of logging is `CHECK(expression)`. When expression evaluates to false, it will print a fatal log. It's kind of like `ASSERT` in gtest, and has other form such as CHECK_EQ, CHECK_GT, and so on. When check fails, the message after will be printed. + +```c++ +CHECK_LT(1, 2) << "This is definitely true, this log will never be seen"; +CHECK_GT(1, 2) << "1 can't be greater than 2"; +``` + +Run the above code you should see a fatal log and the calling stack: + +``` +FATAL: ... Check failed: 1 > 2 (1 vs 2). 1 can't be greater than 2 +#0 0x000000afaa23 butil::debug::StackTrace::StackTrace() +#1 0x000000c29fec logging::LogStream::FlushWithoutReset() +#2 0x000000c2b8e6 logging::LogStream::Flush() +#3 0x000000c2bd63 logging::DestroyLogStream() +#4 0x000000c2a52d logging::LogMessage::~LogMessage() +#5 0x000000a716b2 (anonymous namespace)::StreamingLogTest_check_Test::TestBody() +#6 0x000000d16d04 testing::internal::HandleSehExceptionsInMethodIfSupported<>() +#7 0x000000d19e96 testing::internal::HandleExceptionsInMethodIfSupported<>() +#8 0x000000d08cd4 testing::Test::Run() +#9 0x000000d08dfe testing::TestInfo::Run() +#10 0x000000d08ec4 testing::TestCase::Run() +#11 0x000000d123c7 testing::internal::UnitTestImpl::RunAllTests() +#12 0x000000d16d94 testing::internal::HandleSehExceptionsInMethodIfSupported<>() +``` + +The second column of the callstack is the address of the code segment. You can use `addr2line` to check the corresponding file and line: + +``` +$ addr2line -e ./test_base 0x000000a716b2 +/home/gejun/latest_baidu_rpc/public/common/test/test_streaming_log.cpp:223 +``` + +You **should** use `CHECK_XX` for arithmetic condition so that you can see more detailed information when check failed. + +```c++ +int x = 1; +int y = 2; +CHECK_GT(x, y); // Check failed: x > y (1 vs 2). +CHECK(x > y); // Check failed: x > y. +``` + +Like DLOG, you should NOT include important side effects inside DCHECK. + +## LogSink + +The default destination of streaming log is the screen. You can change it through `logging::SetLogSink`. Users can inherit LogSink and implement their own output logic. We provide an internal LogSink as an example: + +### StringSink + +Inherit both LogSink and string. Store log content inside string and mainly aim for unit test. The following case shows a classic usage of StringSink: + +```c++ +TEST_F(StreamingLogTest, log_at) { + ::logging::StringSink log_str; + ::logging::LogSink* old_sink = ::logging::SetLogSink(&log_str); + LOG_AT(FATAL, "specified_file.cc", 12345) << "file/line is specified"; + // the file:line part should be using the argument given by us. + ASSERT_NE(std::string::npos, log_str.find("specified_file.cc:12345")); + // restore the old sink. + ::logging::SetLogSink(old_sink); +} +``` + From c9039fd42b8dcc26bb63ad52f74636aebd7e0cd0 Mon Sep 17 00:00:00 2001 From: gejun Date: Mon, 18 Sep 2017 20:39:59 +0800 Subject: [PATCH 08/11] Complete translation of client.md --- docs/cn/client.md | 77 +++++------ docs/en/client.md | 341 ++++++++++++++++++++++------------------------ 2 files changed, 194 insertions(+), 224 deletions(-) diff --git a/docs/cn/client.md b/docs/cn/client.md index 19bf091458..eba227df02 100644 --- a/docs/cn/client.md +++ b/docs/cn/client.md @@ -530,13 +530,13 @@ brpc支持以下连接方式: - 连接池:每次RPC前取用空闲连接,结束后归还,一个连接上最多只有一个请求,一个client对一台server可能有多条连接。http 1.1和各类使用nshead的协议都是这个方式。 - 单连接:进程内所有client与一台server最多只有一个连接,一个连接上可能同时有多个请求,回复返回顺序和请求顺序不需要一致,这是baidu_std,hulu_pbrpc,sofa_pbrpc协议的默认选项。 -| | 短连接 | 连接池 | 单连接 | -| ---------- | ---------------------------------------- | --------------------- | ------------------- | -| 长连接 | 否 | 是 | 是 | -| server端连接数 | qps*latency (原理见[little's law](https://en.wikipedia.org/wiki/Little%27s_law)) | qps*latency | 1 | -| 极限qps | 差,且受限于单机端口数 | 中等 | 高 | -| latency | 1.5RTT(connect) + 1RTT + 处理时间 | 1RTT + 处理时间 | 1RTT + 处理时间 | -| cpu占用 | 高, 每次都要tcp connect | 中等, 每个请求都要一次sys write | 低, 合并写出在大流量时减少cpu占用 | +| | 短连接 | 连接池 | 单连接 | +| ------------------- | ---------------------------------------- | --------------------- | ------------------- | +| 长连接 | 否 | 是 | 是 | +| server端连接数(单client) | qps*latency (原理见[little's law](https://en.wikipedia.org/wiki/Little%27s_law)) | qps*latency | 1 | +| 极限qps | 差,且受限于单机端口数 | 中等 | 高 | +| latency | 1.5RTT(connect) + 1RTT + 处理时间 | 1RTT + 处理时间 | 1RTT + 处理时间 | +| cpu占用 | 高, 每次都要tcp connect | 中等, 每个请求都要一次sys write | 低, 合并写出在大流量时减少cpu占用 | 框架会为协议选择默认的连接方式,用户**一般不用修改**。若需要,把ChannelOptions.connection_type设为: @@ -590,7 +590,7 @@ brpc支持[Streaming RPC](streaming_rpc.md),这是一种应用层的连接, ## log_id -通过set_log_id()可设置log_id。这个id会被送到服务器端,一般会被打在日志里,从而把一次检索经过的所有服务串联起来。不同产品线可能有不同的叫法。一些产品线有字符串格式的“s值”,内容也是64位的16进制数,可以转成整型后再设入log_id。 +通过set_log_id()可设置64位整型log_id。这个id会和请求一起被送到服务器端,一般会被打在日志里,从而把一次检索经过的所有服务串联起来。字符串格式的需要转化为64位整形才能设入log_id。 ## 附件 @@ -598,23 +598,8 @@ baidu_std和hulu_pbrpc协议支持附件,这段数据由用户自定义,不 在http协议中,附件对应[message body](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html),比如要POST的数据就设置在request_attachment()中。 -## giano认证 -``` - -// Create a baas::CredentialGenerator using Giano's API -baas::CredentialGenerator generator = CREATE_MOCK_PERSONAL_GENERATOR( - "mock_user", "mock_roles", "mock_group", baas::sdk::BAAS_OK); - -// Create a brpc::policy::GianoAuthenticator using the generator we just created -// and then pass it into brpc::ChannelOptions -brpc::policy::GianoAuthenticator auth(&generator, NULL); -brpc::ChannelOptions option; -option.auth = &auth; -``` -首先通过调用Giano API生成验证器baas::CredentialGenerator,具体可参看[Giano快速上手手册.pdf](http://wiki.baidu.com/download/attachments/37774685/Giano%E5%BF%A -B%E9%80%9F%E4%B8%8A%E6%89%8B%E6%89%8B%E5%86%8C.pdf?version=1&modificationDate=1421990746000&api=v2)。然后按照如上代码一步步将其设置到brpc::ChannelOptions里去。 - -当client设置认证后,任何一个新连接建立后都必须首先发送一段验证信息(通过Giano认证器生成),才能发送后续请求。认证成功后,该连接上的后续请求不会再带有验证消息。 +## 认证 +TODO: Describe how authentication methods are extended. ## 重置 @@ -624,7 +609,11 @@ B%E9%80%9F%E4%B8%8A%E6%89%8B%E6%89%8B%E5%86%8C.pdf?version=1&modificationDate=14 ## 压缩 -set_request_compress_type()设置request的压缩方式,默认不压缩。注意:附件不会被压缩。HTTP body的压缩方法见[client压缩request body](http_client#压缩request-body)。 +set_request_compress_type()设置request的压缩方式,默认不压缩。 + +注意:附件不会被压缩。 + +HTTP body的压缩方法见[client压缩request body](http_client#压缩request-body)。 支持的压缩方法有: @@ -670,13 +659,13 @@ set_request_compress_type()设置request的压缩方式,默认不压缩。注 ### Q: brpc能用unix domain socket吗 -不能。因为同机socket并不走网络,相比domain socket性能只会略微下降,替换为domain socket意义不大。以后可能会扩展支持。 +不能。同机TCP socket并不走网络,相比unix domain socket性能只会略微下降。一些不能用TCP socket的特殊场景可能会需要,以后可能会扩展支持。 -### Q: Fail to connect to xx.xx.xx.xx:xxxx, Connection refused是什么意思 +### Q: Fail to connect to xx.xx.xx.xx:xxxx, Connection refused 一般是对端server没打开端口(很可能挂了)。 -### Q: 经常遇到Connection timedout(不在一个机房) +### Q: 经常遇到至另一个机房的Connection timedout ![img](../images/connection_timedout.png) @@ -700,33 +689,29 @@ struct ChannelOptions { }; ``` -注意连接超时不是RPC超时,RPC超时打印的日志是"Reached timeout=..."。 +注意: 连接超时不是RPC超时,RPC超时打印的日志是"Reached timeout=..."。 ### Q: 为什么同步方式是好的,异步就crash了 -重点检查Controller,Response和done的生命周期。在异步访问中,RPC调用结束并不意味着RPC整个过程结束,而是要在done被调用后才会结束。所以这些对象不应在调用RPC后就释放,而是要在done里面释放。所以你一般不能把这些对象分配在栈上,而应该使用NewCallback等方式分配在堆上。详见[异步访问](client.md#异步访问)。 - -### Q: 我怎么确认server处理了我的请求 - -不一定能。当response返回且成功时,我们确认这个过程一定成功了。当response返回且失败时,我们确认这个过程一定失败了。但当response没有返回时,它可能失败,也可能成功。如果我们选择重试,那一个成功的过程也可能会被再执行一次。所以一般来说RPC服务都应当考虑[幂等](http://en.wikipedia.org/wiki/Idempotence)问题,否则重试可能会导致多次叠加副作用而产生意向不到的结果。比如以读为主的检索服务大都没有副作用而天然幂等,无需特殊处理。而像写也很多的存储服务则要在设计时就加入版本号或序列号之类的机制以拒绝已经发生的过程,保证幂等。 +重点检查Controller,Response和done的生命周期。在异步访问中,RPC调用结束并不意味着RPC整个过程结束,而是在进入done->Run()时才会结束。所以这些对象不应在调用RPC后就释放,而是要在done->Run()里释放。你一般不能把这些对象分配在栈上,而应该分配在堆上。详见[异步访问](client.md#异步访问)。 -### Q: BNS中机器列表已经配置了,但是RPC报"Fail to select server, No data available"错误 +### Q: 怎么确保请求只被处理一次 -使用get_instance_by_service -s your_bns_name 来检查一下所有机器的status状态, 只有status为0的机器才能被client访问. +这不是RPC层面的事情。当response返回且成功时,我们确认这个过程一定成功了。当response返回且失败时,我们确认这个过程一定失败了。但当response没有返回时,它可能失败,也可能成功。如果我们选择重试,那一个成功的过程也可能会被再执行一次。一般来说带副作用的RPC服务都应当考虑[幂等](http://en.wikipedia.org/wiki/Idempotence)问题,否则重试可能会导致多次叠加副作用而产生意向不到的结果。只有读的检索服务大都没有副作用而天然幂等,无需特殊处理。而带写的存储服务则要在设计时就加入版本号或序列号之类的机制以拒绝已经发生的过程,保证幂等。 -### Q: Invalid address=`bns://group.user-persona.dumi.nj03'是什么意思 +### Q: Invalid address=`bns://group.user-persona.dumi.nj03' ``` FATAL 04-07 20:00:03 7778 src/brpc/channel.cpp:123] Invalid address=`bns://group.user-persona.dumi.nj03'. You should use Init(naming_service_name, load_balancer_name, options) to access multiple servers. ``` -访问bns要使用三个参数的Init,它第二个参数是load_balancer_name,而你这里用的是两个参数的Init,框架当你是访问单点,就会报这个错。 +访问名字服务要使用三个参数的Init,其中第二个参数是load_balancer_name,而这里用的是两个参数的Init,框架认为是访问单点,就会报这个错。 -### Q: 两个产品线都使用protobuf,为什么不能互相访问 +### Q: 两端都用protobuf,为什么不能互相访问 -协议 !=protobuf。protobuf负责打包,协议负责定字段。打包格式相同不意味着字段可以互通。协议中可能会包含多个protobuf包,以及额外的长度、校验码、magic number等等。协议的互通是通过在RPC框架内转化为统一的编程接口完成的,而不是在protobuf层面。从广义上来说,protobuf也可以作为打包框架使用,生成其他序列化格式的包,像[idl<=>protobuf](mcpack2pb.md)就是通过protobuf生成了解析idl的代码。 +**协议 !=protobuf**。protobuf负责一个包的序列化,协议中的一个消息可能会包含多个protobuf包,以及额外的长度、校验码、magic number等等。打包格式相同不意味着协议可以互通。在brpc中写一份代码就能服务多协议的能力是通过把不同协议的数据转化为统一的编程接口完成的,而不是在protobuf层面。 ### Q: 为什么C++ client/server 能够互相通信, 和其他语言的client/server 通信会报序列化失败的错误 -检查一下C++ 版本是否开启了压缩 (Controller::set_compress_type), 目前 python/JAVA版的rpc框架还没有实现压缩,互相返回会出现问题。 +检查一下C++ 版本是否开启了压缩 (Controller::set_compress_type), 目前其他语言的rpc框架还没有实现压缩,互相返回会出现问题。 # 附:Client端基本流程 @@ -737,13 +722,13 @@ FATAL 04-07 20:00:03 7778 src/brpc/channel.cpp:123] Invalid address=`bns://group 1. 创建一个[bthread_id](https://github.com/brpc/brpc/blob/master/src/bthread/id.h)作为本次RPC的correlation_id。 2. 根据Channel的创建方式,从进程级的[SocketMap](https://github.com/brpc/brpc/blob/master/src/brpc/socket_map.h)中或从[LoadBalancer](https://github.com/brpc/brpc/blob/master/src/brpc/load_balancer.h)中选择一台下游server作为本次RPC发送的目的地。 3. 根据连接方式(单连接、连接池、短连接),选择一个[Socket](https://github.com/brpc/brpc/blob/master/src/brpc/socket.h)。 -4. 如果开启验证且当前Socket没有被验证过时,第一个请求进入验证分支,其余请求会阻塞直到第一个包含认证信息的请求写入Socket。这是因为server端只对第一个请求进行验证。 +4. 如果开启验证且当前Socket没有被验证过时,第一个请求进入验证分支,其余请求会阻塞直到第一个包含认证信息的请求写入Socket。server端只对第一个请求进行验证。 5. 根据Channel的协议,选择对应的序列化函数把request序列化至[IOBuf](https://github.com/brpc/brpc/blob/master/src/butil/iobuf.h)。 -6. 如果配置了超时,设置定时器。从这个点开始要避免使用Controller对象,因为在设定定时器后->有可能触发超时机制->调用到用户的异步回调->用户在回调中析构Controller。 +6. 如果配置了超时,设置定时器。从这个点开始要避免使用Controller对象,因为在设定定时器后随时可能触发超时->调用到用户的超时回调->用户在回调中析构Controller。 7. 发送准备阶段结束,若上述任何步骤出错,会调用Channel::HandleSendFailed。 8. 将之前序列化好的IOBuf写出到Socket上,同时传入回调Channel::HandleSocketFailed,当连接断开、写失败等错误发生时会调用此回调。 -9. 如果是同步发送,Join correlation_id;如果是异步则至此client端返回。 +9. 如果是同步发送,Join correlation_id;否则至此CallMethod结束。 10. 网络上发消息+收消息。 11. 收到response后,提取出其中的correlation_id,在O(1)时间内找到对应的Controller。这个过程中不需要查找全局哈希表,有良好的多核扩展性。 12. 根据协议格式反序列化response。 -13. 调用Controller::OnRPCReturned,其中会根据错误码判断是否需要重试。如果是异步发送,调用用户回调。最后摧毁correlation_id唤醒Join着的线程。 +13. 调用Controller::OnRPCReturned,可能会根据错误码判断是否需要重试,或让RPC结束。如果是异步发送,调用用户回调。最后摧毁correlation_id唤醒Join着的线程。 diff --git a/docs/en/client.md b/docs/en/client.md index fdf1ca804e..36a2b057c2 100644 --- a/docs/en/client.md +++ b/docs/en/client.md @@ -11,7 +11,7 @@ - No class named brpc::Client. # Channel -Client-side sends requests. It's called [Channel](https://github.com/brpc/brpc/blob/master/src/brpc/channel.h) rather than "Client" in brpc. A channel represents a communication line to one server or multiple servers, which can be used for calling services. +Client-side sends requests. It's called [Channel](https://github.com/brpc/brpc/blob/master/src/brpc/channel.h) rather than "Client" in brpc. A channel represents a communication line to one server or multiple servers, which can be used for calling services. A Channel can be **shared by all threads** in the process. Yon don't need to create separate Channels for each thread, and you don't need to synchronize Channel.CallMethod with lock. However creation and destroying of Channel is **not** thread-safe, make sure the channel is initialized and destroyed only by one thread. @@ -47,7 +47,7 @@ Valid "server_addr_and_port": - www.foo.com:8765 - localhost:9000 -Invalid "server_addr_and_port": +Invalid "server_addr_and_port": - 127.0.0.1:90000 # too large port - 10.39.2.300:8000 # invalid IP @@ -58,7 +58,7 @@ int Init(const char* naming_service_url, const char* load_balancer_name, const ChannelOptions* options); ``` -Channels created by above Init() get server list from the NamingService specified by `naming_service_url` periodically or driven-by-events, and send request to one server chosen from the list according to the algorithm specified by `load_balancer_name` . +Channels created by above Init() get server list from the NamingService specified by `naming_service_url` periodically or driven-by-events, and send request to one server chosen from the list according to the algorithm specified by `load_balancer_name` . You **should not** create such channels ad-hocly each time before a RPC, because creation and destroying of such channels relate to many resources, say NamingService needs to be accessed once at creation otherwise server candidates are unknown. On the other hand, channels are able to be shared by multiple threads safely and has no need to be created frequently. @@ -66,7 +66,7 @@ If `load_balancer_name` is NULL or empty, this Init() is just the one for connec ## Naming Service -Naming service maps a name to a modifiable list of servers. It's positioned as follows at client-side: +Naming service maps a name to a modifiable list of servers. It's positioned as follows at client-side: ![img](../images/ns.png) @@ -78,7 +78,7 @@ General form of `naming_service_url` is "**protocol://service_name**". BNS is the most common naming service inside Baidu. In "bns://rdev.matrix.all", "bns" is protocol and "rdev.matrix.all" is service-name. A related gflag is -ns_access_interval: ![img](../images/ns_access_interval.png) -If the list in BNS is non-empty, but Channel says "no servers", the status bit of the machine in BNS is probably non-zero, which means the machine is unavailable and as a correspondence not added as server candidates of the Channel. Status bits can be checked by: +If the list in BNS is non-empty, but Channel says "no servers", the status bit of the machine in BNS is probably non-zero, which means the machine is unavailable and as a correspondence not added as server candidates of the Channel. Status bits can be checked by: `get_instance_by_service [bns_node_name] -s` @@ -100,7 +100,7 @@ Users can filter servers got from the NamingService before pushing to LoadBalanc ![img](../images/ns_filter.jpg) -Interface of the filter: +Interface of the filter: ```c++ // naming_service_filter.h class NamingServiceFilter { @@ -109,7 +109,7 @@ public: // Return false to filter it out virtual bool Accept(const ServerNode& server) const = 0; }; - + // naming_service.h struct ServerNode { butil::EndPoint addr; @@ -127,7 +127,7 @@ public: return server.tag == "main"; } }; - + int main() { ... MyNamingServiceFilter my_filter; @@ -144,11 +144,11 @@ When there're more than one server to access, we need to divide the traffic. The ![img](../images/lb.png) -The ideal algorithm is to make every request being processed in-time, and crash of any server makes minimal impact. However clients are not able to know delays or congestions happened at servers in realtime, and load balancing algorithms should be light-weight generally, users need to choose proper algorithms for their use cases. Algorithms provided by brpc (specified by `load_balancer_name`): +The ideal algorithm is to make every request being processed in-time, and crash of any server makes minimal impact. However clients are not able to know delays or congestions happened at servers in realtime, and load balancing algorithms should be light-weight generally, users need to choose proper algorithms for their use cases. Algorithms provided by brpc (specified by `load_balancer_name`): ### rr -which is round robin. Always choose next server inside the list, next of the last server is the first one. No other settings. For example there're 3 servers: a,b,c, brpc will send requests to a, b, c, a, b, c, … and so on. Note that presumption of using this algorithm is the machine specs, network latencies, server loads are similar. +which is round robin. Always choose next server inside the list, next of the last server is the first one. No other settings. For example there're 3 servers: a,b,c, brpc will send requests to a, b, c, a, b, c, … and so on. Note that presumption of using this algorithm is the machine specs, network latencies, server loads are similar. ### random @@ -164,11 +164,11 @@ which is consistent hashing. Adding or removing servers does not make destinatio Need to set Controller.set_request_code() before RPC otherwise the RPC will fail. request_code is often a 32-bit hash code of "key part" of the request, and the hashing algorithm does not need to be same with the one used by load balancer. Say `c_murmurhash` can use md5 to compute request_code of the request as well. -[src/brpc/policy/hasher.h](https://github.com/brpc/brpc/blob/master/src/brpc/policy/hasher.h) includes common hash functions. If `std::string key` stands for key part of the request, controller.set_request_code(brpc::policy::MurmurHash32(key.data(), key.size())) sets request_code correctly. +[src/brpc/policy/hasher.h](https://github.com/brpc/brpc/blob/master/src/brpc/policy/hasher.h) includes common hash functions. If `std::string key` stands for key part of the request, controller.set_request_code(brpc::policy::MurmurHash32(key.data(), key.size())) sets request_code correctly. Do distinguish "key" and "attributes" of the request. Don't compute request_code by full content of the request just for quick. Minor change in attributes may result in totally different hash code and change destination dramatically. Another cause is padding, for example: `struct Foo { int32_t a; int64_t b; }` has a 4-byte undefined gap between `a` and `b` on 64-bit machines, result of `hash(&foo, sizeof(foo))` is undefined. Fields need to be packed or serialized before hashing. -Check out [Consistent Hashing](consistent_hashing.md) for more details. +Check out [Consistent Hashing](consistent_hashing.md) for more details. ## Health checking @@ -176,13 +176,13 @@ Servers whose connections are lost are isolated temporarily to prevent them from | Name | Value | Description | Defined At | | ------------------------- | ----- | ---------------------------------------- | ----------------------- | -| health_check_interval (R) | 3 | seconds between consecutive health-checkings | src/brpc/socket_map.cpp | +| health_check_interval (R) | 3 | seconds between consecutive health-checkings | src/brpc/socket_map.cpp | Once a server is connected, it resumes as a server candidate inside LoadBalancer. If a server is removed from NamingService during health-checking, brpc removes it from health-checking as well. # Launch RPC -Generally, we don't use Channel.CallMethod directly, instead we call XXX_Stub generated by protobuf, which feels more like a "method call". The stub has few member fields, being suitable(and recommended) to be put on stack instead of new(). Surely the stub can be saved and re-used as well. Channel.CallMethod and stub are both **thread-safe** and accessible by multiple threads simultaneously. For example: +Generally, we don't use Channel.CallMethod directly, instead we call XXX_Stub generated by protobuf, which feels more like a "method call". The stub has few member fields, being suitable(and recommended) to be put on stack instead of new(). Surely the stub can be saved and re-used as well. Channel.CallMethod and stub are both **thread-safe** and accessible by multiple threads simultaneously. For example: ```c++ XXX_Stub stub(&channel); stub.some_method(controller, request, response, done); @@ -191,11 +191,11 @@ Or even: ```c++ XXX_Stub(&channel).some_method(controller, request, response, done); ``` -A exception is http client, which is not related to protobuf much. Call CallMethod directly to make a http call, setting all parameters to NULL except for `Controller` and `done`, check [Access HTTP](http_client.md) for details. +A exception is http client, which is not related to protobuf much. Call CallMethod directly to make a http call, setting all parameters to NULL except for `Controller` and `done`, check [Access HTTP](http_client.md) for details. ## Synchronous call -CallMethod blocks until response from server is received or error occurred (including timedout). +CallMethod blocks until response from server is received or error occurred (including timedout). response/controller in synchronous call will not be used by brpc again after CallMethod, they can be put on stack safely. Note: if request/response has many fields and being large on size, they'd better be allocated on heap. ```c++ @@ -203,51 +203,51 @@ MyRequest request; MyResponse response; brpc::Controller cntl; XXX_Stub stub(&channel); - + request.set_foo(...); cntl.set_timeout_ms(...); stub.some_method(&cntl, &request, &response, NULL); if (cntl->Failed()) { - // RPC failed. fields in response are undefined, don't use. + // RPC failed. fields in response are undefined, don't use. } else { - // RPC succeeded, response has what we want. + // RPC succeeded, response has what we want. } ``` ## Asynchronous call -Pass a callback `done` to CallMethod, which resumes after sending request, rather than completion of RPC. When the response from server is received or error occurred(including timedout), done->Run() is called. Post-processing code of the RPC should be put in done->Run() instead of after CallMethod. +Pass a callback `done` to CallMethod, which resumes after sending request, rather than completion of RPC. When the response from server is received or error occurred(including timedout), done->Run() is called. Post-processing code of the RPC should be put in done->Run() instead of after CallMethod. Because end of CallMethod does not mean completion of RPC, response/controller may still be used by brpc or done->Run(). Generally they should be allocated on heap and deleted in done->Run(). If they're deleted too early, done->Run() may access invalid memory. -You can new these objects individually and create done by [NewCallback](#use-newcallback), or make response/controller be member of done and [new them together](#Inherit-google::protobuf::Closure). Former one is recommended. +You can new these objects individually and create done by [NewCallback](#use-newcallback), or make response/controller be member of done and [new them together](#Inherit-google::protobuf::Closure). Former one is recommended. -**Request and Channel can be destroyed immediately after asynchronous CallMethod**, which is different from response/controller. Note that "immediately" means destruction of request/Channel can happen **after** CallMethod, not during CallMethod. Deleting a Channel just being used by another thread results in undefined behavior (crash at best). +**Request and Channel can be destroyed immediately after asynchronous CallMethod**, which is different from response/controller. Note that "immediately" means destruction of request/Channel can happen **after** CallMethod, not during CallMethod. Deleting a Channel just being used by another thread results in undefined behavior (crash at best). ### Use NewCallback ```c++ static void OnRPCDone(MyResponse* response, brpc::Controller* cntl) { - // unique_ptr helps us to delete response/cntl automatically. unique_ptr in gcc 3.4 is an emulated version. + // unique_ptr helps us to delete response/cntl automatically. unique_ptr in gcc 3.4 is an emulated version. std::unique_ptr response_guard(response); std::unique_ptr cntl_guard(cntl); if (cntl->Failed()) { - // RPC failed. fields in response are undefined, don't use. + // RPC failed. fields in response are undefined, don't use. } else { // RPC succeeded, response has what we want. Continue the post-processing. } - // Closure created by NewCallback deletes itself at the end of Run. + // Closure created by NewCallback deletes itself at the end of Run. } - + MyResponse* response = new MyResponse; brpc::Controller* cntl = new brpc::Controller; MyService_Stub stub(&channel); - + MyRequest request; // you don't have to new request, even in an asynchronous call. request.set_foo(...); cntl->set_timeout_ms(...); stub.some_method(cntl, &request, response, google::protobuf::NewCallback(OnRPCDone, response, cntl)); ``` -Since protobuf 3 changes NewCallback to private, brpc puts NewCallback in [src/brpc/callback.h](https://github.com/brpc/brpc/blob/master/src/brpc/callback.h) after r32035 (and adds more overloads). If your program has compilation issues with NewCallback, replace google::protobuf::NewCallback with brpc::NewCallback. +Since protobuf 3 changes NewCallback to private, brpc puts NewCallback in [src/brpc/callback.h](https://github.com/brpc/brpc/blob/master/src/brpc/callback.h) after r32035 (and adds more overloads). If your program has compilation issues with NewCallback, replace google::protobuf::NewCallback with brpc::NewCallback. ### Inherit google::protobuf::Closure @@ -258,21 +258,21 @@ public: void Run() { // unique_ptr helps us to delete response/cntl automatically. unique_ptr in gcc 3.4 is an emulated version. std::unique_ptr self_guard(this); - + if (cntl->Failed()) { // RPC failed. fields in response are undefined, don't use. } else { // RPC succeeded, response has what we want. Continue the post-processing. } } - + MyResponse response; brpc::Controller cntl; } - + OnRPCDone* done = new OnRPCDone; MyService_Stub stub(&channel); - + MyRequest request; // you don't have to new request, even in an asynchronous call. request.set_foo(...); done->cntl.set_timeout_ms(...); @@ -301,17 +301,17 @@ stub.method2(controller2, request2, response2, done2); brpc::Join(cid1); brpc::Join(cid2); ``` -Call `Controller.call_id()` to get an id **before launching RPC**, join the id after the RPC. +Call `Controller.call_id()` to get an id **before launching RPC**, join the id after the RPC. -Join() blocks until completion of RPC **and end of done->Run()**, properties of Join: +Join() blocks until completion of RPC **and end of done->Run()**, properties of Join: - If the RPC is complete, Join() returns immediately. -- Multiple threads can Join() one id, all of them will be woken up. -- Synchronous RPC can be Join()-ed in another thread, although we rarely do this. +- Multiple threads can Join() one id, all of them will be woken up. +- Synchronous RPC can be Join()-ed in another thread, although we rarely do this. -Join() was called JoinResponse() before, if you meet deprecated issues during compilation, rename to Join(). +Join() was called JoinResponse() before, if you meet deprecated issues during compilation, rename to Join(). -Calling `Join(controller->call_id())` after completion of RPC is **wrong**, do save call_id before RPC, otherwise the controller may be deleted by done at any time. The Join in following code is **wrong**. +Calling `Join(controller->call_id())` after completion of RPC is **wrong**, do save call_id before RPC, otherwise the controller may be deleted by done at any time. The Join in following code is **wrong**. ```c++ static void on_rpc_done(Controller* controller, MyResponse* response) { @@ -319,7 +319,7 @@ static void on_rpc_done(Controller* controller, MyResponse* response) { delete controller; delete response; } - + Controller* controller1 = new Controller; Controller* controller2 = new Controller; MyResponse* response1 = new MyResponse; @@ -334,7 +334,7 @@ brpc::Join(controller2->call_id()); // WRONG, controller2 may be deleted by on ## Semi-synchronous -Join can be used for implementing "Semi-synchronous" call: blocks until multiple asynchronous calls to complete. Since the callsite blocks until completion of all RPC, controller/response can be put on stack safely. +Join can be used for implementing "Semi-synchronous" call: blocks until multiple asynchronous calls to complete. Since the callsite blocks until completion of all RPC, controller/response can be put on stack safely. ```c++ brpc::Controller cntl1; brpc::Controller cntl2; @@ -347,30 +347,30 @@ stub2.method2(&cntl2, &request2, &response2, brpc::DoNothing()); brpc::Join(cntl1.call_id()); brpc::Join(cntl2.call_id()); ``` -brpc::DoNothing() gets a closure doing nothing, specifically for semi-synchronous calls. Its lifetime is managed by brpc. +brpc::DoNothing() gets a closure doing nothing, specifically for semi-synchronous calls. Its lifetime is managed by brpc. Note that in above example, we access `controller.call_id()` after completion of RPC, which is safe right here, because DoNothing does not delete controller as in `on_rpc_done` in previous example. ## Cancel RPC -`brpc::StartCancel(call_id)` cancels corresponding RPC, call_id must be got from Controller.call_id() **before launching RPC**, race conditions may occur at any other time. +`brpc::StartCancel(call_id)` cancels corresponding RPC, call_id must be got from Controller.call_id() **before launching RPC**, race conditions may occur at any other time. -NOTE: it is `brpc::StartCancel(call_id)`, not `controller->StartCancel()`, which is forbidden and useless. The latter one is provided by protobuf by default and has serious race conditions on lifetime of controller. +NOTE: it is `brpc::StartCancel(call_id)`, not `controller->StartCancel()`, which is forbidden and useless. The latter one is provided by protobuf by default and has serious race conditions on lifetime of controller. As the name implies, RPC may not complete yet after calling StartCancel, you should not touch any field in Controller or delete any associated resources, they should be handled inside done->Run(). If you have to wait for completion of RPC in-place(not recommended), call Join(call_id). -Facts about StartCancel: +Facts about StartCancel: -- call_id can be cancelled before CallMethod, the RPC will end immediately(and done will be called). -- call_id can be cancelled in another thread. -- Cancel an already-cancelled call_id has no effect. Inference: One call_id can be cancelled by multiple threads simultaneously, but only one of them takes effect. -- Cancel here is a client-only feature, **the server-side may not cancel the operation necessarily**, server cancelation is a separate feature. +- call_id can be cancelled before CallMethod, the RPC will end immediately(and done will be called). +- call_id can be cancelled in another thread. +- Cancel an already-cancelled call_id has no effect. Inference: One call_id can be cancelled by multiple threads simultaneously, but only one of them takes effect. +- Cancel here is a client-only feature, **the server-side may not cancel the operation necessarily**, server cancelation is a separate feature. ## Get server-side address and port remote_side() tells where request was sent to, the return type is [butil::EndPoint](https://github.com/brpc/brpc/blob/master/src/butil/endpoint.h), which includes an ipv4 address and port. Calling this method before completion of RPC is undefined. -How to print: +How to print: ```c++ LOG(INFO) << "remote_side=" << cntl->remote_side(); printf("remote_side=%s\n", butil::endpoint2str(cntl->remote_side()).c_str()); @@ -379,16 +379,16 @@ printf("remote_side=%s\n", butil::endpoint2str(cntl->remote_side()).c_str()); local_side() gets address and port of the client-side sending RPC after r31384 -How to print: +How to print: ```c++ -LOG(INFO) << "local_side=" << cntl->local_side(); +LOG(INFO) << "local_side=" << cntl->local_side(); printf("local_side=%s\n", butil::endpoint2str(cntl->local_side()).c_str()); ``` ## Should brpc::Controller be reused? -Not necessary to reuse deliberately. +Not necessary to reuse deliberately. -Controller has miscellaneous fields, some of them are buffers that can be re-used by calling Reset(). +Controller has miscellaneous fields, some of them are buffers that can be re-used by calling Reset(). In most use cases, constructing a Controller(snippet1) and re-using a Controller(snippet2) perform similarily. ```c++ @@ -398,7 +398,7 @@ for (int i = 0; i < n; ++i) { ... stub.CallSomething(..., &controller); } - + // snippet2 brpc::Controller controller; for (int i = 0; i < n; ++i) { @@ -407,30 +407,30 @@ for (int i = 0; i < n; ++i) { stub.CallSomething(..., &controller); } ``` -If the Controller in snippet1 is new-ed on heap, snippet1 has extra cost of "heap allcation" and may be a little slower in some cases. +If the Controller in snippet1 is new-ed on heap, snippet1 has extra cost of "heap allcation" and may be a little slower in some cases. # Settings -Client-side settings has 3 parts: +Client-side settings has 3 parts: -- brpc::ChannelOptions: defined in [src/brpc/channel.h](https://github.com/brpc/brpc/blob/master/src/brpc/channel.h), for initializing Channel, becoming immutable once the initialization is done. -- brpc::Controller: defined in [src/brpc/controller.h](https://github.com/brpc/brpc/blob/master/src/brpc/controller.h), for overriding fields in brpc::ChannelOptions for some RPC according to contexts. -- global gflags: for tuning global behaviors, being unchanged generally. Read comments in [/flags](flags.md) before setting. +- brpc::ChannelOptions: defined in [src/brpc/channel.h](https://github.com/brpc/brpc/blob/master/src/brpc/channel.h), for initializing Channel, becoming immutable once the initialization is done. +- brpc::Controller: defined in [src/brpc/controller.h](https://github.com/brpc/brpc/blob/master/src/brpc/controller.h), for overriding fields in brpc::ChannelOptions for some RPC according to contexts. +- global gflags: for tuning global behaviors, being unchanged generally. Read comments in [/flags](flags.md) before setting. -Controller contains data and options that request may not have. server and client share the same Controller class, but they may set different fields. Read comments in Controller carefully before using. +Controller contains data and options that request may not have. server and client share the same Controller class, but they may set different fields. Read comments in Controller carefully before using. A Controller corresponds to a RPC. A Controller can be re-used by another RPC after Reset(), but a Controller can't be used by multiple RPC simultaneously, no matter the RPCs are started from one thread or not. -Properties of Controller: -1. A Controller can only have one user. Without explicit statement, methods in Controller are **not** thread-safe by default. -2. Due to the fact that Controller is not shared generally, there's no need to manage Controller by shared_ptr. If you do, something might goes wrong. -3. Controller is constructed before RPC and destructed after RPC, some common patterns: +Properties of Controller: +1. A Controller can only have one user. Without explicit statement, methods in Controller are **not** thread-safe by default. +2. Due to the fact that Controller is not shared generally, there's no need to manage Controller by shared_ptr. If you do, something might goes wrong. +3. Controller is constructed before RPC and destructed after RPC, some common patterns: - Put Controller on stack before synchronous RPC, be destructed when out of scope. Note that Controller of asynchronous RPC **must not** be put on stack, otherwise the RPC may still run when the Controller is being destructed and result in undefined behavior. - - new Controller before asynchronous RPC, delete in done. + - new Controller before asynchronous RPC, delete in done. ## Timeout -**ChannelOptions.timeout_ms** is timeout in milliseconds for all RPCs via the Channel, Controller.set_timeout_ms() overrides value for one RPC. Default value is 1 second, Maximum value is 2^31 (about 24 days), -1 means wait indefinitely for response or connection error. +**ChannelOptions.timeout_ms** is timeout in milliseconds for all RPCs via the Channel, Controller.set_timeout_ms() overrides value for one RPC. Default value is 1 second, Maximum value is 2^31 (about 24 days), -1 means wait indefinitely for response or connection error. **ChannelOptions.connect_timeout_ms** is timeout in milliseconds for connecting part of all RPC via the Channel, Default value is 1 second, and -1 means no timeout for connecting. This value is limited to be never greater than timeout_ms. Note that this timeout is different from the connection timeout in TCP, generally this timeout is smaller otherwise establishment of the connection may fail before this timeout due to timeout in TCP layer. @@ -440,7 +440,7 @@ NOTE2: error code of RPC timeout is **ERPCTIMEDOUT (1008) **, ETIMEDOUT is conne ## Retry -ChannelOptions.max_retry is maximum retrying count for all RPC via the channel, Controller.set_max_retry() overrides value for one RPC. Default value is 3. 0 means no retries. +ChannelOptions.max_retry is maximum retrying count for all RPC via the channel, Controller.set_max_retry() overrides value for one RPC. Default value is 3. 0 means no retries. Controller.retried_count() returns number of retries. @@ -448,37 +448,37 @@ Controller.has_backup_request() tells if backup_request was sent. **servers tried before are not retried by best efforts** -Conditions for retrying (AND relations): +Conditions for retrying (AND relations): - Broken connection. -- Timeout is not reached. -- Has retrying quota. Controller.set_max_retry(0) or ChannelOptions.max_retry = 0 disables retries. -- The retry makes sense. If the RPC fails due to request(EREQUEST), no retry will be done because server is very likely to reject the request again, retrying makes no sense here. +- Timeout is not reached. +- Has retrying quota. Controller.set_max_retry(0) or ChannelOptions.max_retry = 0 disables retries. +- The retry makes sense. If the RPC fails due to request(EREQUEST), no retry will be done because server is very likely to reject the request again, retrying makes no sense here. ### Broken connection If the server does not respond and connection is good, retry is not triggered. If you need to send another request after some timeout, use backup request. -How it works: If response does not return within the timeout specified by backup_request_ms, send another request, take whatever the first returned. New request will be sent to a different server that never tried before by best efforts. NOTE: If backup_request_ms is greater than timeout_ms, backup request will never be sent. backup request consumes one retry. backup request does not imply a server-side cancellation. +How it works: If response does not return within the timeout specified by backup_request_ms, send another request, take whatever the first returned. New request will be sent to a different server that never tried before by best efforts. NOTE: If backup_request_ms is greater than timeout_ms, backup request will never be sent. backup request consumes one retry. backup request does not imply a server-side cancellation. -ChannelOptions.backup_request_ms affects all RPC via the Channel, unit is milliseconds, Default value is -1(disabled), Controller.set_backup_request_ms() overrides value for one RPC. +ChannelOptions.backup_request_ms affects all RPC via the Channel, unit is milliseconds, Default value is -1(disabled), Controller.set_backup_request_ms() overrides value for one RPC. ### Timeout is not reached -RPC will be ended soon after the timeout. +RPC will be ended soon after the timeout. ### Has retrying quota -Controller.set_max_retry(0) or ChannelOptions.max_retry = 0 disables retries. +Controller.set_max_retry(0) or ChannelOptions.max_retry = 0 disables retries. ### The retry makes sense -If the RPC fails due to request(EREQUEST), no retry will be done because server is very likely to reject the request again, retrying makes no sense here. +If the RPC fails due to request(EREQUEST), no retry will be done because server is very likely to reject the request again, retrying makes no sense here. -Users can inherit [brpc::RetryPolicy](https://github.com/brpc/brpc/blob/master/src/brpc/retry_policy.h) to customize conditions of retrying. For example brpc does not retry for HTTP related errors by default. If you want to retry for HTTP_STATUS_FORBIDDEN(403) in your app, you can do as follows: +Users can inherit [brpc::RetryPolicy](https://github.com/brpc/brpc/blob/master/src/brpc/retry_policy.h) to customize conditions of retrying. For example brpc does not retry for HTTP related errors by default. If you want to retry for HTTP_STATUS_FORBIDDEN(403) in your app, you can do as follows: ```c++ #include - + class MyRetryPolicy : public brpc::RetryPolicy { public: bool DoRetry(const brpc::Controller* cntl) const { @@ -491,9 +491,9 @@ public: } }; ... - -// Assign the instance to ChannelOptions.retry_policy. -// NOTE: retry_policy must be kept valid during lifetime of Channel, and Channel does not retry_policy, so in most cases RetryPolicy should be created by singleton.. + +// Assign the instance to ChannelOptions.retry_policy. +// NOTE: retry_policy must be kept valid during lifetime of Channel, and Channel does not retry_policy, so in most cases RetryPolicy should be created by singleton.. brpc::ChannelOptions options; static MyRetryPolicy g_my_retry_policy; options.retry_policy = &g_my_retry_policy; @@ -515,41 +515,41 @@ The default protocol used by Channel is baidu_std, which is changeable by settin Supported protocols: -- PROTOCOL_BAIDU_STD or "baidu_std", which is [the standard binary protocol inside Baidu](baidu_std.md), using single connection by default. -- PROTOCOL_HULU_PBRPC or "hulu_pbrpc", which is protocol of hulu-pbrpc, using single connection by default. -- PROTOCOL_NOVA_PBRPC or "nova_pbrpc", which is protocol of Baidu ads union, using pooled connection by default. -- PROTOCOL_HTTP or "http", which is http 1.0 or 1.1, using pooled connection by default (Keep-Alive). Check out [Access HTTP service](http_client.md) for details. +- PROTOCOL_BAIDU_STD or "baidu_std", which is [the standard binary protocol inside Baidu](baidu_std.md), using single connection by default. +- PROTOCOL_HULU_PBRPC or "hulu_pbrpc", which is protocol of hulu-pbrpc, using single connection by default. +- PROTOCOL_NOVA_PBRPC or "nova_pbrpc", which is protocol of Baidu ads union, using pooled connection by default. +- PROTOCOL_HTTP or "http", which is http 1.0 or 1.1, using pooled connection by default (Keep-Alive). Check out [Access HTTP service](http_client.md) for details. - PROTOCOL_SOFA_PBRPC or "sofa_pbrpc", which is protocol of sofa-pbrpc, using single connection by default. -- PROTOCOL_PUBLIC_PBRPC or "public_pbrpc", which is protocol of public_pbrpc, using pooled connection by default. -- PROTOCOL_UBRPC_COMPACK or "ubrpc_compack", which is protocol of public/ubrpc, packing with compack, using pooled connection by default. check out [ubrpc (by protobuf)](ub_client.md) for details. A related protocol is PROTOCOL_UBRPC_MCPACK2 or ubrpc_mcpack2, packing with mcpack2. -- PROTOCOL_NSHEAD_CLIENT or "nshead_client", which is required by UBXXXRequest in baidu-rpc-ub, using pooled connection by default. Check out [Access UB](ub_client.md) for details. +- PROTOCOL_PUBLIC_PBRPC or "public_pbrpc", which is protocol of public_pbrpc, using pooled connection by default. +- PROTOCOL_UBRPC_COMPACK or "ubrpc_compack", which is protocol of public/ubrpc, packing with compack, using pooled connection by default. check out [ubrpc (by protobuf)](ub_client.md) for details. A related protocol is PROTOCOL_UBRPC_MCPACK2 or ubrpc_mcpack2, packing with mcpack2. +- PROTOCOL_NSHEAD_CLIENT or "nshead_client", which is required by UBXXXRequest in baidu-rpc-ub, using pooled connection by default. Check out [Access UB](ub_client.md) for details. - PROTOCOL_NSHEAD or "nshead", which is required by sending NsheadMessage, using pooled connection by default. Check out [nshead+blob](ub_client.md#nshead-blob) for details. -- PROTOCOL_MEMCACHE or "memcache", which is binary protocol of memcached, using **single connection** by default. Check out [access memcached](memcache_client.md) for details. -- PROTOCOL_REDIS or "redis", which is protocol of redis 1.2+ (the one supported by hiredis), using **single connection** by default. Check out [Access Redis](redis_client.md) for details. -- PROTOCOL_NSHEAD_MCPACK or "nshead_mcpack", which is as the name implies, nshead + mcpack (parsed by protobuf via mcpack2pb), using pooled connection by default. -- PROTOCOL_ESP or "esp", for accessing services with esp protocol, using pooled connection by default. +- PROTOCOL_MEMCACHE or "memcache", which is binary protocol of memcached, using **single connection** by default. Check out [access memcached](memcache_client.md) for details. +- PROTOCOL_REDIS or "redis", which is protocol of redis 1.2+ (the one supported by hiredis), using **single connection** by default. Check out [Access Redis](redis_client.md) for details. +- PROTOCOL_NSHEAD_MCPACK or "nshead_mcpack", which is as the name implies, nshead + mcpack (parsed by protobuf via mcpack2pb), using pooled connection by default. +- PROTOCOL_ESP or "esp", for accessing services with esp protocol, using pooled connection by default. ## Connection Type brpc supports following connection types: -- short connection: Established before each RPC, closed after completion. Since each RPC has to pay the overhead of establishing connection, this type is used for occasionally launched RPC, not frequently launched ones. No protocol use this type by default. Connections in http 1.0 are handled similarly as short connections. -- pooled connection: Pick an idle connection from a pool before each RPC, return after completion. One connection carries at most one request at the same time. One client may have multiple connections to one server. http and the protocols using nshead use this type by default. -- single connection: all clients in one process has at most one connection to one server, one connection may carry multiple requests at the same time. The sequence of returning responses does not need to be same as sending requests. This type is used by baidu_std, hulu_pbrpc, sofa_pbrpc by default. +- short connection: Established before each RPC, closed after completion. Since each RPC has to pay the overhead of establishing connection, this type is used for occasionally launched RPC, not frequently launched ones. No protocol use this type by default. Connections in http 1.0 are handled similarly as short connections. +- pooled connection: Pick an unused connection from a pool before each RPC, return after completion. One connection carries at most one request at the same time. One client may have multiple connections to one server. http and the protocols using nshead use this type by default. +- single connection: all clients in one process has at most one connection to one server, one connection may carry multiple requests at the same time. The sequence of received responses does not need to be same as sending requests. This type is used by baidu_std, hulu_pbrpc, sofa_pbrpc by default. -| | short connection | pooled connection | single connection | -| --------------------------- | ---------------------------------------- | --------------------------------------- | ---------------------------------------- | -| long connection | no | yes | yes | -| \#connection at server-side | qps*latency ([little's law](https://en.wikipedia.org/wiki/Little%27s_law)) | qps*latency | 1 | -| peak qps | bad, and limited by max number of ports | medium | high | -| latency | 1.5RTT(connect) + 1RTT + processing time | 1RTT + processing time | 1RTT + processing time | -| cpu usage | high, tcp connect for each RPC | medium, every request needs a sys write | low, writes can be combined to reduce overhead. | +| | short connection | pooled connection | single connection | +| ---------------------------------------- | ---------------------------------------- | --------------------------------------- | ---------------------------------------- | +| long connection | no | yes | yes | +| \#connection at server-side (from a client) | qps*latency ([little's law](https://en.wikipedia.org/wiki/Little%27s_law)) | qps*latency | 1 | +| peak qps | bad, and limited by max number of ports | medium | high | +| latency | 1.5RTT(connect) + 1RTT + processing time | 1RTT + processing time | 1RTT + processing time | +| cpu usage | high, tcp connect for each RPC | medium, every request needs a sys write | low, writes can be combined to reduce overhead. | -brpc chooses best connection type for the protocol by default, users generally have no need to change it. If you do, set ChannelOptions.connection_type to: +brpc chooses best connection type for the protocol by default, users generally have no need to change it. If you do, set ChannelOptions.connection_type to: - CONNECTION_TYPE_SINGLE or "single" : single connection -- CONNECTION_TYPE_POOLED or "pooled": pooled connection. Max number of connections to one server is limited by -max_connection_pool_size: +- CONNECTION_TYPE_POOLED or "pooled": pooled connection. Max number of connections from one client to one server is limited by -max_connection_pool_size: | Name | Value | Description | Defined At | | ---------------------------- | ----- | ---------------------------------------- | ------------------- | @@ -559,7 +559,7 @@ brpc chooses best connection type for the protocol by default, users generally h - "" (empty string) makes brpc chooses the default one. -brpc also supports [Streaming RPC](streaming_rpc.md) which is an application-level connection for transferring streaming data. +brpc also supports [Streaming RPC](streaming_rpc.md) which is an application-level connection for transferring streaming data. ## Close idle connections in pools @@ -572,7 +572,7 @@ If a connection has no read or write within the seconds specified by -idle_timeo ## Defer connection close -Multiple channels may share a connection via referential counting. When a channel releases last reference of the connection, the connection will be closed. But in some scenarios, channels are created just before sending RPC and destroyed after completion, in which case connections are probably closed and re-open again frequently, as costly as short connections. +Multiple channels may share a connection via referential counting. When a channel releases last reference of the connection, the connection will be closed. But in some scenarios, channels are created just before sending RPC and destroyed after completion, in which case connections are probably closed and re-open again frequently, as costly as short connections. One solution is to cache channels commonly used by user, which avoids frequent creation and destroying of channels. However brpc does not offer an utility for doing this right now, and it's not trivial for users to implement it correctly. @@ -582,13 +582,13 @@ Another solution is setting gflag -defer_close_second | ------------------ | ----- | ---------------------------------------- | ----------------------- | | defer_close_second | 0 | Defer close of connections for so many seconds even if the connection is not used by anyone. Close immediately for non-positive values | src/brpc/socket_map.cpp | -After setting, connection is not closed immediately after last referential count, instead it will be closed after so many seconds. If a channel references the connection again during the wait, the connection resumes to normal. No matter how frequent channels are created, this flag limits the frequency of closing connections. Side effect of the flag is that file descriptors are not closed immediately after destroying of channels, if the flag is wrongly set to be large, number of used file descriptors in the process may be large as well. +After setting, connection is not closed immediately after last referential count, instead it will be closed after so many seconds. If a channel references the connection again during the wait, the connection resumes to normal. No matter how frequent channels are created, this flag limits the frequency of closing connections. Side effect of the flag is that file descriptors are not closed immediately after destroying of channels, if the flag is wrongly set to be large, number of active file descriptors in the process may be large as well. -## 连接的缓冲区大小 +## Buffer size of connections --socket_recv_buffer_size设置所有连接的接收缓冲区大小, 默认-1(不修改) +-socket_recv_buffer_size sets receiving buffer size of all connections, -1 by default (not modified) --socket_send_buffer_size设置所有连接的发送缓冲区大小, 默认-1(不修改) +-socket_send_buffer_size sets sending buffer size of all connections, -1 by default (not modified) | Name | Value | Description | Defined At | | ----------------------- | ----- | ---------------------------------------- | ------------------- | @@ -597,49 +597,38 @@ After setting, connection is not closed immediately after last referential coun ## log_id -通过set_log_id()可设置log_id. 这个id会被送到服务器端, 一般会被打在日志里, 从而把一次检索经过的所有服务串联起来. 不同产品线可能有不同的叫法. 一些产品线有字符串格式的"s值", 内容也是64位的16进制数, 可以转成整型后再设入log_id. +set_log_id() sets a 64-bit integral log_id, which is sent to the server-side along with the request, and often printed in server logs to associate different services accessed in a session. String-type log-id must be converted to 64-bit integer before setting. -## 附件 +## Attachment -标准协议和hulu协议支持附件, 这段数据由用户自定义, 不经过protobuf的序列化. 站在client的角度, 设置在Controller::request_attachment()的附件会被server端收到, response_attachment()则包含了server端送回的附件. 附件不受压缩选项影响. +baidu_std and hulu_pbrpc supports attachment, which is set by user to bypass serialization of protobuf. As a client, the data in Controller::request_attachment() will be received by the server and response_attachment() contains attachment sent back by the server. Attachment is not compressed by brpc. -在http协议中, 附件对应[message body](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html), 比如要POST的数据就设置在request_attachment()中. +In http, attachment corresponds to [message body](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html), namely the data to post is stored in request_attachment(). -## giano认证 -``` +## Authentication +TODO: Describe how authentication methods are extended. -// Create a baas::CredentialGenerator using Giano's API -baas::CredentialGenerator generator = CREATE_MOCK_PERSONAL_GENERATOR( - "mock_user", "mock_roles", "mock_group", baas::sdk::BAAS_OK); - -// Create a brpc::policy::GianoAuthenticator using the generator we just created -// and then pass it into brpc::ChannelOptions -brpc::policy::GianoAuthenticator auth(&generator, NULL); -brpc::ChannelOptions option; -option.auth = &auth; -``` -首先通过调用Giano API生成验证器baas::CredentialGenerator, 具体可参看[Giano快速上手手册.pdf](http://wiki.baidu.com/download/attachments/37774685/Giano%E5%BF%A -B%E9%80%9F%E4%B8%8A%E6%89%8B%E6%89%8B%E5%86%8C.pdf?version=1&modificationDate=1421990746000&api=v2). 然后按照如上代码一步步将其设置到brpc::ChannelOptions里去. +## Reset -当client设置认证后, 任何一个新连接建立后都必须首先发送一段验证信息(通过Giano认证器生成), 才能发送后续请求. 认证成功后, 该连接上的后续请求不会再带有验证消息. +This method makes Controller back to the state as if it's just created. -## 重置 +Don't call Reset() during a RPC, which is undefined. -调用Reset方法可让Controller回到刚创建时的状态. +## Compression -别在RPC结束前重置Controller, 行为是未定义的. +set_request_compress_type() sets compress-type of the request, no compression by default. -## 压缩 +NOTE: Attachment is not compressed by brpc. -set_request_compress_type()设置request的压缩方式, 默认不压缩. 注意: 附件不会被压缩. HTTP body的压缩方法见[client压缩request body](http_client#压缩request-body). +Check out [compress request body](http_client#压缩request-body) to compress http body. -支持的压缩方法有: +Supported compressions: -- brpc::CompressTypeSnappy : [snanpy压缩](http://google.github.io/snappy/), 压缩和解压显著快于其他压缩方法, 但压缩率最低. -- brpc::CompressTypeGzip : [gzip压缩](http://en.wikipedia.org/wiki/Gzip), 显著慢于snappy, 但压缩率高 -- brpc::CompressTypeZlib : [zlib压缩](http://en.wikipedia.org/wiki/Zlib), 比gzip快10%~20%, 压缩率略好于gzip, 但速度仍明显慢于snappy. +- brpc::CompressTypeSnappy : [snanpy](http://google.github.io/snappy/), compression and decompression are very fast, but compression ratio is low. +- brpc::CompressTypeGzip : [gzip](http://en.wikipedia.org/wiki/Gzip), significantly slower than snappy, with a higher compression ratio. +- brpc::CompressTypeZlib : [zlib](http://en.wikipedia.org/wiki/Zlib), 10%~20% faster than gzip but still significantly slower than snappy, with slightly better compression ratio than gzip. -下表是多种压缩算法应对重复率很高的数据时的性能, 仅供参考. +Following table lists performance of different methods compressing and decompressing **data with a lot of duplications**, just for reference. | Compress method | Compress size(B) | Compress time(us) | Decompress time(us) | Compress throughput(MB/s) | Decompress throughput(MB/s) | Compress ratio | | --------------- | ---------------- | ----------------- | ------------------- | ------------------------- | --------------------------- | -------------- | @@ -656,7 +645,7 @@ set_request_compress_type()设置request的压缩方式, 默认不压缩. 注意 | Gzip | 229.7803 | 82.71903 | 135.9995 | 377.7849 | 0.54% | | | Zlib | 240.7464 | 54.44099 | 129.8046 | 574.0161 | 0.50% | | -下表是多种压缩算法应对重复率很低的数据时的性能, 仅供参考. +Following table lists performance of different methods compressing and decompressing **data with very few duplications**, just for reference. | Compress method | Compress size(B) | Compress time(us) | Decompress time(us) | Compress throughput(MB/s) | Decompress throughput(MB/s) | Compress ratio | | --------------- | ---------------- | ----------------- | ------------------- | ------------------------- | --------------------------- | -------------- | @@ -675,19 +664,19 @@ set_request_compress_type()设置request的压缩方式, 默认不压缩. 注意 # FAQ -### Q: brpc能用unix domain socket吗 +### Q: Does brpc support unix domain socket? -不能. 因为同机socket并不走网络, 相比domain socket性能只会略微下降, 替换为domain socket意义不大. 以后可能会扩展支持. +No. Local TCP sockets performs just a little slower than unix domain socket since traffic over local TCP sockets bypasses network. Some scenarios where TCP sockets can't be used may require unix domain sockets. We may consider the capability in future. -### Q: Fail to connect to xx.xx.xx.xx:xxxx, Connection refused是什么意思 +### Q: Fail to connect to xx.xx.xx.xx:xxxx, Connection refused -一般是对端server没打开端口(很可能挂了). +The remote server does not serve any more (probably crashed). -### Q: 经常遇到Connection timedout(不在一个机房) +### Q: often met Connection timedout to another IDC ![img](../images/connection_timedout.png) -这个就是连接超时了, 调大连接和RPC超时: +The TCP connection is not established within connection_timeout_ms, you have to tweak options: ```c++ struct ChannelOptions { @@ -697,7 +686,7 @@ struct ChannelOptions { // Default: 200 (milliseconds) // Maximum: 0x7fffffff (roughly 30 days) int32_t connect_timeout_ms; - + // Max duration of RPC over this Channel. -1 means wait indefinitely. // Overridable by Controller.set_timeout_ms(). // Default: 500 (milliseconds) @@ -707,50 +696,46 @@ struct ChannelOptions { }; ``` -注意连接超时不是RPC超时, RPC超时打印的日志是"Reached timeout=...". - -### Q: 为什么同步方式是好的, 异步就crash了 - -重点检查Controller, Response和done的生命周期. 在异步访问中, RPC调用结束并不意味着RPC整个过程结束, 而是要在done被调用后才会结束. 所以这些对象不应在调用RPC后就释放, 而是要在done里面释放. 所以你一般不能把这些对象分配在栈上, 而应该使用NewCallback等方式分配在堆上. 详见[异步访问](client.md#异步访问). +NOTE: Connection timeout is not RPC timeout, which is printed as "Reached timeout=...". -### Q: 我怎么确认server处理了我的请求 +### Q: synchronous call is good, asynchronous call crashes -不一定能. 当response返回且成功时, 我们确认这个过程一定成功了. 当response返回且失败时, 我们确认这个过程一定失败了. 但当response没有返回时, 它可能失败, 也可能成功. 如果我们选择重试, 那一个成功的过程也可能会被再执行一次. 所以一般来说RPC服务都应当考虑[幂等](http://en.wikipedia.org/wiki/Idempotence)问题, 否则重试可能会导致多次叠加副作用而产生意向不到的结果. 比如以读为主的检索服务大都没有副作用而天然幂等, 无需特殊处理. 而像写也很多的存储服务则要在设计时就加入版本号或序列号之类的机制以拒绝已经发生的过程, 保证幂等. +Check lifetime of Controller, Response and done. In asynchronous call, finish of CallMethod is not completion of RPC which is entering of done->Run(). So the objects should not deleted just after CallMethod, instead they should be delete in done->Run(). Generally you should allocate the objects on heap instead of putting them on stack. Check out [Asynchronous call](client.md#asynchronous-call) for details. -### Q: BNS中机器列表已经配置了,但是RPC报"Fail to select server, No data available"错误 +### Q: How to make requests be processed once and only once -使用get_instance_by_service -s your_bns_name 来检查一下所有机器的status状态, 只有status为0的机器才能被client访问. +This issue is not solved on RPC layer. When response returns and being successful, we know the RPC is processed at server-side. When response returns and being rejected, we know the RPC is not processed at server-side. But when response is not returned, server may or may not process the RPC. If we retry, same request may be processed twice at server-side. Generally RPC services with side effects must consider [idempotence](http://en.wikipedia.org/wiki/Idempotence) of the service, otherwise retries may make side effects be done more than once and result in unexpected behavior. Search services with only read often have no side effects (during a search), being idempotent natually. But storage services that need to write have to design versioning or serial-number mechanisms to reject side effects that already happen, to keep idempoent. -### Q: Invalid address=`bns://group.user-persona.dumi.nj03'是什么意思 +### Q: Invalid address=`bns://group.user-persona.dumi.nj03' ``` FATAL 04-07 20:00:03 7778 src/brpc/channel.cpp:123] Invalid address=`bns://group.user-persona.dumi.nj03'. You should use Init(naming_service_name, load_balancer_name, options) to access multiple servers. ``` -访问bns要使用三个参数的Init, 它第二个参数是load_balancer_name, 而你这里用的是两个参数的Init, 框架当你是访问单点, 就会报这个错. +Accessing servers under naming service needs the Init() with 3 parameters(the second param is `load_balancer_name`). The Init() here is with 2 parameters and treated by brpc as accessing single server, producing the error. -### Q: 两个产品线都使用protobuf, 为什么不能互相访问 +### Q: Both sides use protobuf, why can't they communicate with each other -协议 !=protobuf. protobuf负责打包, 协议负责定字段. 打包格式相同不意味着字段可以互通. 协议中可能会包含多个protobuf包, 以及额外的长度、校验码、magic number等等. 协议的互通是通过在RPC框架内转化为统一的编程接口完成的, 而不是在protobuf层面. 从广义上来说, protobuf也可以作为打包框架使用, 生成其他序列化格式的包, 像[idl<=>protobuf](mcpack2pb.md)就是通过protobuf生成了解析idl的代码. +**protocol != protobuf**. protobuf serializes one package and a message of a protocol may contain multiple packages along with extra lengths, checksums, magic numbers. The capability offered by brpc that "write code once and serve multiple protocols" is implemented by converting data from different protocols to unified API, not on protobuf layer. -### Q: 为什么C++ client/server 能够互相通信, 和其他语言的client/server 通信会报序列化失败的错误 +### Q: Why C++ client/server may fail to talk to client/server in other languages -检查一下C++ 版本是否开启了压缩 (Controller::set_compress_type), 目前 python/JAVA版的rpc框架还没有实现压缩, 互相返回会出现问题. +Check if the C++ version turns on compression (Controller::set_compress_type), Currently RPC impl. in other languages do not support compression yet. -# 附:Client端基本流程 +# PS: Workflow at Client-side ![img](../images/client_side.png) -主要步骤: - -1. 创建一个[bthread_id](https://github.com/brpc/brpc/blob/master/src/bthread/id.h)作为本次RPC的correlation_id. -2. 根据Channel的创建方式, 从进程级的[SocketMap](https://github.com/brpc/brpc/blob/master/src/brpc/socket_map.h)中或从[LoadBalancer](https://github.com/brpc/brpc/blob/master/src/brpc/load_balancer.h)中选择一台下游server作为本次RPC发送的目的地. -3. 根据连接方式(单连接、连接池、短连接), 选择一个[Socket](https://github.com/brpc/brpc/blob/master/src/brpc/socket.h). -4. 如果开启验证且当前Socket没有被验证过时, 第一个请求进入验证分支, 其余请求会阻塞直到第一个包含认证信息的请求写入Socket. 这是因为server端只对第一个请求进行验证. -5. 根据Channel的协议, 选择对应的序列化函数把request序列化至[IOBuf](https://github.com/brpc/brpc/blob/master/src/butil/iobuf.h). -6. 如果配置了超时, 设置定时器. 从这个点开始要避免使用Controller对象, 因为在设定定时器后->有可能触发超时机制->调用到用户的异步回调->用户在回调中析构Controller. -7. 发送准备阶段结束, 若上述任何步骤出错, 会调用Channel::HandleSendFailed. -8. 将之前序列化好的IOBuf写出到Socket上, 同时传入回调Channel::HandleSocketFailed, 当连接断开、写失败等错误发生时会调用此回调. -9. 如果是同步发送, Join correlation_id;如果是异步则至此client端返回. -10. 网络上发消息+收消息. -11. 收到response后, 提取出其中的correlation_id, 在O(1)时间内找到对应的Controller. 这个过程中不需要查找全局哈希表, 有良好的多核扩展性. -12. 根据协议格式反序列化response. -13. 调用Controller::OnRPCReturned, 其中会根据错误码判断是否需要重试. 如果是异步发送, 调用用户回调. 最后摧毁correlation_id唤醒Join着的线程. +Steps: + +1. Create a [bthread_id](https://github.com/brpc/brpc/blob/master/src/bthread/id.h) as correlation_id of current RPC. +2. According to how the Channel is initialized, choose a server from global [SocketMap](https://github.com/brpc/brpc/blob/master/src/brpc/socket_map.h) or [LoadBalancer](https://github.com/brpc/brpc/blob/master/src/brpc/load_balancer.h) as destination of the request. +3. Choose a [Socket](https://github.com/brpc/brpc/blob/master/src/brpc/socket.h) according to connection type (single, pooled, short) +4. If authentication is turned on and the Socket is not authenticated yet, first request enters authenticating branch, other requests block until the branch writes authenticating information into the Socket. Server-side only verifies the first request. +5. According to protocol of the Channel, choose corresponding serialization callback to serialize request into [IOBuf](https://github.com/brpc/brpc/blob/master/src/butil/iobuf.h). +6. If timeout is set, setup timer. From this point on, avoid using Controller, since the timer may be triggered at anytime and calls user's callback for timeout, which may delete Controller. +7. Sending phase is completed. If error occurs at any step, Channel::HandleSendFailed is called. +8. Write IOBuf with serialized data into the Socket and add Channel::HandleSocketFailed into id_wait_list of the Socket. The callback will be called when the write is failed or connection is broken before completion of RPC. +9. In synchronous call, Join correlation_id; otherwise CallMethod() returns. +10. Send/receive messages to/from network. +11. After receiving response, get the correlation_id inside, find out associated Controller within O(1) time. The lookup does not need to lock a global hashmap, and scales well. +12. Parse response according to the protocol +13. Call Controller::OnRPCReturned, which may retry errorous RPC, or complete the RPC. Call user's done in asynchronous call. Destroy correlation_id and wakeup joining threads. From d921345533c70f10cc1df56df678e3c4e200468f Mon Sep 17 00:00:00 2001 From: Zhangyi Chen Date: Mon, 18 Sep 2017 21:04:22 +0800 Subject: [PATCH 09/11] Remove Callback/Bind/Tuple from butil --- Makefile | 4 - src/butil/at_exit.cc | 13 +- src/butil/at_exit.h | 10 +- src/butil/barrier_closure.cc | 52 - src/butil/barrier_closure.h | 30 - src/butil/bind.h | 511 --- src/butil/bind_helpers.cc | 14 - src/butil/bind_helpers.h | 544 ---- src/butil/bind_internal.h | 2789 ----------------- src/butil/callback.h | 770 ----- src/butil/callback_forward.h | 17 - src/butil/callback_helpers.cc | 42 - src/butil/callback_helpers.h | 50 - src/butil/callback_internal.cc | 38 - src/butil/callback_internal.h | 178 -- src/butil/callback_list.h | 406 --- .../raw_scoped_refptr_mismatch_checker.h | 1 - src/butil/tuple.h | 1291 -------- test/Makefile | 7 - test/at_exit_unittest.cc | 8 - test/butil_unittest_main.cpp | 1 + test/cancellation_flag_unittest.cc | 1 - test/condition_variable_unittest.cc | 1 - test/crash_logging_unittest.cc | 1 - test/scoped_ptr_unittest.cc | 2 - test/scoped_vector_unittest.cc | 7 +- test/test_file_util_linux.cc | 2 - test/tuple_unittest.cc | 128 - test/weak_ptr_unittest.cc | 1 - 29 files changed, 11 insertions(+), 6908 deletions(-) delete mode 100644 src/butil/barrier_closure.cc delete mode 100644 src/butil/barrier_closure.h delete mode 100644 src/butil/bind.h delete mode 100644 src/butil/bind_helpers.cc delete mode 100644 src/butil/bind_helpers.h delete mode 100644 src/butil/bind_internal.h delete mode 100644 src/butil/callback.h delete mode 100644 src/butil/callback_forward.h delete mode 100644 src/butil/callback_helpers.cc delete mode 100644 src/butil/callback_helpers.h delete mode 100644 src/butil/callback_internal.cc delete mode 100644 src/butil/callback_internal.h delete mode 100644 src/butil/callback_list.h delete mode 100644 src/butil/tuple.h delete mode 100644 test/tuple_unittest.cc diff --git a/Makefile b/Makefile index 76c545250e..5b0f16f90f 100644 --- a/Makefile +++ b/Makefile @@ -41,13 +41,9 @@ BUTIL_SOURCES = \ src/butil/arena.cpp \ src/butil/at_exit.cc \ src/butil/atomicops_internals_x86_gcc.cc \ - src/butil/barrier_closure.cc \ src/butil/base64.cc \ src/butil/base_switches.cc \ src/butil/big_endian.cc \ - src/butil/bind_helpers.cc \ - src/butil/callback_helpers.cc \ - src/butil/callback_internal.cc \ src/butil/cpu.cc \ src/butil/debug/alias.cc \ src/butil/debug/asan_invalid_access.cc \ diff --git a/src/butil/at_exit.cc b/src/butil/at_exit.cc index 82eb538c7e..1c06ec1ebd 100644 --- a/src/butil/at_exit.cc +++ b/src/butil/at_exit.cc @@ -7,8 +7,6 @@ #include #include -#include "butil/bind.h" -#include "butil/callback.h" #include "butil/logging.h" namespace butil { @@ -44,18 +42,13 @@ AtExitManager::~AtExitManager() { // static void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { DCHECK(func); - RegisterTask(butil::Bind(func, param)); -} - -// static -void AtExitManager::RegisterTask(butil::Closure task) { if (!g_top_manager) { NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; return; } AutoLock lock(g_top_manager->lock_); - g_top_manager->stack_.push(task); + g_top_manager->stack_.push({func, param}); } // static @@ -68,8 +61,8 @@ void AtExitManager::ProcessCallbacksNow() { AutoLock lock(g_top_manager->lock_); while (!g_top_manager->stack_.empty()) { - butil::Closure task = g_top_manager->stack_.top(); - task.Run(); + Callback task = g_top_manager->stack_.top(); + task.func(task.param); g_top_manager->stack_.pop(); } } diff --git a/src/butil/at_exit.h b/src/butil/at_exit.h index 9e508e91b4..9d0179e2f9 100644 --- a/src/butil/at_exit.h +++ b/src/butil/at_exit.h @@ -9,7 +9,6 @@ #include "butil/base_export.h" #include "butil/basictypes.h" -#include "butil/callback.h" #include "butil/synchronization/lock.h" namespace butil { @@ -42,9 +41,6 @@ class BASE_EXPORT AtExitManager { // the callback function is void func(void*). static void RegisterCallback(AtExitCallbackType func, void* param); - // Registers the specified task to be called at exit. - static void RegisterTask(butil::Closure task); - // Calls the functions registered with RegisterCallback in LIFO order. It // is possible to register new callbacks after calling this function. static void ProcessCallbacksNow(); @@ -57,8 +53,12 @@ class BASE_EXPORT AtExitManager { explicit AtExitManager(bool shadow); private: + struct Callback { + AtExitCallbackType func; + void* param; + }; butil::Lock lock_; - std::stack stack_; + std::stack stack_; AtExitManager* next_manager_; // Stack of managers to allow shadowing. DISALLOW_COPY_AND_ASSIGN(AtExitManager); diff --git a/src/butil/barrier_closure.cc b/src/butil/barrier_closure.cc deleted file mode 100644 index 11a9c72bad..0000000000 --- a/src/butil/barrier_closure.cc +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2013 The Chromium 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 "butil/barrier_closure.h" - -#include "butil/atomic_ref_count.h" -#include "butil/bind.h" - -namespace { - -// Maintains state for a BarrierClosure. -class BarrierInfo { - public: - BarrierInfo(int num_callbacks_left, const butil::Closure& done_closure); - void Run(); - - private: - butil::AtomicRefCount num_callbacks_left_; - butil::Closure done_closure_; -}; - -BarrierInfo::BarrierInfo(int num_callbacks, const butil::Closure& done_closure) - : num_callbacks_left_(num_callbacks), - done_closure_(done_closure) { -} - -void BarrierInfo::Run() { - DCHECK(!butil::AtomicRefCountIsZero(&num_callbacks_left_)); - if (!butil::AtomicRefCountDec(&num_callbacks_left_)) { - done_closure_.Run(); - done_closure_.Reset(); - } -} - -} // namespace - -namespace butil { - -butil::Closure BarrierClosure(int num_callbacks_left, - const butil::Closure& done_closure) { - DCHECK(num_callbacks_left >= 0); - - if (num_callbacks_left == 0) - done_closure.Run(); - - return butil::Bind(&BarrierInfo::Run, - butil::Owned( - new BarrierInfo(num_callbacks_left, done_closure))); -} - -} // namespace butil diff --git a/src/butil/barrier_closure.h b/src/butil/barrier_closure.h deleted file mode 100644 index 0ff9f91f15..0000000000 --- a/src/butil/barrier_closure.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2013 The Chromium 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_BARRIER_CLOSURE_H_ -#define BASE_BARRIER_CLOSURE_H_ - -#include "butil/base_export.h" -#include "butil/callback_forward.h" - -namespace butil { - -// BarrierClosure executes |done_closure| after it has been invoked -// |num_closures| times. -// -// If |num_closures| is 0, |done_closure| is executed immediately. -// -// BarrierClosure is thread-safe - the count of remaining closures is -// maintained as a butil::AtomicRefCount. |done_closure| will be run on -// the thread that calls the final Run() on the returned closures. -// -// |done_closure| is also Reset() on the final calling thread but due to the -// refcounted nature of callbacks, it is hard to know what thread resources -// will be released on. -BASE_EXPORT butil::Closure BarrierClosure(int num_closures, - const butil::Closure& done_closure); - -} // namespace butil - -#endif // BASE_BARRIER_CLOSURE_H_ diff --git a/src/butil/bind.h b/src/butil/bind.h deleted file mode 100644 index 20bafd9a01..0000000000 --- a/src/butil/bind.h +++ /dev/null @@ -1,511 +0,0 @@ -// This file was GENERATED by command: -// pump.py bind.h.pump -// DO NOT EDIT BY HAND!!! - - -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BIND_H_ -#define BASE_BIND_H_ - -#include "butil/bind_internal.h" -#include "butil/callback_internal.h" - -// ----------------------------------------------------------------------------- -// Usage documentation -// ----------------------------------------------------------------------------- -// -// See butil/callback.h for documentation. -// -// -// ----------------------------------------------------------------------------- -// Implementation notes -// ----------------------------------------------------------------------------- -// -// If you're reading the implementation, before proceeding further, you should -// read the top comment of butil/bind_internal.h for a definition of common -// terms and concepts. -// -// RETURN TYPES -// -// Though Bind()'s result is meant to be stored in a Callback<> type, it -// cannot actually return the exact type without requiring a large amount -// of extra template specializations. The problem is that in order to -// discern the correct specialization of Callback<>, Bind would need to -// unwrap the function signature to determine the signature's arity, and -// whether or not it is a method. -// -// Each unique combination of (arity, function_type, num_prebound) where -// function_type is one of {function, method, const_method} would require -// one specialization. We eventually have to do a similar number of -// specializations anyways in the implementation (see the Invoker<>, -// classes). However, it is avoidable in Bind if we return the result -// via an indirection like we do below. -// -// TODO(ajwong): We might be able to avoid this now, but need to test. -// -// It is possible to move most of the COMPILE_ASSERT asserts into BindState<>, -// but it feels a little nicer to have the asserts here so people do not -// need to crack open bind_internal.h. On the other hand, it makes Bind() -// harder to read. - -namespace butil { - -template -butil::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void()> - ::UnboundRunType> -Bind(Functor functor) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - typedef internal::BindState BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor))); -} - -template -butil::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - typedef internal::BindState::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1)); -} - -template -butil::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2)); -} - -template -butil::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3)); -} - -template -butil::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4)); -} - -template -butil::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p5_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5)); -} - -template -butil::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5, const P6& p6) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p5_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p6_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6)); -} - -template -butil::Callback< - typename internal::BindState< - typename internal::FunctorTraits::RunnableType, - typename internal::FunctorTraits::RunType, - void(typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> - ::UnboundRunType> -Bind(Functor functor, const P1& p1, const P2& p2, const P3& p3, const P4& p4, - const P5& p5, const P6& p6, const P7& p7) { - // Typedefs for how to store and run the functor. - typedef typename internal::FunctorTraits::RunnableType RunnableType; - typedef typename internal::FunctorTraits::RunType RunType; - - // Use RunnableType::RunType instead of RunType above because our - // checks should below for bound references need to know what the actual - // functor is going to interpret the argument as. - typedef internal::FunctionTraits - BoundFunctorTraits; - - // Do not allow binding a non-const reference parameter. Non-const reference - // parameters are disallowed by the Google style guide. Also, binding a - // non-const reference parameter can make for subtle bugs because the - // invoked function will receive a reference to the stored copy of the - // argument and not the original. - COMPILE_ASSERT( - !(is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value || - is_non_const_reference::value ), - do_not_bind_functions_with_nonconst_ref); - - // For methods, we need to be careful for parameter 1. We do not require - // a scoped_refptr because BindState<> itself takes care of AddRef() for - // methods. We also disallow binding of an array as the method's target - // object. - COMPILE_ASSERT( - internal::HasIsMethodTag::value || - !internal::NeedsScopedRefptrButGetsRawPtr::value, - p1_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::HasIsMethodTag::value || - !is_array::value, - first_bound_argument_to_method_cannot_be_array); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p2_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p3_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p4_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p5_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p6_is_refcounted_type_and_needs_scoped_refptr); - COMPILE_ASSERT(!internal::NeedsScopedRefptrButGetsRawPtr::value, - p7_is_refcounted_type_and_needs_scoped_refptr); - typedef internal::BindState::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType, - typename internal::CallbackParamTraits::StorageType)> BindState; - - - return Callback( - new BindState(internal::MakeRunnable(functor), p1, p2, p3, p4, p5, p6, - p7)); -} - -} // namespace butil - -#endif // BASE_BIND_H_ diff --git a/src/butil/bind_helpers.cc b/src/butil/bind_helpers.cc deleted file mode 100644 index 487f291776..0000000000 --- a/src/butil/bind_helpers.cc +++ /dev/null @@ -1,14 +0,0 @@ -// 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. - -#include "butil/bind_helpers.h" - -#include "butil/callback.h" - -namespace butil { - -void DoNothing() { -} - -} // namespace butil diff --git a/src/butil/bind_helpers.h b/src/butil/bind_helpers.h deleted file mode 100644 index e223801da9..0000000000 --- a/src/butil/bind_helpers.h +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This defines a set of argument wrappers and related factory methods that -// can be used specify the refcounting and reference semantics of arguments -// that are bound by the Bind() function in butil/bind.h. -// -// It also defines a set of simple functions and utilities that people want -// when using Callback<> and Bind(). -// -// -// ARGUMENT BINDING WRAPPERS -// -// The wrapper functions are butil::Unretained(), butil::Owned(), butil::Passed(), -// butil::ConstRef(), and butil::IgnoreResult(). -// -// Unretained() allows Bind() to bind a non-refcounted class, and to disable -// refcounting on arguments that are refcounted objects. -// -// Owned() transfers ownership of an object to the Callback resulting from -// bind; the object will be deleted when the Callback is deleted. -// -// Passed() is for transferring movable-but-not-copyable types (eg. scoped_ptr) -// through a Callback. Logically, this signifies a destructive transfer of -// the state of the argument into the target function. Invoking -// Callback::Run() twice on a Callback that was created with a Passed() -// argument will CHECK() because the first invocation would have already -// transferred ownership to the target function. -// -// ConstRef() allows binding a constant reference to an argument rather -// than a copy. -// -// IgnoreResult() is used to adapt a function or Callback with a return type to -// one with a void return. This is most useful if you have a function with, -// say, a pesky ignorable bool return that you want to use with PostTask or -// something else that expect a Callback with a void return. -// -// EXAMPLE OF Unretained(): -// -// class Foo { -// public: -// void func() { cout << "Foo:f" << endl; } -// }; -// -// // In some function somewhere. -// Foo foo; -// Closure foo_callback = -// Bind(&Foo::func, Unretained(&foo)); -// foo_callback.Run(); // Prints "Foo:f". -// -// Without the Unretained() wrapper on |&foo|, the above call would fail -// to compile because Foo does not support the AddRef() and Release() methods. -// -// -// EXAMPLE OF Owned(): -// -// void foo(int* arg) { cout << *arg << endl } -// -// int* pn = new int(1); -// Closure foo_callback = Bind(&foo, Owned(pn)); -// -// foo_callback.Run(); // Prints "1" -// foo_callback.Run(); // Prints "1" -// *n = 2; -// foo_callback.Run(); // Prints "2" -// -// foo_callback.Reset(); // |pn| is deleted. Also will happen when -// // |foo_callback| goes out of scope. -// -// Without Owned(), someone would have to know to delete |pn| when the last -// reference to the Callback is deleted. -// -// -// EXAMPLE OF ConstRef(): -// -// void foo(int arg) { cout << arg << endl } -// -// int n = 1; -// Closure no_ref = Bind(&foo, n); -// Closure has_ref = Bind(&foo, ConstRef(n)); -// -// no_ref.Run(); // Prints "1" -// has_ref.Run(); // Prints "1" -// -// n = 2; -// no_ref.Run(); // Prints "1" -// has_ref.Run(); // Prints "2" -// -// Note that because ConstRef() takes a reference on |n|, |n| must outlive all -// its bound callbacks. -// -// -// EXAMPLE OF IgnoreResult(): -// -// int DoSomething(int arg) { cout << arg << endl; } -// -// // Assign to a Callback with a void return type. -// Callback cb = Bind(IgnoreResult(&DoSomething)); -// cb->Run(1); // Prints "1". -// -// // Prints "1" on |ml|. -// ml->PostTask(FROM_HERE, Bind(IgnoreResult(&DoSomething), 1); -// -// -// EXAMPLE OF Passed(): -// -// void TakesOwnership(scoped_ptr arg) { } -// scoped_ptr CreateFoo() { return scoped_ptr(new Foo()); } -// -// scoped_ptr f(new Foo()); -// -// // |cb| is given ownership of Foo(). |f| is now NULL. -// // You can use f.Pass() in place of &f, but it's more verbose. -// Closure cb = Bind(&TakesOwnership, Passed(&f)); -// -// // Run was never called so |cb| still owns Foo() and deletes -// // it on Reset(). -// cb.Reset(); -// -// // |cb| is given a new Foo created by CreateFoo(). -// cb = Bind(&TakesOwnership, Passed(CreateFoo())); -// -// // |arg| in TakesOwnership() is given ownership of Foo(). |cb| -// // no longer owns Foo() and, if reset, would not delete Foo(). -// cb.Run(); // Foo() is now transferred to |arg| and deleted. -// cb.Run(); // This CHECK()s since Foo() already been used once. -// -// Passed() is particularly useful with PostTask() when you are transferring -// ownership of an argument into a task, but don't necessarily know if the -// task will always be executed. This can happen if the task is cancellable -// or if it is posted to a MessageLoopProxy. -// -// -// SIMPLE FUNCTIONS AND UTILITIES. -// -// DoNothing() - Useful for creating a Closure that does nothing when called. -// DeletePointer() - Useful for creating a Closure that will delete a -// pointer when invoked. Only use this when necessary. -// In most cases MessageLoop::DeleteSoon() is a better -// fit. - -#ifndef BASE_BIND_HELPERS_H_ -#define BASE_BIND_HELPERS_H_ - -#include "butil/basictypes.h" -#include "butil/callback.h" -#include "butil/memory/weak_ptr.h" -#include "butil/type_traits.h" - -namespace butil { -namespace internal { - -// Use the Substitution Failure Is Not An Error (SFINAE) trick to inspect T -// for the existence of AddRef() and Release() functions of the correct -// signature. -// -// http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error -// http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence -// http://stackoverflow.com/questions/4358584/sfinae-approach-comparison -// http://stackoverflow.com/questions/1966362/sfinae-to-check-for-inherited-member-functions -// -// The last link in particular show the method used below. -// -// For SFINAE to work with inherited methods, we need to pull some extra tricks -// with multiple inheritance. In the more standard formulation, the overloads -// of Check would be: -// -// template -// Yes NotTheCheckWeWant(Helper<&C::TargetFunc>*); -// -// template -// No NotTheCheckWeWant(...); -// -// static const bool value = sizeof(NotTheCheckWeWant(0)) == sizeof(Yes); -// -// The problem here is that template resolution will not match -// C::TargetFunc if TargetFunc does not exist directly in C. That is, if -// TargetFunc in inherited from an ancestor, &C::TargetFunc will not match, -// |value| will be false. This formulation only checks for whether or -// not TargetFunc exist directly in the class being introspected. -// -// To get around this, we play a dirty trick with multiple inheritance. -// First, We create a class BaseMixin that declares each function that we -// want to probe for. Then we create a class Base that inherits from both T -// (the class we wish to probe) and BaseMixin. Note that the function -// signature in BaseMixin does not need to match the signature of the function -// we are probing for; thus it's easiest to just use void(void). -// -// Now, if TargetFunc exists somewhere in T, then &Base::TargetFunc has an -// ambiguous resolution between BaseMixin and T. This lets us write the -// following: -// -// template -// No GoodCheck(Helper<&C::TargetFunc>*); -// -// template -// Yes GoodCheck(...); -// -// static const bool value = sizeof(GoodCheck(0)) == sizeof(Yes); -// -// Notice here that the variadic version of GoodCheck() returns Yes here -// instead of No like the previous one. Also notice that we calculate |value| -// by specializing GoodCheck() on Base instead of T. -// -// We've reversed the roles of the variadic, and Helper overloads. -// GoodCheck(Helper<&C::TargetFunc>*), when C = Base, fails to be a valid -// substitution if T::TargetFunc exists. Thus GoodCheck(0) will resolve -// to the variadic version if T has TargetFunc. If T::TargetFunc does not -// exist, then &C::TargetFunc is not ambiguous, and the overload resolution -// will prefer GoodCheck(Helper<&C::TargetFunc>*). -// -// This method of SFINAE will correctly probe for inherited names, but it cannot -// typecheck those names. It's still a good enough sanity check though. -// -// Works on gcc-4.2, gcc-4.4, and Visual Studio 2008. -// -// TODO(ajwong): Move to ref_counted.h or type_traits.h when we've vetted -// this works well. -// -// TODO(ajwong): Make this check for Release() as well. -// See http://crbug.com/82038. -template -class SupportsAddRefAndRelease { - typedef char Yes[1]; - typedef char No[2]; - - struct BaseMixin { - void AddRef(); - }; - -// MSVC warns when you try to use Base if T has a private destructor, the -// common pattern for refcounted types. It does this even though no attempt to -// instantiate Base is made. We disable the warning for this definition. -#if defined(OS_WIN) -#pragma warning(push) -#pragma warning(disable:4624) -#endif - struct Base : public T, public BaseMixin { - }; -#if defined(OS_WIN) -#pragma warning(pop) -#endif - - template struct Helper {}; - - template - static No& Check(Helper<&C::AddRef>*); - - template - static Yes& Check(...); - - public: - static const bool value = sizeof(Check(0)) == sizeof(Yes); -}; - -// Helpers to assert that arguments of a recounted type are bound with a -// scoped_refptr. -template -struct UnsafeBindtoRefCountedArgHelper : false_type { -}; - -template -struct UnsafeBindtoRefCountedArgHelper - : integral_constant::value> { -}; - -template -struct UnsafeBindtoRefCountedArg : false_type { -}; - -template -struct UnsafeBindtoRefCountedArg - : UnsafeBindtoRefCountedArgHelper::value, T> { -}; - -template -class HasIsMethodTag { - typedef char Yes[1]; - typedef char No[2]; - - template - static Yes& Check(typename U::IsMethod*); - - template - static No& Check(...); - - public: - static const bool value = sizeof(Check(0)) == sizeof(Yes); -}; - -template -class UnretainedWrapper { - public: - explicit UnretainedWrapper(T* o) : ptr_(o) {} - T* get() const { return ptr_; } - private: - T* ptr_; -}; - -template -class ConstRefWrapper { - public: - explicit ConstRefWrapper(const T& o) : ptr_(&o) {} - const T& get() const { return *ptr_; } - private: - const T* ptr_; -}; - -template -struct IgnoreResultHelper { - explicit IgnoreResultHelper(T functor) : functor_(functor) {} - - T functor_; -}; - -template -struct IgnoreResultHelper > { - explicit IgnoreResultHelper(const Callback& functor) : functor_(functor) {} - - const Callback& functor_; -}; - -// An alternate implementation is to avoid the destructive copy, and instead -// specialize ParamTraits<> for OwnedWrapper<> to change the StorageType to -// a class that is essentially a scoped_ptr<>. -// -// The current implementation has the benefit though of leaving ParamTraits<> -// fully in callback_internal.h as well as avoiding type conversions during -// storage. -template -class OwnedWrapper { - public: - explicit OwnedWrapper(T* o) : ptr_(o) {} - ~OwnedWrapper() { delete ptr_; } - T* get() const { return ptr_; } - OwnedWrapper(const OwnedWrapper& other) { - ptr_ = other.ptr_; - other.ptr_ = NULL; - } - - private: - mutable T* ptr_; -}; - -// PassedWrapper is a copyable adapter for a scoper that ignores const. -// -// It is needed to get around the fact that Bind() takes a const reference to -// all its arguments. Because Bind() takes a const reference to avoid -// unnecessary copies, it is incompatible with movable-but-not-copyable -// types; doing a destructive "move" of the type into Bind() would violate -// the const correctness. -// -// This conundrum cannot be solved without either C++11 rvalue references or -// a O(2^n) blowup of Bind() templates to handle each combination of regular -// types and movable-but-not-copyable types. Thus we introduce a wrapper type -// that is copyable to transmit the correct type information down into -// BindState<>. Ignoring const in this type makes sense because it is only -// created when we are explicitly trying to do a destructive move. -// -// Two notes: -// 1) PassedWrapper supports any type that has a "Pass()" function. -// This is intentional. The whitelisting of which specific types we -// support is maintained by CallbackParamTraits<>. -// 2) is_valid_ is distinct from NULL because it is valid to bind a "NULL" -// scoper to a Callback and allow the Callback to execute once. -template -class PassedWrapper { - public: - explicit PassedWrapper(T scoper) : is_valid_(true), scoper_(scoper.Pass()) {} - PassedWrapper(const PassedWrapper& other) - : is_valid_(other.is_valid_), scoper_(other.scoper_.Pass()) { - } - T Pass() const { - CHECK(is_valid_); - is_valid_ = false; - return scoper_.Pass(); - } - - private: - mutable bool is_valid_; - mutable T scoper_; -}; - -// Unwrap the stored parameters for the wrappers above. -template -struct UnwrapTraits { - typedef const T& ForwardType; - static ForwardType Unwrap(const T& o) { return o; } -}; - -template -struct UnwrapTraits > { - typedef T* ForwardType; - static ForwardType Unwrap(UnretainedWrapper unretained) { - return unretained.get(); - } -}; - -template -struct UnwrapTraits > { - typedef const T& ForwardType; - static ForwardType Unwrap(ConstRefWrapper const_ref) { - return const_ref.get(); - } -}; - -template -struct UnwrapTraits > { - typedef T* ForwardType; - static ForwardType Unwrap(const scoped_refptr& o) { return o.get(); } -}; - -template -struct UnwrapTraits > { - typedef const WeakPtr& ForwardType; - static ForwardType Unwrap(const WeakPtr& o) { return o; } -}; - -template -struct UnwrapTraits > { - typedef T* ForwardType; - static ForwardType Unwrap(const OwnedWrapper& o) { - return o.get(); - } -}; - -template -struct UnwrapTraits > { - typedef T ForwardType; - static T Unwrap(PassedWrapper& o) { - return o.Pass(); - } -}; - -// Utility for handling different refcounting semantics in the Bind() -// function. -template -struct MaybeRefcount; - -template -struct MaybeRefcount { - static void AddRef(const T&) {} - static void Release(const T&) {} -}; - -template -struct MaybeRefcount { - static void AddRef(const T*) {} - static void Release(const T*) {} -}; - -template -struct MaybeRefcount { - static void AddRef(const T&) {} - static void Release(const T&) {} -}; - -template -struct MaybeRefcount { - static void AddRef(T* o) { o->AddRef(); } - static void Release(T* o) { o->Release(); } -}; - -// No need to additionally AddRef() and Release() since we are storing a -// scoped_refptr<> inside the storage object already. -template -struct MaybeRefcount > { - static void AddRef(const scoped_refptr& o) {} - static void Release(const scoped_refptr& o) {} -}; - -template -struct MaybeRefcount { - static void AddRef(const T* o) { o->AddRef(); } - static void Release(const T* o) { o->Release(); } -}; - -// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a -// method. It is used internally by Bind() to select the correct -// InvokeHelper that will no-op itself in the event the WeakPtr<> for -// the target object is invalidated. -// -// P1 should be the type of the object that will be received of the method. -template -struct IsWeakMethod : public false_type {}; - -template -struct IsWeakMethod > : public true_type {}; - -template -struct IsWeakMethod > > : public true_type {}; - -} // namespace internal - -template -static inline internal::UnretainedWrapper Unretained(T* o) { - return internal::UnretainedWrapper(o); -} - -template -static inline internal::ConstRefWrapper ConstRef(const T& o) { - return internal::ConstRefWrapper(o); -} - -template -static inline internal::OwnedWrapper Owned(T* o) { - return internal::OwnedWrapper(o); -} - -// We offer 2 syntaxes for calling Passed(). The first takes a temporary and -// is best suited for use with the return value of a function. The second -// takes a pointer to the scoper and is just syntactic sugar to avoid having -// to write Passed(scoper.Pass()). -template -static inline internal::PassedWrapper Passed(T scoper) { - return internal::PassedWrapper(scoper.Pass()); -} -template -static inline internal::PassedWrapper Passed(T* scoper) { - return internal::PassedWrapper(scoper->Pass()); -} - -template -static inline internal::IgnoreResultHelper IgnoreResult(T data) { - return internal::IgnoreResultHelper(data); -} - -template -static inline internal::IgnoreResultHelper > -IgnoreResult(const Callback& data) { - return internal::IgnoreResultHelper >(data); -} - -BASE_EXPORT void DoNothing(); - -template -void DeletePointer(T* obj) { - delete obj; -} - -} // namespace butil - -#endif // BASE_BIND_HELPERS_H_ diff --git a/src/butil/bind_internal.h b/src/butil/bind_internal.h deleted file mode 100644 index 68b5e0b429..0000000000 --- a/src/butil/bind_internal.h +++ /dev/null @@ -1,2789 +0,0 @@ -// This file was GENERATED by command: -// pump.py bind_internal.h.pump -// DO NOT EDIT BY HAND!!! - - -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_BIND_INTERNAL_H_ -#define BASE_BIND_INTERNAL_H_ - -#include "butil/bind_helpers.h" -#include "butil/callback_internal.h" -#include "butil/memory/raw_scoped_refptr_mismatch_checker.h" -#include "butil/memory/weak_ptr.h" -#include "butil/type_traits.h" -#include "butil/build_config.h" - -#if defined(OS_WIN) -#include "butil/bind_internal_win.h" -#endif - -namespace butil { -namespace internal { - -// See butil/callback.h for user documentation. -// -// -// CONCEPTS: -// Runnable -- A type (really a type class) that has a single Run() method -// and a RunType typedef that corresponds to the type of Run(). -// A Runnable can declare that it should treated like a method -// call by including a typedef named IsMethod. The value of -// this typedef is NOT inspected, only the existence. When a -// Runnable declares itself a method, Bind() will enforce special -// refcounting + WeakPtr handling semantics for the first -// parameter which is expected to be an object. -// Functor -- A copyable type representing something that should be called. -// All function pointers, Callback<>, and Runnables are functors -// even if the invocation syntax differs. -// RunType -- A function type (as opposed to function _pointer_ type) for -// a Run() function. Usually just a convenience typedef. -// (Bound)ArgsType -- A function type that is being (ab)used to store the -// types of set of arguments. The "return" type is always -// void here. We use this hack so that we do not need -// a new type name for each arity of type. (eg., -// BindState1, BindState2). This makes forward -// declarations and friending much much easier. -// -// Types: -// RunnableAdapter<> -- Wraps the various "function" pointer types into an -// object that adheres to the Runnable interface. -// There are |3*ARITY| RunnableAdapter types. -// FunctionTraits<> -- Type traits that unwrap a function signature into a -// a set of easier to use typedefs. Used mainly for -// compile time asserts. -// There are |ARITY| FunctionTraits types. -// ForceVoidReturn<> -- Helper class for translating function signatures to -// equivalent forms with a "void" return type. -// There are |ARITY| ForceVoidReturn types. -// FunctorTraits<> -- Type traits used determine the correct RunType and -// RunnableType for a Functor. This is where function -// signature adapters are applied. -// There are |ARITY| ForceVoidReturn types. -// MakeRunnable<> -- Takes a Functor and returns an object in the Runnable -// type class that represents the underlying Functor. -// There are |O(1)| MakeRunnable types. -// InvokeHelper<> -- Take a Runnable + arguments and actully invokes it. -// Handle the differing syntaxes needed for WeakPtr<> support, -// and for ignoring return values. This is separate from -// Invoker to avoid creating multiple version of Invoker<> -// which grows at O(n^2) with the arity. -// There are |k*ARITY| InvokeHelper types. -// Invoker<> -- Unwraps the curried parameters and executes the Runnable. -// There are |(ARITY^2 + ARITY)/2| Invoketypes. -// BindState<> -- Stores the curried parameters, and is the main entry point -// into the Bind() system, doing most of the type resolution. -// There are ARITY BindState types. - -// RunnableAdapter<> -// -// The RunnableAdapter<> templates provide a uniform interface for invoking -// a function pointer, method pointer, or const method pointer. The adapter -// exposes a Run() method with an appropriate signature. Using this wrapper -// allows for writing code that supports all three pointer types without -// undue repetition. Without it, a lot of code would need to be repeated 3 -// times. -// -// For method pointers and const method pointers the first argument to Run() -// is considered to be the received of the method. This is similar to STL's -// mem_fun(). -// -// This class also exposes a RunType typedef that is the function type of the -// Run() function. -// -// If and only if the wrapper contains a method or const method pointer, an -// IsMethod typedef is exposed. The existence of this typedef (NOT the value) -// marks that the wrapper should be considered a method wrapper. - -template -class RunnableAdapter; - -// Function: Arity 0. -template -class RunnableAdapter { - public: - typedef R (RunType)(); - - explicit RunnableAdapter(R(*function)()) - : function_(function) { - } - - R Run() { - return function_(); - } - - private: - R (*function_)(); -}; - -// Method: Arity 0. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)()) - : method_(method) { - } - - R Run(T* object) { - return (object->*method_)(); - } - - private: - R (T::*method_)(); -}; - -// Const Method: Arity 0. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)() const) - : method_(method) { - } - - R Run(const T* object) { - return (object->*method_)(); - } - - private: - R (T::*method_)() const; -}; - -// Function: Arity 1. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1); - - explicit RunnableAdapter(R(*function)(A1)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1) { - return function_(CallbackForward(a1)); - } - - private: - R (*function_)(A1); -}; - -// Method: Arity 1. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1) { - return (object->*method_)(CallbackForward(a1)); - } - - private: - R (T::*method_)(A1); -}; - -// Const Method: Arity 1. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1) { - return (object->*method_)(CallbackForward(a1)); - } - - private: - R (T::*method_)(A1) const; -}; - -// Function: Arity 2. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2); - - explicit RunnableAdapter(R(*function)(A1, A2)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2) { - return function_(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (*function_)(A1, A2); -}; - -// Method: Arity 2. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (T::*method_)(A1, A2); -}; - -// Const Method: Arity 2. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2)); - } - - private: - R (T::*method_)(A1, A2) const; -}; - -// Function: Arity 3. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3); - - explicit RunnableAdapter(R(*function)(A1, A2, A3)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (*function_)(A1, A2, A3); -}; - -// Method: Arity 3. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (T::*method_)(A1, A2, A3); -}; - -// Const Method: Arity 3. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } - - private: - R (T::*method_)(A1, A2, A3) const; -}; - -// Function: Arity 4. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3, A4); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (*function_)(A1, A2, A3, A4); -}; - -// Method: Arity 4. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3, A4); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (T::*method_)(A1, A2, A3, A4); -}; - -// Const Method: Arity 4. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } - - private: - R (T::*method_)(A1, A2, A3, A4) const; -}; - -// Function: Arity 5. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3, A4, A5); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (*function_)(A1, A2, A3, A4, A5); -}; - -// Method: Arity 5. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5); -}; - -// Const Method: Arity 5. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5) const; -}; - -// Function: Arity 6. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3, A4, A5, A6); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5, A6)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (*function_)(A1, A2, A3, A4, A5, A6); -}; - -// Method: Arity 6. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5, A6); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6); -}; - -// Const Method: Arity 6. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5, A6); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6) const; -}; - -// Function: Arity 7. -template -class RunnableAdapter { - public: - typedef R (RunType)(A1, A2, A3, A4, A5, A6, A7); - - explicit RunnableAdapter(R(*function)(A1, A2, A3, A4, A5, A6, A7)) - : function_(function) { - } - - R Run(typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6, - typename CallbackParamTraits::ForwardType a7) { - return function_(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } - - private: - R (*function_)(A1, A2, A3, A4, A5, A6, A7); -}; - -// Method: Arity 7. -template -class RunnableAdapter { - public: - typedef R (RunType)(T*, A1, A2, A3, A4, A5, A6, A7); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6, A7)) - : method_(method) { - } - - R Run(T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6, - typename CallbackParamTraits::ForwardType a7) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6, A7); -}; - -// Const Method: Arity 7. -template -class RunnableAdapter { - public: - typedef R (RunType)(const T*, A1, A2, A3, A4, A5, A6, A7); - typedef true_type IsMethod; - - explicit RunnableAdapter(R(T::*method)(A1, A2, A3, A4, A5, A6, A7) const) - : method_(method) { - } - - R Run(const T* object, typename CallbackParamTraits::ForwardType a1, - typename CallbackParamTraits::ForwardType a2, - typename CallbackParamTraits::ForwardType a3, - typename CallbackParamTraits::ForwardType a4, - typename CallbackParamTraits::ForwardType a5, - typename CallbackParamTraits::ForwardType a6, - typename CallbackParamTraits::ForwardType a7) { - return (object->*method_)(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } - - private: - R (T::*method_)(A1, A2, A3, A4, A5, A6, A7) const; -}; - - -// FunctionTraits<> -// -// Breaks a function signature apart into typedefs for easier introspection. -template -struct FunctionTraits; - -template -struct FunctionTraits { - typedef R ReturnType; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; - typedef A6 A6Type; -}; - -template -struct FunctionTraits { - typedef R ReturnType; - typedef A1 A1Type; - typedef A2 A2Type; - typedef A3 A3Type; - typedef A4 A4Type; - typedef A5 A5Type; - typedef A6 A6Type; - typedef A7 A7Type; -}; - - -// ForceVoidReturn<> -// -// Set of templates that support forcing the function return type to void. -template -struct ForceVoidReturn; - -template -struct ForceVoidReturn { - typedef void(RunType)(); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3, A4); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3, A4, A5); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3, A4, A5, A6); -}; - -template -struct ForceVoidReturn { - typedef void(RunType)(A1, A2, A3, A4, A5, A6, A7); -}; - - -// FunctorTraits<> -// -// See description at top of file. -template -struct FunctorTraits { - typedef RunnableAdapter RunnableType; - typedef typename RunnableType::RunType RunType; -}; - -template -struct FunctorTraits > { - typedef typename FunctorTraits::RunnableType RunnableType; - typedef typename ForceVoidReturn< - typename RunnableType::RunType>::RunType RunType; -}; - -template -struct FunctorTraits > { - typedef Callback RunnableType; - typedef typename Callback::RunType RunType; -}; - - -// MakeRunnable<> -// -// Converts a passed in functor to a RunnableType using type inference. - -template -typename FunctorTraits::RunnableType MakeRunnable(const T& t) { - return RunnableAdapter(t); -} - -template -typename FunctorTraits::RunnableType -MakeRunnable(const IgnoreResultHelper& t) { - return MakeRunnable(t.functor_); -} - -template -const typename FunctorTraits >::RunnableType& -MakeRunnable(const Callback& t) { - DCHECK(!t.is_null()); - return t; -} - - -// InvokeHelper<> -// -// There are 3 logical InvokeHelper<> specializations: normal, void-return, -// WeakCalls. -// -// The normal type just calls the underlying runnable. -// -// We need a InvokeHelper to handle void return types in order to support -// IgnoreResult(). Normally, if the Runnable's RunType had a void return, -// the template system would just accept "return functor.Run()" ignoring -// the fact that a void function is being used with return. This piece of -// sugar breaks though when the Runnable's RunType is not void. Thus, we -// need a partial specialization to change the syntax to drop the "return" -// from the invocation call. -// -// WeakCalls similarly need special syntax that is applied to the first -// argument to check if they should no-op themselves. -template -struct InvokeHelper; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable) { - return runnable.Run(); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable) { - runnable.Run(); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1) { - return runnable.Run(CallbackForward(a1)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1) { - runnable.Run(CallbackForward(a1)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get()); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2) { - runnable.Run(CallbackForward(a1), CallbackForward(a2)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5, A6 a6) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, - A6 a6) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5, A6 a6) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6)); - } -}; - -template -struct InvokeHelper { - static ReturnType MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, - A5 a5, A6 a6, A7 a7) { - return runnable.Run(CallbackForward(a1), CallbackForward(a2), - CallbackForward(a3), CallbackForward(a4), CallbackForward(a5), - CallbackForward(a6), CallbackForward(a7)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, - A6 a6, A7 a7) { - runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6), - CallbackForward(a7)); - } -}; - -template -struct InvokeHelper { - static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3, - A4 a4, A5 a5, A6 a6, A7 a7) { - if (!weak_ptr.get()) { - return; - } - runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3), - CallbackForward(a4), CallbackForward(a5), CallbackForward(a6), - CallbackForward(a7)); - } -}; - -#if !defined(_MSC_VER) - -template -struct InvokeHelper { - // WeakCalls are only supported for functions with a void return type. - // Otherwise, the function result would be undefined if the the WeakPtr<> - // is invalidated. - COMPILE_ASSERT(is_void::value, - weak_ptrs_can_only_bind_to_methods_without_return_values); -}; - -#endif - -// Invoker<> -// -// See description at the top of the file. -template -struct Invoker; - -// Arity 0 -> 0. -template -struct Invoker<0, StorageType, R()> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper - ::MakeItSo(storage->runnable_); - } -}; - -// Arity 1 -> 1. -template -struct Invoker<0, StorageType, R(X1)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1)> - ::MakeItSo(storage->runnable_, CallbackForward(x1)); - } -}; - -// Arity 1 -> 0. -template -struct Invoker<1, StorageType, R(X1)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1)); - } -}; - -// Arity 2 -> 2. -template -struct Invoker<0, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 2 -> 1. -template -struct Invoker<1, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 2 -> 0. -template -struct Invoker<2, StorageType, R(X1, X2)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2)); - } -}; - -// Arity 3 -> 3. -template -struct Invoker<0, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 2. -template -struct Invoker<1, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 1. -template -struct Invoker<2, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 3 -> 0. -template -struct Invoker<3, StorageType, R(X1, X2, X3)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3)); - } -}; - -// Arity 4 -> 4. -template -struct Invoker<0, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 3. -template -struct Invoker<1, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 2. -template -struct Invoker<2, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3, X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3, - typename CallbackParamTraits::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 1. -template -struct Invoker<3, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X4); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x4) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper::ForwardType x4)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 4 -> 0. -template -struct Invoker<4, StorageType, R(X1, X2, X3, X4)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4)); - } -}; - -// Arity 5 -> 5. -template -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 4. -template -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 3. -template -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 2. -template -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X4, X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper::ForwardType x4, - typename CallbackParamTraits::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 1. -template -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X5); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x5) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper::ForwardType x5)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 5 -> 0. -template -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5)); - } -}; - -// Arity 6 -> 6. -template -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 5. -template -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 4. -template -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 3. -template -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X4, X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 2. -template -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X5, X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper::ForwardType x5, - typename CallbackParamTraits::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 1. -template -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X6); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x6) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper::ForwardType x6)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 6 -> 0. -template -struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6)); - } -}; - -// Arity 7 -> 7. -template -struct Invoker<0, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X1, X2, X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - - return InvokeHelper::ForwardType x1, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 6. -template -struct Invoker<1, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X2, X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - return InvokeHelper::ForwardType x2, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 5. -template -struct Invoker<2, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X3, X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - return InvokeHelper::ForwardType x3, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 4. -template -struct Invoker<3, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X4, X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - return InvokeHelper::ForwardType x4, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 3. -template -struct Invoker<4, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X5, X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - return InvokeHelper::ForwardType x5, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 2. -template -struct Invoker<5, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X6, X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x6, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - return InvokeHelper::ForwardType x6, - typename CallbackParamTraits::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 1. -template -struct Invoker<6, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*, - typename CallbackParamTraits::ForwardType); - - typedef R(UnboundRunType)(X7); - - static R Run(BindStateBase* base, - typename CallbackParamTraits::ForwardType x7) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - return InvokeHelper::ForwardType x7)> - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - -// Arity 7 -> 0. -template -struct Invoker<7, StorageType, R(X1, X2, X3, X4, X5, X6, X7)> { - typedef R(RunType)(BindStateBase*); - - typedef R(UnboundRunType)(); - - static R Run(BindStateBase* base) { - StorageType* storage = static_cast(base); - - // Local references to make debugger stepping easier. If in a debugger, - // you really want to warp ahead and step through the - // InvokeHelper<>::MakeItSo() call below. - typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits; - typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits; - typedef typename StorageType::Bound3UnwrapTraits Bound3UnwrapTraits; - typedef typename StorageType::Bound4UnwrapTraits Bound4UnwrapTraits; - typedef typename StorageType::Bound5UnwrapTraits Bound5UnwrapTraits; - typedef typename StorageType::Bound6UnwrapTraits Bound6UnwrapTraits; - typedef typename StorageType::Bound7UnwrapTraits Bound7UnwrapTraits; - - typename Bound1UnwrapTraits::ForwardType x1 = - Bound1UnwrapTraits::Unwrap(storage->p1_); - typename Bound2UnwrapTraits::ForwardType x2 = - Bound2UnwrapTraits::Unwrap(storage->p2_); - typename Bound3UnwrapTraits::ForwardType x3 = - Bound3UnwrapTraits::Unwrap(storage->p3_); - typename Bound4UnwrapTraits::ForwardType x4 = - Bound4UnwrapTraits::Unwrap(storage->p4_); - typename Bound5UnwrapTraits::ForwardType x5 = - Bound5UnwrapTraits::Unwrap(storage->p5_); - typename Bound6UnwrapTraits::ForwardType x6 = - Bound6UnwrapTraits::Unwrap(storage->p6_); - typename Bound7UnwrapTraits::ForwardType x7 = - Bound7UnwrapTraits::Unwrap(storage->p7_); - return InvokeHelper - ::MakeItSo(storage->runnable_, CallbackForward(x1), - CallbackForward(x2), CallbackForward(x3), - CallbackForward(x4), CallbackForward(x5), - CallbackForward(x6), CallbackForward(x7)); - } -}; - - -// BindState<> -// -// This stores all the state passed into Bind() and is also where most -// of the template resolution magic occurs. -// -// Runnable is the functor we are binding arguments to. -// RunType is type of the Run() function that the Invoker<> should use. -// Normally, this is the same as the RunType of the Runnable, but it can -// be different if an adapter like IgnoreResult() has been used. -// -// BoundArgsType contains the storage type for all the bound arguments by -// (ab)using a function type. -template -struct BindState; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef false_type IsWeakCall; - typedef Invoker<0, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - explicit BindState(const Runnable& runnable) - : runnable_(runnable) { - } - - virtual ~BindState() { } - - RunnableType runnable_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<1, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1) - : runnable_(runnable), - p1_(p1) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<2, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2) - : runnable_(runnable), - p1_(p1), - p2_(p2) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<3, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<4, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - typedef UnwrapTraits Bound4UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<5, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - typedef UnwrapTraits Bound4UnwrapTraits; - typedef UnwrapTraits Bound5UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<6, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - typedef UnwrapTraits Bound4UnwrapTraits; - typedef UnwrapTraits Bound5UnwrapTraits; - typedef UnwrapTraits Bound6UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5, const P6& p6) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5), - p6_(p6) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; - P6 p6_; -}; - -template -struct BindState : public BindStateBase { - typedef Runnable RunnableType; - typedef IsWeakMethod::value, P1> IsWeakCall; - typedef Invoker<7, BindState, RunType> InvokerType; - typedef typename InvokerType::UnboundRunType UnboundRunType; - - // Convenience typedefs for bound argument types. - typedef UnwrapTraits Bound1UnwrapTraits; - typedef UnwrapTraits Bound2UnwrapTraits; - typedef UnwrapTraits Bound3UnwrapTraits; - typedef UnwrapTraits Bound4UnwrapTraits; - typedef UnwrapTraits Bound5UnwrapTraits; - typedef UnwrapTraits Bound6UnwrapTraits; - typedef UnwrapTraits Bound7UnwrapTraits; - - BindState(const Runnable& runnable, const P1& p1, const P2& p2, const P3& p3, - const P4& p4, const P5& p5, const P6& p6, const P7& p7) - : runnable_(runnable), - p1_(p1), - p2_(p2), - p3_(p3), - p4_(p4), - p5_(p5), - p6_(p6), - p7_(p7) { - MaybeRefcount::value, P1>::AddRef(p1_); - } - - virtual ~BindState() { MaybeRefcount::value, - P1>::Release(p1_); } - - RunnableType runnable_; - P1 p1_; - P2 p2_; - P3 p3_; - P4 p4_; - P5 p5_; - P6 p6_; - P7 p7_; -}; - -} // namespace internal -} // namespace butil - -#endif // BASE_BIND_INTERNAL_H_ diff --git a/src/butil/callback.h b/src/butil/callback.h deleted file mode 100644 index 77f42aabd2..0000000000 --- a/src/butil/callback.h +++ /dev/null @@ -1,770 +0,0 @@ -// This file was GENERATED by command: -// pump.py callback.h.pump -// DO NOT EDIT BY HAND!!! - - -// 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_CALLBACK_H_ -#define BASE_CALLBACK_H_ - -#include "butil/callback_forward.h" -#include "butil/callback_internal.h" -#include "butil/type_traits.h" - -// NOTE: Header files that do not require the full definition of Callback or -// Closure should #include "butil/callback_forward.h" instead of this file. - -// ----------------------------------------------------------------------------- -// Introduction -// ----------------------------------------------------------------------------- -// -// The templated Callback class is a generalized function object. Together -// with the Bind() function in bind.h, they provide a type-safe method for -// performing partial application of functions. -// -// Partial application (or "currying") is the process of binding a subset of -// a function's arguments to produce another function that takes fewer -// arguments. This can be used to pass around a unit of delayed execution, -// much like lexical closures are used in other languages. For example, it -// is used in Chromium code to schedule tasks on different MessageLoops. -// -// A callback with no unbound input parameters (butil::Callback) -// is called a butil::Closure. Note that this is NOT the same as what other -// languages refer to as a closure -- it does not retain a reference to its -// enclosing environment. -// -// MEMORY MANAGEMENT AND PASSING -// -// The Callback objects themselves should be passed by const-reference, and -// stored by copy. They internally store their state via a refcounted class -// and thus do not need to be deleted. -// -// The reason to pass via a const-reference is to avoid unnecessary -// AddRef/Release pairs to the internal state. -// -// -// ----------------------------------------------------------------------------- -// Quick reference for basic stuff -// ----------------------------------------------------------------------------- -// -// BINDING A BARE FUNCTION -// -// int Return5() { return 5; } -// butil::Callback func_cb = butil::Bind(&Return5); -// LOG(INFO) << func_cb.Run(); // Prints 5. -// -// BINDING A CLASS METHOD -// -// The first argument to bind is the member function to call, the second is -// the object on which to call it. -// -// class Ref : public butil::RefCountedThreadSafe { -// public: -// int Foo() { return 3; } -// void PrintBye() { LOG(INFO) << "bye."; } -// }; -// scoped_refptr ref = new Ref(); -// butil::Callback ref_cb = butil::Bind(&Ref::Foo, ref); -// LOG(INFO) << ref_cb.Run(); // Prints out 3. -// -// By default the object must support RefCounted or you will get a compiler -// error. If you're passing between threads, be sure it's -// RefCountedThreadSafe! See "Advanced binding of member functions" below if -// you don't want to use reference counting. -// -// RUNNING A CALLBACK -// -// Callbacks can be run with their "Run" method, which has the same -// signature as the template argument to the callback. -// -// void DoSomething(const butil::Callback& callback) { -// callback.Run(5, "hello"); -// } -// -// Callbacks can be run more than once (they don't get deleted or marked when -// run). However, this precludes using butil::Passed (see below). -// -// void DoSomething(const butil::Callback& callback) { -// double myresult = callback.Run(3.14159); -// myresult += callback.Run(2.71828); -// } -// -// PASSING UNBOUND INPUT PARAMETERS -// -// Unbound parameters are specified at the time a callback is Run(). They are -// specified in the Callback template type: -// -// void MyFunc(int i, const std::string& str) {} -// butil::Callback cb = butil::Bind(&MyFunc); -// cb.Run(23, "hello, world"); -// -// PASSING BOUND INPUT PARAMETERS -// -// Bound parameters are specified when you create thee callback as arguments -// to Bind(). They will be passed to the function and the Run()ner of the -// callback doesn't see those values or even know that the function it's -// calling. -// -// void MyFunc(int i, const std::string& str) {} -// butil::Callback cb = butil::Bind(&MyFunc, 23, "hello world"); -// cb.Run(); -// -// A callback with no unbound input parameters (butil::Callback) -// is called a butil::Closure. So we could have also written: -// -// butil::Closure cb = butil::Bind(&MyFunc, 23, "hello world"); -// -// When calling member functions, bound parameters just go after the object -// pointer. -// -// butil::Closure cb = butil::Bind(&MyClass::MyFunc, this, 23, "hello world"); -// -// PARTIAL BINDING OF PARAMETERS -// -// You can specify some parameters when you create the callback, and specify -// the rest when you execute the callback. -// -// void MyFunc(int i, const std::string& str) {} -// butil::Callback cb = butil::Bind(&MyFunc, 23); -// cb.Run("hello world"); -// -// When calling a function bound parameters are first, followed by unbound -// parameters. -// -// -// ----------------------------------------------------------------------------- -// Quick reference for advanced binding -// ----------------------------------------------------------------------------- -// -// BINDING A CLASS METHOD WITH WEAK POINTERS -// -// butil::Bind(&MyClass::Foo, GetWeakPtr()); -// -// The callback will not be run if the object has already been destroyed. -// DANGER: weak pointers are not threadsafe, so don't use this -// when passing between threads! -// -// BINDING A CLASS METHOD WITH MANUAL LIFETIME MANAGEMENT -// -// butil::Bind(&MyClass::Foo, butil::Unretained(this)); -// -// This disables all lifetime management on the object. You're responsible -// for making sure the object is alive at the time of the call. You break it, -// you own it! -// -// BINDING A CLASS METHOD AND HAVING THE CALLBACK OWN THE CLASS -// -// MyClass* myclass = new MyClass; -// butil::Bind(&MyClass::Foo, butil::Owned(myclass)); -// -// The object will be deleted when the callback is destroyed, even if it's -// not run (like if you post a task during shutdown). Potentially useful for -// "fire and forget" cases. -// -// IGNORING RETURN VALUES -// -// Sometimes you want to call a function that returns a value in a callback -// that doesn't expect a return value. -// -// int DoSomething(int arg) { cout << arg << endl; } -// butil::Callback) cb = -// butil::Bind(butil::IgnoreResult(&DoSomething)); -// -// -// ----------------------------------------------------------------------------- -// Quick reference for binding parameters to Bind() -// ----------------------------------------------------------------------------- -// -// Bound parameters are specified as arguments to Bind() and are passed to the -// function. A callback with no parameters or no unbound parameters is called a -// Closure (butil::Callback and butil::Closure are the same thing). -// -// PASSING PARAMETERS OWNED BY THE CALLBACK -// -// void Foo(int* arg) { cout << *arg << endl; } -// int* pn = new int(1); -// butil::Closure foo_callback = butil::Bind(&foo, butil::Owned(pn)); -// -// The parameter will be deleted when the callback is destroyed, even if it's -// not run (like if you post a task during shutdown). -// -// PASSING PARAMETERS AS A scoped_ptr -// -// void TakesOwnership(scoped_ptr arg) {} -// scoped_ptr f(new Foo); -// // f becomes null during the following call. -// butil::Closure cb = butil::Bind(&TakesOwnership, butil::Passed(&f)); -// -// Ownership of the parameter will be with the callback until the it is run, -// when ownership is passed to the callback function. This means the callback -// can only be run once. If the callback is never run, it will delete the -// object when it's destroyed. -// -// PASSING PARAMETERS AS A scoped_refptr -// -// void TakesOneRef(scoped_refptr arg) {} -// scoped_refptr f(new Foo) -// butil::Closure cb = butil::Bind(&TakesOneRef, f); -// -// This should "just work." The closure will take a reference as long as it -// is alive, and another reference will be taken for the called function. -// -// PASSING PARAMETERS BY REFERENCE -// -// Const references are *copied* unless ConstRef is used. Example: -// -// void foo(const int& arg) { printf("%d %p\n", arg, &arg); } -// int n = 1; -// butil::Closure has_copy = butil::Bind(&foo, n); -// butil::Closure has_ref = butil::Bind(&foo, butil::ConstRef(n)); -// n = 2; -// foo(n); // Prints "2 0xaaaaaaaaaaaa" -// has_copy.Run(); // Prints "1 0xbbbbbbbbbbbb" -// has_ref.Run(); // Prints "2 0xaaaaaaaaaaaa" -// -// Normally parameters are copied in the closure. DANGER: ConstRef stores a -// const reference instead, referencing the original parameter. This means -// that you must ensure the object outlives the callback! -// -// -// ----------------------------------------------------------------------------- -// Implementation notes -// ----------------------------------------------------------------------------- -// -// WHERE IS THIS DESIGN FROM: -// -// The design Callback and Bind is heavily influenced by C++'s -// tr1::function/tr1::bind, and by the "Google Callback" system used inside -// Google. -// -// -// HOW THE IMPLEMENTATION WORKS: -// -// There are three main components to the system: -// 1) The Callback classes. -// 2) The Bind() functions. -// 3) The arguments wrappers (e.g., Unretained() and ConstRef()). -// -// The Callback classes represent a generic function pointer. Internally, -// it stores a refcounted piece of state that represents the target function -// and all its bound parameters. Each Callback specialization has a templated -// constructor that takes an BindState<>*. In the context of the constructor, -// the static type of this BindState<> pointer uniquely identifies the -// function it is representing, all its bound parameters, and a Run() method -// that is capable of invoking the target. -// -// Callback's constructor takes the BindState<>* that has the full static type -// and erases the target function type as well as the types of the bound -// parameters. It does this by storing a pointer to the specific Run() -// function, and upcasting the state of BindState<>* to a -// BindStateBase*. This is safe as long as this BindStateBase pointer -// is only used with the stored Run() pointer. -// -// To BindState<> objects are created inside the Bind() functions. -// These functions, along with a set of internal templates, are responsible for -// -// - Unwrapping the function signature into return type, and parameters -// - Determining the number of parameters that are bound -// - Creating the BindState storing the bound parameters -// - Performing compile-time asserts to avoid error-prone behavior -// - Returning an Callback<> with an arity matching the number of unbound -// parameters and that knows the correct refcounting semantics for the -// target object if we are binding a method. -// -// The Bind functions do the above using type-inference, and template -// specializations. -// -// By default Bind() will store copies of all bound parameters, and attempt -// to refcount a target object if the function being bound is a class method. -// These copies are created even if the function takes parameters as const -// references. (Binding to non-const references is forbidden, see bind.h.) -// -// To change this behavior, we introduce a set of argument wrappers -// (e.g., Unretained(), and ConstRef()). These are simple container templates -// that are passed by value, and wrap a pointer to argument. See the -// file-level comment in butil/bind_helpers.h for more info. -// -// These types are passed to the Unwrap() functions, and the MaybeRefcount() -// functions respectively to modify the behavior of Bind(). The Unwrap() -// and MaybeRefcount() functions change behavior by doing partial -// specialization based on whether or not a parameter is a wrapper type. -// -// ConstRef() is similar to tr1::cref. Unretained() is specific to Chromium. -// -// -// WHY NOT TR1 FUNCTION/BIND? -// -// Direct use of tr1::function and tr1::bind was considered, but ultimately -// rejected because of the number of copy constructors invocations involved -// in the binding of arguments during construction, and the forwarding of -// arguments during invocation. These copies will no longer be an issue in -// C++0x because C++0x will support rvalue reference allowing for the compiler -// to avoid these copies. However, waiting for C++0x is not an option. -// -// Measured with valgrind on gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5), the -// tr1::bind call itself will invoke a non-trivial copy constructor three times -// for each bound parameter. Also, each when passing a tr1::function, each -// bound argument will be copied again. -// -// In addition to the copies taken at binding and invocation, copying a -// tr1::function causes a copy to be made of all the bound parameters and -// state. -// -// Furthermore, in Chromium, it is desirable for the Callback to take a -// reference on a target object when representing a class method call. This -// is not supported by tr1. -// -// Lastly, tr1::function and tr1::bind has a more general and flexible API. -// This includes things like argument reordering by use of -// tr1::bind::placeholder, support for non-const reference parameters, and some -// limited amount of subtyping of the tr1::function object (e.g., -// tr1::function is convertible to tr1::function). -// -// These are not features that are required in Chromium. Some of them, such as -// allowing for reference parameters, and subtyping of functions, may actually -// become a source of errors. Removing support for these features actually -// allows for a simpler implementation, and a terser Currying API. -// -// -// WHY NOT GOOGLE CALLBACKS? -// -// The Google callback system also does not support refcounting. Furthermore, -// its implementation has a number of strange edge cases with respect to type -// conversion of its arguments. In particular, the argument's constness must -// at times match exactly the function signature, or the type-inference might -// break. Given the above, writing a custom solution was easier. -// -// -// MISSING FUNCTIONALITY -// - Invoking the return of Bind. Bind(&foo).Run() does not work; -// - Binding arrays to functions that take a non-const pointer. -// Example: -// void Foo(const char* ptr); -// void Bar(char* ptr); -// Bind(&Foo, "test"); -// Bind(&Bar, "test"); // This fails because ptr is not const. - -namespace butil { - -// First, we forward declare the Callback class template. This informs the -// compiler that the template only has 1 type parameter which is the function -// signature that the Callback is representing. -// -// After this, create template specializations for 0-7 parameters. Note that -// even though the template typelist grows, the specialization still -// only has one type: the function signature. -// -// If you are thinking of forward declaring Callback in your own header file, -// please include "butil/callback_forward.h" instead. -template -class Callback; - -namespace internal { -template -struct BindState; -} // namespace internal - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See butil/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run() const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get()); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See butil/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See butil/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See butil/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See butil/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See butil/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5, A6); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See butil/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5, - typename internal::CallbackParamTraits::ForwardType a6) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5), - internal::CallbackForward(a6)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - -template -class Callback : public internal::CallbackBase { - public: - typedef R(RunType)(A1, A2, A3, A4, A5, A6, A7); - - Callback() : CallbackBase(NULL) { } - - // Note that this constructor CANNOT be explicit, and that Bind() CANNOT - // return the exact Callback<> type. See butil/bind.h for details. - template - Callback(internal::BindState* bind_state) - : CallbackBase(bind_state) { - - // Force the assignment to a local variable of PolymorphicInvoke - // so the compiler will typecheck that the passed in Run() method has - // the correct type. - PolymorphicInvoke invoke_func = - &internal::BindState - ::InvokerType::Run; - polymorphic_invoke_ = reinterpret_cast(invoke_func); - } - - bool Equals(const Callback& other) const { - return CallbackBase::Equals(other); - } - - R Run(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5, - typename internal::CallbackParamTraits::ForwardType a6, - typename internal::CallbackParamTraits::ForwardType a7) const { - PolymorphicInvoke f = - reinterpret_cast(polymorphic_invoke_); - - return f(bind_state_.get(), internal::CallbackForward(a1), - internal::CallbackForward(a2), - internal::CallbackForward(a3), - internal::CallbackForward(a4), - internal::CallbackForward(a5), - internal::CallbackForward(a6), - internal::CallbackForward(a7)); - } - - private: - typedef R(*PolymorphicInvoke)( - internal::BindStateBase*, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType, - typename internal::CallbackParamTraits::ForwardType); - -}; - - -// Syntactic sugar to make Callbacks easier to declare since it -// will be used in a lot of APIs with delayed execution. -typedef Callback Closure; - -} // namespace butil - -#endif // BASE_CALLBACK_H diff --git a/src/butil/callback_forward.h b/src/butil/callback_forward.h deleted file mode 100644 index 3837503112..0000000000 --- a/src/butil/callback_forward.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CALLBACK_FORWARD_H_ -#define BASE_CALLBACK_FORWARD_H_ - -namespace butil { - -template -class Callback; - -typedef Callback Closure; - -} // namespace butil - -#endif // BASE_CALLBACK_FORWARD_H diff --git a/src/butil/callback_helpers.cc b/src/butil/callback_helpers.cc deleted file mode 100644 index ddbba764ba..0000000000 --- a/src/butil/callback_helpers.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2013 The Chromium 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 "butil/callback_helpers.h" - -#include "butil/callback.h" - -namespace butil { - -ScopedClosureRunner::ScopedClosureRunner() { -} - -ScopedClosureRunner::ScopedClosureRunner(const Closure& closure) - : closure_(closure) { -} - -ScopedClosureRunner::~ScopedClosureRunner() { - if (!closure_.is_null()) - closure_.Run(); -} - -void ScopedClosureRunner::Reset() { - Closure old_closure = Release(); - if (!old_closure.is_null()) - old_closure.Run(); -} - -void ScopedClosureRunner::Reset(const Closure& closure) { - Closure old_closure = Release(); - closure_ = closure; - if (!old_closure.is_null()) - old_closure.Run(); -} - -Closure ScopedClosureRunner::Release() { - Closure result = closure_; - closure_.Reset(); - return result; -} - -} // namespace butil diff --git a/src/butil/callback_helpers.h b/src/butil/callback_helpers.h deleted file mode 100644 index e09a301fbe..0000000000 --- a/src/butil/callback_helpers.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This defines helpful methods for dealing with Callbacks. Because Callbacks -// are implemented using templates, with a class per callback signature, adding -// methods to Callback<> itself is unattractive (lots of extra code gets -// generated). Instead, consider adding methods here. -// -// ResetAndReturn(&cb) is like cb.Reset() but allows executing a callback (via a -// copy) after the original callback is Reset(). This can be handy if Run() -// reads/writes the variable holding the Callback. - -#ifndef BASE_CALLBACK_HELPERS_H_ -#define BASE_CALLBACK_HELPERS_H_ - -#include "butil/basictypes.h" -#include "butil/callback.h" -#include "butil/compiler_specific.h" - -namespace butil { - -template -butil::Callback ResetAndReturn(butil::Callback* cb) { - butil::Callback ret(*cb); - cb->Reset(); - return ret; -} - -// ScopedClosureRunner is akin to scoped_ptr for Closures. It ensures that the -// Closure is executed and deleted no matter how the current scope exits. -class BASE_EXPORT ScopedClosureRunner { - public: - ScopedClosureRunner(); - explicit ScopedClosureRunner(const Closure& closure); - ~ScopedClosureRunner(); - - void Reset(); - void Reset(const Closure& closure); - Closure Release() WARN_UNUSED_RESULT; - - private: - Closure closure_; - - DISALLOW_COPY_AND_ASSIGN(ScopedClosureRunner); -}; - -} // namespace butil - -#endif // BASE_CALLBACK_HELPERS_H_ diff --git a/src/butil/callback_internal.cc b/src/butil/callback_internal.cc deleted file mode 100644 index 7f38bc2fcc..0000000000 --- a/src/butil/callback_internal.cc +++ /dev/null @@ -1,38 +0,0 @@ -// 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. - -#include "butil/callback_internal.h" - -#include "butil/logging.h" - -namespace butil { -namespace internal { - -bool CallbackBase::is_null() const { - return bind_state_.get() == NULL; -} - -void CallbackBase::Reset() { - polymorphic_invoke_ = NULL; - // NULL the bind_state_ last, since it may be holding the last ref to whatever - // object owns us, and we may be deleted after that. - bind_state_ = NULL; -} - -bool CallbackBase::Equals(const CallbackBase& other) const { - return bind_state_.get() == other.bind_state_.get() && - polymorphic_invoke_ == other.polymorphic_invoke_; -} - -CallbackBase::CallbackBase(BindStateBase* bind_state) - : bind_state_(bind_state), - polymorphic_invoke_(NULL) { - DCHECK(!bind_state_.get() || bind_state_->HasOneRef()); -} - -CallbackBase::~CallbackBase() { -} - -} // namespace internal -} // namespace butil diff --git a/src/butil/callback_internal.h b/src/butil/callback_internal.h deleted file mode 100644 index ab28de025b..0000000000 --- a/src/butil/callback_internal.h +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file contains utility functions and classes that help the -// implementation, and management of the Callback objects. - -#ifndef BASE_CALLBACK_INTERNAL_H_ -#define BASE_CALLBACK_INTERNAL_H_ - -#include - -#include "butil/base_export.h" -#include "butil/memory/ref_counted.h" -#include "butil/memory/scoped_ptr.h" - -template -class ScopedVector; - -namespace butil { -namespace internal { - -// BindStateBase is used to provide an opaque handle that the Callback -// class can use to represent a function object with bound arguments. It -// behaves as an existential type that is used by a corresponding -// DoInvoke function to perform the function execution. This allows -// us to shield the Callback class from the types of the bound argument via -// "type erasure." -class BindStateBase : public RefCountedThreadSafe { - protected: - friend class RefCountedThreadSafe; - virtual ~BindStateBase() {} -}; - -// Holds the Callback methods that don't require specialization to reduce -// template bloat. -class BASE_EXPORT CallbackBase { - public: - // Returns true if Callback is null (doesn't refer to anything). - bool is_null() const; - - // Returns the Callback into an uninitialized state. - void Reset(); - - protected: - // In C++, it is safe to cast function pointers to function pointers of - // another type. It is not okay to use void*. We create a InvokeFuncStorage - // that that can store our function pointer, and then cast it back to - // the original type on usage. - typedef void(*InvokeFuncStorage)(void); - - // Returns true if this callback equals |other|. |other| may be null. - bool Equals(const CallbackBase& other) const; - - // Allow initializing of |bind_state_| via the constructor to avoid default - // initialization of the scoped_refptr. We do not also initialize - // |polymorphic_invoke_| here because doing a normal assignment in the - // derived Callback templates makes for much nicer compiler errors. - explicit CallbackBase(BindStateBase* bind_state); - - // Force the destructor to be instantiated inside this translation unit so - // that our subclasses will not get inlined versions. Avoids more template - // bloat. - ~CallbackBase(); - - scoped_refptr bind_state_; - InvokeFuncStorage polymorphic_invoke_; -}; - -// A helper template to determine if given type is non-const move-only-type, -// i.e. if a value of the given type should be passed via .Pass() in a -// destructive way. -template struct IsMoveOnlyType { - template - static YesType Test(const typename U::MoveOnlyTypeForCPP03*); - - template - static NoType Test(...); - - static const bool value = sizeof(Test(0)) == sizeof(YesType) && - !is_const::value; -}; - -// This is a typetraits object that's used to take an argument type, and -// extract a suitable type for storing and forwarding arguments. -// -// In particular, it strips off references, and converts arrays to -// pointers for storage; and it avoids accidentally trying to create a -// "reference of a reference" if the argument is a reference type. -// -// This array type becomes an issue for storage because we are passing bound -// parameters by const reference. In this case, we end up passing an actual -// array type in the initializer list which C++ does not allow. This will -// break passing of C-string literals. -template ::value> -struct CallbackParamTraits { - typedef const T& ForwardType; - typedef T StorageType; -}; - -// The Storage should almost be impossible to trigger unless someone manually -// specifies type of the bind parameters. However, in case they do, -// this will guard against us accidentally storing a reference parameter. -// -// The ForwardType should only be used for unbound arguments. -template -struct CallbackParamTraits { - typedef T& ForwardType; - typedef T StorageType; -}; - -// Note that for array types, we implicitly add a const in the conversion. This -// means that it is not possible to bind array arguments to functions that take -// a non-const pointer. Trying to specialize the template based on a "const -// T[n]" does not seem to match correctly, so we are stuck with this -// restriction. -template -struct CallbackParamTraits { - typedef const T* ForwardType; - typedef const T* StorageType; -}; - -// See comment for CallbackParamTraits. -template -struct CallbackParamTraits { - typedef const T* ForwardType; - typedef const T* StorageType; -}; - -// Parameter traits for movable-but-not-copyable scopers. -// -// Callback<>/Bind() understands movable-but-not-copyable semantics where -// the type cannot be copied but can still have its state destructively -// transferred (aka. moved) to another instance of the same type by calling a -// helper function. When used with Bind(), this signifies transferal of the -// object's state to the target function. -// -// For these types, the ForwardType must not be a const reference, or a -// reference. A const reference is inappropriate, and would break const -// correctness, because we are implementing a destructive move. A non-const -// reference cannot be used with temporaries which means the result of a -// function or a cast would not be usable with Callback<> or Bind(). -template -struct CallbackParamTraits { - typedef T ForwardType; - typedef T StorageType; -}; - -// CallbackForward() is a very limited simulation of C++11's std::forward() -// used by the Callback/Bind system for a set of movable-but-not-copyable -// types. It is needed because forwarding a movable-but-not-copyable -// argument to another function requires us to invoke the proper move -// operator to create a rvalue version of the type. The supported types are -// whitelisted below as overloads of the CallbackForward() function. The -// default template compiles out to be a no-op. -// -// In C++11, std::forward would replace all uses of this function. However, it -// is impossible to implement a general std::forward with C++11 due to a lack -// of rvalue references. -// -// In addition to Callback/Bind, this is used by PostTaskAndReplyWithResult to -// simulate std::forward() and forward the result of one Callback as a -// parameter to another callback. This is to support Callbacks that return -// the movable-but-not-copyable types whitelisted above. -template -typename enable_if::value, T>::type& CallbackForward(T& t) { - return t; -} - -template -typename enable_if::value, T>::type CallbackForward(T& t) { - return t.Pass(); -} - -} // namespace internal -} // namespace butil - -#endif // BASE_CALLBACK_INTERNAL_H_ diff --git a/src/butil/callback_list.h b/src/butil/callback_list.h deleted file mode 100644 index c0e6966caf..0000000000 --- a/src/butil/callback_list.h +++ /dev/null @@ -1,406 +0,0 @@ -// This file was GENERATED by command: -// pump.py callback_list.h.pump -// DO NOT EDIT BY HAND!!! - - -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_CALLBACK_LIST_H_ -#define BASE_CALLBACK_LIST_H_ - -#include - -#include "butil/basictypes.h" -#include "butil/callback.h" -#include "butil/callback_internal.h" -#include "butil/compiler_specific.h" -#include "butil/logging.h" -#include "butil/memory/scoped_ptr.h" - -// OVERVIEW: -// -// A container for a list of callbacks. Unlike a normal STL vector or list, -// this container can be modified during iteration without invalidating the -// iterator. It safely handles the case of a callback removing itself -// or another callback from the list while callbacks are being run. -// -// TYPICAL USAGE: -// -// class MyWidget { -// public: -// ... -// -// typedef butil::Callback OnFooCallback; -// -// scoped_ptr::Subscription> -// RegisterCallback(const OnFooCallback& cb) { -// return callback_list_.Add(cb); -// } -// -// private: -// void NotifyFoo(const Foo& foo) { -// callback_list_.Notify(foo); -// } -// -// butil::CallbackList callback_list_; -// -// DISALLOW_COPY_AND_ASSIGN(MyWidget); -// }; -// -// -// class MyWidgetListener { -// public: -// MyWidgetListener::MyWidgetListener() { -// foo_subscription_ = MyWidget::GetCurrent()->RegisterCallback( -// butil::Bind(&MyWidgetListener::OnFoo, this))); -// } -// -// MyWidgetListener::~MyWidgetListener() { -// // Subscription gets deleted automatically and will deregister -// // the callback in the process. -// } -// -// private: -// void OnFoo(const Foo& foo) { -// // Do something. -// } -// -// scoped_ptr::Subscription> -// foo_subscription_; -// -// DISALLOW_COPY_AND_ASSIGN(MyWidgetListener); -// }; - -namespace butil { - -namespace internal { - -template -class CallbackListBase { - public: - class Subscription { - public: - Subscription(CallbackListBase* list, - typename std::list::iterator iter) - : list_(list), - iter_(iter) { - } - - ~Subscription() { - if (list_->active_iterator_count_) { - iter_->Reset(); - } else { - list_->callbacks_.erase(iter_); - if (!list_->removal_callback_.is_null()) - list_->removal_callback_.Run(); - } - } - - private: - CallbackListBase* list_; - typename std::list::iterator iter_; - - DISALLOW_COPY_AND_ASSIGN(Subscription); - }; - - // Add a callback to the list. The callback will remain registered until the - // returned Subscription is destroyed, which must occur before the - // CallbackList is destroyed. - scoped_ptr Add(const CallbackType& cb) WARN_UNUSED_RESULT { - DCHECK(!cb.is_null()); - return scoped_ptr( - new Subscription(this, callbacks_.insert(callbacks_.end(), cb))); - } - - // Sets a callback which will be run when a subscription list is changed. - void set_removal_callback(const Closure& callback) { - removal_callback_ = callback; - } - - // Returns true if there are no subscriptions. This is only valid to call when - // not looping through the list. - bool empty() { - DCHECK_EQ(0, active_iterator_count_); - return callbacks_.empty(); - } - - protected: - // An iterator class that can be used to access the list of callbacks. - class Iterator { - public: - explicit Iterator(CallbackListBase* list) - : list_(list), - list_iter_(list_->callbacks_.begin()) { - ++list_->active_iterator_count_; - } - - Iterator(const Iterator& iter) - : list_(iter.list_), - list_iter_(iter.list_iter_) { - ++list_->active_iterator_count_; - } - - ~Iterator() { - if (list_ && --list_->active_iterator_count_ == 0) { - list_->Compact(); - } - } - - CallbackType* GetNext() { - while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null()) - ++list_iter_; - - CallbackType* cb = NULL; - if (list_iter_ != list_->callbacks_.end()) { - cb = &(*list_iter_); - ++list_iter_; - } - return cb; - } - - private: - CallbackListBase* list_; - typename std::list::iterator list_iter_; - }; - - CallbackListBase() : active_iterator_count_(0) {} - - ~CallbackListBase() { - DCHECK_EQ(0, active_iterator_count_); - DCHECK_EQ(0U, callbacks_.size()); - } - - // Returns an instance of a CallbackListBase::Iterator which can be used - // to run callbacks. - Iterator GetIterator() { - return Iterator(this); - } - - // Compact the list: remove any entries which were NULLed out during - // iteration. - void Compact() { - typename std::list::iterator it = callbacks_.begin(); - bool updated = false; - while (it != callbacks_.end()) { - if ((*it).is_null()) { - updated = true; - it = callbacks_.erase(it); - } else { - ++it; - } - - if (updated && !removal_callback_.is_null()) - removal_callback_.Run(); - } - } - - private: - std::list callbacks_; - int active_iterator_count_; - Closure removal_callback_; - - DISALLOW_COPY_AND_ASSIGN(CallbackListBase); -}; - -} // namespace internal - -template class CallbackList; - -template <> -class CallbackList - : public internal::CallbackListBase > { - public: - typedef Callback CallbackType; - - CallbackList() {} - - void Notify() { - internal::CallbackListBase::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template -class CallbackList - : public internal::CallbackListBase > { - public: - typedef Callback CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits::ForwardType a1) { - typename internal::CallbackListBase::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template -class CallbackList - : public internal::CallbackListBase > { - public: - typedef Callback CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2) { - typename internal::CallbackListBase::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template -class CallbackList - : public internal::CallbackListBase > { - public: - typedef Callback CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3) { - typename internal::CallbackListBase::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template -class CallbackList - : public internal::CallbackListBase > { - public: - typedef Callback CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4) { - typename internal::CallbackListBase::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3, a4); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template -class CallbackList - : public internal::CallbackListBase > { - public: - typedef Callback CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5) { - typename internal::CallbackListBase::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3, a4, a5); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template -class CallbackList - : public internal::CallbackListBase > { - public: - typedef Callback CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5, - typename internal::CallbackParamTraits::ForwardType a6) { - typename internal::CallbackListBase::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3, a4, a5, a6); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -template -class CallbackList - : public internal::CallbackListBase > { - public: - typedef Callback CallbackType; - - CallbackList() {} - - void Notify(typename internal::CallbackParamTraits::ForwardType a1, - typename internal::CallbackParamTraits::ForwardType a2, - typename internal::CallbackParamTraits::ForwardType a3, - typename internal::CallbackParamTraits::ForwardType a4, - typename internal::CallbackParamTraits::ForwardType a5, - typename internal::CallbackParamTraits::ForwardType a6, - typename internal::CallbackParamTraits::ForwardType a7) { - typename internal::CallbackListBase::Iterator it = - this->GetIterator(); - CallbackType* cb; - while ((cb = it.GetNext()) != NULL) { - cb->Run(a1, a2, a3, a4, a5, a6, a7); - } - } - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackList); -}; - -} // namespace butil - -#endif // BASE_CALLBACK_LIST_H_ diff --git a/src/butil/memory/raw_scoped_refptr_mismatch_checker.h b/src/butil/memory/raw_scoped_refptr_mismatch_checker.h index 8683933c5d..ff9dcc930d 100644 --- a/src/butil/memory/raw_scoped_refptr_mismatch_checker.h +++ b/src/butil/memory/raw_scoped_refptr_mismatch_checker.h @@ -7,7 +7,6 @@ #include "butil/memory/ref_counted.h" #include "butil/type_traits.h" -#include "butil/tuple.h" #include "butil/build_config.h" // It is dangerous to post a task with a T* argument where T is a subtype of diff --git a/src/butil/tuple.h b/src/butil/tuple.h deleted file mode 100644 index 09eea1a1f6..0000000000 --- a/src/butil/tuple.h +++ /dev/null @@ -1,1291 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// A Tuple is a generic templatized container, similar in concept to std::pair. -// There are classes Tuple0 to Tuple6, cooresponding to the number of elements -// it contains. The convenient MakeTuple() function takes 0 to 6 arguments, -// and will construct and return the appropriate Tuple object. The functions -// DispatchToMethod and DispatchToFunction take a function pointer or instance -// and method pointer, and unpack a tuple into arguments to the call. -// -// Tuple elements are copied by value, and stored in the tuple. See the unit -// tests for more details of how/when the values are copied. -// -// Example usage: -// // These two methods of creating a Tuple are identical. -// Tuple2 tuple_a(1, "wee"); -// Tuple2 tuple_b = MakeTuple(1, "wee"); -// -// void SomeFunc(int a, const char* b) { } -// DispatchToFunction(&SomeFunc, tuple_a); // SomeFunc(1, "wee") -// DispatchToFunction( -// &SomeFunc, MakeTuple(10, "foo")); // SomeFunc(10, "foo") -// -// struct { void SomeMeth(int a, int b, int c) { } } foo; -// DispatchToMethod(&foo, &Foo::SomeMeth, MakeTuple(1, 2, 3)); -// // foo->SomeMeth(1, 2, 3); - -#ifndef BASE_TUPLE_H__ -#define BASE_TUPLE_H__ - -#include "butil/bind_helpers.h" - -// Traits ---------------------------------------------------------------------- -// -// A simple traits class for tuple arguments. -// -// ValueType: the bare, nonref version of a type (same as the type for nonrefs). -// RefType: the ref version of a type (same as the type for refs). -// ParamType: what type to pass to functions (refs should not be constified). - -template -struct TupleTraits { - typedef P ValueType; - typedef P& RefType; - typedef const P& ParamType; -}; - -template -struct TupleTraits { - typedef P ValueType; - typedef P& RefType; - typedef P& ParamType; -}; - -template -struct TupleTypes { }; - -// Tuple ----------------------------------------------------------------------- -// -// This set of classes is useful for bundling 0 or more heterogeneous data types -// into a single variable. The advantage of this is that it greatly simplifies -// function objects that need to take an arbitrary number of parameters; see -// RunnableMethod and IPC::MessageWithTuple. -// -// Tuple0 is supplied to act as a 'void' type. It can be used, for example, -// when dispatching to a function that accepts no arguments (see the -// Dispatchers below). -// Tuple1 is rarely useful. One such use is when A is non-const ref that you -// want filled by the dispatchee, and the tuple is merely a container for that -// output (a "tier"). See MakeRefTuple and its usages. - -struct Tuple0 { - typedef Tuple0 ValueTuple; - typedef Tuple0 RefTuple; - typedef Tuple0 ParamTuple; -}; - -template -struct Tuple1 { - public: - typedef A TypeA; - - Tuple1() {} - explicit Tuple1(typename TupleTraits::ParamType a) : a(a) {} - - A a; -}; - -template -struct Tuple2 { - public: - typedef A TypeA; - typedef B TypeB; - - Tuple2() {} - Tuple2(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b) - : a(a), b(b) { - } - - A a; - B b; -}; - -template -struct Tuple3 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - - Tuple3() {} - Tuple3(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c) - : a(a), b(b), c(c){ - } - - A a; - B b; - C c; -}; - -template -struct Tuple4 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - - Tuple4() {} - Tuple4(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d) - : a(a), b(b), c(c), d(d) { - } - - A a; - B b; - C c; - D d; -}; - -template -struct Tuple5 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - - Tuple5() {} - Tuple5(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d, - typename TupleTraits::ParamType e) - : a(a), b(b), c(c), d(d), e(e) { - } - - A a; - B b; - C c; - D d; - E e; -}; - -template -struct Tuple6 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - - Tuple6() {} - Tuple6(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d, - typename TupleTraits::ParamType e, - typename TupleTraits::ParamType f) - : a(a), b(b), c(c), d(d), e(e), f(f) { - } - - A a; - B b; - C c; - D d; - E e; - F f; -}; - -template -struct Tuple7 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - typedef G TypeG; - - Tuple7() {} - Tuple7(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d, - typename TupleTraits::ParamType e, - typename TupleTraits::ParamType f, - typename TupleTraits::ParamType g) - : a(a), b(b), c(c), d(d), e(e), f(f), g(g) { - } - - A a; - B b; - C c; - D d; - E e; - F f; - G g; -}; - -template -struct Tuple8 { - public: - typedef A TypeA; - typedef B TypeB; - typedef C TypeC; - typedef D TypeD; - typedef E TypeE; - typedef F TypeF; - typedef G TypeG; - typedef H TypeH; - - Tuple8() {} - Tuple8(typename TupleTraits::ParamType a, - typename TupleTraits::ParamType b, - typename TupleTraits::ParamType c, - typename TupleTraits::ParamType d, - typename TupleTraits::ParamType e, - typename TupleTraits::ParamType f, - typename TupleTraits::ParamType g, - typename TupleTraits::ParamType h) - : a(a), b(b), c(c), d(d), e(e), f(f), g(g), h(h) { - } - - A a; - B b; - C c; - D d; - E e; - F f; - G g; - H h; -}; - -// Tuple types ---------------------------------------------------------------- -// -// Allows for selection of ValueTuple/RefTuple/ParamTuple without needing the -// definitions of class types the tuple takes as parameters. - -template <> -struct TupleTypes< Tuple0 > { - typedef Tuple0 ValueTuple; - typedef Tuple0 RefTuple; - typedef Tuple0 ParamTuple; -}; - -template -struct TupleTypes< Tuple1 > { - typedef Tuple1::ValueType> ValueTuple; - typedef Tuple1::RefType> RefTuple; - typedef Tuple1::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple2 > { - typedef Tuple2::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple2::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple2::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple3 > { - typedef Tuple3::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple3::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple3::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple4 > { - typedef Tuple4::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple4::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple4::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple5 > { - typedef Tuple5::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple5::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple5::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple6 > { - typedef Tuple6::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple6::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple6::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple7 > { - typedef Tuple7::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple7::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple7::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -template -struct TupleTypes< Tuple8 > { - typedef Tuple8::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType, - typename TupleTraits::ValueType> ValueTuple; -typedef Tuple8::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType, - typename TupleTraits::RefType> RefTuple; - typedef Tuple8::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType, - typename TupleTraits::ParamType> ParamTuple; -}; - -// Tuple creators ------------------------------------------------------------- -// -// Helper functions for constructing tuples while inferring the template -// argument types. - -inline Tuple0 MakeTuple() { - return Tuple0(); -} - -template -inline Tuple1 MakeTuple(const A& a) { - return Tuple1(a); -} - -template -inline Tuple2 MakeTuple(const A& a, const B& b) { - return Tuple2(a, b); -} - -template -inline Tuple3 MakeTuple(const A& a, const B& b, const C& c) { - return Tuple3(a, b, c); -} - -template -inline Tuple4 MakeTuple(const A& a, const B& b, const C& c, - const D& d) { - return Tuple4(a, b, c, d); -} - -template -inline Tuple5 MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e) { - return Tuple5(a, b, c, d, e); -} - -template -inline Tuple6 MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e, const F& f) { - return Tuple6(a, b, c, d, e, f); -} - -template -inline Tuple7 MakeTuple(const A& a, const B& b, const C& c, - const D& d, const E& e, const F& f, - const G& g) { - return Tuple7(a, b, c, d, e, f, g); -} - -template -inline Tuple8 MakeTuple(const A& a, const B& b, - const C& c, const D& d, - const E& e, const F& f, - const G& g, const H& h) { - return Tuple8(a, b, c, d, e, f, g, h); -} - -// The following set of helpers make what Boost refers to as "Tiers" - a tuple -// of references. - -template -inline Tuple1 MakeRefTuple(A& a) { - return Tuple1(a); -} - -template -inline Tuple2 MakeRefTuple(A& a, B& b) { - return Tuple2(a, b); -} - -template -inline Tuple3 MakeRefTuple(A& a, B& b, C& c) { - return Tuple3(a, b, c); -} - -template -inline Tuple4 MakeRefTuple(A& a, B& b, C& c, D& d) { - return Tuple4(a, b, c, d); -} - -template -inline Tuple5 MakeRefTuple(A& a, B& b, C& c, D& d, E& e) { - return Tuple5(a, b, c, d, e); -} - -template -inline Tuple6 MakeRefTuple(A& a, B& b, C& c, D& d, E& e, - F& f) { - return Tuple6(a, b, c, d, e, f); -} - -template -inline Tuple7 MakeRefTuple(A& a, B& b, C& c, D& d, - E& e, F& f, G& g) { - return Tuple7(a, b, c, d, e, f, g); -} - -template -inline Tuple8 MakeRefTuple(A& a, B& b, C& c, - D& d, E& e, F& f, - G& g, H& h) { - return Tuple8(a, b, c, d, e, f, g, h); -} - -// Dispatchers ---------------------------------------------------------------- -// -// Helper functions that call the given method on an object, with the unpacked -// tuple arguments. Notice that they all have the same number of arguments, -// so you need only write: -// DispatchToMethod(object, &Object::method, args); -// This is very useful for templated dispatchers, since they don't need to know -// what type |args| is. - -// Non-Static Dispatchers with no out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) { - (obj->*method)(); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a)); -} - -template -inline void DispatchToMethod(ObjT* obj, - Method method, - const Tuple2& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e), - butil::internal::UnwrapTraits::Unwrap(arg.f)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple7& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e), - butil::internal::UnwrapTraits::Unwrap(arg.f), - butil::internal::UnwrapTraits::Unwrap(arg.g)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple8& arg) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e), - butil::internal::UnwrapTraits::Unwrap(arg.f), - butil::internal::UnwrapTraits::Unwrap(arg.g), - butil::internal::UnwrapTraits::Unwrap(arg.h)); -} - -// Static Dispatchers with no out params. - -template -inline void DispatchToFunction(Function function, const Tuple0& arg) { - (*function)(); -} - -template -inline void DispatchToFunction(Function function, const A& arg) { - (*function)(arg); -} - -template -inline void DispatchToFunction(Function function, const Tuple1& arg) { - (*function)(butil::internal::UnwrapTraits::Unwrap(arg.a)); -} - -template -inline void DispatchToFunction(Function function, const Tuple2& arg) { - (*function)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b)); -} - -template -inline void DispatchToFunction(Function function, const Tuple3& arg) { - (*function)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple4& arg) { - (*function)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple5& arg) { - (*function)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple6& arg) { - (*function)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e), - butil::internal::UnwrapTraits::Unwrap(arg.f)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple7& arg) { - (*function)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e), - butil::internal::UnwrapTraits::Unwrap(arg.f), - butil::internal::UnwrapTraits::Unwrap(arg.g)); -} - -template -inline void DispatchToFunction(Function function, - const Tuple8& arg) { - (*function)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e), - butil::internal::UnwrapTraits::Unwrap(arg.f), - butil::internal::UnwrapTraits::Unwrap(arg.g), - butil::internal::UnwrapTraits::Unwrap(arg.h)); -} - -// Dispatchers with 0 out param (as a Tuple0). - -template -inline void DispatchToMethod(ObjT* obj, - Method method, - const Tuple0& arg, Tuple0*) { - (obj->*method)(); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg)); -} - -template -inline void DispatchToMethod(ObjT* obj, - Method method, - const Tuple1& arg, Tuple0*) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a)); -} - -template -inline void DispatchToMethod(ObjT* obj, - Method method, - const Tuple2& arg, Tuple0*) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& arg, Tuple0*) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& arg, Tuple0*) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& arg, Tuple0*) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e)); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& arg, Tuple0*) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(arg.a), - butil::internal::UnwrapTraits::Unwrap(arg.b), - butil::internal::UnwrapTraits::Unwrap(arg.c), - butil::internal::UnwrapTraits::Unwrap(arg.d), - butil::internal::UnwrapTraits::Unwrap(arg.e), - butil::internal::UnwrapTraits::Unwrap(arg.f)); -} - -// Dispatchers with 1 out param. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple1* out) { - (obj->*method)(&out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple1* out) { - (obj->*method)(in, &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple1* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple1* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple1* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple1* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple1* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - &out->a); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple1* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - butil::internal::UnwrapTraits::Unwrap(in.f), - &out->a); -} - -// Dispatchers with 2 out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple2* out) { - (obj->*method)(&out->a, &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple2* out) { - (obj->*method)(in, &out->a, &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple2* out) { - (obj->*method)( - butil::internal::UnwrapTraits::Unwrap(in.a), &out->a, &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple2* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - &out->a, - &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple2* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - &out->a, - &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple2* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - &out->a, - &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple2* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - &out->a, - &out->b); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple2* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - butil::internal::UnwrapTraits::Unwrap(in.f), - &out->a, - &out->b); -} - -// Dispatchers with 3 out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple3* out) { - (obj->*method)(&out->a, &out->b, &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple3* out) { - (obj->*method)(in, &out->a, &out->b, &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple3* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple3* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple3* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple3* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple3* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - &out->a, - &out->b, - &out->c); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple3* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - butil::internal::UnwrapTraits::Unwrap(in.f), - &out->a, - &out->b, - &out->c); -} - -// Dispatchers with 4 out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple4* out) { - (obj->*method)(&out->a, &out->b, &out->c, &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple4* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple4* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple4* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple4* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple4* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple4* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - &out->a, - &out->b, - &out->c, - &out->d); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple4* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - butil::internal::UnwrapTraits::Unwrap(in.f), - &out->a, - &out->b, - &out->c, - &out->d); -} - -// Dispatchers with 5 out params. - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple0& in, - Tuple5* out) { - (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const InA& in, - Tuple5* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple1& in, - Tuple5* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple2& in, - Tuple5* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple3& in, - Tuple5* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple4& in, - Tuple5* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple5& in, - Tuple5* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -template -inline void DispatchToMethod(ObjT* obj, Method method, - const Tuple6& in, - Tuple5* out) { - (obj->*method)(butil::internal::UnwrapTraits::Unwrap(in.a), - butil::internal::UnwrapTraits::Unwrap(in.b), - butil::internal::UnwrapTraits::Unwrap(in.c), - butil::internal::UnwrapTraits::Unwrap(in.d), - butil::internal::UnwrapTraits::Unwrap(in.e), - butil::internal::UnwrapTraits::Unwrap(in.f), - &out->a, - &out->b, - &out->c, - &out->d, - &out->e); -} - -#endif // BASE_TUPLE_H__ diff --git a/test/Makefile b/test/Makefile index cc9412ee1b..44d012a305 100644 --- a/test/Makefile +++ b/test/Makefile @@ -21,15 +21,9 @@ LIBPATHS=$(addprefix -L, $(LIBS)) TEST_BUTIL_SOURCES = \ at_exit_unittest.cc \ atomicops_unittest.cc \ - barrier_closure_unittest.cc \ base64_unittest.cc \ big_endian_unittest.cc \ - bind_unittest.cc \ bits_unittest.cc \ - callback_helpers_unittest.cc \ - callback_list_unittest.cc \ - callback_unittest.cc \ - cancelable_callback_unittest.cc \ hash_tables_unittest.cc \ linked_list_unittest.cc \ mru_cache_unittest.cc \ @@ -98,7 +92,6 @@ TEST_BUTIL_SOURCES = \ time_unittest.cc \ version_unittest.cc \ logging_unittest.cc \ - tuple_unittest.cc \ cacheline_unittest.cpp \ class_name_unittest.cpp \ endpoint_unittest.cpp \ diff --git a/test/at_exit_unittest.cc b/test/at_exit_unittest.cc index 16d00a9aac..997c0b68d7 100644 --- a/test/at_exit_unittest.cc +++ b/test/at_exit_unittest.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "butil/at_exit.h" -#include "butil/bind.h" #include @@ -78,10 +77,3 @@ TEST_F(AtExitTest, Param) { &g_test_counter_1); butil::AtExitManager::ProcessCallbacksNow(); } - -TEST_F(AtExitTest, Task) { - ZeroTestCounters(); - butil::AtExitManager::RegisterTask(butil::Bind(&ExpectParamIsCounter, - &g_test_counter_1)); - butil::AtExitManager::ProcessCallbacksNow(); -} diff --git a/test/butil_unittest_main.cpp b/test/butil_unittest_main.cpp index e3606b5cf5..3ff645d449 100644 --- a/test/butil_unittest_main.cpp +++ b/test/butil_unittest_main.cpp @@ -5,6 +5,7 @@ #include #include "butil/base_switches.h" #include "butil/at_exit.h" +#include "butil/logging.h" #include "test/multiprocess_func_list.h" // Disable coredumps by default to avoid generating a lot of coredumps diff --git a/test/cancellation_flag_unittest.cc b/test/cancellation_flag_unittest.cc index 5e1f90c6a2..08b6e725a8 100644 --- a/test/cancellation_flag_unittest.cc +++ b/test/cancellation_flag_unittest.cc @@ -6,7 +6,6 @@ #include "butil/synchronization/cancellation_flag.h" -#include "butil/bind.h" #include "butil/logging.h" #include "butil/synchronization/spin_wait.h" #include "butil/time/time.h" diff --git a/test/condition_variable_unittest.cc b/test/condition_variable_unittest.cc index eeb6699fac..ef3e4f04ea 100644 --- a/test/condition_variable_unittest.cc +++ b/test/condition_variable_unittest.cc @@ -8,7 +8,6 @@ #include #include -#include "butil/bind.h" #include "butil/logging.h" #include "butil/memory/scoped_ptr.h" #include "butil/synchronization/condition_variable.h" diff --git a/test/crash_logging_unittest.cc b/test/crash_logging_unittest.cc index 7d9f16d803..d0fa432449 100644 --- a/test/crash_logging_unittest.cc +++ b/test/crash_logging_unittest.cc @@ -7,7 +7,6 @@ #include #include -#include "butil/bind.h" #include namespace { diff --git a/test/scoped_ptr_unittest.cc b/test/scoped_ptr_unittest.cc index e8b43105af..8fe2acc5e2 100644 --- a/test/scoped_ptr_unittest.cc +++ b/test/scoped_ptr_unittest.cc @@ -5,8 +5,6 @@ #include "butil/memory/scoped_ptr.h" #include "butil/basictypes.h" -#include "butil/bind.h" -#include "butil/callback.h" #include namespace { diff --git a/test/scoped_vector_unittest.cc b/test/scoped_vector_unittest.cc index db92464151..e5e78f5447 100644 --- a/test/scoped_vector_unittest.cc +++ b/test/scoped_vector_unittest.cc @@ -4,8 +4,6 @@ #include "butil/memory/scoped_vector.h" -#include "butil/bind.h" -#include "butil/callback.h" #include "butil/memory/scoped_ptr.h" #include @@ -272,10 +270,9 @@ TEST(ScopedVectorTest, Passed) { ScopedVector deleter_vector; deleter_vector.push_back(new DeleteCounter(&deletes)); EXPECT_EQ(0, deletes); - butil::Callback(void)> callback = - butil::Bind(&PassThru, butil::Passed(&deleter_vector)); + EXPECT_EQ(0, deletes); - ScopedVector result = callback.Run(); + ScopedVector result = deleter_vector.Pass(); EXPECT_EQ(0, deletes); result.clear(); EXPECT_EQ(1, deletes); diff --git a/test/test_file_util_linux.cc b/test/test_file_util_linux.cc index 9a79bace13..3abb4fe73a 100644 --- a/test/test_file_util_linux.cc +++ b/test/test_file_util_linux.cc @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "test/test_file_util.h" - #include #include #include diff --git a/test/tuple_unittest.cc b/test/tuple_unittest.cc deleted file mode 100644 index 51862dd122..0000000000 --- a/test/tuple_unittest.cc +++ /dev/null @@ -1,128 +0,0 @@ -// 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 "butil/tuple.h" - -#include "butil/compiler_specific.h" -#include - -namespace { - -void DoAdd(int a, int b, int c, int* res) { - *res = a + b + c; -} - -struct Addy { - Addy() { } - void DoAdd(int a, int b, int c, int d, int* res) { - *res = a + b + c + d; - } -}; - -struct Addz { - Addz() { } - void DoAdd(int a, int b, int c, int d, int e, int* res) { - *res = a + b + c + d + e; - } -}; - -} // namespace - -TEST(TupleTest, Basic) { - Tuple0 t0 ALLOW_UNUSED = MakeTuple(); - Tuple1 t1(1); - Tuple2 t2 = MakeTuple(1, static_cast("wee")); - Tuple3 t3(1, 2, 3); - Tuple4 t4(1, 2, 3, &t1.a); - Tuple5 t5(1, 2, 3, 4, &t4.a); - Tuple6 t6(1, 2, 3, 4, 5, &t4.a); - - EXPECT_EQ(1, t1.a); - EXPECT_EQ(1, t2.a); - EXPECT_EQ(1, t3.a); - EXPECT_EQ(2, t3.b); - EXPECT_EQ(3, t3.c); - EXPECT_EQ(1, t4.a); - EXPECT_EQ(2, t4.b); - EXPECT_EQ(3, t4.c); - EXPECT_EQ(1, t5.a); - EXPECT_EQ(2, t5.b); - EXPECT_EQ(3, t5.c); - EXPECT_EQ(4, t5.d); - EXPECT_EQ(1, t6.a); - EXPECT_EQ(2, t6.b); - EXPECT_EQ(3, t6.c); - EXPECT_EQ(4, t6.d); - EXPECT_EQ(5, t6.e); - - EXPECT_EQ(1, t1.a); - DispatchToFunction(&DoAdd, t4); - EXPECT_EQ(6, t1.a); - - int res = 0; - DispatchToFunction(&DoAdd, MakeTuple(9, 8, 7, &res)); - EXPECT_EQ(24, res); - - Addy addy; - EXPECT_EQ(1, t4.a); - DispatchToMethod(&addy, &Addy::DoAdd, t5); - EXPECT_EQ(10, t4.a); - - Addz addz; - EXPECT_EQ(10, t4.a); - DispatchToMethod(&addz, &Addz::DoAdd, t6); - EXPECT_EQ(15, t4.a); -} - -namespace { - -struct CopyLogger { - CopyLogger() { ++TimesConstructed; } - CopyLogger(const CopyLogger& tocopy) { ++TimesConstructed; ++TimesCopied; } - ~CopyLogger() { } - - static int TimesCopied; - static int TimesConstructed; -}; - -void SomeLoggerMethRef(const CopyLogger& logy, const CopyLogger* ptr, bool* b) { - *b = &logy == ptr; -} - -void SomeLoggerMethCopy(CopyLogger logy, const CopyLogger* ptr, bool* b) { - *b = &logy == ptr; -} - -int CopyLogger::TimesCopied = 0; -int CopyLogger::TimesConstructed = 0; - -} // namespace - -TEST(TupleTest, Copying) { - CopyLogger logger; - EXPECT_EQ(0, CopyLogger::TimesCopied); - EXPECT_EQ(1, CopyLogger::TimesConstructed); - - bool res = false; - - // Creating the tuple should copy the class to store internally in the tuple. - Tuple3 tuple(logger, &logger, &res); - tuple.b = &tuple.a; - EXPECT_EQ(2, CopyLogger::TimesConstructed); - EXPECT_EQ(1, CopyLogger::TimesCopied); - - // Our internal Logger and the one passed to the function should be the same. - res = false; - DispatchToFunction(&SomeLoggerMethRef, tuple); - EXPECT_TRUE(res); - EXPECT_EQ(2, CopyLogger::TimesConstructed); - EXPECT_EQ(1, CopyLogger::TimesCopied); - - // Now they should be different, since the function call will make a copy. - res = false; - DispatchToFunction(&SomeLoggerMethCopy, tuple); - EXPECT_FALSE(res); - EXPECT_EQ(3, CopyLogger::TimesConstructed); - EXPECT_EQ(2, CopyLogger::TimesCopied); -} diff --git a/test/weak_ptr_unittest.cc b/test/weak_ptr_unittest.cc index 1b6d5d1472..a4be37f01b 100644 --- a/test/weak_ptr_unittest.cc +++ b/test/weak_ptr_unittest.cc @@ -6,7 +6,6 @@ #include -#include "butil/bind.h" #include "butil/debug/leak_annotations.h" #include "butil/memory/scoped_ptr.h" #include "butil/synchronization/waitable_event.h" From 215c7bc3d03bcb153d58e02aaf4600b05e1e070a Mon Sep 17 00:00:00 2001 From: zyearn Date: Tue, 19 Sep 2017 09:18:08 +0800 Subject: [PATCH 10/11] More translation of new_protocol.md --- docs/cn/new_protocol.md | 2 +- docs/en/new_protocol.md | 144 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 3 deletions(-) diff --git a/docs/cn/new_protocol.md b/docs/cn/new_protocol.md index cb61705e52..f9bc07f7d8 100644 --- a/docs/cn/new_protocol.md +++ b/docs/cn/new_protocol.md @@ -52,7 +52,7 @@ enum ProtocolType { ``` ## 实现回调 -均定义在struct Protocol中,该结构定义在[protocol.h](https://github.com/brpc/brpc/blob/master/src/brpc/protocol.h)。其中的parse必须实现,除此之外server端至少要实现process_request,client端至少要实现serialize_request,pack_request,process_response; +均定义在struct Protocol中,该结构定义在[protocol.h](https://github.com/brpc/brpc/blob/master/src/brpc/protocol.h)。其中的parse必须实现,除此之外server端至少要实现process_request,client端至少要实现serialize_request,pack_request,process_response。 实现协议回调还是比较困难的,这块的代码不会像供普通用户使用的那样,有较好的提示和保护,你得先靠自己搞清楚其他协议中的类似代码,然后再动手,最后发给我们做code review。 diff --git a/docs/en/new_protocol.md b/docs/en/new_protocol.md index 221f6986c3..2c75903dd5 100644 --- a/docs/en/new_protocol.md +++ b/docs/en/new_protocol.md @@ -1,4 +1,4 @@ -# Multi-protocol support in server side +# Multi-protocol support in the server side brpc server supports all protocols in the same port, and it makes deployment and maintenance more convenient in most of the time. Since the format of different protocols is very different, it is hard to support all protocols in the same port unambiguously. In consider of decoupling and extensibility, it is also hard to build a multiplexer for all protocols. Thus our way is to classify all protocols into three categories and try one by one: @@ -8,5 +8,145 @@ brpc server supports all protocols in the same port, and it makes deployment and Considering that there will be only one protocol in most connections, we record the result of last selection so that it will be tried first when further data comes. It reduces the overhead of matching protocols to nearly zero for long connections. Although the process of matching protocols will be run everytime for short connections, the bottleneck of short connections is not in here and this method is still fast enough. If there are lots of new protocols added into brpc in the future, we may consider some heuristic methods to match protocols. -# Multi-protocol support in client side +# Multi-protocol support in the client side +Unlike the server side that protocols must be dynamically determined based on the data on the connection, the client side as the originator, naturally know their own protocol format. As long as the protocol data is sent through connection pool or short connection, which means it has exclusive usage of that connection, then the protocol can have any complex (or bad) format. Since the client will record the protocol when sending the data, it will use that recored protocol to parse the data without any matching overhead when responses come back. There is no magic number in some protocols like memcache, redis, it is hard to distinguish them in the server side, but it is no problem in the client side. + +# Support new protocols + +brpc is designed to add new protocols at any time, just proceed as follows: + +> The protocol that begins with nshead has unified support, just read [this](nshead_service.md). + +## add ProtocolType + +Add new protocol type in ProtocolType in [options.proto](https://github.com/brpc/brpc/blob/master/src/brpc/options.proto). If you need to add new protocol, please contact us to add it for you to make sure there is no conflict with protocols of others. + +Currently we support in ProtocolType(at the end of 2016): +```c++ +enum ProtocolType { + PROTOCOL_UNKNOWN = 0; + PROTOCOL_BAIDU_STD = 1; + PROTOCOL_STREAMING_RPC = 2; + PROTOCOL_HULU_PBRPC = 3; + PROTOCOL_SOFA_PBRPC = 4; + PROTOCOL_RTMP = 5; + PROTOCOL_HTTP = 6; + PROTOCOL_PUBLIC_PBRPC = 7; + PROTOCOL_NOVA_PBRPC = 8; + PROTOCOL_NSHEAD_CLIENT = 9; // implemented in brpc-ub + PROTOCOL_NSHEAD = 10; + PROTOCOL_HADOOP_RPC = 11; + PROTOCOL_HADOOP_SERVER_RPC = 12; + PROTOCOL_MONGO = 13; // server side only + PROTOCOL_UBRPC_COMPACK = 14; + PROTOCOL_DIDX_CLIENT = 15; // Client side only + PROTOCOL_REDIS = 16; // Client side only + PROTOCOL_MEMCACHE = 17; // Client side only + PROTOCOL_ITP = 18; + PROTOCOL_NSHEAD_MCPACK = 19; + PROTOCOL_DISP_IDL = 20; // Client side only + PROTOCOL_ERSDA_CLIENT = 21; // Client side only + PROTOCOL_UBRPC_MCPACK2 = 22; // Client side only +} +``` +## Implement Callbacks + +All callbacks are defined in struct Protocol, which is defined in [protocol.h](https://github.com/brpc/brpc/blob/master/src/brpc/protocol.h). Among all these callbacks, `parse` is a callback that must be implmented. Besides, `process_request` must be implemented in the server side and `serialize_request`, `pack_request`, `process_response` must be implemented in the client side. + +It is difficult to implement callbacks of the protocol. These codes is not like the codes that ordinary users use which has good prompts and protections. You have to figure it out how to handle similar code in other protocols and implement your own protocol, then send it to us to do code review. + +### parse + +```c++ +typedef ParseResult (*Parse)(butil::IOBuf* source, Socket *socket, bool read_eof, const void *arg); +``` +用于把消息从source上切割下来,client端和server端使用同一个parse函数。返回的消息会被递给process_request(server端)或process_response(client端)。 + +参数:source是读取到的二进制内容,socket是对应的连接,read_eof为true表示连接已被对端关闭,arg在server端是对应server的指针,在client端是NULL。 + +ParseResult可能是错误,也可能包含一个切割下来的message,可能的值有: + +- PARSE_ERROR_TRY_OTHERS :不是这个协议,框架会尝试下一个协议。source不能被消费。 +- PARSE_ERROR_NOT_ENOUGH_DATA : 到目前为止数据内容不违反协议,但不构成完整的消息。等到连接上有新数据时,新数据会被append入source并重新调用parse。如果不确定数据是否一定属于这个协议,source不应被消费,如果确定数据属于这个协议,也可以把source的内容转移到内部的状态中去。比如http协议解析中即使source不包含一个完整的http消息,它也会被http parser消费掉,以避免下一次重复解析。 +- PARSE_ERROR_TOO_BIG_DATA : 消息太大,拒绝掉以保护server,连接会被关闭。 +- PARSE_ERROR_NO_RESOURCE : 内部错误,比如资源分配失败。连接会被关闭。 +- PARSE_ERROR_ABSOLUTELY_WRONG : 应该是这个协议(比如magic number匹配了),但是格式不符合预期。连接会被关闭。 + +### serialize_request +```c++ +typedef bool (*SerializeRequest)(butil::IOBuf* request_buf, + Controller* cntl, + const google::protobuf::Message* request); +``` +把request序列化进request_buf,client端必须实现。发生在pack_request之前,一次RPC中只会调用一次。cntl包含某些协议(比如http)需要的信息。成功返回true,否则false。 + +### pack_request +```c++ +typedef int (*PackRequest)(butil::IOBuf* msg, + uint64_t correlation_id, + const google::protobuf::MethodDescriptor* method, + Controller* controller, + const butil::IOBuf& request_buf, + const Authenticator* auth); +``` +把request_buf打包入msg,每次向server发送消息前(包括重试)都会调用。当auth不为空时,需要打包认证信息。成功返回0,否则-1。 + +### process_request +```c++ +typedef void (*ProcessRequest)(InputMessageBase* msg_base); +``` +处理server端parse返回的消息,server端必须实现。可能会在和parse()不同的线程中运行。多个process_request可能同时运行。 + +在r34386后必须在处理结束时调用msg_base->Destroy(),为了防止漏调,考虑使用DestroyingPtr<>。 + +### process_response +```c++ +typedef void (*ProcessResponse)(InputMessageBase* msg); +``` +处理client端parse返回的消息,client端必须实现。可能会在和parse()不同的线程中运行。多个process_response可能同时运行。 + +在r34386后必须在处理结束时调用msg_base->Destroy(),为了防止漏调,考虑使用DestroyingPtr<>。 + +### verify +```c++ +typedef bool (*Verify)(const InputMessageBase* msg); +``` +处理连接的认证,只会对连接上的第一个消息调用,需要支持认证的server端必须实现,不需要认证或仅支持client端的协议可填NULL。成功返回true,否则false。 + +### parse_server_address +```c++ +typedef bool (*ParseServerAddress)(butil::EndPoint* out, const char* server_addr_and_port); +``` +把server_addr_and_port(Channel.Init的一个参数)转化为butil::EndPoint,可选。一些协议对server地址的表达和理解可能是不同的。 + +### get_method_name +```c++ +typedef const std::string& (*GetMethodName)(const google::protobuf::MethodDescriptor* method, + const Controller*); +``` +定制method name,可选。 + +### supported_connection_type + +标记支持的连接方式。如果支持所有连接方式,设为CONNECTION_TYPE_ALL。如果只支持连接池和短连接,设为CONNECTION_TYPE_POOLED_AND_SHORT。 + +### name + +协议的名称,会出现在各种配置和显示中,越简短越好,必须是字符串常量。 + +## 注册到全局 + +实现好的协议要调用RegisterProtocol[注册到全局](https://github.com/brpc/brpc/blob/master/src/brpc/global.cpp),以便brpc发现。就像这样: +```c++ +Protocol http_protocol = { ParseHttpMessage, + SerializeHttpRequest, PackHttpRequest, + ProcessHttpRequest, ProcessHttpResponse, + VerifyHttpRequest, ParseHttpServerAddress, + GetHttpMethodName, + CONNECTION_TYPE_POOLED_AND_SHORT, + "http" }; +if (RegisterProtocol(PROTOCOL_HTTP, http_protocol) != 0) { + exit(1); +} +``` From fdea93a02dfd224a3d4055a7142f7d0d27feaed8 Mon Sep 17 00:00:00 2001 From: old-bear Date: Tue, 19 Sep 2017 10:21:29 +0800 Subject: [PATCH 11/11] Remove a redundant link --- docs/cn/http_client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cn/http_client.md b/docs/cn/http_client.md index b689ca5216..a452028126 100644 --- a/docs/cn/http_client.md +++ b/docs/cn/http_client.md @@ -83,7 +83,7 @@ URL的一般形式如下图: 确实,在简单使用场景下,这两者有所重复,但在复杂场景中,两者差别很大,比如: -- 访问挂在bns下的多个http server。此时Channel.Init传入的是bns节点名称,对uri()的赋值则是包含Host的完整URL(比如"www.foo.com/index.html?name=value"),BNS下所有的http server都会看到"Host: [www.foo.com](http://www.foo.com/)";uri()也可以是只包含路径的URL,比如"/index.html?name=value",框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。 +- 访问挂在bns下的多个http server。此时Channel.Init传入的是bns节点名称,对uri()的赋值则是包含Host的完整URL(比如"www.foo.com/index.html?name=value"),BNS下所有的http server都会看到"Host: www.foo.com";uri()也可以是只包含路径的URL,比如"/index.html?name=value",框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。 - 通过http proxy访问目标server。此时Channel.Init传入的是proxy server的地址,但uri()填入的是目标server的URL。 # 常见设置