summaryrefslogtreecommitdiffstats
path: root/chromium/base
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-23 17:21:03 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-01-23 16:25:15 +0000
commitc551f43206405019121bd2b2c93714319a0a3300 (patch)
tree1f48c30631c421fd4bbb3c36da20183c8a2ed7d7 /chromium/base
parent7961cea6d1041e3e454dae6a1da660b453efd238 (diff)
BASELINE: Update Chromium to 79.0.3945.139
Change-Id: I336b7182fab9bca80b709682489c07db112eaca5 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/base')
-rw-r--r--chromium/base/BUILD.gn64
-rw-r--r--chromium/base/allocator/allocator.gni4
-rw-r--r--chromium/base/allocator/debugallocation_shim.cc5
-rw-r--r--chromium/base/allocator/partition_allocator/partition_alloc.cc28
-rw-r--r--chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc278
-rw-r--r--chromium/base/allocator/partition_allocator/partition_bucket.cc8
-rw-r--r--chromium/base/allocator/partition_allocator/partition_freelist_entry.h55
-rw-r--r--chromium/base/allocator/partition_allocator/partition_page.h35
-rw-r--r--chromium/base/allocator/partition_allocator/partition_root_base.h4
-rw-r--r--chromium/base/allocator/partition_allocator/random.h2
-rw-r--r--chromium/base/android/jni_generator/BUILD.gn1
-rw-r--r--chromium/base/auto_reset.h33
-rw-r--r--chromium/base/auto_reset_unittest.cc (renamed from chromium/base/auto_reset_unittests.cc)0
-rw-r--r--chromium/base/big_endian.cc24
-rw-r--r--chromium/base/big_endian.h16
-rw-r--r--chromium/base/big_endian_unittest.cc58
-rw-r--r--chromium/base/bind_internal.h17
-rw-r--r--chromium/base/callback.h2
-rw-r--r--chromium/base/callback_helpers_unittest.cc4
-rw-r--r--chromium/base/containers/README.md50
-rw-r--r--chromium/base/containers/checked_iterators.h276
-rw-r--r--chromium/base/containers/flat_map.h18
-rw-r--r--chromium/base/containers/flat_map_unittest.cc41
-rw-r--r--chromium/base/containers/flat_set.h6
-rw-r--r--chromium/base/containers/flat_set_unittest.cc9
-rw-r--r--chromium/base/containers/flat_tree.h125
-rw-r--r--chromium/base/containers/flat_tree_unittest.cc118
-rw-r--r--chromium/base/containers/intrusive_heap.h5
-rw-r--r--chromium/base/containers/span.h10
-rw-r--r--chromium/base/containers/span_unittest.cc109
-rw-r--r--chromium/base/feature_list.cc29
-rw-r--r--chromium/base/feature_list.h19
-rw-r--r--chromium/base/feature_list_unittest.cc136
-rw-r--r--chromium/base/file_version_info_win.cc96
-rw-r--r--chromium/base/file_version_info_win.h16
-rw-r--r--chromium/base/file_version_info_win_unittest.cc11
-rw-r--r--chromium/base/files/file.cc24
-rw-r--r--chromium/base/files/file.h10
-rw-r--r--chromium/base/files/file_path_watcher_unittest.cc2
-rw-r--r--chromium/base/files/file_posix.cc16
-rw-r--r--chromium/base/files/file_unittest.cc23
-rw-r--r--chromium/base/files/file_util.h17
-rw-r--r--chromium/base/files/file_util_posix.cc65
-rw-r--r--chromium/base/files/file_util_unittest.cc28
-rw-r--r--chromium/base/files/file_util_win.cc34
-rw-r--r--chromium/base/files/scoped_temp_dir.cc10
-rw-r--r--chromium/base/files/scoped_temp_dir_unittest.cc17
-rw-r--r--chromium/base/fuchsia/filtered_service_directory.h2
-rw-r--r--chromium/base/fuchsia/scoped_service_binding.h2
-rw-r--r--chromium/base/fuchsia/scoped_service_binding_unittest.cc16
-rw-r--r--chromium/base/fuchsia/service_directory_test_base.h5
-rw-r--r--chromium/base/fuchsia/service_provider_impl_unittest.cc5
-rw-r--r--chromium/base/hash/sha1.cc10
-rw-r--r--chromium/base/hash/sha1.h6
-rw-r--r--chromium/base/hash/sha1_boringssl.cc13
-rw-r--r--chromium/base/hash/sha1_unittest.cc75
-rw-r--r--chromium/base/i18n/icu_util.cc183
-rw-r--r--chromium/base/i18n/icu_util.h28
-rw-r--r--chromium/base/i18n/icu_util_unittest.cc82
-rw-r--r--chromium/base/immediate_crash.h10
-rw-r--r--chromium/base/ios/crb_protocol_observers_unittest.mm10
-rw-r--r--chromium/base/ios/weak_nsobject.mm32
-rw-r--r--chromium/base/json/json_common.h42
-rw-r--r--chromium/base/json/json_parser.cc32
-rw-r--r--chromium/base/json/json_parser.h7
-rw-r--r--chromium/base/json/json_perftest.cc8
-rw-r--r--chromium/base/json/json_reader.cc12
-rw-r--r--chromium/base/json/json_reader.h15
-rw-r--r--chromium/base/json/json_reader_fuzzer.cc22
-rw-r--r--chromium/base/json/json_writer.cc20
-rw-r--r--chromium/base/json/json_writer.h18
-rw-r--r--chromium/base/json/json_writer_unittest.cc48
-rw-r--r--chromium/base/linux_util.cc99
-rw-r--r--chromium/base/linux_util.h8
-rw-r--r--chromium/base/linux_util_unittest.cc76
-rw-r--r--chromium/base/logging.cc11
-rw-r--r--chromium/base/logging.h34
-rw-r--r--chromium/base/logging_unittest.cc8
-rw-r--r--chromium/base/mac/bind_objc_block_unittest.mm7
-rw-r--r--chromium/base/mac/foundation_util.mm3
-rw-r--r--chromium/base/mac/foundation_util_unittest.mm208
-rw-r--r--chromium/base/mac/objc_release_properties_unittest.mm9
-rw-r--r--chromium/base/mac/scoped_nsautorelease_pool.h2
-rw-r--r--chromium/base/mac/scoped_nsobject.h11
-rw-r--r--chromium/base/mac/scoped_nsobject_unittest.mm7
-rw-r--r--chromium/base/memory/scoped_refptr.h12
-rw-r--r--chromium/base/message_loop/message_loop.cc41
-rw-r--r--chromium/base/message_loop/message_loop.h57
-rw-r--r--chromium/base/message_loop/message_loop_unittest.cc25
-rw-r--r--chromium/base/message_loop/message_pump_io_ios_unittest.cc1
-rw-r--r--chromium/base/message_loop/message_pump_mac_unittest.mm14
-rw-r--r--chromium/base/message_loop/message_pump_perftest.cc9
-rw-r--r--chromium/base/metrics/histogram_macros.h2
-rw-r--r--chromium/base/native_library_win.cc65
-rw-r--r--chromium/base/numerics/safe_math_arm_impl.h8
-rw-r--r--chromium/base/process/kill.h7
-rw-r--r--chromium/base/process/kill_win.cc6
-rw-r--r--chromium/base/process/launch_win.cc8
-rw-r--r--chromium/base/process/process_unittest.cc16
-rw-r--r--chromium/base/profiler/register_context.h19
-rw-r--r--chromium/base/profiler/stack_copier.h1
-rw-r--r--chromium/base/profiler/stack_copier_signal.cc28
-rw-r--r--chromium/base/profiler/stack_copier_signal.h36
-rw-r--r--chromium/base/profiler/stack_copier_suspend.cc82
-rw-r--r--chromium/base/profiler/stack_copier_suspend.h38
-rw-r--r--chromium/base/profiler/stack_copier_suspend_unittest.cc201
-rw-r--r--chromium/base/profiler/stack_copier_unittest.cc20
-rw-r--r--chromium/base/profiler/stack_sampler_android.cc4
-rw-r--r--chromium/base/profiler/stack_sampler_impl.cc77
-rw-r--r--chromium/base/profiler/stack_sampler_impl.h15
-rw-r--r--chromium/base/profiler/stack_sampler_impl_unittest.cc137
-rw-r--r--chromium/base/profiler/stack_sampler_mac.cc6
-rw-r--r--chromium/base/profiler/stack_sampler_win.cc6
-rw-r--r--chromium/base/profiler/suspendable_thread_delegate.h59
-rw-r--r--chromium/base/profiler/suspendable_thread_delegate_mac.cc (renamed from chromium/base/profiler/thread_delegate_mac.cc)28
-rw-r--r--chromium/base/profiler/suspendable_thread_delegate_mac.h (renamed from chromium/base/profiler/thread_delegate_mac.h)27
-rw-r--r--chromium/base/profiler/suspendable_thread_delegate_win.cc (renamed from chromium/base/profiler/thread_delegate_win.cc)31
-rw-r--r--chromium/base/profiler/suspendable_thread_delegate_win.h (renamed from chromium/base/profiler/thread_delegate_win.h)27
-rw-r--r--chromium/base/profiler/thread_delegate.h37
-rw-r--r--chromium/base/profiler/thread_delegate_android.cc59
-rw-r--r--chromium/base/profiler/thread_delegate_android.h25
-rw-r--r--chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc35
-rw-r--r--chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h4
-rw-r--r--chromium/base/strings/string_number_conversions.cc21
-rw-r--r--chromium/base/strings/string_number_conversions.h3
-rw-r--r--chromium/base/strings/string_piece.cc5
-rw-r--r--chromium/base/strings/string_split.cc160
-rw-r--r--chromium/base/strings/string_split.h26
-rw-r--r--chromium/base/strings/string_util.cc62
-rw-r--r--chromium/base/strings/string_util.h47
-rw-r--r--chromium/base/strings/string_util_unittest.cc7
-rw-r--r--chromium/base/strings/string_util_win.h13
-rw-r--r--chromium/base/strings/stringprintf.cc48
-rw-r--r--chromium/base/strings/stringprintf.h14
-rw-r--r--chromium/base/strings/stringprintf_unittest.cc45
-rw-r--r--chromium/base/syslog_logging.cc6
-rw-r--r--chromium/base/system/sys_info.h5
-rw-r--r--chromium/base/system/sys_info_ios.mm40
-rw-r--r--chromium/base/system/sys_info_win.cc6
-rw-r--r--chromium/base/task/post_job.cc94
-rw-r--r--chromium/base/task/post_job.h100
-rw-r--r--chromium/base/task/post_job_unittest.cc40
-rw-r--r--chromium/base/task/post_task.cc57
-rw-r--r--chromium/base/task/post_task.h47
-rw-r--r--chromium/base/task/post_task_unittest.cc157
-rw-r--r--chromium/base/task/promise/abstract_promise.cc67
-rw-r--r--chromium/base/task/promise/abstract_promise.h208
-rw-r--r--chromium/base/task/promise/abstract_promise_unittest.cc13
-rw-r--r--chromium/base/task/promise/dependent_list.h2
-rw-r--r--chromium/base/task/promise/finally_executor.h12
-rw-r--r--chromium/base/task/promise/helpers.cc50
-rw-r--r--chromium/base/task/promise/helpers.h146
-rw-r--r--chromium/base/task/promise/helpers_unittest.cc17
-rw-r--r--chromium/base/task/promise/no_op_promise_executor.cc13
-rw-r--r--chromium/base/task/promise/no_op_promise_executor.h8
-rw-r--r--chromium/base/task/promise/post_task_executor.h12
-rw-r--r--chromium/base/task/promise/post_task_executor_unittest.cc22
-rw-r--r--chromium/base/task/promise/promise.h335
-rw-r--r--chromium/base/task/promise/promise_unittest.cc88
-rw-r--r--chromium/base/task/promise/promise_value.h6
-rw-r--r--chromium/base/task/promise/then_and_catch_executor.cc18
-rw-r--r--chromium/base/task/promise/then_and_catch_executor.h87
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager.h19
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl.cc84
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl.h19
-rw-r--r--chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc16
-rw-r--r--chromium/base/task/sequence_manager/sequenced_task_source.h8
-rw-r--r--chromium/base/task/sequence_manager/task_queue_impl.cc14
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector.cc9
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector.h6
-rw-r--r--chromium/base/task/sequence_manager/task_queue_selector_unittest.cc45
-rw-r--r--chromium/base/task/sequence_manager/tasks.cc15
-rw-r--r--chromium/base/task/sequence_manager/tasks.h14
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_impl.cc4
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc4
-rw-r--r--chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc18
-rw-r--r--chromium/base/task/sequence_manager/work_queue.cc10
-rw-r--r--chromium/base/task/sequence_manager/work_queue.h5
-rw-r--r--chromium/base/task/sequence_manager/work_queue_sets.cc13
-rw-r--r--chromium/base/task/sequence_manager/work_queue_sets.h6
-rw-r--r--chromium/base/task/sequence_manager/work_queue_sets_unittest.cc30
-rw-r--r--chromium/base/task/sequence_manager/work_queue_unittest.cc30
-rw-r--r--chromium/base/task/simple_task_executor.cc62
-rw-r--r--chromium/base/task/simple_task_executor.h52
-rw-r--r--chromium/base/task/single_thread_task_executor.cc3
-rw-r--r--chromium/base/task/single_thread_task_executor.h2
-rw-r--r--chromium/base/task/single_thread_task_executor_unittest.cc58
-rw-r--r--chromium/base/task/task_executor.cc19
-rw-r--r--chromium/base/task/task_executor.h7
-rw-r--r--chromium/base/task/task_traits.h38
-rw-r--r--chromium/base/task/task_traits_unittest.nc2
-rw-r--r--chromium/base/task/thread_pool/historical_histogram_data.md92
-rw-r--r--chromium/base/task/thread_pool/initialization_util.cc3
-rw-r--r--chromium/base/task/thread_pool/job_task_source.cc330
-rw-r--r--chromium/base/task/thread_pool/job_task_source.h140
-rw-r--r--chromium/base/task/thread_pool/job_task_source_unittest.cc119
-rw-r--r--chromium/base/task/thread_pool/pooled_task_runner_delegate.h4
-rw-r--r--chromium/base/task/thread_pool/priority_queue.cc8
-rw-r--r--chromium/base/task/thread_pool/priority_queue.h2
-rw-r--r--chromium/base/task/thread_pool/priority_queue_unittest.cc14
-rw-r--r--chromium/base/task/thread_pool/sequence.cc26
-rw-r--r--chromium/base/task/thread_pool/sequence.h3
-rw-r--r--chromium/base/task/thread_pool/sequence_unittest.cc3
-rw-r--r--chromium/base/task/thread_pool/service_thread.cc28
-rw-r--r--chromium/base/task/thread_pool/service_thread_unittest.cc8
-rw-r--r--chromium/base/task/thread_pool/task_source.cc6
-rw-r--r--chromium/base/task/thread_pool/task_source.h23
-rw-r--r--chromium/base/task/thread_pool/task_tracker.cc316
-rw-r--r--chromium/base/task/thread_pool/task_tracker.h46
-rw-r--r--chromium/base/task/thread_pool/task_tracker_unittest.cc16
-rw-r--r--chromium/base/task/thread_pool/test_utils.cc5
-rw-r--r--chromium/base/task/thread_pool/test_utils.h1
-rw-r--r--chromium/base/task/thread_pool/thread_group.cc4
-rw-r--r--chromium/base/task/thread_pool/thread_group.h2
-rw-r--r--chromium/base/task/thread_pool/thread_group_impl.cc123
-rw-r--r--chromium/base/task/thread_pool/thread_group_impl.h5
-rw-r--r--chromium/base/task/thread_pool/thread_group_unittest.cc76
-rw-r--r--chromium/base/task/thread_pool/thread_pool_impl.cc27
-rw-r--r--chromium/base/task/thread_pool/thread_pool_impl.h5
-rw-r--r--chromium/base/task_runner.cc15
-rw-r--r--chromium/base/task_runner.h9
-rw-r--r--chromium/base/test/BUILD.gn32
-rw-r--r--chromium/base/threading/platform_thread_unittest.cc7
-rw-r--r--chromium/base/threading/platform_thread_win.cc50
-rw-r--r--chromium/base/threading/platform_thread_win.h4
-rw-r--r--chromium/base/threading/platform_thread_win_unittest.cc20
-rw-r--r--chromium/base/threading/sequenced_task_runner_handle.cc6
-rw-r--r--chromium/base/threading/thread.cc97
-rw-r--r--chromium/base/threading/thread.h20
-rw-r--r--chromium/base/threading/thread_perftest.cc1
-rw-r--r--chromium/base/threading/thread_restrictions.h17
-rw-r--r--chromium/base/threading/thread_task_runner_handle.cc7
-rw-r--r--chromium/base/threading/thread_unittest.cc43
-rw-r--r--chromium/base/time/time.cc46
-rw-r--r--chromium/base/time/time.h121
-rw-r--r--chromium/base/time/time_exploded_posix.cc14
-rw-r--r--chromium/base/time/time_unittest.cc4
-rw-r--r--chromium/base/time/time_win_unittest.cc6
-rw-r--r--chromium/base/trace_event/OWNERS1
-rw-r--r--chromium/base/trace_event/blame_context.cc6
-rw-r--r--chromium/base/trace_event/builtin_categories.h6
-rw-r--r--chromium/base/trace_event/malloc_dump_provider.cc5
-rw-r--r--chromium/base/trace_event/memory_dump_manager.cc3
-rw-r--r--chromium/base/trace_event/memory_dump_manager_unittest.cc115
-rw-r--r--chromium/base/trace_event/memory_dump_request_args.h9
-rw-r--r--chromium/base/trace_event/memory_infra_background_whitelist.cc6
-rw-r--r--chromium/base/trace_event/trace_config.cc73
-rw-r--r--chromium/base/trace_event/trace_config.h14
-rw-r--r--chromium/base/trace_event/trace_config_unittest.cc20
-rw-r--r--chromium/base/trace_event/trace_log.cc2
-rw-r--r--chromium/base/trace_event/trace_log.h1
-rw-r--r--chromium/base/trace_event/traced_value.cc12
-rw-r--r--chromium/base/unguessable_token.h30
-rw-r--r--chromium/base/util/memory_pressure/BUILD.gn13
-rw-r--r--chromium/base/util/memory_pressure/fake_memory_pressure_monitor.cc (renamed from chromium/base/memory/fake_memory_pressure_monitor.cc)8
-rw-r--r--chromium/base/util/memory_pressure/fake_memory_pressure_monitor.h (renamed from chromium/base/memory/fake_memory_pressure_monitor.h)20
-rw-r--r--chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc17
-rw-r--r--chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h3
-rw-r--r--chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc1
-rw-r--r--chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc3
-rw-r--r--chromium/base/values.cc88
-rw-r--r--chromium/base/values.h57
-rw-r--r--chromium/base/values_unittest.cc129
-rw-r--r--chromium/base/version.cc2
-rw-r--r--chromium/base/version.h2
-rw-r--r--chromium/base/win/com_init_check_hook.h5
-rwxr-xr-xchromium/base/win/embedded_i18n/create_string_rc.py2
-rw-r--r--chromium/base/win/embedded_i18n/language_selector.cc129
-rw-r--r--chromium/base/win/embedded_i18n/language_selector.h20
-rw-r--r--chromium/base/win/embedded_i18n/language_selector_unittest.cc173
-rw-r--r--chromium/base/win/enum_variant.cc39
-rw-r--r--chromium/base/win/enum_variant.h29
-rw-r--r--chromium/base/win/enum_variant_unittest.cc128
-rw-r--r--chromium/base/win/i18n.cc19
-rw-r--r--chromium/base/win/i18n.h6
-rw-r--r--chromium/base/win/i18n_unittest.cc8
-rw-r--r--chromium/base/win/iunknown_impl.cc42
-rw-r--r--chromium/base/win/iunknown_impl.h38
-rw-r--r--chromium/base/win/iunknown_impl_unittest.cc49
-rw-r--r--chromium/base/win/object_watcher.cc21
-rw-r--r--chromium/base/win/object_watcher.h17
-rw-r--r--chromium/base/win/pe_image_unittest.cc16
-rw-r--r--chromium/base/win/registry.cc148
-rw-r--r--chromium/base/win/registry.h66
-rw-r--r--chromium/base/win/registry_unittest.cc146
-rw-r--r--chromium/base/win/scoped_bstr.cc8
-rw-r--r--chromium/base/win/scoped_bstr.h5
-rw-r--r--chromium/base/win/scoped_bstr_unittest.cc14
-rw-r--r--chromium/base/win/shortcut.cc31
-rw-r--r--chromium/base/win/shortcut.h15
-rw-r--r--chromium/base/win/shortcut_unittest.cc29
-rw-r--r--chromium/base/win/static_constants.cc13
-rw-r--r--chromium/base/win/static_constants.h21
-rw-r--r--chromium/base/win/win_client_metrics.h41
-rw-r--r--chromium/base/win/win_util.cc112
-rw-r--r--chromium/base/win/win_util.h23
-rw-r--r--chromium/base/win/win_util_unittest.cc27
-rw-r--r--chromium/base/win/windows_types.h3
-rw-r--r--chromium/base/win/windows_version.cc33
-rw-r--r--chromium/base/win/wmi.cc53
-rw-r--r--chromium/base/win/wmi.h19
-rw-r--r--chromium/base/win/wmi_unittest.cc6
302 files changed, 7142 insertions, 4148 deletions
diff --git a/chromium/base/BUILD.gn b/chromium/base/BUILD.gn
index c093596dba0..200b8daecfb 100644
--- a/chromium/base/BUILD.gn
+++ b/chromium/base/BUILD.gn
@@ -34,6 +34,7 @@ import("//build/nocompile.gni")
import("//build/timestamp.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
import("//testing/test.gni")
+import("//third_party/icu/config.gni")
if (is_mac) {
# Used to generate fuzzer corpus :base_mach_port_rendezvous_convert_corpus.
@@ -317,6 +318,7 @@ jumbo_component("base") {
"ios/scoped_critical_action.mm",
"ios/weak_nsobject.h",
"ios/weak_nsobject.mm",
+ "json/json_common.h",
"json/json_file_value_serializer.cc",
"json/json_file_value_serializer.h",
"json/json_parser.cc",
@@ -618,6 +620,10 @@ jumbo_component("base") {
"profiler/stack_buffer.h",
"profiler/stack_copier.cc",
"profiler/stack_copier.h",
+ "profiler/stack_copier_signal.cc",
+ "profiler/stack_copier_signal.h",
+ "profiler/stack_copier_suspend.cc",
+ "profiler/stack_copier_suspend.h",
"profiler/stack_sampler.cc",
"profiler/stack_sampler.h",
"profiler/stack_sampler_android.cc",
@@ -627,13 +633,14 @@ jumbo_component("base") {
"profiler/stack_sampler_win.cc",
"profiler/stack_sampling_profiler.cc",
"profiler/stack_sampling_profiler.h",
+ "profiler/suspendable_thread_delegate.h",
+ "profiler/suspendable_thread_delegate_mac.cc",
+ "profiler/suspendable_thread_delegate_mac.h",
+ "profiler/suspendable_thread_delegate_win.cc",
+ "profiler/suspendable_thread_delegate_win.h",
"profiler/thread_delegate.h",
"profiler/thread_delegate_android.cc",
"profiler/thread_delegate_android.h",
- "profiler/thread_delegate_mac.cc",
- "profiler/thread_delegate_mac.h",
- "profiler/thread_delegate_win.cc",
- "profiler/thread_delegate_win.h",
"profiler/unwinder.h",
"rand_util.cc",
"rand_util.h",
@@ -810,6 +817,8 @@ jumbo_component("base") {
"task/sequence_manager/work_queue.h",
"task/sequence_manager/work_queue_sets.cc",
"task/sequence_manager/work_queue_sets.h",
+ "task/simple_task_executor.cc",
+ "task/simple_task_executor.h",
"task/single_thread_task_executor.cc",
"task/single_thread_task_executor.h",
"task/single_thread_task_runner_thread_mode.h",
@@ -1065,8 +1074,6 @@ jumbo_component("base") {
"win/i18n.h",
"win/iat_patch_function.cc",
"win/iat_patch_function.h",
- "win/iunknown_impl.cc",
- "win/iunknown_impl.h",
"win/map.h",
"win/message_window.cc",
"win/message_window.h",
@@ -1244,6 +1251,7 @@ jumbo_component("base") {
"//base/allocator:buildflags",
"//base/third_party/double_conversion",
"//base/third_party/dynamic_annotations",
+ "//build:branding_buildflags",
"//third_party/modp_b64",
]
@@ -1366,6 +1374,8 @@ jumbo_component("base") {
"android/java_exception_reporter.h",
"android/java_handler_thread.cc",
"android/java_handler_thread.h",
+ "android/java_heap_dump_generator.cc",
+ "android/java_heap_dump_generator.h",
"android/java_runtime.cc",
"android/java_runtime.h",
"android/jni_android.cc",
@@ -2158,6 +2168,11 @@ static_library("base_static") {
]
if (is_win) {
+ sources += [
+ "win/static_constants.cc",
+ "win/static_constants.h",
+ ]
+
public_deps = [
"//base/win:pe_image",
]
@@ -2475,7 +2490,7 @@ test("base_unittests") {
"android/unguessable_token_android_unittest.cc",
"at_exit_unittest.cc",
"atomicops_unittest.cc",
- "auto_reset_unittests.cc",
+ "auto_reset_unittest.cc",
"barrier_closure_unittest.cc",
"base64_unittest.cc",
"base64url_unittest.cc",
@@ -2541,6 +2556,7 @@ test("base_unittests") {
"i18n/character_encoding_unittest.cc",
"i18n/file_util_icu_unittest.cc",
"i18n/icu_string_conversions_unittest.cc",
+ "i18n/icu_util_unittest.cc",
"i18n/message_formatter_unittest.cc",
"i18n/number_formatting_unittest.cc",
"i18n/rtl_unittest.cc",
@@ -2637,6 +2653,7 @@ test("base_unittests") {
"process/process_util_unittest.cc",
"profiler/metadata_recorder_unittest.cc",
"profiler/sample_metadata_unittest.cc",
+ "profiler/stack_copier_suspend_unittest.cc",
"profiler/stack_copier_unittest.cc",
"profiler/stack_sampler_impl_unittest.cc",
"profiler/stack_sampling_profiler_unittest.cc",
@@ -2685,6 +2702,7 @@ test("base_unittests") {
"task/common/operations_controller_unittest.cc",
"task/common/task_annotator_unittest.cc",
"task/lazy_task_runner_unittest.cc",
+ "task/post_job_unittest.cc",
"task/post_task_unittest.cc",
"task/promise/abstract_promise_unittest.cc",
"task/promise/dependent_list_unittest.cc",
@@ -2704,6 +2722,7 @@ test("base_unittests") {
"task/sequence_manager/work_deduplicator_unittest.cc",
"task/sequence_manager/work_queue_sets_unittest.cc",
"task/sequence_manager/work_queue_unittest.cc",
+ "task/single_thread_task_executor_unittest.cc",
"task/task_traits_extension_unittest.cc",
"task/task_traits_unittest.cc",
"task/thread_pool/can_run_policy_test.h",
@@ -2810,7 +2829,6 @@ test("base_unittests") {
"win/hstring_compare_unittest.cc",
"win/hstring_reference_unittest.cc",
"win/i18n_unittest.cc",
- "win/iunknown_impl_unittest.cc",
"win/map_unittest.cc",
"win/message_window_unittest.cc",
"win/object_watcher_unittest.cc",
@@ -2919,6 +2937,15 @@ test("base_unittests") {
]
}
+ if (icu_use_data_file) {
+ if (is_android) {
+ deps += [ "//third_party/icu:icu_extra_assets" ]
+ } else {
+ deps += [ "//third_party/icu:extra_icudata" ]
+ data += [ "$root_out_dir/icudtl_extra.dat" ]
+ }
+ }
+
if (is_ios) {
# ios does not use test_launcher to run gtests.
sources -= [
@@ -2985,7 +3012,10 @@ test("base_unittests") {
}
if (is_desktop_linux) {
- sources += [ "nix/xdg_util_unittest.cc" ]
+ sources += [
+ "linux_util_unittest.cc",
+ "nix/xdg_util_unittest.cc",
+ ]
}
if (!use_glib) {
@@ -3143,6 +3173,7 @@ if (is_android) {
"android/java/src/org/chromium/base/UnguessableToken.java",
"android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
"android/java/src/org/chromium/base/library_loader/LibraryPrefetcher.java",
+ "android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
"android/java/src/org/chromium/base/metrics/RecordHistogram.java",
"android/java/src/org/chromium/base/metrics/RecordUserAction.java",
"android/java/src/org/chromium/base/metrics/StatisticsRecorderAndroid.java",
@@ -3182,9 +3213,9 @@ if (is_android) {
deps = [
":jni_java",
"//third_party/android_deps:android_support_v4_java",
+ "//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/android_deps:com_android_support_collections_java",
"//third_party/android_deps:com_android_support_multidex_java",
- "//third_party/android_deps:com_android_support_support_annotations_java",
"//third_party/jsr-305:jsr_305_javalib",
]
@@ -3223,6 +3254,7 @@ if (is_android) {
"android/java/src/org/chromium/base/ObservableSupplier.java",
"android/java/src/org/chromium/base/ObservableSupplierImpl.java",
"android/java/src/org/chromium/base/PackageUtils.java",
+ "android/java/src/org/chromium/base/PackageManagerUtils.java",
"android/java/src/org/chromium/base/PathService.java",
"android/java/src/org/chromium/base/PathUtils.java",
"android/java/src/org/chromium/base/PiiElider.java",
@@ -3253,6 +3285,15 @@ if (is_android) {
"android/java/src/org/chromium/base/annotations/NativeClassQualifiedName.java",
"android/java/src/org/chromium/base/annotations/RemovableInRelease.java",
"android/java/src/org/chromium/base/annotations/UsedByReflection.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnLollipop.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnLollipopMR1.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnM.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnN.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnNMR1.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnO.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnOMR1.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnP.java",
+ "android/java/src/org/chromium/base/annotations/VerifiesOnQ.java",
"android/java/src/org/chromium/base/compat/ApiHelperForM.java",
"android/java/src/org/chromium/base/compat/ApiHelperForN.java",
"android/java/src/org/chromium/base/compat/ApiHelperForO.java",
@@ -3282,6 +3323,7 @@ if (is_android) {
"android/java/src/org/chromium/base/process_launcher/ChildProcessService.java",
"android/java/src/org/chromium/base/process_launcher/ChildProcessServiceDelegate.java",
"android/java/src/org/chromium/base/process_launcher/FileDescriptorInfo.java",
+ "android/java/src/org/chromium/base/memory/JavaHeapDumpGenerator.java",
"android/java/src/org/chromium/base/memory/MemoryPressureMonitor.java",
"android/java/src/org/chromium/base/memory/MemoryPressureCallback.java",
"android/java/src/org/chromium/base/memory/MemoryPressureUma.java",
@@ -3363,7 +3405,7 @@ if (is_android) {
":base_java",
":jni_java",
"//testing/android/reporter:reporter_java",
- "//third_party/android_deps:com_android_support_support_annotations_java",
+ "//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/android_deps:com_android_support_support_compat_java",
"//third_party/android_sdk:android_support_chromium_java",
"//third_party/android_sdk:android_test_base_java",
diff --git a/chromium/base/allocator/allocator.gni b/chromium/base/allocator/allocator.gni
index 9c82dd29ba9..62e03b364d8 100644
--- a/chromium/base/allocator/allocator.gni
+++ b/chromium/base/allocator/allocator.gni
@@ -2,11 +2,13 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build/config/chromecast_build.gni")
import("//build/config/sanitizers/sanitizers.gni")
# Temporarily disable tcmalloc on arm64 linux to get rid of compilation errors.
if (is_android || is_mac || is_ios || is_asan || is_lsan || is_tsan ||
- is_msan || is_win || is_fuchsia || (is_linux && target_cpu == "arm64")) {
+ is_msan || is_win || is_fuchsia || (is_linux && target_cpu == "arm64") ||
+ (is_cast_audio_only && target_cpu == "arm")) {
_default_allocator = "none"
} else {
_default_allocator = "tcmalloc"
diff --git a/chromium/base/allocator/debugallocation_shim.cc b/chromium/base/allocator/debugallocation_shim.cc
index 479cfcad72d..24addf9d099 100644
--- a/chromium/base/allocator/debugallocation_shim.cc
+++ b/chromium/base/allocator/debugallocation_shim.cc
@@ -7,9 +7,10 @@
// AFDO can mess with them. Better not to use AFDO there. This is a
// temporary hack. We will add a mechanism in the build system to
// avoid using -fauto-profile for tcmalloc files.
-#if !defined(__clang__) && (defined(OS_CHROMEOS) || __GNUC__ > 5)
+#if !defined(__clang__) && \
+ (defined(OS_CHROMEOS) || (__GNUC__ > 5 && __GNUC__ < 7))
// Note that this option only seems to be available in the chromeos GCC 4.9
-// toolchain, and stock GCC 5 and up.
+// toolchain, and stock GCC 5 upto 7.
#pragma GCC optimize ("no-auto-profile")
#endif
diff --git a/chromium/base/allocator/partition_allocator/partition_alloc.cc b/chromium/base/allocator/partition_allocator/partition_alloc.cc
index 4b6d55fdf35..297a6bbe76e 100644
--- a/chromium/base/allocator/partition_allocator/partition_alloc.cc
+++ b/chromium/base/allocator/partition_allocator/partition_alloc.cc
@@ -493,14 +493,14 @@ static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) {
size_t slot_index = (reinterpret_cast<char*>(entry) - ptr) / slot_size;
DCHECK(slot_index < num_slots);
slot_usage[slot_index] = 0;
- entry = internal::PartitionFreelistEntry::Transform(entry->next);
+ entry = internal::EncodedPartitionFreelistEntry::Decode(entry->next);
#if !defined(OS_WIN)
// If we have a slot where the masked freelist entry is 0, we can actually
// discard that freelist entry because touching a discarded page is
// guaranteed to return original content or 0. (Note that this optimization
// won't fire on big-endian machines because the masking function is
// negation.)
- if (!internal::PartitionFreelistEntry::Transform(entry))
+ if (!internal::PartitionFreelistEntry::Encode(entry))
last_slot = slot_index;
#endif
}
@@ -534,25 +534,33 @@ static size_t PartitionPurgePage(internal::PartitionPage* page, bool discard) {
DCHECK(truncated_slots > 0);
size_t num_new_entries = 0;
page->num_unprovisioned_slots += static_cast<uint16_t>(truncated_slots);
+
// Rewrite the freelist.
- internal::PartitionFreelistEntry** entry_ptr = &page->freelist_head;
+ internal::PartitionFreelistEntry* head = nullptr;
+ internal::PartitionFreelistEntry* back = head;
for (size_t slot_index = 0; slot_index < num_slots; ++slot_index) {
if (slot_usage[slot_index])
continue;
+
auto* entry = reinterpret_cast<internal::PartitionFreelistEntry*>(
ptr + (slot_size * slot_index));
- *entry_ptr = internal::PartitionFreelistEntry::Transform(entry);
- entry_ptr = reinterpret_cast<internal::PartitionFreelistEntry**>(entry);
+ if (!head) {
+ head = entry;
+ back = entry;
+ } else {
+ back->next = internal::PartitionFreelistEntry::Encode(entry);
+ back = entry;
+ }
num_new_entries++;
#if !defined(OS_WIN)
last_slot = slot_index;
#endif
}
- // Terminate the freelist chain.
- *entry_ptr = nullptr;
- // The freelist head is stored unmasked.
- page->freelist_head =
- internal::PartitionFreelistEntry::Transform(page->freelist_head);
+
+ page->freelist_head = head;
+ if (back)
+ back->next = internal::PartitionFreelistEntry::Encode(nullptr);
+
DCHECK(num_new_entries == num_slots - page->num_allocated_slots);
// Discard the memory.
DiscardSystemPages(begin_ptr, unprovisioned_bytes);
diff --git a/chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc b/chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc
index 85b782a5eef..bdd85f0ae1d 100644
--- a/chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc
+++ b/chromium/base/allocator/partition_allocator/partition_alloc_perftest.cc
@@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <atomic>
#include <vector>
+
#include "base/allocator/partition_allocator/partition_alloc.h"
+#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/timer/lap_timer.h"
-
#include "build/build_config.h"
-
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/perf/perf_test.h"
@@ -28,19 +29,47 @@ constexpr int kMultiBucketIncrement = 13;
// Final size is 24 + (13 * 22) = 310 bytes.
constexpr int kMultiBucketRounds = 22;
-class MemoryAllocationPerfTest : public testing::Test {
+class AllocatingThread : public PlatformThread::Delegate {
public:
- MemoryAllocationPerfTest()
- : timer_(kWarmupRuns, kTimeLimit, kTimeCheckInterval) {}
- void SetUp() override { alloc_.init(); }
- void TearDown() override {
- alloc_.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages |
- PartitionPurgeDiscardUnusedSystemPages);
+ explicit AllocatingThread(PartitionAllocatorGeneric* allocator)
+ : allocator_(allocator), should_stop_(false) {
+ PlatformThread::Create(0, this, &thread_handle_);
}
- LapTimer timer_;
- PartitionAllocatorGeneric alloc_;
+
+ ~AllocatingThread() override {
+ should_stop_ = true;
+ PlatformThread::Join(thread_handle_);
+ }
+
+ // Allocates and frees memory in a loop until |should_stop_| becomes true.
+ void ThreadMain() override {
+ uint64_t count = 0;
+ while (true) {
+ // Only check |should_stop_| every 2^15 iterations, as it is a
+ // sequentially consistent access, hence expensive.
+ if (count % (1 << 15) == 0 && should_stop_)
+ break;
+ void* data = allocator_->root()->Alloc(10, "");
+ allocator_->root()->Free(data);
+ count++;
+ }
+ }
+
+ PartitionAllocatorGeneric* allocator_;
+ std::atomic<bool> should_stop_;
+ PlatformThreadHandle thread_handle_;
};
+void DisplayResults(const std::string& measurement,
+ const std::string& modifier,
+ size_t iterations_per_second) {
+ perf_test::PrintResult(measurement, modifier, "", iterations_per_second,
+ "runs/s", true);
+ perf_test::PrintResult(measurement, modifier, "",
+ static_cast<size_t>(1e9 / iterations_per_second),
+ "ns/run", true);
+}
+
class MemoryAllocationPerfNode {
public:
MemoryAllocationPerfNode* GetNext() const { return next_; }
@@ -59,110 +88,169 @@ class MemoryAllocationPerfNode {
MemoryAllocationPerfNode* next_ = nullptr;
};
-TEST_F(MemoryAllocationPerfTest, SingleBucket) {
- timer_.Reset();
- MemoryAllocationPerfNode* first = reinterpret_cast<MemoryAllocationPerfNode*>(
- alloc_.root()->Alloc(40, "<testing>"));
- MemoryAllocationPerfNode* cur = first;
- do {
- MemoryAllocationPerfNode* next =
+class MemoryAllocationPerfTest : public testing::Test {
+ public:
+ MemoryAllocationPerfTest()
+ : timer_(kWarmupRuns, kTimeLimit, kTimeCheckInterval) {}
+ void SetUp() override { alloc_.init(); }
+ void TearDown() override {
+ alloc_.root()->PurgeMemory(PartitionPurgeDecommitEmptyPages |
+ PartitionPurgeDiscardUnusedSystemPages);
+ }
+
+ protected:
+ void TestSingleBucket() {
+ MemoryAllocationPerfNode* first =
reinterpret_cast<MemoryAllocationPerfNode*>(
alloc_.root()->Alloc(40, "<testing>"));
- CHECK_NE(next, nullptr);
- cur->SetNext(next);
- cur = next;
- timer_.NextLap();
- } while (!timer_.HasTimeLimitExpired());
- // next_ = nullptr only works if the class constructor is called (it's not
- // called in this case because then we can allocate arbitrary-length
- // payloads.)
- cur->SetNext(nullptr);
-
- MemoryAllocationPerfNode::FreeAll(first, alloc_);
-
- perf_test::PrintResult("MemoryAllocationPerfTest",
- " single bucket allocation (40 bytes)", "",
- timer_.LapsPerSecond(), "runs/s", true);
-}
-
-TEST_F(MemoryAllocationPerfTest, SingleBucketWithFree) {
- timer_.Reset();
- // Allocate an initial element to make sure the bucket stays set up.
- void* elem = alloc_.root()->Alloc(40, "<testing>");
- do {
- void* cur = alloc_.root()->Alloc(40, "<testing>");
- CHECK_NE(cur, nullptr);
- alloc_.root()->Free(cur);
- timer_.NextLap();
- } while (!timer_.HasTimeLimitExpired());
-
- alloc_.root()->Free(elem);
- perf_test::PrintResult("MemoryAllocationPerfTest",
- " single bucket allocation + free (40 bytes)", "",
- timer_.LapsPerSecond(), "runs/s", true);
-}
-// Failing on Nexus5x: crbug.com/949838
-#if defined(OS_ANDROID)
-#define MAYBE_MultiBucket DISABLED_MultiBucket
-#else
-#define MAYBE_MultiBucket MultiBucket
-#endif
-TEST_F(MemoryAllocationPerfTest, MAYBE_MultiBucket) {
- timer_.Reset();
- MemoryAllocationPerfNode* first = reinterpret_cast<MemoryAllocationPerfNode*>(
- alloc_.root()->Alloc(40, "<testing>"));
- MemoryAllocationPerfNode* cur = first;
- do {
- for (int i = 0; i < kMultiBucketRounds; i++) {
+ timer_.Reset();
+ MemoryAllocationPerfNode* cur = first;
+ do {
MemoryAllocationPerfNode* next =
- reinterpret_cast<MemoryAllocationPerfNode*>(alloc_.root()->Alloc(
- kMultiBucketMinimumSize + (i * kMultiBucketIncrement),
- "<testing>"));
+ reinterpret_cast<MemoryAllocationPerfNode*>(
+ alloc_.root()->Alloc(40, "<testing>"));
CHECK_NE(next, nullptr);
cur->SetNext(next);
cur = next;
- }
- timer_.NextLap();
- } while (!timer_.HasTimeLimitExpired());
- cur->SetNext(nullptr);
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
+ // next_ = nullptr only works if the class constructor is called (it's not
+ // called in this case because then we can allocate arbitrary-length
+ // payloads.)
+ cur->SetNext(nullptr);
- MemoryAllocationPerfNode::FreeAll(first, alloc_);
+ MemoryAllocationPerfNode::FreeAll(first, alloc_);
- perf_test::PrintResult("MemoryAllocationPerfTest", " multi-bucket allocation",
- "", timer_.LapsPerSecond() * kMultiBucketRounds,
- "runs/s", true);
-}
+ DisplayResults("MemoryAllocationPerfTest",
+ " single bucket allocation (40 bytes)",
+ timer_.LapsPerSecond());
+ }
-TEST_F(MemoryAllocationPerfTest, MultiBucketWithFree) {
- timer_.Reset();
- std::vector<void*> elems;
- // Do an initial round of allocation to make sure that the buckets stay in use
- // (and aren't accidentally released back to the OS).
- for (int i = 0; i < kMultiBucketRounds; i++) {
- void* cur = alloc_.root()->Alloc(
- kMultiBucketMinimumSize + (i * kMultiBucketIncrement), "<testing>");
- CHECK_NE(cur, nullptr);
- elems.push_back(cur);
+ void TestSingleBucketWithFree() {
+ // Allocate an initial element to make sure the bucket stays set up.
+ void* elem = alloc_.root()->Alloc(40, "<testing>");
+
+ timer_.Reset();
+ do {
+ void* cur = alloc_.root()->Alloc(40, "<testing>");
+ CHECK_NE(cur, nullptr);
+ alloc_.root()->Free(cur);
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
+
+ alloc_.root()->Free(elem);
+ DisplayResults("MemoryAllocationPerfTest",
+ " single bucket allocation + free (40 bytes)",
+ timer_.LapsPerSecond());
+ }
+
+ void TestMultiBucket() {
+ MemoryAllocationPerfNode* first =
+ reinterpret_cast<MemoryAllocationPerfNode*>(
+ alloc_.root()->Alloc(40, "<testing>"));
+ MemoryAllocationPerfNode* cur = first;
+
+ timer_.Reset();
+ do {
+ for (int i = 0; i < kMultiBucketRounds; i++) {
+ MemoryAllocationPerfNode* next =
+ reinterpret_cast<MemoryAllocationPerfNode*>(alloc_.root()->Alloc(
+ kMultiBucketMinimumSize + (i * kMultiBucketIncrement),
+ "<testing>"));
+ CHECK_NE(next, nullptr);
+ cur->SetNext(next);
+ cur = next;
+ }
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
+ cur->SetNext(nullptr);
+
+ MemoryAllocationPerfNode::FreeAll(first, alloc_);
+
+ DisplayResults("MemoryAllocationPerfTest", " multi-bucket allocation",
+ timer_.LapsPerSecond() * kMultiBucketRounds);
}
- do {
+ void TestMultiBucketWithFree() {
+ std::vector<void*> elems;
+ elems.reserve(kMultiBucketRounds);
+ // Do an initial round of allocation to make sure that the buckets stay in
+ // use (and aren't accidentally released back to the OS).
for (int i = 0; i < kMultiBucketRounds; i++) {
void* cur = alloc_.root()->Alloc(
kMultiBucketMinimumSize + (i * kMultiBucketIncrement), "<testing>");
CHECK_NE(cur, nullptr);
- alloc_.root()->Free(cur);
+ elems.push_back(cur);
+ }
+
+ timer_.Reset();
+ do {
+ for (int i = 0; i < kMultiBucketRounds; i++) {
+ void* cur = alloc_.root()->Alloc(
+ kMultiBucketMinimumSize + (i * kMultiBucketIncrement), "<testing>");
+ CHECK_NE(cur, nullptr);
+ alloc_.root()->Free(cur);
+ }
+ timer_.NextLap();
+ } while (!timer_.HasTimeLimitExpired());
+
+ for (void* ptr : elems) {
+ alloc_.root()->Free(ptr);
}
- timer_.NextLap();
- } while (!timer_.HasTimeLimitExpired());
- for (void* ptr : elems) {
- alloc_.root()->Free(ptr);
+ DisplayResults("MemoryAllocationPerfTest",
+ " multi-bucket allocation + free",
+ timer_.LapsPerSecond() * kMultiBucketRounds);
}
- perf_test::PrintResult(
- "MemoryAllocationPerfTest", " multi-bucket allocation + free", "",
- timer_.LapsPerSecond() * kMultiBucketRounds, "runs/s", true);
+ LapTimer timer_;
+ PartitionAllocatorGeneric alloc_;
+};
+
+TEST_F(MemoryAllocationPerfTest, SingleBucket) {
+ TestSingleBucket();
+}
+
+TEST_F(MemoryAllocationPerfTest, SingleBucketWithCompetingThread) {
+ AllocatingThread t(&alloc_);
+ TestSingleBucket();
+}
+
+TEST_F(MemoryAllocationPerfTest, SingleBucketWithFree) {
+ TestSingleBucketWithFree();
+}
+
+TEST_F(MemoryAllocationPerfTest, SingleBucketWithFreeWithCompetingThread) {
+ AllocatingThread t(&alloc_);
+ TestSingleBucketWithFree();
+}
+
+// Failing on Nexus5x: crbug.com/949838
+#if defined(OS_ANDROID)
+#define MAYBE_MultiBucket DISABLED_MultiBucket
+#define MAYBE_MultiBucketWithCompetingThread \
+ DISABLED_MultiBucketWithCompetingThread
+#else
+#define MAYBE_MultiBucket MultiBucket
+#define MAYBE_MultiBucketWithCompetingThread MultiBucketWithCompetingThread
+#endif
+TEST_F(MemoryAllocationPerfTest, MAYBE_MultiBucket) {
+ TestMultiBucket();
+}
+
+TEST_F(MemoryAllocationPerfTest, MAYBE_MultiBucketWithCompetingThread) {
+ AllocatingThread t(&alloc_);
+ TestMultiBucket();
+}
+
+TEST_F(MemoryAllocationPerfTest, MultiBucketWithFree) {
+ TestMultiBucketWithFree();
+}
+
+TEST_F(MemoryAllocationPerfTest, MultiBucketWithFreeWithCompetingThread) {
+ AllocatingThread t(&alloc_);
+ TestMultiBucketWithFree();
}
} // anonymous namespace
diff --git a/chromium/base/allocator/partition_allocator/partition_bucket.cc b/chromium/base/allocator/partition_allocator/partition_bucket.cc
index 8e54f552b22..2e239a80fa7 100644
--- a/chromium/base/allocator/partition_allocator/partition_bucket.cc
+++ b/chromium/base/allocator/partition_allocator/partition_bucket.cc
@@ -79,7 +79,7 @@ ALWAYS_INLINE PartitionPage* PartitionDirectMap(PartitionRootBase* root,
page->freelist_head = reinterpret_cast<PartitionFreelistEntry*>(slot);
PartitionFreelistEntry* next_entry =
reinterpret_cast<PartitionFreelistEntry*>(slot);
- next_entry->next = PartitionFreelistEntry::Transform(nullptr);
+ next_entry->next = PartitionFreelistEntry::Encode(nullptr);
DCHECK(!bucket->active_pages_head);
DCHECK(!bucket->empty_pages_head);
@@ -394,10 +394,10 @@ ALWAYS_INLINE char* PartitionBucket::AllocAndFillFreelist(PartitionPage* page) {
freelist_pointer += size;
PartitionFreelistEntry* next_entry =
reinterpret_cast<PartitionFreelistEntry*>(freelist_pointer);
- entry->next = PartitionFreelistEntry::Transform(next_entry);
+ entry->next = PartitionFreelistEntry::Encode(next_entry);
entry = next_entry;
}
- entry->next = PartitionFreelistEntry::Transform(nullptr);
+ entry->next = PartitionFreelistEntry::Encode(nullptr);
} else {
page->freelist_head = nullptr;
}
@@ -555,7 +555,7 @@ void* PartitionBucket::SlowPathAlloc(PartitionRootBase* root,
if (LIKELY(new_page->freelist_head != nullptr)) {
PartitionFreelistEntry* entry = new_page->freelist_head;
PartitionFreelistEntry* new_head =
- PartitionFreelistEntry::Transform(entry->next);
+ EncodedPartitionFreelistEntry::Decode(entry->next);
new_page->freelist_head = new_head;
new_page->num_allocated_slots++;
return entry;
diff --git a/chromium/base/allocator/partition_allocator/partition_freelist_entry.h b/chromium/base/allocator/partition_allocator/partition_freelist_entry.h
index 7e3282ef412..ce3763b88d7 100644
--- a/chromium/base/allocator/partition_allocator/partition_freelist_entry.h
+++ b/chromium/base/allocator/partition_allocator/partition_freelist_entry.h
@@ -15,33 +15,56 @@
namespace base {
namespace internal {
-// TODO(ajwong): Introduce an EncodedFreelistEntry type and then replace
-// Transform() with Encode()/Decode() such that the API provides some static
-// type safety.
-//
-// https://crbug.com/787153
+struct EncodedPartitionFreelistEntry;
+
struct PartitionFreelistEntry {
- PartitionFreelistEntry* next;
+ EncodedPartitionFreelistEntry* next;
+
+ PartitionFreelistEntry() = delete;
+ ~PartitionFreelistEntry() = delete;
- static ALWAYS_INLINE PartitionFreelistEntry* Transform(
+ ALWAYS_INLINE static EncodedPartitionFreelistEntry* Encode(
PartitionFreelistEntry* ptr) {
-// We use bswap on little endian as a fast mask for two reasons:
-// 1) If an object is freed and its vtable used where the attacker doesn't
-// get the chance to run allocations between the free and use, the vtable
-// dereference is likely to fault.
-// 2) If the attacker has a linear buffer overflow and elects to try and
-// corrupt a freelist pointer, partial pointer overwrite attacks are
-// thwarted.
-// For big endian, similar guarantees are arrived at with a negation.
+ return reinterpret_cast<EncodedPartitionFreelistEntry*>(Transform(ptr));
+ }
+
+ private:
+ friend struct EncodedPartitionFreelistEntry;
+ static ALWAYS_INLINE void* Transform(void* ptr) {
+ // We use bswap on little endian as a fast mask for two reasons:
+ // 1) If an object is freed and its vtable used where the attacker doesn't
+ // get the chance to run allocations between the free and use, the vtable
+ // dereference is likely to fault.
+ // 2) If the attacker has a linear buffer overflow and elects to try and
+ // corrupt a freelist pointer, partial pointer overwrite attacks are
+ // thwarted.
+ // For big endian, similar guarantees are arrived at with a negation.
#if defined(ARCH_CPU_BIG_ENDIAN)
uintptr_t masked = ~reinterpret_cast<uintptr_t>(ptr);
#else
uintptr_t masked = ByteSwapUintPtrT(reinterpret_cast<uintptr_t>(ptr));
#endif
- return reinterpret_cast<PartitionFreelistEntry*>(masked);
+ return reinterpret_cast<void*>(masked);
+ }
+};
+
+struct EncodedPartitionFreelistEntry {
+ char scrambled[sizeof(PartitionFreelistEntry*)];
+
+ EncodedPartitionFreelistEntry() = delete;
+ ~EncodedPartitionFreelistEntry() = delete;
+
+ ALWAYS_INLINE static PartitionFreelistEntry* Decode(
+ EncodedPartitionFreelistEntry* ptr) {
+ return reinterpret_cast<PartitionFreelistEntry*>(
+ PartitionFreelistEntry::Transform(ptr));
}
};
+static_assert(sizeof(PartitionFreelistEntry) ==
+ sizeof(EncodedPartitionFreelistEntry),
+ "Should not have padding");
+
} // namespace internal
} // namespace base
diff --git a/chromium/base/allocator/partition_allocator/partition_page.h b/chromium/base/allocator/partition_allocator/partition_page.h
index 5a0e70f9711..d2e580bdda8 100644
--- a/chromium/base/allocator/partition_allocator/partition_page.h
+++ b/chromium/base/allocator/partition_allocator/partition_page.h
@@ -11,8 +11,27 @@
#include "base/allocator/partition_allocator/partition_bucket.h"
#include "base/allocator/partition_allocator/partition_cookie.h"
#include "base/allocator/partition_allocator/partition_freelist_entry.h"
+#include "base/allocator/partition_allocator/random.h"
#include "base/logging.h"
+namespace {
+
+// Returns true if we've hit the end of a random-length period. We don't want to
+// invoke `RandomValue` too often, because we call this function in a hot spot
+// (`Free`), and `RandomValue` incurs the cost of atomics.
+#if !DCHECK_IS_ON()
+bool RandomPeriod() {
+ static thread_local uint8_t counter = 0;
+ if (UNLIKELY(counter == 0)) {
+ counter = base::RandomValue();
+ }
+ counter--;
+ return counter == 0;
+}
+#endif
+
+} // namespace
+
namespace base {
namespace internal {
@@ -201,29 +220,35 @@ ALWAYS_INLINE size_t PartitionPage::get_raw_size() const {
}
ALWAYS_INLINE void PartitionPage::Free(void* ptr) {
-#if DCHECK_IS_ON()
size_t slot_size = this->bucket->slot_size;
const size_t raw_size = get_raw_size();
if (raw_size) {
slot_size = raw_size;
}
+#if DCHECK_IS_ON()
// If these asserts fire, you probably corrupted memory.
PartitionCookieCheckValue(ptr);
PartitionCookieCheckValue(reinterpret_cast<char*>(ptr) + slot_size -
kCookieSize);
memset(ptr, kFreedByte, slot_size);
+#else
+ // `memset` only once in a while.
+ if (UNLIKELY(RandomPeriod())) {
+ memset(ptr, kFreedByte, slot_size);
+ }
#endif
DCHECK(this->num_allocated_slots);
- CHECK(ptr != freelist_head); // Catches an immediate double free.
+ // Catches an immediate double free.
+ CHECK(ptr != freelist_head);
// Look for double free one level deeper in debug.
- DCHECK(!freelist_head || ptr != internal::PartitionFreelistEntry::Transform(
- freelist_head->next));
+ DCHECK(!freelist_head ||
+ ptr != EncodedPartitionFreelistEntry::Decode(freelist_head->next));
internal::PartitionFreelistEntry* entry =
static_cast<internal::PartitionFreelistEntry*>(ptr);
- entry->next = internal::PartitionFreelistEntry::Transform(freelist_head);
+ entry->next = internal::PartitionFreelistEntry::Encode(freelist_head);
freelist_head = entry;
--this->num_allocated_slots;
if (UNLIKELY(this->num_allocated_slots <= 0)) {
diff --git a/chromium/base/allocator/partition_allocator/partition_root_base.h b/chromium/base/allocator/partition_allocator/partition_root_base.h
index 9e971f9f712..a3f9175b3cb 100644
--- a/chromium/base/allocator/partition_allocator/partition_root_base.h
+++ b/chromium/base/allocator/partition_allocator/partition_root_base.h
@@ -107,8 +107,8 @@ ALWAYS_INLINE void* PartitionRootBase::AllocFromBucket(PartitionBucket* bucket,
// the size metadata.
DCHECK(page->get_raw_size() == 0);
internal::PartitionFreelistEntry* new_head =
- internal::PartitionFreelistEntry::Transform(
- static_cast<internal::PartitionFreelistEntry*>(ret)->next);
+ internal::EncodedPartitionFreelistEntry::Decode(
+ page->freelist_head->next);
page->freelist_head = new_head;
page->num_allocated_slots++;
} else {
diff --git a/chromium/base/allocator/partition_allocator/random.h b/chromium/base/allocator/partition_allocator/random.h
index 85cb66d21de..a9aaa7f6ccb 100644
--- a/chromium/base/allocator/partition_allocator/random.h
+++ b/chromium/base/allocator/partition_allocator/random.h
@@ -15,7 +15,7 @@ namespace base {
// `base::RandUint64` which is very unpredictable, but which is expensive due to
// the need to call into the kernel. Therefore this generator uses a fast,
// entirely user-space function after initialization.
-uint32_t RandomValue();
+BASE_EXPORT uint32_t RandomValue();
// Sets the seed for the random number generator to a known value, to cause the
// RNG to generate a predictable sequence of outputs. May be called multiple
diff --git a/chromium/base/android/jni_generator/BUILD.gn b/chromium/base/android/jni_generator/BUILD.gn
index dc671366bab..83d4b3ea3e5 100644
--- a/chromium/base/android/jni_generator/BUILD.gn
+++ b/chromium/base/android/jni_generator/BUILD.gn
@@ -111,5 +111,4 @@ java_annotation_processor("jni_processor") {
]
srcjar_deps = [ ":processor_args_java" ]
- jacoco_never_instrument = true
}
diff --git a/chromium/base/auto_reset.h b/chromium/base/auto_reset.h
index 0ab0af7bb21..d8ce87b600d 100644
--- a/chromium/base/auto_reset.h
+++ b/chromium/base/auto_reset.h
@@ -7,8 +7,6 @@
#include <utility>
-#include "base/macros.h"
-
// base::AutoReset<> is useful for setting a variable to a new value only within
// a particular scope. An base::AutoReset<> object resets a variable to its
// original value upon destruction, making it an alternative to writing
@@ -20,19 +18,23 @@
namespace base {
-template<typename T>
+template <typename T>
class AutoReset {
public:
- AutoReset(T* scoped_variable, T new_value)
+ template <typename U>
+ AutoReset(T* scoped_variable, U&& new_value)
: scoped_variable_(scoped_variable),
- original_value_(std::move(*scoped_variable)) {
- *scoped_variable_ = std::move(new_value);
- }
+ original_value_(
+ std::exchange(*scoped_variable_, std::forward<U>(new_value))) {}
AutoReset(AutoReset&& other)
- : scoped_variable_(other.scoped_variable_),
- original_value_(std::move(other.original_value_)) {
- other.scoped_variable_ = nullptr;
+ : scoped_variable_(std::exchange(other.scoped_variable_, nullptr)),
+ original_value_(std::move(other.original_value_)) {}
+
+ AutoReset& operator=(AutoReset&& rhs) {
+ scoped_variable_ = std::exchange(rhs.scoped_variable_, nullptr);
+ original_value_ = std::move(rhs.original_value_);
+ return *this;
}
~AutoReset() {
@@ -40,20 +42,9 @@ class AutoReset {
*scoped_variable_ = std::move(original_value_);
}
- AutoReset& operator=(AutoReset&& rhs) {
- if (this != &rhs) {
- scoped_variable_ = rhs.scoped_variable_;
- rhs.scoped_variable_ = nullptr;
- original_value_ = std::move(rhs.original_value_);
- }
- return *this;
- }
-
private:
T* scoped_variable_;
T original_value_;
-
- DISALLOW_COPY_AND_ASSIGN(AutoReset);
};
} // namespace base
diff --git a/chromium/base/auto_reset_unittests.cc b/chromium/base/auto_reset_unittest.cc
index d42c6a08b07..d42c6a08b07 100644
--- a/chromium/base/auto_reset_unittests.cc
+++ b/chromium/base/auto_reset_unittest.cc
diff --git a/chromium/base/big_endian.cc b/chromium/base/big_endian.cc
index 514581fe415..9e9e672e490 100644
--- a/chromium/base/big_endian.cc
+++ b/chromium/base/big_endian.cc
@@ -4,6 +4,7 @@
#include "base/big_endian.h"
+#include "base/numerics/checked_math.h"
#include "base/strings/string_piece.h"
namespace base {
@@ -59,6 +60,29 @@ bool BigEndianReader::ReadU64(uint64_t* value) {
return Read(value);
}
+template <typename T>
+bool BigEndianReader::ReadLengthPrefixed(base::StringPiece* out) {
+ T t_len;
+ if (!Read(&t_len))
+ return false;
+ size_t len = strict_cast<size_t>(t_len);
+ const char* original_ptr = ptr_;
+ if (!Skip(len)) {
+ ptr_ -= sizeof(T);
+ return false;
+ }
+ *out = base::StringPiece(original_ptr, len);
+ return true;
+}
+
+bool BigEndianReader::ReadU8LengthPrefixed(base::StringPiece* out) {
+ return ReadLengthPrefixed<uint8_t>(out);
+}
+
+bool BigEndianReader::ReadU16LengthPrefixed(base::StringPiece* out) {
+ return ReadLengthPrefixed<uint16_t>(out);
+}
+
BigEndianWriter::BigEndianWriter(char* buf, size_t len)
: ptr_(buf), end_(ptr_ + len) {}
diff --git a/chromium/base/big_endian.h b/chromium/base/big_endian.h
index 8fea64f248d..96650a6955f 100644
--- a/chromium/base/big_endian.h
+++ b/chromium/base/big_endian.h
@@ -67,10 +67,26 @@ class BASE_EXPORT BigEndianReader {
bool ReadU32(uint32_t* value);
bool ReadU64(uint64_t* value);
+ // Reads a length-prefixed region:
+ // 1. reads a big-endian length L from the buffer;
+ // 2. sets |*out| to a StringPiece over the next L many bytes
+ // of the buffer (beyond the end of the bytes encoding the length); and
+ // 3. skips the main reader past this L-byte substring.
+ //
+ // Fails if reading a U8 or U16 fails, or if the parsed length is greater
+ // than the number of bytes remaining in the stream.
+ //
+ // On failure, leaves the stream at the same position
+ // as before the call.
+ bool ReadU8LengthPrefixed(base::StringPiece* out);
+ bool ReadU16LengthPrefixed(base::StringPiece* out);
+
private:
// Hidden to promote type safety.
template<typename T>
bool Read(T* v);
+ template <typename T>
+ bool ReadLengthPrefixed(base::StringPiece* out);
const char* ptr_;
const char* end_;
diff --git a/chromium/base/big_endian_unittest.cc b/chromium/base/big_endian_unittest.cc
index 7f8d9a556d8..07c208a3a1c 100644
--- a/chromium/base/big_endian_unittest.cc
+++ b/chromium/base/big_endian_unittest.cc
@@ -42,6 +42,64 @@ TEST(BigEndianReaderTest, ReadsValues) {
EXPECT_EQ(expected.data(), piece.data());
}
+TEST(BigEndianReaderTest, ReadsLengthPrefixedValues) {
+ {
+ char u8_prefixed_data[] = {8, 8, 9, 0xA, 0xB, 0xC, 0xD,
+ 0xE, 0xF, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E};
+ BigEndianReader reader(u8_prefixed_data, sizeof(u8_prefixed_data));
+
+ base::StringPiece piece;
+ ASSERT_TRUE(reader.ReadU8LengthPrefixed(&piece));
+ // |reader| should skip both a u8 and the length-8 length-prefixed field.
+ EXPECT_EQ(reader.ptr(), u8_prefixed_data + 9);
+ EXPECT_EQ(piece.size(), 8u);
+ EXPECT_EQ(piece.data(), u8_prefixed_data + 1);
+ }
+
+ {
+ char u16_prefixed_data[] = {0, 8, 0xD, 0xE, 0xF,
+ 0x1A, 0x2B, 0x3C, 0x4D, 0x5E};
+ BigEndianReader reader(u16_prefixed_data, sizeof(u16_prefixed_data));
+ base::StringPiece piece;
+ ASSERT_TRUE(reader.ReadU16LengthPrefixed(&piece));
+ // |reader| should skip both a u16 and the length-8 length-prefixed field.
+ EXPECT_EQ(reader.ptr(), u16_prefixed_data + 10);
+ EXPECT_EQ(piece.size(), 8u);
+ EXPECT_EQ(piece.data(), u16_prefixed_data + 2);
+
+ // With no data left, we shouldn't be able to
+ // read another u8 length prefix (or a u16 length prefix,
+ // for that matter).
+ EXPECT_FALSE(reader.ReadU8LengthPrefixed(&piece));
+ EXPECT_FALSE(reader.ReadU16LengthPrefixed(&piece));
+ }
+
+ {
+ // Make sure there's no issue reading a zero-value length prefix.
+ char u16_prefixed_data[3] = {};
+ BigEndianReader reader(u16_prefixed_data, sizeof(u16_prefixed_data));
+ base::StringPiece piece;
+ ASSERT_TRUE(reader.ReadU16LengthPrefixed(&piece));
+ EXPECT_EQ(reader.ptr(), u16_prefixed_data + 2);
+ EXPECT_EQ(piece.data(), u16_prefixed_data + 2);
+ EXPECT_EQ(piece.size(), 0u);
+ }
+}
+
+TEST(BigEndianReaderTest, LengthPrefixedReadsFailGracefully) {
+ // We can't read 0xF (or, for that matter, 0xF8) bytes after the length
+ // prefix: there isn't enough data.
+ char data[] = {0xF, 8, 9, 0xA, 0xB, 0xC, 0xD,
+ 0xE, 0xF, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E};
+ BigEndianReader reader(data, sizeof(data));
+ base::StringPiece piece;
+ EXPECT_FALSE(reader.ReadU8LengthPrefixed(&piece));
+ EXPECT_EQ(data, reader.ptr());
+
+ EXPECT_FALSE(reader.ReadU16LengthPrefixed(&piece));
+ EXPECT_EQ(data, reader.ptr());
+}
+
TEST(BigEndianReaderTest, RespectsLength) {
char data[8];
char buf[2];
diff --git a/chromium/base/bind_internal.h b/chromium/base/bind_internal.h
index b2f1c3c4c9c..02c06b7b584 100644
--- a/chromium/base/bind_internal.h
+++ b/chromium/base/bind_internal.h
@@ -354,10 +354,9 @@ template <typename Functor, typename SFINAE>
struct FunctorTraits;
// For empty callable types.
-// This specialization is intended to allow binding captureless lambdas by
-// base::Bind(), based on the fact that captureless lambdas are empty while
-// capturing lambdas are not. This also allows any functors as far as it's an
-// empty class.
+// This specialization is intended to allow binding captureless lambdas, based
+// on the fact that captureless lambdas are empty while capturing lambdas are
+// not. This also allows any functors as far as it's an empty class.
// Example:
//
// // Captureless lambdas are allowed.
@@ -791,10 +790,10 @@ BanUnconstructedRefCountedReceiver(const Receiver& receiver, Unused&&...) {
//
// scoped_refptr<Foo> oo = Foo::Create();
DCHECK(receiver->HasAtLeastOneRef())
- << "base::Bind() refuses to create the first reference to ref-counted "
- "objects. That is typically happens around PostTask() in their "
- "constructor, and such objects can be destroyed before `new` returns "
- "if the task resolves fast enough.";
+ << "base::Bind{Once,Repeating}() refuses to create the first reference "
+ "to ref-counted objects. That typically happens around PostTask() in "
+ "their constructor, and such objects can be destroyed before `new` "
+ "returns if the task resolves fast enough.";
}
// BindState<>
@@ -917,7 +916,7 @@ using MakeBindStateType =
// };
//
// WeakPtr<Foo> oo = nullptr;
-// base::Bind(&Foo::bar, oo).Run();
+// base::BindOnce(&Foo::bar, oo).Run();
template <typename T>
struct IsWeakReceiver : std::false_type {};
diff --git a/chromium/base/callback.h b/chromium/base/callback.h
index e08f9b872b7..1427faaaea9 100644
--- a/chromium/base/callback.h
+++ b/chromium/base/callback.h
@@ -140,7 +140,7 @@ class RepeatingCallback<R(Args...)> : public internal::CallbackBaseCopyable {
RepeatingCallback cb = std::move(*this);
PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(cb.polymorphic_invoke());
- return f(cb.bind_state_.get(), std::forward<Args>(args)...);
+ return f(std::move(cb).bind_state_.get(), std::forward<Args>(args)...);
}
};
diff --git a/chromium/base/callback_helpers_unittest.cc b/chromium/base/callback_helpers_unittest.cc
index 00b2ef59ebe..78c7ad0ad5d 100644
--- a/chromium/base/callback_helpers_unittest.cc
+++ b/chromium/base/callback_helpers_unittest.cc
@@ -13,8 +13,8 @@
namespace {
TEST(CallbackHelpersTest, IsBaseCallback) {
- // Check that base::Closures and references to them are considered
- // base::Callbacks.
+ // Check that base::{Once,Repeating}Closures and references to them are
+ // considered base::{Once,Repeating}Callbacks.
static_assert(base::IsBaseCallback<base::OnceClosure>::value, "");
static_assert(base::IsBaseCallback<base::RepeatingClosure>::value, "");
static_assert(base::IsBaseCallback<base::OnceClosure&&>::value, "");
diff --git a/chromium/base/containers/README.md b/chromium/base/containers/README.md
index 4adc38d36d4..7700c37945f 100644
--- a/chromium/base/containers/README.md
+++ b/chromium/base/containers/README.md
@@ -29,30 +29,35 @@ Google naming. Be sure to use the base namespace.
### Usage advice
- * Generally avoid `std::unordered_set` and `std::unordered_map`. In the
- common case, query performance is unlikely to be sufficiently higher than
+* Generally avoid `std::unordered_set` and `std::unordered_map`. In the common
+ case, query performance is unlikely to be sufficiently higher than
`std::map` to make a difference, insert performance is slightly worse, and
the memory overhead is high. This makes sense mostly for large tables where
you expect a lot of lookups.
- * Most maps and sets in Chrome are small and contain objects that can be
- moved efficiently. In this case, consider `base::flat_map` and
- `base::flat_set`. You need to be aware of the maximum expected size of
- the container since individual inserts and deletes are O(n), giving O(n^2)
- construction time for the entire map. But because it avoids mallocs in most
- cases, inserts are better or comparable to other containers even for
- several dozen items, and efficiently-moved types are unlikely to have
- performance problems for most cases until you have hundreds of items. If
- your container can be constructed in one shot, the constructor from vector
- gives O(n log n) construction times and it should be strictly better than
- a `std::map`.
-
- * `base::small_map` has better runtime memory usage without the poor
- mutation performance of large containers that `base::flat_map` has. But this
- advantage is partially offset by additional code size. Prefer in cases
- where you make many objects so that the code/heap tradeoff is good.
-
- * Use `std::map` and `std::set` if you can't decide. Even if they're not
+* Most maps and sets in Chrome are small and contain objects that can be moved
+ efficiently. In this case, consider `base::flat_map` and `base::flat_set`.
+ You need to be aware of the maximum expected size of the container since
+ individual inserts and deletes are O(n), giving O(n^2) construction time for
+ the entire map. But because it avoids mallocs in most cases, inserts are
+ better or comparable to other containers even for several dozen items, and
+ efficiently-moved types are unlikely to have performance problems for most
+ cases until you have hundreds of items. If your container can be constructed
+ in one shot, the constructor from vector gives O(n log n) construction times
+ and it should be strictly better than a `std::map`.
+
+ Conceptually inserting a range of n elements into a `base::flat_map` or
+ `base::flat_set` behaves as if insert() was called for each individually
+ element. Thus in case the input range contains repeated elements, only the
+ first one of these duplicates will be inserted into the container. This
+ behaviour applies to construction from a range as well.
+
+* `base::small_map` has better runtime memory usage without the poor mutation
+ performance of large containers that `base::flat_map` has. But this
+ advantage is partially offset by additional code size. Prefer in cases where
+ you make many objects so that the code/heap tradeoff is good.
+
+* Use `std::map` and `std::set` if you can't decide. Even if they're not
great, they're unlikely to be bad or surprising.
### Map and set details
@@ -155,7 +160,7 @@ std::generate_n(std::back_inserter(ptr_vec), 5, []{
});
// Construct a set.
-UniquePtrSet<int> ptr_set(std::move(ptr_vec), base::KEEP_FIRST_OF_DUPES);
+UniquePtrSet<int> ptr_set(std::move(ptr_vec));
// Use raw pointers to lookup keys.
int* ptr = ptr_set.begin()->get();
@@ -165,8 +170,7 @@ EXPECT_TRUE(ptr_set.find(ptr) == ptr_set.begin());
Example `flat_map<std::string, int>`:
```cpp
-base::flat_map<std::string, int> str_to_int({{"a", 1}, {"c", 2},{"b", 2}},
- base::KEEP_FIRST_OF_DUPES);
+base::flat_map<std::string, int> str_to_int({{"a", 1}, {"c", 2},{"b", 2}});
// Does not construct temporary strings.
str_to_int.find("c")->second = 3;
diff --git a/chromium/base/containers/checked_iterators.h b/chromium/base/containers/checked_iterators.h
index b7eb7b4c7a9..bf3d6949371 100644
--- a/chromium/base/containers/checked_iterators.h
+++ b/chromium/base/containers/checked_iterators.h
@@ -7,6 +7,7 @@
#include <iterator>
#include <memory>
+#include <type_traits>
#include "base/containers/util.h"
#include "base/logging.h"
@@ -14,234 +15,106 @@
namespace base {
template <typename T>
-class CheckedRandomAccessConstIterator;
-
-template <typename T>
-class CheckedRandomAccessIterator {
+class CheckedContiguousIterator {
public:
using difference_type = std::ptrdiff_t;
- using value_type = typename std::iterator_traits<T*>::value_type;
+ using value_type = std::remove_cv_t<T>;
using pointer = T*;
using reference = T&;
using iterator_category = std::random_access_iterator_tag;
- friend class CheckedRandomAccessConstIterator<T>;
+ // Required for converting constructor below.
+ template <typename U>
+ friend class CheckedContiguousIterator;
- CheckedRandomAccessIterator() = default;
- CheckedRandomAccessIterator(T* start, const T* end)
- : CheckedRandomAccessIterator(start, start, end) {}
- CheckedRandomAccessIterator(T* start, T* current, const T* end)
+ constexpr CheckedContiguousIterator() = default;
+ constexpr CheckedContiguousIterator(T* start, const T* end)
+ : CheckedContiguousIterator(start, start, end) {}
+ constexpr CheckedContiguousIterator(const T* start, T* current, const T* end)
: start_(start), current_(current), end_(end) {
- CHECK(start <= current);
- CHECK(current <= end);
+ CHECK_LE(start, current);
+ CHECK_LE(current, end);
}
- CheckedRandomAccessIterator(const CheckedRandomAccessIterator& other) =
+ constexpr CheckedContiguousIterator(const CheckedContiguousIterator& other) =
default;
- ~CheckedRandomAccessIterator() = default;
-
- CheckedRandomAccessIterator& operator=(
- const CheckedRandomAccessIterator& other) = default;
-
- bool operator==(const CheckedRandomAccessIterator& other) const {
- CheckComparable(other);
- return current_ == other.current_;
- }
-
- bool operator!=(const CheckedRandomAccessIterator& other) const {
- CheckComparable(other);
- return current_ != other.current_;
- }
-
- bool operator<(const CheckedRandomAccessIterator& other) const {
- CheckComparable(other);
- return current_ < other.current_;
- }
-
- bool operator<=(const CheckedRandomAccessIterator& other) const {
- CheckComparable(other);
- return current_ <= other.current_;
- }
-
- CheckedRandomAccessIterator& operator++() {
- CHECK(current_ != end_);
- ++current_;
- return *this;
- }
-
- CheckedRandomAccessIterator operator++(int) {
- CheckedRandomAccessIterator old = *this;
- ++*this;
- return old;
- }
-
- CheckedRandomAccessIterator& operator--() {
- CHECK(current_ != start_);
- --current_;
- return *this;
- }
-
- CheckedRandomAccessIterator& operator--(int) {
- CheckedRandomAccessIterator old = *this;
- --*this;
- return old;
- }
-
- CheckedRandomAccessIterator& operator+=(difference_type rhs) {
- if (rhs > 0) {
- CHECK_LE(rhs, end_ - current_);
- } else {
- CHECK_LE(-rhs, current_ - start_);
- }
- current_ += rhs;
- return *this;
- }
-
- CheckedRandomAccessIterator operator+(difference_type rhs) const {
- CheckedRandomAccessIterator it = *this;
- it += rhs;
- return it;
- }
-
- CheckedRandomAccessIterator& operator-=(difference_type rhs) {
- if (rhs < 0) {
- CHECK_LE(rhs, end_ - current_);
- } else {
- CHECK_LE(-rhs, current_ - start_);
- }
- current_ -= rhs;
- return *this;
- }
-
- CheckedRandomAccessIterator operator-(difference_type rhs) const {
- CheckedRandomAccessIterator it = *this;
- it -= rhs;
- return it;
- }
-
- friend difference_type operator-(const CheckedRandomAccessIterator& lhs,
- const CheckedRandomAccessIterator& rhs) {
- CHECK(lhs.start_ == rhs.start_);
- CHECK(lhs.end_ == rhs.end_);
- return lhs.current_ - rhs.current_;
- }
-
- reference operator*() const {
- CHECK(current_ != end_);
- return *current_;
- }
-
- pointer operator->() const {
- CHECK(current_ != end_);
- return current_;
- }
-
- static bool IsRangeMoveSafe(const CheckedRandomAccessIterator& from_begin,
- const CheckedRandomAccessIterator& from_end,
- const CheckedRandomAccessIterator& to)
- WARN_UNUSED_RESULT {
- if (from_end < from_begin)
- return false;
- const auto from_begin_uintptr = get_uintptr(from_begin.current_);
- const auto from_end_uintptr = get_uintptr(from_end.current_);
- const auto to_begin_uintptr = get_uintptr(to.current_);
- const auto to_end_uintptr =
- get_uintptr((to + std::distance(from_begin, from_end)).current_);
-
- return to_begin_uintptr >= from_end_uintptr ||
- to_end_uintptr <= from_begin_uintptr;
- }
-
- private:
- void CheckComparable(const CheckedRandomAccessIterator& other) const {
- CHECK_EQ(start_, other.start_);
- CHECK_EQ(end_, other.end_);
- }
-
- const T* start_ = nullptr;
- T* current_ = nullptr;
- const T* end_ = nullptr;
-};
-
-template <typename T>
-class CheckedRandomAccessConstIterator {
- public:
- using difference_type = std::ptrdiff_t;
- using value_type = typename std::iterator_traits<T*>::value_type;
- using pointer = const T*;
- using reference = const T&;
- using iterator_category = std::random_access_iterator_tag;
- CheckedRandomAccessConstIterator() = default;
- CheckedRandomAccessConstIterator(T* start, const T* end)
- : CheckedRandomAccessConstIterator(start, start, end) {}
- CheckedRandomAccessConstIterator(T* start, T* current, const T* end)
- : start_(start), current_(current), end_(end) {
- CHECK(start <= current);
- CHECK(current <= end);
- }
- CheckedRandomAccessConstIterator(
- const CheckedRandomAccessConstIterator& other) = default;
- CheckedRandomAccessConstIterator(const CheckedRandomAccessIterator<T>& other)
+ // Converting constructor allowing conversions like CRAI<T> to CRAI<const T>,
+ // but disallowing CRAI<const T> to CRAI<T> or CRAI<Derived> to CRAI<Base>,
+ // which are unsafe. Furthermore, this is the same condition as used by the
+ // converting constructors of std::span<T> and std::unique_ptr<T[]>.
+ // See https://wg21.link/n4042 for details.
+ template <
+ typename U,
+ std::enable_if_t<std::is_convertible<U (*)[], T (*)[]>::value>* = nullptr>
+ constexpr CheckedContiguousIterator(const CheckedContiguousIterator<U>& other)
: start_(other.start_), current_(other.current_), end_(other.end_) {
// We explicitly don't delegate to the 3-argument constructor here. Its
// CHECKs would be redundant, since we expect |other| to maintain its own
// invariant. However, DCHECKs never hurt anybody. Presumably.
- DCHECK(other.start_ <= other.current_);
- DCHECK(other.current_ <= other.end_);
+ DCHECK_LE(other.start_, other.current_);
+ DCHECK_LE(other.current_, other.end_);
}
- ~CheckedRandomAccessConstIterator() = default;
- CheckedRandomAccessConstIterator& operator=(
- const CheckedRandomAccessConstIterator& other) = default;
+ ~CheckedContiguousIterator() = default;
- CheckedRandomAccessConstIterator& operator=(
- CheckedRandomAccessConstIterator& other) = default;
+ constexpr CheckedContiguousIterator& operator=(
+ const CheckedContiguousIterator& other) = default;
- bool operator==(const CheckedRandomAccessConstIterator& other) const {
+ constexpr bool operator==(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ == other.current_;
}
- bool operator!=(const CheckedRandomAccessConstIterator& other) const {
+ constexpr bool operator!=(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ != other.current_;
}
- bool operator<(const CheckedRandomAccessConstIterator& other) const {
+ constexpr bool operator<(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ < other.current_;
}
- bool operator<=(const CheckedRandomAccessConstIterator& other) const {
+ constexpr bool operator<=(const CheckedContiguousIterator& other) const {
CheckComparable(other);
return current_ <= other.current_;
}
- CheckedRandomAccessConstIterator& operator++() {
- CHECK(current_ != end_);
+ constexpr bool operator>(const CheckedContiguousIterator& other) const {
+ CheckComparable(other);
+ return current_ > other.current_;
+ }
+
+ constexpr bool operator>=(const CheckedContiguousIterator& other) const {
+ CheckComparable(other);
+ return current_ >= other.current_;
+ }
+
+ constexpr CheckedContiguousIterator& operator++() {
+ CHECK_NE(current_, end_);
++current_;
return *this;
}
- CheckedRandomAccessConstIterator operator++(int) {
- CheckedRandomAccessConstIterator old = *this;
+ constexpr CheckedContiguousIterator operator++(int) {
+ CheckedContiguousIterator old = *this;
++*this;
return old;
}
- CheckedRandomAccessConstIterator& operator--() {
- CHECK(current_ != start_);
+ constexpr CheckedContiguousIterator& operator--() {
+ CHECK_NE(current_, start_);
--current_;
return *this;
}
- CheckedRandomAccessConstIterator& operator--(int) {
- CheckedRandomAccessConstIterator old = *this;
+ constexpr CheckedContiguousIterator& operator--(int) {
+ CheckedContiguousIterator old = *this;
--*this;
return old;
}
- CheckedRandomAccessConstIterator& operator+=(difference_type rhs) {
+ constexpr CheckedContiguousIterator& operator+=(difference_type rhs) {
if (rhs > 0) {
CHECK_LE(rhs, end_ - current_);
} else {
@@ -251,13 +124,13 @@ class CheckedRandomAccessConstIterator {
return *this;
}
- CheckedRandomAccessConstIterator operator+(difference_type rhs) const {
- CheckedRandomAccessConstIterator it = *this;
+ constexpr CheckedContiguousIterator operator+(difference_type rhs) const {
+ CheckedContiguousIterator it = *this;
it += rhs;
return it;
}
- CheckedRandomAccessConstIterator& operator-=(difference_type rhs) {
+ constexpr CheckedContiguousIterator& operator-=(difference_type rhs) {
if (rhs < 0) {
CHECK_LE(rhs, end_ - current_);
} else {
@@ -267,34 +140,40 @@ class CheckedRandomAccessConstIterator {
return *this;
}
- CheckedRandomAccessConstIterator operator-(difference_type rhs) const {
- CheckedRandomAccessConstIterator it = *this;
+ constexpr CheckedContiguousIterator operator-(difference_type rhs) const {
+ CheckedContiguousIterator it = *this;
it -= rhs;
return it;
}
- friend difference_type operator-(
- const CheckedRandomAccessConstIterator& lhs,
- const CheckedRandomAccessConstIterator& rhs) {
- CHECK(lhs.start_ == rhs.start_);
- CHECK(lhs.end_ == rhs.end_);
+ constexpr friend difference_type operator-(
+ const CheckedContiguousIterator& lhs,
+ const CheckedContiguousIterator& rhs) {
+ CHECK_EQ(lhs.start_, rhs.start_);
+ CHECK_EQ(lhs.end_, rhs.end_);
return lhs.current_ - rhs.current_;
}
- reference operator*() const {
- CHECK(current_ != end_);
+ constexpr reference operator*() const {
+ CHECK_NE(current_, end_);
return *current_;
}
- pointer operator->() const {
- CHECK(current_ != end_);
+ constexpr pointer operator->() const {
+ CHECK_NE(current_, end_);
return current_;
}
- static bool IsRangeMoveSafe(
- const CheckedRandomAccessConstIterator& from_begin,
- const CheckedRandomAccessConstIterator& from_end,
- const CheckedRandomAccessConstIterator& to) WARN_UNUSED_RESULT {
+ constexpr reference operator[](difference_type rhs) const {
+ CHECK_GE(rhs, 0);
+ CHECK_LT(rhs, end_ - current_);
+ return current_[rhs];
+ }
+
+ static bool IsRangeMoveSafe(const CheckedContiguousIterator& from_begin,
+ const CheckedContiguousIterator& from_end,
+ const CheckedContiguousIterator& to)
+ WARN_UNUSED_RESULT {
if (from_end < from_begin)
return false;
const auto from_begin_uintptr = get_uintptr(from_begin.current_);
@@ -308,16 +187,19 @@ class CheckedRandomAccessConstIterator {
}
private:
- void CheckComparable(const CheckedRandomAccessConstIterator& other) const {
+ constexpr void CheckComparable(const CheckedContiguousIterator& other) const {
CHECK_EQ(start_, other.start_);
CHECK_EQ(end_, other.end_);
}
const T* start_ = nullptr;
- const T* current_ = nullptr;
+ T* current_ = nullptr;
const T* end_ = nullptr;
};
+template <typename T>
+using CheckedContiguousConstIterator = CheckedContiguousIterator<const T>;
+
} // namespace base
#endif // BASE_CONTAINERS_CHECKED_ITERATORS_H_
diff --git a/chromium/base/containers/flat_map.h b/chromium/base/containers/flat_map.h
index 481e40861ce..a98f0bcd298 100644
--- a/chromium/base/containers/flat_map.h
+++ b/chromium/base/containers/flat_map.h
@@ -60,15 +60,12 @@ struct GetKeyFromValuePairFirst {
//
// Constructors (inputs need not be sorted):
// flat_map(InputIterator first, InputIterator last,
-// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
// const Compare& compare = Compare());
// flat_map(const flat_map&);
// flat_map(flat_map&&);
// flat_map(std::vector<value_type>,
-// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
// const Compare& compare = Compare()); // Re-use storage.
// flat_map(std::initializer_list<value_type> ilist,
-// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
// const Compare& comp = Compare());
//
// Assignment functions:
@@ -108,8 +105,7 @@ struct GetKeyFromValuePairFirst {
// pair<iterator, bool> insert(value_type&&);
// iterator insert(const_iterator hint, const value_type&);
// iterator insert(const_iterator hint, value_type&&);
-// void insert(InputIterator first, InputIterator last,
-// FlatContainerDupes = KEEP_FIRST_OF_DUPES);
+// void insert(InputIterator first, InputIterator last);
// pair<iterator, bool> insert_or_assign(K&&, M&&);
// iterator insert_or_assign(const_iterator hint, K&&, M&&);
// pair<iterator, bool> emplace(Args&&...);
@@ -183,18 +179,15 @@ class flat_map : public ::base::internal::flat_tree<
template <class InputIterator>
flat_map(InputIterator first,
InputIterator last,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
const Compare& comp = Compare());
flat_map(const flat_map&) = default;
flat_map(flat_map&&) noexcept = default;
flat_map(std::vector<value_type> items,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
const Compare& comp = Compare());
flat_map(std::initializer_list<value_type> ilist,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
const Compare& comp = Compare());
~flat_map() = default;
@@ -249,22 +242,19 @@ template <class Key, class Mapped, class Compare>
template <class InputIterator>
flat_map<Key, Mapped, Compare>::flat_map(InputIterator first,
InputIterator last,
- FlatContainerDupes dupe_handling,
const Compare& comp)
- : tree(first, last, dupe_handling, comp) {}
+ : tree(first, last, comp) {}
template <class Key, class Mapped, class Compare>
flat_map<Key, Mapped, Compare>::flat_map(std::vector<value_type> items,
- FlatContainerDupes dupe_handling,
const Compare& comp)
- : tree(std::move(items), dupe_handling, comp) {}
+ : tree(std::move(items), comp) {}
template <class Key, class Mapped, class Compare>
flat_map<Key, Mapped, Compare>::flat_map(
std::initializer_list<value_type> ilist,
- FlatContainerDupes dupe_handling,
const Compare& comp)
- : flat_map(std::begin(ilist), std::end(ilist), dupe_handling, comp) {}
+ : flat_map(std::begin(ilist), std::end(ilist), comp) {}
// ----------------------------------------------------------------------------
// Assignments.
diff --git a/chromium/base/containers/flat_map_unittest.cc b/chromium/base/containers/flat_map_unittest.cc
index 87958bde414..e48de3cd972 100644
--- a/chromium/base/containers/flat_map_unittest.cc
+++ b/chromium/base/containers/flat_map_unittest.cc
@@ -41,11 +41,6 @@ TEST(FlatMap, RangeConstructor) {
flat_map<int, int> first(std::begin(input_vals), std::end(input_vals));
EXPECT_THAT(first, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 1),
std::make_pair(3, 1)));
-
- flat_map<int, int> last(std::begin(input_vals), std::end(input_vals),
- KEEP_LAST_OF_DUPES);
- EXPECT_THAT(last, ElementsAre(std::make_pair(1, 3), std::make_pair(2, 3),
- std::make_pair(3, 3)));
}
TEST(FlatMap, MoveConstructor) {
@@ -68,36 +63,18 @@ TEST(FlatMap, MoveConstructor) {
TEST(FlatMap, VectorConstructor) {
using IntPair = std::pair<int, int>;
using IntMap = flat_map<int, int>;
- {
- std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}};
- IntMap map(std::move(vect));
- EXPECT_THAT(map, ElementsAre(IntPair(1, 1), IntPair(2, 1)));
- }
- {
- std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}};
- IntMap map(std::move(vect), KEEP_LAST_OF_DUPES);
- EXPECT_THAT(map, ElementsAre(IntPair(1, 2), IntPair(2, 1)));
- }
+ std::vector<IntPair> vect{{1, 1}, {1, 2}, {2, 1}};
+ IntMap map(std::move(vect));
+ EXPECT_THAT(map, ElementsAre(IntPair(1, 1), IntPair(2, 1)));
}
TEST(FlatMap, InitializerListConstructor) {
- {
- flat_map<int, int> cont(
- {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}});
- EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2),
- std::make_pair(3, 3), std::make_pair(4, 4),
- std::make_pair(5, 5), std::make_pair(8, 8),
- std::make_pair(10, 10)));
- }
- {
- flat_map<int, int> cont(
- {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}},
- KEEP_LAST_OF_DUPES);
- EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 2), std::make_pair(2, 2),
- std::make_pair(3, 3), std::make_pair(4, 4),
- std::make_pair(5, 5), std::make_pair(8, 8),
- std::make_pair(10, 10)));
- }
+ flat_map<int, int> cont(
+ {{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 2}, {10, 10}, {8, 8}});
+ EXPECT_THAT(cont, ElementsAre(std::make_pair(1, 1), std::make_pair(2, 2),
+ std::make_pair(3, 3), std::make_pair(4, 4),
+ std::make_pair(5, 5), std::make_pair(8, 8),
+ std::make_pair(10, 10)));
}
TEST(FlatMap, InitializerListAssignment) {
diff --git a/chromium/base/containers/flat_set.h b/chromium/base/containers/flat_set.h
index 9fab64af3bf..8e890ef233d 100644
--- a/chromium/base/containers/flat_set.h
+++ b/chromium/base/containers/flat_set.h
@@ -46,15 +46,12 @@ namespace base {
//
// Constructors (inputs need not be sorted):
// flat_set(InputIterator first, InputIterator last,
-// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
// const Compare& compare = Compare());
// flat_set(const flat_set&);
// flat_set(flat_set&&);
// flat_set(std::vector<Key>,
-// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
// const Compare& compare = Compare()); // Re-use storage.
// flat_set(std::initializer_list<value_type> ilist,
-// FlatContainerDupes = KEEP_FIRST_OF_DUPES,
// const Compare& comp = Compare());
//
// Assignment functions:
@@ -90,8 +87,7 @@ namespace base {
// Insert and accessor functions:
// pair<iterator, bool> insert(const key_type&);
// pair<iterator, bool> insert(key_type&&);
-// void insert(InputIterator first, InputIterator last,
-// FlatContainerDupes = KEEP_FIRST_OF_DUPES);
+// void insert(InputIterator first, InputIterator last);
// iterator insert(const_iterator hint, const key_type&);
// iterator insert(const_iterator hint, key_type&&);
// pair<iterator, bool> emplace(Args&&...);
diff --git a/chromium/base/containers/flat_set_unittest.cc b/chromium/base/containers/flat_set_unittest.cc
index 45969754985..7b10c730645 100644
--- a/chromium/base/containers/flat_set_unittest.cc
+++ b/chromium/base/containers/flat_set_unittest.cc
@@ -38,16 +38,15 @@ TEST(FlatSet, IncompleteType) {
TEST(FlatSet, RangeConstructor) {
flat_set<int>::value_type input_vals[] = {1, 1, 1, 2, 2, 2, 3, 3, 3};
- flat_set<int> cont(std::begin(input_vals), std::end(input_vals),
- base::KEEP_FIRST_OF_DUPES);
+ flat_set<int> cont(std::begin(input_vals), std::end(input_vals));
EXPECT_THAT(cont, ElementsAre(1, 2, 3));
}
TEST(FlatSet, MoveConstructor) {
int input_range[] = {1, 2, 3, 4};
- flat_set<MoveOnlyInt> original(std::begin(input_range), std::end(input_range),
- base::KEEP_FIRST_OF_DUPES);
+ flat_set<MoveOnlyInt> original(std::begin(input_range),
+ std::end(input_range));
flat_set<MoveOnlyInt> moved(std::move(original));
EXPECT_EQ(1U, moved.count(MoveOnlyInt(1)));
@@ -57,7 +56,7 @@ TEST(FlatSet, MoveConstructor) {
}
TEST(FlatSet, InitializerListConstructor) {
- flat_set<int> cont({1, 2, 3, 4, 5, 6, 10, 8}, KEEP_FIRST_OF_DUPES);
+ flat_set<int> cont({1, 2, 3, 4, 5, 6, 10, 8});
EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
}
diff --git a/chromium/base/containers/flat_tree.h b/chromium/base/containers/flat_tree.h
index 5222e388ea9..89c75d70692 100644
--- a/chromium/base/containers/flat_tree.h
+++ b/chromium/base/containers/flat_tree.h
@@ -14,11 +14,6 @@
namespace base {
-enum FlatContainerDupes {
- KEEP_FIRST_OF_DUPES,
- KEEP_LAST_OF_DUPES,
-};
-
namespace internal {
// This is a convenience method returning true if Iterator is at least a
@@ -30,34 +25,6 @@ constexpr bool is_multipass() {
typename std::iterator_traits<Iterator>::iterator_category>::value;
}
-// This algorithm is like unique() from the standard library except it
-// selects only the last of consecutive values instead of the first.
-template <class Iterator, class BinaryPredicate>
-Iterator LastUnique(Iterator first, Iterator last, BinaryPredicate compare) {
- Iterator replacable = std::adjacent_find(first, last, compare);
-
- // No duplicate elements found.
- if (replacable == last)
- return last;
-
- first = std::next(replacable);
-
- // Last element is a duplicate but all others are unique.
- if (first == last)
- return replacable;
-
- // This loop is based on std::adjacent_find but std::adjacent_find doesn't
- // quite cut it.
- for (Iterator next = std::next(first); next != last; ++next, ++first) {
- if (!compare(*first, *next))
- *replacable++ = std::move(*first);
- }
-
- // Last element should be copied unconditionally.
- *replacable++ = std::move(*first);
- return replacable;
-}
-
// Uses SFINAE to detect whether type has is_transparent member.
template <typename T, typename = void>
struct IsTransparentCompare : std::false_type {};
@@ -136,18 +103,15 @@ class flat_tree {
template <class InputIterator>
flat_tree(InputIterator first,
InputIterator last,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
const key_compare& comp = key_compare());
flat_tree(const flat_tree&);
flat_tree(flat_tree&&) noexcept = default;
flat_tree(std::vector<value_type> items,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
const key_compare& comp = key_compare());
flat_tree(std::initializer_list<value_type> ilist,
- FlatContainerDupes dupe_handling = KEEP_FIRST_OF_DUPES,
const key_compare& comp = key_compare());
~flat_tree();
@@ -222,12 +186,9 @@ class flat_tree {
iterator insert(const_iterator position_hint, value_type&& x);
// This method inserts the values from the range [first, last) into the
- // current tree. In case of KEEP_LAST_OF_DUPES newly added elements can
- // overwrite existing values.
+ // current tree.
template <class InputIterator>
- void insert(InputIterator first,
- InputIterator last,
- FlatContainerDupes dupes = KEEP_FIRST_OF_DUPES);
+ void insert(InputIterator first, InputIterator last);
template <class... Args>
std::pair<iterator, bool> emplace(Args&&... args);
@@ -447,28 +408,17 @@ class flat_tree {
return {position, false};
}
- void sort_and_unique(iterator first,
- iterator last,
- FlatContainerDupes dupes) {
+ void sort_and_unique(iterator first, iterator last) {
// Preserve stability for the unique code below.
- std::stable_sort(first, last, impl_.get_value_comp());
+ std::stable_sort(first, last, value_comp());
- auto comparator = [this](const value_type& lhs, const value_type& rhs) {
+ auto equal_comp = [this](const value_type& lhs, const value_type& rhs) {
// lhs is already <= rhs due to sort, therefore
// !(lhs < rhs) <=> lhs == rhs.
- return !impl_.get_value_comp()(lhs, rhs);
+ return !value_comp()(lhs, rhs);
};
- iterator erase_after;
- switch (dupes) {
- case KEEP_FIRST_OF_DUPES:
- erase_after = std::unique(first, last, comparator);
- break;
- case KEEP_LAST_OF_DUPES:
- erase_after = LastUnique(first, last, comparator);
- break;
- }
- erase(erase_after, last);
+ erase(std::unique(first, last, equal_comp), last);
}
// To support comparators that may not be possible to default-construct, we
@@ -512,10 +462,9 @@ template <class InputIterator>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
InputIterator first,
InputIterator last,
- FlatContainerDupes dupe_handling,
const KeyCompare& comp)
: impl_(comp, first, last) {
- sort_and_unique(begin(), end(), dupe_handling);
+ sort_and_unique(begin(), end());
}
template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
@@ -525,18 +474,16 @@ flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
std::vector<value_type> items,
- FlatContainerDupes dupe_handling,
const KeyCompare& comp)
: impl_(comp, std::move(items)) {
- sort_and_unique(begin(), end(), dupe_handling);
+ sort_and_unique(begin(), end());
}
template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::flat_tree(
std::initializer_list<value_type> ilist,
- FlatContainerDupes dupe_handling,
const KeyCompare& comp)
- : flat_tree(std::begin(ilist), std::end(ilist), dupe_handling, comp) {}
+ : flat_tree(std::begin(ilist), std::end(ilist), comp) {}
template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::~flat_tree() = default;
@@ -556,7 +503,7 @@ template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
auto flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::operator=(
std::initializer_list<value_type> ilist) -> flat_tree& {
impl_.body_ = ilist;
- sort_and_unique(begin(), end(), KEEP_FIRST_OF_DUPES);
+ sort_and_unique(begin(), end());
return *this;
}
@@ -717,62 +664,38 @@ template <class Key, class Value, class GetKeyFromValue, class KeyCompare>
template <class InputIterator>
void flat_tree<Key, Value, GetKeyFromValue, KeyCompare>::insert(
InputIterator first,
- InputIterator last,
- FlatContainerDupes dupes) {
+ InputIterator last) {
if (first == last)
return;
- // Cache results whether existing elements should be overwritten and whether
- // inserting new elements happens immediately or will be done in a batch.
- const bool overwrite_existing = dupes == KEEP_LAST_OF_DUPES;
- const bool insert_inplace =
- is_multipass<InputIterator>() && std::next(first) == last;
-
- if (insert_inplace) {
- if (overwrite_existing) {
- for (; first != last; ++first)
- insert_or_assign(*first);
- } else
- std::copy(first, last, std::inserter(*this, end()));
+ // Dispatch to single element insert if the input range contains a single
+ // element.
+ if (is_multipass<InputIterator>() && std::next(first) == last) {
+ insert(end(), *first);
return;
}
// Provide a convenience lambda to obtain an iterator pointing past the last
// old element. This needs to be dymanic due to possible re-allocations.
- const size_type original_size = size();
- auto middle = [this, original_size]() {
- return std::next(begin(), original_size);
- };
+ auto middle = [this, size = size()] { return std::next(begin(), size); };
// For batch updates initialize the first insertion point.
- difference_type pos_first_new = original_size;
+ difference_type pos_first_new = size();
// Loop over the input range while appending new values and overwriting
// existing ones, if applicable. Keep track of the first insertion point.
- if (overwrite_existing) {
- for (; first != last; ++first) {
- std::pair<iterator, bool> result =
- append_or_assign(begin(), middle(), *first);
- if (result.second) {
- pos_first_new =
- std::min(pos_first_new, std::distance(begin(), result.first));
- }
- }
- } else {
- for (; first != last; ++first) {
- std::pair<iterator, bool> result =
- append_unique(begin(), middle(), *first);
- if (result.second) {
- pos_first_new =
- std::min(pos_first_new, std::distance(begin(), result.first));
- }
+ for (; first != last; ++first) {
+ std::pair<iterator, bool> result = append_unique(begin(), middle(), *first);
+ if (result.second) {
+ pos_first_new =
+ std::min(pos_first_new, std::distance(begin(), result.first));
}
}
// The new elements might be unordered and contain duplicates, so post-process
// the just inserted elements and merge them with the rest, inserting them at
// the previously found spot.
- sort_and_unique(middle(), end(), dupes);
+ sort_and_unique(middle(), end());
std::inplace_merge(std::next(begin(), pos_first_new), middle(), end(),
value_comp());
}
diff --git a/chromium/base/containers/flat_tree_unittest.cc b/chromium/base/containers/flat_tree_unittest.cc
index 212ef773166..c305a0c20c8 100644
--- a/chromium/base/containers/flat_tree_unittest.cc
+++ b/chromium/base/containers/flat_tree_unittest.cc
@@ -188,68 +188,6 @@ TEST(FlatTree, IsMultipass) {
"RandomAccessIterator is multipass");
}
-TEST(FlatTree, LastUnique) {
- using Pair = std::pair<int, int>;
- using Vect = std::vector<Pair>;
-
- auto cmp = [](const Pair& lhs, const Pair& rhs) {
- return lhs.first == rhs.first;
- };
-
- // Empty case.
- Vect empty;
- EXPECT_EQ(empty.end(), LastUnique(empty.begin(), empty.end(), cmp));
-
- // Single element.
- Vect one;
- one.push_back(Pair(1, 1));
- EXPECT_EQ(one.end(), LastUnique(one.begin(), one.end(), cmp));
- ASSERT_EQ(1u, one.size());
- EXPECT_THAT(one, ElementsAre(Pair(1, 1)));
-
- // Two elements, already unique.
- Vect two_u;
- two_u.push_back(Pair(1, 1));
- two_u.push_back(Pair(2, 2));
- EXPECT_EQ(two_u.end(), LastUnique(two_u.begin(), two_u.end(), cmp));
- EXPECT_THAT(two_u, ElementsAre(Pair(1, 1), Pair(2, 2)));
-
- // Two elements, dupes.
- Vect two_d;
- two_d.push_back(Pair(1, 1));
- two_d.push_back(Pair(1, 2));
- auto last = LastUnique(two_d.begin(), two_d.end(), cmp);
- EXPECT_EQ(two_d.begin() + 1, last);
- two_d.erase(last, two_d.end());
- EXPECT_THAT(two_d, ElementsAre(Pair(1, 2)));
-
- // Non-dupes, dupes, non-dupes.
- Vect ndn;
- ndn.push_back(Pair(1, 1));
- ndn.push_back(Pair(2, 1));
- ndn.push_back(Pair(2, 2));
- ndn.push_back(Pair(2, 3));
- ndn.push_back(Pair(3, 1));
- last = LastUnique(ndn.begin(), ndn.end(), cmp);
- EXPECT_EQ(ndn.begin() + 3, last);
- ndn.erase(last, ndn.end());
- EXPECT_THAT(ndn, ElementsAre(Pair(1, 1), Pair(2, 3), Pair(3, 1)));
-
- // Dupes, non-dupes, dupes.
- Vect dnd;
- dnd.push_back(Pair(1, 1));
- dnd.push_back(Pair(1, 2));
- dnd.push_back(Pair(1, 3));
- dnd.push_back(Pair(2, 1));
- dnd.push_back(Pair(3, 1));
- dnd.push_back(Pair(3, 2));
- dnd.push_back(Pair(3, 3));
- last = LastUnique(dnd.begin(), dnd.end(), cmp);
- EXPECT_EQ(dnd.begin() + 3, last);
- dnd.erase(last, dnd.end());
- EXPECT_THAT(dnd, ElementsAre(Pair(1, 3), Pair(2, 1), Pair(3, 3)));
-}
-
// ----------------------------------------------------------------------------
// Class.
@@ -350,7 +288,6 @@ TEST(FlatTree, DefaultConstructor) {
// flat_tree(InputIterator first,
// InputIterator last,
-// FlatContainerDupes dupe_handling,
// const Compare& comp = Compare())
TEST(FlatTree, RangeConstructor) {
@@ -362,12 +299,6 @@ TEST(FlatTree, RangeConstructor) {
MakeInputIterator(std::end(input_vals)));
EXPECT_THAT(first_of,
ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1)));
-
- IntPairTree last_of(MakeInputIterator(std::begin(input_vals)),
- MakeInputIterator(std::end(input_vals)),
- KEEP_LAST_OF_DUPES);
- EXPECT_THAT(last_of,
- ElementsAre(IntPair(1, 3), IntPair(2, 3), IntPair(3, 3)));
}
{
TreeWithStrangeCompare::value_type input_vals[] = {1, 1, 1, 2, 2,
@@ -375,7 +306,6 @@ TEST(FlatTree, RangeConstructor) {
TreeWithStrangeCompare cont(MakeInputIterator(std::begin(input_vals)),
MakeInputIterator(std::end(input_vals)),
- KEEP_FIRST_OF_DUPES,
NonDefaultConstructibleCompare(0));
EXPECT_THAT(cont, ElementsAre(1, 2, 3));
}
@@ -408,7 +338,7 @@ TEST(FlatTree, MoveConstructor) {
EXPECT_EQ(1U, moved.count(MoveOnlyInt(4)));
}
-// flat_tree(std::vector<value_type>, FlatContainerDupes dupe_handling)
+// flat_tree(std::vector<value_type>)
TEST(FlatTree, VectorConstructor) {
using Pair = std::pair<int, MoveOnlyInt>;
@@ -434,15 +364,9 @@ TEST(FlatTree, VectorConstructor) {
const Pair& first = *(tree.begin() + 1);
ASSERT_EQ(2, first.first);
ASSERT_EQ(0, first.second.data());
-
- // Test KEEP_LAST_OF_DUPES with a simple vector constructor.
- std::vector<IntPair> int_storage{{1, 1}, {1, 2}, {2, 1}};
- IntPairTree int_tree(std::move(int_storage), KEEP_LAST_OF_DUPES);
- EXPECT_THAT(int_tree, ElementsAre(IntPair(1, 2), IntPair(2, 1)));
}
// flat_tree(std::initializer_list<value_type> ilist,
-// FlatContainerDupes dupe_handling,
// const Compare& comp = Compare())
TEST(FlatTree, InitializerListConstructor) {
@@ -455,7 +379,7 @@ TEST(FlatTree, InitializerListConstructor) {
EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
}
{
- TreeWithStrangeCompare cont({1, 2, 3, 4, 5, 6, 10, 8}, KEEP_FIRST_OF_DUPES,
+ TreeWithStrangeCompare cont({1, 2, 3, 4, 5, 6, 10, 8},
NonDefaultConstructibleCompare(0));
EXPECT_THAT(cont, ElementsAre(1, 2, 3, 4, 5, 6, 8, 10));
}
@@ -463,10 +387,6 @@ TEST(FlatTree, InitializerListConstructor) {
IntPairTree first_of({{1, 1}, {2, 1}, {1, 2}});
EXPECT_THAT(first_of, ElementsAre(IntPair(1, 1), IntPair(2, 1)));
}
- {
- IntPairTree last_of({{1, 1}, {2, 1}, {1, 2}}, KEEP_LAST_OF_DUPES);
- EXPECT_THAT(last_of, ElementsAre(IntPair(1, 2), IntPair(2, 1)));
- }
}
// ----------------------------------------------------------------------------
@@ -791,14 +711,6 @@ TEST(FlatTree, InsertIterIter) {
{
IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
- IntPair int_pairs[] = {{1, 2}};
- cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES);
- EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 1), IntPair(3, 1),
- IntPair(4, 1)));
- }
-
- {
- IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
IntPair int_pairs[] = {{5, 1}};
cont.insert(std::begin(int_pairs), std::end(int_pairs));
EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
@@ -807,14 +719,6 @@ TEST(FlatTree, InsertIterIter) {
{
IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
- IntPair int_pairs[] = {{5, 1}};
- cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES);
- EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
- IntPair(4, 1), IntPair(5, 1)));
- }
-
- {
- IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}};
cont.insert(std::begin(int_pairs), std::end(int_pairs));
EXPECT_THAT(cont, ElementsAre(IntPair(1, 1), IntPair(2, 1), IntPair(3, 1),
@@ -823,14 +727,6 @@ TEST(FlatTree, InsertIterIter) {
{
IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
- IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}};
- cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES);
- EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 2), IntPair(3, 2),
- IntPair(4, 2)));
- }
-
- {
- IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2},
{8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}};
cont.insert(std::begin(int_pairs), std::end(int_pairs));
@@ -838,16 +734,6 @@ TEST(FlatTree, InsertIterIter) {
IntPair(4, 1), IntPair(5, 2), IntPair(6, 2),
IntPair(7, 2), IntPair(8, 2)));
}
-
- {
- IntIntMap cont({{1, 1}, {2, 1}, {3, 1}, {4, 1}});
- IntPair int_pairs[] = {{3, 2}, {1, 2}, {4, 2}, {2, 2}, {7, 2}, {6, 2},
- {8, 2}, {5, 2}, {5, 3}, {6, 3}, {7, 3}, {8, 3}};
- cont.insert(std::begin(int_pairs), std::end(int_pairs), KEEP_LAST_OF_DUPES);
- EXPECT_THAT(cont, ElementsAre(IntPair(1, 2), IntPair(2, 2), IntPair(3, 2),
- IntPair(4, 2), IntPair(5, 3), IntPair(6, 3),
- IntPair(7, 3), IntPair(8, 3)));
- }
}
// template <class... Args>
diff --git a/chromium/base/containers/intrusive_heap.h b/chromium/base/containers/intrusive_heap.h
index 53d3909560c..99f61b46784 100644
--- a/chromium/base/containers/intrusive_heap.h
+++ b/chromium/base/containers/intrusive_heap.h
@@ -131,6 +131,7 @@
#include <algorithm>
#include <functional>
+#include <limits>
#include <type_traits>
#include <utility>
#include <vector>
@@ -149,7 +150,7 @@ namespace base {
// in place.
class BASE_EXPORT HeapHandle {
public:
- enum : size_t { kInvalidIndex = -1 };
+ enum : size_t { kInvalidIndex = std::numeric_limits<size_t>::max() };
constexpr HeapHandle() = default;
constexpr HeapHandle(const HeapHandle& other) = default;
@@ -715,7 +716,7 @@ IntrusiveHeap<T, Compare, HeapHandleAccessor>::~IntrusiveHeap() {
template <typename T, typename Compare, typename HeapHandleAccessor>
IntrusiveHeap<T, Compare, HeapHandleAccessor>&
IntrusiveHeap<T, Compare, HeapHandleAccessor>::operator=(
- IntrusiveHeap&& other) {
+ IntrusiveHeap&& other) noexcept {
clear();
impl_ = std::move(other.impl_);
return *this;
diff --git a/chromium/base/containers/span.h b/chromium/base/containers/span.h
index f56172d1a06..fc7f58d8e4b 100644
--- a/chromium/base/containers/span.h
+++ b/chromium/base/containers/span.h
@@ -224,8 +224,8 @@ class span : public internal::ExtentStorage<Extent> {
using difference_type = ptrdiff_t;
using pointer = T*;
using reference = T&;
- using iterator = CheckedRandomAccessIterator<T>;
- using const_iterator = CheckedRandomAccessConstIterator<T>;
+ using iterator = CheckedContiguousIterator<T>;
+ using const_iterator = CheckedContiguousConstIterator<T>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
static constexpr index_type extent = Extent;
@@ -405,8 +405,10 @@ class span : public internal::ExtentStorage<Extent> {
constexpr T* data() const noexcept { return data_; }
// [span.iter], span iterator support
- iterator begin() const noexcept { return iterator(data_, data_ + size()); }
- iterator end() const noexcept {
+ constexpr iterator begin() const noexcept {
+ return iterator(data_, data_ + size());
+ }
+ constexpr iterator end() const noexcept {
return iterator(data_, data_ + size(), data_ + size());
}
diff --git a/chromium/base/containers/span_unittest.cc b/chromium/base/containers/span_unittest.cc
index 5ec3f3f4748..e8daf948420 100644
--- a/chromium/base/containers/span_unittest.cc
+++ b/chromium/base/containers/span_unittest.cc
@@ -9,6 +9,7 @@
#include <algorithm>
#include <memory>
#include <string>
+#include <type_traits>
#include <vector>
#include "base/containers/checked_iterators.h"
@@ -22,6 +23,24 @@ using ::testing::Pointwise;
namespace base {
+namespace {
+
+// constexpr implementation of std::equal's 4 argument overload.
+template <class InputIterator1, class InputIterator2>
+constexpr bool constexpr_equal(InputIterator1 first1,
+ InputIterator1 last1,
+ InputIterator2 first2,
+ InputIterator2 last2) {
+ for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
+ if (*first1 != *first2)
+ return false;
+ }
+
+ return first1 == last1 && first2 == last2;
+}
+
+} // namespace
+
TEST(SpanTest, DefaultConstructor) {
span<int> dynamic_span;
EXPECT_EQ(nullptr, dynamic_span.data());
@@ -474,6 +493,17 @@ TEST(SpanTest, TemplatedSubspan) {
}
}
+TEST(SpanTest, SubscriptedBeginIterator) {
+ int array[] = {1, 2, 3};
+ span<const int> const_span(array);
+ for (size_t i = 0; i < const_span.size(); ++i)
+ EXPECT_EQ(array[i], const_span.begin()[i]);
+
+ span<int> mutable_span(array);
+ for (size_t i = 0; i < mutable_span.size(); ++i)
+ EXPECT_EQ(array[i], mutable_span.begin()[i]);
+}
+
TEST(SpanTest, TemplatedFirstOnDynamicSpan) {
int array[] = {1, 2, 3};
span<const int> span(array);
@@ -950,6 +980,21 @@ TEST(SpanTest, Iterator) {
EXPECT_THAT(results, ElementsAre(1, 6, 1, 8, 0));
}
+TEST(SpanTest, ConstexprIterator) {
+ static constexpr int kArray[] = {1, 6, 1, 8, 0};
+ constexpr span<const int> span(kArray);
+
+ static_assert(constexpr_equal(std::begin(kArray), std::end(kArray),
+ span.begin(), span.end()),
+ "");
+ static_assert(1 == span.begin()[0], "");
+ static_assert(1 == *(span.begin() += 0), "");
+ static_assert(6 == *(span.begin() += 1), "");
+
+ static_assert(1 == *((span.begin() + 1) -= 1), "");
+ static_assert(6 == *((span.begin() + 1) -= 0), "");
+}
+
TEST(SpanTest, ReverseIterator) {
static constexpr int kArray[] = {1, 6, 1, 8, 0};
constexpr span<const int> span(kArray);
@@ -1212,12 +1257,16 @@ TEST(SpanTest, EnsureConstexprGoodness) {
TEST(SpanTest, OutOfBoundsDeath) {
constexpr span<int, 0> kEmptySpan;
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan[0], "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.begin()[0], "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.end()[0], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.first(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.last(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptySpan.subspan(1), "");
constexpr span<int> kEmptyDynamicSpan;
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan[0], "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.begin()[0], "");
+ ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.end()[0], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.front(), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.first(1), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.last(1), "");
@@ -1228,6 +1277,8 @@ TEST(SpanTest, OutOfBoundsDeath) {
constexpr span<const int> kNonEmptyDynamicSpan(kArray);
EXPECT_EQ(3U, kNonEmptyDynamicSpan.size());
ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan[4], "");
+ ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[-1], "");
+ ASSERT_DEATH_IF_SUPPORTED(kNonEmptyDynamicSpan.begin()[3], "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(10), "");
ASSERT_DEATH_IF_SUPPORTED(kEmptyDynamicSpan.subspan(1, 7), "");
}
@@ -1242,47 +1293,73 @@ TEST(SpanTest, IteratorIsRangeMoveSafe) {
// Overlapping ranges.
for (const int dest_start_index : kOverlappingStartIndexes) {
- EXPECT_FALSE(CheckedRandomAccessIterator<const int>::IsRangeMoveSafe(
+ EXPECT_FALSE(CheckedContiguousIterator<const int>::IsRangeMoveSafe(
span.begin(), span.end(),
- CheckedRandomAccessIterator<const int>(
+ CheckedContiguousIterator<const int>(
span.data() + dest_start_index,
span.data() + dest_start_index + kNumElements)));
- EXPECT_FALSE(CheckedRandomAccessConstIterator<const int>::IsRangeMoveSafe(
+ EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
span.cbegin(), span.cend(),
- CheckedRandomAccessConstIterator<const int>(
+ CheckedContiguousConstIterator<const int>(
span.data() + dest_start_index,
span.data() + dest_start_index + kNumElements)));
}
// Non-overlapping ranges.
for (const int dest_start_index : kNonOverlappingStartIndexes) {
- EXPECT_TRUE(CheckedRandomAccessIterator<const int>::IsRangeMoveSafe(
+ EXPECT_TRUE(CheckedContiguousIterator<const int>::IsRangeMoveSafe(
span.begin(), span.end(),
- CheckedRandomAccessIterator<const int>(
+ CheckedContiguousIterator<const int>(
span.data() + dest_start_index,
span.data() + dest_start_index + kNumElements)));
- EXPECT_TRUE(CheckedRandomAccessConstIterator<const int>::IsRangeMoveSafe(
+ EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
span.cbegin(), span.cend(),
- CheckedRandomAccessConstIterator<const int>(
+ CheckedContiguousConstIterator<const int>(
span.data() + dest_start_index,
span.data() + dest_start_index + kNumElements)));
}
// IsRangeMoveSafe is true if the length to be moved is 0.
- EXPECT_TRUE(CheckedRandomAccessIterator<const int>::IsRangeMoveSafe(
+ EXPECT_TRUE(CheckedContiguousIterator<const int>::IsRangeMoveSafe(
span.begin(), span.begin(),
- CheckedRandomAccessIterator<const int>(span.data(), span.data())));
- EXPECT_TRUE(CheckedRandomAccessConstIterator<const int>::IsRangeMoveSafe(
+ CheckedContiguousIterator<const int>(span.data(), span.data())));
+ EXPECT_TRUE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
span.cbegin(), span.cbegin(),
- CheckedRandomAccessConstIterator<const int>(span.data(), span.data())));
+ CheckedContiguousConstIterator<const int>(span.data(), span.data())));
// IsRangeMoveSafe is false if end < begin.
- EXPECT_FALSE(CheckedRandomAccessIterator<const int>::IsRangeMoveSafe(
+ EXPECT_FALSE(CheckedContiguousIterator<const int>::IsRangeMoveSafe(
span.end(), span.begin(),
- CheckedRandomAccessIterator<const int>(span.data(), span.data())));
- EXPECT_FALSE(CheckedRandomAccessConstIterator<const int>::IsRangeMoveSafe(
+ CheckedContiguousIterator<const int>(span.data(), span.data())));
+ EXPECT_FALSE(CheckedContiguousConstIterator<const int>::IsRangeMoveSafe(
span.cend(), span.cbegin(),
- CheckedRandomAccessConstIterator<const int>(span.data(), span.data())));
+ CheckedContiguousConstIterator<const int>(span.data(), span.data())));
+}
+
+TEST(SpanTest, Sort) {
+ int array[] = {5, 4, 3, 2, 1};
+
+ span<int> dynamic_span = array;
+ std::sort(dynamic_span.begin(), dynamic_span.end());
+ EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5));
+ std::sort(dynamic_span.rbegin(), dynamic_span.rend());
+ EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1));
+
+ span<int, 5> static_span = array;
+ std::sort(static_span.rbegin(), static_span.rend(), std::greater<>());
+ EXPECT_THAT(array, ElementsAre(1, 2, 3, 4, 5));
+ std::sort(static_span.begin(), static_span.end(), std::greater<>());
+ EXPECT_THAT(array, ElementsAre(5, 4, 3, 2, 1));
+}
+
+TEST(SpanTest, IteratorConversions) {
+ static_assert(std::is_convertible<span<int>::iterator,
+ span<int>::const_iterator>::value,
+ "Error: iterator should be convertible to const_iterator");
+
+ static_assert(!std::is_convertible<span<int>::const_iterator,
+ span<int>::iterator>::value,
+ "Error: const_iterator should not be convertible to iterator");
}
} // namespace base
diff --git a/chromium/base/feature_list.cc b/chromium/base/feature_list.cc
index 41e1775bed9..916b82485fe 100644
--- a/chromium/base/feature_list.cc
+++ b/chromium/base/feature_list.cc
@@ -29,6 +29,17 @@ FeatureList* g_feature_list_instance = nullptr;
// Tracks whether the FeatureList instance was initialized via an accessor.
bool g_initialized_from_accessor = false;
+#if DCHECK_IS_ON()
+const char* g_reason_overrides_disallowed = nullptr;
+
+void DCheckOverridesAllowed() {
+ const bool feature_overrides_allowed = !g_reason_overrides_disallowed;
+ DCHECK(feature_overrides_allowed) << g_reason_overrides_disallowed;
+}
+#else
+void DCheckOverridesAllowed() {}
+#endif
+
// An allocator entry for a feature in shared memory. The FeatureEntry is
// followed by a base::Pickle object that contains the feature and trial name.
struct FeatureEntry {
@@ -86,6 +97,23 @@ FeatureList::FeatureList() = default;
FeatureList::~FeatureList() = default;
+FeatureList::ScopedDisallowOverrides::ScopedDisallowOverrides(
+ const char* reason)
+#if DCHECK_IS_ON()
+ : previous_reason_(g_reason_overrides_disallowed) {
+ g_reason_overrides_disallowed = reason;
+}
+#else
+{
+}
+#endif
+
+FeatureList::ScopedDisallowOverrides::~ScopedDisallowOverrides() {
+#if DCHECK_IS_ON()
+ g_reason_overrides_disallowed = previous_reason_;
+#endif
+}
+
void FeatureList::InitializeFromCommandLine(
const std::string& enable_features,
const std::string& disable_features) {
@@ -385,6 +413,7 @@ void FeatureList::RegisterOverride(StringPiece feature_name,
OverrideState overridden_state,
FieldTrial* field_trial) {
DCHECK(!initialized_);
+ DCheckOverridesAllowed();
if (field_trial) {
DCHECK(IsValidFeatureOrFieldTrialName(field_trial->trial_name()))
<< field_trial->trial_name();
diff --git a/chromium/base/feature_list.h b/chromium/base/feature_list.h
index a6f39daa25e..f46b30dd1ee 100644
--- a/chromium/base/feature_list.h
+++ b/chromium/base/feature_list.h
@@ -24,6 +24,8 @@ class FieldTrial;
class FieldTrialList;
// Specifies whether a given feature is enabled or disabled by default.
+// NOTE: The actual runtime state may be different, due to a field trial or a
+// command line switch.
enum FeatureState {
FEATURE_DISABLED_BY_DEFAULT,
FEATURE_ENABLED_BY_DEFAULT,
@@ -42,6 +44,8 @@ struct BASE_EXPORT Feature {
const char* const name;
// The default state (i.e. enabled or disabled) for this feature.
+ // NOTE: The actual runtime state may be different, due to a field trial or a
+ // command line switch.
const FeatureState default_state;
};
@@ -96,6 +100,21 @@ class BASE_EXPORT FeatureList {
FeatureList();
~FeatureList();
+ // Used by common test fixture classes to prevent abuse of ScopedFeatureList
+ // after multiple threads have started.
+ class BASE_EXPORT ScopedDisallowOverrides {
+ public:
+ explicit ScopedDisallowOverrides(const char* reason);
+ ~ScopedDisallowOverrides();
+
+ private:
+#if DCHECK_IS_ON()
+ const char* const previous_reason_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisallowOverrides);
+ };
+
// Specifies whether a feature override enables or disables the feature.
enum OverrideState {
OVERRIDE_USE_DEFAULT,
diff --git a/chromium/base/feature_list_unittest.cc b/chromium/base/feature_list_unittest.cc
index 813fb25229f..de9bc5d7fe8 100644
--- a/chromium/base/feature_list_unittest.cc
+++ b/chromium/base/feature_list_unittest.cc
@@ -18,6 +18,7 @@
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -45,26 +46,14 @@ std::string SortFeatureListString(const std::string& feature_list) {
class FeatureListTest : public testing::Test {
public:
- FeatureListTest() : feature_list_(nullptr) {
- RegisterFeatureListInstance(std::make_unique<FeatureList>());
+ FeatureListTest() {
+ // Provide an empty FeatureList to each test by default.
+ scoped_feature_list_.InitWithFeatureList(std::make_unique<FeatureList>());
}
- ~FeatureListTest() override { ClearFeatureListInstance(); }
-
- void RegisterFeatureListInstance(std::unique_ptr<FeatureList> feature_list) {
- FeatureList::ClearInstanceForTesting();
- feature_list_ = feature_list.get();
- FeatureList::SetInstance(std::move(feature_list));
- }
- void ClearFeatureListInstance() {
- FeatureList::ClearInstanceForTesting();
- feature_list_ = nullptr;
- }
-
- FeatureList* feature_list() { return feature_list_; }
+ ~FeatureListTest() override = default;
private:
- // Weak. Owned by the FeatureList::SetInstance().
- FeatureList* feature_list_;
+ test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(FeatureListTest);
};
@@ -96,11 +85,11 @@ TEST_F(FeatureListTest, InitializeFromCommandLine) {
test_case.enable_features,
test_case.disable_features));
- ClearFeatureListInstance();
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
feature_list->InitializeFromCommandLine(test_case.enable_features,
test_case.disable_features);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
EXPECT_EQ(test_case.expected_feature_on_state,
FeatureList::IsEnabled(kFeatureOnByDefault))
@@ -115,19 +104,23 @@ TEST_F(FeatureListTest, CheckFeatureIdentity) {
// Tests that CheckFeatureIdentity() correctly detects when two different
// structs with the same feature name are passed to it.
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::make_unique<FeatureList>());
+ FeatureList* feature_list = FeatureList::GetInstance();
+
// Call it twice for each feature at the top of the file, since the first call
// makes it remember the entry and the second call will verify it.
- EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault));
- EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault));
- EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault));
- EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault));
+ EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault));
+ EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault));
+ EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault));
+ EXPECT_TRUE(feature_list->CheckFeatureIdentity(kFeatureOffByDefault));
// Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which
// should return false.
struct Feature kFeatureOnByDefault2 {
kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT
};
- EXPECT_FALSE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault2));
+ EXPECT_FALSE(feature_list->CheckFeatureIdentity(kFeatureOnByDefault2));
}
TEST_F(FeatureListTest, FieldTrialOverrides) {
@@ -150,10 +143,8 @@ TEST_F(FeatureListTest, FieldTrialOverrides) {
const auto& test_case = test_cases[i];
SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i));
- ClearFeatureListInstance();
-
FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
@@ -161,7 +152,8 @@ TEST_F(FeatureListTest, FieldTrialOverrides) {
test_case.trial1_state, trial1);
feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName,
test_case.trial2_state, trial2);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
// Initially, neither trial should be active.
EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name()));
@@ -185,7 +177,7 @@ TEST_F(FeatureListTest, FieldTrialOverrides) {
TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) {
FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A");
FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B");
@@ -193,7 +185,8 @@ TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) {
kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1);
feature_list->RegisterFieldTrialOverride(
kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
// Initially, neither trial should be active.
EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name()));
@@ -213,10 +206,8 @@ TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) {
}
TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) {
- ClearFeatureListInstance();
-
FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
// The feature is explicitly enabled on the command-line.
feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, "");
@@ -225,7 +216,8 @@ TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) {
FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A");
feature_list->RegisterFieldTrialOverride(
kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, trial);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
// Command-line should take precedence.
@@ -237,10 +229,8 @@ TEST_F(FeatureListTest, CommandLineEnableTakesPrecedenceOverFieldTrial) {
}
TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) {
- ClearFeatureListInstance();
-
FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
// The feature is explicitly disabled on the command-line.
feature_list->InitializeFromCommandLine("", kFeatureOffByDefaultName);
@@ -249,7 +239,8 @@ TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) {
FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A");
feature_list->RegisterFieldTrialOverride(
kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, trial);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name()));
// Command-line should take precedence.
@@ -261,10 +252,8 @@ TEST_F(FeatureListTest, CommandLineDisableTakesPrecedenceOverFieldTrial) {
}
TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) {
- ClearFeatureListInstance();
-
FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
// No features are overridden from the command line yet
EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
@@ -304,7 +293,8 @@ TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) {
kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE));
EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine(
kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE));
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
// Check the expected feature states for good measure.
EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
@@ -336,10 +326,8 @@ TEST_F(FeatureListTest, AssociateReportingFieldTrial) {
test_case.enable_features,
test_case.disable_features));
- ClearFeatureListInstance();
-
FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
feature_list->InitializeFromCommandLine(test_case.enable_features,
test_case.disable_features);
@@ -364,7 +352,8 @@ TEST_F(FeatureListTest, AssociateReportingFieldTrial) {
EXPECT_EQ(test_case.expected_enable_trial_created, enable_trial != nullptr);
EXPECT_EQ(test_case.expected_disable_trial_created,
disable_trial != nullptr);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
if (disable_trial) {
@@ -380,8 +369,6 @@ TEST_F(FeatureListTest, AssociateReportingFieldTrial) {
}
TEST_F(FeatureListTest, RegisterExtraFeatureOverrides) {
- ClearFeatureListInstance();
-
auto feature_list = std::make_unique<FeatureList>();
std::vector<FeatureList::FeatureOverrideInfo> overrides;
overrides.push_back({std::cref(kFeatureOnByDefault),
@@ -389,15 +376,14 @@ TEST_F(FeatureListTest, RegisterExtraFeatureOverrides) {
overrides.push_back({std::cref(kFeatureOffByDefault),
FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault));
EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
}
TEST_F(FeatureListTest, InitializeFromCommandLineThenRegisterExtraOverrides) {
- ClearFeatureListInstance();
-
auto feature_list = std::make_unique<FeatureList>();
feature_list->InitializeFromCommandLine(kFeatureOnByDefaultName,
kFeatureOffByDefaultName);
@@ -407,7 +393,8 @@ TEST_F(FeatureListTest, InitializeFromCommandLineThenRegisterExtraOverrides) {
overrides.push_back({std::cref(kFeatureOffByDefault),
FeatureList::OverrideState::OVERRIDE_ENABLE_FEATURE});
feature_list->RegisterExtraFeatureOverrides(std::move(overrides));
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
// The InitializeFromCommandLine supersedes the RegisterExtraFeatureOverrides
// because it was called first.
@@ -423,9 +410,8 @@ TEST_F(FeatureListTest, InitializeFromCommandLineThenRegisterExtraOverrides) {
}
TEST_F(FeatureListTest, GetFeatureOverrides) {
- ClearFeatureListInstance();
FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
feature_list->InitializeFromCommandLine("A,X", "D");
Feature feature_b = {"B", FEATURE_ENABLED_BY_DEFAULT};
@@ -442,7 +428,8 @@ TEST_F(FeatureListTest, GetFeatureOverrides) {
FeatureList::OVERRIDE_ENABLE_FEATURE,
trial);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
std::string enable_features;
std::string disable_features;
@@ -458,16 +445,16 @@ TEST_F(FeatureListTest, GetFeatureOverrides) {
}
TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) {
- ClearFeatureListInstance();
FieldTrialList field_trial_list(nullptr);
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
feature_list->InitializeFromCommandLine("A,X", "D");
FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
feature_list->RegisterFieldTrialOverride(
kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
std::string enable_features;
std::string disable_features;
@@ -478,25 +465,25 @@ TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) {
}
TEST_F(FeatureListTest, GetFieldTrial) {
- ClearFeatureListInstance();
FieldTrialList field_trial_list(nullptr);
FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group");
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
feature_list->RegisterFieldTrialOverride(
kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial);
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
EXPECT_EQ(trial, FeatureList::GetFieldTrial(kFeatureOnByDefault));
EXPECT_EQ(nullptr, FeatureList::GetFieldTrial(kFeatureOffByDefault));
}
TEST_F(FeatureListTest, InitializeFromCommandLine_WithFieldTrials) {
- ClearFeatureListInstance();
FieldTrialList field_trial_list(nullptr);
FieldTrialList::CreateFieldTrial("Trial", "Group");
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
feature_list->InitializeFromCommandLine("A,OffByDefault<Trial,X", "D");
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
EXPECT_FALSE(FieldTrialList::IsTrialActive("Trial"));
EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault));
@@ -504,14 +491,14 @@ TEST_F(FeatureListTest, InitializeFromCommandLine_WithFieldTrials) {
}
TEST_F(FeatureListTest, InitializeFromCommandLine_UseDefault) {
- ClearFeatureListInstance();
FieldTrialList field_trial_list(nullptr);
FieldTrialList::CreateFieldTrial("T1", "Group");
FieldTrialList::CreateFieldTrial("T2", "Group");
- std::unique_ptr<FeatureList> feature_list(new FeatureList);
+ auto feature_list = std::make_unique<FeatureList>();
feature_list->InitializeFromCommandLine(
"A,*OffByDefault<T1,*OnByDefault<T2,X", "D");
- RegisterFeatureListInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
EXPECT_FALSE(FieldTrialList::IsTrialActive("T1"));
EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
@@ -523,10 +510,10 @@ TEST_F(FeatureListTest, InitializeFromCommandLine_UseDefault) {
}
TEST_F(FeatureListTest, InitializeInstance) {
- ClearFeatureListInstance();
-
std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
- FeatureList::SetInstance(std::move(feature_list));
+ test::ScopedFeatureList scoped_feature_list;
+ scoped_feature_list.InitWithFeatureList(std::move(feature_list));
+
EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
@@ -542,7 +529,9 @@ TEST_F(FeatureListTest, InitializeInstance) {
}
TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) {
- ClearFeatureListInstance();
+ std::unique_ptr<FeatureList> original_feature_list =
+ FeatureList::ClearInstanceForTesting();
+
// This test case simulates the calling pattern found in code which does not
// explicitly initialize the features list.
// All IsEnabled() calls should return the default value in this scenario.
@@ -550,6 +539,9 @@ TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) {
EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault));
EXPECT_EQ(nullptr, FeatureList::GetInstance());
EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault));
+
+ if (original_feature_list)
+ FeatureList::RestoreInstanceForTesting(std::move(original_feature_list));
}
TEST_F(FeatureListTest, StoreAndRetrieveFeaturesFromSharedMemory) {
diff --git a/chromium/base/file_version_info_win.cc b/chromium/base/file_version_info_win.cc
index c45765dba5d..8b8e5c6b5c3 100644
--- a/chromium/base/file_version_info_win.cc
+++ b/chromium/base/file_version_info_win.cc
@@ -7,6 +7,8 @@
#include <windows.h>
#include <stddef.h>
+#include <utility>
+
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
@@ -15,8 +17,6 @@
#include "base/threading/scoped_blocking_call.h"
#include "base/win/resource_util.h"
-using base::FilePath;
-
namespace {
struct LanguageAndCodePage {
@@ -24,26 +24,23 @@ struct LanguageAndCodePage {
WORD code_page;
};
-// Returns the \\VarFileInfo\\Translation value extracted from the
+// Returns the \VarFileInfo\Translation value extracted from the
// VS_VERSION_INFO resource in |data|.
LanguageAndCodePage* GetTranslate(const void* data) {
- LanguageAndCodePage* translate = nullptr;
- UINT length;
- if (::VerQueryValue(data, L"\\VarFileInfo\\Translation",
- reinterpret_cast<void**>(&translate), &length)) {
- return translate;
- }
+ static constexpr wchar_t kTranslation[] = L"\\VarFileInfo\\Translation";
+ LPVOID translate = nullptr;
+ UINT dummy_size;
+ if (::VerQueryValue(data, kTranslation, &translate, &dummy_size))
+ return static_cast<LanguageAndCodePage*>(translate);
return nullptr;
}
-VS_FIXEDFILEINFO* GetVsFixedFileInfo(const void* data) {
- VS_FIXEDFILEINFO* fixed_file_info = nullptr;
- UINT length;
- if (::VerQueryValue(data, L"\\", reinterpret_cast<void**>(&fixed_file_info),
- &length)) {
- return fixed_file_info;
- }
- return nullptr;
+const VS_FIXEDFILEINFO& GetVsFixedFileInfo(const void* data) {
+ static constexpr wchar_t kRoot[] = L"\\";
+ LPVOID fixed_file_info = nullptr;
+ UINT dummy_size;
+ CHECK(::VerQueryValue(data, kRoot, &fixed_file_info, &dummy_size));
+ return *static_cast<VS_FIXEDFILEINFO*>(fixed_file_info);
}
} // namespace
@@ -70,13 +67,13 @@ FileVersionInfo::CreateFileVersionInfoForModule(HMODULE module) {
// static
std::unique_ptr<FileVersionInfo> FileVersionInfo::CreateFileVersionInfo(
- const FilePath& file_path) {
+ const base::FilePath& file_path) {
return FileVersionInfoWin::CreateFileVersionInfoWin(file_path);
}
// static
std::unique_ptr<FileVersionInfoWin>
-FileVersionInfoWin::CreateFileVersionInfoWin(const FilePath& file_path) {
+FileVersionInfoWin::CreateFileVersionInfoWin(const base::FilePath& file_path) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
@@ -140,47 +137,46 @@ base::string16 FileVersionInfoWin::special_build() {
}
bool FileVersionInfoWin::GetValue(const base::char16* name,
- base::string16* value_str) {
- WORD lang_codepage[8];
- size_t i = 0;
- // Use the language and codepage from the DLL.
- lang_codepage[i++] = language_;
- lang_codepage[i++] = code_page_;
- // Use the default language and codepage from the DLL.
- lang_codepage[i++] = ::GetUserDefaultLangID();
- lang_codepage[i++] = code_page_;
- // Use the language from the DLL and Latin codepage (most common).
- lang_codepage[i++] = language_;
- lang_codepage[i++] = 1252;
- // Use the default language and Latin codepage (most common).
- lang_codepage[i++] = ::GetUserDefaultLangID();
- lang_codepage[i++] = 1252;
-
- i = 0;
- while (i < base::size(lang_codepage)) {
+ base::string16* value) const {
+ const struct LanguageAndCodePage lang_codepages[] = {
+ // Use the language and codepage from the DLL.
+ {language_, code_page_},
+ // Use the default language and codepage from the DLL.
+ {::GetUserDefaultLangID(), code_page_},
+ // Use the language from the DLL and Latin codepage (most common).
+ {language_, 1252},
+ // Use the default language and Latin codepage (most common).
+ {::GetUserDefaultLangID(), 1252},
+ };
+
+ for (const auto& lang_codepage : lang_codepages) {
wchar_t sub_block[MAX_PATH];
- WORD language = lang_codepage[i++];
- WORD code_page = lang_codepage[i++];
_snwprintf_s(sub_block, MAX_PATH, MAX_PATH,
- L"\\StringFileInfo\\%04x%04x\\%ls", language, code_page,
- base::as_wcstr(name));
- LPVOID value = NULL;
+ L"\\StringFileInfo\\%04x%04x\\%ls", lang_codepage.language,
+ lang_codepage.code_page, base::as_wcstr(name));
+ LPVOID value_ptr = nullptr;
uint32_t size;
- BOOL r = ::VerQueryValue(data_, sub_block, &value, &size);
- if (r && value) {
- value_str->assign(static_cast<base::char16*>(value));
+ BOOL r = ::VerQueryValue(data_, sub_block, &value_ptr, &size);
+ if (r && value_ptr && size) {
+ value->assign(static_cast<base::char16*>(value_ptr), size - 1);
return true;
}
}
return false;
}
-base::string16 FileVersionInfoWin::GetStringValue(const base::char16* name) {
+base::string16 FileVersionInfoWin::GetStringValue(
+ const base::char16* name) const {
base::string16 str;
- if (GetValue(name, &str))
- return str;
- else
- return base::string16();
+ GetValue(name, &str);
+ return str;
+}
+
+base::Version FileVersionInfoWin::GetFileVersion() const {
+ return base::Version({HIWORD(fixed_file_info_.dwFileVersionMS),
+ LOWORD(fixed_file_info_.dwFileVersionMS),
+ HIWORD(fixed_file_info_.dwFileVersionLS),
+ LOWORD(fixed_file_info_.dwFileVersionLS)});
}
FileVersionInfoWin::FileVersionInfoWin(std::vector<uint8_t>&& data,
diff --git a/chromium/base/file_version_info_win.h b/chromium/base/file_version_info_win.h
index 7c83dafc147..f0dbde87972 100644
--- a/chromium/base/file_version_info_win.h
+++ b/chromium/base/file_version_info_win.h
@@ -16,6 +16,7 @@
#include "base/base_export.h"
#include "base/file_version_info.h"
#include "base/macros.h"
+#include "base/version.h"
struct tagVS_FIXEDFILEINFO;
typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO;
@@ -37,15 +38,16 @@ class BASE_EXPORT FileVersionInfoWin : public FileVersionInfo {
base::string16 file_description() override;
base::string16 file_version() override;
- // Lets you access other properties not covered above.
- bool GetValue(const base::char16* name, base::string16* value);
+ // Lets you access other properties not covered above. |value| is only
+ // modified if GetValue() returns true.
+ bool GetValue(const base::char16* name, base::string16* value) const;
// Similar to GetValue but returns a string16 (empty string if the property
// does not exist).
- base::string16 GetStringValue(const base::char16* name);
+ base::string16 GetStringValue(const base::char16* name) const;
- // Get the fixed file info if it exists. Otherwise NULL
- const VS_FIXEDFILEINFO* fixed_file_info() const { return fixed_file_info_; }
+ // Get file version number in dotted version format.
+ base::Version GetFileVersion() const;
// Behaves like CreateFileVersionInfo, but returns a FileVersionInfoWin.
static std::unique_ptr<FileVersionInfoWin> CreateFileVersionInfoWin(
@@ -66,8 +68,8 @@ class BASE_EXPORT FileVersionInfoWin : public FileVersionInfo {
const WORD language_;
const WORD code_page_;
- // This is a pointer into |data_| if it exists. Otherwise nullptr.
- const VS_FIXEDFILEINFO* const fixed_file_info_;
+ // This is a reference for a portion of |data_|.
+ const VS_FIXEDFILEINFO& fixed_file_info_;
DISALLOW_COPY_AND_ASSIGN(FileVersionInfoWin);
};
diff --git a/chromium/base/file_version_info_win_unittest.cc b/chromium/base/file_version_info_win_unittest.cc
index 7a761abcae1..6c81bb7cb30 100644
--- a/chromium/base/file_version_info_win_unittest.cc
+++ b/chromium/base/file_version_info_win_unittest.cc
@@ -152,4 +152,15 @@ TYPED_TEST(FileVersionInfoTest, CustomProperties) {
version_info_win->GetValue(STRING16_LITERAL("Unknown property"), &str));
EXPECT_EQ(base::string16(), version_info_win->GetStringValue(
STRING16_LITERAL("Unknown property")));
+
+ EXPECT_EQ(base::Version(std::vector<uint32_t>{1, 0, 0, 1}),
+ version_info_win->GetFileVersion());
+}
+
+TYPED_TEST(FileVersionInfoTest, NoVersionInfo) {
+ FilePath dll_path = GetTestDataPath();
+ dll_path = dll_path.AppendASCII("no_version_info.dll");
+
+ TypeParam factory(dll_path);
+ ASSERT_FALSE(factory.Create());
}
diff --git a/chromium/base/files/file.cc b/chromium/base/files/file.cc
index e8934b1cdc3..da0b52ef48a 100644
--- a/chromium/base/files/file.cc
+++ b/chromium/base/files/file.cc
@@ -6,6 +6,7 @@
#include "base/files/file_path.h"
#include "base/files/file_tracing.h"
#include "base/metrics/histogram.h"
+#include "base/numerics/safe_conversions.h"
#include "base/timer/elapsed_timer.h"
#include "build/build_config.h"
@@ -96,6 +97,29 @@ void File::Initialize(const FilePath& path, uint32_t flags) {
}
#endif
+bool File::ReadAndCheck(int64_t offset, span<uint8_t> data) {
+ int size = checked_cast<int>(data.size());
+ return Read(offset, reinterpret_cast<char*>(data.data()), size) == size;
+}
+
+bool File::ReadAtCurrentPosAndCheck(span<uint8_t> data) {
+ int size = checked_cast<int>(data.size());
+ return ReadAtCurrentPos(reinterpret_cast<char*>(data.data()), size) == size;
+}
+
+bool File::WriteAndCheck(int64_t offset, span<const uint8_t> data) {
+ int size = checked_cast<int>(data.size());
+ return Write(offset, reinterpret_cast<const char*>(data.data()), size) ==
+ size;
+}
+
+bool File::WriteAtCurrentPosAndCheck(span<const uint8_t> data) {
+ int size = checked_cast<int>(data.size());
+ return WriteAtCurrentPos(reinterpret_cast<const char*>(data.data()), size) ==
+ size;
+}
+
+// static
std::string File::ErrorToString(Error error) {
switch (error) {
case FILE_OK:
diff --git a/chromium/base/files/file.h b/chromium/base/files/file.h
index cc157a49e97..d61c9f43b7b 100644
--- a/chromium/base/files/file.h
+++ b/chromium/base/files/file.h
@@ -10,10 +10,10 @@
#include <string>
#include "base/base_export.h"
+#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/file_tracing.h"
#include "base/files/platform_file.h"
-#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "build/build_config.h"
@@ -201,6 +201,14 @@ class BASE_EXPORT File {
// (relative to the start) or -1 in case of error.
int64_t Seek(Whence whence, int64_t offset);
+ // Simplified versions of Read() and friends (see below) that check the int
+ // return value and just return a boolean. They return true if and only if
+ // the function read in / wrote out exactly |size| bytes of data.
+ bool ReadAndCheck(int64_t offset, span<uint8_t> data);
+ bool ReadAtCurrentPosAndCheck(span<uint8_t> data);
+ bool WriteAndCheck(int64_t offset, span<const uint8_t> data);
+ bool WriteAtCurrentPosAndCheck(span<const uint8_t> data);
+
// Reads the given number of bytes (or until EOF is reached) starting with the
// given offset. Returns the number of bytes read, or -1 on error. Note that
// this function makes a best effort to read all data on all platforms, so it
diff --git a/chromium/base/files/file_path_watcher_unittest.cc b/chromium/base/files/file_path_watcher_unittest.cc
index 35ee50294ca..91e116d2075 100644
--- a/chromium/base/files/file_path_watcher_unittest.cc
+++ b/chromium/base/files/file_path_watcher_unittest.cc
@@ -399,7 +399,7 @@ TEST_F(FilePathWatcherTest, DisappearingDirectory) {
std::unique_ptr<TestDelegate> delegate(new TestDelegate(collector()));
ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
- ASSERT_TRUE(base::DeleteFile(dir, true));
+ ASSERT_TRUE(base::DeleteFileRecursively(dir));
ASSERT_TRUE(WaitForEvents());
}
diff --git a/chromium/base/files/file_posix.cc b/chromium/base/files/file_posix.cc
index 00ce7dee428..f8bdf1b4fd9 100644
--- a/chromium/base/files/file_posix.cc
+++ b/chromium/base/files/file_posix.cc
@@ -130,6 +130,13 @@ void File::Info::FromStat(const stat_wrapper_t& stat_info) {
is_symbolic_link = S_ISLNK(stat_info.st_mode);
size = stat_info.st_size;
+ // Get last modification time, last access time, and creation time from
+ // |stat_info|.
+ // Note: st_ctime is actually last status change time when the inode was last
+ // updated, which happens on any metadata change. It is not the file's
+ // creation time. However, other than on Mac & iOS where the actual file
+ // creation time is included as st_birthtime, the rest of POSIX platforms have
+ // no portable way to get the creation time.
#if defined(OS_LINUX) || defined(OS_FUCHSIA)
time_t last_modified_sec = stat_info.st_mtim.tv_sec;
int64_t last_modified_nsec = stat_info.st_mtim.tv_nsec;
@@ -144,7 +151,14 @@ void File::Info::FromStat(const stat_wrapper_t& stat_info) {
int64_t last_accessed_nsec = stat_info.st_atime_nsec;
time_t creation_time_sec = stat_info.st_ctime;
int64_t creation_time_nsec = stat_info.st_ctime_nsec;
-#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
+#elif defined(OS_MACOSX) || defined(OS_IOS)
+ time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
+ int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
+ time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
+ int64_t last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
+ time_t creation_time_sec = stat_info.st_birthtimespec.tv_sec;
+ int64_t creation_time_nsec = stat_info.st_birthtimespec.tv_nsec;
+#elif defined(OS_BSD)
time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
int64_t last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
diff --git a/chromium/base/files/file_unittest.cc b/chromium/base/files/file_unittest.cc
index 6d536538ce0..799883cfc8a 100644
--- a/chromium/base/files/file_unittest.cc
+++ b/chromium/base/files/file_unittest.cc
@@ -432,6 +432,29 @@ TEST(FileTest, DISABLED_TouchGetInfo) {
creation_time.ToInternalValue());
}
+// Test we can retrieve the file's creation time through File::GetInfo().
+TEST(FileTest, GetInfoForCreationTime) {
+ int64_t before_creation_time_s =
+ base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds();
+
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.GetPath().AppendASCII("test_file");
+ File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
+ EXPECT_TRUE(file.IsValid());
+
+ int64_t after_creation_time_s =
+ base::Time::Now().ToDeltaSinceWindowsEpoch().InSeconds();
+
+ base::File::Info info;
+ EXPECT_TRUE(file.GetInfo(&info));
+ EXPECT_GE(info.creation_time.ToDeltaSinceWindowsEpoch().InSeconds(),
+ before_creation_time_s);
+ EXPECT_LE(info.creation_time.ToDeltaSinceWindowsEpoch().InSeconds(),
+ after_creation_time_s);
+}
+
TEST(FileTest, ReadAtCurrentPosition) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
diff --git a/chromium/base/files/file_util.h b/chromium/base/files/file_util.h
index 59fbf44e6e0..571f90f8eff 100644
--- a/chromium/base/files/file_util.h
+++ b/chromium/base/files/file_util.h
@@ -63,13 +63,28 @@ BASE_EXPORT int64_t ComputeDirectorySize(const FilePath& root_path);
// Returns true if successful, false otherwise. It is considered successful
// to attempt to delete a file that does not exist.
//
-// In posix environment and if |path| is a symbolic link, this deletes only
+// In POSIX environment and if |path| is a symbolic link, this deletes only
// the symlink. (even if the symlink points to a non-existent file)
//
// WARNING: USING THIS WITH recursive==true IS EQUIVALENT
// TO "rm -rf", SO USE WITH CAUTION.
+//
+// Note: The |recursive| parameter is in the process of being removed. Use
+// DeleteFileRecursively() instead. See https://crbug.com/1009837
BASE_EXPORT bool DeleteFile(const FilePath& path, bool recursive);
+// Deletes the given path, whether it's a file or a directory.
+// If it's a directory, it's perfectly happy to delete all of the
+// directory's contents, including subdirectories and their contents.
+// Returns true if successful, false otherwise. It is considered successful
+// to attempt to delete a file that does not exist.
+//
+// In POSIX environment and if |path| is a symbolic link, this deletes only
+// the symlink. (even if the symlink points to a non-existent file)
+//
+// WARNING: USING THIS EQUIVALENT TO "rm -rf", SO USE WITH CAUTION.
+BASE_EXPORT bool DeleteFileRecursively(const FilePath& path);
+
#if defined(OS_WIN)
// Schedules to delete the given path, whether it's a file or a directory, until
// the operating system is restarted.
diff --git a/chromium/base/files/file_util_posix.cc b/chromium/base/files/file_util_posix.cc
index 5743863ed89..bedcc78877a 100644
--- a/chromium/base/files/file_util_posix.cc
+++ b/chromium/base/files/file_util_posix.cc
@@ -43,6 +43,7 @@
#include "base/system/sys_info.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/time/time.h"
+#include "build/branding_buildflags.h"
#include "build/build_config.h"
#if defined(OS_MACOSX)
@@ -134,7 +135,7 @@ std::string TempFileName() {
return StringPrintf(".%s.XXXXXX", base::mac::BaseBundleID());
#endif
-#if defined(GOOGLE_CHROME_BUILD)
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
return std::string(".com.google.Chrome.XXXXXX");
#else
return std::string(".org.chromium.Chromium.XXXXXX");
@@ -319,37 +320,12 @@ bool DoCopyDirectory(const FilePath& from_path,
return true;
}
-#endif // !defined(OS_NACL_NONSFI)
-
-#if !defined(OS_MACOSX)
-// Appends |mode_char| to |mode| before the optional character set encoding; see
-// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for
-// details.
-std::string AppendModeCharacter(StringPiece mode, char mode_char) {
- std::string result(mode.as_string());
- size_t comma_pos = result.find(',');
- result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1,
- mode_char);
- return result;
-}
-#endif
-
-} // namespace
-
-#if !defined(OS_NACL_NONSFI)
-FilePath MakeAbsoluteFilePath(const FilePath& input) {
- ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
- char full_path[PATH_MAX];
- if (realpath(input.value().c_str(), full_path) == nullptr)
- return FilePath();
- return FilePath(full_path);
-}
// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*"
// which works both with and without the recursive flag. I'm not sure we need
// that functionality. If not, remove from file_util_win.cc, otherwise add it
// here.
-bool DeleteFile(const FilePath& path, bool recursive) {
+bool DoDeleteFile(const FilePath& path, bool recursive) {
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
#if defined(OS_ANDROID)
@@ -389,6 +365,39 @@ bool DeleteFile(const FilePath& path, bool recursive) {
}
return success;
}
+#endif // !defined(OS_NACL_NONSFI)
+
+#if !defined(OS_MACOSX)
+// Appends |mode_char| to |mode| before the optional character set encoding; see
+// https://www.gnu.org/software/libc/manual/html_node/Opening-Streams.html for
+// details.
+std::string AppendModeCharacter(StringPiece mode, char mode_char) {
+ std::string result(mode.as_string());
+ size_t comma_pos = result.find(',');
+ result.insert(comma_pos == std::string::npos ? result.length() : comma_pos, 1,
+ mode_char);
+ return result;
+}
+#endif
+
+} // namespace
+
+#if !defined(OS_NACL_NONSFI)
+FilePath MakeAbsoluteFilePath(const FilePath& input) {
+ ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
+ char full_path[PATH_MAX];
+ if (realpath(input.value().c_str(), full_path) == nullptr)
+ return FilePath();
+ return FilePath(full_path);
+}
+
+bool DeleteFile(const FilePath& path, bool recursive) {
+ return DoDeleteFile(path, recursive);
+}
+
+bool DeleteFileRecursively(const FilePath& path) {
+ return DoDeleteFile(path, /*recursive=*/true);
+}
bool ReplaceFile(const FilePath& from_path,
const FilePath& to_path,
@@ -1172,7 +1181,7 @@ bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
if (!CopyDirectory(from_path, to_path, true))
return false;
- DeleteFile(from_path, true);
+ DeleteFileRecursively(from_path);
return true;
}
diff --git a/chromium/base/files/file_util_unittest.cc b/chromium/base/files/file_util_unittest.cc
index fc6e5ed57f0..d79ec1fd2e7 100644
--- a/chromium/base/files/file_util_unittest.cc
+++ b/chromium/base/files/file_util_unittest.cc
@@ -1015,7 +1015,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) {
EXPECT_EQ(1, c2.size());
// Delete the file.
- EXPECT_TRUE(DeleteFile(subdir_path, true));
+ EXPECT_TRUE(DeleteFileRecursively(subdir_path));
EXPECT_FALSE(PathExists(subdir_path));
}
@@ -1395,7 +1395,7 @@ TEST_F(FileUtilTest, DeleteNonExistent) {
EXPECT_TRUE(DeleteFile(non_existent, false));
ASSERT_FALSE(PathExists(non_existent));
- EXPECT_TRUE(DeleteFile(non_existent, true));
+ EXPECT_TRUE(DeleteFileRecursively(non_existent));
ASSERT_FALSE(PathExists(non_existent));
}
@@ -1406,7 +1406,7 @@ TEST_F(FileUtilTest, DeleteNonExistentWithNonExistentParent) {
EXPECT_TRUE(DeleteFile(non_existent, false));
ASSERT_FALSE(PathExists(non_existent));
- EXPECT_TRUE(DeleteFile(non_existent, true));
+ EXPECT_TRUE(DeleteFileRecursively(non_existent));
ASSERT_FALSE(PathExists(non_existent));
}
@@ -1426,7 +1426,7 @@ TEST_F(FileUtilTest, DeleteFile) {
ASSERT_TRUE(PathExists(file_name));
// Make sure it's deleted
- EXPECT_TRUE(DeleteFile(file_name, true));
+ EXPECT_TRUE(DeleteFileRecursively(file_name));
EXPECT_FALSE(PathExists(file_name));
}
@@ -1482,7 +1482,7 @@ TEST_F(FileUtilTest, DeleteWildCard) {
EXPECT_TRUE(PathExists(subdir_path));
// Delete recursively and make sure all contents are deleted
- EXPECT_TRUE(DeleteFile(directory_contents, true));
+ EXPECT_TRUE(DeleteFileRecursively(directory_contents));
EXPECT_FALSE(PathExists(file_name));
EXPECT_FALSE(PathExists(subdir_path));
}
@@ -1504,7 +1504,7 @@ TEST_F(FileUtilTest, DeleteNonExistantWildCard) {
EXPECT_TRUE(PathExists(subdir_path));
// Delete recursively and check nothing got deleted
- EXPECT_TRUE(DeleteFile(directory_contents, true));
+ EXPECT_TRUE(DeleteFileRecursively(directory_contents));
EXPECT_TRUE(PathExists(subdir_path));
}
#endif
@@ -1560,11 +1560,11 @@ TEST_F(FileUtilTest, DeleteDirRecursive) {
ASSERT_TRUE(PathExists(subdir_path2));
// Delete recursively and check that the empty dir got deleted
- EXPECT_TRUE(DeleteFile(subdir_path2, true));
+ EXPECT_TRUE(DeleteFileRecursively(subdir_path2));
EXPECT_FALSE(PathExists(subdir_path2));
// Delete recursively and check that everything got deleted
- EXPECT_TRUE(DeleteFile(test_subdir, true));
+ EXPECT_TRUE(DeleteFileRecursively(test_subdir));
EXPECT_FALSE(PathExists(file_name));
EXPECT_FALSE(PathExists(subdir_path1));
EXPECT_FALSE(PathExists(test_subdir));
@@ -1609,7 +1609,7 @@ TEST_F(FileUtilTest, DeleteDirRecursiveWithOpenFile) {
// Delete recursively and check that at least the second file got deleted.
// This ensures that un-deletable files don't impact those that can be.
- DeleteFile(test_subdir, true);
+ DeleteFileRecursively(test_subdir);
EXPECT_FALSE(PathExists(file_name2));
#if defined(OS_LINUX)
@@ -2750,7 +2750,7 @@ TEST_F(FileUtilTest, CreateDirectoryTest) {
EXPECT_TRUE(PathExists(test_path));
EXPECT_FALSE(CreateDirectory(test_path));
- EXPECT_TRUE(DeleteFile(test_root, true));
+ EXPECT_TRUE(DeleteFileRecursively(test_root));
EXPECT_FALSE(PathExists(test_root));
EXPECT_FALSE(PathExists(test_path));
@@ -2797,7 +2797,7 @@ TEST_F(FileUtilTest, DetectDirectoryTest) {
EXPECT_FALSE(DirectoryExists(test_path));
EXPECT_TRUE(DeleteFile(test_path, false));
- EXPECT_TRUE(DeleteFile(test_root, true));
+ EXPECT_TRUE(DeleteFileRecursively(test_root));
}
TEST_F(FileUtilTest, FileEnumeratorTest) {
@@ -2954,13 +2954,13 @@ TEST_F(FileUtilTest, AppendToFile) {
// Create a fresh, empty copy of this directory.
if (PathExists(data_dir)) {
- ASSERT_TRUE(DeleteFile(data_dir, true));
+ ASSERT_TRUE(DeleteFileRecursively(data_dir));
}
ASSERT_TRUE(CreateDirectory(data_dir));
// Create a fresh, empty copy of this directory.
if (PathExists(data_dir)) {
- ASSERT_TRUE(DeleteFile(data_dir, true));
+ ASSERT_TRUE(DeleteFileRecursively(data_dir));
}
ASSERT_TRUE(CreateDirectory(data_dir));
FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt")));
@@ -3470,7 +3470,7 @@ TEST_F(FileUtilTest, TouchFile) {
// Create a fresh, empty copy of this directory.
if (PathExists(data_dir)) {
- ASSERT_TRUE(DeleteFile(data_dir, true));
+ ASSERT_TRUE(DeleteFileRecursively(data_dir));
}
ASSERT_TRUE(CreateDirectory(data_dir));
diff --git a/chromium/base/files/file_util_win.cc b/chromium/base/files/file_util_win.cc
index 94cff1ee29f..4d47144d0c4 100644
--- a/chromium/base/files/file_util_win.cc
+++ b/chromium/base/files/file_util_win.cc
@@ -348,18 +348,7 @@ DWORD DoDeleteFile(const FilePath& path, bool recursive) {
: ReturnLastErrorOrSuccessOnNotFound();
}
-} // namespace
-
-FilePath MakeAbsoluteFilePath(const FilePath& input) {
- ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
- char16 file_path[MAX_PATH];
- if (!_wfullpath(as_writable_wcstr(file_path), as_wcstr(input.value()),
- MAX_PATH))
- return FilePath();
- return FilePath(file_path);
-}
-
-bool DeleteFile(const FilePath& path, bool recursive) {
+bool DeleteFileAndRecordMetrics(const FilePath& path, bool recursive) {
static constexpr char kRecursive[] = "DeleteFile.Recursive";
static constexpr char kNonRecursive[] = "DeleteFile.NonRecursive";
const StringPiece operation(recursive ? kRecursive : kNonRecursive);
@@ -379,6 +368,25 @@ bool DeleteFile(const FilePath& path, bool recursive) {
return false;
}
+} // namespace
+
+FilePath MakeAbsoluteFilePath(const FilePath& input) {
+ ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
+ char16 file_path[MAX_PATH];
+ if (!_wfullpath(as_writable_wcstr(file_path), as_wcstr(input.value()),
+ MAX_PATH))
+ return FilePath();
+ return FilePath(file_path);
+}
+
+bool DeleteFile(const FilePath& path, bool recursive) {
+ return DeleteFileAndRecordMetrics(path, recursive);
+}
+
+bool DeleteFileRecursively(const FilePath& path) {
+ return DeleteFileAndRecordMetrics(path, /*recursive=*/true);
+}
+
bool DeleteFileAfterReboot(const FilePath& path) {
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
@@ -1025,7 +1033,7 @@ bool CopyAndDeleteDirectory(const FilePath& from_path,
const FilePath& to_path) {
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
if (CopyDirectory(from_path, to_path, true)) {
- if (DeleteFile(from_path, true))
+ if (DeleteFileRecursively(from_path))
return true;
// Like Move, this function is not transactional, so we just
diff --git a/chromium/base/files/scoped_temp_dir.cc b/chromium/base/files/scoped_temp_dir.cc
index 01ec0f0caab..359b442579e 100644
--- a/chromium/base/files/scoped_temp_dir.cc
+++ b/chromium/base/files/scoped_temp_dir.cc
@@ -29,7 +29,7 @@ bool ScopedTempDir::CreateUniqueTempDir() {
// This "scoped_dir" prefix is only used on Windows and serves as a template
// for the unique name.
- if (!base::CreateNewTempDirectory(kScopedDirPrefix, &path_))
+ if (!CreateNewTempDirectory(kScopedDirPrefix, &path_))
return false;
return true;
@@ -40,11 +40,11 @@ bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) {
return false;
// If |base_path| does not exist, create it.
- if (!base::CreateDirectory(base_path))
+ if (!CreateDirectory(base_path))
return false;
// Create a new, uniquely named directory under |base_path|.
- if (!base::CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_))
+ if (!CreateTemporaryDirInDir(base_path, kScopedDirPrefix, &path_))
return false;
return true;
@@ -54,7 +54,7 @@ bool ScopedTempDir::Set(const FilePath& path) {
if (!path_.empty())
return false;
- if (!DirectoryExists(path) && !base::CreateDirectory(path))
+ if (!DirectoryExists(path) && !CreateDirectory(path))
return false;
path_ = path;
@@ -65,7 +65,7 @@ bool ScopedTempDir::Delete() {
if (path_.empty())
return false;
- bool ret = base::DeleteFile(path_, true);
+ bool ret = DeleteFileRecursively(path_);
if (ret) {
// We only clear the path if deleted the directory.
path_.clear();
diff --git a/chromium/base/files/scoped_temp_dir_unittest.cc b/chromium/base/files/scoped_temp_dir_unittest.cc
index 024b438aa01..122d138bdb7 100644
--- a/chromium/base/files/scoped_temp_dir_unittest.cc
+++ b/chromium/base/files/scoped_temp_dir_unittest.cc
@@ -14,8 +14,7 @@ namespace base {
TEST(ScopedTempDir, FullPath) {
FilePath test_path;
- base::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"),
- &test_path);
+ CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"), &test_path);
// Against an existing dir, it should get destroyed when leaving scope.
EXPECT_TRUE(DirectoryExists(test_path));
@@ -56,7 +55,7 @@ TEST(ScopedTempDir, TempDir) {
test_path = dir.GetPath();
EXPECT_TRUE(DirectoryExists(test_path));
FilePath tmp_dir;
- EXPECT_TRUE(base::GetTempDir(&tmp_dir));
+ EXPECT_TRUE(GetTempDir(&tmp_dir));
EXPECT_TRUE(test_path.value().find(tmp_dir.value()) != std::string::npos);
}
EXPECT_FALSE(DirectoryExists(test_path));
@@ -65,8 +64,8 @@ TEST(ScopedTempDir, TempDir) {
TEST(ScopedTempDir, UniqueTempDirUnderPath) {
// Create a path which will contain a unique temp path.
FilePath base_path;
- ASSERT_TRUE(base::CreateNewTempDirectory(FILE_PATH_LITERAL("base_dir"),
- &base_path));
+ ASSERT_TRUE(
+ CreateNewTempDirectory(FILE_PATH_LITERAL("base_dir"), &base_path));
FilePath test_path;
{
@@ -78,7 +77,7 @@ TEST(ScopedTempDir, UniqueTempDirUnderPath) {
EXPECT_TRUE(test_path.value().find(base_path.value()) != std::string::npos);
}
EXPECT_FALSE(DirectoryExists(test_path));
- base::DeleteFile(base_path, true);
+ DeleteFileRecursively(base_path);
}
TEST(ScopedTempDir, MultipleInvocations) {
@@ -99,10 +98,10 @@ TEST(ScopedTempDir, MultipleInvocations) {
TEST(ScopedTempDir, LockedTempDir) {
ScopedTempDir dir;
EXPECT_TRUE(dir.CreateUniqueTempDir());
- base::File file(dir.GetPath().Append(FILE_PATH_LITERAL("temp")),
- base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ File file(dir.GetPath().Append(FILE_PATH_LITERAL("temp")),
+ File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
EXPECT_TRUE(file.IsValid());
- EXPECT_EQ(base::File::FILE_OK, file.error_details());
+ EXPECT_EQ(File::FILE_OK, file.error_details());
EXPECT_FALSE(dir.Delete()); // We should not be able to delete.
EXPECT_FALSE(dir.GetPath().empty()); // We should still have a valid path.
file.Close();
diff --git a/chromium/base/fuchsia/filtered_service_directory.h b/chromium/base/fuchsia/filtered_service_directory.h
index 8b4bce395b6..dc1205423d8 100644
--- a/chromium/base/fuchsia/filtered_service_directory.h
+++ b/chromium/base/fuchsia/filtered_service_directory.h
@@ -18,7 +18,7 @@
namespace base {
namespace fuchsia {
-// ServiceDirectory that uses the supplied ServiceDirectoryClient to satisfy
+// ServiceDirectory that uses the supplied sys::ServiceDirectory to satisfy
// requests for only a restricted set of services.
class BASE_EXPORT FilteredServiceDirectory {
public:
diff --git a/chromium/base/fuchsia/scoped_service_binding.h b/chromium/base/fuchsia/scoped_service_binding.h
index ea11d0272b7..fe37cbeedae 100644
--- a/chromium/base/fuchsia/scoped_service_binding.h
+++ b/chromium/base/fuchsia/scoped_service_binding.h
@@ -152,7 +152,7 @@ class ScopedSingleClientServiceBinding
dispatcher);
}
- void OnBindingEmpty() {
+ void OnBindingEmpty(zx_status_t status) {
binding_.set_error_handler(nullptr);
std::move(on_last_client_callback_).Run();
}
diff --git a/chromium/base/fuchsia/scoped_service_binding_unittest.cc b/chromium/base/fuchsia/scoped_service_binding_unittest.cc
index ddbea039e2a..4368055cad4 100644
--- a/chromium/base/fuchsia/scoped_service_binding_unittest.cc
+++ b/chromium/base/fuchsia/scoped_service_binding_unittest.cc
@@ -106,5 +106,21 @@ TEST_F(ScopedServiceBindingTest, ConnectDebugService) {
VerifyTestInterface(&release_stub, ZX_ERR_PEER_CLOSED);
}
+TEST_F(ScopedServiceBindingTest, SingleBindingSetOnLastClientCallback) {
+ service_binding_.reset();
+ ScopedSingleClientServiceBinding<testfidl::TestInterface>
+ single_service_binding(outgoing_directory_.get(), &test_service_);
+
+ base::RunLoop run_loop;
+ single_service_binding.SetOnLastClientCallback(run_loop.QuitClosure());
+
+ auto current_client =
+ public_service_directory_->Connect<testfidl::TestInterface>();
+ VerifyTestInterface(&current_client, ZX_OK);
+ current_client.Unbind();
+
+ run_loop.Run();
+}
+
} // namespace fuchsia
} // namespace base
diff --git a/chromium/base/fuchsia/service_directory_test_base.h b/chromium/base/fuchsia/service_directory_test_base.h
index 3ff6006627e..8cfbeb3aa06 100644
--- a/chromium/base/fuchsia/service_directory_test_base.h
+++ b/chromium/base/fuchsia/service_directory_test_base.h
@@ -31,9 +31,8 @@ class ServiceDirectoryTestBase : public testing::Test {
protected:
const RunLoop::ScopedRunTimeoutForTest run_timeout_;
- base::test::TaskEnvironment task_environment_{
- base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY,
- base::test::TaskEnvironment::MainThreadType::IO};
+ base::test::SingleThreadTaskEnvironment task_environment_{
+ base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
std::unique_ptr<sys::OutgoingDirectory> outgoing_directory_;
TestInterfaceImpl test_service_;
diff --git a/chromium/base/fuchsia/service_provider_impl_unittest.cc b/chromium/base/fuchsia/service_provider_impl_unittest.cc
index dcb8c0b7690..bc77af08065 100644
--- a/chromium/base/fuchsia/service_provider_impl_unittest.cc
+++ b/chromium/base/fuchsia/service_provider_impl_unittest.cc
@@ -55,9 +55,8 @@ class ServiceProviderImplTest : public testing::Test {
}
protected:
- base::test::TaskEnvironment task_environment_{
- base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY,
- base::test::TaskEnvironment::MainThreadType::IO};
+ base::test::SingleThreadTaskEnvironment task_environment_{
+ base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
TestInterfaceImpl test_service_;
sys::OutgoingDirectory service_directory_;
diff --git a/chromium/base/hash/sha1.cc b/chromium/base/hash/sha1.cc
index 1a809d1cb46..4528d0bea8f 100644
--- a/chromium/base/hash/sha1.cc
+++ b/chromium/base/hash/sha1.cc
@@ -29,10 +29,6 @@ namespace base {
//
// to reuse the instance of sha, call sha.Init();
-// TODO(jhawkins): Replace this implementation with a per-platform
-// implementation using each platform's crypto library. See
-// http://crbug.com/47218
-
class SecureHashAlgorithm {
public:
SecureHashAlgorithm() { Init(); }
@@ -191,6 +187,12 @@ void SecureHashAlgorithm::Process() {
cursor = 0;
}
+std::array<uint8_t, kSHA1Length> SHA1HashSpan(span<const uint8_t> data) {
+ std::array<uint8_t, kSHA1Length> hash;
+ SHA1HashBytes(data.data(), data.size(), hash.data());
+ return hash;
+}
+
std::string SHA1HashString(const std::string& str) {
char hash[SecureHashAlgorithm::kDigestSizeBytes];
SHA1HashBytes(reinterpret_cast<const unsigned char*>(str.c_str()),
diff --git a/chromium/base/hash/sha1.h b/chromium/base/hash/sha1.h
index 525137eab3b..dc0d59f153e 100644
--- a/chromium/base/hash/sha1.h
+++ b/chromium/base/hash/sha1.h
@@ -7,9 +7,11 @@
#include <stddef.h>
+#include <array>
#include <string>
#include "base/base_export.h"
+#include "base/containers/span.h"
namespace base {
@@ -17,6 +19,10 @@ namespace base {
enum { kSHA1Length = 20 }; // Length in bytes of a SHA-1 hash.
+// Computes the SHA-1 hash of the input |data| and returns the full hash.
+BASE_EXPORT std::array<uint8_t, kSHA1Length> SHA1HashSpan(
+ span<const uint8_t> data);
+
// Computes the SHA-1 hash of the input string |str| and returns the full
// hash.
BASE_EXPORT std::string SHA1HashString(const std::string& str);
diff --git a/chromium/base/hash/sha1_boringssl.cc b/chromium/base/hash/sha1_boringssl.cc
index 53eafbc84db..7e9d604fb34 100644
--- a/chromium/base/hash/sha1_boringssl.cc
+++ b/chromium/base/hash/sha1_boringssl.cc
@@ -12,17 +12,24 @@
namespace base {
-void SHA1HashBytes(const unsigned char* data, size_t len, unsigned char* hash) {
+std::array<uint8_t, kSHA1Length> SHA1HashSpan(span<const uint8_t> data) {
CRYPTO_library_init();
- SHA1(data, len, hash);
+ std::array<uint8_t, kSHA1Length> digest;
+ SHA1(data.data(), data.size(), digest.data());
+ return digest;
}
std::string SHA1HashString(const std::string& str) {
CRYPTO_library_init();
std::string digest;
SHA1(reinterpret_cast<const uint8_t*>(str.data()), str.size(),
- reinterpret_cast<uint8_t*>(base::WriteInto(&digest, kSHA1Length + 1)));
+ reinterpret_cast<uint8_t*>(WriteInto(&digest, kSHA1Length + 1)));
return digest;
}
+void SHA1HashBytes(const unsigned char* data, size_t len, unsigned char* hash) {
+ CRYPTO_library_init();
+ SHA1(data, len, hash);
+}
+
} // namespace base
diff --git a/chromium/base/hash/sha1_unittest.cc b/chromium/base/hash/sha1_unittest.cc
index 3d69ef6c3cf..f87221dd5af 100644
--- a/chromium/base/hash/sha1_unittest.cc
+++ b/chromium/base/hash/sha1_unittest.cc
@@ -14,12 +14,14 @@ TEST(SHA1Test, Test1) {
// Example A.1 from FIPS 180-2: one-block message.
std::string input = "abc";
- int expected[] = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
- 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d};
+ static constexpr int kExpected[] = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81,
+ 0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50,
+ 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d};
std::string output = base::SHA1HashString(input);
+ ASSERT_EQ(base::kSHA1Length, output.size());
for (size_t i = 0; i < base::kSHA1Length; i++)
- EXPECT_EQ(expected[i], output[i] & 0xFF);
+ EXPECT_EQ(kExpected[i], output[i] & 0xFF);
}
TEST(SHA1Test, Test2) {
@@ -27,68 +29,87 @@ TEST(SHA1Test, Test2) {
std::string input =
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
- int expected[] = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae,
- 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1};
+ static constexpr int kExpected[] = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2,
+ 0x6e, 0xba, 0xae, 0x4a, 0xa1, 0xf9, 0x51,
+ 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1};
std::string output = base::SHA1HashString(input);
+ ASSERT_EQ(base::kSHA1Length, output.size());
for (size_t i = 0; i < base::kSHA1Length; i++)
- EXPECT_EQ(expected[i], output[i] & 0xFF);
+ EXPECT_EQ(kExpected[i], output[i] & 0xFF);
}
TEST(SHA1Test, Test3) {
// Example A.3 from FIPS 180-2: long message.
std::string input(1000000, 'a');
- int expected[] = {0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e,
- 0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f};
+ static constexpr int kExpected[] = {0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda,
+ 0xa4, 0xf6, 0x1e, 0xeb, 0x2b, 0xdb, 0xad,
+ 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f};
std::string output = base::SHA1HashString(input);
+ ASSERT_EQ(base::kSHA1Length, output.size());
for (size_t i = 0; i < base::kSHA1Length; i++)
- EXPECT_EQ(expected[i], output[i] & 0xFF);
+ EXPECT_EQ(kExpected[i], output[i] & 0xFF);
}
-TEST(SHA1Test, Test1Bytes) {
+TEST(SHA1Test, Test1BytesAndSpan) {
// Example A.1 from FIPS 180-2: one-block message.
std::string input = "abc";
unsigned char output[base::kSHA1Length];
- unsigned char expected[] = {0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81,
- 0x6a, 0xba, 0x3e, 0x25, 0x71, 0x78, 0x50,
- 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d};
+ static constexpr unsigned char kExpected[] = {
+ 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e,
+ 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d};
base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
- input.length(), output);
+ input.size(), output);
for (size_t i = 0; i < base::kSHA1Length; i++)
- EXPECT_EQ(expected[i], output[i]);
+ EXPECT_EQ(kExpected[i], output[i]);
+
+ std::array<uint8_t, base::kSHA1Length> output_array =
+ base::SHA1HashSpan(base::as_bytes(base::make_span(input)));
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(kExpected[i], output_array[i]);
}
-TEST(SHA1Test, Test2Bytes) {
+TEST(SHA1Test, Test2BytesAndSpan) {
// Example A.2 from FIPS 180-2: multi-block message.
std::string input =
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
unsigned char output[base::kSHA1Length];
- unsigned char expected[] = {0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2,
- 0x6e, 0xba, 0xae, 0x4a, 0xa1, 0xf9, 0x51,
- 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1};
+ static constexpr unsigned char kExpected[] = {
+ 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae,
+ 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1};
base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
- input.length(), output);
+ input.size(), output);
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(kExpected[i], output[i]);
+
+ std::array<uint8_t, base::kSHA1Length> output_array =
+ base::SHA1HashSpan(base::as_bytes(base::make_span(input)));
for (size_t i = 0; i < base::kSHA1Length; i++)
- EXPECT_EQ(expected[i], output[i]);
+ EXPECT_EQ(kExpected[i], output_array[i]);
}
-TEST(SHA1Test, Test3Bytes) {
+TEST(SHA1Test, Test3BytesAndSpan) {
// Example A.3 from FIPS 180-2: long message.
std::string input(1000000, 'a');
unsigned char output[base::kSHA1Length];
- unsigned char expected[] = {0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda,
- 0xa4, 0xf6, 0x1e, 0xeb, 0x2b, 0xdb, 0xad,
- 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f};
+ static constexpr unsigned char kExpected[] = {
+ 0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4, 0xf6, 0x1e,
+ 0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6f};
base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()),
- input.length(), output);
+ input.size(), output);
+ for (size_t i = 0; i < base::kSHA1Length; i++)
+ EXPECT_EQ(kExpected[i], output[i]);
+
+ std::array<uint8_t, base::kSHA1Length> output_array =
+ base::SHA1HashSpan(base::as_bytes(base::make_span(input)));
for (size_t i = 0; i < base::kSHA1Length; i++)
- EXPECT_EQ(expected[i], output[i]);
+ EXPECT_EQ(kExpected[i], output_array[i]);
}
diff --git a/chromium/base/i18n/icu_util.cc b/chromium/base/i18n/icu_util.cc
index f4942878550..38d39108dc5 100644
--- a/chromium/base/i18n/icu_util.cc
+++ b/chromium/base/i18n/icu_util.cc
@@ -37,10 +37,6 @@
#include "base/mac/foundation_util.h"
#endif
-#if defined(OS_FUCHSIA)
-#include "base/base_paths_fuchsia.h"
-#endif
-
namespace base {
namespace i18n {
@@ -69,9 +65,11 @@ wchar_t g_debug_icu_pf_filename[_MAX_PATH];
// build pkg configurations, etc). 'l' stands for Little Endian.
// This variable is exported through the header file.
const char kIcuDataFileName[] = "icudtl.dat";
+const char kIcuExtraDataFileName[] = "icudtl_extra.dat";
+
#if defined(OS_ANDROID)
-const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
-#endif
+const char kAssetsPathPrefix[] = "assets/";
+#endif // defined(OS_ANDROID)
// File handle intentionally never closed. Not using File here because its
// Windows implementation guards against two instances owning the same
@@ -79,25 +77,31 @@ const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
PlatformFile g_icudtl_pf = kInvalidPlatformFile;
MemoryMappedFile* g_icudtl_mapped_file = nullptr;
MemoryMappedFile::Region g_icudtl_region;
-
-void LazyInitIcuDataFile() {
- if (g_icudtl_pf != kInvalidPlatformFile) {
- return;
- }
+PlatformFile g_icudtl_extra_pf = kInvalidPlatformFile;
+MemoryMappedFile* g_icudtl_extra_mapped_file = nullptr;
+MemoryMappedFile::Region g_icudtl_extra_region;
+
+struct PfRegion {
+ public:
+ PlatformFile pf;
+ MemoryMappedFile::Region region;
+};
+
+std::unique_ptr<PfRegion> OpenIcuDataFile(const std::string& filename) {
+ auto result = std::make_unique<PfRegion>();
#if defined(OS_ANDROID)
- int fd =
- android::OpenApkAsset(kAndroidAssetsIcuDataFileName, &g_icudtl_region);
- g_icudtl_pf = fd;
- if (fd != -1) {
- return;
+ result->pf =
+ android::OpenApkAsset(kAssetsPathPrefix + filename, &result->region);
+ if (result->pf != -1) {
+ return result;
}
-// For unit tests, data file is located on disk, so try there as a fallback.
#endif // defined(OS_ANDROID)
+ // For unit tests, data file is located on disk, so try there as a fallback.
#if !defined(OS_MACOSX)
FilePath data_path;
if (!PathService::Get(DIR_ASSETS, &data_path)) {
- LOG(ERROR) << "Can't find " << kIcuDataFileName;
- return;
+ LOG(ERROR) << "Can't find " << filename;
+ return nullptr;
}
#if defined(OS_WIN)
// TODO(brucedawson): http://crbug.com/445616
@@ -105,7 +109,7 @@ void LazyInitIcuDataFile() {
wcscpy_s(tmp_buffer, as_wcstr(data_path.value()));
debug::Alias(tmp_buffer);
#endif
- data_path = data_path.AppendASCII(kIcuDataFileName);
+ data_path = data_path.AppendASCII(filename);
#if defined(OS_WIN)
// TODO(brucedawson): http://crbug.com/445616
@@ -116,8 +120,7 @@ void LazyInitIcuDataFile() {
#else // !defined(OS_MACOSX)
// Assume it is in the framework bundle's Resources directory.
- ScopedCFTypeRef<CFStringRef> data_file_name(
- SysUTF8ToCFStringRef(kIcuDataFileName));
+ ScopedCFTypeRef<CFStringRef> data_file_name(SysUTF8ToCFStringRef(filename));
FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name);
#if defined(OS_IOS)
FilePath override_data_path = ios::FilePathOfEmbeddedICU();
@@ -126,8 +129,8 @@ void LazyInitIcuDataFile() {
}
#endif // !defined(OS_IOS)
if (data_path.empty()) {
- LOG(ERROR) << kIcuDataFileName << " not found in bundle";
- return;
+ LOG(ERROR) << filename << " not found in bundle";
+ return nullptr;
}
#endif // !defined(OS_MACOSX)
File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
@@ -139,8 +142,8 @@ void LazyInitIcuDataFile() {
g_debug_icu_pf_filename[0] = 0;
#endif // OS_WIN
- g_icudtl_pf = file.TakePlatformFile();
- g_icudtl_region = MemoryMappedFile::Region::kWholeFile;
+ result->pf = file.TakePlatformFile();
+ result->region = MemoryMappedFile::Region::kWholeFile;
}
#if defined(OS_WIN)
else {
@@ -150,6 +153,47 @@ void LazyInitIcuDataFile() {
wcscpy_s(g_debug_icu_pf_filename, as_wcstr(data_path.value()));
}
#endif // OS_WIN
+
+ return result;
+}
+
+void LazyOpenIcuDataFile() {
+ if (g_icudtl_pf != kInvalidPlatformFile) {
+ return;
+ }
+ auto pf_region = OpenIcuDataFile(kIcuDataFileName);
+ if (!pf_region) {
+ return;
+ }
+ g_icudtl_pf = pf_region->pf;
+ g_icudtl_region = pf_region->region;
+}
+
+int LoadIcuData(PlatformFile data_fd,
+ const MemoryMappedFile::Region& data_region,
+ std::unique_ptr<MemoryMappedFile>* out_mapped_data_file,
+ UErrorCode* out_error_code) {
+ if (data_fd == kInvalidPlatformFile) {
+ LOG(ERROR) << "Invalid file descriptor to ICU data received.";
+ return 1; // To debug http://crbug.com/445616.
+ }
+
+ out_mapped_data_file->reset(new MemoryMappedFile());
+ if (!(*out_mapped_data_file)->Initialize(File(data_fd), data_region)) {
+ LOG(ERROR) << "Couldn't mmap icu data file";
+ return 2; // To debug http://crbug.com/445616.
+ }
+
+ (*out_error_code) = U_ZERO_ERROR;
+ udata_setCommonData(const_cast<uint8_t*>((*out_mapped_data_file)->data()),
+ out_error_code);
+ if (U_FAILURE(*out_error_code)) {
+ LOG(ERROR) << "Failed to initialize ICU with data file: "
+ << u_errorName(*out_error_code);
+ return 3; // To debug http://crbug.com/445616.
+ }
+
+ return 0;
}
bool InitializeICUWithFileDescriptorInternal(
@@ -160,28 +204,20 @@ bool InitializeICUWithFileDescriptorInternal(
g_debug_icu_load = 0; // To debug http://crbug.com/445616.
return true;
}
- if (data_fd == kInvalidPlatformFile) {
- g_debug_icu_load = 1; // To debug http://crbug.com/445616.
- LOG(ERROR) << "Invalid file descriptor to ICU data received.";
- return false;
- }
- std::unique_ptr<MemoryMappedFile> icudtl_mapped_file(new MemoryMappedFile());
- if (!icudtl_mapped_file->Initialize(File(data_fd), data_region)) {
- g_debug_icu_load = 2; // To debug http://crbug.com/445616.
- LOG(ERROR) << "Couldn't mmap icu data file";
+ std::unique_ptr<MemoryMappedFile> mapped_file;
+ UErrorCode err;
+ g_debug_icu_load = LoadIcuData(data_fd, data_region, &mapped_file, &err);
+ if (g_debug_icu_load == 1 || g_debug_icu_load == 2) {
return false;
}
- g_icudtl_mapped_file = icudtl_mapped_file.release();
+ g_icudtl_mapped_file = mapped_file.release();
- UErrorCode err = U_ZERO_ERROR;
- udata_setCommonData(const_cast<uint8_t*>(g_icudtl_mapped_file->data()), &err);
- if (err != U_ZERO_ERROR) {
- g_debug_icu_load = 3; // To debug http://crbug.com/445616.
+ if (g_debug_icu_load == 3) {
g_debug_icu_last_error = err;
}
#if defined(OS_ANDROID)
- else {
+ else if (g_debug_icu_load == 0) {
// On Android, we can't leave it up to ICU to set the default timezone
// because ICU's timezone detection does not work in many timezones (e.g.
// Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host
@@ -195,7 +231,7 @@ bool InitializeICUWithFileDescriptorInternal(
#endif
// Never try to load ICU data from files.
udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
- return err == U_ZERO_ERROR;
+ return U_SUCCESS(err);
}
#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
#endif // !defined(OS_NACL)
@@ -204,7 +240,23 @@ bool InitializeICUWithFileDescriptorInternal(
#if !defined(OS_NACL)
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
-#if defined(OS_ANDROID)
+bool InitializeExtraICUWithFileDescriptor(
+ PlatformFile data_fd,
+ const MemoryMappedFile::Region& data_region) {
+ if (g_icudtl_pf != kInvalidPlatformFile) {
+ // Must call InitializeExtraICUWithFileDescriptor() before
+ // InitializeICUWithFileDescriptor().
+ return false;
+ }
+ std::unique_ptr<MemoryMappedFile> mapped_file;
+ UErrorCode err;
+ if (LoadIcuData(data_fd, data_region, &mapped_file, &err) != 0) {
+ return false;
+ }
+ g_icudtl_extra_mapped_file = mapped_file.release();
+ return true;
+}
+
bool InitializeICUWithFileDescriptor(
PlatformFile data_fd,
const MemoryMappedFile::Region& data_region) {
@@ -220,7 +272,14 @@ PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
*out_region = g_icudtl_region;
return g_icudtl_pf;
}
-#endif
+
+PlatformFile GetIcuExtraDataFileHandle(MemoryMappedFile::Region* out_region) {
+ if (g_icudtl_extra_pf == kInvalidPlatformFile) {
+ return kInvalidPlatformFile;
+ }
+ *out_region = g_icudtl_extra_region;
+ return g_icudtl_extra_pf;
+}
const uint8_t* GetRawIcuMemory() {
CHECK(g_icudtl_mapped_file);
@@ -244,7 +303,28 @@ bool InitializeICUFromRawMemory(const uint8_t* raw_memory) {
#endif
}
-#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+bool InitializeExtraICU() {
+ if (g_icudtl_pf != kInvalidPlatformFile) {
+ // Must call InitializeExtraICU() before InitializeICU().
+ return false;
+ }
+ auto pf_region = OpenIcuDataFile(kIcuExtraDataFileName);
+ if (!pf_region) {
+ return false;
+ }
+ g_icudtl_extra_pf = pf_region->pf;
+ g_icudtl_extra_region = pf_region->region;
+ std::unique_ptr<MemoryMappedFile> mapped_file;
+ UErrorCode err;
+ if (LoadIcuData(g_icudtl_extra_pf, g_icudtl_extra_region, &mapped_file,
+ &err) != 0) {
+ return false;
+ }
+ g_icudtl_extra_mapped_file = mapped_file.release();
+ return true;
+}
+
+#endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
bool InitializeICU() {
#if DCHECK_IS_ON()
@@ -261,7 +341,7 @@ bool InitializeICU() {
// it is needed. This can fail if the process is sandboxed at that time.
// Instead, we map the file in and hand off the data so the sandbox won't
// cause any problems.
- LazyInitIcuDataFile();
+ LazyOpenIcuDataFile();
result =
InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
#if defined(OS_WIN)
@@ -299,5 +379,16 @@ void AllowMultipleInitializeCallsForTesting() {
#endif
}
+#if !defined(OS_NACL)
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+void ResetGlobalsForTesting() {
+ g_icudtl_pf = kInvalidPlatformFile;
+ g_icudtl_mapped_file = nullptr;
+ g_icudtl_extra_pf = kInvalidPlatformFile;
+ g_icudtl_extra_mapped_file = nullptr;
+}
+#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif // !defined(OS_NACL)
+
} // namespace i18n
} // namespace base
diff --git a/chromium/base/i18n/icu_util.h b/chromium/base/i18n/icu_util.h
index a861ce6c9ac..4324dc577a7 100644
--- a/chromium/base/i18n/icu_util.h
+++ b/chromium/base/i18n/icu_util.h
@@ -23,18 +23,28 @@ namespace i18n {
BASE_I18N_EXPORT bool InitializeICU();
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
-#if defined(OS_ANDROID)
-// Returns the PlatformFile and Region that was initialized by InitializeICU().
-// Use with InitializeICUWithFileDescriptor().
+// Loads ICU's extra data tables from disk for the current process. If used must
+// be called before InitializeICU().
+BASE_I18N_EXPORT bool InitializeExtraICU();
+// Returns the PlatformFile and Region that was initialized by InitializeICU()
+// or InitializeExtraICU(). Use with InitializeICUWithFileDescriptor() or
+// InitializeExtraICUWithFileDescriptor().
BASE_I18N_EXPORT PlatformFile GetIcuDataFileHandle(
MemoryMappedFile::Region* out_region);
+BASE_I18N_EXPORT PlatformFile
+GetIcuExtraDataFileHandle(MemoryMappedFile::Region* out_region);
-// Android uses a file descriptor passed by browser process to initialize ICU
-// in render processes.
+// Loads ICU extra data file from file descriptor passed by browser process to
+// initialize ICU in render processes. If used must be called before
+// InitializeICUWithFileDescriptor().
+BASE_I18N_EXPORT bool InitializeExtraICUWithFileDescriptor(
+ PlatformFile data_fd,
+ const MemoryMappedFile::Region& data_region);
+// Loads ICU data file from file descriptor passed by browser process to
+// initialize ICU in render processes.
BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor(
PlatformFile data_fd,
const MemoryMappedFile::Region& data_region);
-#endif
// Returns a void pointer to the memory mapped ICU data file.
//
@@ -60,6 +70,12 @@ BASE_I18N_EXPORT bool InitializeICUFromRawMemory(const uint8_t* raw_memory);
// In a test binary, the call above might occur twice.
BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting();
+#if !defined(OS_NACL)
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+BASE_I18N_EXPORT void ResetGlobalsForTesting();
+#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif // !defined(OS_NACL)
+
} // namespace i18n
} // namespace base
diff --git a/chromium/base/i18n/icu_util_unittest.cc b/chromium/base/i18n/icu_util_unittest.cc
new file mode 100644
index 00000000000..75b6c4aab2e
--- /dev/null
+++ b/chromium/base/i18n/icu_util_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/i18n/icu_util.h"
+
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if !defined(OS_NACL)
+#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+
+namespace base {
+namespace i18n {
+
+class IcuUtilTest : public testing::Test {
+ protected:
+ void SetUp() override { ResetGlobalsForTesting(); }
+};
+
+#if defined(OS_ANDROID)
+
+TEST_F(IcuUtilTest, InitializeIcuSucceeds) {
+ bool success = InitializeICU();
+
+ ASSERT_TRUE(success);
+}
+
+TEST_F(IcuUtilTest, ExtraFileNotInitializedAtStart) {
+ MemoryMappedFile::Region region;
+ PlatformFile file = GetIcuExtraDataFileHandle(&region);
+
+ ASSERT_EQ(file, kInvalidPlatformFile);
+}
+
+TEST_F(IcuUtilTest, InitializeExtraIcuSucceeds) {
+ bool success = InitializeExtraICU();
+
+ ASSERT_TRUE(success);
+}
+
+TEST_F(IcuUtilTest, CannotInitializeExtraIcuAfterIcu) {
+ InitializeICU();
+ bool success = InitializeExtraICU();
+
+ ASSERT_FALSE(success);
+}
+
+TEST_F(IcuUtilTest, ExtraFileInitializedAfterInit) {
+ InitializeExtraICU();
+ MemoryMappedFile::Region region;
+ PlatformFile file = GetIcuExtraDataFileHandle(&region);
+
+ ASSERT_NE(file, kInvalidPlatformFile);
+}
+
+TEST_F(IcuUtilTest, InitializeExtraIcuFromFdSucceeds) {
+ InitializeExtraICU();
+ MemoryMappedFile::Region region;
+ PlatformFile pf = GetIcuExtraDataFileHandle(&region);
+ bool success = InitializeExtraICUWithFileDescriptor(pf, region);
+
+ ASSERT_TRUE(success);
+}
+
+TEST_F(IcuUtilTest, CannotInitializeExtraIcuFromFdAfterIcu) {
+ InitializeExtraICU();
+ InitializeICU();
+ MemoryMappedFile::Region region;
+ PlatformFile pf = GetIcuExtraDataFileHandle(&region);
+ bool success = InitializeExtraICUWithFileDescriptor(pf, region);
+
+ ASSERT_FALSE(success);
+}
+
+#endif // defined(OS_ANDROID)
+
+} // namespace i18n
+} // namespace base
+
+#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
+#endif // !defined(OS_NACL)
diff --git a/chromium/base/immediate_crash.h b/chromium/base/immediate_crash.h
index 94ee14f1289..94158fe3ceb 100644
--- a/chromium/base/immediate_crash.h
+++ b/chromium/base/immediate_crash.h
@@ -101,20 +101,14 @@
#define TRAP_SEQUENCE1_() __asm volatile("brk #0\n")
// Intentionally empty: __builtin_unreachable() is always part of the sequence
-// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64
+// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64,
+// https://crbug.com/958373
#define TRAP_SEQUENCE2_() __asm volatile("")
#else
#define TRAP_SEQUENCE1_() asm volatile("int3")
-
-#if defined(ARCH_CPU_64_BITS)
-// Intentionally empty: __builtin_unreachable() is always part of the sequence
-// (see IMMEDIATE_CRASH below) and already emits a ud2 on Win64
-#define TRAP_SEQUENCE2_() asm volatile("")
-#else
#define TRAP_SEQUENCE2_() asm volatile("ud2")
-#endif // defined(ARCH_CPU_64_bits)
#endif // __clang__
diff --git a/chromium/base/ios/crb_protocol_observers_unittest.mm b/chromium/base/ios/crb_protocol_observers_unittest.mm
index 07f5cff035c..6321ac1378a 100644
--- a/chromium/base/ios/crb_protocol_observers_unittest.mm
+++ b/chromium/base/ios/crb_protocol_observers_unittest.mm
@@ -5,7 +5,6 @@
#import "base/ios/crb_protocol_observers.h"
#include "base/ios/weak_nsobject.h"
#include "base/logging.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/mac/scoped_nsobject.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
@@ -122,11 +121,10 @@ TEST_F(CRBProtocolObserversTest, WeakReference) {
[observers_ addObserver:partial_observer_];
- {
- // Need an autorelease pool here, because
- // -[CRBProtocolObservers forwardInvocation:] creates a temporary
- // autoreleased array that holds all the observers.
- base::mac::ScopedNSAutoreleasePool pool;
+ // Need an autorelease pool here, because
+ // -[CRBProtocolObservers forwardInvocation:] creates a temporary
+ // autoreleased array that holds all the observers.
+ @autoreleasepool {
[observers_ requiredMethod];
EXPECT_TRUE([partial_observer_ requiredMethodInvoked]);
}
diff --git a/chromium/base/ios/weak_nsobject.mm b/chromium/base/ios/weak_nsobject.mm
index c017b1d1877..eaf700f6c4e 100644
--- a/chromium/base/ios/weak_nsobject.mm
+++ b/chromium/base/ios/weak_nsobject.mm
@@ -4,7 +4,6 @@
#include "base/ios/weak_nsobject.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/mac/scoped_nsobject.h"
namespace {
@@ -34,23 +33,24 @@ WeakContainer::~WeakContainer() {}
+ (scoped_refptr<base::WeakContainer>)containerForObject:(id)object {
if (object == nil)
return nullptr;
- // The autoreleasePool is needed here as the call to objc_getAssociatedObject
+ // The autoreleasepool is needed here as the call to objc_getAssociatedObject
// returns an autoreleased object which is better released sooner than later.
- base::mac::ScopedNSAutoreleasePool pool;
- CRBWeakNSProtocolSentinel* sentinel =
- objc_getAssociatedObject(object, &sentinelObserverKey_);
- if (!sentinel) {
- base::scoped_nsobject<CRBWeakNSProtocolSentinel> newSentinel(
- [[CRBWeakNSProtocolSentinel alloc]
- initWithContainer:new base::WeakContainer(object)]);
- sentinel = newSentinel;
- objc_setAssociatedObject(object, &sentinelObserverKey_, sentinel,
- OBJC_ASSOCIATION_RETAIN);
- // The retain count is 2. One retain is due to the alloc, the other to the
- // association with the weak object.
- DCHECK_EQ(2u, [sentinel retainCount]);
+ @autoreleasepool {
+ CRBWeakNSProtocolSentinel* sentinel =
+ objc_getAssociatedObject(object, &sentinelObserverKey_);
+ if (!sentinel) {
+ base::scoped_nsobject<CRBWeakNSProtocolSentinel> newSentinel(
+ [[CRBWeakNSProtocolSentinel alloc]
+ initWithContainer:new base::WeakContainer(object)]);
+ sentinel = newSentinel;
+ objc_setAssociatedObject(object, &sentinelObserverKey_, sentinel,
+ OBJC_ASSOCIATION_RETAIN);
+ // The retain count is 2. One retain is due to the alloc, the other to the
+ // association with the weak object.
+ DCHECK_EQ(2u, [sentinel retainCount]);
+ }
+ return [sentinel container];
}
- return [sentinel container];
}
- (id)initWithContainer:(scoped_refptr<base::WeakContainer>)container {
diff --git a/chromium/base/json/json_common.h b/chromium/base/json/json_common.h
new file mode 100644
index 00000000000..c0fd3eab82b
--- /dev/null
+++ b/chromium/base/json/json_common.h
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium 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_JSON_JSON_COMMON_H_
+#define BASE_JSON_JSON_COMMON_H_
+
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace base {
+namespace internal {
+
+// Chosen to support 99.9% of documents found in the wild late 2016.
+// http://crbug.com/673263
+const size_t kAbsoluteMaxDepth = 200;
+
+// Simple class that checks for maximum recursion/stack overflow.
+class StackMarker {
+ public:
+ StackMarker(size_t max_depth, size_t* depth)
+ : max_depth_(max_depth), depth_(depth) {
+ ++(*depth_);
+ DCHECK_LE(*depth_, max_depth_);
+ }
+ ~StackMarker() { --(*depth_); }
+
+ bool IsTooDeep() const { return *depth_ >= max_depth_; }
+
+ private:
+ const size_t max_depth_;
+ size_t* const depth_;
+
+ DISALLOW_COPY_AND_ASSIGN(StackMarker);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_JSON_JSON_COMMON_H_
diff --git a/chromium/base/json/json_parser.cc b/chromium/base/json/json_parser.cc
index 3d9d53d120e..65a4b7dd02e 100644
--- a/chromium/base/json/json_parser.cc
+++ b/chromium/base/json/json_parser.cc
@@ -26,28 +26,6 @@ namespace internal {
namespace {
const int32_t kExtendedASCIIStart = 0x80;
-
-// Simple class that checks for maximum recursion/"stack overflow."
-class StackMarker {
- public:
- StackMarker(int max_depth, int* depth)
- : max_depth_(max_depth), depth_(depth) {
- ++(*depth_);
- DCHECK_LE(*depth_, max_depth_);
- }
- ~StackMarker() {
- --(*depth_);
- }
-
- bool IsTooDeep() const { return *depth_ >= max_depth_; }
-
- private:
- const int max_depth_;
- int* const depth_;
-
- DISALLOW_COPY_AND_ASSIGN(StackMarker);
-};
-
constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD;
} // namespace
@@ -55,7 +33,7 @@ constexpr uint32_t kUnicodeReplacementPoint = 0xFFFD;
// This is U+FFFD.
const char kUnicodeReplacementString[] = "\xEF\xBF\xBD";
-JSONParser::JSONParser(int options, int max_depth)
+JSONParser::JSONParser(int options, size_t max_depth)
: options_(options),
max_depth_(max_depth),
index_(0),
@@ -65,7 +43,7 @@ JSONParser::JSONParser(int options, int max_depth)
error_code_(JSONReader::JSON_NO_ERROR),
error_line_(0),
error_column_(0) {
- CHECK_LE(max_depth, JSONReader::kStackMaxDepth);
+ CHECK_LE(max_depth, kAbsoluteMaxDepth);
}
JSONParser::~JSONParser() = default;
@@ -385,8 +363,10 @@ Optional<Value> JSONParser::ConsumeDictionary() {
}
ConsumeChar(); // Closing '}'.
-
- return Value(Value::DictStorage(std::move(dict_storage), KEEP_LAST_OF_DUPES));
+ // Reverse |dict_storage| to keep the last of elements with the same key in
+ // the input.
+ std::reverse(dict_storage.begin(), dict_storage.end());
+ return Value(Value::DictStorage(std::move(dict_storage)));
}
Optional<Value> JSONParser::ConsumeList() {
diff --git a/chromium/base/json/json_parser.h b/chromium/base/json/json_parser.h
index 78523363db9..548fb282700 100644
--- a/chromium/base/json/json_parser.h
+++ b/chromium/base/json/json_parser.h
@@ -14,6 +14,7 @@
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
+#include "base/json/json_common.h"
#include "base/json/json_reader.h"
#include "base/macros.h"
#include "base/optional.h"
@@ -43,7 +44,7 @@ class JSONParserTest;
// of the next token.
class BASE_EXPORT JSONParser {
public:
- JSONParser(int options, int max_depth = JSONReader::kStackMaxDepth);
+ JSONParser(int options, size_t max_depth = kAbsoluteMaxDepth);
~JSONParser();
// Parses the input string according to the set options and returns the
@@ -215,7 +216,7 @@ class BASE_EXPORT JSONParser {
const int options_;
// Maximum depth to parse.
- const int max_depth_;
+ const size_t max_depth_;
// The input stream being parsed. Note: Not guaranteed to NUL-terminated.
StringPiece input_;
@@ -224,7 +225,7 @@ class BASE_EXPORT JSONParser {
int index_;
// The number of times the parser has recursed (current stack depth).
- int stack_depth_;
+ size_t stack_depth_;
// The line number that the parser is at currently.
int line_number_;
diff --git a/chromium/base/json/json_perftest.cc b/chromium/base/json/json_perftest.cc
index 1e0ea33d310..c62f9516dbb 100644
--- a/chromium/base/json/json_perftest.cc
+++ b/chromium/base/json/json_perftest.cc
@@ -25,10 +25,10 @@ DictionaryValue GenerateDict() {
root.SetStringKey("String", "Foo");
ListValue list;
- list.GetList().emplace_back(2.718);
- list.GetList().emplace_back(false);
- list.GetList().emplace_back(123);
- list.GetList().emplace_back("Bar");
+ list.Append(2.718);
+ list.Append(false);
+ list.Append(123);
+ list.Append("Bar");
root.SetKey("List", std::move(list));
return root;
diff --git a/chromium/base/json/json_reader.cc b/chromium/base/json/json_reader.cc
index d21eac9d180..43f66416624 100644
--- a/chromium/base/json/json_reader.cc
+++ b/chromium/base/json/json_reader.cc
@@ -13,10 +13,6 @@
namespace base {
-// Chosen to support 99.9% of documents found in the wild late 2016.
-// http://crbug.com/673263
-const int JSONReader::kStackMaxDepth = 200;
-
// Values 1000 and above are used by JSONFileValueSerializer::JsonFileError.
static_assert(JSONReader::JSON_PARSE_ERROR_COUNT < 1000,
"JSONReader error out of bounds");
@@ -49,20 +45,22 @@ JSONReader::ValueWithError::~ValueWithError() = default;
JSONReader::ValueWithError& JSONReader::ValueWithError::operator=(
ValueWithError&& other) = default;
-JSONReader::JSONReader(int options, int max_depth)
+JSONReader::JSONReader(int options, size_t max_depth)
: parser_(new internal::JSONParser(options, max_depth)) {}
JSONReader::~JSONReader() = default;
// static
-Optional<Value> JSONReader::Read(StringPiece json, int options, int max_depth) {
+Optional<Value> JSONReader::Read(StringPiece json,
+ int options,
+ size_t max_depth) {
internal::JSONParser parser(options, max_depth);
return parser.Parse(json);
}
std::unique_ptr<Value> JSONReader::ReadDeprecated(StringPiece json,
int options,
- int max_depth) {
+ size_t max_depth) {
Optional<Value> value = Read(json, options, max_depth);
return value ? Value::ToUniquePtrValue(std::move(*value)) : nullptr;
}
diff --git a/chromium/base/json/json_reader.h b/chromium/base/json/json_reader.h
index fb458cfe7c8..3eee07ccc3b 100644
--- a/chromium/base/json/json_reader.h
+++ b/chromium/base/json/json_reader.h
@@ -32,6 +32,7 @@
#include <string>
#include "base/base_export.h"
+#include "base/json/json_common.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
@@ -58,8 +59,6 @@ enum JSONParserOptions {
class BASE_EXPORT JSONReader {
public:
- static const int kStackMaxDepth;
-
// Error codes during parsing.
enum JsonParseError {
JSON_NO_ERROR = 0,
@@ -105,7 +104,8 @@ class BASE_EXPORT JSONReader {
static const char kInputTooLarge[];
// Constructs a reader.
- JSONReader(int options = JSON_PARSE_RFC, int max_depth = kStackMaxDepth);
+ JSONReader(int options = JSON_PARSE_RFC,
+ size_t max_depth = internal::kAbsoluteMaxDepth);
~JSONReader();
@@ -113,16 +113,17 @@ class BASE_EXPORT JSONReader {
// If |json| is not a properly formed JSON string, returns base::nullopt.
static Optional<Value> Read(StringPiece json,
int options = JSON_PARSE_RFC,
- int max_depth = kStackMaxDepth);
+ size_t max_depth = internal::kAbsoluteMaxDepth);
// Deprecated. Use the Read() method above.
// Reads and parses |json|, returning a Value.
// If |json| is not a properly formed JSON string, returns nullptr.
// Wrap this in base::FooValue::From() to check the Value is of type Foo and
// convert to a FooValue at the same time.
- static std::unique_ptr<Value> ReadDeprecated(StringPiece json,
- int options = JSON_PARSE_RFC,
- int max_depth = kStackMaxDepth);
+ static std::unique_ptr<Value> ReadDeprecated(
+ StringPiece json,
+ int options = JSON_PARSE_RFC,
+ size_t max_depth = internal::kAbsoluteMaxDepth);
// Reads and parses |json| like Read(). Returns a ValueWithError, which on
// error, will be populated with a formatted error message, an error code, and
diff --git a/chromium/base/json/json_reader_fuzzer.cc b/chromium/base/json/json_reader_fuzzer.cc
index 7bff954e9a1..6be408c7a44 100644
--- a/chromium/base/json/json_reader_fuzzer.cc
+++ b/chromium/base/json/json_reader_fuzzer.cc
@@ -3,8 +3,11 @@
// found in the LICENSE file.
#include "base/json/json_reader.h"
+#include "base/json/json_writer.h"
#include "base/values.h"
+namespace base {
+
// Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < 2)
@@ -15,11 +18,26 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
std::unique_ptr<char[]> input(new char[size - 1]);
memcpy(input.get(), data, size - 1);
- base::StringPiece input_string(input.get(), size - 1);
+ StringPiece input_string(input.get(), size - 1);
const int options = data[size - 1];
- base::JSONReader::ReadAndReturnValueWithError(input_string, options);
+ JSONReader::ValueWithError json_val =
+ JSONReader::ReadAndReturnValueWithError(input_string, options);
+
+ if (json_val.value) {
+ // Check that the value can be serialized and deserialized back to an
+ // equivalent |Value|.
+ const Value& value = json_val.value.value();
+ std::string serialized;
+ CHECK(JSONWriter::Write(value, &serialized));
+
+ Optional<Value> deserialized = JSONReader::Read(StringPiece(serialized));
+ CHECK(deserialized);
+ CHECK(value.Equals(&deserialized.value()));
+ }
return 0;
}
+
+} // namespace base
diff --git a/chromium/base/json/json_writer.cc b/chromium/base/json/json_writer.cc
index 76a93b2db00..f3d2dc14911 100644
--- a/chromium/base/json/json_writer.cc
+++ b/chromium/base/json/json_writer.cc
@@ -25,19 +25,20 @@ const char kPrettyPrintLineEnding[] = "\n";
#endif
// static
-bool JSONWriter::Write(const Value& node, std::string* json) {
- return WriteWithOptions(node, 0, json);
+bool JSONWriter::Write(const Value& node, std::string* json, size_t max_depth) {
+ return WriteWithOptions(node, 0, json, max_depth);
}
// static
bool JSONWriter::WriteWithOptions(const Value& node,
int options,
- std::string* json) {
+ std::string* json,
+ size_t max_depth) {
json->clear();
// Is there a better way to estimate the size of the output?
json->reserve(1024);
- JSONWriter writer(options, json);
+ JSONWriter writer(options, json, max_depth);
bool result = writer.BuildJSONString(node, 0U);
if (options & OPTIONS_PRETTY_PRINT)
@@ -46,16 +47,23 @@ bool JSONWriter::WriteWithOptions(const Value& node,
return result;
}
-JSONWriter::JSONWriter(int options, std::string* json)
+JSONWriter::JSONWriter(int options, std::string* json, size_t max_depth)
: omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
omit_double_type_preservation_(
(options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0),
pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
- json_string_(json) {
+ json_string_(json),
+ max_depth_(max_depth),
+ stack_depth_(0) {
DCHECK(json);
+ CHECK_LE(max_depth, internal::kAbsoluteMaxDepth);
}
bool JSONWriter::BuildJSONString(const Value& node, size_t depth) {
+ internal::StackMarker depth_check(max_depth_, &stack_depth_);
+ if (depth_check.IsTooDeep())
+ return false;
+
switch (node.type()) {
case Value::Type::NONE:
json_string_->append("null");
diff --git a/chromium/base/json/json_writer.h b/chromium/base/json/json_writer.h
index 57cb8c16a29..d66b46f2863 100644
--- a/chromium/base/json/json_writer.h
+++ b/chromium/base/json/json_writer.h
@@ -10,6 +10,7 @@
#include <string>
#include "base/base_export.h"
+#include "base/json/json_common.h"
#include "base/macros.h"
namespace base {
@@ -42,16 +43,21 @@ class BASE_EXPORT JSONWriter {
// TODO(tc): Should we generate json if it would be invalid json (e.g.,
// |node| is not a DictionaryValue/ListValue or if there are inf/-inf float
// values)? Return true on success and false on failure.
- static bool Write(const Value& node, std::string* json);
+ static bool Write(const Value& node,
+ std::string* json,
+ size_t max_depth = internal::kAbsoluteMaxDepth);
// Same as above but with |options| which is a bunch of JSONWriter::Options
// bitwise ORed together. Return true on success and false on failure.
static bool WriteWithOptions(const Value& node,
int options,
- std::string* json);
+ std::string* json,
+ size_t max_depth = internal::kAbsoluteMaxDepth);
private:
- JSONWriter(int options, std::string* json);
+ JSONWriter(int options,
+ std::string* json,
+ size_t max_depth = internal::kAbsoluteMaxDepth);
// Called recursively to build the JSON string. When completed,
// |json_string_| will contain the JSON.
@@ -67,6 +73,12 @@ class BASE_EXPORT JSONWriter {
// Where we write JSON data as we generate it.
std::string* json_string_;
+ // Maximum depth to write.
+ const size_t max_depth_;
+
+ // The number of times the writer has recursed (current stack depth).
+ size_t stack_depth_;
+
DISALLOW_COPY_AND_ASSIGN(JSONWriter);
};
diff --git a/chromium/base/json/json_writer_unittest.cc b/chromium/base/json/json_writer_unittest.cc
index 291a225f6ff..81d8e3d4be8 100644
--- a/chromium/base/json/json_writer_unittest.cc
+++ b/chromium/base/json/json_writer_unittest.cc
@@ -61,9 +61,9 @@ TEST(JSONWriterTest, NestedTypes) {
ListValue list;
DictionaryValue inner_dict;
inner_dict.SetIntKey("inner int", 10);
- list.GetList().push_back(std::move(inner_dict));
- list.GetList().emplace_back(Value::Type::LIST);
- list.GetList().emplace_back(true);
+ list.Append(std::move(inner_dict));
+ list.Append(Value(Value::Type::LIST));
+ list.Append(true);
root_dict.SetKey("list", std::move(list));
// Test the pretty-printer.
@@ -121,11 +121,11 @@ TEST(JSONWriterTest, BinaryValues) {
EXPECT_TRUE(output_js.empty());
ListValue binary_list;
- binary_list.GetList().emplace_back(kBufferSpan);
- binary_list.GetList().emplace_back(5);
- binary_list.GetList().emplace_back(kBufferSpan);
- binary_list.GetList().emplace_back(2);
- binary_list.GetList().emplace_back(kBufferSpan);
+ binary_list.Append(Value(kBufferSpan));
+ binary_list.Append(5);
+ binary_list.Append(Value(kBufferSpan));
+ binary_list.Append(2);
+ binary_list.Append(Value(kBufferSpan));
EXPECT_FALSE(JSONWriter::Write(binary_list, &output_js));
EXPECT_TRUE(JSONWriter::WriteWithOptions(
binary_list, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js));
@@ -154,4 +154,36 @@ TEST(JSONWriterTest, DoublesAsInts) {
EXPECT_EQ("10000000000", output_js);
}
+TEST(JSONWriterTest, StackOverflow) {
+ std::string output_js;
+ ListValue deep_list;
+ ListValue* next_list = &deep_list;
+
+ const size_t max_depth = 100000;
+
+ for (size_t i = 0; i < max_depth; ++i) {
+ ListValue inner_list;
+ next_list->Append(std::move(inner_list));
+ next_list->GetList(0, &next_list);
+ }
+
+ EXPECT_FALSE(JSONWriter::Write(deep_list, &output_js));
+ EXPECT_FALSE(JSONWriter::WriteWithOptions(
+ deep_list, JSONWriter::OPTIONS_PRETTY_PRINT, &output_js));
+
+ // We cannot just let deep_list tear down since it
+ // would cause a stack overflow. Therefore, we tear
+ // down from the inner lists outwards safely.
+ const size_t step = 200;
+ for (size_t i = max_depth - step; i > 0; i -= step) {
+ next_list = &deep_list;
+ for (size_t curr_depth = 0; curr_depth < i && next_list; ++curr_depth) {
+ if (!next_list->GetList(0, &next_list))
+ next_list = nullptr;
+ }
+ if (next_list)
+ next_list->Remove(0, nullptr);
+ }
+}
+
} // namespace base
diff --git a/chromium/base/linux_util.cc b/chromium/base/linux_util.cc
index caf471a39e1..2da24e1d89b 100644
--- a/chromium/base/linux_util.cc
+++ b/chromium/base/linux_util.cc
@@ -13,6 +13,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <iomanip>
#include <memory>
#include "base/command_line.h"
@@ -28,6 +29,8 @@
#include "base/synchronization/lock.h"
#include "build/build_config.h"
+namespace base {
+
namespace {
// Not needed for OS_CHROMEOS.
@@ -43,7 +46,7 @@ class LinuxDistroHelper {
public:
// Retrieves the Singleton.
static LinuxDistroHelper* GetInstance() {
- return base::Singleton<LinuxDistroHelper>::get();
+ return Singleton<LinuxDistroHelper>::get();
}
// The simple state machine goes from:
@@ -55,7 +58,7 @@ class LinuxDistroHelper {
// we automatically move to STATE_CHECK_STARTED so nobody else will
// do the check.
LinuxDistroState State() {
- base::AutoLock scoped_lock(lock_);
+ AutoLock scoped_lock(lock_);
if (STATE_DID_NOT_CHECK == state_) {
state_ = STATE_CHECK_STARTED;
return STATE_DID_NOT_CHECK;
@@ -65,21 +68,72 @@ class LinuxDistroHelper {
// Indicate the check finished, move to STATE_CHECK_FINISHED.
void CheckFinished() {
- base::AutoLock scoped_lock(lock_);
+ AutoLock scoped_lock(lock_);
DCHECK_EQ(STATE_CHECK_STARTED, state_);
state_ = STATE_CHECK_FINISHED;
}
private:
- base::Lock lock_;
+ Lock lock_;
LinuxDistroState state_;
};
+
+#if !defined(OS_CHROMEOS)
+std::string GetKeyValueFromOSReleaseFile(const std::string& input,
+ const char* key) {
+ StringPairs key_value_pairs;
+ SplitStringIntoKeyValuePairs(input, '=', '\n', &key_value_pairs);
+ for (const auto& pair : key_value_pairs) {
+ const std::string& key_str = pair.first;
+ const std::string& value_str = pair.second;
+ if (key_str == key) {
+ // It can contain quoted characters.
+ std::stringstream ss;
+ std::string pretty_name;
+ ss << value_str;
+ // Quoted with a single tick?
+ if (value_str[0] == '\'')
+ ss >> std::quoted(pretty_name, '\'');
+ else
+ ss >> std::quoted(pretty_name);
+
+ return pretty_name;
+ }
+ }
+
+ return "";
+}
+
+bool ReadDistroFromOSReleaseFile(const char* file) {
+ static const char kPrettyName[] = "PRETTY_NAME";
+
+ std::string os_release_content;
+ if (!ReadFileToString(FilePath(file), &os_release_content))
+ return false;
+
+ std::string pretty_name =
+ GetKeyValueFromOSReleaseFile(os_release_content, kPrettyName);
+ if (pretty_name.empty())
+ return false;
+
+ SetLinuxDistro(pretty_name);
+ return true;
+}
+
+// https://www.freedesktop.org/software/systemd/man/os-release.html
+void GetDistroNameFromOSRelease() {
+ static const char* const kFilesToCheck[] = {"/etc/os-release",
+ "/usr/lib/os-release"};
+ for (const char* file : kFilesToCheck) {
+ if (ReadDistroFromOSReleaseFile(file))
+ return;
+ }
+}
+#endif // if !defined(OS_CHROMEOS)
#endif // if defined(OS_LINUX)
} // namespace
-namespace base {
-
// Account for the terminating null character.
static const int kDistroSize = 128 + 1;
@@ -94,6 +148,21 @@ char g_linux_distro[kDistroSize] =
"Unknown";
#endif
+// This function is only supposed to be used in tests. The declaration in the
+// header file is guarded by "#if defined(UNIT_TEST)" so that they can be used
+// by tests but not non-test code. However, this .cc file is compiled as part
+// of "base" where "UNIT_TEST" is not defined. So we need to specify
+// "BASE_EXPORT" here again so that they are visible to tests.
+BASE_EXPORT std::string GetKeyValueFromOSReleaseFileForTesting(
+ const std::string& input,
+ const char* key) {
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
+ return GetKeyValueFromOSReleaseFile(input, key);
+#else
+ return "";
+#endif // defined(OS_LINUX) && !defined(OS_CHROMEOS)
+}
+
std::string GetLinuxDistro() {
#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
return g_linux_distro;
@@ -106,20 +175,8 @@ std::string GetLinuxDistro() {
return "Unknown"; // Don't wait for other thread to finish.
DCHECK_EQ(state, STATE_DID_NOT_CHECK);
// We do this check only once per process. If it fails, there's
- // little reason to believe it will work if we attempt to run
- // lsb_release again.
- std::vector<std::string> argv;
- argv.push_back("lsb_release");
- argv.push_back("-d");
- std::string output;
- GetAppOutput(CommandLine(argv), &output);
- if (output.length() > 0) {
- // lsb_release -d should return: Description:<tab>Distro Info
- const char field[] = "Description:\t";
- if (output.compare(0, strlen(field), field) == 0) {
- SetLinuxDistro(output.substr(strlen(field)));
- }
- }
+ // little reason to believe it will work if we attempt to run it again.
+ GetDistroNameFromOSRelease();
distro_state_singleton->CheckFinished();
return g_linux_distro;
#else
@@ -137,7 +194,7 @@ void SetLinuxDistro(const std::string& distro) {
bool GetThreadsForProcess(pid_t pid, std::vector<pid_t>* tids) {
// 25 > strlen("/proc//task") + strlen(std::to_string(INT_MAX)) + 1 = 22
char buf[25];
- base::strings::SafeSPrintf(buf, "/proc/%d/task", pid);
+ strings::SafeSPrintf(buf, "/proc/%d/task", pid);
DirReaderPosix dir_reader(buf);
if (!dir_reader.IsValid()) {
diff --git a/chromium/base/linux_util.h b/chromium/base/linux_util.h
index 568b4e86eac..80cd6e40814 100644
--- a/chromium/base/linux_util.h
+++ b/chromium/base/linux_util.h
@@ -22,6 +22,14 @@ BASE_EXPORT extern char g_linux_distro[];
// Get the Linux Distro if we can, or return "Unknown".
BASE_EXPORT std::string GetLinuxDistro();
+#if defined(UNIT_TEST)
+// Get the value of given key from the given input (content of the
+// /etc/os-release file. Exposed for testing.
+BASE_EXPORT std::string GetKeyValueFromOSReleaseFileForTesting(
+ const std::string& input,
+ const char* key);
+#endif // defined(UNIT_TEST)
+
// Set the Linux Distro string.
BASE_EXPORT void SetLinuxDistro(const std::string& distro);
diff --git a/chromium/base/linux_util_unittest.cc b/chromium/base/linux_util_unittest.cc
new file mode 100644
index 00000000000..28d162a8912
--- /dev/null
+++ b/chromium/base/linux_util_unittest.cc
@@ -0,0 +1,76 @@
+// Copyright 2019 The Chromium 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/linux_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kPrettyName[] = "PRETTY_NAME";
+
+TEST(LinuxUtilTest, ParseEtcOsReleaseFile) {
+ const char kOsRelease[] = R"X(
+NAME=Fedora
+VERSION="30 (Workstation Edition\)\"
+ID=fedora
+VERSION_ID=30
+VERSION_CODENAME=""
+PLATFORM_ID="platform:f30
+PRETTY_NAME="Fedora 30 (Workstation Edition)"
+ANSI_COLOR="0;34"
+LOGO=fedora-logo-icon
+CPE_NAME="cpe:/o:fedoraproject:fedora:30"
+HOME_URL="https://fedoraproject.org/"
+DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f30/system-administrators-guide/"
+SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help"
+BUG_REPORT_URL="https://bugzilla.redhat.com/"
+REDHAT_BUGZILLA_PRODUCT="Fedora"
+REDHAT_BUGZILLA_PRODUCT_VERSION=30
+REDHAT_SUPPORT_PRODUCT="Fedora"
+REDHAT_SUPPORT_PRODUCT_VERSION=30
+PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy"
+VARIANT="Workstation Edition"
+VARIANT_ID=workstation)X";
+
+ const char kOsReleaseMissingPrettyName[] = R"(
+NAME=Fedora
+VERSION='30 (Workstation Edition)'
+VARIANT_ID=workstation)";
+
+ std::string value =
+ base::GetKeyValueFromOSReleaseFileForTesting(kOsRelease, kPrettyName);
+ EXPECT_EQ(value, "Fedora 30 (Workstation Edition)");
+ // Missing key in the file
+ value = base::GetKeyValueFromOSReleaseFileForTesting(
+ kOsReleaseMissingPrettyName, kPrettyName);
+ EXPECT_EQ(value, "");
+ // Value quoted with single ticks
+ value = base::GetKeyValueFromOSReleaseFileForTesting(
+ kOsReleaseMissingPrettyName, "VERSION");
+ EXPECT_EQ(value, "30 (Workstation Edition)");
+ // Empty file
+ value = base::GetKeyValueFromOSReleaseFileForTesting("", kPrettyName);
+ EXPECT_EQ(value, "");
+ // Misspelled key
+ value =
+ base::GetKeyValueFromOSReleaseFileForTesting(kOsRelease, "PRETY_NAME");
+ EXPECT_EQ(value, "");
+ // Broken key=value format
+ value = base::GetKeyValueFromOSReleaseFileForTesting("A/B", kPrettyName);
+ EXPECT_EQ(value, "");
+ // Empty values
+ value =
+ base::GetKeyValueFromOSReleaseFileForTesting("PRETTY_NAME=", kPrettyName);
+ EXPECT_EQ(value, "");
+ value = base::GetKeyValueFromOSReleaseFileForTesting("PRETTY_NAME=\"\"",
+ kPrettyName);
+ EXPECT_EQ(value, "");
+ // Only one key=value in the whole file
+ value = base::GetKeyValueFromOSReleaseFileForTesting("PRETTY_NAME=\"Linux\"",
+ kPrettyName);
+ EXPECT_EQ(value, "Linux");
+}
+
+} // namespace
diff --git a/chromium/base/logging.cc b/chromium/base/logging.cc
index 04aabb67465..282b26ff4cf 100644
--- a/chromium/base/logging.cc
+++ b/chromium/base/logging.cc
@@ -858,9 +858,13 @@ LogMessage::~LogMessage() {
fx_logger_t* logger = fx_log_get_logger();
if (logger) {
- // Temporarily pop the trailing newline, since fx_logger will add one.
+ // Temporarily remove the trailing newline from |str_newline|'s C-string
+ // representation, since fx_logger will add a newline of its own.
str_newline.pop_back();
- fx_logger_log(logger, severity, nullptr, str_newline.c_str());
+ std::string message =
+ base::StringPrintf("%s(%d) %s", file_basename_, line_,
+ str_newline.c_str() + message_start_);
+ fx_logger_log(logger, severity, nullptr, message.data());
str_newline.push_back('\n');
}
#endif // OS_FUCHSIA
@@ -962,6 +966,9 @@ void LogMessage::Init(const char* file, int line) {
if (last_slash_pos != base::StringPiece::npos)
filename.remove_prefix(last_slash_pos + 1);
+ // Stores the base name as the null-terminated suffix substring of |filename|.
+ file_basename_ = filename.data();
+
// TODO(darin): It might be nice if the columns were fixed width.
stream_ << '[';
diff --git a/chromium/base/logging.h b/chromium/base/logging.h
index 772c274e2f1..0713dbdab1e 100644
--- a/chromium/base/logging.h
+++ b/chromium/base/logging.h
@@ -530,9 +530,9 @@ BASE_EXPORT extern std::ostream* g_swallow_stream;
class CheckOpResult {
public:
// |message| must be non-null if and only if the check failed.
- CheckOpResult(std::string* message) : message_(message) {}
+ constexpr CheckOpResult(std::string* message) : message_(message) {}
// Returns true if the check succeeded.
- operator bool() const { return !message_; }
+ constexpr operator bool() const { return !message_; }
// Returns the message.
std::string* message() { return message_; }
@@ -685,20 +685,21 @@ std::string* MakeCheckOpString<std::string, std::string>(
// The checked condition is wrapped with ANALYZER_ASSUME_TRUE, which under
// static analysis builds, blocks analysis of the current path if the
// condition is false.
-#define DEFINE_CHECK_OP_IMPL(name, op) \
- template <class t1, class t2> \
- inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
- const char* names) { \
- if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
- return NULL; \
- else \
- return ::logging::MakeCheckOpString(v1, v2, names); \
- } \
- inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
- if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
- return NULL; \
- else \
- return ::logging::MakeCheckOpString(v1, v2, names); \
+#define DEFINE_CHECK_OP_IMPL(name, op) \
+ template <class t1, class t2> \
+ constexpr std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+ const char* names) { \
+ if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
+ return nullptr; \
+ else \
+ return ::logging::MakeCheckOpString(v1, v2, names); \
+ } \
+ constexpr std::string* Check##name##Impl(int v1, int v2, \
+ const char* names) { \
+ if (ANALYZER_ASSUME_TRUE(v1 op v2)) \
+ return nullptr; \
+ else \
+ return ::logging::MakeCheckOpString(v1, v2, names); \
}
DEFINE_CHECK_OP_IMPL(EQ, ==)
DEFINE_CHECK_OP_IMPL(NE, !=)
@@ -920,6 +921,7 @@ class BASE_EXPORT LogMessage {
// The file and line information passed in to the constructor.
const char* file_;
const int line_;
+ const char* file_basename_;
// This is useful since the LogMessage class uses a lot of Win32 calls
// that will lose the value of GLE and the code that called the log function
diff --git a/chromium/base/logging_unittest.cc b/chromium/base/logging_unittest.cc
index cef46410320..14adbfc4f8b 100644
--- a/chromium/base/logging_unittest.cc
+++ b/chromium/base/logging_unittest.cc
@@ -40,6 +40,7 @@
#include <fuchsia/logger/cpp/fidl.h>
#include <fuchsia/logger/cpp/fidl_test_base.h>
#include <lib/fidl/cpp/binding.h>
+#include <lib/sys/cpp/component_context.h>
#include <lib/zx/channel.h>
#include <lib/zx/event.h>
#include <lib/zx/exception.h>
@@ -51,8 +52,8 @@
#include <zircon/syscalls/exception.h>
#include <zircon/types.h>
+#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/service_directory_client.h"
#endif // OS_FUCHSIA
namespace logging {
@@ -1008,8 +1009,9 @@ TEST_F(LoggingTest, FuchsiaSystemLogging) {
std::make_unique<fuchsia::logger::LogFilterOptions>();
options->tags = {"base_unittests__exec"};
fuchsia::logger::LogPtr logger =
- base::fuchsia::ServiceDirectoryClient::ForCurrentProcess()
- ->ConnectToService<fuchsia::logger::Log>();
+ base::fuchsia::ComponentContextForCurrentProcess()
+ ->svc()
+ ->Connect<fuchsia::logger::Log>();
logger->DumpLogs(binding.NewBinding(), std::move(options));
listener.RunUntilDone();
} while (!listener.DidReceiveString(kLogMessage, &logged_message));
diff --git a/chromium/base/mac/bind_objc_block_unittest.mm b/chromium/base/mac/bind_objc_block_unittest.mm
index 8c4a9892298..485f421be67 100644
--- a/chromium/base/mac/bind_objc_block_unittest.mm
+++ b/chromium/base/mac/bind_objc_block_unittest.mm
@@ -7,7 +7,6 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gtest_mac.h"
@@ -108,8 +107,7 @@ TEST(BindObjcBlockTest, TestSixArguments) {
TEST(BindObjcBlockTest, TestBlockMoveable) {
base::OnceClosure c;
__block BOOL invoked_block = NO;
- {
- base::mac::ScopedNSAutoreleasePool autorelease_pool;
+ @autoreleasepool {
c = base::BindOnce(base::RetainBlock(^(std::unique_ptr<BOOL> v) {
invoked_block = *v;
}),
@@ -139,8 +137,7 @@ TEST(BindObjcBlockTest, TestBlockDeallocation) {
TEST(BindObjcBlockTest, TestBlockReleased) {
base::WeakNSObject<NSObject> weak_nsobject;
- {
- base::mac::ScopedNSAutoreleasePool autorelease_pool;
+ @autoreleasepool {
NSObject* nsobject = [[[NSObject alloc] init] autorelease];
weak_nsobject.reset(nsobject);
diff --git a/chromium/base/mac/foundation_util.mm b/chromium/base/mac/foundation_util.mm
index 8b20ebc678c..2a83d4d8158 100644
--- a/chromium/base/mac/foundation_util.mm
+++ b/chromium/base/mac/foundation_util.mm
@@ -15,6 +15,7 @@
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "base/strings/sys_string_conversions.h"
+#include "build/branding_buildflags.h"
#include "build/build_config.h"
#if !defined(OS_IOS)
@@ -239,7 +240,7 @@ const char* BaseBundleID() {
return base_bundle_id;
}
-#if defined(GOOGLE_CHROME_BUILD)
+#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
return "com.google.Chrome";
#else
return "org.chromium.Chromium";
diff --git a/chromium/base/mac/foundation_util_unittest.mm b/chromium/base/mac/foundation_util_unittest.mm
index df8ef24b5ae..677264b2b82 100644
--- a/chromium/base/mac/foundation_util_unittest.mm
+++ b/chromium/base/mac/foundation_util_unittest.mm
@@ -11,7 +11,6 @@
#include "base/files/file_path.h"
#include "base/format_macros.h"
#include "base/mac/scoped_cftyperef.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
@@ -162,112 +161,107 @@ TEST(FoundationUtilTest, CFCast) {
}
TEST(FoundationUtilTest, ObjCCast) {
- ScopedNSAutoreleasePool pool;
-
- id test_array = @[];
- id test_array_mutable = [NSMutableArray array];
- id test_data = [NSData data];
- id test_data_mutable = [NSMutableData dataWithCapacity:10];
- id test_date = [NSDate date];
- id test_dict = @{ @"meaning" : @42 };
- id test_dict_mutable = [NSMutableDictionary dictionaryWithCapacity:10];
- id test_number = @42;
- id test_null = [NSNull null];
- id test_set = [NSSet setWithObject:@"string object"];
- id test_set_mutable = [NSMutableSet setWithCapacity:10];
- id test_str = [NSString string];
- id test_str_const = @"bonjour";
- id test_str_mutable = [NSMutableString stringWithCapacity:10];
-
- // Make sure the allocations of NS types are good.
- EXPECT_TRUE(test_array);
- EXPECT_TRUE(test_array_mutable);
- EXPECT_TRUE(test_data);
- EXPECT_TRUE(test_data_mutable);
- EXPECT_TRUE(test_date);
- EXPECT_TRUE(test_dict);
- EXPECT_TRUE(test_dict_mutable);
- EXPECT_TRUE(test_number);
- EXPECT_TRUE(test_null);
- EXPECT_TRUE(test_set);
- EXPECT_TRUE(test_set_mutable);
- EXPECT_TRUE(test_str);
- EXPECT_TRUE(test_str_const);
- EXPECT_TRUE(test_str_mutable);
-
- // Casting the id correctly provides the same pointer.
- EXPECT_EQ(test_array, ObjCCast<NSArray>(test_array));
- EXPECT_EQ(test_array_mutable, ObjCCast<NSArray>(test_array_mutable));
- EXPECT_EQ(test_data, ObjCCast<NSData>(test_data));
- EXPECT_EQ(test_data_mutable, ObjCCast<NSData>(test_data_mutable));
- EXPECT_EQ(test_date, ObjCCast<NSDate>(test_date));
- EXPECT_EQ(test_dict, ObjCCast<NSDictionary>(test_dict));
- EXPECT_EQ(test_dict_mutable, ObjCCast<NSDictionary>(test_dict_mutable));
- EXPECT_EQ(test_number, ObjCCast<NSNumber>(test_number));
- EXPECT_EQ(test_null, ObjCCast<NSNull>(test_null));
- EXPECT_EQ(test_set, ObjCCast<NSSet>(test_set));
- EXPECT_EQ(test_set_mutable, ObjCCast<NSSet>(test_set_mutable));
- EXPECT_EQ(test_str, ObjCCast<NSString>(test_str));
- EXPECT_EQ(test_str_const, ObjCCast<NSString>(test_str_const));
- EXPECT_EQ(test_str_mutable, ObjCCast<NSString>(test_str_mutable));
-
- // When given an incorrect ObjC cast, provide nil.
- EXPECT_FALSE(ObjCCast<NSString>(test_array));
- EXPECT_FALSE(ObjCCast<NSString>(test_array_mutable));
- EXPECT_FALSE(ObjCCast<NSString>(test_data));
- EXPECT_FALSE(ObjCCast<NSString>(test_data_mutable));
- EXPECT_FALSE(ObjCCast<NSSet>(test_date));
- EXPECT_FALSE(ObjCCast<NSSet>(test_dict));
- EXPECT_FALSE(ObjCCast<NSNumber>(test_dict_mutable));
- EXPECT_FALSE(ObjCCast<NSNull>(test_number));
- EXPECT_FALSE(ObjCCast<NSDictionary>(test_null));
- EXPECT_FALSE(ObjCCast<NSDictionary>(test_set));
- EXPECT_FALSE(ObjCCast<NSDate>(test_set_mutable));
- EXPECT_FALSE(ObjCCast<NSData>(test_str));
- EXPECT_FALSE(ObjCCast<NSData>(test_str_const));
- EXPECT_FALSE(ObjCCast<NSArray>(test_str_mutable));
-
- // Giving a nil provides a nil.
- EXPECT_FALSE(ObjCCast<NSArray>(nil));
- EXPECT_FALSE(ObjCCast<NSData>(nil));
- EXPECT_FALSE(ObjCCast<NSDate>(nil));
- EXPECT_FALSE(ObjCCast<NSDictionary>(nil));
- EXPECT_FALSE(ObjCCast<NSNull>(nil));
- EXPECT_FALSE(ObjCCast<NSNumber>(nil));
- EXPECT_FALSE(ObjCCast<NSSet>(nil));
- EXPECT_FALSE(ObjCCast<NSString>(nil));
-
- // ObjCCastStrict: correct cast results in correct pointer being returned.
- EXPECT_EQ(test_array, ObjCCastStrict<NSArray>(test_array));
- EXPECT_EQ(test_array_mutable,
- ObjCCastStrict<NSArray>(test_array_mutable));
- EXPECT_EQ(test_data, ObjCCastStrict<NSData>(test_data));
- EXPECT_EQ(test_data_mutable,
- ObjCCastStrict<NSData>(test_data_mutable));
- EXPECT_EQ(test_date, ObjCCastStrict<NSDate>(test_date));
- EXPECT_EQ(test_dict, ObjCCastStrict<NSDictionary>(test_dict));
- EXPECT_EQ(test_dict_mutable,
- ObjCCastStrict<NSDictionary>(test_dict_mutable));
- EXPECT_EQ(test_number, ObjCCastStrict<NSNumber>(test_number));
- EXPECT_EQ(test_null, ObjCCastStrict<NSNull>(test_null));
- EXPECT_EQ(test_set, ObjCCastStrict<NSSet>(test_set));
- EXPECT_EQ(test_set_mutable,
- ObjCCastStrict<NSSet>(test_set_mutable));
- EXPECT_EQ(test_str, ObjCCastStrict<NSString>(test_str));
- EXPECT_EQ(test_str_const,
- ObjCCastStrict<NSString>(test_str_const));
- EXPECT_EQ(test_str_mutable,
- ObjCCastStrict<NSString>(test_str_mutable));
-
- // ObjCCastStrict: Giving a nil provides a nil.
- EXPECT_FALSE(ObjCCastStrict<NSArray>(nil));
- EXPECT_FALSE(ObjCCastStrict<NSData>(nil));
- EXPECT_FALSE(ObjCCastStrict<NSDate>(nil));
- EXPECT_FALSE(ObjCCastStrict<NSDictionary>(nil));
- EXPECT_FALSE(ObjCCastStrict<NSNull>(nil));
- EXPECT_FALSE(ObjCCastStrict<NSNumber>(nil));
- EXPECT_FALSE(ObjCCastStrict<NSSet>(nil));
- EXPECT_FALSE(ObjCCastStrict<NSString>(nil));
+ @autoreleasepool {
+ id test_array = @[];
+ id test_array_mutable = [NSMutableArray array];
+ id test_data = [NSData data];
+ id test_data_mutable = [NSMutableData dataWithCapacity:10];
+ id test_date = [NSDate date];
+ id test_dict = @{@"meaning" : @42};
+ id test_dict_mutable = [NSMutableDictionary dictionaryWithCapacity:10];
+ id test_number = @42;
+ id test_null = [NSNull null];
+ id test_set = [NSSet setWithObject:@"string object"];
+ id test_set_mutable = [NSMutableSet setWithCapacity:10];
+ id test_str = [NSString string];
+ id test_str_const = @"bonjour";
+ id test_str_mutable = [NSMutableString stringWithCapacity:10];
+
+ // Make sure the allocations of NS types are good.
+ EXPECT_TRUE(test_array);
+ EXPECT_TRUE(test_array_mutable);
+ EXPECT_TRUE(test_data);
+ EXPECT_TRUE(test_data_mutable);
+ EXPECT_TRUE(test_date);
+ EXPECT_TRUE(test_dict);
+ EXPECT_TRUE(test_dict_mutable);
+ EXPECT_TRUE(test_number);
+ EXPECT_TRUE(test_null);
+ EXPECT_TRUE(test_set);
+ EXPECT_TRUE(test_set_mutable);
+ EXPECT_TRUE(test_str);
+ EXPECT_TRUE(test_str_const);
+ EXPECT_TRUE(test_str_mutable);
+
+ // Casting the id correctly provides the same pointer.
+ EXPECT_EQ(test_array, ObjCCast<NSArray>(test_array));
+ EXPECT_EQ(test_array_mutable, ObjCCast<NSArray>(test_array_mutable));
+ EXPECT_EQ(test_data, ObjCCast<NSData>(test_data));
+ EXPECT_EQ(test_data_mutable, ObjCCast<NSData>(test_data_mutable));
+ EXPECT_EQ(test_date, ObjCCast<NSDate>(test_date));
+ EXPECT_EQ(test_dict, ObjCCast<NSDictionary>(test_dict));
+ EXPECT_EQ(test_dict_mutable, ObjCCast<NSDictionary>(test_dict_mutable));
+ EXPECT_EQ(test_number, ObjCCast<NSNumber>(test_number));
+ EXPECT_EQ(test_null, ObjCCast<NSNull>(test_null));
+ EXPECT_EQ(test_set, ObjCCast<NSSet>(test_set));
+ EXPECT_EQ(test_set_mutable, ObjCCast<NSSet>(test_set_mutable));
+ EXPECT_EQ(test_str, ObjCCast<NSString>(test_str));
+ EXPECT_EQ(test_str_const, ObjCCast<NSString>(test_str_const));
+ EXPECT_EQ(test_str_mutable, ObjCCast<NSString>(test_str_mutable));
+
+ // When given an incorrect ObjC cast, provide nil.
+ EXPECT_FALSE(ObjCCast<NSString>(test_array));
+ EXPECT_FALSE(ObjCCast<NSString>(test_array_mutable));
+ EXPECT_FALSE(ObjCCast<NSString>(test_data));
+ EXPECT_FALSE(ObjCCast<NSString>(test_data_mutable));
+ EXPECT_FALSE(ObjCCast<NSSet>(test_date));
+ EXPECT_FALSE(ObjCCast<NSSet>(test_dict));
+ EXPECT_FALSE(ObjCCast<NSNumber>(test_dict_mutable));
+ EXPECT_FALSE(ObjCCast<NSNull>(test_number));
+ EXPECT_FALSE(ObjCCast<NSDictionary>(test_null));
+ EXPECT_FALSE(ObjCCast<NSDictionary>(test_set));
+ EXPECT_FALSE(ObjCCast<NSDate>(test_set_mutable));
+ EXPECT_FALSE(ObjCCast<NSData>(test_str));
+ EXPECT_FALSE(ObjCCast<NSData>(test_str_const));
+ EXPECT_FALSE(ObjCCast<NSArray>(test_str_mutable));
+
+ // Giving a nil provides a nil.
+ EXPECT_FALSE(ObjCCast<NSArray>(nil));
+ EXPECT_FALSE(ObjCCast<NSData>(nil));
+ EXPECT_FALSE(ObjCCast<NSDate>(nil));
+ EXPECT_FALSE(ObjCCast<NSDictionary>(nil));
+ EXPECT_FALSE(ObjCCast<NSNull>(nil));
+ EXPECT_FALSE(ObjCCast<NSNumber>(nil));
+ EXPECT_FALSE(ObjCCast<NSSet>(nil));
+ EXPECT_FALSE(ObjCCast<NSString>(nil));
+
+ // ObjCCastStrict: correct cast results in correct pointer being returned.
+ EXPECT_EQ(test_array, ObjCCastStrict<NSArray>(test_array));
+ EXPECT_EQ(test_array_mutable, ObjCCastStrict<NSArray>(test_array_mutable));
+ EXPECT_EQ(test_data, ObjCCastStrict<NSData>(test_data));
+ EXPECT_EQ(test_data_mutable, ObjCCastStrict<NSData>(test_data_mutable));
+ EXPECT_EQ(test_date, ObjCCastStrict<NSDate>(test_date));
+ EXPECT_EQ(test_dict, ObjCCastStrict<NSDictionary>(test_dict));
+ EXPECT_EQ(test_dict_mutable,
+ ObjCCastStrict<NSDictionary>(test_dict_mutable));
+ EXPECT_EQ(test_number, ObjCCastStrict<NSNumber>(test_number));
+ EXPECT_EQ(test_null, ObjCCastStrict<NSNull>(test_null));
+ EXPECT_EQ(test_set, ObjCCastStrict<NSSet>(test_set));
+ EXPECT_EQ(test_set_mutable, ObjCCastStrict<NSSet>(test_set_mutable));
+ EXPECT_EQ(test_str, ObjCCastStrict<NSString>(test_str));
+ EXPECT_EQ(test_str_const, ObjCCastStrict<NSString>(test_str_const));
+ EXPECT_EQ(test_str_mutable, ObjCCastStrict<NSString>(test_str_mutable));
+
+ // ObjCCastStrict: Giving a nil provides a nil.
+ EXPECT_FALSE(ObjCCastStrict<NSArray>(nil));
+ EXPECT_FALSE(ObjCCastStrict<NSData>(nil));
+ EXPECT_FALSE(ObjCCastStrict<NSDate>(nil));
+ EXPECT_FALSE(ObjCCastStrict<NSDictionary>(nil));
+ EXPECT_FALSE(ObjCCastStrict<NSNull>(nil));
+ EXPECT_FALSE(ObjCCastStrict<NSNumber>(nil));
+ EXPECT_FALSE(ObjCCastStrict<NSSet>(nil));
+ EXPECT_FALSE(ObjCCastStrict<NSString>(nil));
+ }
}
TEST(FoundationUtilTest, GetValueFromDictionary) {
diff --git a/chromium/base/mac/objc_release_properties_unittest.mm b/chromium/base/mac/objc_release_properties_unittest.mm
index 4b51e42b2ec..6804669d15c 100644
--- a/chromium/base/mac/objc_release_properties_unittest.mm
+++ b/chromium/base/mac/objc_release_properties_unittest.mm
@@ -5,7 +5,6 @@
#include "base/mac/objc_release_properties.h"
#include "base/stl_util.h"
-#import "base/mac/scoped_nsautorelease_pool.h"
#include "testing/gtest/include/gtest/gtest.h"
#import <objc/runtime.h>
@@ -268,9 +267,7 @@ TEST(ObjCReleasePropertiesTest, SesameStreet) {
// Make sure that worked before things get more involved.
EXPECT_EQ(3, ah_ah_ah);
- {
- base::mac::ScopedNSAutoreleasePool pool;
-
+ @autoreleasepool {
test_object.baseCvcRetain = [CountVonCount countVonCount];
test_object.baseCvcCopy = [CountVonCount countVonCount];
test_object.baseCvcAssign = baseAssign;
@@ -324,9 +321,7 @@ TEST(ObjCReleasePropertiesTest, SesameStreet) {
// readonly.
EXPECT_EQ(6, ah_ah_ah);
- {
- base::mac::ScopedNSAutoreleasePool pool;
-
+ @autoreleasepool {
// Put things back to how they were.
test_object.baseCvcRetain = [CountVonCount countVonCount];
test_object.baseCvcCopy = [CountVonCount countVonCount];
diff --git a/chromium/base/mac/scoped_nsautorelease_pool.h b/chromium/base/mac/scoped_nsautorelease_pool.h
index 4d15e6da4cf..6f58f30a8d9 100644
--- a/chromium/base/mac/scoped_nsautorelease_pool.h
+++ b/chromium/base/mac/scoped_nsautorelease_pool.h
@@ -21,6 +21,8 @@ namespace mac {
// sends it a -drain message when destroyed. This allows an autorelease pool to
// be maintained in ordinary C++ code without bringing in any direct Objective-C
// dependency.
+//
+// Use only in C++ code; use @autoreleasepool in Obj-C(++) code.
class BASE_EXPORT ScopedNSAutoreleasePool {
public:
diff --git a/chromium/base/mac/scoped_nsobject.h b/chromium/base/mac/scoped_nsobject.h
index d970d03e8bc..b7d119546fc 100644
--- a/chromium/base/mac/scoped_nsobject.h
+++ b/chromium/base/mac/scoped_nsobject.h
@@ -36,11 +36,10 @@ namespace base {
// scoped_nsprotocol<> has the same behavior as scoped_nsobject, but can be used
// with protocols.
//
-// scoped_nsobject<> is not to be used for NSAutoreleasePools. For
-// NSAutoreleasePools use ScopedNSAutoreleasePool from
-// scoped_nsautorelease_pool.h instead.
-// We check for bad uses of scoped_nsobject and NSAutoreleasePool at compile
-// time with a template specialization (see below).
+// scoped_nsobject<> is not to be used for NSAutoreleasePools. For C++ code use
+// NSAutoreleasePool; for Objective-C(++) code use @autoreleasepool instead. We
+// check for bad uses of scoped_nsobject and NSAutoreleasePool at compile time
+// with a template specialization (see below).
//
// If Automatic Reference Counting (aka ARC) is enabled then the ownership
// policy is not controllable by the user as ARC make it really difficult to
@@ -187,7 +186,7 @@ class scoped_nsobject : public scoped_nsprotocol<NST*> {
#if !defined(__has_feature) || !__has_feature(objc_arc)
static_assert(std::is_same<NST, NSAutoreleasePool>::value == false,
- "Use ScopedNSAutoreleasePool instead");
+ "Use @autoreleasepool instead");
#endif
};
diff --git a/chromium/base/mac/scoped_nsobject_unittest.mm b/chromium/base/mac/scoped_nsobject_unittest.mm
index 72d52422582..d33e96e5750 100644
--- a/chromium/base/mac/scoped_nsobject_unittest.mm
+++ b/chromium/base/mac/scoped_nsobject_unittest.mm
@@ -4,7 +4,6 @@
#include <vector>
-#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/mac/scoped_nsobject.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -24,8 +23,7 @@ TEST(ScopedNSObjectTest, ScopedNSObject) {
base::scoped_nsobject<NSObject> p3 = p1;
ASSERT_EQ(p1.get(), p3.get());
ASSERT_EQ(2u, [p1 retainCount]);
- {
- base::mac::ScopedNSAutoreleasePool pool;
+ @autoreleasepool {
p3 = p1;
}
ASSERT_EQ(p1.get(), p3.get());
@@ -46,8 +44,7 @@ TEST(ScopedNSObjectTest, ScopedNSObject) {
base::scoped_nsobject<NSObject> p6 = p1;
ASSERT_EQ(3u, [p6 retainCount]);
- {
- base::mac::ScopedNSAutoreleasePool pool;
+ @autoreleasepool {
p6.autorelease();
ASSERT_EQ(nil, p6.get());
ASSERT_EQ(3u, [p1 retainCount]);
diff --git a/chromium/base/memory/scoped_refptr.h b/chromium/base/memory/scoped_refptr.h
index 23dec28c4a4..ee8b75b3f88 100644
--- a/chromium/base/memory/scoped_refptr.h
+++ b/chromium/base/memory/scoped_refptr.h
@@ -25,10 +25,17 @@ class RefCounted;
template <class, typename>
class RefCountedThreadSafe;
class SequencedTaskRunner;
+class WrappedPromise;
template <typename T>
scoped_refptr<T> AdoptRef(T* t);
+namespace internal {
+
+class BasePromise;
+
+} // namespace internal
+
namespace subtle {
enum AdoptRefTag { kAdoptRefTag };
@@ -260,6 +267,11 @@ class scoped_refptr {
friend scoped_refptr<U> base::AdoptRef(U*);
friend class ::base::SequencedTaskRunner;
+ // Friend access so these classes can use the constructor below as part of a
+ // binary size optimization.
+ friend class ::base::internal::BasePromise;
+ friend class ::base::WrappedPromise;
+
// Returns the owned pointer (if any), releasing ownership to the caller. The
// caller is responsible for managing the lifetime of the reference.
T* release();
diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc
index a8a66308a1c..7e0b9c5520d 100644
--- a/chromium/base/message_loop/message_loop.cc
+++ b/chromium/base/message_loop/message_loop.cc
@@ -161,45 +161,4 @@ void MessageLoop::SetTaskRunner(
sequence_manager_->SetTaskRunner(task_runner);
}
-#if !defined(OS_NACL)
-
-//------------------------------------------------------------------------------
-// MessageLoopForUI
-
-MessageLoopForUI::MessageLoopForUI(MessagePumpType type) : MessageLoop(type) {
-#if defined(OS_ANDROID)
- DCHECK(type == MessagePumpType::UI || type == MessagePumpType::JAVA);
-#else
- DCHECK_EQ(type, MessagePumpType::UI);
-#endif
-}
-
-#if defined(OS_IOS)
-void MessageLoopForUI::Attach() {
- sequence_manager_->AttachToMessagePump();
-}
-#endif // defined(OS_IOS)
-
-#if defined(OS_ANDROID)
-void MessageLoopForUI::Abort() {
- static_cast<MessagePumpForUI*>(pump_)->Abort();
-}
-
-bool MessageLoopForUI::IsAborted() {
- return static_cast<MessagePumpForUI*>(pump_)->IsAborted();
-}
-
-void MessageLoopForUI::QuitWhenIdle(base::OnceClosure callback) {
- static_cast<MessagePumpForUI*>(pump_)->QuitWhenIdle(std::move(callback));
-}
-#endif // defined(OS_ANDROID)
-
-#if defined(OS_WIN)
-void MessageLoopForUI::EnableWmQuit() {
- static_cast<MessagePumpForUI*>(pump_)->EnableWmQuit();
-}
-#endif // defined(OS_WIN)
-
-#endif // !defined(OS_NACL)
-
} // namespace base
diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h
index cc760ffab17..fa5f214c588 100644
--- a/chromium/base/message_loop/message_loop.h
+++ b/chromium/base/message_loop/message_loop.h
@@ -23,10 +23,6 @@
namespace base {
-namespace internal {
-class MessageLoopThreadDelegate;
-} // namespace internal
-
class MessageLoopImpl;
class MessagePump;
class TaskObserver;
@@ -160,7 +156,6 @@ class BASE_EXPORT MessageLoop {
friend class MessageLoopTypedTest;
friend class ScheduleWorkTest;
friend class Thread;
- friend class internal::MessageLoopThreadDelegate;
friend class sequence_manager::internal::SequenceManagerImpl;
FRIEND_TEST_ALL_PREFIXES(MessageLoopTest, DeleteUnboundLoop);
@@ -202,58 +197,6 @@ class BASE_EXPORT MessageLoop {
DISALLOW_COPY_AND_ASSIGN(MessageLoop);
};
-#if !defined(OS_NACL)
-
-//-----------------------------------------------------------------------------
-// MessageLoopForUI extends MessageLoop with methods that are particular to a
-// MessageLoop instantiated with TYPE_UI.
-//
-// By instantiating a MessageLoopForUI on the current thread, the owner enables
-// native UI message pumping.
-//
-// MessageLoopCurrentForUI is exposed statically on its thread via
-// MessageLoopCurrentForUI::Get() to provide additional functionality.
-//
-class BASE_EXPORT MessageLoopForUI : public MessageLoop {
- public:
- explicit MessageLoopForUI(MessagePumpType type = MessagePumpType::UI);
-
-#if defined(OS_IOS)
- // On iOS, the main message loop cannot be Run(). Instead call Attach(),
- // which connects this MessageLoop to the UI thread's CFRunLoop and allows
- // PostTask() to work.
- void Attach();
-#endif
-
-#if defined(OS_ANDROID)
- // On Android there are cases where we want to abort immediately without
- // calling Quit(), in these cases we call Abort().
- void Abort();
-
- // True if this message pump has been aborted.
- bool IsAborted();
-
- // Since Run() is never called on Android, and the message loop is run by the
- // java Looper, quitting the RunLoop won't join the thread, so we need a
- // callback to run when the RunLoop goes idle to let the Java thread know when
- // it can safely quit.
- void QuitWhenIdle(base::OnceClosure callback);
-#endif
-
-#if defined(OS_WIN)
- // See method of the same name in the Windows MessagePumpForUI implementation.
- void EnableWmQuit();
-#endif
-};
-
-// Do not add any member variables to MessageLoopForUI! This is important b/c
-// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra
-// data that you need should be stored on the MessageLoop's pump_ instance.
-static_assert(sizeof(MessageLoop) == sizeof(MessageLoopForUI),
- "MessageLoopForUI should not have extra member variables");
-
-#endif // !defined(OS_NACL)
-
//-----------------------------------------------------------------------------
// MessageLoopForIO extends MessageLoop with methods that are particular to a
// MessageLoop instantiated with TYPE_IO.
diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc
index 9d5e92d50bb..02cb94b5b47 100644
--- a/chromium/base/message_loop/message_loop_unittest.cc
+++ b/chromium/base/message_loop/message_loop_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/message_loop/message_loop.h"
+
#include <stddef.h>
#include <stdint.h>
@@ -1507,29 +1509,6 @@ TEST_F(MessageLoopTest, WmQuitIsIgnored) {
EXPECT_TRUE(task_was_run);
}
-TEST_F(MessageLoopTest, WmQuitIsNotIgnoredWithEnableWmQuit) {
- MessageLoop loop(MessagePumpType::UI);
- static_cast<MessageLoopForUI*>(&loop)->EnableWmQuit();
-
- // Post a WM_QUIT message to the current thread.
- ::PostQuitMessage(0);
-
- // Post a task to the current thread, with a small delay to make it less
- // likely that we process the posted task before looking for WM_* messages.
- RunLoop run_loop;
- loop.task_runner()->PostDelayedTask(FROM_HERE,
- BindOnce(
- [](OnceClosure closure) {
- ADD_FAILURE();
- std::move(closure).Run();
- },
- run_loop.QuitClosure()),
- TestTimeouts::tiny_timeout());
-
- // Run the loop. It should not result in ADD_FAILURE() getting called.
- run_loop.Run();
-}
-
TEST_F(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) {
MessageLoop message_loop(MessagePumpType::UI);
diff --git a/chromium/base/message_loop/message_pump_io_ios_unittest.cc b/chromium/base/message_loop/message_pump_io_ios_unittest.cc
index 4d15d44db32..aec10012a7c 100644
--- a/chromium/base/message_loop/message_pump_io_ios_unittest.cc
+++ b/chromium/base/message_loop/message_pump_io_ios_unittest.cc
@@ -7,6 +7,7 @@
#include <unistd.h>
#include "base/macros.h"
+#include "base/message_loop/message_pump_for_io.h"
#include "base/posix/eintr_wrapper.h"
#include "base/test/gtest_util.h"
#include "base/threading/thread.h"
diff --git a/chromium/base/message_loop/message_pump_mac_unittest.mm b/chromium/base/message_loop/message_pump_mac_unittest.mm
index d5ff5c9fd59..85f4779868a 100644
--- a/chromium/base/message_loop/message_pump_mac_unittest.mm
+++ b/chromium/base/message_loop/message_pump_mac_unittest.mm
@@ -9,9 +9,9 @@
#include "base/mac/scoped_cftyperef.h"
#import "base/mac/scoped_nsobject.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_current.h"
#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -145,7 +145,8 @@ void RunTaskInMode(CFRunLoopMode mode, OnceClosure task) {
// Tests the correct behavior of ScopedPumpMessagesInPrivateModes.
TEST(MessagePumpMacTest, ScopedPumpMessagesInPrivateModes) {
- MessageLoopForUI message_loop;
+ test::SingleThreadTaskEnvironment task_environment(
+ test::SingleThreadTaskEnvironment::MainThreadType::UI);
CFRunLoopMode kRegular = kCFRunLoopDefaultMode;
CFRunLoopMode kPrivate = CFSTR("NSUnhighlightMenuRunLoopMode");
@@ -193,7 +194,8 @@ TEST(MessagePumpMacTest, ScopedPumpMessagesInPrivateModes) {
// Tests that private message loop modes are not pumped while a modal dialog is
// present.
TEST(MessagePumpMacTest, ScopedPumpMessagesAttemptWithModalDialog) {
- MessageLoopForUI message_loop;
+ test::SingleThreadTaskEnvironment task_environment(
+ test::SingleThreadTaskEnvironment::MainThreadType::UI);
{
base::ScopedPumpMessagesInPrivateModes allow_private;
@@ -239,7 +241,8 @@ TEST(MessagePumpMacTest, ScopedPumpMessagesAttemptWithModalDialog) {
// terminal such as SSH (as opposed to Chromoting) to investigate the issue.
//
TEST(MessagePumpMacTest, DontInvalidateTimerInNativeRunLoop) {
- MessageLoopForUI message_loop;
+ test::SingleThreadTaskEnvironment task_environment(
+ test::SingleThreadTaskEnvironment::MainThreadType::UI);
NSWindow* window =
[[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
styleMask:NSBorderlessWindowMask
@@ -291,7 +294,8 @@ TEST(MessagePumpMacTest, DontInvalidateTimerInNativeRunLoop) {
}
TEST(MessagePumpMacTest, QuitWithModalWindow) {
- MessageLoopForUI message_loop;
+ test::SingleThreadTaskEnvironment task_environment(
+ test::SingleThreadTaskEnvironment::MainThreadType::UI);
NSWindow* window =
[[[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 100, 100)
styleMask:NSBorderlessWindowMask
diff --git a/chromium/base/message_loop/message_pump_perftest.cc b/chromium/base/message_loop/message_pump_perftest.cc
index 1af026f7cd2..7c7cdd4decd 100644
--- a/chromium/base/message_loop/message_pump_perftest.cc
+++ b/chromium/base/message_loop/message_pump_perftest.cc
@@ -9,7 +9,6 @@
#include "base/bind_helpers.h"
#include "base/format_macros.h"
#include "base/memory/ptr_util.h"
-#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_current.h"
#include "base/message_loop/message_pump_type.h"
#include "base/single_thread_task_runner.h"
@@ -98,12 +97,7 @@ class ScheduleWorkTest : public testing::Test {
target_.reset(new Thread("test"));
Thread::Options options(target_type, 0u);
-
- std::unique_ptr<MessageLoop> message_loop =
- MessageLoop::CreateUnbound(target_type);
- message_loop_ = message_loop.get();
- options.delegate =
- new internal::MessageLoopThreadDelegate(std::move(message_loop));
+ options.message_pump_type = target_type;
target_->StartWithOptions(options);
// Without this, it's possible for the scheduling threads to start and run
@@ -203,7 +197,6 @@ class ScheduleWorkTest : public testing::Test {
private:
std::unique_ptr<Thread> target_;
- MessageLoop* message_loop_;
#if defined(OS_ANDROID)
std::unique_ptr<JavaHandlerThreadForTest> java_thread_;
#endif
diff --git a/chromium/base/metrics/histogram_macros.h b/chromium/base/metrics/histogram_macros.h
index 9423fc07ad7..6edb65a93fd 100644
--- a/chromium/base/metrics/histogram_macros.h
+++ b/chromium/base/metrics/histogram_macros.h
@@ -187,7 +187,7 @@
// underflow bucket.
// Sample usage:
-// UMA_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", 1, 100000000, 100);
+// UMA_HISTOGRAM_CUSTOM_COUNTS("My.Histogram", sample, 1, 100000000, 50);
#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \
INTERNAL_HISTOGRAM_CUSTOM_COUNTS_WITH_FLAG( \
name, sample, min, max, bucket_count, \
diff --git a/chromium/base/native_library_win.cc b/chromium/base/native_library_win.cc
index 08d520d061b..74413b6c323 100644
--- a/chromium/base/native_library_win.cc
+++ b/chromium/base/native_library_win.cc
@@ -8,7 +8,6 @@
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
-#include "base/optional.h"
#include "base/path_service.h"
#include "base/scoped_native_library.h"
#include "base/strings/string_util.h"
@@ -170,12 +169,12 @@ NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
return module;
}
-Optional<FilePath> GetSystemLibraryName(FilePath::StringPieceType name) {
+FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
FilePath library_path;
// Use an absolute path to load the DLL to avoid DLL preloading attacks.
- if (!base::PathService::Get(base::DIR_SYSTEM, &library_path))
- return base::nullopt;
- return make_optional(library_path.Append(name));
+ if (PathService::Get(DIR_SYSTEM, &library_path))
+ library_path = library_path.Append(name);
+ return library_path;
}
} // namespace
@@ -210,18 +209,19 @@ std::string GetLoadableModuleName(StringPiece name) {
NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
NativeLibraryLoadError* error) {
- Optional<FilePath> library_path = GetSystemLibraryName(name);
- if (library_path)
- return LoadSystemLibraryHelper(library_path.value(), error);
- if (error)
- error->code = ERROR_NOT_FOUND;
- return nullptr;
+ FilePath library_path = GetSystemLibraryName(name);
+ if (library_path.empty()) {
+ if (error)
+ error->code = ERROR_NOT_FOUND;
+ return nullptr;
+ }
+ return LoadSystemLibraryHelper(library_path, error);
}
NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
NativeLibraryLoadError* error) {
- Optional<FilePath> library_path = GetSystemLibraryName(name);
- if (!library_path) {
+ FilePath library_path = GetSystemLibraryName(name);
+ if (library_path.empty()) {
if (error)
error->code = ERROR_NOT_FOUND;
return nullptr;
@@ -231,25 +231,28 @@ NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
// Dllmain.
ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
ScopedNativeLibrary module;
- if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
- as_wcstr(library_path.value().value()),
- ScopedNativeLibrary::Receiver(module).get())) {
- // Load and pin the library since it wasn't already loaded.
- module = ScopedNativeLibrary(
- LoadSystemLibraryHelper(library_path.value(), error));
- if (module.is_valid()) {
- ScopedNativeLibrary temp;
- if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
- as_wcstr(library_path.value().value()),
- ScopedNativeLibrary::Receiver(temp).get())) {
- if (error)
- error->code = ::GetLastError();
- // Return nullptr since we failed to pin the module.
- return nullptr;
- }
- }
+ if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
+ as_wcstr(library_path.value()),
+ ScopedNativeLibrary::Receiver(module).get())) {
+ return module.release();
}
- return module.release();
+
+ // Load and pin the library since it wasn't already loaded.
+ module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
+ if (!module.is_valid())
+ return nullptr;
+
+ ScopedNativeLibrary temp;
+ if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
+ as_wcstr(library_path.value()),
+ ScopedNativeLibrary::Receiver(temp).get())) {
+ return module.release();
+ }
+
+ if (error)
+ error->code = ::GetLastError();
+ // Return nullptr since we failed to pin the module.
+ return nullptr;
}
} // namespace base
diff --git a/chromium/base/numerics/safe_math_arm_impl.h b/chromium/base/numerics/safe_math_arm_impl.h
index a7cda1bb238..ff86bd0b73b 100644
--- a/chromium/base/numerics/safe_math_arm_impl.h
+++ b/chromium/base/numerics/safe_math_arm_impl.h
@@ -57,8 +57,8 @@ struct ClampedAddFastAsmOp {
return saturated_cast<V>(x + y);
int32_t result;
- int32_t x_i32 = x;
- int32_t y_i32 = y;
+ int32_t x_i32 = checked_cast<int32_t>(x);
+ int32_t y_i32 = checked_cast<int32_t>(y);
asm("qadd %[result], %[first], %[second]"
: [result] "=r"(result)
@@ -83,8 +83,8 @@ struct ClampedSubFastAsmOp {
return saturated_cast<V>(x - y);
int32_t result;
- int32_t x_i32 = x;
- int32_t y_i32 = y;
+ int32_t x_i32 = checked_cast<int32_t>(x);
+ int32_t y_i32 = checked_cast<int32_t>(y);
asm("qsub %[result], %[first], %[second]"
: [result] "=r"(result)
diff --git a/chromium/base/process/kill.h b/chromium/base/process/kill.h
index 9acfb8a7388..70a04d97e5a 100644
--- a/chromium/base/process/kill.h
+++ b/chromium/base/process/kill.h
@@ -29,6 +29,7 @@ const DWORD kNormalTerminationExitCode = 0;
const DWORD kDebuggerInactiveExitCode = 0xC0000354;
const DWORD kKeyboardInterruptExitCode = 0xC000013A;
const DWORD kDebuggerTerminatedExitCode = 0x40010004;
+const DWORD kStatusInvalidImageHashExitCode = 0xC0000428;
// This exit code is used by the Windows task manager when it kills a
// process. It's value is obviously not that unique, and it's
@@ -46,6 +47,7 @@ const DWORD kProcessKilledExitCode = 1;
// exit code arguments to KillProcess*(), use platform/application
// specific values instead.
enum TerminationStatus {
+ // clang-format off
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
@@ -64,7 +66,12 @@ enum TerminationStatus {
#endif
TERMINATION_STATUS_LAUNCH_FAILED, // child process never launched
TERMINATION_STATUS_OOM, // Process died due to oom
+#if defined(OS_WIN)
+ // On Windows, the OS terminated process due to code integrity failure.
+ TERMINATION_STATUS_INTEGRITY_FAILURE,
+#endif
TERMINATION_STATUS_MAX_ENUM
+ // clang-format on
};
// Attempts to kill all the processes on the current machine that were launched
diff --git a/chromium/base/process/kill_win.cc b/chromium/base/process/kill_win.cc
index 7a664429bcd..3b85dea1cd4 100644
--- a/chromium/base/process/kill_win.cc
+++ b/chromium/base/process/kill_win.cc
@@ -61,6 +61,7 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
*exit_code = tmp_exit_code;
+ // clang-format off
switch (tmp_exit_code) {
case win::kNormalTerminationExitCode:
return TERMINATION_STATUS_NORMAL_TERMINATION;
@@ -74,10 +75,15 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
// object memory limits.
case win::kOomExceptionCode: // Ran out of memory.
return TERMINATION_STATUS_OOM;
+ // This exit code means the process failed an OS integrity check.
+ // This is tested in ProcessMitigationsTest.* in sandbox.
+ case win::kStatusInvalidImageHashExitCode:
+ return TERMINATION_STATUS_INTEGRITY_FAILURE;
default:
// All other exit codes indicate crashes.
return TERMINATION_STATUS_PROCESS_CRASHED;
}
+ // clang-format on
}
bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc
index 6bf9e188d66..59c4e8f7e27 100644
--- a/chromium/base/process/launch_win.cc
+++ b/chromium/base/process/launch_win.cc
@@ -287,9 +287,9 @@ Process LaunchProcess(const string16& cmdline,
<< "job. https://crbug.com/820996";
if (options.as_user) {
flags |= CREATE_UNICODE_ENVIRONMENT;
- void* enviroment_block = nullptr;
+ void* environment_block = nullptr;
- if (!CreateEnvironmentBlock(&enviroment_block, options.as_user, FALSE)) {
+ if (!CreateEnvironmentBlock(&environment_block, options.as_user, FALSE)) {
DPLOG(ERROR);
return Process();
}
@@ -300,9 +300,9 @@ Process LaunchProcess(const string16& cmdline,
BOOL launched = CreateProcessAsUser(
options.as_user, nullptr, as_writable_wcstr(writable_cmdline), nullptr,
- nullptr, inherit_handles, flags, enviroment_block, current_directory,
+ nullptr, inherit_handles, flags, environment_block, current_directory,
startup_info, &temp_process_info);
- DestroyEnvironmentBlock(enviroment_block);
+ DestroyEnvironmentBlock(environment_block);
if (!launched) {
DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline)
<< std::endl;
diff --git a/chromium/base/process/process_unittest.cc b/chromium/base/process/process_unittest.cc
index 68126460557..18db6892399 100644
--- a/chromium/base/process/process_unittest.cc
+++ b/chromium/base/process/process_unittest.cc
@@ -271,13 +271,7 @@ TEST_F(ProcessTest, WaitForExitWithTimeout) {
// 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.
-// Flaky on Windows: https://crbug.com/931721.
-#if defined(OS_WIN)
-#define MAYBE_SetProcessBackgrounded DISABLED_SetProcessBackgrounded
-#else
-#define MAYBE_SetProcessBackgrounded SetProcessBackgrounded
-#endif
-TEST_F(ProcessTest, MAYBE_SetProcessBackgrounded) {
+TEST_F(ProcessTest, SetProcessBackgrounded) {
if (!Process::CanBackgroundProcesses())
return;
Process process(SpawnChild("SimpleChildProcess"));
@@ -305,15 +299,9 @@ TEST_F(ProcessTest, MAYBE_SetProcessBackgrounded) {
EXPECT_EQ(old_priority, new_priority);
}
-// Flaky on Windows: https://crbug.com/931721.
-#if defined(OS_WIN)
-#define MAYBE_SetProcessBackgroundedSelf DISABLED_SetProcessBackgroundedSelf
-#else
-#define MAYBE_SetProcessBackgroundedSelf SetProcessBackgroundedSelf
-#endif
// Same as SetProcessBackgrounded but to this very process. It uses
// a different code path at least for Windows.
-TEST_F(ProcessTest, MAYBE_SetProcessBackgroundedSelf) {
+TEST_F(ProcessTest, SetProcessBackgroundedSelf) {
if (!Process::CanBackgroundProcesses())
return;
Process process = Process::Current();
diff --git a/chromium/base/profiler/register_context.h b/chromium/base/profiler/register_context.h
index 9c6eaf1b1b2..556b34c27aa 100644
--- a/chromium/base/profiler/register_context.h
+++ b/chromium/base/profiler/register_context.h
@@ -17,6 +17,8 @@
#include <windows.h>
#elif defined(OS_MACOSX)
#include <mach/machine/thread_status.h>
+#elif defined(OS_ANDROID) && !defined(ARCH_CPU_64_BITS)
+#include <sys/ucontext.h>
#endif
namespace base {
@@ -83,6 +85,23 @@ inline uintptr_t& RegisterContextInstructionPointer(
return AsUintPtr(&context->__rip);
}
+#elif defined(OS_ANDROID) && defined(ARCH_CPU_ARM_FAMILY) && \
+ defined(ARCH_CPU_32_BITS) // #if defined(OS_WIN)
+
+using RegisterContext = mcontext_t;
+
+inline uintptr_t& RegisterContextStackPointer(mcontext_t* context) {
+ return AsUintPtr(&context->arm_sp);
+}
+
+inline uintptr_t& RegisterContextFramePointer(mcontext_t* context) {
+ return AsUintPtr(&context->arm_fp);
+}
+
+inline uintptr_t& RegisterContextInstructionPointer(mcontext_t* context) {
+ return AsUintPtr(&context->arm_ip);
+}
+
#else // #if defined(OS_WIN)
// Placeholders for other platforms.
diff --git a/chromium/base/profiler/stack_copier.h b/chromium/base/profiler/stack_copier.h
index cea4574362f..921e1324e1d 100644
--- a/chromium/base/profiler/stack_copier.h
+++ b/chromium/base/profiler/stack_copier.h
@@ -33,6 +33,7 @@ class BASE_EXPORT StackCopier {
ProfileBuilder* profile_builder,
RegisterContext* thread_context) = 0;
+ protected:
// If the value at |pointer| points to the original stack, rewrite it to point
// to the corresponding location in the copied stack.
//
diff --git a/chromium/base/profiler/stack_copier_signal.cc b/chromium/base/profiler/stack_copier_signal.cc
new file mode 100644
index 00000000000..ae858701252
--- /dev/null
+++ b/chromium/base/profiler/stack_copier_signal.cc
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium 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/profiler/stack_copier_signal.h"
+
+#include "base/profiler/metadata_recorder.h"
+#include "base/profiler/sample_metadata.h"
+#include "base/profiler/stack_buffer.h"
+#include "base/profiler/suspendable_thread_delegate.h"
+
+namespace base {
+
+StackCopierSignal::StackCopierSignal(
+ std::unique_ptr<ThreadDelegate> thread_delegate)
+ : thread_delegate_(std::move(thread_delegate)) {}
+
+StackCopierSignal::~StackCopierSignal() = default;
+
+bool StackCopierSignal::CopyStack(StackBuffer* stack_buffer,
+ uintptr_t* stack_top,
+ ProfileBuilder* profile_builder,
+ RegisterContext* thread_context) {
+ // TODO(wittman): Implement signal-based stack copying.
+ return false;
+}
+
+} // namespace base
diff --git a/chromium/base/profiler/stack_copier_signal.h b/chromium/base/profiler/stack_copier_signal.h
new file mode 100644
index 00000000000..14f48969200
--- /dev/null
+++ b/chromium/base/profiler/stack_copier_signal.h
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium 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_PROFILER_STACK_COPIER_SIGNAL_H_
+#define BASE_PROFILER_STACK_COPIER_SIGNAL_H_
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/profiler/stack_copier.h"
+
+namespace base {
+
+class ThreadDelegate;
+
+// Supports stack copying on platforms where a signal must be delivered to the
+// profiled thread and the stack is copied from the signal handler.
+class BASE_EXPORT StackCopierSignal : public StackCopier {
+ public:
+ StackCopierSignal(std::unique_ptr<ThreadDelegate> thread_delegate);
+ ~StackCopierSignal() override;
+
+ // StackCopier:
+ bool CopyStack(StackBuffer* stack_buffer,
+ uintptr_t* stack_top,
+ ProfileBuilder* profile_builder,
+ RegisterContext* thread_context) override;
+
+ private:
+ std::unique_ptr<ThreadDelegate> thread_delegate_;
+};
+
+} // namespace base
+
+#endif // BASE_PROFILER_STACK_COPIER_SIGNAL_H_
diff --git a/chromium/base/profiler/stack_copier_suspend.cc b/chromium/base/profiler/stack_copier_suspend.cc
new file mode 100644
index 00000000000..a12aba595d4
--- /dev/null
+++ b/chromium/base/profiler/stack_copier_suspend.cc
@@ -0,0 +1,82 @@
+// Copyright 2019 The Chromium 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/profiler/stack_copier_suspend.h"
+
+#include "base/profiler/metadata_recorder.h"
+#include "base/profiler/sample_metadata.h"
+#include "base/profiler/stack_buffer.h"
+#include "base/profiler/suspendable_thread_delegate.h"
+
+namespace base {
+
+StackCopierSuspend::StackCopierSuspend(
+ std::unique_ptr<SuspendableThreadDelegate> thread_delegate)
+ : thread_delegate_(std::move(thread_delegate)) {}
+
+StackCopierSuspend::~StackCopierSuspend() = default;
+
+// Suspends the thread, copies the stack state, and resumes the thread. The
+// copied stack state includes the stack itself, the top address of the stack
+// copy, the register context, and the current metadata state. Returns true on
+// success, and returns the copied state via the params.
+//
+// NO HEAP ALLOCATIONS within the ScopedSuspendThread scope.
+bool StackCopierSuspend::CopyStack(StackBuffer* stack_buffer,
+ uintptr_t* stack_top,
+ ProfileBuilder* profile_builder,
+ RegisterContext* thread_context) {
+ const uintptr_t top = thread_delegate_->GetStackBaseAddress();
+ uintptr_t bottom = 0;
+ const uint8_t* stack_copy_bottom = nullptr;
+ {
+ // The MetadataProvider must be created before the ScopedSuspendThread
+ // because it acquires a lock in its constructor that might otherwise be
+ // held by the target thread, resulting in deadlock.
+ std::unique_ptr<base::ProfileBuilder::MetadataProvider> get_metadata_items =
+ base::GetSampleMetadataRecorder()->CreateMetadataProvider();
+
+ // Allocation of the ScopedSuspendThread object itself is OK since it
+ // necessarily occurs before the thread is suspended by the object.
+ std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread>
+ suspend_thread = thread_delegate_->CreateScopedSuspendThread();
+
+ if (!suspend_thread->WasSuccessful())
+ return false;
+
+ if (!thread_delegate_->GetThreadContext(thread_context))
+ return false;
+
+ bottom = RegisterContextStackPointer(thread_context);
+
+ // The StackBuffer allocation is expected to be at least as large as the
+ // largest stack region allocation on the platform, but check just in case
+ // it isn't *and* the actual stack itself exceeds the buffer allocation
+ // size.
+ if ((top - bottom) > stack_buffer->size())
+ return false;
+
+ if (!thread_delegate_->CanCopyStack(bottom))
+ return false;
+
+ profile_builder->RecordMetadata(get_metadata_items.get());
+
+ stack_copy_bottom = CopyStackContentsAndRewritePointers(
+ reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top),
+ StackBuffer::kPlatformStackAlignment, stack_buffer->buffer());
+ }
+
+ *stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) + (top - bottom);
+
+ for (uintptr_t* reg :
+ thread_delegate_->GetRegistersToRewrite(thread_context)) {
+ *reg = RewritePointerIfInOriginalStack(reinterpret_cast<uint8_t*>(bottom),
+ reinterpret_cast<uintptr_t*>(top),
+ stack_copy_bottom, *reg);
+ }
+
+ return true;
+}
+
+} // namespace base
diff --git a/chromium/base/profiler/stack_copier_suspend.h b/chromium/base/profiler/stack_copier_suspend.h
new file mode 100644
index 00000000000..67976251b51
--- /dev/null
+++ b/chromium/base/profiler/stack_copier_suspend.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium 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_PROFILER_STACK_COPIER_SUSPEND_H_
+#define BASE_PROFILER_STACK_COPIER_SUSPEND_H_
+
+#include <memory>
+
+#include "base/base_export.h"
+#include "base/profiler/stack_copier.h"
+
+namespace base {
+
+class SuspendableThreadDelegate;
+
+// Supports stack copying on platforms where the profiled thread must be
+// explicitly suspended from the profiler thread and the stack is copied from
+// the profiler thread.
+class BASE_EXPORT StackCopierSuspend : public StackCopier {
+ public:
+ StackCopierSuspend(
+ std::unique_ptr<SuspendableThreadDelegate> thread_delegate);
+ ~StackCopierSuspend() override;
+
+ // StackCopier:
+ bool CopyStack(StackBuffer* stack_buffer,
+ uintptr_t* stack_top,
+ ProfileBuilder* profile_builder,
+ RegisterContext* thread_context) override;
+
+ private:
+ std::unique_ptr<SuspendableThreadDelegate> thread_delegate_;
+};
+
+} // namespace base
+
+#endif // BASE_PROFILER_STACK_COPIER_SUSPEND_H_
diff --git a/chromium/base/profiler/stack_copier_suspend_unittest.cc b/chromium/base/profiler/stack_copier_suspend_unittest.cc
new file mode 100644
index 00000000000..a496ade1602
--- /dev/null
+++ b/chromium/base/profiler/stack_copier_suspend_unittest.cc
@@ -0,0 +1,201 @@
+// Copyright 2019 The Chromium 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 <algorithm>
+#include <cstring>
+#include <memory>
+#include <numeric>
+#include <utility>
+
+#include "base/profiler/profile_builder.h"
+#include "base/profiler/stack_buffer.h"
+#include "base/profiler/stack_copier_suspend.h"
+#include "base/profiler/suspendable_thread_delegate.h"
+#include "base/stl_util.h"
+#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+using ::testing::ElementsAre;
+
+// A thread delegate for use in tests that provides the expected behavior when
+// operating on the supplied fake stack.
+class TestSuspendableThreadDelegate : public SuspendableThreadDelegate {
+ public:
+ class TestScopedSuspendThread
+ : public SuspendableThreadDelegate::ScopedSuspendThread {
+ public:
+ TestScopedSuspendThread() = default;
+
+ TestScopedSuspendThread(const TestScopedSuspendThread&) = delete;
+ TestScopedSuspendThread& operator=(const TestScopedSuspendThread&) = delete;
+
+ bool WasSuccessful() const override { return true; }
+ };
+
+ TestSuspendableThreadDelegate(const std::vector<uintptr_t>& fake_stack,
+ // The register context will be initialized to
+ // *|thread_context| if non-null.
+ RegisterContext* thread_context = nullptr)
+ : fake_stack_(fake_stack), thread_context_(thread_context) {}
+
+ TestSuspendableThreadDelegate(const TestSuspendableThreadDelegate&) = delete;
+ TestSuspendableThreadDelegate& operator=(
+ const TestSuspendableThreadDelegate&) = delete;
+
+ std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() override {
+ return std::make_unique<TestScopedSuspendThread>();
+ }
+
+ bool GetThreadContext(RegisterContext* thread_context) override {
+ if (thread_context_)
+ *thread_context = *thread_context_;
+ // Set the stack pointer to be consistent with the provided fake stack.
+ RegisterContextStackPointer(thread_context) =
+ reinterpret_cast<uintptr_t>(&fake_stack_[0]);
+ RegisterContextInstructionPointer(thread_context) =
+ reinterpret_cast<uintptr_t>(fake_stack_[0]);
+ return true;
+ }
+
+ uintptr_t GetStackBaseAddress() const override {
+ return reinterpret_cast<uintptr_t>(&fake_stack_[0] + fake_stack_.size());
+ }
+
+ bool CanCopyStack(uintptr_t stack_pointer) override { return true; }
+
+ std::vector<uintptr_t*> GetRegistersToRewrite(
+ RegisterContext* thread_context) override {
+ return {&RegisterContextFramePointer(thread_context)};
+ }
+
+ private:
+ // Must be a reference to retain the underlying allocation from the vector
+ // passed to the constructor.
+ const std::vector<uintptr_t>& fake_stack_;
+ RegisterContext* thread_context_;
+};
+
+class TestProfileBuilder : public ProfileBuilder {
+ public:
+ TestProfileBuilder() = default;
+
+ TestProfileBuilder(const TestProfileBuilder&) = delete;
+ TestProfileBuilder& operator=(const TestProfileBuilder&) = delete;
+
+ // ProfileBuilder
+ ModuleCache* GetModuleCache() override { return nullptr; }
+
+ void RecordMetadata(
+ base::ProfileBuilder::MetadataProvider* metadata_provider) override {
+ recorded_metadata_ = true;
+ }
+
+ void OnSampleCompleted(std::vector<Frame> frames) override {}
+ void OnProfileCompleted(TimeDelta profile_duration,
+ TimeDelta sampling_period) override {}
+
+ private:
+ bool recorded_metadata_ = false;
+};
+
+} // namespace
+
+TEST(StackCopierSuspendTest, CopyStack) {
+ const std::vector<uintptr_t> stack = {0, 1, 2, 3, 4};
+ StackCopierSuspend stack_copier_suspend(
+ std::make_unique<TestSuspendableThreadDelegate>(stack));
+
+ std::unique_ptr<StackBuffer> stack_buffer =
+ std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
+ uintptr_t stack_top = 0;
+ TestProfileBuilder profile_builder;
+ RegisterContext register_context = {0};
+ stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
+ &profile_builder, &register_context);
+
+ uintptr_t* stack_copy_bottom =
+ reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
+ std::vector<uintptr_t> stack_copy(stack_copy_bottom,
+ stack_copy_bottom + stack.size());
+ EXPECT_EQ(stack, stack_copy);
+}
+
+TEST(StackCopierSuspendTest, CopyStackBufferTooSmall) {
+ std::vector<uintptr_t> stack = {0, 1, 2, 3, 4};
+ StackCopierSuspend stack_copier_suspend(
+ std::make_unique<TestSuspendableThreadDelegate>(stack));
+
+ std::unique_ptr<StackBuffer> stack_buffer =
+ std::make_unique<StackBuffer>((stack.size() - 1) * sizeof(uintptr_t));
+ // Make the buffer different than the input stack.
+ stack_buffer->buffer()[0] = 100;
+ uintptr_t stack_top = 0;
+ TestProfileBuilder profile_builder;
+ RegisterContext register_context = {0};
+ stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
+ &profile_builder, &register_context);
+
+ uintptr_t* stack_copy_bottom =
+ reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
+ std::vector<uintptr_t> stack_copy(stack_copy_bottom,
+ stack_copy_bottom + stack.size());
+ // Use the buffer not being overwritten as a proxy for the unwind being
+ // aborted.
+ EXPECT_NE(stack, stack_copy);
+}
+
+TEST(StackCopierSuspendTest, CopyStackAndRewritePointers) {
+ // Allocate space for the stack, then make its elements point to themselves.
+ std::vector<uintptr_t> stack(2);
+ stack[0] = reinterpret_cast<uintptr_t>(&stack[0]);
+ stack[1] = reinterpret_cast<uintptr_t>(&stack[1]);
+ StackCopierSuspend stack_copier_suspend(
+ std::make_unique<TestSuspendableThreadDelegate>(stack));
+
+ std::unique_ptr<StackBuffer> stack_buffer =
+ std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
+ uintptr_t stack_top = 0;
+ TestProfileBuilder profile_builder;
+ RegisterContext register_context = {0};
+ stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
+ &profile_builder, &register_context);
+
+ uintptr_t* stack_copy_bottom =
+ reinterpret_cast<uintptr_t*>(stack_buffer.get()->buffer());
+ std::vector<uintptr_t> stack_copy(stack_copy_bottom,
+ stack_copy_bottom + stack.size());
+ EXPECT_THAT(stack_copy,
+ ElementsAre(reinterpret_cast<uintptr_t>(stack_copy_bottom),
+ reinterpret_cast<uintptr_t>(stack_copy_bottom) +
+ sizeof(uintptr_t)));
+}
+
+TEST(StackCopierSuspendTest, RewriteRegisters) {
+ std::vector<uintptr_t> stack = {0, 1, 2};
+ RegisterContext register_context = {0};
+ RegisterContextFramePointer(&register_context) =
+ reinterpret_cast<uintptr_t>(&stack[1]);
+ StackCopierSuspend stack_copier_suspend(
+ std::make_unique<TestSuspendableThreadDelegate>(stack,
+ &register_context));
+
+ std::unique_ptr<StackBuffer> stack_buffer =
+ std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
+ uintptr_t stack_top = 0;
+ TestProfileBuilder profile_builder;
+ stack_copier_suspend.CopyStack(stack_buffer.get(), &stack_top,
+ &profile_builder, &register_context);
+
+ uintptr_t stack_copy_bottom =
+ reinterpret_cast<uintptr_t>(stack_buffer.get()->buffer());
+ EXPECT_EQ(stack_copy_bottom + sizeof(uintptr_t),
+ RegisterContextFramePointer(&register_context));
+}
+
+} // namespace base
diff --git a/chromium/base/profiler/stack_copier_unittest.cc b/chromium/base/profiler/stack_copier_unittest.cc
index a065c9f9a72..b4a81ef4b8b 100644
--- a/chromium/base/profiler/stack_copier_unittest.cc
+++ b/chromium/base/profiler/stack_copier_unittest.cc
@@ -15,6 +15,12 @@ namespace base {
namespace {
+class CopyFunctions : public StackCopier {
+ public:
+ using StackCopier::CopyStackContentsAndRewritePointers;
+ using StackCopier::RewritePointerIfInOriginalStack;
+};
+
static constexpr size_t kTestStackBufferSize = sizeof(uintptr_t) * 4;
union alignas(StackBuffer::kPlatformStackAlignment) TestStackBuffer {
@@ -29,7 +35,7 @@ TEST(StackCopierTest, RewritePointerIfInOriginalStack_InStack) {
uintptr_t original_stack[4];
uintptr_t stack_copy[4];
EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack_copy[2]),
- StackCopier::RewritePointerIfInOriginalStack(
+ CopyFunctions::RewritePointerIfInOriginalStack(
reinterpret_cast<uint8_t*>(&original_stack[0]),
&original_stack[0] + base::size(original_stack),
reinterpret_cast<uint8_t*>(&stack_copy[0]),
@@ -44,7 +50,7 @@ TEST(StackCopierTest, RewritePointerIfInOriginalStack_NotInStack) {
uintptr_t stack_copy[4];
EXPECT_EQ(reinterpret_cast<uintptr_t>(&non_stack_location),
- StackCopier::RewritePointerIfInOriginalStack(
+ CopyFunctions::RewritePointerIfInOriginalStack(
reinterpret_cast<uint8_t*>(&original_stack[0]),
&original_stack[0] + size(original_stack),
reinterpret_cast<uint8_t*>(&stack_copy[0]),
@@ -62,7 +68,7 @@ TEST(StackCopierTest, StackCopy) {
reinterpret_cast<uintptr_t>(&original_stack.as_uintptr[1]);
TestStackBuffer stack_copy;
- StackCopier::CopyStackContentsAndRewritePointers(
+ CopyFunctions::CopyStackContentsAndRewritePointers(
&original_stack.as_uint8[0],
&original_stack.as_uintptr[0] + size(original_stack.as_uintptr),
StackBuffer::kPlatformStackAlignment, &stack_copy.as_uintptr[0]);
@@ -97,7 +103,7 @@ TEST(StackCopierTest, StackCopy_NonAlignedStackPointerCopy) {
TestStackBuffer stack_copy_buffer = {{0}};
const uint8_t* stack_copy_bottom =
- StackCopier::CopyStackContentsAndRewritePointers(
+ CopyFunctions::CopyStackContentsAndRewritePointers(
unaligned_stack_bottom, stack_top,
StackBuffer::kPlatformStackAlignment,
&stack_copy_buffer.as_uintptr[0]);
@@ -144,7 +150,7 @@ TEST(StackCopierTest, StackCopy_NonAlignedStackPointerUnalignedRewriteAtStart) {
TestStackBuffer stack_copy_buffer = {{0}};
const uint8_t* stack_copy_bottom =
- StackCopier::CopyStackContentsAndRewritePointers(
+ CopyFunctions::CopyStackContentsAndRewritePointers(
unaligned_stack_bottom,
&stack_buffer.as_uintptr[0] + size(stack_buffer.as_uintptr),
StackBuffer::kPlatformStackAlignment,
@@ -181,7 +187,7 @@ TEST(StackCopierTest,
TestStackBuffer stack_copy_buffer = {{0}};
const uint8_t* stack_copy_bottom =
- StackCopier::CopyStackContentsAndRewritePointers(
+ CopyFunctions::CopyStackContentsAndRewritePointers(
unaligned_stack_bottom,
&stack_buffer.as_uintptr[0] + size(stack_buffer.as_uintptr),
StackBuffer::kPlatformStackAlignment,
@@ -212,7 +218,7 @@ TEST(StackCopierTest, StackCopy_NonAlignedStackPointerAlignedRewrite) {
TestStackBuffer stack_copy_buffer = {{0}};
- StackCopier::CopyStackContentsAndRewritePointers(
+ CopyFunctions::CopyStackContentsAndRewritePointers(
unaligned_stack_bottom,
&stack_buffer.as_uintptr[0] + size(stack_buffer.as_uintptr),
StackBuffer::kPlatformStackAlignment, &stack_copy_buffer.as_uintptr[0]);
diff --git a/chromium/base/profiler/stack_sampler_android.cc b/chromium/base/profiler/stack_sampler_android.cc
index e3190e3a69d..85b3626b53e 100644
--- a/chromium/base/profiler/stack_sampler_android.cc
+++ b/chromium/base/profiler/stack_sampler_android.cc
@@ -7,6 +7,7 @@
#include <pthread.h>
#include "base/profiler/native_unwinder_android.h"
+#include "base/profiler/stack_copier_signal.h"
#include "base/profiler/stack_sampler_impl.h"
#include "base/profiler/thread_delegate_android.h"
#include "base/threading/platform_thread.h"
@@ -18,7 +19,8 @@ std::unique_ptr<StackSampler> StackSampler::Create(
ModuleCache* module_cache,
StackSamplerTestDelegate* test_delegate) {
return std::make_unique<StackSamplerImpl>(
- std::make_unique<ThreadDelegateAndroid>(),
+ std::make_unique<StackCopierSignal>(
+ std::make_unique<ThreadDelegateAndroid>(thread_id)),
std::make_unique<NativeUnwinderAndroid>(), module_cache, test_delegate);
}
diff --git a/chromium/base/profiler/stack_sampler_impl.cc b/chromium/base/profiler/stack_sampler_impl.cc
index 1e63b54ed2f..f80e9c91fcf 100644
--- a/chromium/base/profiler/stack_sampler_impl.cc
+++ b/chromium/base/profiler/stack_sampler_impl.cc
@@ -11,7 +11,7 @@
#include "base/profiler/sample_metadata.h"
#include "base/profiler/stack_buffer.h"
#include "base/profiler/stack_copier.h"
-#include "base/profiler/thread_delegate.h"
+#include "base/profiler/suspendable_thread_delegate.h"
#include "base/profiler/unwinder.h"
// IMPORTANT NOTE: Some functions within this implementation are invoked while
@@ -23,12 +23,11 @@
namespace base {
-StackSamplerImpl::StackSamplerImpl(
- std::unique_ptr<ThreadDelegate> thread_delegate,
- std::unique_ptr<Unwinder> native_unwinder,
- ModuleCache* module_cache,
- StackSamplerTestDelegate* test_delegate)
- : thread_delegate_(std::move(thread_delegate)),
+StackSamplerImpl::StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier,
+ std::unique_ptr<Unwinder> native_unwinder,
+ ModuleCache* module_cache,
+ StackSamplerTestDelegate* test_delegate)
+ : stack_copier_(std::move(stack_copier)),
native_unwinder_(std::move(native_unwinder)),
module_cache_(module_cache),
test_delegate_(test_delegate) {}
@@ -46,8 +45,8 @@ void StackSamplerImpl::RecordStackFrames(StackBuffer* stack_buffer,
RegisterContext thread_context;
uintptr_t stack_top;
- bool success =
- CopyStack(stack_buffer, &stack_top, profile_builder, &thread_context);
+ bool success = stack_copier_->CopyStack(stack_buffer, &stack_top,
+ profile_builder, &thread_context);
if (!success)
return;
@@ -70,66 +69,6 @@ std::vector<Frame> StackSamplerImpl::WalkStackForTesting(
aux_unwinder);
}
-// Suspends the thread, copies its stack, top address of the stack copy, and
-// register context, records the current metadata, then resumes the thread.
-// Returns true on success, and returns the copied state via the params. NO HEAP
-// ALLOCATIONS within the ScopedSuspendThread scope.
-bool StackSamplerImpl::CopyStack(StackBuffer* stack_buffer,
- uintptr_t* stack_top,
- ProfileBuilder* profile_builder,
- RegisterContext* thread_context) {
- const uintptr_t top = thread_delegate_->GetStackBaseAddress();
- uintptr_t bottom = 0;
- const uint8_t* stack_copy_bottom = nullptr;
- {
- // The MetadataProvider must be created before the ScopedSuspendThread
- // because it acquires a lock in its constructor that might otherwise be
- // held by the target thread, resulting in deadlock.
- std::unique_ptr<base::ProfileBuilder::MetadataProvider> get_metadata_items =
- base::GetSampleMetadataRecorder()->CreateMetadataProvider();
-
- // Allocation of the ScopedSuspendThread object itself is OK since it
- // necessarily occurs before the thread is suspended by the object.
- std::unique_ptr<ThreadDelegate::ScopedSuspendThread> suspend_thread =
- thread_delegate_->CreateScopedSuspendThread();
-
- if (!suspend_thread->WasSuccessful())
- return false;
-
- if (!thread_delegate_->GetThreadContext(thread_context))
- return false;
-
- bottom = RegisterContextStackPointer(thread_context);
-
- // The StackBuffer allocation is expected to be at least as large as the
- // largest stack region allocation on the platform, but check just in case
- // it isn't *and* the actual stack itself exceeds the buffer allocation
- // size.
- if ((top - bottom) > stack_buffer->size())
- return false;
-
- if (!thread_delegate_->CanCopyStack(bottom))
- return false;
-
- profile_builder->RecordMetadata(get_metadata_items.get());
-
- stack_copy_bottom = StackCopier::CopyStackContentsAndRewritePointers(
- reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top),
- StackBuffer::kPlatformStackAlignment, stack_buffer->buffer());
- }
-
- *stack_top = reinterpret_cast<uintptr_t>(stack_copy_bottom) + (top - bottom);
-
- for (uintptr_t* reg :
- thread_delegate_->GetRegistersToRewrite(thread_context)) {
- *reg = StackCopier::RewritePointerIfInOriginalStack(
- reinterpret_cast<uint8_t*>(bottom), reinterpret_cast<uintptr_t*>(top),
- stack_copy_bottom, *reg);
- }
-
- return true;
-}
-
// static
std::vector<Frame> StackSamplerImpl::WalkStack(ModuleCache* module_cache,
RegisterContext* thread_context,
diff --git a/chromium/base/profiler/stack_sampler_impl.h b/chromium/base/profiler/stack_sampler_impl.h
index b28d90c1d04..226ccdb9435 100644
--- a/chromium/base/profiler/stack_sampler_impl.h
+++ b/chromium/base/profiler/stack_sampler_impl.h
@@ -14,14 +14,14 @@
namespace base {
-class ThreadDelegate;
+class StackCopier;
class Unwinder;
-// Cross-platform stack sampler implementation. Delegates to ThreadDelegate for
-// platform-specific implementation.
+// Cross-platform stack sampler implementation. Delegates to StackCopier for the
+// platform-specific stack copying implementation.
class BASE_EXPORT StackSamplerImpl : public StackSampler {
public:
- StackSamplerImpl(std::unique_ptr<ThreadDelegate> delegate,
+ StackSamplerImpl(std::unique_ptr<StackCopier> stack_copier,
std::unique_ptr<Unwinder> native_unwinder,
ModuleCache* module_cache,
StackSamplerTestDelegate* test_delegate = nullptr);
@@ -43,18 +43,13 @@ class BASE_EXPORT StackSamplerImpl : public StackSampler {
Unwinder* aux_unwinder);
private:
- bool CopyStack(StackBuffer* stack_buffer,
- uintptr_t* stack_top,
- ProfileBuilder* profile_builder,
- RegisterContext* thread_context);
-
static std::vector<Frame> WalkStack(ModuleCache* module_cache,
RegisterContext* thread_context,
uintptr_t stack_top,
Unwinder* native_unwinder,
Unwinder* aux_unwinder);
- const std::unique_ptr<ThreadDelegate> thread_delegate_;
+ const std::unique_ptr<StackCopier> stack_copier_;
const std::unique_ptr<Unwinder> native_unwinder_;
std::unique_ptr<Unwinder> aux_unwinder_;
ModuleCache* const module_cache_;
diff --git a/chromium/base/profiler/stack_sampler_impl_unittest.cc b/chromium/base/profiler/stack_sampler_impl_unittest.cc
index f9d56c3a239..5e3389db705 100644
--- a/chromium/base/profiler/stack_sampler_impl_unittest.cc
+++ b/chromium/base/profiler/stack_sampler_impl_unittest.cc
@@ -10,8 +10,9 @@
#include "base/profiler/profile_builder.h"
#include "base/profiler/stack_buffer.h"
+#include "base/profiler/stack_copier.h"
#include "base/profiler/stack_sampler_impl.h"
-#include "base/profiler/thread_delegate.h"
+#include "base/profiler/suspendable_thread_delegate.h"
#include "base/profiler/unwinder.h"
#include "base/sampling_heap_profiler/module_cache.h"
#include "base/stl_util.h"
@@ -44,60 +45,31 @@ class TestProfileBuilder : public ProfileBuilder {
ModuleCache* module_cache_;
};
-// A thread delegate for use in tests that provides the expected behavior when
+// A stack copier for use in tests that provides the expected behavior when
// operating on the supplied fake stack.
-class TestThreadDelegate : public ThreadDelegate {
+class TestStackCopier : public StackCopier {
public:
- class TestScopedSuspendThread : public ThreadDelegate::ScopedSuspendThread {
- public:
- TestScopedSuspendThread() = default;
-
- TestScopedSuspendThread(const TestScopedSuspendThread&) = delete;
- TestScopedSuspendThread& operator=(const TestScopedSuspendThread&) = delete;
-
- bool WasSuccessful() const override { return true; }
- };
-
- TestThreadDelegate(const std::vector<uintptr_t>& fake_stack,
- // The register context will be initialized to
- // *|thread_context| if non-null.
- RegisterContext* thread_context = nullptr)
- : fake_stack_(fake_stack), thread_context_(thread_context) {}
-
- TestThreadDelegate(const TestThreadDelegate&) = delete;
- TestThreadDelegate& operator=(const TestThreadDelegate&) = delete;
-
- std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() override {
- return std::make_unique<TestScopedSuspendThread>();
- }
-
- bool GetThreadContext(RegisterContext* thread_context) override {
- if (thread_context_)
- *thread_context = *thread_context_;
+ TestStackCopier(const std::vector<uintptr_t>& fake_stack)
+ : fake_stack_(fake_stack) {}
+
+ bool CopyStack(StackBuffer* stack_buffer,
+ uintptr_t* stack_top,
+ ProfileBuilder* profile_builder,
+ RegisterContext* thread_context) override {
+ std::memcpy(stack_buffer->buffer(), &fake_stack_[0], fake_stack_.size());
+ *stack_top =
+ reinterpret_cast<uintptr_t>(&fake_stack_[0] + fake_stack_.size());
// Set the stack pointer to be consistent with the provided fake stack.
RegisterContextStackPointer(thread_context) =
reinterpret_cast<uintptr_t>(&fake_stack_[0]);
- RegisterContextInstructionPointer(thread_context) =
- reinterpret_cast<uintptr_t>(fake_stack_[0]);
- return true;
- }
-
- uintptr_t GetStackBaseAddress() const override {
- return reinterpret_cast<uintptr_t>(&fake_stack_[0] + fake_stack_.size());
- }
-
- bool CanCopyStack(uintptr_t stack_pointer) override { return true; }
- std::vector<uintptr_t*> GetRegistersToRewrite(
- RegisterContext* thread_context) override {
- return {&RegisterContextFramePointer(thread_context)};
+ return true;
}
private:
// Must be a reference to retain the underlying allocation from the vector
// passed to the constructor.
const std::vector<uintptr_t>& fake_stack_;
- RegisterContext* thread_context_;
};
// Trivial unwinder implementation for testing.
@@ -230,13 +202,19 @@ class FakeTestUnwinder : public Unwinder {
} // namespace
-TEST(StackSamplerImplTest, CopyStack) {
+// TODO(crbug.com/1001923): Fails on Linux MSan.
+#if defined(OS_LINUX)
+#define MAYBE_CopyStack DISABLED_MAYBE_CopyStack
+#else
+#define MAYBE_CopyStack CopyStack
+#endif
+TEST(StackSamplerImplTest, MAYBE_CopyStack) {
ModuleCache module_cache;
const std::vector<uintptr_t> stack = {0, 1, 2, 3, 4};
InjectModuleForContextInstructionPointer(stack, &module_cache);
std::vector<uintptr_t> stack_copy;
StackSamplerImpl stack_sampler_impl(
- std::make_unique<TestThreadDelegate>(stack),
+ std::make_unique<TestStackCopier>(stack),
std::make_unique<TestUnwinder>(stack.size(), &stack_copy), &module_cache);
std::unique_ptr<StackBuffer> stack_buffer =
@@ -247,75 +225,6 @@ TEST(StackSamplerImplTest, CopyStack) {
EXPECT_EQ(stack, stack_copy);
}
-TEST(StackSamplerImplTest, CopyStackBufferTooSmall) {
- ModuleCache module_cache;
- std::vector<uintptr_t> stack = {0, 1, 2, 3, 4};
- InjectModuleForContextInstructionPointer(stack, &module_cache);
- std::vector<uintptr_t> stack_copy;
- StackSamplerImpl stack_sampler_impl(
- std::make_unique<TestThreadDelegate>(stack),
- std::make_unique<TestUnwinder>(stack.size(), &stack_copy), &module_cache);
-
- std::unique_ptr<StackBuffer> stack_buffer =
- std::make_unique<StackBuffer>((stack.size() - 1) * sizeof(uintptr_t));
- // Make the buffer different than the input stack.
- stack_buffer->buffer()[0] = 100;
- TestProfileBuilder profile_builder(&module_cache);
-
- stack_sampler_impl.RecordStackFrames(stack_buffer.get(), &profile_builder);
-
- // Use the buffer not being overwritten as a proxy for the unwind being
- // aborted.
- EXPECT_NE(stack, stack_copy);
-}
-
-TEST(StackSamplerImplTest, CopyStackAndRewritePointers) {
- ModuleCache module_cache;
- // Allocate space for the stack, then make its elements point to themselves.
- std::vector<uintptr_t> stack(2);
- stack[0] = reinterpret_cast<uintptr_t>(&stack[0]);
- stack[1] = reinterpret_cast<uintptr_t>(&stack[1]);
- InjectModuleForContextInstructionPointer(stack, &module_cache);
- std::vector<uintptr_t> stack_copy;
- uintptr_t stack_copy_bottom;
- StackSamplerImpl stack_sampler_impl(
- std::make_unique<TestThreadDelegate>(stack),
- std::make_unique<TestUnwinder>(stack.size(), &stack_copy,
- &stack_copy_bottom),
- &module_cache);
-
- std::unique_ptr<StackBuffer> stack_buffer =
- std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
- TestProfileBuilder profile_builder(&module_cache);
-
- stack_sampler_impl.RecordStackFrames(stack_buffer.get(), &profile_builder);
-
- EXPECT_THAT(stack_copy, ElementsAre(stack_copy_bottom,
- stack_copy_bottom + sizeof(uintptr_t)));
-}
-
-TEST(StackSamplerImplTest, RewriteRegisters) {
- ModuleCache module_cache;
- std::vector<uintptr_t> stack = {0, 1, 2};
- InjectModuleForContextInstructionPointer(stack, &module_cache);
- uintptr_t stack_copy_bottom;
- RegisterContext thread_context;
- RegisterContextFramePointer(&thread_context) =
- reinterpret_cast<uintptr_t>(&stack[1]);
- StackSamplerImpl stack_sampler_impl(
- std::make_unique<TestThreadDelegate>(stack, &thread_context),
- std::make_unique<TestUnwinder>(stack.size(), nullptr, &stack_copy_bottom),
- &module_cache);
-
- std::unique_ptr<StackBuffer> stack_buffer =
- std::make_unique<StackBuffer>(stack.size() * sizeof(uintptr_t));
- TestProfileBuilder profile_builder(&module_cache);
- stack_sampler_impl.RecordStackFrames(stack_buffer.get(), &profile_builder);
-
- EXPECT_EQ(stack_copy_bottom + sizeof(uintptr_t),
- RegisterContextFramePointer(&thread_context));
-}
-
TEST(StackSamplerImplTest, WalkStack_Completed) {
ModuleCache module_cache;
RegisterContext thread_context;
diff --git a/chromium/base/profiler/stack_sampler_mac.cc b/chromium/base/profiler/stack_sampler_mac.cc
index 54ca505e216..1ab0eaf7a7f 100644
--- a/chromium/base/profiler/stack_sampler_mac.cc
+++ b/chromium/base/profiler/stack_sampler_mac.cc
@@ -5,8 +5,9 @@
#include "base/profiler/stack_sampler.h"
#include "base/profiler/native_unwinder_mac.h"
+#include "base/profiler/stack_copier_suspend.h"
#include "base/profiler/stack_sampler_impl.h"
-#include "base/profiler/thread_delegate_mac.h"
+#include "base/profiler/suspendable_thread_delegate_mac.h"
namespace base {
@@ -16,7 +17,8 @@ std::unique_ptr<StackSampler> StackSampler::Create(
ModuleCache* module_cache,
StackSamplerTestDelegate* test_delegate) {
return std::make_unique<StackSamplerImpl>(
- std::make_unique<ThreadDelegateMac>(thread_id),
+ std::make_unique<StackCopierSuspend>(
+ std::make_unique<SuspendableThreadDelegateMac>(thread_id)),
std::make_unique<NativeUnwinderMac>(module_cache), module_cache,
test_delegate);
}
diff --git a/chromium/base/profiler/stack_sampler_win.cc b/chromium/base/profiler/stack_sampler_win.cc
index fb09fb79709..83307609aa1 100644
--- a/chromium/base/profiler/stack_sampler_win.cc
+++ b/chromium/base/profiler/stack_sampler_win.cc
@@ -5,8 +5,9 @@
#include "base/profiler/stack_sampler.h"
#include "base/profiler/native_unwinder_win.h"
+#include "base/profiler/stack_copier_suspend.h"
#include "base/profiler/stack_sampler_impl.h"
-#include "base/profiler/thread_delegate_win.h"
+#include "base/profiler/suspendable_thread_delegate_win.h"
#include "build/build_config.h"
namespace base {
@@ -18,7 +19,8 @@ std::unique_ptr<StackSampler> StackSampler::Create(
StackSamplerTestDelegate* test_delegate) {
#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)
return std::make_unique<StackSamplerImpl>(
- std::make_unique<ThreadDelegateWin>(thread_id),
+ std::make_unique<StackCopierSuspend>(
+ std::make_unique<SuspendableThreadDelegateWin>(thread_id)),
std::make_unique<NativeUnwinderWin>(), module_cache, test_delegate);
#else
return nullptr;
diff --git a/chromium/base/profiler/suspendable_thread_delegate.h b/chromium/base/profiler/suspendable_thread_delegate.h
new file mode 100644
index 00000000000..b5dfcd7bf12
--- /dev/null
+++ b/chromium/base/profiler/suspendable_thread_delegate.h
@@ -0,0 +1,59 @@
+// Copyright 2019 The Chromium 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_PROFILER_SUSPENDABLE_THREAD_DELEGATE_H_
+#define BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_H_
+
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/profiler/register_context.h"
+#include "base/profiler/thread_delegate.h"
+
+namespace base {
+
+// Platform-specific thread and stack manipulation delegate, for use by the
+// platform-independent stack copying/walking implementation in
+// StackSamplerImpl for suspension-based stack copying.
+//
+// IMPORTANT NOTE: Most methods in this interface are invoked while the target
+// thread is suspended so must not do any allocation from the heap, including
+// indirectly via use of DCHECK/CHECK or other logging statements. Otherwise the
+// implementation can deadlock on heap locks acquired by the target thread
+// before it was suspended. These functions are commented with "NO HEAP
+// ALLOCATIONS".
+class BASE_EXPORT SuspendableThreadDelegate : public ThreadDelegate {
+ public:
+ // Implementations of this interface should suspend the thread for the
+ // object's lifetime. NO HEAP ALLOCATIONS between the time the thread is
+ // suspended and resumed.
+ class BASE_EXPORT ScopedSuspendThread {
+ public:
+ ScopedSuspendThread() = default;
+ virtual ~ScopedSuspendThread() = default;
+
+ ScopedSuspendThread(const ScopedSuspendThread&) = delete;
+ ScopedSuspendThread& operator=(const ScopedSuspendThread&) = delete;
+
+ virtual bool WasSuccessful() const = 0;
+ };
+
+ SuspendableThreadDelegate() = default;
+
+ // Creates an object that holds the thread suspended for its lifetime.
+ virtual std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() = 0;
+
+ // Gets the register context for the thread.
+ // NO HEAP ALLOCATIONS.
+ virtual bool GetThreadContext(RegisterContext* thread_context) = 0;
+
+ // Returns true if the thread's stack can be copied, where the bottom address
+ // of the thread is at |stack_pointer|.
+ // NO HEAP ALLOCATIONS.
+ virtual bool CanCopyStack(uintptr_t stack_pointer) = 0;
+};
+
+} // namespace base
+
+#endif // BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_H_
diff --git a/chromium/base/profiler/thread_delegate_mac.cc b/chromium/base/profiler/suspendable_thread_delegate_mac.cc
index 7632c4efed5..81323969d74 100644
--- a/chromium/base/profiler/thread_delegate_mac.cc
+++ b/chromium/base/profiler/suspendable_thread_delegate_mac.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/profiler/thread_delegate_mac.h"
+#include "base/profiler/suspendable_thread_delegate_mac.h"
#include <mach/mach.h>
#include <mach/thread_act.h>
@@ -36,7 +36,7 @@ bool GetThreadState(thread_act_t target_thread, x86_thread_state64_t* state) {
// ScopedSuspendThread --------------------------------------------------------
// NO HEAP ALLOCATIONS after thread_suspend.
-ThreadDelegateMac::ScopedSuspendThread::ScopedSuspendThread(
+SuspendableThreadDelegateMac::ScopedSuspendThread::ScopedSuspendThread(
mach_port_t thread_port)
: thread_port_(thread_suspend(thread_port) == KERN_SUCCESS
? thread_port
@@ -44,7 +44,7 @@ ThreadDelegateMac::ScopedSuspendThread::ScopedSuspendThread(
// NO HEAP ALLOCATIONS. The MACH_CHECK is OK because it provides a more noisy
// failure mode than deadlocking.
-ThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() {
+SuspendableThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() {
if (!WasSuccessful())
return;
@@ -52,13 +52,14 @@ ThreadDelegateMac::ScopedSuspendThread::~ScopedSuspendThread() {
MACH_CHECK(kr == KERN_SUCCESS, kr) << "thread_resume";
}
-bool ThreadDelegateMac::ScopedSuspendThread::WasSuccessful() const {
+bool SuspendableThreadDelegateMac::ScopedSuspendThread::WasSuccessful() const {
return thread_port_ != MACH_PORT_NULL;
}
-// ThreadDelegateMac ----------------------------------------------------------
+// SuspendableThreadDelegateMac -----------------------------------------------
-ThreadDelegateMac::ThreadDelegateMac(mach_port_t thread_port)
+SuspendableThreadDelegateMac::SuspendableThreadDelegateMac(
+ mach_port_t thread_port)
: thread_port_(thread_port),
thread_stack_base_address_(reinterpret_cast<uintptr_t>(
pthread_get_stackaddr_np(pthread_from_mach_thread_np(thread_port)))) {
@@ -70,29 +71,30 @@ ThreadDelegateMac::ThreadDelegateMac(mach_port_t thread_port)
GetThreadState(thread_port_, &thread_state);
}
-ThreadDelegateMac::~ThreadDelegateMac() = default;
+SuspendableThreadDelegateMac::~SuspendableThreadDelegateMac() = default;
-std::unique_ptr<ThreadDelegate::ScopedSuspendThread>
-ThreadDelegateMac::CreateScopedSuspendThread() {
+std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread>
+SuspendableThreadDelegateMac::CreateScopedSuspendThread() {
return std::make_unique<ScopedSuspendThread>(thread_port_);
}
// NO HEAP ALLOCATIONS.
-bool ThreadDelegateMac::GetThreadContext(x86_thread_state64_t* thread_context) {
+bool SuspendableThreadDelegateMac::GetThreadContext(
+ x86_thread_state64_t* thread_context) {
return GetThreadState(thread_port_, thread_context);
}
// NO HEAP ALLOCATIONS.
-uintptr_t ThreadDelegateMac::GetStackBaseAddress() const {
+uintptr_t SuspendableThreadDelegateMac::GetStackBaseAddress() const {
return thread_stack_base_address_;
}
// NO HEAP ALLOCATIONS.
-bool ThreadDelegateMac::CanCopyStack(uintptr_t stack_pointer) {
+bool SuspendableThreadDelegateMac::CanCopyStack(uintptr_t stack_pointer) {
return true;
}
-std::vector<uintptr_t*> ThreadDelegateMac::GetRegistersToRewrite(
+std::vector<uintptr_t*> SuspendableThreadDelegateMac::GetRegistersToRewrite(
x86_thread_state64_t* thread_context) {
return {
&AsUintPtr(&thread_context->__rbx), &AsUintPtr(&thread_context->__rbp),
diff --git a/chromium/base/profiler/thread_delegate_mac.h b/chromium/base/profiler/suspendable_thread_delegate_mac.h
index 1ff2497fe1e..de3f30668ed 100644
--- a/chromium/base/profiler/thread_delegate_mac.h
+++ b/chromium/base/profiler/suspendable_thread_delegate_mac.h
@@ -2,14 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef BASE_PROFILER_THREAD_DELEGATE_MAC_H_
-#define BASE_PROFILER_THREAD_DELEGATE_MAC_H_
+#ifndef BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_MAC_H_
+#define BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_MAC_H_
#include <mach/mach.h>
#include "base/base_export.h"
#include "base/profiler/native_unwinder_mac.h"
-#include "base/profiler/thread_delegate.h"
+#include "base/profiler/suspendable_thread_delegate.h"
#include "base/sampling_heap_profiler/module_cache.h"
#include "base/threading/platform_thread.h"
@@ -17,9 +17,11 @@ namespace base {
// Platform- and thread-specific implementation in support of stack sampling on
// Mac.
-class BASE_EXPORT ThreadDelegateMac : public ThreadDelegate {
+class BASE_EXPORT SuspendableThreadDelegateMac
+ : public SuspendableThreadDelegate {
public:
- class ScopedSuspendThread : public ThreadDelegate::ScopedSuspendThread {
+ class ScopedSuspendThread
+ : public SuspendableThreadDelegate::ScopedSuspendThread {
public:
explicit ScopedSuspendThread(mach_port_t thread_port);
~ScopedSuspendThread() override;
@@ -33,14 +35,15 @@ class BASE_EXPORT ThreadDelegateMac : public ThreadDelegate {
mach_port_t thread_port_;
};
- ThreadDelegateMac(mach_port_t thread_port);
- ~ThreadDelegateMac() override;
+ SuspendableThreadDelegateMac(mach_port_t thread_port);
+ ~SuspendableThreadDelegateMac() override;
- ThreadDelegateMac(const ThreadDelegateMac&) = delete;
- ThreadDelegateMac& operator=(const ThreadDelegateMac&) = delete;
+ SuspendableThreadDelegateMac(const SuspendableThreadDelegateMac&) = delete;
+ SuspendableThreadDelegateMac& operator=(const SuspendableThreadDelegateMac&) =
+ delete;
- // ThreadDelegate
- std::unique_ptr<ThreadDelegate::ScopedSuspendThread>
+ // SuspendableThreadDelegate
+ std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread>
CreateScopedSuspendThread() override;
bool GetThreadContext(x86_thread_state64_t* thread_context) override;
uintptr_t GetStackBaseAddress() const override;
@@ -58,4 +61,4 @@ class BASE_EXPORT ThreadDelegateMac : public ThreadDelegate {
} // namespace base
-#endif // BASE_PROFILER_THREAD_DELEGATE_MAC_H_
+#endif // BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_MAC_H_
diff --git a/chromium/base/profiler/thread_delegate_win.cc b/chromium/base/profiler/suspendable_thread_delegate_win.cc
index 71ba5aaee47..d7369e50b68 100644
--- a/chromium/base/profiler/thread_delegate_win.cc
+++ b/chromium/base/profiler/suspendable_thread_delegate_win.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/profiler/thread_delegate_win.h"
+#include "base/profiler/suspendable_thread_delegate_win.h"
#include <windows.h>
#include <winternl.h>
@@ -136,7 +136,7 @@ ScopedDisablePriorityBoost::~ScopedDisablePriorityBoost() {
// ScopedSuspendThread --------------------------------------------------------
// NO HEAP ALLOCATIONS after ::SuspendThread.
-ThreadDelegateWin::ScopedSuspendThread::ScopedSuspendThread(
+SuspendableThreadDelegateWin::ScopedSuspendThread::ScopedSuspendThread(
HANDLE thread_handle)
: thread_handle_(thread_handle),
was_successful_(::SuspendThread(thread_handle) !=
@@ -144,7 +144,7 @@ ThreadDelegateWin::ScopedSuspendThread::ScopedSuspendThread(
// NO HEAP ALLOCATIONS. The CHECK is OK because it provides a more noisy failure
// mode than deadlocking.
-ThreadDelegateWin::ScopedSuspendThread::~ScopedSuspendThread() {
+SuspendableThreadDelegateWin::ScopedSuspendThread::~ScopedSuspendThread() {
if (!was_successful_)
return;
@@ -164,46 +164,48 @@ ThreadDelegateWin::ScopedSuspendThread::~ScopedSuspendThread() {
CHECK(resume_thread_succeeded) << "ResumeThread failed: " << GetLastError();
}
-bool ThreadDelegateWin::ScopedSuspendThread::WasSuccessful() const {
+bool SuspendableThreadDelegateWin::ScopedSuspendThread::WasSuccessful() const {
return was_successful_;
}
-// ThreadDelegateWin ----------------------------------------------------------
+// SuspendableThreadDelegateWin
+// ----------------------------------------------------------
-ThreadDelegateWin::ThreadDelegateWin(PlatformThreadId thread_id)
+SuspendableThreadDelegateWin::SuspendableThreadDelegateWin(
+ PlatformThreadId thread_id)
: thread_handle_(GetThreadHandle(thread_id)),
thread_stack_base_address_(reinterpret_cast<uintptr_t>(
GetThreadEnvironmentBlock(thread_handle_.Get())->Tib.StackBase)) {}
-ThreadDelegateWin::~ThreadDelegateWin() = default;
+SuspendableThreadDelegateWin::~SuspendableThreadDelegateWin() = default;
-std::unique_ptr<ThreadDelegate::ScopedSuspendThread>
-ThreadDelegateWin::CreateScopedSuspendThread() {
+std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread>
+SuspendableThreadDelegateWin::CreateScopedSuspendThread() {
return std::make_unique<ScopedSuspendThread>(thread_handle_.Get());
}
// NO HEAP ALLOCATIONS.
-bool ThreadDelegateWin::GetThreadContext(CONTEXT* thread_context) {
+bool SuspendableThreadDelegateWin::GetThreadContext(CONTEXT* thread_context) {
*thread_context = {0};
thread_context->ContextFlags = CONTEXT_FULL;
return ::GetThreadContext(thread_handle_.Get(), thread_context) != 0;
}
// NO HEAP ALLOCATIONS.
-uintptr_t ThreadDelegateWin::GetStackBaseAddress() const {
+uintptr_t SuspendableThreadDelegateWin::GetStackBaseAddress() const {
return thread_stack_base_address_;
}
// Tests whether |stack_pointer| points to a location in the guard page. NO HEAP
// ALLOCATIONS.
-bool ThreadDelegateWin::CanCopyStack(uintptr_t stack_pointer) {
+bool SuspendableThreadDelegateWin::CanCopyStack(uintptr_t stack_pointer) {
// Dereferencing a pointer in the guard page in a thread that doesn't own the
// stack results in a STATUS_GUARD_PAGE_VIOLATION exception and a crash. This
// occurs very rarely, but reliably over the population.
return !PointsToGuardPage(stack_pointer);
}
-std::vector<uintptr_t*> ThreadDelegateWin::GetRegistersToRewrite(
+std::vector<uintptr_t*> SuspendableThreadDelegateWin::GetRegistersToRewrite(
CONTEXT* thread_context) {
// Return the set of non-volatile registers.
return {
@@ -215,7 +217,8 @@ std::vector<uintptr_t*> ThreadDelegateWin::GetRegistersToRewrite(
&thread_context->X19, &thread_context->X20, &thread_context->X21,
&thread_context->X22, &thread_context->X23, &thread_context->X24,
&thread_context->X25, &thread_context->X26, &thread_context->X27,
- &thread_context->X28, &thread_context->Fp, &thread_context->Lr
+ &thread_context->X28, &thread_context->Fp, &thread_context->Lr,
+ &thread_context->Sp
#endif
};
}
diff --git a/chromium/base/profiler/thread_delegate_win.h b/chromium/base/profiler/suspendable_thread_delegate_win.h
index 9e1d781e67b..2452dbe47db 100644
--- a/chromium/base/profiler/thread_delegate_win.h
+++ b/chromium/base/profiler/suspendable_thread_delegate_win.h
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef BASE_PROFILER_THREAD_DELEGATE_WIN_H_
-#define BASE_PROFILER_THREAD_DELEGATE_WIN_H_
+#ifndef BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_WIN_H_
+#define BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_WIN_H_
#include <windows.h>
#include "base/base_export.h"
-#include "base/profiler/thread_delegate.h"
+#include "base/profiler/suspendable_thread_delegate.h"
#include "base/threading/platform_thread.h"
#include "base/win/scoped_handle.h"
@@ -16,9 +16,11 @@ namespace base {
// Platform- and thread-specific implementation in support of stack sampling on
// Windows.
-class BASE_EXPORT ThreadDelegateWin : public ThreadDelegate {
+class BASE_EXPORT SuspendableThreadDelegateWin
+ : public SuspendableThreadDelegate {
public:
- class ScopedSuspendThread : public ThreadDelegate::ScopedSuspendThread {
+ class ScopedSuspendThread
+ : public SuspendableThreadDelegate::ScopedSuspendThread {
public:
explicit ScopedSuspendThread(HANDLE thread_handle);
~ScopedSuspendThread() override;
@@ -32,14 +34,15 @@ class BASE_EXPORT ThreadDelegateWin : public ThreadDelegate {
DISALLOW_COPY_AND_ASSIGN(ScopedSuspendThread);
};
- explicit ThreadDelegateWin(PlatformThreadId thread_id);
- ~ThreadDelegateWin() override;
+ explicit SuspendableThreadDelegateWin(PlatformThreadId thread_id);
+ ~SuspendableThreadDelegateWin() override;
- ThreadDelegateWin(const ThreadDelegateWin&) = delete;
- ThreadDelegateWin& operator=(const ThreadDelegateWin&) = delete;
+ SuspendableThreadDelegateWin(const SuspendableThreadDelegateWin&) = delete;
+ SuspendableThreadDelegateWin& operator=(const SuspendableThreadDelegateWin&) =
+ delete;
- // ThreadDelegate
- std::unique_ptr<ThreadDelegate::ScopedSuspendThread>
+ // SuspendableThreadDelegate
+ std::unique_ptr<SuspendableThreadDelegate::ScopedSuspendThread>
CreateScopedSuspendThread() override;
bool GetThreadContext(CONTEXT* thread_context) override;
uintptr_t GetStackBaseAddress() const override;
@@ -54,4 +57,4 @@ class BASE_EXPORT ThreadDelegateWin : public ThreadDelegate {
} // namespace base
-#endif // BASE_PROFILER_THREAD_DELEGATE_WIN_H_
+#endif // BASE_PROFILER_SUSPENDABLE_THREAD_DELEGATE_WIN_H_
diff --git a/chromium/base/profiler/thread_delegate.h b/chromium/base/profiler/thread_delegate.h
index d0f0f028405..bb8b50b6354 100644
--- a/chromium/base/profiler/thread_delegate.h
+++ b/chromium/base/profiler/thread_delegate.h
@@ -8,58 +8,25 @@
#include <vector>
#include "base/base_export.h"
-#include "base/profiler/frame.h"
#include "base/profiler/register_context.h"
namespace base {
// Platform-specific thread and stack manipulation delegate, for use by the
// platform-independent stack copying/walking implementation in
-// StackSamplerImpl.
-//
-// IMPORTANT NOTE: Most methods in this interface are invoked while the target
-// thread is suspended so must not do any allocation from the heap, including
-// indirectly via use of DCHECK/CHECK or other logging statements. Otherwise the
-// implementation can deadlock on heap locks acquired by the target thread
-// before it was suspended. These functions are commented with "NO HEAP
-// ALLOCATIONS".
+// StackSamplerImpl. Provides the common interface across signal- and
+// suspend-based stack copy implementations.
class BASE_EXPORT ThreadDelegate {
public:
- // Implementations of this interface should suspend the thread for the
- // object's lifetime. NO HEAP ALLOCATIONS between the time the thread is
- // suspended and resumed.
- class BASE_EXPORT ScopedSuspendThread {
- public:
- ScopedSuspendThread() = default;
- virtual ~ScopedSuspendThread() = default;
-
- ScopedSuspendThread(const ScopedSuspendThread&) = delete;
- ScopedSuspendThread& operator=(const ScopedSuspendThread&) = delete;
-
- virtual bool WasSuccessful() const = 0;
- };
-
ThreadDelegate() = default;
virtual ~ThreadDelegate() = default;
ThreadDelegate(const ThreadDelegate&) = delete;
ThreadDelegate& operator=(const ThreadDelegate&) = delete;
- // Creates an object that holds the thread suspended for its lifetime.
- virtual std::unique_ptr<ScopedSuspendThread> CreateScopedSuspendThread() = 0;
-
- // Gets the register context for the thread.
- // NO HEAP ALLOCATIONS.
- virtual bool GetThreadContext(RegisterContext* thread_context) = 0;
-
// Gets the base address of the thread's stack.
virtual uintptr_t GetStackBaseAddress() const = 0;
- // Returns true if the thread's stack can be copied, where the bottom address
- // of the thread is at |stack_pointer|.
- // NO HEAP ALLOCATIONS.
- virtual bool CanCopyStack(uintptr_t stack_pointer) = 0;
-
// Returns a list of registers that should be rewritten to point into the
// stack copy, if they originally pointed into the original stack.
// May heap allocate.
diff --git a/chromium/base/profiler/thread_delegate_android.cc b/chromium/base/profiler/thread_delegate_android.cc
index f61f51f0224..4711cf6589c 100644
--- a/chromium/base/profiler/thread_delegate_android.cc
+++ b/chromium/base/profiler/thread_delegate_android.cc
@@ -2,8 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <pthread.h>
+
#include "base/profiler/thread_delegate_android.h"
+#include "build/build_config.h"
+
// IMPORTANT NOTE: Some functions within this implementation are invoked while
// the target thread is suspended so it must not do any allocation from the
// heap, including indirectly via use of DCHECK/CHECK or other logging
@@ -13,39 +17,50 @@
namespace base {
-// ScopedSuspendThread --------------------------------------------------------
+namespace {
-bool ThreadDelegateAndroid::ScopedSuspendThread::WasSuccessful() const {
- return false;
+uintptr_t GetThreadStackBaseAddress(PlatformThreadId thread_id) {
+ pthread_attr_t attr;
+ pthread_getattr_np(thread_id, &attr);
+ void* base_address;
+ size_t size;
+ pthread_attr_getstack(&attr, &base_address, &size);
+ return reinterpret_cast<uintptr_t>(base_address);
}
-// ThreadDelegateAndroid ------------------------------------------------------
+} // namespace
-std::unique_ptr<ThreadDelegate::ScopedSuspendThread>
-ThreadDelegateAndroid::CreateScopedSuspendThread() {
- return std::make_unique<ScopedSuspendThread>();
-}
+ThreadDelegateAndroid::ThreadDelegateAndroid(PlatformThreadId thread_id)
+ : thread_stack_base_address_(GetThreadStackBaseAddress(thread_id)) {}
-// NO HEAP ALLOCATIONS.
-bool ThreadDelegateAndroid::GetThreadContext(RegisterContext* thread_context) {
- return false;
-}
-
-// NO HEAP ALLOCATIONS.
uintptr_t ThreadDelegateAndroid::GetStackBaseAddress() const {
- // It's okay for the stub to return zero here: GetStackBaseAddress() if
- // ScopedSuspendThread fails, which it always will in the stub.
- return 0;
-}
-
-// NO HEAP ALLOCATIONS.
-bool ThreadDelegateAndroid::CanCopyStack(uintptr_t stack_pointer) {
- return false;
+ return thread_stack_base_address_;
}
std::vector<uintptr_t*> ThreadDelegateAndroid::GetRegistersToRewrite(
RegisterContext* thread_context) {
+#if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
+ return {
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r0),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r1),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r2),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r3),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r4),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r5),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r6),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r7),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r8),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r9),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_r10),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_fp),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_ip),
+ reinterpret_cast<uintptr_t*>(&thread_context->arm_sp),
+ // arm_lr and arm_pc do not require rewriting because they contain
+ // addresses of executable code, not addresses in the stack.
+ };
+#else // #if defined(ARCH_CPU_ARM_FAMILY) && defined(ARCH_CPU_32_BITS)
return {};
+#endif
}
} // namespace base
diff --git a/chromium/base/profiler/thread_delegate_android.h b/chromium/base/profiler/thread_delegate_android.h
index 59ff674ac6d..96e1e3c45c1 100644
--- a/chromium/base/profiler/thread_delegate_android.h
+++ b/chromium/base/profiler/thread_delegate_android.h
@@ -7,41 +7,28 @@
#include "base/base_export.h"
#include "base/profiler/thread_delegate.h"
+#include "base/threading/platform_thread.h"
namespace base {
// Platform- and thread-specific implementation in support of stack sampling on
// Android.
//
-// TODO(charliea): Implement this class.
-// See: https://crbug.com/988574
+// TODO(https://crbug.com/988579): Implement this class.
class BASE_EXPORT ThreadDelegateAndroid : public ThreadDelegate {
public:
- class ScopedSuspendThread : public ThreadDelegate::ScopedSuspendThread {
- public:
- ScopedSuspendThread() = default;
- ~ScopedSuspendThread() override = default;
-
- ScopedSuspendThread(const ScopedSuspendThread&) = delete;
- ScopedSuspendThread& operator=(const ScopedSuspendThread&) = delete;
-
- bool WasSuccessful() const override;
- };
-
- ThreadDelegateAndroid() = default;
- ~ThreadDelegateAndroid() override = default;
+ ThreadDelegateAndroid(PlatformThreadId thread_id);
ThreadDelegateAndroid(const ThreadDelegateAndroid&) = delete;
ThreadDelegateAndroid& operator=(const ThreadDelegateAndroid&) = delete;
// ThreadDelegate
- std::unique_ptr<ThreadDelegate::ScopedSuspendThread>
- CreateScopedSuspendThread() override;
- bool GetThreadContext(RegisterContext* thread_context) override;
uintptr_t GetStackBaseAddress() const override;
- bool CanCopyStack(uintptr_t stack_pointer) override;
std::vector<uintptr_t*> GetRegistersToRewrite(
RegisterContext* thread_context) override;
+
+ private:
+ const uintptr_t thread_stack_base_address_;
};
} // namespace base
diff --git a/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc b/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc
index 6bee5c0e442..3d9d928f341 100644
--- a/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc
+++ b/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.cc
@@ -412,11 +412,11 @@ void PoissonAllocationSampler::RecordAlloc(void* address,
return;
if (UNLIKELY(!g_running.load(std::memory_order_relaxed))) {
- // Sampling is in fact disabled. Put a large negative value into
- // the accumulator. It needs to be large enough to have this code
- // not trigger frequently, and small enough to eventually start collecting
- // samples when the sampling is enabled.
- g_accumulated_bytes_tls = -static_cast<intptr_t>(kWarmupInterval);
+ // Sampling is in fact disabled. Reset the state of the sampler.
+ // We do this check off the fast-path, because it's quite a rare state when
+ // allocation hooks are installed but the sampler is not running.
+ g_sampling_interval_initialized_tls = false;
+ g_accumulated_bytes_tls = 0;
return;
}
@@ -433,6 +433,21 @@ void PoissonAllocationSampler::DoRecordAlloc(intptr_t accumulated_bytes,
return;
size_t mean_interval = g_sampling_interval.load(std::memory_order_relaxed);
+
+ if (UNLIKELY(!g_sampling_interval_initialized_tls)) {
+ g_sampling_interval_initialized_tls = true;
+ // This is the very first allocation on the thread. It always makes it
+ // passing the condition at |RecordAlloc|, because g_accumulated_bytes_tls
+ // is initialized with zero due to TLS semantics.
+ // Generate proper sampling interval instance and make sure the allocation
+ // has indeed crossed the threshold before counting it as a sample.
+ accumulated_bytes -= GetNextSampleInterval(mean_interval);
+ if (accumulated_bytes < 0) {
+ g_accumulated_bytes_tls = accumulated_bytes;
+ return;
+ }
+ }
+
size_t samples = accumulated_bytes / mean_interval;
accumulated_bytes %= mean_interval;
@@ -443,16 +458,6 @@ void PoissonAllocationSampler::DoRecordAlloc(intptr_t accumulated_bytes,
g_accumulated_bytes_tls = accumulated_bytes;
- if (UNLIKELY(!g_sampling_interval_initialized_tls)) {
- g_sampling_interval_initialized_tls = true;
- // This is the very first allocation on the thread. It always produces an
- // extra sample because g_accumulated_bytes_tls is initialized with zero
- // due to TLS semantics.
- // Make sure we don't count this extra sample.
- if (!--samples)
- return;
- }
-
if (UNLIKELY(ScopedMuteThreadSamples::IsMuted()))
return;
diff --git a/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h b/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h
index 6006bff19b6..116123940a6 100644
--- a/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h
+++ b/chromium/base/sampling_heap_profiler/poisson_allocation_sampler.h
@@ -35,10 +35,6 @@ class BASE_EXPORT PoissonAllocationSampler {
public:
enum AllocatorType : uint32_t { kMalloc, kPartitionAlloc, kBlinkGC };
- // When the sampler is just enabled it needs to see up to that amount
- // of allocation sizes before it starts recording samples.
- static constexpr size_t kWarmupInterval = 1 << 20; // 1MB.
-
class SamplesObserver {
public:
virtual ~SamplesObserver() = default;
diff --git a/chromium/base/strings/string_number_conversions.cc b/chromium/base/strings/string_number_conversions.cc
index 461502bb438..67bd3d0fd69 100644
--- a/chromium/base/strings/string_number_conversions.cc
+++ b/chromium/base/strings/string_number_conversions.cc
@@ -15,6 +15,7 @@
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_math.h"
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/third_party/double_conversion/double-conversion/double-conversion.h"
@@ -425,14 +426,15 @@ bool StringToSizeT(StringPiece16 input, size_t* output) {
return String16ToIntImpl(input, output);
}
-bool StringToDouble(const std::string& input, double* output) {
+template <typename STRING, typename CHAR>
+bool StringToDoubleImpl(STRING input, const CHAR* data, double* output) {
static NoDestructor<double_conversion::StringToDoubleConverter> converter(
double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES |
double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK,
0.0, 0, nullptr, nullptr);
int processed_characters_count;
- *output = converter->StringToDouble(input.c_str(), input.size(),
+ *output = converter->StringToDouble(data, input.size(),
&processed_characters_count);
// Cases to return false:
@@ -444,16 +446,17 @@ bool StringToDouble(const std::string& input, double* output) {
// - If the first character is a space, there was leading whitespace
return !input.empty() && *output != HUGE_VAL && *output != -HUGE_VAL &&
static_cast<size_t>(processed_characters_count) == input.size() &&
- !isspace(input[0]);
+ !IsUnicodeWhitespace(input[0]);
}
-// Note: if you need to add String16ToDouble, first ask yourself if it's
-// really necessary. If it is, probably the best implementation here is to
-// convert to 8-bit and then use the 8-bit version.
+bool StringToDouble(StringPiece input, double* output) {
+ return StringToDoubleImpl(input, input.data(), output);
+}
-// Note: if you need to add an iterator range version of StringToDouble, first
-// ask yourself if it's really necessary. If it is, probably the best
-// implementation here is to instantiate a string and use the string version.
+bool StringToDouble(StringPiece16 input, double* output) {
+ return StringToDoubleImpl(
+ input, reinterpret_cast<const uint16_t*>(input.data()), output);
+}
std::string HexEncode(const void* bytes, size_t size) {
static const char kHexChars[] = "0123456789ABCDEF";
diff --git a/chromium/base/strings/string_number_conversions.h b/chromium/base/strings/string_number_conversions.h
index 55f80a6d863..bc4edf289d6 100644
--- a/chromium/base/strings/string_number_conversions.h
+++ b/chromium/base/strings/string_number_conversions.h
@@ -98,7 +98,8 @@ BASE_EXPORT bool StringToSizeT(StringPiece16 input, size_t* output);
// If your input is locale specific, use ICU to read the number.
// WARNING: Will write to |output| even when returning false.
// Read the comments here and above StringToInt() carefully.
-BASE_EXPORT bool StringToDouble(const std::string& input, double* output);
+BASE_EXPORT bool StringToDouble(StringPiece input, double* output);
+BASE_EXPORT bool StringToDouble(StringPiece16 input, double* output);
// Hex encoding ----------------------------------------------------------------
diff --git a/chromium/base/strings/string_piece.cc b/chromium/base/strings/string_piece.cc
index 278849fac9d..19e132f1e36 100644
--- a/chromium/base/strings/string_piece.cc
+++ b/chromium/base/strings/string_piece.cc
@@ -219,8 +219,11 @@ size_t find_first_of(const StringPiece& self,
size_t find_first_of(const StringPiece16& self,
const StringPiece16& s,
size_t pos) {
+ // Use the faster std::find() if searching for a single character.
StringPiece16::const_iterator found =
- std::find_first_of(self.begin() + pos, self.end(), s.begin(), s.end());
+ s.size() == 1 ? std::find(self.begin() + pos, self.end(), s[0])
+ : std::find_first_of(self.begin() + pos, self.end(),
+ s.begin(), s.end());
if (found == self.end())
return StringPiece16::npos;
return found - self.begin();
diff --git a/chromium/base/strings/string_split.cc b/chromium/base/strings/string_split.cc
index 1456c3df940..9a99925ca8a 100644
--- a/chromium/base/strings/string_split.cc
+++ b/chromium/base/strings/string_split.cc
@@ -14,27 +14,15 @@ namespace base {
namespace {
-// PieceToOutputType converts a StringPiece as needed to a given output type,
-// which is either the same type of StringPiece (a NOP) or the corresponding
-// non-piece string type.
-//
-// The default converter is a NOP, it works when the OutputType is the
-// correct StringPiece.
-template<typename Str, typename OutputType>
-OutputType PieceToOutputType(BasicStringPiece<Str> piece) {
- return piece;
-}
-template<> // Convert StringPiece to std::string
-std::string PieceToOutputType<std::string, std::string>(StringPiece piece) {
- return piece.as_string();
-}
-template<> // Convert StringPiece16 to string16.
-string16 PieceToOutputType<string16, string16>(StringPiece16 piece) {
- return piece.as_string();
-}
-
// Returns either the ASCII or UTF-16 whitespace.
template<typename Str> BasicStringPiece<Str> WhitespaceForType();
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+template <>
+WStringPiece WhitespaceForType<std::wstring>() {
+ return kWhitespaceWide;
+}
+#endif
+
template<> StringPiece16 WhitespaceForType<string16>() {
return kWhitespaceUTF16;
}
@@ -42,37 +30,12 @@ template<> StringPiece WhitespaceForType<std::string>() {
return kWhitespaceASCII;
}
-// Optimize the single-character case to call find() on the string instead,
-// since this is the common case and can be made faster. This could have been
-// done with template specialization too, but would have been less clear.
-//
-// There is no corresponding FindFirstNotOf because StringPiece already
-// implements these different versions that do the optimized searching.
-size_t FindFirstOf(StringPiece piece, char c, size_t pos) {
- return piece.find(c, pos);
-}
-size_t FindFirstOf(StringPiece16 piece, char16 c, size_t pos) {
- return piece.find(c, pos);
-}
-size_t FindFirstOf(StringPiece piece, StringPiece one_of, size_t pos) {
- return piece.find_first_of(one_of, pos);
-}
-size_t FindFirstOf(StringPiece16 piece, StringPiece16 one_of, size_t pos) {
- return piece.find_first_of(one_of, pos);
-}
-
// General string splitter template. Can take 8- or 16-bit input, can produce
-// the corresponding string or StringPiece output, and can take single- or
-// multiple-character delimiters.
-//
-// DelimiterType is either a character (Str::value_type) or a string piece of
-// multiple characters (BasicStringPiece<Str>). StringPiece has a version of
-// find for both of these cases, and the single-character version is the most
-// common and can be implemented faster, which is why this is a template.
-template<typename Str, typename OutputStringType, typename DelimiterType>
+// the corresponding string or StringPiece output.
+template <typename OutputStringType, typename Str>
static std::vector<OutputStringType> SplitStringT(
BasicStringPiece<Str> str,
- DelimiterType delimiter,
+ BasicStringPiece<Str> delimiter,
WhitespaceHandling whitespace,
SplitResult result_type) {
std::vector<OutputStringType> result;
@@ -81,7 +44,7 @@ static std::vector<OutputStringType> SplitStringT(
size_t start = 0;
while (start != Str::npos) {
- size_t end = FindFirstOf(str, delimiter, start);
+ size_t end = str.find_first_of(delimiter, start);
BasicStringPiece<Str> piece;
if (end == Str::npos) {
@@ -96,7 +59,7 @@ static std::vector<OutputStringType> SplitStringT(
piece = TrimString(piece, WhitespaceForType<Str>(), TRIM_ALL);
if (result_type == SPLIT_WANT_ALL || !piece.empty())
- result.push_back(PieceToOutputType<Str, OutputStringType>(piece));
+ result.emplace_back(piece);
}
return result;
}
@@ -130,16 +93,16 @@ bool AppendStringKeyValue(StringPiece input,
return true;
}
-template <typename Str, typename OutputStringType>
-void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
- BasicStringPiece<Str> delimiter,
- WhitespaceHandling whitespace,
- SplitResult result_type,
- std::vector<OutputStringType>* result) {
+template <typename OutputStringType, typename Str>
+std::vector<OutputStringType> SplitStringUsingSubstrT(
+ BasicStringPiece<Str> input,
+ BasicStringPiece<Str> delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
using Piece = BasicStringPiece<Str>;
using size_type = typename Piece::size_type;
- result->clear();
+ std::vector<OutputStringType> result;
for (size_type begin_index = 0, end_index = 0; end_index != Piece::npos;
begin_index = end_index + delimiter.size()) {
end_index = input.find(delimiter, begin_index);
@@ -151,8 +114,10 @@ void SplitStringUsingSubstrT(BasicStringPiece<Str> input,
term = TrimString(term, WhitespaceForType<Str>(), TRIM_ALL);
if (result_type == SPLIT_WANT_ALL || !term.empty())
- result->push_back(PieceToOutputType<Str, OutputStringType>(term));
+ result.emplace_back(term);
}
+
+ return result;
}
} // namespace
@@ -161,48 +126,29 @@ std::vector<std::string> SplitString(StringPiece input,
StringPiece separators,
WhitespaceHandling whitespace,
SplitResult result_type) {
- if (separators.size() == 1) {
- return SplitStringT<std::string, std::string, char>(
- input, separators[0], whitespace, result_type);
- }
- return SplitStringT<std::string, std::string, StringPiece>(
- input, separators, whitespace, result_type);
+ return SplitStringT<std::string>(input, separators, whitespace, result_type);
}
std::vector<string16> SplitString(StringPiece16 input,
StringPiece16 separators,
WhitespaceHandling whitespace,
SplitResult result_type) {
- if (separators.size() == 1) {
- return SplitStringT<string16, string16, char16>(
- input, separators[0], whitespace, result_type);
- }
- return SplitStringT<string16, string16, StringPiece16>(
- input, separators, whitespace, result_type);
+ return SplitStringT<string16>(input, separators, whitespace, result_type);
}
std::vector<StringPiece> SplitStringPiece(StringPiece input,
StringPiece separators,
WhitespaceHandling whitespace,
SplitResult result_type) {
- if (separators.size() == 1) {
- return SplitStringT<std::string, StringPiece, char>(
- input, separators[0], whitespace, result_type);
- }
- return SplitStringT<std::string, StringPiece, StringPiece>(
- input, separators, whitespace, result_type);
+ return SplitStringT<StringPiece>(input, separators, whitespace, result_type);
}
std::vector<StringPiece16> SplitStringPiece(StringPiece16 input,
StringPiece16 separators,
WhitespaceHandling whitespace,
SplitResult result_type) {
- if (separators.size() == 1) {
- return SplitStringT<string16, StringPiece16, char16>(
- input, separators[0], whitespace, result_type);
- }
- return SplitStringT<string16, StringPiece16, StringPiece16>(
- input, separators, whitespace, result_type);
+ return SplitStringT<StringPiece16>(input, separators, whitespace,
+ result_type);
}
bool SplitStringIntoKeyValuePairs(StringPiece input,
@@ -240,18 +186,16 @@ std::vector<string16> SplitStringUsingSubstr(StringPiece16 input,
StringPiece16 delimiter,
WhitespaceHandling whitespace,
SplitResult result_type) {
- std::vector<string16> result;
- SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
- return result;
+ return SplitStringUsingSubstrT<string16>(input, delimiter, whitespace,
+ result_type);
}
std::vector<std::string> SplitStringUsingSubstr(StringPiece input,
StringPiece delimiter,
WhitespaceHandling whitespace,
SplitResult result_type) {
- std::vector<std::string> result;
- SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
- return result;
+ return SplitStringUsingSubstrT<std::string>(input, delimiter, whitespace,
+ result_type);
}
std::vector<StringPiece16> SplitStringPieceUsingSubstr(
@@ -260,8 +204,8 @@ std::vector<StringPiece16> SplitStringPieceUsingSubstr(
WhitespaceHandling whitespace,
SplitResult result_type) {
std::vector<StringPiece16> result;
- SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
- return result;
+ return SplitStringUsingSubstrT<StringPiece16>(input, delimiter, whitespace,
+ result_type);
}
std::vector<StringPiece> SplitStringPieceUsingSubstr(
@@ -269,9 +213,41 @@ std::vector<StringPiece> SplitStringPieceUsingSubstr(
StringPiece delimiter,
WhitespaceHandling whitespace,
SplitResult result_type) {
- std::vector<StringPiece> result;
- SplitStringUsingSubstrT(input, delimiter, whitespace, result_type, &result);
- return result;
+ return SplitStringUsingSubstrT<StringPiece>(input, delimiter, whitespace,
+ result_type);
+}
+
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+std::vector<std::wstring> SplitString(WStringPiece input,
+ WStringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringT<std::wstring>(input, separators, whitespace, result_type);
+}
+
+std::vector<WStringPiece> SplitStringPiece(WStringPiece input,
+ WStringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringT<WStringPiece>(input, separators, whitespace, result_type);
+}
+
+std::vector<std::wstring> SplitStringUsingSubstr(WStringPiece input,
+ WStringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringUsingSubstrT<std::wstring>(input, delimiter, whitespace,
+ result_type);
+}
+
+std::vector<WStringPiece> SplitStringPieceUsingSubstr(
+ WStringPiece input,
+ WStringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type) {
+ return SplitStringUsingSubstrT<WStringPiece>(input, delimiter, whitespace,
+ result_type);
}
+#endif
} // namespace base
diff --git a/chromium/base/strings/string_split.h b/chromium/base/strings/string_split.h
index 406dd2abe79..d3181a78f4e 100644
--- a/chromium/base/strings/string_split.h
+++ b/chromium/base/strings/string_split.h
@@ -12,6 +12,7 @@
#include "base/base_export.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
+#include "build/build_config.h"
namespace base {
@@ -132,6 +133,31 @@ BASE_EXPORT std::vector<StringPiece> SplitStringPieceUsingSubstr(
WhitespaceHandling whitespace,
SplitResult result_type);
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+BASE_EXPORT std::vector<std::wstring> SplitString(WStringPiece input,
+ WStringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+BASE_EXPORT std::vector<WStringPiece> SplitStringPiece(
+ WStringPiece input,
+ WStringPiece separators,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+BASE_EXPORT std::vector<std::wstring> SplitStringUsingSubstr(
+ WStringPiece input,
+ WStringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+
+BASE_EXPORT std::vector<WStringPiece> SplitStringPieceUsingSubstr(
+ WStringPiece input,
+ WStringPiece delimiter,
+ WhitespaceHandling whitespace,
+ SplitResult result_type);
+#endif
+
} // namespace base
#endif // BASE_STRINGS_STRING_SPLIT_H_
diff --git a/chromium/base/strings/string_util.cc b/chromium/base/strings/string_util.cc
index a8fcb8d2e20..03ea3972a86 100644
--- a/chromium/base/strings/string_util.cc
+++ b/chromium/base/strings/string_util.cc
@@ -262,8 +262,8 @@ bool RemoveChars(const std::string& input,
return ReplaceCharsT(input, remove_chars, StringPiece(), output);
}
-template<typename Str>
-TrimPositions TrimStringT(const Str& input,
+template <typename Str>
+TrimPositions TrimStringT(BasicStringPiece<Str> input,
BasicStringPiece<Str> trim_chars,
TrimPositions positions,
Str* output) {
@@ -271,40 +271,40 @@ TrimPositions TrimStringT(const Str& input,
// a StringPiece version of input to be able to call find* on it with the
// StringPiece version of trim_chars (normally the trim_chars will be a
// constant so avoid making a copy).
- BasicStringPiece<Str> input_piece(input);
const size_t last_char = input.length() - 1;
- const size_t first_good_char = (positions & TRIM_LEADING) ?
- input_piece.find_first_not_of(trim_chars) : 0;
- const size_t last_good_char = (positions & TRIM_TRAILING) ?
- input_piece.find_last_not_of(trim_chars) : last_char;
+ const size_t first_good_char =
+ (positions & TRIM_LEADING) ? input.find_first_not_of(trim_chars) : 0;
+ const size_t last_good_char = (positions & TRIM_TRAILING)
+ ? input.find_last_not_of(trim_chars)
+ : last_char;
// When the string was all trimmed, report that we stripped off characters
// from whichever position the caller was interested in. For empty input, we
// stripped no characters, but we still need to clear |output|.
- if (input.empty() ||
- (first_good_char == Str::npos) || (last_good_char == Str::npos)) {
+ if (input.empty() || first_good_char == Str::npos ||
+ last_good_char == Str::npos) {
bool input_was_empty = input.empty(); // in case output == &input
output->clear();
return input_was_empty ? TRIM_NONE : positions;
}
// Trim.
- *output =
- input.substr(first_good_char, last_good_char - first_good_char + 1);
+ output->assign(input.data() + first_good_char,
+ last_good_char - first_good_char + 1);
// Return where we trimmed from.
return static_cast<TrimPositions>(
- ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
- ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
+ (first_good_char == 0 ? TRIM_NONE : TRIM_LEADING) |
+ (last_good_char == last_char ? TRIM_NONE : TRIM_TRAILING));
}
-bool TrimString(const string16& input,
+bool TrimString(StringPiece16 input,
StringPiece16 trim_chars,
string16* output) {
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
}
-bool TrimString(const std::string& input,
+bool TrimString(StringPiece input,
StringPiece trim_chars,
std::string* output) {
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
@@ -370,7 +370,7 @@ void TruncateUTF8ToByteSize(const std::string& input,
output->clear();
}
-TrimPositions TrimWhitespace(const string16& input,
+TrimPositions TrimWhitespace(StringPiece16 input,
TrimPositions positions,
string16* output) {
return TrimStringT(input, StringPiece16(kWhitespaceUTF16), positions, output);
@@ -381,7 +381,7 @@ StringPiece16 TrimWhitespace(StringPiece16 input,
return TrimStringPieceT(input, StringPiece16(kWhitespaceUTF16), positions);
}
-TrimPositions TrimWhitespaceASCII(const std::string& input,
+TrimPositions TrimWhitespaceASCII(StringPiece input,
TrimPositions positions,
std::string* output) {
return TrimStringT(input, StringPiece(kWhitespaceASCII), positions, output);
@@ -913,7 +913,7 @@ void ReplaceSubstringsAfterOffset(std::string* str,
template <class string_type>
inline typename string_type::value_type* WriteIntoT(string_type* str,
size_t length_with_null) {
- DCHECK_GT(length_with_null, 1u);
+ DCHECK_GE(length_with_null, 1u);
str->reserve(length_with_null);
str->resize(length_with_null - 1);
return &((*str)[0]);
@@ -1085,6 +1085,32 @@ string16 ReplaceStringPlaceholders(const string16& format_string,
return result;
}
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+
+TrimPositions TrimWhitespace(WStringPiece input,
+ TrimPositions positions,
+ std::wstring* output) {
+ return TrimStringT(input, WStringPiece(kWhitespaceWide), positions, output);
+}
+
+WStringPiece TrimWhitespace(WStringPiece input, TrimPositions positions) {
+ return TrimStringPieceT(input, WStringPiece(kWhitespaceWide), positions);
+}
+
+bool TrimString(WStringPiece input,
+ WStringPiece trim_chars,
+ std::wstring* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+WStringPiece TrimString(WStringPiece input,
+ WStringPiece trim_chars,
+ TrimPositions positions) {
+ return TrimStringPieceT(input, trim_chars, positions);
+}
+
+#endif
+
// The following code is compatible with the OpenBSD lcpy interface. See:
// http://www.gratisoft.us/todd/papers/strlcpy.html
// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c
diff --git a/chromium/base/strings/string_util.h b/chromium/base/strings/string_util.h
index 1398a9ba542..ec0bee3b824 100644
--- a/chromium/base/strings/string_util.h
+++ b/chromium/base/strings/string_util.h
@@ -204,10 +204,10 @@ enum TrimPositions {
//
// It is safe to use the same variable for both |input| and |output| (this is
// the normal usage to trim in-place).
-BASE_EXPORT bool TrimString(const string16& input,
+BASE_EXPORT bool TrimString(StringPiece16 input,
StringPiece16 trim_chars,
string16* output);
-BASE_EXPORT bool TrimString(const std::string& input,
+BASE_EXPORT bool TrimString(StringPiece input,
StringPiece trim_chars,
std::string* output);
@@ -269,6 +269,24 @@ inline const char16* as_u16cstr(const wchar_t* str) {
inline const char16* as_u16cstr(WStringPiece str) {
return reinterpret_cast<const char16*>(str.data());
}
+
+// Utility functions to convert between base::WStringPiece and
+// base::StringPiece16.
+inline WStringPiece AsWStringPiece(StringPiece16 str) {
+ return WStringPiece(as_wcstr(str.data()), str.size());
+}
+
+inline StringPiece16 AsStringPiece16(WStringPiece str) {
+ return StringPiece16(as_u16cstr(str.data()), str.size());
+}
+
+inline std::wstring AsWString(StringPiece16 str) {
+ return std::wstring(as_wcstr(str.data()), str.size());
+}
+
+inline string16 AsString16(WStringPiece str) {
+ return string16(as_u16cstr(str.data()), str.size());
+}
#endif // defined(WCHAR_T_IS_UTF16)
// Trims any whitespace from either end of the input string.
@@ -278,12 +296,12 @@ inline const char16* as_u16cstr(WStringPiece str) {
//
// The std::string versions return where whitespace was found.
// NOTE: Safe to use the same variable for both input and output.
-BASE_EXPORT TrimPositions TrimWhitespace(const string16& input,
+BASE_EXPORT TrimPositions TrimWhitespace(StringPiece16 input,
TrimPositions positions,
string16* output);
BASE_EXPORT StringPiece16 TrimWhitespace(StringPiece16 input,
TrimPositions positions);
-BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input,
+BASE_EXPORT TrimPositions TrimWhitespaceASCII(StringPiece input,
TrimPositions positions,
std::string* output);
BASE_EXPORT StringPiece TrimWhitespaceASCII(StringPiece input,
@@ -457,10 +475,6 @@ BASE_EXPORT void ReplaceSubstringsAfterOffset(
// convenient in that is can be used inline in the call, and fast in that it
// avoids copying the results of the call from a char* into a string.
//
-// |length_with_null| must be at least 2, since otherwise the underlying string
-// would have size 0, and trying to access &((*str)[0]) in that case can result
-// in a number of problems.
-//
// Internally, this takes linear time because the resize() call 0-fills the
// underlying array for potentially all
// (|length_with_null - 1| * sizeof(string_type::value_type)) bytes. Ideally we
@@ -518,6 +532,23 @@ BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
const string16& a,
size_t* offset);
+#if defined(OS_WIN) && defined(BASE_STRING16_IS_STD_U16STRING)
+BASE_EXPORT TrimPositions TrimWhitespace(WStringPiece input,
+ TrimPositions positions,
+ std::wstring* output);
+
+BASE_EXPORT WStringPiece TrimWhitespace(WStringPiece input,
+ TrimPositions positions);
+
+BASE_EXPORT bool TrimString(WStringPiece input,
+ WStringPiece trim_chars,
+ std::wstring* output);
+
+BASE_EXPORT WStringPiece TrimString(WStringPiece input,
+ WStringPiece trim_chars,
+ TrimPositions positions);
+#endif
+
} // namespace base
#if defined(OS_WIN)
diff --git a/chromium/base/strings/string_util_unittest.cc b/chromium/base/strings/string_util_unittest.cc
index 2e0e35e5596..58eda91f0a1 100644
--- a/chromium/base/strings/string_util_unittest.cc
+++ b/chromium/base/strings/string_util_unittest.cc
@@ -1414,6 +1414,13 @@ TEST_F(WriteIntoTest, WriteInto) {
WritesCorrectly(2);
WritesCorrectly(5000);
+ // Validate that WriteInto handles 0-length strings
+ std::string empty;
+ const char kOriginal[] = "original";
+ strncpy(WriteInto(&empty, 1), kOriginal, 0);
+ EXPECT_STREQ("", empty.c_str());
+ EXPECT_EQ(0u, empty.size());
+
// Validate that WriteInto doesn't modify other strings
// when using a Copy-on-Write implementation.
const char kLive[] = "live";
diff --git a/chromium/base/strings/string_util_win.h b/chromium/base/strings/string_util_win.h
index 7f260bfc8b4..791f60e3e4c 100644
--- a/chromium/base/strings/string_util_win.h
+++ b/chromium/base/strings/string_util_win.h
@@ -39,6 +39,19 @@ inline int vswprintf(wchar_t* buffer, size_t size,
return length;
}
+// Windows only overload of base::WriteInto for std::wstring. See the comment
+// above the cross-platform version in //base/strings/string_util.h for details.
+// TODO(crbug.com/911896): Rename this to WriteInto once base::string16 is
+// std::u16string on all platforms and using the name WriteInto here no longer
+// causes redefinition errors.
+inline wchar_t* WriteIntoW(std::wstring* str, size_t length_with_null) {
+ // Note: As of C++11 std::strings are guaranteed to be 0-terminated. Thus it
+ // is enough to reserve space for one char less.
+ DCHECK_GE(length_with_null, 1u);
+ str->resize(length_with_null - 1);
+ return &((*str)[0]);
+}
+
} // namespace base
#endif // BASE_STRINGS_STRING_UTIL_WIN_H_
diff --git a/chromium/base/strings/stringprintf.cc b/chromium/base/strings/stringprintf.cc
index 72160699203..738cc63bbe8 100644
--- a/chromium/base/strings/stringprintf.cc
+++ b/chromium/base/strings/stringprintf.cc
@@ -39,18 +39,25 @@ inline int vsnprintfT(wchar_t* buffer,
va_list argptr) {
return base::vswprintf(buffer, buf_size, format, argptr);
}
+inline int vsnprintfT(char16_t* buffer,
+ size_t buf_size,
+ const char16_t* format,
+ va_list argptr) {
+ return base::vswprintf(reinterpret_cast<wchar_t*>(buffer), buf_size,
+ reinterpret_cast<const wchar_t*>(format), argptr);
+}
#endif
// Templatized backend for StringPrintF/StringAppendF. This does not finalize
// the va_list, the caller is expected to do that.
-template <class StringType>
-static void StringAppendVT(StringType* dst,
- const typename StringType::value_type* format,
+template <class CharT>
+static void StringAppendVT(std::basic_string<CharT>* dst,
+ const CharT* format,
va_list ap) {
// First try with a small fixed size buffer.
// This buffer size should be kept in sync with StringUtilTest.GrowBoundary
// and StringUtilTest.StringPrintfBounds.
- typename StringType::value_type stack_buf[1024];
+ CharT stack_buf[1024];
va_list ap_copy;
va_copy(ap_copy, ap);
@@ -93,7 +100,7 @@ static void StringAppendVT(StringType* dst,
return;
}
- std::vector<typename StringType::value_type> mem_buf(mem_length);
+ std::vector<CharT> mem_buf(mem_length);
// NOTE: You can only use a va_list once. Since we're in a while loop, we
// need to make a new copy each time so we don't use up the original.
@@ -129,6 +136,15 @@ std::wstring StringPrintf(const wchar_t* format, ...) {
va_end(ap);
return result;
}
+
+std::u16string StringPrintf(const char16_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::u16string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
#endif
std::string StringPrintV(const char* format, va_list ap) {
@@ -156,6 +172,17 @@ const std::wstring& SStringPrintf(std::wstring* dst,
va_end(ap);
return *dst;
}
+
+const std::u16string& SStringPrintf(std::u16string* dst,
+ const char16_t* format,
+ ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
#endif
void StringAppendF(std::string* dst, const char* format, ...) {
@@ -172,6 +199,13 @@ void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
StringAppendV(dst, format, ap);
va_end(ap);
}
+
+void StringAppendF(std::u16string* dst, const char16_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
#endif
void StringAppendV(std::string* dst, const char* format, va_list ap) {
@@ -182,6 +216,10 @@ void StringAppendV(std::string* dst, const char* format, va_list ap) {
void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) {
StringAppendVT(dst, format, ap);
}
+
+void StringAppendV(std::u16string* dst, const char16_t* format, va_list ap) {
+ StringAppendVT(dst, format, ap);
+}
#endif
} // namespace base
diff --git a/chromium/base/strings/stringprintf.h b/chromium/base/strings/stringprintf.h
index 341c2ef8a64..a8d5bc84e9a 100644
--- a/chromium/base/strings/stringprintf.h
+++ b/chromium/base/strings/stringprintf.h
@@ -19,8 +19,14 @@ namespace base {
BASE_EXPORT std::string StringPrintf(const char* format, ...)
PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
#if defined(OS_WIN)
+// Note: Unfortunately compile time checking of the format string for UTF-16
+// strings is not supported by any compiler, thus these functions should be used
+// carefully and sparingly. Also applies to SStringPrintf and StringAppendV
+// below.
BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...)
WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
+BASE_EXPORT std::u16string StringPrintf(const char16_t* format, ...)
+ WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT;
#endif
// Return a C++ string given vprintf-like input.
@@ -35,6 +41,9 @@ BASE_EXPORT const std::string& SStringPrintf(std::string* dst,
BASE_EXPORT const std::wstring& SStringPrintf(std::wstring* dst,
const wchar_t* format,
...) WPRINTF_FORMAT(2, 3);
+BASE_EXPORT const std::u16string& SStringPrintf(std::u16string* dst,
+ const char16_t* format,
+ ...) WPRINTF_FORMAT(2, 3);
#endif
// Append result to a supplied string.
@@ -43,6 +52,8 @@ BASE_EXPORT void StringAppendF(std::string* dst, const char* format, ...)
#if defined(OS_WIN)
BASE_EXPORT void StringAppendF(std::wstring* dst, const wchar_t* format, ...)
WPRINTF_FORMAT(2, 3);
+BASE_EXPORT void StringAppendF(std::u16string* dst, const char16_t* format, ...)
+ WPRINTF_FORMAT(2, 3);
#endif
// Lower-level routine that takes a va_list and appends to a specified
@@ -53,6 +64,9 @@ BASE_EXPORT void StringAppendV(std::string* dst, const char* format, va_list ap)
BASE_EXPORT void StringAppendV(std::wstring* dst,
const wchar_t* format,
va_list ap) WPRINTF_FORMAT(2, 0);
+BASE_EXPORT void StringAppendV(std::u16string* dst,
+ const char16_t* format,
+ va_list ap) WPRINTF_FORMAT(2, 0);
#endif
} // namespace base
diff --git a/chromium/base/strings/stringprintf_unittest.cc b/chromium/base/strings/stringprintf_unittest.cc
index dbc9b74cc2e..557ed54d1df 100644
--- a/chromium/base/strings/stringprintf_unittest.cc
+++ b/chromium/base/strings/stringprintf_unittest.cc
@@ -18,7 +18,10 @@ namespace {
// A helper for the StringAppendV test that follows.
//
// Just forwards its args to StringAppendV.
-static void StringAppendVTestHelper(std::string* out, const char* format, ...) {
+template <class CharT>
+static void StringAppendVTestHelper(std::basic_string<CharT>* out,
+ const CharT* format,
+ ...) {
va_list ap;
va_start(ap, format);
StringAppendV(out, format, ap);
@@ -35,6 +38,7 @@ TEST(StringPrintfTest, StringPrintfMisc) {
EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w'));
#if defined(OS_WIN)
EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w'));
+ EXPECT_EQ(u"123hello w", StringPrintf(u"%3d%2ls %1lc", 123, u"hello", 'w'));
#endif
}
@@ -47,6 +51,10 @@ TEST(StringPrintfTest, StringAppendfEmptyString) {
std::wstring valuew(L"Hello");
StringAppendF(&valuew, L"%ls", L"");
EXPECT_EQ(L"Hello", valuew);
+
+ std::u16string value16(u"Hello");
+ StringAppendF(&value16, u"%ls", u"");
+ EXPECT_EQ(u"Hello", value16);
#endif
}
@@ -59,6 +67,10 @@ TEST(StringPrintfTest, StringAppendfString) {
std::wstring valuew(L"Hello");
StringAppendF(&valuew, L" %ls", L"World");
EXPECT_EQ(L"Hello World", valuew);
+
+ std::u16string value16(u"Hello");
+ StringAppendF(&value16, u" %ls", u"World");
+ EXPECT_EQ(u"Hello World", value16);
#endif
}
@@ -71,6 +83,10 @@ TEST(StringPrintfTest, StringAppendfInt) {
std::wstring valuew(L"Hello");
StringAppendF(&valuew, L" %d", 123);
EXPECT_EQ(L"Hello 123", valuew);
+
+ std::u16string value16(u"Hello");
+ StringAppendF(&value16, u" %d", 123);
+ EXPECT_EQ(u"Hello 123", value16);
#endif
}
@@ -79,12 +95,13 @@ TEST(StringPrintfTest, StringAppendfInt) {
TEST(StringPrintfTest, StringPrintfBounds) {
const int kSrcLen = 1026;
char src[kSrcLen];
- for (auto& i : src)
- i = 'A';
+ std::fill_n(src, kSrcLen, 'A');
wchar_t srcw[kSrcLen];
- for (auto& i : srcw)
- i = 'A';
+ std::fill_n(srcw, kSrcLen, 'A');
+
+ char16_t src16[kSrcLen];
+ std::fill_n(src16, kSrcLen, 'A');
for (int i = 1; i < 3; i++) {
src[kSrcLen - i] = 0;
@@ -97,6 +114,14 @@ TEST(StringPrintfTest, StringPrintfBounds) {
std::wstring outw;
SStringPrintf(&outw, L"%ls", srcw);
EXPECT_STREQ(srcw, outw.c_str());
+
+ src16[kSrcLen - i] = 0;
+ std::u16string out16;
+ SStringPrintf(&out16, u"%ls", src16);
+ // EXPECT_STREQ does not support const char16_t* strings yet.
+ // Dispatch to the const wchar_t* overload instead.
+ EXPECT_STREQ(reinterpret_cast<const wchar_t*>(src16),
+ reinterpret_cast<const wchar_t*>(out16.c_str()));
#endif
}
}
@@ -129,6 +154,16 @@ TEST(StringPrintfTest, StringAppendV) {
std::string out;
StringAppendVTestHelper(&out, "%d foo %s", 1, "bar");
EXPECT_EQ("1 foo bar", out);
+
+#if defined(OS_WIN)
+ std::wstring outw;
+ StringAppendVTestHelper(&outw, L"%d foo %ls", 1, L"bar");
+ EXPECT_EQ(L"1 foo bar", outw);
+
+ std::u16string out16;
+ StringAppendVTestHelper(&out16, u"%d foo %ls", 1, u"bar");
+ EXPECT_EQ(u"1 foo bar", out16);
+#endif
}
// Test the boundary condition for the size of the string_util's
diff --git a/chromium/base/syslog_logging.cc b/chromium/base/syslog_logging.cc
index 68483efccd5..6b7280dbc41 100644
--- a/chromium/base/syslog_logging.cc
+++ b/chromium/base/syslog_logging.cc
@@ -34,7 +34,7 @@ namespace {
std::string* g_event_source_name = nullptr;
uint16_t g_category = 0;
uint32_t g_event_id = 0;
-base::string16* g_user_sid = nullptr;
+std::wstring* g_user_sid = nullptr;
} // namespace
@@ -46,7 +46,7 @@ void SetEventSource(const std::string& name,
g_category = category;
g_event_id = event_id;
DCHECK_EQ(nullptr, g_user_sid);
- g_user_sid = new base::string16();
+ g_user_sid = new std::wstring();
base::win::GetUserSidString(g_user_sid);
}
@@ -102,7 +102,7 @@ EventLogMessage::~EventLogMessage() {
}
LPCSTR strings[1] = {message.data()};
PSID user_sid = nullptr;
- if (!::ConvertStringSidToSid(base::as_wcstr(*g_user_sid), &user_sid)) {
+ if (!::ConvertStringSidToSid(g_user_sid->c_str(), &user_sid)) {
stream() << " !!ERROR GETTING USER SID!!";
}
diff --git a/chromium/base/system/sys_info.h b/chromium/base/system/sys_info.h
index 80577b1b937..e5ffb2aeff5 100644
--- a/chromium/base/system/sys_info.h
+++ b/chromium/base/system/sys_info.h
@@ -83,6 +83,11 @@ class BASE_EXPORT SysInfo {
// Note: validate any new usage with the privacy team.
// TODO(crbug.com/907518): Implement support on other platforms.
std::string serial_number;
+
+ bool operator==(const HardwareInfo& rhs) const {
+ return manufacturer == rhs.manufacturer && model == rhs.model &&
+ serial_number == rhs.serial_number;
+ }
};
// Returns via |callback| a struct containing descriptive UTF-8 strings for
// the current machine manufacturer and model, or empty strings if the
diff --git a/chromium/base/system/sys_info_ios.mm b/chromium/base/system/sys_info_ios.mm
index e8ae8fb41d1..1dd9dc18ad8 100644
--- a/chromium/base/system/sys_info_ios.mm
+++ b/chromium/base/system/sys_info_ios.mm
@@ -13,7 +13,6 @@
#include "base/logging.h"
#include "base/mac/scoped_mach_port.h"
-#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/process/process_metrics.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
@@ -43,9 +42,10 @@ std::string SysInfo::OperatingSystemName() {
static dispatch_once_t get_system_name_once;
static std::string* system_name;
dispatch_once(&get_system_name_once, ^{
- base::mac::ScopedNSAutoreleasePool pool;
- system_name = new std::string(
- SysNSStringToUTF8([[UIDevice currentDevice] systemName]));
+ @autoreleasepool {
+ system_name = new std::string(
+ SysNSStringToUTF8([[UIDevice currentDevice] systemName]));
+ }
});
// Examples of returned value: 'iPhone OS' on iPad 5.1.1
// and iPhone 5.1.1.
@@ -57,9 +57,10 @@ std::string SysInfo::OperatingSystemVersion() {
static dispatch_once_t get_system_version_once;
static std::string* system_version;
dispatch_once(&get_system_version_once, ^{
- base::mac::ScopedNSAutoreleasePool pool;
- system_version = new std::string(
- SysNSStringToUTF8([[UIDevice currentDevice] systemVersion]));
+ @autoreleasepool {
+ system_version = new std::string(
+ SysNSStringToUTF8([[UIDevice currentDevice] systemVersion]));
+ }
});
return *system_version;
}
@@ -68,18 +69,19 @@ std::string SysInfo::OperatingSystemVersion() {
void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
int32_t* minor_version,
int32_t* bugfix_version) {
- base::mac::ScopedNSAutoreleasePool pool;
- std::string system_version = OperatingSystemVersion();
- if (!system_version.empty()) {
- // Try to parse out the version numbers from the string.
- int num_read = sscanf(system_version.c_str(), "%d.%d.%d", major_version,
- minor_version, bugfix_version);
- if (num_read < 1)
- *major_version = 0;
- if (num_read < 2)
- *minor_version = 0;
- if (num_read < 3)
- *bugfix_version = 0;
+ @autoreleasepool {
+ std::string system_version = OperatingSystemVersion();
+ if (!system_version.empty()) {
+ // Try to parse out the version numbers from the string.
+ int num_read = sscanf(system_version.c_str(), "%d.%d.%d", major_version,
+ minor_version, bugfix_version);
+ if (num_read < 1)
+ *major_version = 0;
+ if (num_read < 2)
+ *minor_version = 0;
+ if (num_read < 3)
+ *bugfix_version = 0;
+ }
}
}
diff --git a/chromium/base/system/sys_info_win.cc b/chromium/base/system/sys_info_win.cc
index 87650d8892f..a600296695e 100644
--- a/chromium/base/system/sys_info_win.cc
+++ b/chromium/base/system/sys_info_win.cc
@@ -171,9 +171,9 @@ SysInfo::HardwareInfo SysInfo::GetHardwareInfoSync() {
win::WmiComputerSystemInfo wmi_info = win::WmiComputerSystemInfo::Get();
HardwareInfo info;
- info.manufacturer = UTF16ToUTF8(wmi_info.manufacturer());
- info.model = UTF16ToUTF8(wmi_info.model());
- info.serial_number = UTF16ToUTF8(wmi_info.serial_number());
+ info.manufacturer = WideToUTF8(wmi_info.manufacturer());
+ info.model = WideToUTF8(wmi_info.model());
+ info.serial_number = WideToUTF8(wmi_info.serial_number());
DCHECK(IsStringUTF8(info.manufacturer));
DCHECK(IsStringUTF8(info.model));
DCHECK(IsStringUTF8(info.serial_number));
diff --git a/chromium/base/task/post_job.cc b/chromium/base/task/post_job.cc
index e57b32e4b40..3c83068995a 100644
--- a/chromium/base/task/post_job.cc
+++ b/chromium/base/task/post_job.cc
@@ -4,8 +4,11 @@
#include "base/task/post_job.h"
+#include "base/task/scoped_set_task_priority_for_current_thread.h"
#include "base/task/thread_pool/job_task_source.h"
#include "base/task/thread_pool/pooled_task_runner_delegate.h"
+#include "base/task/thread_pool/thread_pool_impl.h"
+#include "base/task/thread_pool/thread_pool_instance.h"
namespace base {
namespace experimental {
@@ -16,7 +19,6 @@ JobDelegate::JobDelegate(
: task_source_(task_source),
pooled_task_runner_delegate_(pooled_task_runner_delegate) {
DCHECK(task_source_);
- DCHECK(pooled_task_runner_delegate_);
#if DCHECK_IS_ON()
recorded_increase_version_ = task_source_->GetConcurrencyIncreaseVersion();
// Record max concurrency before running the worker task.
@@ -42,7 +44,9 @@ bool JobDelegate::ShouldYield() {
AssertExpectedConcurrency(recorded_max_concurrency_);
#endif // DCHECK_IS_ON()
const bool should_yield =
- pooled_task_runner_delegate_->ShouldYield(task_source_);
+ task_source_->ShouldYield() ||
+ (pooled_task_runner_delegate_ &&
+ pooled_task_runner_delegate_->ShouldYield(task_source_));
#if DCHECK_IS_ON()
last_should_yield_ = should_yield;
@@ -98,5 +102,91 @@ void JobDelegate::AssertExpectedConcurrency(size_t expected_max_concurrency) {
#endif // DCHECK_IS_ON()
}
+JobHandle::JobHandle() = default;
+
+JobHandle::JobHandle(scoped_refptr<internal::JobTaskSource> task_source)
+ : task_source_(std::move(task_source)) {}
+
+JobHandle::~JobHandle() {
+ DCHECK(!task_source_)
+ << "The Job must be cancelled, detached or joined before its "
+ "JobHandle is destroyed.";
+}
+
+JobHandle::JobHandle(JobHandle&&) = default;
+
+JobHandle& JobHandle::operator=(JobHandle&& other) {
+ DCHECK(!task_source_)
+ << "The Job must be cancelled, detached or joined before its "
+ "JobHandle is re-assigned.";
+ task_source_ = std::move(other.task_source_);
+ return *this;
+}
+
+void JobHandle::UpdatePriority(TaskPriority new_priority) {
+ task_source_->delegate()->UpdatePriority(task_source_, new_priority);
+}
+
+void JobHandle::NotifyConcurrencyIncrease() {
+ task_source_->NotifyConcurrencyIncrease();
+}
+
+void JobHandle::Join() {
+ DCHECK_GE(internal::GetTaskPriorityForCurrentThread(),
+ task_source_->priority_racy())
+ << "Join may not be called on Job with higher priority than the current "
+ "one.";
+ UpdatePriority(internal::GetTaskPriorityForCurrentThread());
+ bool must_run = task_source_->WillJoin();
+ while (must_run)
+ must_run = task_source_->RunJoinTask();
+ // Remove |task_source_| from the ThreadPool to prevent access to
+ // |max_concurrency_callback| after Join().
+ task_source_->delegate()->RemoveJobTaskSource(task_source_);
+ task_source_ = nullptr;
+}
+
+void JobHandle::Cancel() {
+ task_source_->Cancel();
+ Join();
+}
+
+void JobHandle::CancelAndDetach() {
+ task_source_->Cancel();
+ Detach();
+}
+
+void JobHandle::Detach() {
+ DCHECK(task_source_);
+ task_source_ = nullptr;
+}
+
+JobHandle PostJob(const Location& from_here,
+ const TaskTraits& traits,
+ RepeatingCallback<void(JobDelegate*)> worker_task,
+ RepeatingCallback<size_t()> max_concurrency_callback) {
+ DCHECK(ThreadPoolInstance::Get())
+ << "Ref. Prerequisite section of post_task.h.\n\n"
+ "Hint: if this is in a unit test, you're likely merely missing a "
+ "base::test::TaskEnvironment member in your fixture.\n";
+ DCHECK(traits.use_thread_pool())
+ << "The base::ThreadPool() trait is mandatory with PostJob().";
+ DCHECK_EQ(traits.extension_id(),
+ TaskTraitsExtensionStorage::kInvalidExtensionId)
+ << "Extension traits cannot be used with PostJob().";
+ TaskTraits adjusted_traits = traits;
+ adjusted_traits.InheritPriority(internal::GetTaskPriorityForCurrentThread());
+ auto task_source = base::MakeRefCounted<internal::JobTaskSource>(
+ from_here, adjusted_traits, std::move(worker_task),
+ std::move(max_concurrency_callback),
+ static_cast<internal::ThreadPoolImpl*>(ThreadPoolInstance::Get()));
+ const bool queued =
+ static_cast<internal::ThreadPoolImpl*>(ThreadPoolInstance::Get())
+ ->EnqueueJobTaskSource(task_source);
+ if (queued)
+ return internal::JobTaskSource::CreateJobHandle(std::move(task_source));
+ return JobHandle();
+}
+
} // namespace experimental
} // namespace base \ No newline at end of file
diff --git a/chromium/base/task/post_job.h b/chromium/base/task/post_job.h
index de6b2d66ac3..cf8ca1aec70 100644
--- a/chromium/base/task/post_job.h
+++ b/chromium/base/task/post_job.h
@@ -6,8 +6,12 @@
#define BASE_TASK_POST_JOB_H_
#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/task/task_traits.h"
#include "base/time/time.h"
namespace base {
@@ -23,8 +27,9 @@ class BASE_EXPORT JobDelegate {
public:
// A JobDelegate is instantiated for each worker task that is run.
// |task_source| is the task source whose worker task is running with this
- // delegate and |pooled_task_runner_delegate| provides communication with the
- // thread pool.
+ // delegate and |pooled_task_runner_delegate| is used by ShouldYield() to
+ // check whether the pool wants this worker task to yield (null if this worker
+ // should never yield -- e.g. when the main thread is a worker).
JobDelegate(internal::JobTaskSource* task_source,
internal::PooledTaskRunnerDelegate* pooled_task_runner_delegate);
~JobDelegate();
@@ -42,7 +47,7 @@ class BASE_EXPORT JobDelegate {
void YieldIfNeeded();
// Notifies the scheduler that max concurrency was increased, and the number
- // of worker should be adjusted.
+ // of worker should be adjusted accordingly. See PostJob() for more details.
void NotifyConcurrencyIncrease();
private:
@@ -67,6 +72,95 @@ class BASE_EXPORT JobDelegate {
DISALLOW_COPY_AND_ASSIGN(JobDelegate);
};
+// Handle returned when posting a Job. Provides methods to control execution of
+// the posted Job.
+class BASE_EXPORT JobHandle {
+ public:
+ JobHandle();
+ // A job must either be joined, canceled or detached before the JobHandle is
+ // destroyed.
+ ~JobHandle();
+
+ JobHandle(JobHandle&&);
+ JobHandle& operator=(JobHandle&&);
+
+ // Update this Job's priority.
+ void UpdatePriority(TaskPriority new_priority);
+
+ // Notifies the scheduler that max concurrency was increased, and the number
+ // of workers should be adjusted accordingly. See PostJob() for more details.
+ void NotifyConcurrencyIncrease();
+
+ // Contributes to the job on this thread. Doesn't return until all tasks have
+ // completed and max concurrency becomes 0. This also promotes this Job's
+ // priority to be at least as high as the calling thread's priority.
+ void Join();
+
+ // Forces all existing workers to yield ASAP. Waits until they have all
+ // returned from the Job's callback before returning.
+ void Cancel();
+
+ // Forces all existing workers to yield ASAP but doesn’t wait for them.
+ // Warning, this is dangerous if the Job's callback is bound to or has access
+ // to state which may be deleted after this call.
+ void CancelAndDetach();
+
+ // Can be invoked before ~JobHandle() to avoid waiting on the job completing.
+ void Detach();
+
+ private:
+ friend class internal::JobTaskSource;
+
+ explicit JobHandle(scoped_refptr<internal::JobTaskSource> task_source);
+
+ scoped_refptr<internal::JobTaskSource> task_source_;
+
+ DISALLOW_COPY_AND_ASSIGN(JobHandle);
+};
+
+// Posts a repeating |worker_task| with specific |traits| to run in parallel.
+// Returns a JobHandle associated with the Job, which can be joined, canceled or
+// detached.
+// To avoid scheduling overhead, |worker_task| should do as much work as
+// possible in a loop when invoked, and JobDelegate::ShouldYield() should be
+// periodically invoked to conditionally exit and let the scheduler prioritize
+// work.
+//
+// A canonical implementation of |worker_task| looks like:
+// void WorkerTask(JobDelegate* job_delegate) {
+// while (!job_delegate->ShouldYield()) {
+// auto work_item = worker_queue.TakeWorkItem(); // Smallest unit of work.
+// if (!work_item)
+// return:
+// ProcessWork(work_item);
+// }
+// }
+//
+// |max_concurrency_callback| controls the maximum number of threads calling
+// |worker_task| concurrently. |worker_task| is only invoked if the number of
+// threads previously running |worker_task| was less than the value returned by
+// |max_concurrency_callback|. In general, |max_concurrency_callback| should
+// return the latest number of incomplete work items (smallest unit of work)
+// left to processed. JobHandle/JobDelegate::NotifyConcurrencyIncrease() *must*
+// be invoked shortly after |max_concurrency_callback| starts returning a value
+// larger than previously returned values. This usually happens when new work
+// items are added and the API user wants additional threads to invoke
+// |worker_task| concurrently. The callbacks may be called concurrently on any
+// thread until the job is complete. If the job handle is detached, the
+// callbacks may still be called, so they must not access global state that
+// could be destroyed.
+//
+// |traits| requirements:
+// - base::ThreadPool() must be specified.
+// - Extension traits (e.g. BrowserThread) cannot be specified.
+// - base::ThreadPolicy must be specified if the priority of the task runner
+// will ever be increased from BEST_EFFORT.
+JobHandle BASE_EXPORT
+PostJob(const Location& from_here,
+ const TaskTraits& traits,
+ RepeatingCallback<void(JobDelegate*)> worker_task,
+ RepeatingCallback<size_t()> max_concurrency_callback);
+
} // namespace experimental
} // namespace base
diff --git a/chromium/base/task/post_job_unittest.cc b/chromium/base/task/post_job_unittest.cc
new file mode 100644
index 00000000000..3fc3d6d4b07
--- /dev/null
+++ b/chromium/base/task/post_job_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium 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/task/post_job.h"
+
+#include <atomic>
+
+#include "base/task/test_task_traits_extension.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/gtest_util.h"
+#include "base/test/task_environment.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(PostJobTest, PostJobSimple) {
+ test::TaskEnvironment task_environment;
+ std::atomic_size_t num_tasks_to_run(4);
+ auto handle = experimental::PostJob(
+ FROM_HERE, ThreadPool(),
+ BindLambdaForTesting(
+ [&](experimental::JobDelegate* delegate) { --num_tasks_to_run; }),
+ BindLambdaForTesting([&]() -> size_t { return num_tasks_to_run; }));
+ handle.Join();
+ DCHECK_EQ(num_tasks_to_run, 0U);
+}
+
+TEST(PostJobTest, PostJobExtension) {
+ testing::FLAGS_gtest_death_test_style = "threadsafe";
+ EXPECT_DCHECK_DEATH({
+ auto handle = experimental::PostJob(
+ FROM_HERE, TestExtensionBoolTrait(),
+ BindRepeating([](experimental::JobDelegate* delegate) {}),
+ BindRepeating([]() -> size_t { return 0; }));
+ });
+}
+
+} // namespace base \ No newline at end of file
diff --git a/chromium/base/task/post_task.cc b/chromium/base/task/post_task.cc
index 76415fbfe56..244e5f6adc8 100644
--- a/chromium/base/task/post_task.cc
+++ b/chromium/base/task/post_task.cc
@@ -41,6 +41,13 @@ TaskTraits GetTaskTraitsWithExplicitPriority(TaskTraits traits) {
}
TaskExecutor* GetTaskExecutorForTraits(const TaskTraits& traits) {
+ if (traits.use_current_thread()) {
+ TaskExecutor* executor = GetTaskExecutorForCurrentThread();
+ DCHECK(executor) << "Couldn't find a TaskExecutor for this thread. Note "
+ "you can't use base::CurrentThread in a one-off "
+ "base::ThreadPool task.";
+ return executor;
+ }
TaskExecutor* executor = GetRegisteredTaskExecutorForTraits(traits);
DCHECK(executor || ThreadPoolInstance::Get())
<< "Ref. Prerequisite section of post_task.h.\n\n"
@@ -139,54 +146,4 @@ scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner(
}
#endif // defined(OS_WIN)
-// TODO(crbug.com/968047): Update all call sites and remove these forwarding
-// wrappers.
-bool PostTaskWithTraits(const Location& from_here,
- const TaskTraits& traits,
- OnceClosure task) {
- return PostTask(from_here, traits, std::move(task));
-}
-
-bool PostDelayedTaskWithTraits(const Location& from_here,
- const TaskTraits& traits,
- OnceClosure task,
- TimeDelta delay) {
- return PostDelayedTask(from_here, traits, std::move(task), delay);
-}
-
-bool PostTaskWithTraitsAndReply(const Location& from_here,
- const TaskTraits& traits,
- OnceClosure task,
- OnceClosure reply) {
- return PostTaskAndReply(from_here, traits, std::move(task), std::move(reply));
-}
-
-scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits(const TaskTraits& traits) {
- return CreateTaskRunner(traits);
-}
-
-scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunnerWithTraits(
- const TaskTraits& traits) {
- return CreateSequencedTaskRunner(traits);
-}
-
-scoped_refptr<UpdateableSequencedTaskRunner>
-CreateUpdateableSequencedTaskRunnerWithTraits(const TaskTraits& traits) {
- return CreateUpdateableSequencedTaskRunner(traits);
-}
-
-scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunnerWithTraits(
- const TaskTraits& traits,
- SingleThreadTaskRunnerThreadMode thread_mode) {
- return CreateSingleThreadTaskRunner(traits, thread_mode);
-}
-
-#if defined(OS_WIN)
-scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunnerWithTraits(
- const TaskTraits& traits,
- SingleThreadTaskRunnerThreadMode thread_mode) {
- return CreateCOMSTATaskRunner(traits, thread_mode);
-}
-#endif // defined(OS_WIN)
-
} // namespace base
diff --git a/chromium/base/task/post_task.h b/chromium/base/task/post_task.h
index b02fd65a032..9f9b5a24fa3 100644
--- a/chromium/base/task/post_task.h
+++ b/chromium/base/task/post_task.h
@@ -171,21 +171,6 @@ bool PostTaskAndReplyWithResult(const Location& from_here,
std::move(reply), Owned(result)));
}
-// Temporary wrapper for PostTaskAndReplyWithResult.
-// TODO(crbug.com/968047): Update all call sites and remove.
-template <template <typename> class CallbackType,
- typename TaskReturnType,
- typename ReplyArgType,
- typename = EnableIfIsBaseCallback<CallbackType>>
-bool PostTaskWithTraitsAndReplyWithResult(
- const Location& from_here,
- const TaskTraits& traits,
- CallbackType<TaskReturnType()> task,
- CallbackType<void(ReplyArgType)> reply) {
- return PostTaskAndReplyWithResult(from_here, traits, std::move(task),
- std::move(reply));
-}
-
// Returns a TaskRunner whose PostTask invocations result in scheduling tasks
// using |traits|. Tasks may run in any order and in parallel.
BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunner(
@@ -247,38 +232,6 @@ BASE_EXPORT scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner(
SingleThreadTaskRunnerThreadMode::SHARED);
#endif // defined(OS_WIN)
-// Temporary wrappers for the task posting APIs while we remove the "WithTraits"
-// suffix.
-// TODO(crbug.com/968047): Update all call sites and remove.
-BASE_EXPORT bool PostTaskWithTraits(const Location& from_here,
- const TaskTraits& traits,
- OnceClosure task);
-BASE_EXPORT bool PostDelayedTaskWithTraits(const Location& from_here,
- const TaskTraits& traits,
- OnceClosure task,
- TimeDelta delay);
-BASE_EXPORT bool PostTaskWithTraitsAndReply(const Location& from_here,
- const TaskTraits& traits,
- OnceClosure task,
- OnceClosure reply);
-BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunnerWithTraits(
- const TaskTraits& traits);
-BASE_EXPORT scoped_refptr<SequencedTaskRunner>
-CreateSequencedTaskRunnerWithTraits(const TaskTraits& traits);
-BASE_EXPORT scoped_refptr<UpdateableSequencedTaskRunner>
-CreateUpdateableSequencedTaskRunnerWithTraits(const TaskTraits& traits);
-BASE_EXPORT scoped_refptr<SingleThreadTaskRunner>
-CreateSingleThreadTaskRunnerWithTraits(
- const TaskTraits& traits,
- SingleThreadTaskRunnerThreadMode thread_mode =
- SingleThreadTaskRunnerThreadMode::SHARED);
-#if defined(OS_WIN)
-BASE_EXPORT scoped_refptr<SingleThreadTaskRunner>
-CreateCOMSTATaskRunnerWithTraits(const TaskTraits& traits,
- SingleThreadTaskRunnerThreadMode thread_mode =
- SingleThreadTaskRunnerThreadMode::SHARED);
-#endif // defined(OS_WIN)
-
} // namespace base
#endif // BASE_TASK_POST_TASK_H_
diff --git a/chromium/base/task/post_task_unittest.cc b/chromium/base/task/post_task_unittest.cc
index ed7fb3db3bb..798eba8e580 100644
--- a/chromium/base/task/post_task_unittest.cc
+++ b/chromium/base/task/post_task_unittest.cc
@@ -5,9 +5,11 @@
#include "base/task/post_task.h"
#include "base/bind_helpers.h"
+#include "base/run_loop.h"
#include "base/task/scoped_set_task_priority_for_current_thread.h"
#include "base/task/task_executor.h"
#include "base/task/test_task_traits_extension.h"
+#include "base/test/bind_test_util.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "base/test/test_simple_task_runner.h"
@@ -17,6 +19,8 @@
using ::testing::_;
using ::testing::Invoke;
+using ::testing::IsNull;
+using ::testing::NotNull;
using ::testing::Return;
namespace base {
@@ -177,6 +181,159 @@ TEST_F(PostTaskTestWithExecutor, PostTaskToTaskExecutor) {
}
}
+TEST_F(PostTaskTestWithExecutor,
+ ThreadPoolTaskRunnerGetTaskExecutorForCurrentThread) {
+ auto task_runner = CreateTaskRunner({ThreadPool()});
+ RunLoop run_loop;
+
+ EXPECT_TRUE(task_runner->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ // We don't have an executor for a ThreadPool task runner becuse they
+ // are for one shot tasks.
+ EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull());
+ run_loop.Quit();
+ })));
+
+ run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+ ThreadPoolSequencedTaskRunnerGetTaskExecutorForCurrentThread) {
+ auto sequenced_task_runner = CreateSequencedTaskRunner({ThreadPool()});
+ RunLoop run_loop;
+
+ EXPECT_TRUE(sequenced_task_runner->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ EXPECT_THAT(GetTaskExecutorForCurrentThread(), NotNull());
+ run_loop.Quit();
+ })));
+
+ run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+ ThreadPoolSingleThreadTaskRunnerGetTaskExecutorForCurrentThread) {
+ auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()});
+ RunLoop run_loop;
+
+ EXPECT_TRUE(single_thread_task_runner->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ EXPECT_THAT(GetTaskExecutorForCurrentThread(), NotNull());
+ run_loop.Quit();
+ })));
+
+ run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor, ThreadPoolTaskRunnerCurrentThreadTrait) {
+ auto task_runner = CreateTaskRunner({ThreadPool()});
+ RunLoop run_loop;
+
+ EXPECT_TRUE(task_runner->PostTask(FROM_HERE, BindLambdaForTesting([&]() {
+ // CurrentThread is meaningless in this
+ // context.
+ EXPECT_DCHECK_DEATH(
+ PostTask(FROM_HERE, {CurrentThread()},
+ DoNothing()));
+ run_loop.Quit();
+ })));
+
+ run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+ ThreadPoolSequencedTaskRunnerCurrentThreadTrait) {
+ auto sequenced_task_runner = CreateSequencedTaskRunner({ThreadPool()});
+ RunLoop run_loop;
+
+ auto current_thread_task = BindLambdaForTesting([&]() {
+ EXPECT_TRUE(sequenced_task_runner->RunsTasksInCurrentSequence());
+ run_loop.Quit();
+ });
+
+ EXPECT_TRUE(sequenced_task_runner->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ EXPECT_TRUE(
+ PostTask(FROM_HERE, {CurrentThread()}, current_thread_task));
+ })));
+
+ run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+ ThreadPoolSingleThreadTaskRunnerCurrentThreadTrait) {
+ auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()});
+ RunLoop run_loop;
+
+ auto current_thread_task = BindLambdaForTesting([&]() {
+ EXPECT_TRUE(single_thread_task_runner->RunsTasksInCurrentSequence());
+ run_loop.Quit();
+ });
+
+ EXPECT_TRUE(single_thread_task_runner->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ EXPECT_TRUE(
+ PostTask(FROM_HERE, {CurrentThread()}, current_thread_task));
+ })));
+
+ run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor, ThreadPoolCurrentThreadChangePriority) {
+ auto single_thread_task_runner =
+ CreateSingleThreadTaskRunner({ThreadPool(), TaskPriority::USER_BLOCKING});
+ RunLoop run_loop;
+
+ auto current_thread_task = BindLambdaForTesting([&]() {
+ EXPECT_TRUE(single_thread_task_runner->RunsTasksInCurrentSequence());
+ run_loop.Quit();
+ });
+
+ EXPECT_TRUE(single_thread_task_runner->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ // We should be able to request a priority change, although it may be
+ // ignored.
+ EXPECT_TRUE(PostTask(FROM_HERE,
+ {CurrentThread(), TaskPriority::USER_VISIBLE},
+ current_thread_task));
+ })));
+
+ run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+ ThreadPoolCurrentThreadCantChangeShutdownBehavior) {
+ auto single_thread_task_runner = CreateSingleThreadTaskRunner(
+ {ThreadPool(), TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+ RunLoop run_loop;
+
+ EXPECT_TRUE(single_thread_task_runner->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ EXPECT_DCHECK_DEATH(PostTask(
+ FROM_HERE, {CurrentThread(), TaskShutdownBehavior::BLOCK_SHUTDOWN},
+ DoNothing()));
+ run_loop.Quit();
+ })));
+
+ run_loop.Run();
+}
+
+TEST_F(PostTaskTestWithExecutor,
+ ThreadPoolCurrentThreadCantSetSyncPrimitivesInNonSyncTaskRunner) {
+ auto single_thread_task_runner = CreateSingleThreadTaskRunner({ThreadPool()});
+ RunLoop run_loop;
+
+ EXPECT_TRUE(single_thread_task_runner->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ EXPECT_DCHECK_DEATH(
+ PostTask(FROM_HERE, {CurrentThread(), WithBaseSyncPrimitives()},
+ DoNothing()));
+ run_loop.Quit();
+ })));
+
+ run_loop.Run();
+}
+
TEST_F(PostTaskTestWithExecutor, RegisterExecutorTwice) {
testing::FLAGS_gtest_death_test_style = "threadsafe";
EXPECT_DCHECK_DEATH(
diff --git a/chromium/base/task/promise/abstract_promise.cc b/chromium/base/task/promise/abstract_promise.cc
index f48ba8517e2..315cfd30bfc 100644
--- a/chromium/base/task/promise/abstract_promise.cc
+++ b/chromium/base/task/promise/abstract_promise.cc
@@ -8,6 +8,7 @@
#include "base/lazy_instance.h"
#include "base/sequenced_task_runner.h"
#include "base/task/promise/dependent_list.h"
+#include "base/task/promise/post_task_executor.h"
#include "base/threading/sequenced_task_runner_handle.h"
namespace base {
@@ -65,6 +66,10 @@ AbstractPromise::~AbstractPromise() {
OnCanceled();
}
+void AbstractPromise::EmplaceResolvedVoid() {
+ emplace(Resolved<void>());
+}
+
bool AbstractPromise::IsCanceled() const {
if (dependents_.IsCanceled())
return true;
@@ -290,7 +295,7 @@ AbstractPromise* AbstractPromise::GetCurriedPromise() {
const PromiseExecutor* AbstractPromise::GetExecutor() const {
if (!value_.ContainsPromiseExecutor())
return nullptr;
- return value_.Get<internal::PromiseExecutor>();
+ return value_.Get<PromiseExecutor>();
}
PromiseExecutor::PrerequisitePolicy AbstractPromise::GetPrerequisitePolicy() {
@@ -509,7 +514,7 @@ void AbstractPromise::OnRejectMakeDependantsUseCurriedPrerequisite(
void AbstractPromise::DispatchPromise() {
if (task_runner_) {
- task_runner_->PostPromiseInternal(this, TimeDelta());
+ task_runner_->PostPromiseInternal(WrappedPromise(this), TimeDelta());
} else {
Execute();
}
@@ -670,14 +675,66 @@ void AbstractPromise::AdjacencyList::Clear() {
prerequisite_list_.clear();
} else {
// If there's multiple prerequisites we can't do that because the
- // DependentList::Nodes may still be in use by some of them. Instead we
- // release our prerequisite references and rely on refcounting to release
- // the owning AbstractPromise.
+ // DependentList::Nodes may still be in use by some of them.
+ // Instead we release our prerequisite references and rely on refcounting to
+ // release the owning AbstractPromise.
for (DependentList::Node& node : prerequisite_list_) {
node.ClearPrerequisite();
}
}
}
+BasePromise::BasePromise() = default;
+
+BasePromise::BasePromise(
+ scoped_refptr<internal::AbstractPromise> abstract_promise)
+ : abstract_promise_(std::move(abstract_promise)) {}
+
+BasePromise::BasePromise(const BasePromise& other) = default;
+BasePromise::BasePromise(BasePromise&& other) = default;
+
+BasePromise& BasePromise::operator=(const BasePromise& other) = default;
+BasePromise& BasePromise::operator=(BasePromise&& other) = default;
+
+BasePromise::~BasePromise() = default;
+
} // namespace internal
+
+WrappedPromise::WrappedPromise() = default;
+
+WrappedPromise::WrappedPromise(scoped_refptr<internal::AbstractPromise> promise)
+ : promise_(std::move(promise)) {}
+
+WrappedPromise::WrappedPromise(internal::PassedPromise&& passed_promise)
+ : promise_(passed_promise.Release(), subtle::kAdoptRefTag) {
+ DCHECK(promise_);
+}
+
+WrappedPromise::WrappedPromise(const Location& from_here, OnceClosure task)
+ : WrappedPromise(internal::AbstractPromise::CreateNoPrerequisitePromise(
+ from_here,
+ RejectPolicy::kMustCatchRejection,
+ internal::DependentList::ConstructUnresolved(),
+ internal::PromiseExecutor::Data(
+ base::in_place_type_t<internal::PostTaskExecutor<void>>(),
+ std::move(task)))) {}
+
+WrappedPromise::WrappedPromise(const WrappedPromise& other) = default;
+WrappedPromise::WrappedPromise(WrappedPromise&& other) = default;
+
+WrappedPromise& WrappedPromise::operator=(const WrappedPromise& other) =
+ default;
+WrappedPromise& WrappedPromise::operator=(WrappedPromise&& other) = default;
+
+WrappedPromise::~WrappedPromise() = default;
+
+void WrappedPromise::Execute() {
+ DCHECK(promise_);
+ promise_->Execute();
+}
+
+void WrappedPromise::Clear() {
+ promise_ = nullptr;
+}
+
} // namespace base
diff --git a/chromium/base/task/promise/abstract_promise.h b/chromium/base/task/promise/abstract_promise.h
index 9f67c688bd4..c16132c187c 100644
--- a/chromium/base/task/promise/abstract_promise.h
+++ b/chromium/base/task/promise/abstract_promise.h
@@ -23,6 +23,9 @@ class TaskRunner;
template <typename ResolveType, typename RejectType>
class ManualPromiseResolver;
+template <typename ResolveType, typename RejectType>
+class Promise;
+
// AbstractPromise Memory Management.
//
// Consider a chain of promises: P1, P2 & P3
@@ -265,13 +268,71 @@ enum class RejectPolicy {
kCatchNotRequired,
};
+class WrappedPromise;
+
namespace internal {
template <typename T, typename... Args>
class PromiseCallbackHelper;
-class PromiseHolder;
+class AbstractPromise;
class AbstractPromiseTest;
+class BasePromise;
+
+// A binary size optimization to reduce the overhead of passing a scoped_refptr
+// to Promise<> returned by PostTask. There are many thousands of PostTasks so
+// even a single extra instruction (such as the scoped_refptr move constructor
+// clearing the pointer) adds up. This is why we're not constructing a Promise<>
+// with a scoped_refptr.
+//
+// The constructor calls AddRef, it's up to the owner of this object to either
+// call Clear (which calls Release) or AbstractPromise in order to pass
+// ownership onto a WrappedPromise.
+class BASE_EXPORT PassedPromise {
+ public:
+ explicit inline PassedPromise(const scoped_refptr<AbstractPromise>& promise);
+
+ PassedPromise() : promise_(nullptr) {}
+
+ PassedPromise(const PassedPromise&) = delete;
+ PassedPromise& operator=(const PassedPromise&) = delete;
+
+#if DCHECK_IS_ON()
+ PassedPromise(PassedPromise&& other) noexcept : promise_(other.promise_) {
+ DCHECK(promise_);
+ other.promise_ = nullptr;
+ }
+
+ PassedPromise& operator=(PassedPromise&& other) noexcept {
+ DCHECK(!promise_);
+ promise_ = other.promise_;
+ DCHECK(promise_);
+ other.promise_ = nullptr;
+ return *this;
+ }
+
+ ~PassedPromise() {
+ DCHECK(!promise_) << "The PassedPromise must be Cleared or passed onto a "
+ "Wrapped Promise";
+ }
+#else
+ PassedPromise(PassedPromise&&) noexcept = default;
+ PassedPromise& operator=(PassedPromise&&) noexcept = default;
+#endif
+
+ AbstractPromise* Release() {
+ AbstractPromise* promise = promise_;
+#if DCHECK_IS_ON()
+ promise_ = nullptr;
+#endif
+ return promise;
+ }
+
+ AbstractPromise* get() const { return promise_; }
+
+ private:
+ AbstractPromise* promise_;
+};
// Internal promise representation, maintains a graph of dependencies and posts
// promises as they become ready. In debug builds various sanity checks are
@@ -308,12 +369,11 @@ class BASE_EXPORT AbstractPromise
RejectPolicy reject_policy,
ConstructType tag,
PromiseExecutor::Data&& executor_data) noexcept {
- scoped_refptr<AbstractPromise> promise =
- subtle::AdoptRefIfNeeded(new internal::AbstractPromise(
- nullptr, from_here, nullptr, reject_policy,
- tag, std::move(executor_data)),
- AbstractPromise::kRefCountPreference);
- return promise;
+ return subtle::AdoptRefIfNeeded(
+ new internal::AbstractPromise(nullptr, from_here, nullptr,
+ reject_policy, tag,
+ std::move(executor_data)),
+ AbstractPromise::kRefCountPreference);
}
AbstractPromise(const AbstractPromise&) = delete;
@@ -347,7 +407,9 @@ class BASE_EXPORT AbstractPromise
public:
PromiseValue& value() { return value_; }
+#if DCHECK_IS_ON()
~ValueHandle() { value_.reset(); }
+#endif
private:
friend class AbstractPromise;
@@ -357,6 +419,8 @@ class BASE_EXPORT AbstractPromise
PromiseValue& value_;
};
+ // Used for promise results that require move semantics. E.g. a promise chain
+ // involving a std::unique_ptr<>.
ValueHandle TakeValue() { return ValueHandle(value_); }
// Returns nullptr if there isn't a curried promise.
@@ -380,6 +444,10 @@ class BASE_EXPORT AbstractPromise
"Use scoped_refptr<AbstractPromise> instead");
}
+ // An out-of line emplace(Resolved<void>()); Useful for reducing binary
+ // bloat in executor templates.
+ void EmplaceResolvedVoid();
+
// This is separate from AbstractPromise to reduce the memory footprint of
// regular PostTask without promise chains.
class BASE_EXPORT AdjacencyList {
@@ -457,9 +525,14 @@ class BASE_EXPORT AbstractPromise
void IgnoreUncaughtCatchForTesting();
- private:
- friend class AbstractPromiseTest;
+ // Signals that this promise was cancelled. If executor hasn't run yet, this
+ // will prevent it from running and cancels any dependent promises unless they
+ // have PrerequisitePolicy::kAny, in which case they will only be canceled if
+ // all of their prerequisites are canceled. If OnCanceled() or OnResolved() or
+ // OnRejected() has already run, this does nothing.
+ void OnCanceled();
+ private:
friend base::RefCountedThreadSafe<AbstractPromise>;
friend class AbstractPromiseTest;
@@ -470,8 +543,6 @@ class BASE_EXPORT AbstractPromise
template <typename T, typename... Args>
friend class PromiseCallbackHelper;
- friend class PromiseHolder;
-
template <typename ConstructType>
AbstractPromise(const scoped_refptr<TaskRunner>& task_runner,
const Location& from_here,
@@ -520,13 +591,6 @@ class BASE_EXPORT AbstractPromise
// have been canceled, in which case null is returned.
AbstractPromise* FindCurriedAncestor();
- // Signals that this promise was cancelled. If executor hasn't run yet, this
- // will prevent it from running and cancels any dependent promises unless they
- // have PrerequisitePolicy::kAny, in which case they will only be canceled if
- // all of their prerequisites are canceled. If OnCanceled() or OnResolved() or
- // OnRejected() has already run, this does nothing.
- void OnCanceled();
-
// Signals that |value_| now contains a resolve value. Dependent promises may
// scheduled for execution.
void OnResolved();
@@ -714,7 +778,115 @@ class BASE_EXPORT AbstractPromise
std::unique_ptr<AdjacencyList> prerequisites_;
};
+PassedPromise::PassedPromise(const scoped_refptr<AbstractPromise>& promise)
+ : promise_(promise.get()) {
+ promise_->AddRef();
+}
+
+// Non-templatized base class of the Promise<> template. This is a binary size
+// optimization, letting us use an out of line destructor in the template
+// instead of the more complex scoped_refptr<> destructor.
+class BASE_EXPORT BasePromise {
+ public:
+ BasePromise();
+
+ BasePromise(const BasePromise& other);
+ BasePromise(BasePromise&& other) noexcept;
+
+ BasePromise& operator=(const BasePromise& other);
+ BasePromise& operator=(BasePromise&& other) noexcept;
+
+ // We want an out of line destructor to reduce binary size.
+ ~BasePromise();
+
+ // Returns true if the promise is not null.
+ operator bool() const { return abstract_promise_.get(); }
+
+ protected:
+ struct InlineConstructor {};
+
+ explicit BasePromise(
+ scoped_refptr<internal::AbstractPromise> abstract_promise);
+
+ // We want this to be inlined to reduce binary size for the Promise<>
+ // constructor. Its a template to bypass ChromiumStyle plugin which otherwise
+ // insists this is out of line.
+ template <typename T>
+ explicit BasePromise(internal::PassedPromise&& passed_promise,
+ T InlineConstructor)
+ : abstract_promise_(passed_promise.Release(), subtle::kAdoptRefTag) {}
+
+ scoped_refptr<internal::AbstractPromise> abstract_promise_;
+};
+
} // namespace internal
+
+// Wrapper around scoped_refptr<base::internal::AbstractPromise> which is
+// intended for use by TaskRunner implementations.
+class BASE_EXPORT WrappedPromise {
+ public:
+ WrappedPromise();
+
+ explicit WrappedPromise(scoped_refptr<internal::AbstractPromise> promise);
+
+ WrappedPromise(const WrappedPromise& other);
+ WrappedPromise(WrappedPromise&& other) noexcept;
+
+ WrappedPromise& operator=(const WrappedPromise& other);
+ WrappedPromise& operator=(WrappedPromise&& other) noexcept;
+
+ explicit WrappedPromise(internal::PassedPromise&& passed_promise);
+
+ // Constructs a promise to run |task|.
+ WrappedPromise(const Location& from_here, OnceClosure task);
+
+ // If the WrappedPromise hasn't been executed, cleared or taken by
+ // TakeForTesting, it will be canceled to prevent memory leaks of dependent
+ // tasks that will never run.
+ ~WrappedPromise();
+
+ // Returns true if the promise is not null.
+ operator bool() const { return promise_.get(); }
+
+ bool IsCanceled() const {
+ DCHECK(promise_);
+ return promise_->IsCanceled();
+ }
+
+ void OnCanceled() {
+ DCHECK(promise_);
+ promise_->OnCanceled();
+ }
+
+ // Can only be called once, clears |promise_| after execution.
+ void Execute();
+
+ // Clears |promise_|.
+ void Clear();
+
+ const Location& from_here() const {
+ DCHECK(promise_);
+ return promise_->from_here();
+ }
+
+ scoped_refptr<internal::AbstractPromise>& GetForTesting() { return promise_; }
+
+ scoped_refptr<internal::AbstractPromise> TakeForTesting() {
+ return std::move(promise_);
+ }
+
+ private:
+ template <typename ResolveType, typename RejectType>
+ friend class Promise;
+
+ template <typename T, typename... Args>
+ friend class internal::PromiseCallbackHelper;
+
+ friend class Promises;
+
+ scoped_refptr<internal::AbstractPromise> promise_;
+};
+
} // namespace base
#endif // BASE_TASK_PROMISE_ABSTRACT_PROMISE_H_
diff --git a/chromium/base/task/promise/abstract_promise_unittest.cc b/chromium/base/task/promise/abstract_promise_unittest.cc
index ea8d31fd898..5cbe4498d54 100644
--- a/chromium/base/task/promise/abstract_promise_unittest.cc
+++ b/chromium/base/task/promise/abstract_promise_unittest.cc
@@ -238,10 +238,13 @@ class AbstractPromiseTest : public testing::Test {
#endif
std::move(settings.callback));
- return AbstractPromise::Create(
- settings.task_runner, settings.from_here,
- std::move(settings.prerequisites), settings.reject_policy,
- DependentList::ConstructUnresolved(), std::move(executor_data));
+ return WrappedPromise(AbstractPromise::Create(
+ settings.task_runner, settings.from_here,
+ std::move(settings.prerequisites),
+ settings.reject_policy,
+ DependentList::ConstructUnresolved(),
+ std::move(executor_data)))
+ .TakeForTesting();
}
PromiseSettings settings;
@@ -293,7 +296,7 @@ class AbstractPromiseTest : public testing::Test {
PromiseSettingsBuilder AllPromise(
Location from_here,
- std::vector<internal::DependentList::Node> prerequisite_list) {
+ std::vector<DependentList::Node> prerequisite_list) {
PromiseSettingsBuilder builder(
from_here, std::make_unique<AbstractPromise::AdjacencyList>(
std::move(prerequisite_list)));
diff --git a/chromium/base/task/promise/dependent_list.h b/chromium/base/task/promise/dependent_list.h
index 020bdbfc77f..3245c1cb48f 100644
--- a/chromium/base/task/promise/dependent_list.h
+++ b/chromium/base/task/promise/dependent_list.h
@@ -59,7 +59,7 @@ class BASE_EXPORT DependentList {
// Align Node on an 8-byte boundary to ensure the first 3 bits are 0 and can
// be used to store additional state (see static_asserts below).
- class BASE_EXPORT alignas(8) Node {
+ class BASE_EXPORT ALIGNAS(8) Node {
public:
Node();
explicit Node(Node&& other) noexcept;
diff --git a/chromium/base/task/promise/finally_executor.h b/chromium/base/task/promise/finally_executor.h
index 7dc1c798a5c..a80f81c5ee9 100644
--- a/chromium/base/task/promise/finally_executor.h
+++ b/chromium/base/task/promise/finally_executor.h
@@ -43,9 +43,15 @@ class FinallyExecutor {
void Execute(AbstractPromise* promise) {
AbstractPromise* prerequisite = promise->GetOnlyPrerequisite();
- CallbackT* resolve_executor = static_cast<CallbackT*>(&common_.callback_);
- RunHelper<CallbackT, void, ResolveStorage, RejectStorage>::Run(
- std::move(*resolve_executor), prerequisite, promise);
+ // Internally RunHelper uses const RepeatingCallback<>& to avoid the
+ // binary size overhead of moving a scoped_refptr<> about. We respect
+ // the onceness of the callback and RunHelper will overwrite the callback
+ // with the result.
+ using RepeatingCB = typename ToRepeatingCallback<CallbackT>::value;
+ RepeatingCB* resolve_executor =
+ static_cast<RepeatingCB*>(&common_.callback_);
+ RunHelper<RepeatingCB, void, ResolveStorage, RejectStorage>::Run(
+ *resolve_executor, prerequisite, promise);
}
#if DCHECK_IS_ON()
diff --git a/chromium/base/task/promise/helpers.cc b/chromium/base/task/promise/helpers.cc
index 8a68a123956..f100cac0c29 100644
--- a/chromium/base/task/promise/helpers.cc
+++ b/chromium/base/task/promise/helpers.cc
@@ -11,32 +11,11 @@
namespace base {
namespace internal {
-PromiseHolder::PromiseHolder(scoped_refptr<AbstractPromise> promise)
- : promise_(std::move(promise)) {}
-
-PromiseHolder::~PromiseHolder() {
- // Detect if the promise was not executed and if so cancel to ensure memory
- // is released.
- if (promise_)
- promise_->OnCanceled();
-}
-
-PromiseHolder::PromiseHolder(PromiseHolder&& other)
- : promise_(std::move(other.promise_)) {}
-
-scoped_refptr<AbstractPromise> PromiseHolder::Unwrap() const {
- return std::move(promise_);
-}
-
-scoped_refptr<TaskRunner> GetCurrentSequence() {
- return SequencedTaskRunnerHandle::Get();
-}
-
DoNothing ToCallbackBase(DoNothing task) {
return task;
}
-scoped_refptr<AbstractPromise> ConstructAbstractPromiseWithSinglePrerequisite(
+PassedPromise ConstructAbstractPromiseWithSinglePrerequisite(
const scoped_refptr<TaskRunner>& task_runner,
const Location& from_here,
AbstractPromise* prerequisite,
@@ -46,26 +25,35 @@ scoped_refptr<AbstractPromise> ConstructAbstractPromiseWithSinglePrerequisite(
if (!prerequisite) {
// Ensure the destructor for |executor_data| runs.
PromiseExecutor dummy_executor(std::move(executor_data));
- return nullptr;
+ return PassedPromise();
}
- return AbstractPromise::Create(
+ return PassedPromise(AbstractPromise::Create(
task_runner, from_here,
std::make_unique<AbstractPromise::AdjacencyList>(prerequisite),
RejectPolicy::kMustCatchRejection,
- internal::DependentList::ConstructUnresolved(), std::move(executor_data));
+ internal::DependentList::ConstructUnresolved(),
+ std::move(executor_data)));
}
-scoped_refptr<AbstractPromise> ConstructManualPromiseResolverPromise(
+PassedPromise ConstructHereAbstractPromiseWithSinglePrerequisite(
const Location& from_here,
- RejectPolicy reject_policy,
- bool can_resolve,
- bool can_reject) {
- return AbstractPromise::CreateNoPrerequisitePromise(
+ AbstractPromise* prerequisite,
+ internal::PromiseExecutor::Data&& executor_data) noexcept {
+ return ConstructAbstractPromiseWithSinglePrerequisite(
+ SequencedTaskRunnerHandle::Get(), from_here, prerequisite,
+ std::move(executor_data));
+}
+
+PassedPromise ConstructManualPromiseResolverPromise(const Location& from_here,
+ RejectPolicy reject_policy,
+ bool can_resolve,
+ bool can_reject) {
+ return PassedPromise(AbstractPromise::CreateNoPrerequisitePromise(
from_here, reject_policy, internal::DependentList::ConstructUnresolved(),
internal::PromiseExecutor::Data(
in_place_type_t<internal::NoOpPromiseExecutor>(), can_resolve,
- can_reject));
+ can_reject)));
}
} // namespace internal
diff --git a/chromium/base/task/promise/helpers.h b/chromium/base/task/promise/helpers.h
index 29cb49011ab..87d9ff64bec 100644
--- a/chromium/base/task/promise/helpers.h
+++ b/chromium/base/task/promise/helpers.h
@@ -19,11 +19,6 @@ class DoNothing;
namespace internal {
-// A wrapper around SequencedTaskRunnerHandle::Get(). This file is included by
-// base/task_runner.h which means we can't include anything that depends on
-// that!
-scoped_refptr<TaskRunner> BASE_EXPORT GetCurrentSequence();
-
template <typename T>
using ToNonVoidT = std::conditional_t<std::is_void<T>::value, Void, T>;
@@ -412,9 +407,26 @@ class ArgMoveSemanticsHelper {
}
};
+// Helper for converting a callback to its repeating variant.
+template <typename Cb>
+struct ToRepeatingCallback;
+
+template <typename Cb>
+struct ToRepeatingCallback<OnceCallback<Cb>> {
+ using value = RepeatingCallback<Cb>;
+};
+
+template <typename Cb>
+struct ToRepeatingCallback<RepeatingCallback<Cb>> {
+ using value = RepeatingCallback<Cb>;
+};
+
// Helper for running a promise callback and storing the result if any.
//
-// Callback = signature of the callback to execute,
+// Callback = signature of the callback to execute. Note we use repeating
+// callbacks to avoid the binary size overhead of a once callback which will
+// generate a destructor which is redundant because we overwrite the executor
+// with the promise result which also triggers the destructor.
// ArgStorageType = type of the callback parameter (or void if none)
// ResolveStorage = type to use for resolve, usually Resolved<T>.
// RejectStorage = type to use for reject, usually Rejected<T>.
@@ -431,18 +443,18 @@ template <typename CbResult,
typename ArgStorageType,
typename ResolveStorage,
typename RejectStorage>
-struct RunHelper<OnceCallback<CbResult(CbArg)>,
+struct RunHelper<RepeatingCallback<CbResult(CbArg)>,
ArgStorageType,
ResolveStorage,
RejectStorage> {
- using Callback = OnceCallback<CbResult(CbArg)>;
+ using Callback = RepeatingCallback<CbResult(CbArg)>;
- static void Run(Callback&& executor,
+ static void Run(const Callback& executor,
AbstractPromise* arg,
AbstractPromise* result) {
EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(
- result, std::move(executor).Run(
- ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg)));
+ result,
+ executor.Run(ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg)));
}
};
@@ -451,19 +463,18 @@ template <typename CbArg,
typename ArgStorageType,
typename ResolveStorage,
typename RejectStorage>
-struct RunHelper<OnceCallback<void(CbArg)>,
+struct RunHelper<RepeatingCallback<void(CbArg)>,
ArgStorageType,
ResolveStorage,
RejectStorage> {
- using Callback = OnceCallback<void(CbArg)>;
+ using Callback = RepeatingCallback<void(CbArg)>;
- static void Run(Callback&& executor,
+ static void Run(const Callback& executor,
AbstractPromise* arg,
AbstractPromise* result) {
static_assert(std::is_void<typename ResolveStorage::Type>::value, "");
- std::move(executor).Run(
- ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg));
- result->emplace(Resolved<void>());
+ executor.Run(ArgMoveSemanticsHelper<CbArg, ArgStorageType>::Get(arg));
+ result->EmplaceResolvedVoid();
}
};
@@ -472,17 +483,17 @@ template <typename CbResult,
typename ArgStorageType,
typename ResolveStorage,
typename RejectStorage>
-struct RunHelper<OnceCallback<CbResult()>,
+struct RunHelper<RepeatingCallback<CbResult()>,
ArgStorageType,
ResolveStorage,
RejectStorage> {
- using Callback = OnceCallback<CbResult()>;
+ using Callback = RepeatingCallback<CbResult()>;
- static void Run(Callback&& executor,
+ static void Run(const Callback& executor,
AbstractPromise* arg,
AbstractPromise* result) {
- EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(
- result, std::move(executor).Run());
+ EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(result,
+ executor.Run());
}
};
@@ -490,16 +501,16 @@ struct RunHelper<OnceCallback<CbResult()>,
template <typename ArgStorageType,
typename ResolveStorage,
typename RejectStorage>
-struct RunHelper<OnceCallback<void()>,
+struct RunHelper<RepeatingCallback<void()>,
ArgStorageType,
ResolveStorage,
RejectStorage> {
- static void Run(OnceCallback<void()>&& executor,
+ static void Run(const RepeatingCallback<void()>& executor,
AbstractPromise* arg,
AbstractPromise* result) {
static_assert(std::is_void<typename ResolveStorage::Type>::value, "");
- std::move(executor).Run();
- result->emplace(Resolved<void>());
+ executor.Run();
+ result->EmplaceResolvedVoid();
}
};
@@ -538,79 +549,51 @@ template <typename CbResult,
typename... CbArgs,
typename ResolveStorage,
typename RejectStorage>
-struct RunHelper<OnceCallback<CbResult(CbArgs...)>,
+struct RunHelper<RepeatingCallback<CbResult(CbArgs...)>,
Resolved<std::tuple<CbArgs...>>,
ResolveStorage,
RejectStorage> {
- using Callback = OnceCallback<CbResult(CbArgs...)>;
+ using Callback = RepeatingCallback<CbResult(CbArgs...)>;
using StorageType = Resolved<std::tuple<CbArgs...>>;
using IndexSequence = std::index_sequence_for<CbArgs...>;
- static void Run(Callback&& executor,
+ static void Run(const Callback& executor,
AbstractPromise* arg,
AbstractPromise* result) {
AbstractPromise::ValueHandle value = arg->TakeValue();
std::tuple<CbArgs...>& tuple = value.value().Get<StorageType>()->value;
- RunInternal(std::move(executor), tuple, result,
+ RunInternal(executor, tuple, result,
std::integral_constant<bool, std::is_void<CbResult>::value>(),
IndexSequence{});
}
private:
template <typename Callback, size_t... Indices>
- static void RunInternal(Callback&& executor,
+ static void RunInternal(const Callback& executor,
std::tuple<CbArgs...>& tuple,
AbstractPromise* result,
std::false_type void_result,
std::index_sequence<Indices...>) {
- EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(
- std::move(executor).Run(
- TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>,
- Indices>::Get(tuple)...));
+ EmplaceHelper<ResolveStorage, RejectStorage>::Emplace(executor.Run(
+ TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>,
+ Indices>::Get(tuple)...));
}
template <typename Callback, size_t... Indices>
- static void RunInternal(Callback&& executor,
+ static void RunInternal(const Callback& executor,
std::tuple<CbArgs...>& tuple,
AbstractPromise* result,
std::true_type void_result,
std::index_sequence<Indices...>) {
- std::move(executor).Run(
- TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>,
- Indices>::Get(tuple)...);
- result->emplace(Resolved<void>());
+ executor.Run(TupleArgMoveSemanticsHelper<Callback, std::tuple<CbArgs...>,
+ Indices>::Get(tuple)...);
+ result->EmplaceResolvedVoid();
}
};
-// For use with base::Bind*. Cancels the promise if the callback was not run by
-// the time the callback is deleted.
-class BASE_EXPORT PromiseHolder {
- public:
- explicit PromiseHolder(scoped_refptr<internal::AbstractPromise> promise);
-
- ~PromiseHolder();
-
- PromiseHolder(PromiseHolder&& other);
-
- scoped_refptr<internal::AbstractPromise> Unwrap() const;
-
- private:
- mutable scoped_refptr<internal::AbstractPromise> promise_;
-};
-
-} // namespace internal
-
-template <>
-struct BindUnwrapTraits<internal::PromiseHolder> {
- static scoped_refptr<internal::AbstractPromise> Unwrap(
- const internal::PromiseHolder& o) {
- return o.Unwrap();
- }
-};
-
-namespace internal {
-
-// Used by ManualPromiseResolver<> to generate callbacks.
+// Used by ManualPromiseResolver<> to generate callbacks. Note the use of
+// WrappedPromise, this is necessary because we want to cancel the promise (to
+// release memory) if the callback gets deleted without having being run.
template <typename T, typename... Args>
class PromiseCallbackHelper {
public:
@@ -624,7 +607,7 @@ class PromiseCallbackHelper {
std::forward<Args>(args)...);
promise->OnResolved();
},
- PromiseHolder(promise));
+ promise);
}
static RepeatingCallback GetRepeatingResolveCallback(
@@ -635,7 +618,7 @@ class PromiseCallbackHelper {
std::forward<Args>(args)...);
promise->OnResolved();
},
- PromiseHolder(promise));
+ promise);
}
static Callback GetRejectCallback(scoped_refptr<AbstractPromise>& promise) {
@@ -645,7 +628,7 @@ class PromiseCallbackHelper {
std::forward<Args>(args)...);
promise->OnRejected();
},
- PromiseHolder(promise));
+ promise);
}
static RepeatingCallback GetRepeatingRejectCallback(
@@ -656,7 +639,7 @@ class PromiseCallbackHelper {
std::forward<Args>(args)...);
promise->OnRejected();
},
- PromiseHolder(promise));
+ promise);
}
};
@@ -679,8 +662,7 @@ struct IsValidPromiseArg<PromiseType&, CallbackArgType> {
// rejection storage type.
template <typename RejectT>
struct AllPromiseRejectHelper {
- static void Reject(AbstractPromise* result,
- const scoped_refptr<AbstractPromise>& prerequisite) {
+ static void Reject(AbstractPromise* result, AbstractPromise* prerequisite) {
result->emplace(scoped_refptr<AbstractPromise>(prerequisite));
}
};
@@ -709,14 +691,20 @@ CallbackBase&& ToCallbackBase(const CallbackT&& task) {
// Helps reduce template bloat by moving AbstractPromise construction out of
// line.
-scoped_refptr<AbstractPromise> BASE_EXPORT
-ConstructAbstractPromiseWithSinglePrerequisite(
+PassedPromise BASE_EXPORT ConstructAbstractPromiseWithSinglePrerequisite(
const scoped_refptr<TaskRunner>& task_runner,
const Location& from_here,
AbstractPromise* prerequsite,
- internal::PromiseExecutor::Data&& executor_data) noexcept;
+ PromiseExecutor::Data&& executor_data) noexcept;
+
+// Like ConstructAbstractPromiseWithSinglePrerequisite except tasks are posted
+// onto SequencedTaskRunnerHandle::Get().
+PassedPromise BASE_EXPORT ConstructHereAbstractPromiseWithSinglePrerequisite(
+ const Location& from_here,
+ AbstractPromise* prerequsite,
+ PromiseExecutor::Data&& executor_data) noexcept;
-scoped_refptr<AbstractPromise> BASE_EXPORT
+PassedPromise BASE_EXPORT
ConstructManualPromiseResolverPromise(const Location& from_here,
RejectPolicy reject_policy,
bool can_resolve,
diff --git a/chromium/base/task/promise/helpers_unittest.cc b/chromium/base/task/promise/helpers_unittest.cc
index afdc9e7079e..f13fb5c8c7a 100644
--- a/chromium/base/task/promise/helpers_unittest.cc
+++ b/chromium/base/task/promise/helpers_unittest.cc
@@ -187,8 +187,9 @@ TEST(EmplaceHelper, EmplacePromiseResult) {
TEST(EmplaceHelper, EmplacePromise) {
scoped_refptr<AbstractPromise> promise =
DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
- scoped_refptr<AbstractPromise> curried = DoNothingPromiseBuilder(FROM_HERE);
+ PassedPromise curried = NoOpPromiseExecutor::Create(
+ FROM_HERE, false, false, RejectPolicy::kCatchNotRequired);
EmplaceHelper<Resolved<int>, Rejected<NoReject>>::Emplace(
promise.get(), Promise<int>(std::move(curried)));
@@ -243,8 +244,8 @@ TEST(RunHelper, CallbackVoidArgumentIntResult) {
scoped_refptr<AbstractPromise> result =
DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
- RunHelper<OnceCallback<int()>, Resolved<void>, Resolved<int>,
- Rejected<std::string>>::Run(BindOnce([]() { return 123; }),
+ RunHelper<RepeatingCallback<int()>, Resolved<void>, Resolved<int>,
+ Rejected<std::string>>::Run(BindRepeating([]() { return 123; }),
arg.get(), result.get());
EXPECT_EQ(result->value().template Get<Resolved<int>>()->value, 123);
@@ -255,8 +256,8 @@ TEST(RunHelper, CallbackVoidArgumentVoidResult) {
scoped_refptr<AbstractPromise> result =
DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
- RunHelper<OnceCallback<void()>, Resolved<void>, Resolved<void>,
- Rejected<std::string>>::Run(BindOnce([]() {}), arg.get(),
+ RunHelper<RepeatingCallback<void()>, Resolved<void>, Resolved<void>,
+ Rejected<std::string>>::Run(BindRepeating([]() {}), arg.get(),
result.get());
EXPECT_TRUE(result->value().ContainsResolved());
@@ -268,8 +269,8 @@ TEST(RunHelper, CallbackIntArgumentIntResult) {
DoNothingPromiseBuilder(FROM_HERE).SetCanResolve(true);
arg->emplace(Resolved<int>(123));
- RunHelper<OnceCallback<int(int)>, Resolved<int>, Resolved<int>,
- Rejected<std::string>>::Run(BindOnce([](int value) {
+ RunHelper<RepeatingCallback<int(int)>, Resolved<int>, Resolved<int>,
+ Rejected<std::string>>::Run(BindRepeating([](int value) {
return value + 1;
}),
arg.get(), result.get());
@@ -284,7 +285,7 @@ TEST(RunHelper, CallbackIntArgumentArgumentVoidResult) {
arg->emplace(Resolved<int>(123));
int value;
- RunHelper<OnceCallback<void(int)>, Resolved<int>, Resolved<void>,
+ RunHelper<RepeatingCallback<void(int)>, Resolved<int>, Resolved<void>,
Rejected<std::string>>::Run(BindLambdaForTesting([&](int arg) {
value = arg;
}),
diff --git a/chromium/base/task/promise/no_op_promise_executor.cc b/chromium/base/task/promise/no_op_promise_executor.cc
index 74a5bcc57c8..e168ed91007 100644
--- a/chromium/base/task/promise/no_op_promise_executor.cc
+++ b/chromium/base/task/promise/no_op_promise_executor.cc
@@ -45,15 +45,14 @@ bool NoOpPromiseExecutor::CanReject() const {
void NoOpPromiseExecutor::Execute(AbstractPromise* promise) {}
// static
-scoped_refptr<internal::AbstractPromise> NoOpPromiseExecutor::Create(
- Location from_here,
- bool can_resolve,
- bool can_reject,
- RejectPolicy reject_policy) {
- return AbstractPromise::CreateNoPrerequisitePromise(
+PassedPromise NoOpPromiseExecutor::Create(Location from_here,
+ bool can_resolve,
+ bool can_reject,
+ RejectPolicy reject_policy) {
+ return PassedPromise(AbstractPromise::CreateNoPrerequisitePromise(
from_here, reject_policy, DependentList::ConstructUnresolved(),
PromiseExecutor::Data(in_place_type_t<NoOpPromiseExecutor>(), can_resolve,
- can_reject));
+ can_reject)));
}
} // namespace internal
diff --git a/chromium/base/task/promise/no_op_promise_executor.h b/chromium/base/task/promise/no_op_promise_executor.h
index 005ee8c0130..13f8ef0891b 100644
--- a/chromium/base/task/promise/no_op_promise_executor.h
+++ b/chromium/base/task/promise/no_op_promise_executor.h
@@ -21,10 +21,10 @@ class BASE_EXPORT NoOpPromiseExecutor {
static constexpr PromiseExecutor::PrerequisitePolicy kPrerequisitePolicy =
PromiseExecutor::PrerequisitePolicy::kNever;
- static scoped_refptr<AbstractPromise> Create(Location from_here,
- bool can_resolve,
- bool can_reject,
- RejectPolicy reject_policy);
+ static PassedPromise Create(Location from_here,
+ bool can_resolve,
+ bool can_reject,
+ RejectPolicy reject_policy);
PromiseExecutor::PrerequisitePolicy GetPrerequisitePolicy() const;
bool IsCancelled() const;
diff --git a/chromium/base/task/promise/post_task_executor.h b/chromium/base/task/promise/post_task_executor.h
index c018113de63..e3882ba1712 100644
--- a/chromium/base/task/promise/post_task_executor.h
+++ b/chromium/base/task/promise/post_task_executor.h
@@ -52,10 +52,14 @@ class PostTaskExecutor {
static_assert(sizeof(CallbackBase) == sizeof(OnceCallback<ReturnType()>),
"We assume it's possible to cast from CallbackBase to "
"OnceCallback<ReturnType()>");
- OnceCallback<ReturnType()>* task =
- static_cast<OnceCallback<ReturnType()>*>(&task_);
- internal::RunHelper<OnceCallback<ReturnType()>, void, ResolveStorage,
- RejectStorage>::Run(std::move(*task), nullptr, promise);
+ // Internally RunHelper uses const RepeatingCallback<>& to avoid the
+ // binary size overhead of moving a scoped_refptr<> about. We respect
+ // the onceness of the callback and RunHelper will overwrite the callback
+ // with the result.
+ RepeatingCallback<ReturnType()>* task =
+ static_cast<RepeatingCallback<ReturnType()>*>(&task_);
+ internal::RunHelper<RepeatingCallback<ReturnType()>, void, ResolveStorage,
+ RejectStorage>::Run(*task, nullptr, promise);
}
private:
diff --git a/chromium/base/task/promise/post_task_executor_unittest.cc b/chromium/base/task/promise/post_task_executor_unittest.cc
index 8e86aaefad4..1d3fc57e4d0 100644
--- a/chromium/base/task/promise/post_task_executor_unittest.cc
+++ b/chromium/base/task/promise/post_task_executor_unittest.cc
@@ -16,9 +16,8 @@ namespace internal {
class PostTaskExecutorTest : public testing::Test {
public:
template <typename CallbackT>
- scoped_refptr<internal::AbstractPromise> CreatePostTaskPromise(
- const Location& from_here,
- CallbackT&& task) {
+ WrappedPromise CreatePostTaskPromise(const Location& from_here,
+ CallbackT&& task) {
// Extract properties from |task| callback.
using CallbackTraits = CallbackTraits<std::decay_t<CallbackT>>;
@@ -27,20 +26,20 @@ class PostTaskExecutorTest : public testing::Test {
internal::PostTaskExecutor<typename CallbackTraits::ReturnType>>(),
internal::ToCallbackBase(std::move(task)));
- return AbstractPromise::CreateNoPrerequisitePromise(
+ return WrappedPromise(AbstractPromise::CreateNoPrerequisitePromise(
from_here, RejectPolicy::kMustCatchRejection,
internal::DependentList::ConstructUnresolved(),
- std::move(executor_data));
+ std::move(executor_data)));
}
};
TEST_F(PostTaskExecutorTest, OnceClosure) {
bool run = false;
- scoped_refptr<AbstractPromise> p = CreatePostTaskPromise(
+ WrappedPromise p = CreatePostTaskPromise(
FROM_HERE, BindOnce([](bool* run) { *run = true; }, &run));
- p->Execute();
+ p.Execute();
EXPECT_TRUE(run);
}
@@ -48,20 +47,19 @@ TEST_F(PostTaskExecutorTest, OnceClosure) {
TEST_F(PostTaskExecutorTest, RepeatingClosure) {
bool run = false;
- scoped_refptr<AbstractPromise> p = CreatePostTaskPromise(
+ WrappedPromise p = CreatePostTaskPromise(
FROM_HERE, BindRepeating([](bool* run) { *run = true; }, &run));
- p->Execute();
+ p.Execute();
EXPECT_TRUE(run);
}
TEST_F(PostTaskExecutorTest, DoNothing) {
// Check it compiles and the executor doesn't crash when run.
- scoped_refptr<AbstractPromise> p =
- CreatePostTaskPromise(FROM_HERE, DoNothing());
+ WrappedPromise p = CreatePostTaskPromise(FROM_HERE, DoNothing());
- p->Execute();
+ p.Execute();
}
} // namespace internal
diff --git a/chromium/base/task/promise/promise.h b/chromium/base/task/promise/promise.h
index 4f2275ea183..467f74cb8e4 100644
--- a/chromium/base/task/promise/promise.h
+++ b/chromium/base/task/promise/promise.h
@@ -30,12 +30,12 @@ BASE_EXPORT scoped_refptr<TaskRunner> CreateTaskRunner(
// callback will be posted immediately, otherwise it has to wait.
//
// Promise<> is copyable, moveable and thread safe. Under the hood
-// internal::AbstractPromise is refcounted so retaining multiple Promises<> will
+// AbstractPromise is refcounted so retaining multiple Promises<> will
// prevent that part of the promise graph from being released.
template <typename ResolveType, typename RejectType = NoReject>
-class Promise {
+class Promise : public internal::BasePromise {
public:
- Promise() : abstract_promise_(nullptr) {}
+ Promise() = default;
static_assert(
!std::is_reference<ResolveType>::value ||
@@ -49,16 +49,17 @@ class Promise {
explicit Promise(
scoped_refptr<internal::AbstractPromise> abstract_promise) noexcept
- : abstract_promise_(std::move(abstract_promise)) {}
+ : BasePromise(std::move(abstract_promise)) {}
- ~Promise() = default;
+ // Every PostTask calls this constructor so we need to be careful to avoid
+ // unnecessary binary bloat.
+ explicit Promise(internal::PassedPromise passed_promise) noexcept
+ : BasePromise(std::move(passed_promise),
+ BasePromise::InlineConstructor()) {}
- operator bool() const { return !!abstract_promise_; }
+ ~Promise() = default;
- bool IsCancelledForTesting() const {
- DCHECK(abstract_promise_);
- return abstract_promise_->IsCanceled();
- }
+ bool IsCancelledForTesting() const { return abstract_promise_->IsCanceled(); }
// Waits until the promise has settled and if resolved it returns the resolved
// value.
@@ -75,8 +76,10 @@ class Promise {
}
DCHECK(abstract_promise_->IsResolved())
<< "Can't take resolved value, promise wasn't resolved.";
- return std::move(
- abstract_promise_->TakeValue().value().Get<Resolved<T>>()->value);
+ return std::move(abstract_promise_->TakeValue()
+ .value()
+ .template Get<Resolved<T>>()
+ ->value);
}
// Waits until the promise has settled and if rejected it returns the rejected
@@ -95,8 +98,10 @@ class Promise {
abstract_promise_->IgnoreUncaughtCatchForTesting();
DCHECK(abstract_promise_->IsRejected())
<< "Can't take rejected value, promise wasn't rejected.";
- return std::move(
- abstract_promise_->TakeValue().value().Get<Rejected<T>>()->value);
+ return std::move(abstract_promise_->TakeValue()
+ .value()
+ .template Get<Rejected<T>>()
+ ->value);
}
bool IsResolvedForTesting() const {
@@ -122,22 +127,22 @@ class Promise {
//
// |task_runner| is const-ref to avoid bloat due the destructor (which posts a
// task).
- template <typename RejectCb>
+ template <typename CatchCb>
auto CatchOn(const scoped_refptr<TaskRunner>& task_runner,
const Location& from_here,
- RejectCb on_reject) noexcept {
+ CatchCb on_reject) noexcept {
DCHECK(!on_reject.is_null());
// Extract properties from the |on_reject| callback.
- using RejectCallbackTraits = internal::CallbackTraits<RejectCb>;
- using RejectCallbackArgT = typename RejectCallbackTraits::ArgType;
+ using CatchCallbackTraits = internal::CallbackTraits<CatchCb>;
+ using CatchCallbackArgT = typename CatchCallbackTraits::ArgType;
// Compute the resolve and reject types of the returned Promise.
using ReturnedPromiseTraits =
internal::PromiseCombiner<ResolveType,
NoReject, // We've caught the reject case.
- typename RejectCallbackTraits::ResolveType,
- typename RejectCallbackTraits::RejectType>;
+ typename CatchCallbackTraits::ResolveType,
+ typename CatchCallbackTraits::RejectType>;
using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
@@ -148,13 +153,13 @@ class Promise {
static_assert(ReturnedPromiseTraits::valid,
"Ambiguous promise resolve type");
static_assert(
- internal::IsValidPromiseArg<RejectType, RejectCallbackArgT>::value ||
- std::is_void<RejectCallbackArgT>::value,
+ internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value ||
+ std::is_void<CatchCallbackArgT>::value,
"|on_reject| callback must accept Promise::RejectType or void.");
static_assert(
- !std::is_reference<RejectCallbackArgT>::value ||
- std::is_const<std::remove_reference_t<RejectCallbackArgT>>::value,
+ !std::is_reference<CatchCallbackArgT>::value ||
+ std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value,
"Google C++ Style: References in function parameters must be const.");
return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
@@ -163,7 +168,7 @@ class Promise {
internal::PromiseExecutor::Data(
in_place_type_t<internal::ThenAndCatchExecutor<
OnceClosure, // Never called.
- OnceCallback<typename RejectCallbackTraits::SignatureType>,
+ OnceCallback<typename CatchCallbackTraits::SignatureType>,
internal::NoCallback, RejectType,
Resolved<ReturnedPromiseResolveT>,
Rejected<ReturnedPromiseRejectT>>>(),
@@ -171,18 +176,59 @@ class Promise {
internal::ToCallbackBase(std::move(on_reject)))));
}
- template <typename RejectCb>
+ template <typename CatchCb>
auto CatchOn(const TaskTraits& traits,
const Location& from_here,
- RejectCb&& on_reject) noexcept {
+ CatchCb&& on_reject) noexcept {
return CatchOn(CreateTaskRunner(traits), from_here,
- std::forward<RejectCb>(on_reject));
+ std::forward<CatchCb>(on_reject));
}
- template <typename RejectCb>
- auto CatchHere(const Location& from_here, RejectCb&& on_reject) noexcept {
- return CatchOn(internal::GetCurrentSequence(), from_here,
- std::forward<RejectCb>(on_reject));
+ template <typename CatchCb>
+ auto CatchHere(const Location& from_here, CatchCb&& on_reject) noexcept {
+ DCHECK(!on_reject.is_null());
+
+ // Extract properties from the |on_reject| callback.
+ using CatchCallbackTraits = internal::CallbackTraits<CatchCb>;
+ using CatchCallbackArgT = typename CatchCallbackTraits::ArgType;
+
+ // Compute the resolve and reject types of the returned Promise.
+ using ReturnedPromiseTraits =
+ internal::PromiseCombiner<ResolveType,
+ NoReject, // We've caught the reject case.
+ typename CatchCallbackTraits::ResolveType,
+ typename CatchCallbackTraits::RejectType>;
+ using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
+ using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
+
+ static_assert(!std::is_same<NoReject, RejectType>::value,
+ "Can't catch a NoReject promise.");
+
+ // Check we wouldn't need to return Promise<Variant<...>, ...>
+ static_assert(ReturnedPromiseTraits::valid,
+ "Ambiguous promise resolve type");
+ static_assert(
+ internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value ||
+ std::is_void<CatchCallbackArgT>::value,
+ "|on_reject| callback must accept Promise::RejectType or void.");
+
+ static_assert(
+ !std::is_reference<CatchCallbackArgT>::value ||
+ std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value,
+ "Google C++ Style: References in function parameters must be const.");
+
+ return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+ ConstructHereAbstractPromiseWithSinglePrerequisite(
+ from_here, abstract_promise_.get(),
+ internal::PromiseExecutor::Data(
+ in_place_type_t<internal::ThenAndCatchExecutor<
+ OnceClosure, // Never called.
+ OnceCallback<typename CatchCallbackTraits::SignatureType>,
+ internal::NoCallback, RejectType,
+ Resolved<ReturnedPromiseResolveT>,
+ Rejected<ReturnedPromiseRejectT>>>(),
+ OnceClosure(),
+ internal::ToCallbackBase(std::move(on_reject)))));
}
// A task to execute |on_resolve| is posted on |task_runner| as soon as this
@@ -198,23 +244,22 @@ class Promise {
//
// |task_runner| is const-ref to avoid bloat due the destructor (which posts a
// task).
- template <typename ResolveCb>
+ template <typename ThenCb>
auto ThenOn(const scoped_refptr<TaskRunner>& task_runner,
const Location& from_here,
- ResolveCb on_resolve) noexcept {
+ ThenCb on_resolve) noexcept {
DCHECK(!on_resolve.is_null());
// Extract properties from the |on_resolve| callback.
- using ResolveCallbackTraits =
- internal::CallbackTraits<std::decay_t<ResolveCb>>;
- using ResolveCallbackArgT = typename ResolveCallbackTraits::ArgType;
+ using ThenCallbackTraits = internal::CallbackTraits<std::decay_t<ThenCb>>;
+ using ThenCallbackArgT = typename ThenCallbackTraits::ArgType;
// Compute the resolve and reject types of the returned Promise.
using ReturnedPromiseTraits =
internal::PromiseCombiner<NoResolve, // We've caught the resolve case.
RejectType,
- typename ResolveCallbackTraits::ResolveType,
- typename ResolveCallbackTraits::RejectType>;
+ typename ThenCallbackTraits::ResolveType,
+ typename ThenCallbackTraits::RejectType>;
using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
@@ -223,13 +268,13 @@ class Promise {
"Ambiguous promise reject type");
static_assert(
- internal::IsValidPromiseArg<ResolveType, ResolveCallbackArgT>::value ||
- std::is_void<ResolveCallbackArgT>::value,
+ internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value ||
+ std::is_void<ThenCallbackArgT>::value,
"|on_resolve| callback must accept Promise::ResolveType or void.");
static_assert(
- !std::is_reference<ResolveCallbackArgT>::value ||
- std::is_const<std::remove_reference_t<ResolveCallbackArgT>>::value,
+ !std::is_reference<ThenCallbackArgT>::value ||
+ std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value,
"Google C++ Style: References in function parameters must be const.");
return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
@@ -237,7 +282,7 @@ class Promise {
task_runner, from_here, abstract_promise_.get(),
internal::PromiseExecutor::Data(
in_place_type_t<internal::ThenAndCatchExecutor<
- OnceCallback<typename ResolveCallbackTraits::SignatureType>,
+ OnceCallback<typename ThenCallbackTraits::SignatureType>,
OnceClosure, ResolveType, internal::NoCallback,
Resolved<ReturnedPromiseResolveT>,
Rejected<ReturnedPromiseRejectT>>>(),
@@ -245,18 +290,56 @@ class Promise {
OnceClosure())));
}
- template <typename ResolveCb>
+ template <typename ThenCb>
auto ThenOn(const TaskTraits& traits,
const Location& from_here,
- ResolveCb&& on_resolve) noexcept {
+ ThenCb&& on_resolve) noexcept {
return ThenOn(CreateTaskRunner(traits), from_here,
- std::forward<ResolveCb>(on_resolve));
+ std::forward<ThenCb>(on_resolve));
}
- template <typename ResolveCb>
- auto ThenHere(const Location& from_here, ResolveCb&& on_resolve) noexcept {
- return ThenOn(internal::GetCurrentSequence(), from_here,
- std::forward<ResolveCb>(on_resolve));
+ template <typename ThenCb>
+ auto ThenHere(const Location& from_here, ThenCb&& on_resolve) noexcept {
+ DCHECK(!on_resolve.is_null());
+
+ // Extract properties from the |on_resolve| callback.
+ using ThenCallbackTraits = internal::CallbackTraits<std::decay_t<ThenCb>>;
+ using ThenCallbackArgT = typename ThenCallbackTraits::ArgType;
+
+ // Compute the resolve and reject types of the returned Promise.
+ using ReturnedPromiseTraits =
+ internal::PromiseCombiner<NoResolve, // We've caught the resolve case.
+ RejectType,
+ typename ThenCallbackTraits::ResolveType,
+ typename ThenCallbackTraits::RejectType>;
+ using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
+ using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
+
+ // Check we wouldn't need to return Promise<..., Variant<...>>
+ static_assert(ReturnedPromiseTraits::valid,
+ "Ambiguous promise reject type");
+
+ static_assert(
+ internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value ||
+ std::is_void<ThenCallbackArgT>::value,
+ "|on_resolve| callback must accept Promise::ResolveType or void.");
+
+ static_assert(
+ !std::is_reference<ThenCallbackArgT>::value ||
+ std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value,
+ "Google C++ Style: References in function parameters must be const.");
+
+ return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+ ConstructHereAbstractPromiseWithSinglePrerequisite(
+ from_here, abstract_promise_.get(),
+ internal::PromiseExecutor::Data(
+ in_place_type_t<internal::ThenAndCatchExecutor<
+ OnceCallback<typename ThenCallbackTraits::SignatureType>,
+ OnceClosure, ResolveType, internal::NoCallback,
+ Resolved<ReturnedPromiseResolveT>,
+ Rejected<ReturnedPromiseRejectT>>>(),
+ internal::ToCallbackBase(std::move(on_resolve)),
+ OnceClosure())));
}
// A task to execute |on_reject| is posted on |task_runner| as soon as this
@@ -279,26 +362,26 @@ class Promise {
//
// |task_runner| is const-ref to avoid bloat due the destructor (which posts a
// task).
- template <typename ResolveCb, typename RejectCb>
+ template <typename ThenCb, typename CatchCb>
auto ThenOn(const scoped_refptr<TaskRunner>& task_runner,
const Location& from_here,
- ResolveCb on_resolve,
- RejectCb on_reject) noexcept {
+ ThenCb on_resolve,
+ CatchCb on_reject) noexcept {
DCHECK(!on_resolve.is_null());
DCHECK(!on_reject.is_null());
// Extract properties from the |on_resolve| and |on_reject| callbacks.
- using ResolveCallbackTraits = internal::CallbackTraits<ResolveCb>;
- using RejectCallbackTraits = internal::CallbackTraits<RejectCb>;
- using ResolveCallbackArgT = typename ResolveCallbackTraits::ArgType;
- using RejectCallbackArgT = typename RejectCallbackTraits::ArgType;
+ using ThenCallbackTraits = internal::CallbackTraits<ThenCb>;
+ using CatchCallbackTraits = internal::CallbackTraits<CatchCb>;
+ using ThenCallbackArgT = typename ThenCallbackTraits::ArgType;
+ using CatchCallbackArgT = typename CatchCallbackTraits::ArgType;
// Compute the resolve and reject types of the returned Promise.
using ReturnedPromiseTraits =
- internal::PromiseCombiner<typename ResolveCallbackTraits::ResolveType,
- typename ResolveCallbackTraits::RejectType,
- typename RejectCallbackTraits::ResolveType,
- typename RejectCallbackTraits::RejectType>;
+ internal::PromiseCombiner<typename ThenCallbackTraits::ResolveType,
+ typename ThenCallbackTraits::RejectType,
+ typename CatchCallbackTraits::ResolveType,
+ typename CatchCallbackTraits::RejectType>;
using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
@@ -310,23 +393,23 @@ class Promise {
"compatible types.");
static_assert(
- internal::IsValidPromiseArg<ResolveType, ResolveCallbackArgT>::value ||
- std::is_void<ResolveCallbackArgT>::value,
+ internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value ||
+ std::is_void<ThenCallbackArgT>::value,
"|on_resolve| callback must accept Promise::ResolveType or void.");
static_assert(
- internal::IsValidPromiseArg<RejectType, RejectCallbackArgT>::value ||
- std::is_void<RejectCallbackArgT>::value,
+ internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value ||
+ std::is_void<CatchCallbackArgT>::value,
"|on_reject| callback must accept Promise::RejectType or void.");
static_assert(
- !std::is_reference<ResolveCallbackArgT>::value ||
- std::is_const<std::remove_reference_t<ResolveCallbackArgT>>::value,
+ !std::is_reference<ThenCallbackArgT>::value ||
+ std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value,
"Google C++ Style: References in function parameters must be const.");
static_assert(
- !std::is_reference<RejectCallbackArgT>::value ||
- std::is_const<std::remove_reference_t<RejectCallbackArgT>>::value,
+ !std::is_reference<CatchCallbackArgT>::value ||
+ std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value,
"Google C++ Style: References in function parameters must be const.");
return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
@@ -334,31 +417,84 @@ class Promise {
task_runner, from_here, abstract_promise_.get(),
internal::PromiseExecutor::Data(
in_place_type_t<internal::ThenAndCatchExecutor<
- OnceCallback<typename ResolveCallbackTraits::SignatureType>,
- OnceCallback<typename RejectCallbackTraits::SignatureType>,
+ OnceCallback<typename ThenCallbackTraits::SignatureType>,
+ OnceCallback<typename CatchCallbackTraits::SignatureType>,
ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>,
Rejected<ReturnedPromiseRejectT>>>(),
internal::ToCallbackBase(std::move(on_resolve)),
internal::ToCallbackBase(std::move(on_reject)))));
}
- template <typename ResolveCb, typename RejectCb>
+ template <typename ThenCb, typename CatchCb>
auto ThenOn(const TaskTraits& traits,
const Location& from_here,
- ResolveCb on_resolve,
- RejectCb on_reject) noexcept {
+ ThenCb on_resolve,
+ CatchCb on_reject) noexcept {
return ThenOn(CreateTaskRunner(traits), from_here,
- std::forward<ResolveCb>(on_resolve),
- std::forward<RejectCb>(on_reject));
+ std::forward<ThenCb>(on_resolve),
+ std::forward<CatchCb>(on_reject));
}
- template <typename ResolveCb, typename RejectCb>
+ template <typename ThenCb, typename CatchCb>
auto ThenHere(const Location& from_here,
- ResolveCb on_resolve,
- RejectCb on_reject) noexcept {
- return ThenOn(internal::GetCurrentSequence(), from_here,
- std::forward<ResolveCb>(on_resolve),
- std::forward<RejectCb>(on_reject));
+ ThenCb on_resolve,
+ CatchCb on_reject) noexcept {
+ DCHECK(!on_resolve.is_null());
+ DCHECK(!on_reject.is_null());
+
+ // Extract properties from the |on_resolve| and |on_reject| callbacks.
+ using ThenCallbackTraits = internal::CallbackTraits<ThenCb>;
+ using CatchCallbackTraits = internal::CallbackTraits<CatchCb>;
+ using ThenCallbackArgT = typename ThenCallbackTraits::ArgType;
+ using CatchCallbackArgT = typename CatchCallbackTraits::ArgType;
+
+ // Compute the resolve and reject types of the returned Promise.
+ using ReturnedPromiseTraits =
+ internal::PromiseCombiner<typename ThenCallbackTraits::ResolveType,
+ typename ThenCallbackTraits::RejectType,
+ typename CatchCallbackTraits::ResolveType,
+ typename CatchCallbackTraits::RejectType>;
+ using ReturnedPromiseResolveT = typename ReturnedPromiseTraits::ResolveType;
+ using ReturnedPromiseRejectT = typename ReturnedPromiseTraits::RejectType;
+
+ static_assert(!std::is_same<NoReject, RejectType>::value,
+ "Can't catch a NoReject promise.");
+
+ static_assert(ReturnedPromiseTraits::valid,
+ "|on_resolve| callback and |on_resolve| callback must return "
+ "compatible types.");
+
+ static_assert(
+ internal::IsValidPromiseArg<ResolveType, ThenCallbackArgT>::value ||
+ std::is_void<ThenCallbackArgT>::value,
+ "|on_resolve| callback must accept Promise::ResolveType or void.");
+
+ static_assert(
+ internal::IsValidPromiseArg<RejectType, CatchCallbackArgT>::value ||
+ std::is_void<CatchCallbackArgT>::value,
+ "|on_reject| callback must accept Promise::RejectType or void.");
+
+ static_assert(
+ !std::is_reference<ThenCallbackArgT>::value ||
+ std::is_const<std::remove_reference_t<ThenCallbackArgT>>::value,
+ "Google C++ Style: References in function parameters must be const.");
+
+ static_assert(
+ !std::is_reference<CatchCallbackArgT>::value ||
+ std::is_const<std::remove_reference_t<CatchCallbackArgT>>::value,
+ "Google C++ Style: References in function parameters must be const.");
+
+ return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+ ConstructHereAbstractPromiseWithSinglePrerequisite(
+ from_here, abstract_promise_.get(),
+ internal::PromiseExecutor::Data(
+ in_place_type_t<internal::ThenAndCatchExecutor<
+ OnceCallback<typename ThenCallbackTraits::SignatureType>,
+ OnceCallback<typename CatchCallbackTraits::SignatureType>,
+ ResolveType, RejectType, Resolved<ReturnedPromiseResolveT>,
+ Rejected<ReturnedPromiseRejectT>>>(),
+ internal::ToCallbackBase(std::move(on_resolve)),
+ internal::ToCallbackBase(std::move(on_reject)))));
}
// A task to execute |finally_callback| on |task_runner| is posted after the
@@ -405,8 +541,24 @@ class Promise {
template <typename FinallyCb>
auto FinallyHere(const Location& from_here,
FinallyCb finally_callback) noexcept {
- return FinallyOn(internal::GetCurrentSequence(), from_here,
- std::move(finally_callback));
+ // Extract properties from |finally_callback| callback.
+ using CallbackTraits = internal::CallbackTraits<FinallyCb>;
+ using ReturnedPromiseResolveT = typename CallbackTraits::ResolveType;
+ using ReturnedPromiseRejectT = typename CallbackTraits::RejectType;
+
+ using CallbackArgT = typename CallbackTraits::ArgType;
+ static_assert(std::is_void<CallbackArgT>::value,
+ "|finally_callback| callback must have no arguments");
+
+ return Promise<ReturnedPromiseResolveT, ReturnedPromiseRejectT>(
+ ConstructHereAbstractPromiseWithSinglePrerequisite(
+ from_here, abstract_promise_.get(),
+ internal::PromiseExecutor::Data(
+ in_place_type_t<internal::FinallyExecutor<
+ OnceCallback<typename CallbackTraits::ReturnType()>,
+ Resolved<ReturnedPromiseResolveT>,
+ Rejected<ReturnedPromiseRejectT>>>(),
+ internal::ToCallbackBase(std::move(finally_callback)))));
}
template <typename... Args>
@@ -442,8 +594,6 @@ class Promise {
nullptr, from_here, nullptr, RejectPolicy::kMustCatchRejection,
internal::DependentList::ConstructResolved(),
std::move(executor_data)));
- promise->emplace(in_place_type_t<Rejected<RejectType>>(),
- std::forward<Args>(args)...);
return Promise<ResolveType, RejectType>(std::move(promise));
}
@@ -454,6 +604,11 @@ class Promise {
abstract_promise_->IgnoreUncaughtCatchForTesting();
}
+ const scoped_refptr<internal::AbstractPromise>& GetScopedRefptrForTesting()
+ const {
+ return abstract_promise_;
+ }
+
private:
template <typename A, typename B>
friend class Promise;
@@ -471,8 +626,6 @@ class Promise {
template <typename A, typename B>
friend class ManualPromiseResolver;
-
- scoped_refptr<internal::AbstractPromise> abstract_promise_;
};
// Used for manually resolving and rejecting a Promise. This is for
@@ -624,8 +777,8 @@ class Promises {
std::vector<internal::DependentList::Node> prerequisite_list(
sizeof...(promises));
int i = 0;
- for (auto&& p : {promises.abstract_promise_...}) {
- prerequisite_list[i++].SetPrerequisite(p.get());
+ for (auto&& p : {promises.abstract_promise_.get()...}) {
+ prerequisite_list[i++].SetPrerequisite(p);
}
internal::PromiseExecutor::Data executor_data(
diff --git a/chromium/base/task/promise/promise_unittest.cc b/chromium/base/task/promise/promise_unittest.cc
index bb3dda41cb3..d81adacb1a0 100644
--- a/chromium/base/task/promise/promise_unittest.cc
+++ b/chromium/base/task/promise/promise_unittest.cc
@@ -106,6 +106,86 @@ TEST(PromiseMemoryLeakTest, TargetTaskRunnerClearsTasks) {
EXPECT_TRUE(delete_reply_flag);
}
+TEST(PromiseMemoryLeakTest, GetResolveCallbackNeverRun) {
+ test::TaskEnvironment task_environment_;
+ OnceCallback<void(int)> cb;
+ MockObject mock_object;
+ bool delete_task_flag = false;
+
+ {
+ ManualPromiseResolver<int> p(FROM_HERE);
+ cb = p.GetResolveCallback();
+
+ p.promise().ThenHere(
+ FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object),
+ MakeRefCounted<ObjectToDelete>(&delete_task_flag)));
+ }
+
+ EXPECT_FALSE(delete_task_flag);
+ cb = OnceCallback<void(int)>();
+ EXPECT_TRUE(delete_task_flag);
+}
+
+TEST(PromiseMemoryLeakTest, GetRepeatingResolveCallbackNeverRun) {
+ test::TaskEnvironment task_environment_;
+ RepeatingCallback<void(int)> cb;
+ MockObject mock_object;
+ bool delete_task_flag = false;
+
+ {
+ ManualPromiseResolver<int> p(FROM_HERE);
+ cb = p.GetRepeatingResolveCallback();
+
+ p.promise().ThenHere(
+ FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object),
+ MakeRefCounted<ObjectToDelete>(&delete_task_flag)));
+ }
+
+ EXPECT_FALSE(delete_task_flag);
+ cb = RepeatingCallback<void(int)>();
+ EXPECT_TRUE(delete_task_flag);
+}
+
+TEST(PromiseMemoryLeakTest, GetRejectCallbackNeverRun) {
+ test::TaskEnvironment task_environment_;
+ OnceCallback<void(int)> cb;
+ MockObject mock_object;
+ bool delete_task_flag = false;
+
+ {
+ ManualPromiseResolver<void, int> p(FROM_HERE);
+ cb = p.GetRejectCallback();
+
+ p.promise().CatchHere(
+ FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object),
+ MakeRefCounted<ObjectToDelete>(&delete_task_flag)));
+ }
+
+ EXPECT_FALSE(delete_task_flag);
+ cb = OnceCallback<void(int)>();
+ EXPECT_TRUE(delete_task_flag);
+}
+
+TEST(PromiseMemoryLeakTest, GetRepeatingRejectCallbackNeverRun) {
+ test::TaskEnvironment task_environment_;
+ RepeatingCallback<void(int)> cb;
+ MockObject mock_object;
+ bool delete_task_flag = false;
+
+ {
+ ManualPromiseResolver<void, int> p(FROM_HERE);
+ cb = p.GetRepeatingRejectCallback();
+
+ p.promise().CatchHere(
+ FROM_HERE, BindOnce(&MockObject::Task, Unretained(&mock_object),
+ MakeRefCounted<ObjectToDelete>(&delete_task_flag)));
+ }
+
+ EXPECT_FALSE(delete_task_flag);
+ cb = RepeatingCallback<void(int)>();
+ EXPECT_TRUE(delete_task_flag);
+}
+
TEST_F(PromiseTest, GetResolveCallbackThen) {
ManualPromiseResolver<int> p(FROM_HERE);
p.GetResolveCallback().Run(123);
@@ -1602,13 +1682,17 @@ TEST_F(PromiseTest, MoveOnlyTypeMultipleCatchesNotAllowed) {
auto p = Promise<void, std::unique_ptr<int>>::CreateRejected(
FROM_HERE, std::make_unique<int>(123));
- p.CatchHere(FROM_HERE,
- BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
+ auto r = p.CatchHere(
+ FROM_HERE, BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
EXPECT_DCHECK_DEATH({
p.CatchHere(FROM_HERE,
BindOnce([](std::unique_ptr<int> i) { EXPECT_EQ(123, *i); }));
});
+
+ // TODO(alexclarke): Temporary, remove when SequenceManager handles promises
+ // natively.
+ r.GetScopedRefptrForTesting()->OnCanceled();
#endif
}
diff --git a/chromium/base/task/promise/promise_value.h b/chromium/base/task/promise/promise_value.h
index 32ec758716d..3b1cfc43464 100644
--- a/chromium/base/task/promise/promise_value.h
+++ b/chromium/base/task/promise/promise_value.h
@@ -40,9 +40,6 @@ struct Resolved {
"Can't have Resolved<NoResolve>");
}
- Resolved(const Resolved& other) = default;
- Resolved(Resolved&& other) = default;
-
// Conversion constructor accepts any arguments except Resolved<T>.
template <
typename... Args,
@@ -75,9 +72,6 @@ struct Rejected {
"Can't have Rejected<NoReject>");
}
- Rejected(const Rejected& other) = default;
- Rejected(Rejected&& other) = default;
-
// Conversion constructor accepts any arguments except Rejected<T>.
template <
typename... Args,
diff --git a/chromium/base/task/promise/then_and_catch_executor.cc b/chromium/base/task/promise/then_and_catch_executor.cc
index 5f7f8f09ad0..5eb38e99b78 100644
--- a/chromium/base/task/promise/then_and_catch_executor.cc
+++ b/chromium/base/task/promise/then_and_catch_executor.cc
@@ -8,14 +8,14 @@ namespace base {
namespace internal {
bool ThenAndCatchExecutorCommon::IsCancelled() const {
- if (!resolve_callback_.is_null()) {
+ if (!then_callback_.is_null()) {
// If there is both a resolve and a reject executor they must be canceled
// at the same time.
- DCHECK(reject_callback_.is_null() ||
- reject_callback_.IsCancelled() == resolve_callback_.IsCancelled());
- return resolve_callback_.IsCancelled();
+ DCHECK(catch_callback_.is_null() ||
+ catch_callback_.IsCancelled() == then_callback_.IsCancelled());
+ return then_callback_.IsCancelled();
}
- return reject_callback_.IsCancelled();
+ return catch_callback_.IsCancelled();
}
void ThenAndCatchExecutorCommon::Execute(AbstractPromise* promise,
@@ -23,16 +23,16 @@ void ThenAndCatchExecutorCommon::Execute(AbstractPromise* promise,
ExecuteCallback execute_catch) {
AbstractPromise* prerequisite = promise->GetOnlyPrerequisite();
if (prerequisite->IsResolved()) {
- if (ProcessNullCallback(resolve_callback_, prerequisite, promise))
+ if (ProcessNullCallback(then_callback_, prerequisite, promise))
return;
- execute_then(prerequisite, promise, &resolve_callback_);
+ execute_then(prerequisite, promise, &then_callback_);
} else {
DCHECK(prerequisite->IsRejected());
- if (ProcessNullCallback(reject_callback_, prerequisite, promise))
+ if (ProcessNullCallback(catch_callback_, prerequisite, promise))
return;
- execute_catch(prerequisite, promise, &reject_callback_);
+ execute_catch(prerequisite, promise, &catch_callback_);
}
}
diff --git a/chromium/base/task/promise/then_and_catch_executor.h b/chromium/base/task/promise/then_and_catch_executor.h
index 56d30ba2a11..7668fc1fbcb 100644
--- a/chromium/base/task/promise/then_and_catch_executor.h
+++ b/chromium/base/task/promise/then_and_catch_executor.h
@@ -17,11 +17,11 @@ namespace internal {
// Exists to reduce template bloat.
class BASE_EXPORT ThenAndCatchExecutorCommon {
public:
- ThenAndCatchExecutorCommon(internal::CallbackBase&& resolve_executor,
- internal::CallbackBase&& reject_executor) noexcept
- : resolve_callback_(std::move(resolve_executor)),
- reject_callback_(std::move(reject_executor)) {
- DCHECK(!resolve_callback_.is_null() || !reject_callback_.is_null());
+ ThenAndCatchExecutorCommon(internal::CallbackBase&& then_callback,
+ internal::CallbackBase&& catch_callback) noexcept
+ : then_callback_(std::move(then_callback)),
+ catch_callback_(std::move(catch_callback)) {
+ DCHECK(!then_callback_.is_null() || !catch_callback_.is_null());
}
~ThenAndCatchExecutorCommon() = default;
@@ -44,24 +44,23 @@ class BASE_EXPORT ThenAndCatchExecutorCommon {
AbstractPromise* arg,
AbstractPromise* result);
- CallbackBase resolve_callback_;
- CallbackBase reject_callback_;
+ CallbackBase then_callback_;
+ CallbackBase catch_callback_;
};
// Tag signals no callback which is used to eliminate dead code.
struct NoCallback {};
-template <typename ResolveOnceCallback,
- typename RejectOnceCallback,
+template <typename ThenOnceCallback,
+ typename CatchOnceCallback,
typename ArgResolve,
typename ArgReject,
typename ResolveStorage,
typename RejectStorage>
class ThenAndCatchExecutor {
public:
- using ResolveReturnT =
- typename CallbackTraits<ResolveOnceCallback>::ReturnType;
- using RejectReturnT = typename CallbackTraits<RejectOnceCallback>::ReturnType;
+ using ThenReturnT = typename CallbackTraits<ThenOnceCallback>::ReturnType;
+ using CatchReturnT = typename CallbackTraits<CatchOnceCallback>::ReturnType;
using PrerequisiteCouldResolve =
std::integral_constant<bool,
!std::is_same<ArgResolve, NoCallback>::value>;
@@ -69,8 +68,8 @@ class ThenAndCatchExecutor {
std::integral_constant<bool, !std::is_same<ArgReject, NoCallback>::value>;
ThenAndCatchExecutor(CallbackBase&& resolve_callback,
- CallbackBase&& reject_callback) noexcept
- : common_(std::move(resolve_callback), std::move(reject_callback)) {}
+ CallbackBase&& catch_callback) noexcept
+ : common_(std::move(resolve_callback), std::move(catch_callback)) {}
bool IsCancelled() const { return common_.IsCancelled(); }
@@ -85,29 +84,29 @@ class ThenAndCatchExecutor {
#if DCHECK_IS_ON()
PromiseExecutor::ArgumentPassingType ResolveArgumentPassingType() const {
- return common_.resolve_callback_.is_null()
+ return common_.then_callback_.is_null()
? PromiseExecutor::ArgumentPassingType::kNoCallback
- : CallbackTraits<ResolveOnceCallback>::argument_passing_type;
+ : CallbackTraits<ThenOnceCallback>::argument_passing_type;
}
PromiseExecutor::ArgumentPassingType RejectArgumentPassingType() const {
- return common_.reject_callback_.is_null()
+ return common_.catch_callback_.is_null()
? PromiseExecutor::ArgumentPassingType::kNoCallback
- : CallbackTraits<RejectOnceCallback>::argument_passing_type;
+ : CallbackTraits<CatchOnceCallback>::argument_passing_type;
}
bool CanResolve() const {
- return (!common_.resolve_callback_.is_null() &&
- PromiseCallbackTraits<ResolveReturnT>::could_resolve) ||
- (!common_.reject_callback_.is_null() &&
- PromiseCallbackTraits<RejectReturnT>::could_resolve);
+ return (!common_.then_callback_.is_null() &&
+ PromiseCallbackTraits<ThenReturnT>::could_resolve) ||
+ (!common_.catch_callback_.is_null() &&
+ PromiseCallbackTraits<CatchReturnT>::could_resolve);
}
bool CanReject() const {
- return (!common_.resolve_callback_.is_null() &&
- PromiseCallbackTraits<ResolveReturnT>::could_reject) ||
- (!common_.reject_callback_.is_null() &&
- PromiseCallbackTraits<RejectReturnT>::could_reject);
+ return (!common_.then_callback_.is_null() &&
+ PromiseCallbackTraits<ThenReturnT>::could_reject) ||
+ (!common_.catch_callback_.is_null() &&
+ PromiseCallbackTraits<CatchReturnT>::could_reject);
}
#endif
@@ -121,8 +120,8 @@ class ThenAndCatchExecutor {
static void ExecuteCatch(AbstractPromise* prerequisite,
AbstractPromise* promise,
- CallbackBase* reject_callback) {
- ExecuteCatchInternal(prerequisite, promise, reject_callback,
+ CallbackBase* catch_callback) {
+ ExecuteCatchInternal(prerequisite, promise, catch_callback,
PrerequisiteCouldReject());
}
@@ -130,10 +129,16 @@ class ThenAndCatchExecutor {
AbstractPromise* promise,
CallbackBase* resolve_callback,
std::true_type can_resolve) {
- RunHelper<ResolveOnceCallback, Resolved<ArgResolve>, ResolveStorage,
- RejectStorage>::
- Run(std::move(*static_cast<ResolveOnceCallback*>(resolve_callback)),
- prerequisite, promise);
+ // Internally RunHelper uses const RepeatingCallback<>& to avoid the
+ // binary size overhead of moving a scoped_refptr<> about. We respect
+ // the onceness of the callback and RunHelper will overwrite the callback
+ // with the result.
+ using RepeatingThenCB =
+ typename ToRepeatingCallback<ThenOnceCallback>::value;
+ RunHelper<
+ RepeatingThenCB, Resolved<ArgResolve>, ResolveStorage,
+ RejectStorage>::Run(*static_cast<RepeatingThenCB*>(resolve_callback),
+ prerequisite, promise);
}
static void ExecuteThenInternal(AbstractPromise* prerequisite,
@@ -145,17 +150,23 @@ class ThenAndCatchExecutor {
static void ExecuteCatchInternal(AbstractPromise* prerequisite,
AbstractPromise* promise,
- CallbackBase* reject_callback,
+ CallbackBase* catch_callback,
std::true_type can_reject) {
- RunHelper<RejectOnceCallback, Rejected<ArgReject>, ResolveStorage,
- RejectStorage>::
- Run(std::move(*static_cast<RejectOnceCallback*>(reject_callback)),
- prerequisite, promise);
+ // Internally RunHelper uses const RepeatingCallback<>& to avoid the
+ // binary size overhead of moving a scoped_refptr<> about. We respect
+ // the onceness of the callback and RunHelper will overwrite the callback
+ // with the result.
+ using RepeatingCatchCB =
+ typename ToRepeatingCallback<CatchOnceCallback>::value;
+ RunHelper<
+ RepeatingCatchCB, Rejected<ArgReject>, ResolveStorage,
+ RejectStorage>::Run(*static_cast<RepeatingCatchCB*>(catch_callback),
+ prerequisite, promise);
}
static void ExecuteCatchInternal(AbstractPromise* prerequisite,
AbstractPromise* promise,
- CallbackBase* reject_callback,
+ CallbackBase* catch_callback,
std::false_type can_reject) {
// |prerequisite| can't reject so don't generate dead code.
}
diff --git a/chromium/base/task/sequence_manager/sequence_manager.h b/chromium/base/task/sequence_manager/sequence_manager.h
index cd2163d7793..6e1cb0b752b 100644
--- a/chromium/base/task/sequence_manager/sequence_manager.h
+++ b/chromium/base/task/sequence_manager/sequence_manager.h
@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/message_loop/message_pump_type.h"
#include "base/message_loop/timer_slack.h"
+#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/task/sequence_manager/task_queue_impl.h"
#include "base/task/sequence_manager/task_time_observer.h"
@@ -19,6 +20,7 @@
namespace base {
class MessagePump;
+class TaskObserver;
namespace sequence_manager {
@@ -102,6 +104,11 @@ class BASE_EXPORT SequenceManager {
kNone,
kEnabled,
kEnabledWithBacktrace,
+
+ // Logs high priority tasks and the lower priority tasks they skipped
+ // past. Useful for debugging test failures caused by scheduler policy
+ // changes.
+ kReorderedOnly,
};
TaskLogging task_execution_logging = TaskLogging::kNone;
@@ -141,6 +148,10 @@ class BASE_EXPORT SequenceManager {
// performs this initialization automatically.
virtual void BindToCurrentThread() = 0;
+ // Returns the task runner the current task was posted on. Returns null if no
+ // task is currently running. Must be called on the bound thread.
+ virtual scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() = 0;
+
// Finishes the initialization for a SequenceManager created via
// CreateUnboundSequenceManagerWithPump(). Must not be called in any other
// circumstances. The ownership of the pump is transferred to SequenceManager.
@@ -238,6 +249,14 @@ class BASE_EXPORT SequenceManager {
virtual std::unique_ptr<NativeWorkHandle> OnNativeWorkPending(
TaskQueue::QueuePriority priority) = 0;
+ // Adds an observer which reports task execution. Can only be called on the
+ // same thread that |this| is running on.
+ virtual void AddTaskObserver(TaskObserver* task_observer) = 0;
+
+ // Removes an observer which reports task execution. Can only be called on the
+ // same thread that |this| is running on.
+ virtual void RemoveTaskObserver(TaskObserver* task_observer) = 0;
+
protected:
virtual std::unique_ptr<internal::TaskQueueImpl> CreateTaskQueueImpl(
const TaskQueue::Spec& spec) = 0;
diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl.cc b/chromium/base/task/sequence_manager/sequence_manager_impl.cc
index 26977d6687b..7eea742aeb2 100644
--- a/chromium/base/task/sequence_manager/sequence_manager_impl.cc
+++ b/chromium/base/task/sequence_manager/sequence_manager_impl.cc
@@ -320,6 +320,16 @@ void SequenceManagerImpl::BindToCurrentThread(
BindToMessagePump(std::move(pump));
}
+scoped_refptr<SequencedTaskRunner>
+SequenceManagerImpl::GetTaskRunnerForCurrentTask() {
+ DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
+ if (main_thread_only().task_execution_stack.empty())
+ return nullptr;
+ return main_thread_only()
+ .task_execution_stack.back()
+ .pending_task.task_runner;
+}
+
void SequenceManagerImpl::CompleteInitializationOnBoundThread() {
controller_->AddNestingObserver(this);
main_thread_only().nesting_observer_registered_ = true;
@@ -488,10 +498,10 @@ const char* RunTaskTraceNameForPriority(TaskQueue::QueuePriority priority) {
} // namespace
-Optional<Task> SequenceManagerImpl::TakeTask() {
- Optional<Task> task = TakeTaskImpl();
+Task* SequenceManagerImpl::SelectNextTask() {
+ Task* task = SelectNextTaskImpl();
if (!task)
- return base::nullopt;
+ return nullptr;
ExecutingTask& executing_task =
*main_thread_only().task_execution_stack.rbegin();
@@ -503,62 +513,70 @@ Optional<Task> SequenceManagerImpl::TakeTask() {
"task_type", executing_task.task_type);
TRACE_EVENT_BEGIN0("sequence_manager", executing_task.task_queue_name);
-#if DCHECK_IS_ON() && !defined(OS_NACL)
- LogTaskDebugInfo(executing_task);
-#endif
-
return task;
}
#if DCHECK_IS_ON() && !defined(OS_NACL)
void SequenceManagerImpl::LogTaskDebugInfo(
- const ExecutingTask& executing_task) {
+ const WorkQueue* selected_work_queue) const {
+ const Task* task = selected_work_queue->GetFrontTask();
switch (settings_.task_execution_logging) {
case Settings::TaskLogging::kNone:
break;
case Settings::TaskLogging::kEnabled:
- LOG(INFO) << "#"
- << static_cast<uint64_t>(
- executing_task.pending_task.enqueue_order())
- << " " << executing_task.task_queue_name
- << (executing_task.pending_task.cross_thread_
- ? " Run crossthread "
- : " Run ")
- << executing_task.pending_task.posted_from.ToString();
+ LOG(INFO) << "#" << static_cast<uint64_t>(task->enqueue_order()) << " "
+ << selected_work_queue->task_queue()->GetName()
+ << (task->cross_thread_ ? " Run crossthread " : " Run ")
+ << task->posted_from.ToString();
break;
case Settings::TaskLogging::kEnabledWithBacktrace: {
std::array<const void*, PendingTask::kTaskBacktraceLength + 1> task_trace;
- task_trace[0] = executing_task.pending_task.posted_from.program_counter();
- std::copy(executing_task.pending_task.task_backtrace.begin(),
- executing_task.pending_task.task_backtrace.end(),
+ task_trace[0] = task->posted_from.program_counter();
+ std::copy(task->task_backtrace.begin(), task->task_backtrace.end(),
task_trace.begin() + 1);
size_t length = 0;
while (length < task_trace.size() && task_trace[length])
++length;
if (length == 0)
break;
- LOG(INFO) << "#"
- << static_cast<uint64_t>(
- executing_task.pending_task.enqueue_order())
- << " " << executing_task.task_queue_name
- << (executing_task.pending_task.cross_thread_
- ? " Run crossthread "
- : " Run ")
+ LOG(INFO) << "#" << static_cast<uint64_t>(task->enqueue_order()) << " "
+ << selected_work_queue->task_queue()->GetName()
+ << (task->cross_thread_ ? " Run crossthread " : " Run ")
<< debug::StackTrace(task_trace.data(), length);
break;
}
+
+ case Settings::TaskLogging::kReorderedOnly: {
+ std::vector<const Task*> skipped_tasks;
+ main_thread_only().selector.CollectSkippedOverLowerPriorityTasks(
+ selected_work_queue, &skipped_tasks);
+
+ if (skipped_tasks.empty())
+ break;
+
+ LOG(INFO) << "#" << static_cast<uint64_t>(task->enqueue_order()) << " "
+ << selected_work_queue->task_queue()->GetName()
+ << (task->cross_thread_ ? " Run crossthread " : " Run ")
+ << task->posted_from.ToString();
+
+ for (const Task* skipped_task : skipped_tasks) {
+ LOG(INFO) << "# (skipped over) "
+ << static_cast<uint64_t>(skipped_task->enqueue_order()) << " "
+ << skipped_task->posted_from.ToString();
+ }
+ }
}
}
#endif // DCHECK_IS_ON() && !defined(OS_NACL)
-Optional<Task> SequenceManagerImpl::TakeTaskImpl() {
+Task* SequenceManagerImpl::SelectNextTaskImpl() {
CHECK(Validate());
DCHECK_CALLED_ON_VALID_THREAD(associated_thread_->thread_checker);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("sequence_manager"),
- "SequenceManagerImpl::TakeTask");
+ "SequenceManagerImpl::SelectNextTask");
ReloadEmptyWorkQueues();
LazyNow lazy_now(controller_->GetClock());
@@ -579,7 +597,7 @@ Optional<Task> SequenceManagerImpl::TakeTaskImpl() {
this, AsValueWithSelectorResult(work_queue, /* force_verbose */ false));
if (!work_queue)
- return nullopt;
+ return nullptr;
// If the head task was canceled, remove it and run the selector again.
if (UNLIKELY(work_queue->RemoveAllCanceledTasksFromFront()))
@@ -604,9 +622,13 @@ Optional<Task> SequenceManagerImpl::TakeTaskImpl() {
work_queue->task_queue()->GetQueuePriority()))) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("sequence_manager"),
"SequenceManager.YieldToNative");
- return nullopt;
+ return nullptr;
}
+#if DCHECK_IS_ON() && !defined(OS_NACL)
+ LogTaskDebugInfo(work_queue);
+#endif // DCHECK_IS_ON() && !defined(OS_NACL)
+
main_thread_only().task_execution_stack.emplace_back(
work_queue->TakeTaskFromWorkQueue(), work_queue->task_queue(),
InitializeTaskTiming(work_queue->task_queue()));
@@ -615,7 +637,7 @@ Optional<Task> SequenceManagerImpl::TakeTaskImpl() {
*main_thread_only().task_execution_stack.rbegin();
NotifyWillProcessTask(&executing_task, &lazy_now);
- return std::move(executing_task.pending_task);
+ return &executing_task.pending_task;
}
}
diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl.h b/chromium/base/task/sequence_manager/sequence_manager_impl.h
index a8c1659f52a..1ee7944a06e 100644
--- a/chromium/base/task/sequence_manager/sequence_manager_impl.h
+++ b/chromium/base/task/sequence_manager/sequence_manager_impl.h
@@ -25,6 +25,7 @@
#include "base/message_loop/message_pump_type.h"
#include "base/pending_task.h"
#include "base/run_loop.h"
+#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
#include "base/task/common/task_annotator.h"
@@ -41,8 +42,6 @@
namespace base {
-class TaskObserver;
-
namespace trace_event {
class ConvertableToTraceFormat;
} // namespace trace_event
@@ -103,6 +102,7 @@ class BASE_EXPORT SequenceManagerImpl
// SequenceManager implementation:
void BindToCurrentThread() override;
+ scoped_refptr<SequencedTaskRunner> GetTaskRunnerForCurrentTask() override;
void BindToMessagePump(std::unique_ptr<MessagePump> message_pump) override;
void SetObserver(Observer* observer) override;
void AddTaskTimeObserver(TaskTimeObserver* task_time_observer) override;
@@ -126,16 +126,16 @@ class BASE_EXPORT SequenceManagerImpl
std::string DescribeAllPendingTasks() const override;
std::unique_ptr<NativeWorkHandle> OnNativeWorkPending(
TaskQueue::QueuePriority priority) override;
+ void AddTaskObserver(TaskObserver* task_observer) override;
+ void RemoveTaskObserver(TaskObserver* task_observer) override;
// SequencedTaskSource implementation:
- Optional<Task> TakeTask() override;
+ Task* SelectNextTask() override;
void DidRunTask() override;
TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override;
bool HasPendingHighResolutionTasks() override;
bool OnSystemIdle() override;
- void AddTaskObserver(TaskObserver* task_observer);
- void RemoveTaskObserver(TaskObserver* task_observer);
void AddDestructionObserver(
MessageLoopCurrent::DestructionObserver* destruction_observer);
void RemoveDestructionObserver(
@@ -230,7 +230,8 @@ class BASE_EXPORT SequenceManagerImpl
// We have to track rentrancy because we support nested runloops but the
// selector interface is unaware of those. This struct keeps track off all
- // task related state needed to make pairs of TakeTask() / DidRunTask() work.
+ // task related state needed to make pairs of SelectNextTask() / DidRunTask()
+ // work.
struct ExecutingTask {
ExecutingTask(Task&& task,
internal::TaskQueueImpl* task_queue,
@@ -377,8 +378,8 @@ class BASE_EXPORT SequenceManagerImpl
void RecordCrashKeys(const PendingTask&);
// Helper to terminate all scoped trace events to allow starting new ones
- // in TakeTask().
- Optional<Task> TakeTaskImpl();
+ // in SelectNextTask().
+ Task* SelectNextTaskImpl();
// Check if a task of priority |priority| should run given the pending set of
// native work.
@@ -388,7 +389,7 @@ class BASE_EXPORT SequenceManagerImpl
TimeDelta GetDelayTillNextDelayedTask(LazyNow* lazy_now) const;
#if DCHECK_IS_ON()
- void LogTaskDebugInfo(const ExecutingTask& executing_task);
+ void LogTaskDebugInfo(const internal::WorkQueue* work_queue) const;
#endif
// Determines if wall time or thread time should be recorded for the next
diff --git a/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc b/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc
index cb2f91b8176..3e1bb5dc3db 100644
--- a/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc
+++ b/chromium/base/task/sequence_manager/sequence_manager_impl_unittest.cc
@@ -566,6 +566,18 @@ class TestCountUsesTimeSource : public TickClock {
DISALLOW_COPY_AND_ASSIGN(TestCountUsesTimeSource);
};
+TEST_P(SequenceManagerTest, GetCorrectTaskRunnerForCurrentTask) {
+ auto queue = CreateTaskQueue();
+
+ queue->task_runner()->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ EXPECT_EQ(queue->task_runner(),
+ sequence_manager()->GetTaskRunnerForCurrentTask());
+ }));
+
+ RunLoop().RunUntilIdle();
+}
+
TEST_P(SequenceManagerTest, NowNotCalledIfUnneeded) {
sequence_manager()->SetWorkBatchSize(6);
@@ -2356,9 +2368,9 @@ TEST_P(SequenceManagerTest, TaskQueueObserver_ImmediateTask) {
Mock::VerifyAndClearExpectations(&observer);
// Unless the immediate work queue is emptied.
- sequence_manager()->TakeTask();
+ sequence_manager()->SelectNextTask();
sequence_manager()->DidRunTask();
- sequence_manager()->TakeTask();
+ sequence_manager()->SelectNextTask();
sequence_manager()->DidRunTask();
EXPECT_CALL(observer, OnPostTask(_, TimeDelta()));
EXPECT_CALL(observer, OnQueueNextWakeUpChanged(_));
diff --git a/chromium/base/task/sequence_manager/sequenced_task_source.h b/chromium/base/task/sequence_manager/sequenced_task_source.h
index b1153fb32e8..5ea8874ab5e 100644
--- a/chromium/base/task/sequence_manager/sequenced_task_source.h
+++ b/chromium/base/task/sequence_manager/sequenced_task_source.h
@@ -19,13 +19,13 @@ class SequencedTaskSource {
public:
virtual ~SequencedTaskSource() = default;
- // Returns the next task to run from this source or nullopt if
+ // Returns the next task to run from this source or nullptr if
// there're no more tasks ready to run. If a task is returned,
- // DidRunTask() must be invoked before the next call to TakeTask().
- virtual Optional<Task> TakeTask() = 0;
+ // DidRunTask() must be invoked before the next call to SelectNextTask().
+ virtual Task* SelectNextTask() = 0;
// Notifies this source that the task previously obtained
- // from TakeTask() has been completed.
+ // from SelectNextTask() has been completed.
virtual void DidRunTask() = 0;
// Returns the delay till the next task or TimeDelta::Max()
diff --git a/chromium/base/task/sequence_manager/task_queue_impl.cc b/chromium/base/task/sequence_manager/task_queue_impl.cc
index d109f915ded..7c22f9c1121 100644
--- a/chromium/base/task/sequence_manager/task_queue_impl.cc
+++ b/chromium/base/task/sequence_manager/task_queue_impl.cc
@@ -78,16 +78,18 @@ TaskQueueImpl::TaskRunner::~TaskRunner() {}
bool TaskQueueImpl::TaskRunner::PostDelayedTask(const Location& location,
OnceClosure callback,
TimeDelta delay) {
- return task_poster_->PostTask(PostedTask(std::move(callback), location, delay,
- Nestable::kNestable, task_type_));
+ return task_poster_->PostTask(PostedTask(this, std::move(callback), location,
+ delay, Nestable::kNestable,
+ task_type_));
}
bool TaskQueueImpl::TaskRunner::PostNonNestableDelayedTask(
const Location& location,
OnceClosure callback,
TimeDelta delay) {
- return task_poster_->PostTask(PostedTask(std::move(callback), location, delay,
- Nestable::kNonNestable, task_type_));
+ return task_poster_->PostTask(PostedTask(this, std::move(callback), location,
+ delay, Nestable::kNonNestable,
+ task_type_));
}
bool TaskQueueImpl::TaskRunner::RunsTasksInCurrentSequence() const {
@@ -423,8 +425,10 @@ void TaskQueueImpl::PushOntoDelayedIncomingQueue(Task pending_task) {
#endif
// TODO(altimin): Add a copy method to Task to capture metadata here.
+ auto task_runner = pending_task.task_runner;
PostImmediateTaskImpl(
- PostedTask(BindOnce(&TaskQueueImpl::ScheduleDelayedWorkTask,
+ PostedTask(std::move(task_runner),
+ BindOnce(&TaskQueueImpl::ScheduleDelayedWorkTask,
Unretained(this), std::move(pending_task)),
FROM_HERE, TimeDelta(), Nestable::kNonNestable,
pending_task.task_type),
diff --git a/chromium/base/task/sequence_manager/task_queue_selector.cc b/chromium/base/task/sequence_manager/task_queue_selector.cc
index 5e3a14a8e80..cb58d9b4f3a 100644
--- a/chromium/base/task/sequence_manager/task_queue_selector.cc
+++ b/chromium/base/task/sequence_manager/task_queue_selector.cc
@@ -159,6 +159,15 @@ void TaskQueueSelector::WorkQueueSetBecameNonEmpty(size_t set_index) {
}
}
+void TaskQueueSelector::CollectSkippedOverLowerPriorityTasks(
+ const internal::WorkQueue* selected_work_queue,
+ std::vector<const Task*>* result) const {
+ delayed_work_queue_sets_.CollectSkippedOverLowerPriorityTasks(
+ selected_work_queue, result);
+ immediate_work_queue_sets_.CollectSkippedOverLowerPriorityTasks(
+ selected_work_queue, result);
+}
+
#if DCHECK_IS_ON() || !defined(NDEBUG)
bool TaskQueueSelector::CheckContainsQueueForTest(
const internal::TaskQueueImpl* queue) const {
diff --git a/chromium/base/task/sequence_manager/task_queue_selector.h b/chromium/base/task/sequence_manager/task_queue_selector.h
index 2ae9b52ef05..8a79a5e52bd 100644
--- a/chromium/base/task/sequence_manager/task_queue_selector.h
+++ b/chromium/base/task/sequence_manager/task_queue_selector.h
@@ -76,6 +76,12 @@ class BASE_EXPORT TaskQueueSelector : public WorkQueueSets::Observer {
void WorkQueueSetBecameEmpty(size_t set_index) override;
void WorkQueueSetBecameNonEmpty(size_t set_index) override;
+ // Populates |result| with tasks with lower priority than the first task from
+ // |selected_work_queue| which could otherwise run now.
+ void CollectSkippedOverLowerPriorityTasks(
+ const internal::WorkQueue* selected_work_queue,
+ std::vector<const Task*>* result) const;
+
protected:
WorkQueueSets* delayed_work_queue_sets() { return &delayed_work_queue_sets_; }
diff --git a/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc b/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc
index c99366471ba..504477048dd 100644
--- a/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc
+++ b/chromium/base/task/sequence_manager/task_queue_selector_unittest.cc
@@ -74,7 +74,7 @@ class TaskQueueSelectorTestBase : public testing::Test {
EnqueueOrderGenerator enqueue_order_generator;
for (size_t i = 0; i < num_tasks; i++) {
task_queues_[queue_indices[i]]->immediate_work_queue()->Push(
- Task(PostedTask(test_closure_, FROM_HERE), TimeTicks(),
+ Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
EnqueueOrder(), enqueue_order_generator.GenerateNext()));
}
}
@@ -84,15 +84,15 @@ class TaskQueueSelectorTestBase : public testing::Test {
size_t num_tasks) {
for (size_t i = 0; i < num_tasks; i++) {
task_queues_[queue_indices[i]]->immediate_work_queue()->Push(Task(
- PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(),
- EnqueueOrder::FromIntForTesting(enqueue_orders[i])));
+ PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
+ EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_orders[i])));
}
}
void PushTask(const size_t queue_index, const size_t enqueue_order) {
task_queues_[queue_index]->immediate_work_queue()->Push(
- Task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(),
- EnqueueOrder::FromIntForTesting(enqueue_order)));
+ Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
+ EnqueueOrder(), EnqueueOrder::FromIntForTesting(enqueue_order)));
}
std::vector<size_t> PopTasksAndReturnQueueIndices() {
@@ -666,8 +666,8 @@ TEST_F(TaskQueueSelectorTest, ChooseWithPriority_Empty) {
TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyDelayed) {
task_queues_[0]->delayed_work_queue()->Push(
- Task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(),
- EnqueueOrder::FromIntForTesting(2)));
+ Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
+ EnqueueOrder(), EnqueueOrder::FromIntForTesting(2)));
bool chose_delayed_over_immediate = false;
EXPECT_EQ(
@@ -680,8 +680,8 @@ TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyDelayed) {
TEST_F(TaskQueueSelectorTest, ChooseWithPriority_OnlyImmediate) {
task_queues_[0]->immediate_work_queue()->Push(
- Task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(),
- EnqueueOrder::FromIntForTesting(2)));
+ Task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
+ EnqueueOrder(), EnqueueOrder::FromIntForTesting(2)));
bool chose_delayed_over_immediate = false;
EXPECT_EQ(
@@ -706,8 +706,8 @@ TEST_F(TaskQueueSelectorTest, TestObserverWithOneBlockedQueue) {
task_queue->SetQueueEnabled(false);
selector.DisableQueue(task_queue.get());
- Task task(PostedTask(test_closure_, FROM_HERE), TimeTicks(), EnqueueOrder(),
- EnqueueOrder::FromIntForTesting(2));
+ Task task(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
+ EnqueueOrder(), EnqueueOrder::FromIntForTesting(2));
task_queue->immediate_work_queue()->Push(std::move(task));
EXPECT_EQ(nullptr, selector.SelectWorkQueueToService());
@@ -736,10 +736,10 @@ TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) {
selector.SetQueuePriority(task_queue2.get(), TaskQueue::kControlPriority);
- Task task1(PostedTask(test_closure_, FROM_HERE), TimeTicks(),
+ Task task1(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
EnqueueOrder::FromIntForTesting(2),
EnqueueOrder::FromIntForTesting(2));
- Task task2(PostedTask(test_closure_, FROM_HERE), TimeTicks(),
+ Task task2(PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
EnqueueOrder::FromIntForTesting(3),
EnqueueOrder::FromIntForTesting(3));
task_queue->immediate_work_queue()->Push(std::move(task1));
@@ -762,6 +762,21 @@ TEST_F(TaskQueueSelectorTest, TestObserverWithTwoBlockedQueues) {
task_queue2->UnregisterTaskQueue();
}
+TEST_F(TaskQueueSelectorTest, CollectSkippedOverLowerPriorityTasks) {
+ size_t queue_order[] = {0, 1, 2, 3, 2, 1, 0};
+ PushTasks(queue_order, 7);
+ selector_.SetQueuePriority(task_queues_[3].get(), TaskQueue::kHighPriority);
+
+ std::vector<const Task*> result;
+ selector_.CollectSkippedOverLowerPriorityTasks(
+ task_queues_[3]->immediate_work_queue(), &result);
+
+ ASSERT_EQ(3u, result.size());
+ EXPECT_EQ(2u, result[0]->enqueue_order()); // The order here isn't important.
+ EXPECT_EQ(3u, result[1]->enqueue_order());
+ EXPECT_EQ(4u, result[2]->enqueue_order());
+}
+
class DisabledAntiStarvationLogicTaskQueueSelectorTest
: public TaskQueueSelectorTestBase,
public testing::WithParamInterface<TaskQueue::QueuePriority> {
@@ -839,13 +854,13 @@ class ChooseWithPriorityTest
TEST_P(ChooseWithPriorityTest, RoundRobinTest) {
task_queues_[0]->immediate_work_queue()->Push(Task(
- PostedTask(test_closure_, FROM_HERE), TimeTicks(),
+ PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
EnqueueOrder::FromIntForTesting(GetParam().immediate_task_enqueue_order),
EnqueueOrder::FromIntForTesting(
GetParam().immediate_task_enqueue_order)));
task_queues_[0]->delayed_work_queue()->Push(Task(
- PostedTask(test_closure_, FROM_HERE), TimeTicks(),
+ PostedTask(nullptr, test_closure_, FROM_HERE), TimeTicks(),
EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order),
EnqueueOrder::FromIntForTesting(GetParam().delayed_task_enqueue_order)));
diff --git a/chromium/base/task/sequence_manager/tasks.cc b/chromium/base/task/sequence_manager/tasks.cc
index 14bd306b947..a3bd5ce60f6 100644
--- a/chromium/base/task/sequence_manager/tasks.cc
+++ b/chromium/base/task/sequence_manager/tasks.cc
@@ -17,6 +17,7 @@ Task::Task(internal::PostedTask posted_task,
desired_run_time,
posted_task.nestable),
task_type(posted_task.task_type),
+ task_runner(std::move(posted_task.task_runner)),
enqueue_order_(enqueue_order) {
// We use |sequence_num| in DelayedWakeUp for ordering purposes and it
// may wrap around to a negative number during the static cast, hence,
@@ -28,9 +29,15 @@ Task::Task(internal::PostedTask posted_task,
queue_time = posted_task.queue_time;
}
-namespace internal {
+Task::Task(Task&& move_from) = default;
+
+Task::~Task() = default;
-PostedTask::PostedTask(OnceClosure callback,
+Task& Task::operator=(Task&& other) = default;
+
+namespace internal {
+PostedTask::PostedTask(scoped_refptr<SequencedTaskRunner> task_runner,
+ OnceClosure callback,
Location location,
TimeDelta delay,
Nestable nestable,
@@ -39,7 +46,8 @@ PostedTask::PostedTask(OnceClosure callback,
location(location),
delay(delay),
nestable(nestable),
- task_type(task_type) {}
+ task_type(task_type),
+ task_runner(std::move(task_runner)) {}
PostedTask::PostedTask(PostedTask&& move_from) noexcept
: callback(std::move(move_from.callback)),
@@ -47,6 +55,7 @@ PostedTask::PostedTask(PostedTask&& move_from) noexcept
delay(move_from.delay),
nestable(move_from.nestable),
task_type(move_from.task_type),
+ task_runner(std::move(move_from.task_runner)),
queue_time(move_from.queue_time) {}
PostedTask::~PostedTask() = default;
diff --git a/chromium/base/task/sequence_manager/tasks.h b/chromium/base/task/sequence_manager/tasks.h
index 0c886c4b9ca..d1e1912ead2 100644
--- a/chromium/base/task/sequence_manager/tasks.h
+++ b/chromium/base/task/sequence_manager/tasks.h
@@ -6,6 +6,7 @@
#define BASE_TASK_SEQUENCE_MANAGER_TASKS_H_
#include "base/pending_task.h"
+#include "base/sequenced_task_runner.h"
#include "base/task/sequence_manager/enqueue_order.h"
namespace base {
@@ -21,7 +22,8 @@ enum class WakeUpResolution { kLow, kHigh };
// Wrapper around PostTask method arguments and the assigned task type.
// Eventually it becomes a PendingTask once accepted by a TaskQueueImpl.
struct BASE_EXPORT PostedTask {
- explicit PostedTask(OnceClosure callback = OnceClosure(),
+ explicit PostedTask(scoped_refptr<SequencedTaskRunner> task_runner,
+ OnceClosure callback = OnceClosure(),
Location location = Location(),
TimeDelta delay = TimeDelta(),
Nestable nestable = Nestable::kNestable,
@@ -34,6 +36,9 @@ struct BASE_EXPORT PostedTask {
TimeDelta delay;
Nestable nestable;
TaskType task_type;
+ // The task runner this task is running on. Can be used by task runners that
+ // support posting back to the "current sequence".
+ scoped_refptr<SequencedTaskRunner> task_runner;
// The time at which the task was queued.
TimeTicks queue_time;
@@ -76,6 +81,9 @@ struct BASE_EXPORT Task : public PendingTask {
EnqueueOrder enqueue_order = EnqueueOrder(),
internal::WakeUpResolution wake_up_resolution =
internal::WakeUpResolution::kLow);
+ Task(Task&& move_from);
+ ~Task();
+ Task& operator=(Task&& other);
internal::DelayedWakeUp delayed_wake_up() const {
return internal::DelayedWakeUp{delayed_run_time, sequence_num};
@@ -97,6 +105,10 @@ struct BASE_EXPORT Task : public PendingTask {
TaskType task_type;
+ // The task runner this task is running on. Can be used by task runners that
+ // support posting back to the "current sequence".
+ scoped_refptr<SequencedTaskRunner> task_runner;
+
#if DCHECK_IS_ON()
bool cross_thread_;
#endif
diff --git a/chromium/base/task/sequence_manager/thread_controller_impl.cc b/chromium/base/task/sequence_manager/thread_controller_impl.cc
index 22bffdb8a5b..676b9a6708d 100644
--- a/chromium/base/task/sequence_manager/thread_controller_impl.cc
+++ b/chromium/base/task/sequence_manager/thread_controller_impl.cc
@@ -174,7 +174,7 @@ void ThreadControllerImpl::DoWork(WorkType work_type) {
WeakPtr<ThreadControllerImpl> weak_ptr = weak_factory_.GetWeakPtr();
// TODO(scheduler-dev): Consider moving to a time based work batch instead.
for (int i = 0; i < main_sequence_only().work_batch_size_; i++) {
- Optional<PendingTask> task = sequence_->TakeTask();
+ Task* task = sequence_->SelectNextTask();
if (!task)
break;
@@ -189,7 +189,7 @@ void ThreadControllerImpl::DoWork(WorkType work_type) {
// Trace events should finish before we call DidRunTask to ensure that
// SequenceManager trace events do not interfere with them.
TRACE_TASK_EXECUTION("ThreadControllerImpl::RunTask", *task);
- task_annotator_.RunTask("SequenceManager RunTask", &*task);
+ task_annotator_.RunTask("SequenceManager RunTask", task);
}
if (!weak_ptr)
diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index deeda0e4260..9b0a058efbc 100644
--- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -342,7 +342,7 @@ TimeDelta ThreadControllerWithMessagePumpImpl::DoWorkImpl(
DCHECK(main_thread_only().task_source);
for (int i = 0; i < main_thread_only().work_batch_size; i++) {
- Optional<Task> task = main_thread_only().task_source->TakeTask();
+ Task* task = main_thread_only().task_source->SelectNextTask();
if (!task)
break;
@@ -362,7 +362,7 @@ TimeDelta ThreadControllerWithMessagePumpImpl::DoWorkImpl(
// Trace events should finish before we call DidRunTask to ensure that
// SequenceManager trace events do not interfere with them.
TRACE_TASK_EXECUTION("ThreadControllerImpl::RunTask", *task);
- task_annotator_.RunTask("SequenceManager RunTask", &*task);
+ task_annotator_.RunTask("SequenceManager RunTask", task);
}
#if DCHECK_IS_ON()
diff --git a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
index 64eb8f7e74b..3c02becf16d 100644
--- a/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
+++ b/chromium/base/task/sequence_manager/thread_controller_with_message_pump_impl_unittest.cc
@@ -79,17 +79,17 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource {
explicit FakeSequencedTaskSource(TickClock* clock) : clock_(clock) {}
~FakeSequencedTaskSource() override = default;
- Optional<Task> TakeTask() override {
+ Task* SelectNextTask() override {
if (tasks_.empty())
- return nullopt;
+ return nullptr;
if (tasks_.front().delayed_run_time > clock_->NowTicks())
- return nullopt;
- Task task = std::move(tasks_.front());
+ return nullptr;
+ running_stack_.push_back(std::move(tasks_.front()));
tasks_.pop();
- return task;
+ return &running_stack_.back();
}
- void DidRunTask() override {}
+ void DidRunTask() override { running_stack_.pop_back(); }
TimeDelta DelayTillNextTask(LazyNow* lazy_now) const override {
if (tasks_.empty())
@@ -106,8 +106,9 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource {
TimeTicks delayed_run_time) {
DCHECK(tasks_.empty() || delayed_run_time.is_null() ||
tasks_.back().delayed_run_time < delayed_run_time);
- tasks_.push(Task(internal::PostedTask(std::move(task), posted_from),
- delayed_run_time, EnqueueOrder::FromIntForTesting(13)));
+ tasks_.push(
+ Task(internal::PostedTask(nullptr, std::move(task), posted_from),
+ delayed_run_time, EnqueueOrder::FromIntForTesting(13)));
}
bool HasPendingHighResolutionTasks() override { return false; }
@@ -117,6 +118,7 @@ class FakeSequencedTaskSource : public internal::SequencedTaskSource {
private:
TickClock* clock_;
std::queue<Task> tasks_;
+ std::vector<Task> running_stack_;
};
TimeTicks Seconds(int seconds) {
diff --git a/chromium/base/task/sequence_manager/work_queue.cc b/chromium/base/task/sequence_manager/work_queue.cc
index 0d9df4a150a..2dfd04da230 100644
--- a/chromium/base/task/sequence_manager/work_queue.cc
+++ b/chromium/base/task/sequence_manager/work_queue.cc
@@ -305,6 +305,16 @@ void WorkQueue::PopTaskForTesting() {
tasks_.pop_front();
}
+void WorkQueue::CollectTasksOlderThan(EnqueueOrder reference,
+ std::vector<const Task*>* result) const {
+ for (const Task& task : tasks_) {
+ if (task.enqueue_order() >= reference)
+ break;
+
+ result->push_back(&task);
+ }
+}
+
} // namespace internal
} // namespace sequence_manager
} // namespace base
diff --git a/chromium/base/task/sequence_manager/work_queue.h b/chromium/base/task/sequence_manager/work_queue.h
index 849d48cac5d..65fdee4ca28 100644
--- a/chromium/base/task/sequence_manager/work_queue.h
+++ b/chromium/base/task/sequence_manager/work_queue.h
@@ -161,6 +161,11 @@ class BASE_EXPORT WorkQueue {
// Test support function. This should not be used in production code.
void PopTaskForTesting();
+ // Iterates through |tasks_| adding any that are older than |reference| to
+ // |result|.
+ void CollectTasksOlderThan(EnqueueOrder reference,
+ std::vector<const Task*>* result) const;
+
private:
bool InsertFenceImpl(EnqueueOrder fence);
diff --git a/chromium/base/task/sequence_manager/work_queue_sets.cc b/chromium/base/task/sequence_manager/work_queue_sets.cc
index c2f9886271d..68ec9613338 100644
--- a/chromium/base/task/sequence_manager/work_queue_sets.cc
+++ b/chromium/base/task/sequence_manager/work_queue_sets.cc
@@ -237,6 +237,19 @@ bool WorkQueueSets::ContainsWorkQueueForTest(
}
#endif
+void WorkQueueSets::CollectSkippedOverLowerPriorityTasks(
+ const internal::WorkQueue* selected_work_queue,
+ std::vector<const Task*>* result) const {
+ EnqueueOrder selected_enqueue_order;
+ CHECK(selected_work_queue->GetFrontTaskEnqueueOrder(&selected_enqueue_order));
+ for (size_t priority = selected_work_queue->work_queue_set_index() + 1;
+ priority < TaskQueue::kQueuePriorityCount; priority++) {
+ for (const OldestTaskEnqueueOrder& pair : work_queue_heaps_[priority]) {
+ pair.value->CollectTasksOlderThan(selected_enqueue_order, result);
+ }
+ }
+}
+
} // namespace internal
} // namespace sequence_manager
} // namespace base
diff --git a/chromium/base/task/sequence_manager/work_queue_sets.h b/chromium/base/task/sequence_manager/work_queue_sets.h
index 90cc6d789c1..f128c62c369 100644
--- a/chromium/base/task/sequence_manager/work_queue_sets.h
+++ b/chromium/base/task/sequence_manager/work_queue_sets.h
@@ -95,6 +95,12 @@ class BASE_EXPORT WorkQueueSets {
const char* GetName() const { return name_; }
+ // Collects ready tasks which where skipped over when |selected_work_queue|
+ // was selected. Note this is somewhat expensive.
+ void CollectSkippedOverLowerPriorityTasks(
+ const internal::WorkQueue* selected_work_queue,
+ std::vector<const Task*>* result) const;
+
private:
struct OldestTaskEnqueueOrder {
EnqueueOrder key;
diff --git a/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc b/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc
index 30f3bb8fa68..6450573d5b1 100644
--- a/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc
+++ b/chromium/base/task/sequence_manager/work_queue_sets_unittest.cc
@@ -51,14 +51,14 @@ class WorkQueueSetsTest : public testing::Test {
}
Task FakeTaskWithEnqueueOrder(int enqueue_order) {
- Task fake_task(PostedTask(BindOnce([] {}), FROM_HERE), TimeTicks(),
+ Task fake_task(PostedTask(nullptr, BindOnce([] {}), FROM_HERE), TimeTicks(),
EnqueueOrder(),
EnqueueOrder::FromIntForTesting(enqueue_order));
return fake_task;
}
Task FakeNonNestableTaskWithEnqueueOrder(int enqueue_order) {
- Task fake_task(PostedTask(BindOnce([] {}), FROM_HERE), TimeTicks(),
+ Task fake_task(PostedTask(nullptr, BindOnce([] {}), FROM_HERE), TimeTicks(),
EnqueueOrder(),
EnqueueOrder::FromIntForTesting(enqueue_order));
fake_task.nestable = Nestable::kNonNestable;
@@ -331,6 +331,32 @@ TEST_F(WorkQueueSetsTest, PushNonNestableTaskToFront) {
EXPECT_EQ(queue1, work_queue_sets_->GetOldestQueueInSet(set));
}
+TEST_F(WorkQueueSetsTest, CollectSkippedOverLowerPriorityTasks) {
+ WorkQueue* queue1 = NewTaskQueue("queue1");
+ WorkQueue* queue2 = NewTaskQueue("queue2");
+ WorkQueue* queue3 = NewTaskQueue("queue3");
+
+ work_queue_sets_->ChangeSetIndex(queue1, 3);
+ work_queue_sets_->ChangeSetIndex(queue2, 2);
+ work_queue_sets_->ChangeSetIndex(queue3, 1);
+
+ queue1->Push(FakeTaskWithEnqueueOrder(1));
+ queue1->Push(FakeTaskWithEnqueueOrder(2));
+ queue2->Push(FakeTaskWithEnqueueOrder(3));
+ queue3->Push(FakeTaskWithEnqueueOrder(4));
+ queue3->Push(FakeTaskWithEnqueueOrder(5));
+ queue2->Push(FakeTaskWithEnqueueOrder(6));
+ queue1->Push(FakeTaskWithEnqueueOrder(7));
+
+ std::vector<const Task*> result;
+ work_queue_sets_->CollectSkippedOverLowerPriorityTasks(queue3, &result);
+
+ ASSERT_EQ(3u, result.size());
+ EXPECT_EQ(3u, result[0]->enqueue_order()); // The order here isn't important.
+ EXPECT_EQ(1u, result[1]->enqueue_order());
+ EXPECT_EQ(2u, result[2]->enqueue_order());
+}
+
} // namespace internal
} // namespace sequence_manager
} // namespace base
diff --git a/chromium/base/task/sequence_manager/work_queue_unittest.cc b/chromium/base/task/sequence_manager/work_queue_unittest.cc
index 721f60fda7d..1ab88bf5065 100644
--- a/chromium/base/task/sequence_manager/work_queue_unittest.cc
+++ b/chromium/base/task/sequence_manager/work_queue_unittest.cc
@@ -73,23 +73,23 @@ class WorkQueueTest : public testing::Test {
protected:
Task FakeCancelableTaskWithEnqueueOrder(int enqueue_order,
WeakPtr<Cancelable> weak_ptr) {
- Task fake_task(
- PostedTask(BindOnce(&Cancelable::NopTask, weak_ptr), FROM_HERE),
- TimeTicks(), EnqueueOrder(),
- EnqueueOrder::FromIntForTesting(enqueue_order));
+ Task fake_task(PostedTask(nullptr, BindOnce(&Cancelable::NopTask, weak_ptr),
+ FROM_HERE),
+ TimeTicks(), EnqueueOrder(),
+ EnqueueOrder::FromIntForTesting(enqueue_order));
return fake_task;
}
Task FakeTaskWithEnqueueOrder(int enqueue_order) {
- Task fake_task(PostedTask(BindOnce(&NopTask), FROM_HERE), TimeTicks(),
- EnqueueOrder(),
+ Task fake_task(PostedTask(nullptr, BindOnce(&NopTask), FROM_HERE),
+ TimeTicks(), EnqueueOrder(),
EnqueueOrder::FromIntForTesting(enqueue_order));
return fake_task;
}
Task FakeNonNestableTaskWithEnqueueOrder(int enqueue_order) {
- Task fake_task(PostedTask(BindOnce(&NopTask), FROM_HERE), TimeTicks(),
- EnqueueOrder(),
+ Task fake_task(PostedTask(nullptr, BindOnce(&NopTask), FROM_HERE),
+ TimeTicks(), EnqueueOrder(),
EnqueueOrder::FromIntForTesting(enqueue_order));
fake_task.nestable = Nestable::kNonNestable;
return fake_task;
@@ -555,6 +555,20 @@ TEST_F(WorkQueueTest, RemoveAllCanceledTasksFromFrontQueueBlockedByFence) {
EXPECT_FALSE(work_queue_->GetFrontTaskEnqueueOrder(&enqueue_order));
}
+TEST_F(WorkQueueTest, CollectTasksOlderThan) {
+ work_queue_->Push(FakeTaskWithEnqueueOrder(2));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(3));
+ work_queue_->Push(FakeTaskWithEnqueueOrder(4));
+
+ std::vector<const Task*> result;
+ work_queue_->CollectTasksOlderThan(EnqueueOrder::FromIntForTesting(4),
+ &result);
+
+ ASSERT_EQ(2u, result.size());
+ EXPECT_EQ(2u, result[0]->enqueue_order());
+ EXPECT_EQ(3u, result[1]->enqueue_order());
+}
+
} // namespace internal
} // namespace sequence_manager
} // namespace base
diff --git a/chromium/base/task/simple_task_executor.cc b/chromium/base/task/simple_task_executor.cc
new file mode 100644
index 00000000000..ffccf52be86
--- /dev/null
+++ b/chromium/base/task/simple_task_executor.cc
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium 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/task/simple_task_executor.h"
+
+namespace base {
+
+SimpleTaskExecutor::SimpleTaskExecutor(
+ scoped_refptr<SingleThreadTaskRunner> task_queue)
+ : task_queue_(std::move(task_queue)),
+ previous_task_executor_(GetTaskExecutorForCurrentThread()) {
+ DCHECK(task_queue_);
+ // The TaskExecutor API does not expect nesting, but this can happen in tests
+ // so we have to work around it here.
+ if (previous_task_executor_)
+ SetTaskExecutorForCurrentThread(nullptr);
+ SetTaskExecutorForCurrentThread(this);
+}
+
+SimpleTaskExecutor::~SimpleTaskExecutor() {
+ if (previous_task_executor_)
+ SetTaskExecutorForCurrentThread(nullptr);
+ SetTaskExecutorForCurrentThread(previous_task_executor_);
+}
+
+bool SimpleTaskExecutor::PostDelayedTask(const Location& from_here,
+ const TaskTraits& traits,
+ OnceClosure task,
+ TimeDelta delay) {
+ return task_queue_->PostDelayedTask(from_here, std::move(task), delay);
+}
+
+scoped_refptr<TaskRunner> SimpleTaskExecutor::CreateTaskRunner(
+ const TaskTraits& traits) {
+ return task_queue_;
+}
+
+scoped_refptr<SequencedTaskRunner>
+SimpleTaskExecutor::CreateSequencedTaskRunner(const TaskTraits& traits) {
+ return task_queue_;
+}
+
+scoped_refptr<SingleThreadTaskRunner>
+SimpleTaskExecutor::CreateSingleThreadTaskRunner(
+ const TaskTraits& traits,
+ SingleThreadTaskRunnerThreadMode thread_mode) {
+ return task_queue_;
+}
+
+#if defined(OS_WIN)
+scoped_refptr<SingleThreadTaskRunner>
+SimpleTaskExecutor::CreateCOMSTATaskRunner(
+ const TaskTraits& traits,
+ SingleThreadTaskRunnerThreadMode thread_mode) {
+ // It seems pretty unlikely this will be used on a comsta task thread.
+ NOTREACHED();
+ return task_queue_;
+}
+#endif // defined(OS_WIN)
+
+} // namespace base
diff --git a/chromium/base/task/simple_task_executor.h b/chromium/base/task/simple_task_executor.h
new file mode 100644
index 00000000000..7d9a74dc5d2
--- /dev/null
+++ b/chromium/base/task/simple_task_executor.h
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TASK_SIMPLE_TASK_EXECUTOR_H_
+#define BASE_TASK_SIMPLE_TASK_EXECUTOR_H_
+
+#include "base/task/task_executor.h"
+#include "build/build_config.h"
+
+namespace base {
+
+// A simple TaskExecutor with exactly one SingleThreadTaskRunner.
+// Must be instantiated and destroyed on the thread that runs tasks for the
+// SingleThreadTaskRunner.
+class BASE_EXPORT SimpleTaskExecutor : public TaskExecutor {
+ public:
+ explicit SimpleTaskExecutor(scoped_refptr<SingleThreadTaskRunner> task_queue);
+
+ ~SimpleTaskExecutor() override;
+
+ bool PostDelayedTask(const Location& from_here,
+ const TaskTraits& traits,
+ OnceClosure task,
+ TimeDelta delay) override;
+
+ scoped_refptr<TaskRunner> CreateTaskRunner(const TaskTraits& traits) override;
+
+ scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunner(
+ const TaskTraits& traits) override;
+
+ scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
+ const TaskTraits& traits,
+ SingleThreadTaskRunnerThreadMode thread_mode) override;
+
+#if defined(OS_WIN)
+ scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner(
+ const TaskTraits& traits,
+ SingleThreadTaskRunnerThreadMode thread_mode) override;
+#endif // defined(OS_WIN)
+
+ private:
+ const scoped_refptr<SingleThreadTaskRunner> task_queue_;
+
+ // In tests there may already be a TaskExecutor registered for the thread, we
+ // keep tack of the previous TaskExecutor and restored it upon destruction.
+ TaskExecutor* const previous_task_executor_;
+};
+
+} // namespace base
+
+#endif // BASE_TASK_SIMPLE_TASK_EXECUTOR_H_
diff --git a/chromium/base/task/single_thread_task_executor.cc b/chromium/base/task/single_thread_task_executor.cc
index 7e12b784faf..01d993be952 100644
--- a/chromium/base/task/single_thread_task_executor.cc
+++ b/chromium/base/task/single_thread_task_executor.cc
@@ -17,7 +17,8 @@ SingleThreadTaskExecutor::SingleThreadTaskExecutor(MessagePumpType type)
.Build())),
default_task_queue_(sequence_manager_->CreateTaskQueue(
sequence_manager::TaskQueue::Spec("default_tq"))),
- type_(type) {
+ type_(type),
+ simple_task_executor_(task_runner()) {
sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner());
sequence_manager_->BindToMessagePump(MessagePump::Create(type));
diff --git a/chromium/base/task/single_thread_task_executor.h b/chromium/base/task/single_thread_task_executor.h
index d350420d797..c048a830cf1 100644
--- a/chromium/base/task/single_thread_task_executor.h
+++ b/chromium/base/task/single_thread_task_executor.h
@@ -11,6 +11,7 @@
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_pump_type.h"
#include "base/single_thread_task_runner.h"
+#include "base/task/simple_task_executor.h"
namespace base {
@@ -40,6 +41,7 @@ class BASE_EXPORT SingleThreadTaskExecutor {
std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_;
scoped_refptr<sequence_manager::TaskQueue> default_task_queue_;
MessagePumpType type_;
+ SimpleTaskExecutor simple_task_executor_;
DISALLOW_COPY_AND_ASSIGN(SingleThreadTaskExecutor);
};
diff --git a/chromium/base/task/single_thread_task_executor_unittest.cc b/chromium/base/task/single_thread_task_executor_unittest.cc
new file mode 100644
index 00000000000..b64294f404f
--- /dev/null
+++ b/chromium/base/task/single_thread_task_executor_unittest.cc
@@ -0,0 +1,58 @@
+// Copyright 2019 The Chromium 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/task/single_thread_task_executor.h"
+
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "base/test/bind_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+namespace base {
+
+TEST(SingleThreadTaskExecutorTest, GetTaskExecutorForCurrentThread) {
+ EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull());
+
+ {
+ SingleThreadTaskExecutor single_thread_task_executor;
+ EXPECT_THAT(GetTaskExecutorForCurrentThread(), NotNull());
+ }
+
+ EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull());
+}
+
+TEST(SingleThreadTaskExecutorTest,
+ GetTaskExecutorForCurrentThreadInPostedTask) {
+ SingleThreadTaskExecutor single_thread_task_executor;
+ TaskExecutor* task_executor = GetTaskExecutorForCurrentThread();
+
+ EXPECT_THAT(task_executor, NotNull());
+
+ RunLoop run_loop;
+ single_thread_task_executor.task_runner()->PostTask(
+ FROM_HERE, BindLambdaForTesting([&]() {
+ EXPECT_EQ(GetTaskExecutorForCurrentThread(), task_executor);
+ run_loop.Quit();
+ }));
+
+ run_loop.Run();
+}
+
+TEST(SingleThreadTaskExecutorTest, CurrentThread) {
+ SingleThreadTaskExecutor single_thread_task_executor;
+
+ EXPECT_EQ(single_thread_task_executor.task_runner(),
+ base::CreateSingleThreadTaskRunner({base::CurrentThread()}));
+
+ // There's only one task queue so priority is ignored.
+ EXPECT_EQ(single_thread_task_executor.task_runner(),
+ base::CreateSingleThreadTaskRunner(
+ {base::CurrentThread(), base::TaskPriority::BEST_EFFORT}));
+}
+
+} // namespace base
diff --git a/chromium/base/task/task_executor.cc b/chromium/base/task/task_executor.cc
index 8b527b0cd5b..355fa3f445f 100644
--- a/chromium/base/task/task_executor.cc
+++ b/chromium/base/task/task_executor.cc
@@ -6,8 +6,10 @@
#include <type_traits>
+#include "base/no_destructor.h"
#include "base/task/task_traits.h"
#include "base/task/task_traits_extension.h"
+#include "base/threading/thread_local.h"
namespace base {
@@ -30,6 +32,21 @@ static_assert(
} // namespace
+ThreadLocalPointer<TaskExecutor>* GetTLSForCurrentTaskExecutor() {
+ static NoDestructor<ThreadLocalPointer<TaskExecutor>> instance;
+ return instance.get();
+}
+
+void SetTaskExecutorForCurrentThread(TaskExecutor* task_executor) {
+ DCHECK(!task_executor || !GetTLSForCurrentTaskExecutor()->Get() ||
+ GetTLSForCurrentTaskExecutor()->Get() == task_executor);
+ GetTLSForCurrentTaskExecutor()->Set(task_executor);
+}
+
+TaskExecutor* GetTaskExecutorForCurrentThread() {
+ return GetTLSForCurrentTaskExecutor()->Get();
+}
+
void RegisterTaskExecutor(uint8_t extension_id, TaskExecutor* task_executor) {
DCHECK_NE(extension_id, TaskTraitsExtensionStorage::kInvalidExtensionId);
DCHECK_LE(extension_id, TaskTraitsExtensionStorage::kMaxExtensionId);
@@ -57,4 +74,4 @@ TaskExecutor* GetRegisteredTaskExecutorForTraits(const TaskTraits& traits) {
return nullptr;
}
-} // namespace base \ No newline at end of file
+} // namespace base
diff --git a/chromium/base/task/task_executor.h b/chromium/base/task/task_executor.h
index b4e79e14c9a..a062fdc4391 100644
--- a/chromium/base/task/task_executor.h
+++ b/chromium/base/task/task_executor.h
@@ -74,6 +74,13 @@ void BASE_EXPORT RegisterTaskExecutor(uint8_t extension_id,
TaskExecutor* task_executor);
void BASE_EXPORT UnregisterTaskExecutorForTesting(uint8_t extension_id);
+// Stores the provided TaskExecutor in TLS for the current thread, to be used by
+// tasks with the CurrentThread() trait.
+void BASE_EXPORT SetTaskExecutorForCurrentThread(TaskExecutor* task_executor);
+
+// Returns the task executor registered for the current thread.
+BASE_EXPORT TaskExecutor* GetTaskExecutorForCurrentThread();
+
// Determines whether a registered TaskExecutor will handle tasks with the given
// |traits| and, if so, returns a pointer to it. Otherwise, returns |nullptr|.
TaskExecutor* GetRegisteredTaskExecutorForTraits(const TaskTraits& traits);
diff --git a/chromium/base/task/task_traits.h b/chromium/base/task/task_traits.h
index c1e52ed0f07..03abf17913f 100644
--- a/chromium/base/task/task_traits.h
+++ b/chromium/base/task/task_traits.h
@@ -185,6 +185,14 @@ struct WithBaseSyncPrimitives {};
// between tasks, see base::PostTask::CreateSequencedTaskRunner.
struct ThreadPool {};
+// Tasks and task runners with this thread will run tasks on the virtual thread
+// (sequence) they are posted/created from. Other traits may be specified
+// alongside this one to refine properties for the associated tasks
+// (e.g. base::TaskPriority or content::BrowserTaskType) as long as those traits
+// are compatible with the current thread (e.g. cannot specify base::MayBlock()
+// on a non-blocking thread or alter base::TaskShutdownBehavior).
+struct CurrentThread {};
+
// Describes metadata for a single task or a group of tasks.
class BASE_EXPORT TaskTraits {
public:
@@ -196,6 +204,7 @@ class BASE_EXPORT TaskTraits {
ValidTrait(MayBlock);
ValidTrait(WithBaseSyncPrimitives);
ValidTrait(ThreadPool);
+ ValidTrait(CurrentThread);
};
// Invoking this constructor without arguments produces TaskTraits that are
@@ -255,15 +264,22 @@ class BASE_EXPORT TaskTraits {
may_block_(trait_helpers::HasTrait<MayBlock, ArgTypes...>()),
with_base_sync_primitives_(
trait_helpers::HasTrait<WithBaseSyncPrimitives, ArgTypes...>()),
- use_thread_pool_(trait_helpers::HasTrait<ThreadPool, ArgTypes...>()) {
+ use_thread_pool_(trait_helpers::HasTrait<ThreadPool, ArgTypes...>()),
+ use_current_thread_(
+ trait_helpers::HasTrait<CurrentThread, ArgTypes...>()) {
constexpr bool has_thread_pool =
trait_helpers::HasTrait<ThreadPool, ArgTypes...>();
constexpr bool has_extension =
!trait_helpers::AreValidTraits<ValidTrait, ArgTypes...>::value;
+ constexpr bool has_current_thread =
+ trait_helpers::HasTrait<CurrentThread, ArgTypes...>();
+ static_assert(
+ !has_current_thread || !has_thread_pool,
+ "base::CurrentThread is mutually exclusive with base::ThreadPool");
static_assert(
- has_thread_pool ^ has_extension,
+ has_thread_pool ^ has_extension || has_current_thread,
"Traits must explicitly specify a destination (e.g. ThreadPool or a "
- "named thread like BrowserThread)");
+ "named thread like BrowserThread, or CurrentThread)");
}
constexpr TaskTraits(const TaskTraits& other) = default;
@@ -271,14 +287,15 @@ class BASE_EXPORT TaskTraits {
// TODO(eseckler): Default the comparison operator once C++20 arrives.
bool operator==(const TaskTraits& other) const {
- static_assert(sizeof(TaskTraits) == 15,
+ static_assert(sizeof(TaskTraits) == 16,
"Update comparison operator when TaskTraits change");
return extension_ == other.extension_ && priority_ == other.priority_ &&
shutdown_behavior_ == other.shutdown_behavior_ &&
thread_policy_ == other.thread_policy_ &&
may_block_ == other.may_block_ &&
with_base_sync_primitives_ == other.with_base_sync_primitives_ &&
- use_thread_pool_ == other.use_thread_pool_;
+ use_thread_pool_ == other.use_thread_pool_ &&
+ use_current_thread_ == other.use_current_thread_;
}
// Sets the priority of tasks with these traits to |priority|.
@@ -335,6 +352,10 @@ class BASE_EXPORT TaskTraits {
// Returns true if tasks with these traits execute on the thread pool.
constexpr bool use_thread_pool() const { return use_thread_pool_; }
+ // Returns true if tasks with these traits execute on the virtual thread
+ // (sequence) they are posted/created from.
+ constexpr bool use_current_thread() const { return use_current_thread_; }
+
uint8_t extension_id() const { return extension_.extension_id; }
// Access the extension data by parsing it into the provided extension type.
@@ -353,6 +374,7 @@ class BASE_EXPORT TaskTraits {
TaskPriority priority,
bool may_block,
bool use_thread_pool,
+ bool use_current_thread,
TaskTraitsExtensionStorage extension)
: extension_(extension),
priority_(static_cast<uint8_t>(priority) |
@@ -362,8 +384,9 @@ class BASE_EXPORT TaskTraits {
thread_policy_(static_cast<uint8_t>(ThreadPolicy::PREFER_BACKGROUND)),
may_block_(may_block),
with_base_sync_primitives_(false),
- use_thread_pool_(use_thread_pool) {
- static_assert(sizeof(TaskTraits) == 15, "Keep this constructor up to date");
+ use_thread_pool_(use_thread_pool),
+ use_current_thread_(use_current_thread) {
+ static_assert(sizeof(TaskTraits) == 16, "Keep this constructor up to date");
}
// This bit is set in |priority_|, |shutdown_behavior_| and |thread_policy_|
@@ -378,6 +401,7 @@ class BASE_EXPORT TaskTraits {
bool may_block_;
bool with_base_sync_primitives_;
bool use_thread_pool_;
+ bool use_current_thread_;
};
// Returns string literals for the enums defined in this file. These methods
diff --git a/chromium/base/task/task_traits_unittest.nc b/chromium/base/task/task_traits_unittest.nc
index 8755ca028ff..73fc7be5506 100644
--- a/chromium/base/task/task_traits_unittest.nc
+++ b/chromium/base/task/task_traits_unittest.nc
@@ -26,6 +26,8 @@ constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN,
TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN};
#elif defined(NCTEST_TASK_TRAITS_INVALID_TYPE) // [r"no matching constructor for initialization of 'const base::TaskTraits'"]
constexpr TaskTraits traits = {TaskShutdownBehavior::BLOCK_SHUTDOWN, true};
+#elif defined(NCTEST_TASK_TRAITS_CURRENT_THREAD_AND_THREADPOOL) // [r"base::CurrentThread is mutually exclusive with base::ThreadPool"]
+constexpr TaskTraits traits = {ThreadPool(), CurrentThread()};
#endif
} // namespace base
diff --git a/chromium/base/task/thread_pool/historical_histogram_data.md b/chromium/base/task/thread_pool/historical_histogram_data.md
new file mode 100644
index 00000000000..d253731b6fb
--- /dev/null
+++ b/chromium/base/task/thread_pool/historical_histogram_data.md
@@ -0,0 +1,92 @@
+# Historical Histogram Data
+
+This page presents data captured from `base::ThreadPool` histograms at a given
+point in time so it can be used in future design decisions.
+
+All data is 28-day aggregation on Stable channel.
+
+## Number of tasks between waits
+
+Number of tasks between two waits by a foreground worker thread in a
+browser/renderer process.
+
+Histogram name: ThreadPool.NumTasksBetweenWaits.(Browser/Renderer).Foreground
+Date: August 2019
+Values in tables below are percentiles.
+
+### Windows
+
+| Number of tasks | Browser process | Renderer process |
+|-----------------|-----------------|------------------|
+| 1 | 87 | 92 |
+| 2 | 95 | 98 |
+| 5 | 99 | 100 |
+
+### Mac
+
+| Number of tasks | Browser process | Renderer process |
+|-----------------|-----------------|------------------|
+| 1 | 81 | 90 |
+| 2 | 92 | 97 |
+| 5 | 98 | 100 |
+
+### Android
+
+| Number of tasks | Browser process | Renderer process |
+|-----------------|-----------------|------------------|
+| 1 | 92 | 96 |
+| 2 | 97 | 98 |
+| 5 | 99 | 100 |
+
+
+## Number of tasks run while queueing
+
+Number of tasks run by ThreadPool while task was queuing (from time task was
+posted until time it was run). Recorded for dummy heartbeat tasks in the
+*browser* process. The heartbeat recording avoids dependencies between this
+report and other work in the system.
+
+Histogram name: ThreadPool.NumTasksRunWhileQueuing.Browser.*
+Date: September 2019
+Values in tables below are percentiles.
+
+Note: In *renderer* processes, on all platforms/priorities, 0 tasks are run
+while queuing at 99.5th percentile.
+
+### Windows
+
+| Number of tasks | USER_BLOCKING | USER_VISIBLE | BEST_EFFORT |
+|-----------------|---------------|--------------|-------------|
+| 0 | 95 | 93 | 90 |
+| 1 | 98 | 95 | 92 |
+| 2 | 99 | 96 | 93 |
+| 5 | 100 | 98 | 95 |
+
+### Mac
+
+| Number of tasks | USER_BLOCKING | USER_VISIBLE | BEST_EFFORT |
+|-----------------|---------------|--------------|-------------|
+| 0 | 100 | 100 | 99 |
+| 1 | 100 | 100 | 99 |
+| 2 | 100 | 100 | 99 |
+| 5 | 100 | 100 | 100 |
+
+### Android
+
+| Number of tasks | USER_BLOCKING | USER_VISIBLE | BEST_EFFORT |
+|-----------------|---------------|--------------|-------------|
+| 0 | 99 | 98 | 97 |
+| 1 | 100 | 99 | 99 |
+| 2 | 100 | 99 | 99 |
+| 5 | 100 | 100 | 100 |
+
+### Chrome OS
+
+For all priorities, 0 tasks are run while queueing at 99.5th percentile.
+
+### Analysis
+
+The number of tasks that run while a BEST_EFFORT task is queued is unexpectedly
+low. We should explore creating threads less aggressively, at the expense of
+keeping BEST_EFFORT tasks in the queue for a longer time. See
+[Bug 906079](https://crbug.com/906079).
diff --git a/chromium/base/task/thread_pool/initialization_util.cc b/chromium/base/task/thread_pool/initialization_util.cc
index d70c4c3d27d..6fd197300c9 100644
--- a/chromium/base/task/thread_pool/initialization_util.cc
+++ b/chromium/base/task/thread_pool/initialization_util.cc
@@ -6,6 +6,7 @@
#include <algorithm>
+#include "base/numerics/ranges.h"
#include "base/system/sys_info.h"
namespace base {
@@ -16,7 +17,7 @@ int RecommendedMaxNumberOfThreadsInThreadGroup(int min,
int offset) {
const int num_of_cores = SysInfo::NumberOfProcessors();
const int threads = std::ceil<int>(num_of_cores * cores_multiplier) + offset;
- return std::min(max, std::max(min, threads));
+ return ClampToRange(threads, min, max);
}
} // namespace base
diff --git a/chromium/base/task/thread_pool/job_task_source.cc b/chromium/base/task/thread_pool/job_task_source.cc
index c6e5f9733c5..be52945d0d9 100644
--- a/chromium/base/task/thread_pool/job_task_source.cc
+++ b/chromium/base/task/thread_pool/job_task_source.cc
@@ -19,6 +19,120 @@
namespace base {
namespace internal {
+// Memory ordering on |state_| operations
+//
+// The write operation on |state_| in WillRunTask() uses
+// std::memory_order_release, matched by std::memory_order_acquire on read
+// operations (in DidProcessTask()) to establish a
+// Release-Acquire ordering. When a call to WillRunTask() is caused by an
+// increase of max concurrency followed by an associated
+// NotifyConcurrencyIncrease(), the priority queue lock guarantees an
+// happens-after relation with NotifyConcurrencyIncrease(). This ensures that an
+// increase of max concurrency that happened-before NotifyConcurrencyIncrease()
+// is visible to a read operation that happens-after WillRunTask().
+//
+// In DidProcessTask(), this is necessary to
+// ensure that the task source is always re-enqueued when it needs to. When the
+// task source needs to be queued, either because the current task yielded or
+// because of NotifyConcurrencyIncrease(), one of the following is true:
+// A) DidProcessTask() happens-after WillRunTask():
+// T1: Current task returns (because it is done) or yields.
+// T2: Increases the value returned by GetMaxConcurrency()
+// NotifyConcurrencyIncrease() enqueues the task source
+// T3: WillRunTask(), in response to the concurrency increase - Release
+// Does not keep the TaskSource in PriorityQueue because it is at max
+// concurrency
+// T1: DidProcessTask() - Acquire - Because of memory barrier, sees the same
+// (or newer) max concurrency as T2
+// Re-enqueues the TaskSource because no longer at max concurrency
+// Without the memory barrier, T1 may see an outdated max concurrency that
+// is lower than the actual max concurrency and won't re-enqueue the
+// task source, because it thinks it's already saturated.
+// The task source often needs to be re-enqueued if its task
+// completed because it yielded and |max_concurrency| wasn't decreased.
+// B) DidProcessTask() happens-before WillRunTask():
+// T1: Current task returns (because it is done) or yields
+// T2: Increases the value returned by GetMaxConcurrency()
+// NotifyConcurrencyIncrease() enqueues the task source
+// T1: DidProcessTask() - Acquire (ineffective)
+// Since the task source is already in the queue, it doesn't matter
+// whether T1 re-enqueues the task source or not.
+// Note that stale values the other way around can cause incorrectly
+// re-enqueuing this task_source, which is not an issue because the queues
+// support empty task sources.
+
+JobTaskSource::State::State() = default;
+JobTaskSource::State::~State() = default;
+
+JobTaskSource::State::Value JobTaskSource::State::Cancel() {
+ return {value_.fetch_or(kCanceledMask, std::memory_order_relaxed)};
+}
+
+JobTaskSource::State::Value
+JobTaskSource::State::TryIncrementWorkerCountFromWorkerRelease(
+ size_t max_concurrency) {
+ uint32_t value_before_add = value_.load(std::memory_order_relaxed);
+
+ // std::memory_order_release on success to establish Release-Acquire ordering
+ // with DecrementWorkerCountAcquire() (see Memory Ordering comment at top of
+ // the file).
+ while (!(value_before_add & kCanceledMask) &&
+ (value_before_add >> kWorkerCountBitOffset) < max_concurrency &&
+ !value_.compare_exchange_weak(
+ value_before_add, value_before_add + kWorkerCountIncrement,
+ std::memory_order_release, std::memory_order_relaxed)) {
+ }
+ return {value_before_add};
+}
+
+JobTaskSource::State::Value
+JobTaskSource::State::DecrementWorkerCountFromWorkerAcquire() {
+ const size_t value_before_sub =
+ value_.fetch_sub(kWorkerCountIncrement, std::memory_order_acquire);
+ DCHECK((value_before_sub >> kWorkerCountBitOffset) > 0);
+ return {value_before_sub};
+}
+
+JobTaskSource::State::Value
+JobTaskSource::State::IncrementWorkerCountFromJoiningThread() {
+ size_t value_before_add =
+ value_.fetch_add(kWorkerCountIncrement, std::memory_order_relaxed);
+ return {value_before_add};
+}
+
+JobTaskSource::State::Value
+JobTaskSource::State::DecrementWorkerCountFromJoiningThread() {
+ const size_t value_before_sub =
+ value_.fetch_sub(kWorkerCountIncrement, std::memory_order_relaxed);
+ DCHECK((value_before_sub >> kWorkerCountBitOffset) > 0);
+ return {value_before_sub};
+}
+
+JobTaskSource::State::Value JobTaskSource::State::Load() const {
+ return {value_.load(std::memory_order_relaxed)};
+}
+
+JobTaskSource::JoinFlag::JoinFlag() = default;
+JobTaskSource::JoinFlag::~JoinFlag() = default;
+
+void JobTaskSource::JoinFlag::SetWaiting() {
+ const auto previous_value =
+ value_.exchange(kWaitingForWorkerToYield, std::memory_order_relaxed);
+ DCHECK(previous_value == kNotWaiting);
+}
+
+bool JobTaskSource::JoinFlag::ShouldWorkerYield() {
+ // The fetch_and() sets the state to kWaitingForWorkerToSignal if it was
+ // previously kWaitingForWorkerToYield, otherwise it leaves it unchanged.
+ return value_.fetch_and(kWaitingForWorkerToSignal,
+ std::memory_order_relaxed) ==
+ kWaitingForWorkerToYield;
+}
+
+bool JobTaskSource::JoinFlag::ShouldWorkerSignal() {
+ return value_.exchange(kNotWaiting, std::memory_order_relaxed) != kNotWaiting;
+}
+
JobTaskSource::JobTaskSource(
const Location& from_here,
const TaskTraits& traits,
@@ -28,64 +142,122 @@ JobTaskSource::JobTaskSource(
: TaskSource(traits, nullptr, TaskSourceExecutionMode::kJob),
from_here_(from_here),
max_concurrency_callback_(std::move(max_concurrency_callback)),
- worker_task_(base::BindRepeating(
- [](JobTaskSource* self,
- const RepeatingCallback<void(experimental::JobDelegate*)>&
- worker_task) {
+ worker_task_(std::move(worker_task)),
+ primary_task_(base::BindRepeating(
+ [](JobTaskSource* self) {
// Each worker task has its own delegate with associated state.
experimental::JobDelegate job_delegate{self, self->delegate_};
- worker_task.Run(&job_delegate);
+ self->worker_task_.Run(&job_delegate);
},
- base::Unretained(this),
- std::move(worker_task))),
+ base::Unretained(this))),
queue_time_(TimeTicks::Now()),
delegate_(delegate) {
DCHECK(delegate_);
}
JobTaskSource::~JobTaskSource() {
-#if DCHECK_IS_ON()
- auto worker_count = worker_count_.load(std::memory_order_relaxed);
// Make sure there's no outstanding active run operation left.
- DCHECK(worker_count == 0U || worker_count == kInvalidWorkerCount)
- << worker_count;
-#endif
+ DCHECK_EQ(state_.Load().worker_count(), 0U);
}
ExecutionEnvironment JobTaskSource::GetExecutionEnvironment() {
return {SequenceToken::Create(), nullptr};
}
+bool JobTaskSource::WillJoin() {
+ {
+ CheckedAutoLock auto_lock(lock_);
+ DCHECK(!worker_released_condition_); // This may only be called once.
+ worker_released_condition_ = lock_.CreateConditionVariable();
+ }
+ // std::memory_order_relaxed on |worker_count_| is sufficient because call to
+ // GetMaxConcurrency() is used for a best effort early exit. Stale values will
+ // only cause WaitForParticipationOpportunity() to be called.
+ const auto state_before_add = state_.IncrementWorkerCountFromJoiningThread();
+
+ if (!state_before_add.is_canceled() &&
+ state_before_add.worker_count() < GetMaxConcurrency()) {
+ return true;
+ }
+ return WaitForParticipationOpportunity();
+}
+
+bool JobTaskSource::RunJoinTask() {
+ experimental::JobDelegate job_delegate{this, nullptr};
+ worker_task_.Run(&job_delegate);
+
+ // std::memory_order_relaxed on |worker_count_| is sufficient because the call
+ // to GetMaxConcurrency() is used for a best effort early exit. Stale values
+ // will only cause WaitForParticipationOpportunity() to be called.
+ const auto state = state_.Load();
+ if (!state.is_canceled() && state.worker_count() <= GetMaxConcurrency())
+ return true;
+
+ return WaitForParticipationOpportunity();
+}
+
+void JobTaskSource::Cancel(TaskSource::Transaction* transaction) {
+ // Sets the kCanceledMask bit on |state_| so that further calls to
+ // WillRunTask() never succeed. std::memory_order_relaxed is sufficient
+ // because this task source never needs to be re-enqueued after Cancel().
+ state_.Cancel();
+}
+
+bool JobTaskSource::WaitForParticipationOpportunity() {
+ CheckedAutoLock auto_lock(lock_);
+
+ // std::memory_order_relaxed is sufficient because no other state is
+ // synchronized with |state_| outside of |lock_|.
+ auto state = state_.Load();
+ size_t max_concurrency = GetMaxConcurrency();
+
+ // Wait until either:
+ // A) |worker_count| is below or equal to max concurrency and state is not
+ // canceled.
+ // B) All other workers returned and |worker_count| is 1.
+ while (!((state.worker_count() <= max_concurrency && !state.is_canceled()) ||
+ state.worker_count() == 1)) {
+ // std::memory_order_relaxed is sufficient because no other state is
+ // synchronized with |join_flag_| outside of |lock_|.
+ join_flag_.SetWaiting();
+
+ // To avoid unnecessarily waiting, if either condition A) or B) change
+ // |lock_| is taken and |worker_released_condition_| signaled if necessary:
+ // 1- In DidProcessTask(), after worker count is decremented.
+ // 2- In NotifyConcurrencyIncrease(), following a max_concurrency increase.
+ worker_released_condition_->Wait();
+ state = state_.Load();
+ max_concurrency = GetMaxConcurrency();
+ }
+ // Case A:
+ if (state.worker_count() <= max_concurrency && !state.is_canceled())
+ return true;
+ // Case B:
+ // Only the joining thread remains.
+ DCHECK_EQ(state.worker_count(), 1U);
+ DCHECK(state.is_canceled() || max_concurrency == 0U);
+ state_.DecrementWorkerCountFromJoiningThread();
+ return false;
+}
+
TaskSource::RunStatus JobTaskSource::WillRunTask() {
- // When this call is caused by an increase of max concurrency followed by an
- // associated NotifyConcurrencyIncrease(), the priority queue lock guarantees
- // an happens-after relation with NotifyConcurrencyIncrease(). The memory
- // operations on |worker_count| below and in DidProcessTask() use
- // std::memory_order_release and std::memory_order_acquire respectively to
- // establish a Release-Acquire ordering. This ensures that all memory
- // side-effects made before this point, including an increase of max
- // concurrency followed by NotifyConcurrencyIncrease() are visible to a
- // DidProcessTask() call which is ordered after this one.
const size_t max_concurrency = GetMaxConcurrency();
- size_t worker_count_before_add =
- worker_count_.load(std::memory_order_relaxed);
-
- // std::memory_order_release on success to make the newest |max_concurrency|
- // visible to a thread that calls DidProcessTask() containing a matching
- // std::memory_order_acquire.
- while (worker_count_before_add < max_concurrency &&
- !worker_count_.compare_exchange_weak(
- worker_count_before_add, worker_count_before_add + 1,
- std::memory_order_release, std::memory_order_relaxed)) {
- }
+ // std::memory_order_release on success to establish Release-Acquire ordering
+ // with read operations (see Memory Ordering comment at top of the file).
+ const auto state_before_add =
+ state_.TryIncrementWorkerCountFromWorkerRelease(max_concurrency);
+
// Don't allow this worker to run the task if either:
- // A) |worker_count_| is already at |max_concurrency|.
- // B) |max_concurrency| was lowered below or to |worker_count_|.
- // C) |worker_count_| was invalidated.
- if (worker_count_before_add >= max_concurrency) {
- // The caller is prevented from running a task from this TaskSource.
+ // A) |state_| was canceled.
+ // B) |worker_count| is already at |max_concurrency|.
+ // C) |max_concurrency| was lowered below or to |worker_count|.
+ // Case A:
+ if (state_before_add.is_canceled())
+ return RunStatus::kDisallowed;
+ const size_t worker_count_before_add = state_before_add.worker_count();
+ // Case B) or C):
+ if (worker_count_before_add >= max_concurrency)
return RunStatus::kDisallowed;
- }
DCHECK_LT(worker_count_before_add, max_concurrency);
return max_concurrency == worker_count_before_add + 1
@@ -96,15 +268,23 @@ TaskSource::RunStatus JobTaskSource::WillRunTask() {
size_t JobTaskSource::GetRemainingConcurrency() const {
// std::memory_order_relaxed is sufficient because no other state is
// synchronized with GetRemainingConcurrency().
- const size_t worker_count = worker_count_.load(std::memory_order_relaxed);
+ const auto state = state_.Load();
const size_t max_concurrency = GetMaxConcurrency();
// Avoid underflows.
- if (worker_count > max_concurrency)
+ if (state.is_canceled() || state.worker_count() > max_concurrency)
return 0;
- return max_concurrency - worker_count;
+ return max_concurrency - state.worker_count();
}
void JobTaskSource::NotifyConcurrencyIncrease() {
+ {
+ // Lock is taken to access |join_flag_| below and signal
+ // |worker_released_condition_|.
+ CheckedAutoLock auto_lock(lock_);
+ if (join_flag_.ShouldWorkerSignal())
+ worker_released_condition_->Signal();
+ }
+
#if DCHECK_IS_ON()
{
AutoLock auto_lock(version_lock_);
@@ -125,6 +305,14 @@ size_t JobTaskSource::GetMaxConcurrency() const {
return max_concurrency_callback_.Run();
}
+bool JobTaskSource::ShouldYield() {
+ // It is safe to read |join_flag_| without a lock since this
+ // variable is atomic, keeping in mind that threads may not immediately see
+ // the new value when it is updated.
+ return TS_UNCHECKED_READ(join_flag_).ShouldWorkerYield() ||
+ state_.Load().is_canceled();
+}
+
#if DCHECK_IS_ON()
size_t JobTaskSource::GetConcurrencyIncreaseVersion() const {
@@ -151,45 +339,36 @@ bool JobTaskSource::WaitForConcurrencyIncreaseUpdate(size_t recorded_version) {
#endif // DCHECK_IS_ON()
Optional<Task> JobTaskSource::TakeTask(TaskSource::Transaction* transaction) {
- DCHECK_GT(worker_count_.load(std::memory_order_relaxed), 0U);
- DCHECK(worker_task_);
- return base::make_optional<Task>(from_here_, worker_task_, TimeDelta());
+ // JobTaskSource members are not lock-protected so no need to acquire a lock
+ // if |transaction| is nullptr.
+ DCHECK_GT(state_.Load().worker_count(), 0U);
+ DCHECK(primary_task_);
+ return base::make_optional<Task>(from_here_, primary_task_, TimeDelta());
}
bool JobTaskSource::DidProcessTask(TaskSource::Transaction* transaction) {
- size_t worker_count_before_sub =
- worker_count_.load(std::memory_order_relaxed);
-
- // std::memory_order_acquire on |worker_count_| is necessary to establish
- // Release-Acquire ordering (see WillRunTask()).
- // When the task source needs to be queued, either because the current task
- // yielded or because of NotifyConcurrencyIncrease(), one of the following is
- // true:
- // A) The JobTaskSource is already in the queue (no worker picked up the
- // extra work yet): Incorrectly returning false is fine and the memory
- // barrier may be ineffective.
- // B) The JobTaskSource() is no longer in the queue: The Release-Acquire
- // ordering with WillRunTask() established by |worker_count| ensures that
- // the upcoming call for GetMaxConcurrency() happens-after any
- // NotifyConcurrencyIncrease() that happened-before WillRunTask(). If
- // this task completed because it yielded, this barrier guarantees that
- // it sees an up-to-date concurrency value and correctly re-enqueues.
- //
- // Note that stale values the other way around (incorrectly re-enqueuing) are
- // not an issue because the queues support empty task sources.
- while (worker_count_before_sub != kInvalidWorkerCount &&
- !worker_count_.compare_exchange_weak(
- worker_count_before_sub, worker_count_before_sub - 1,
- std::memory_order_acquire, std::memory_order_relaxed)) {
- }
- if (worker_count_before_sub == kInvalidWorkerCount)
+ // Lock is needed to access |join_flag_| below and signal
+ // |worker_released_condition_|. If |transaction|, then |lock_| is already
+ // taken.
+ CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_);
+ AnnotateAcquiredLockAlias annotate(lock_, lock_);
+
+ // std::memory_order_acquire to establish Release-Acquire ordering with
+ // WillRunTask() (see Memory Ordering comment at top of the file).
+ const auto state_before_sub = state_.DecrementWorkerCountFromWorkerAcquire();
+
+ if (join_flag_.ShouldWorkerSignal())
+ worker_released_condition_->Signal();
+
+ // A canceled task source should never get re-enqueued.
+ if (state_before_sub.is_canceled())
return false;
- DCHECK_GT(worker_count_before_sub, 0U);
+ DCHECK_GT(state_before_sub.worker_count(), 0U);
// Re-enqueue the TaskSource if the task ran and the worker count is below the
// max concurrency.
- return worker_count_before_sub <= GetMaxConcurrency();
+ return state_before_sub.worker_count() <= GetMaxConcurrency();
}
SequenceSortKey JobTaskSource::GetSortKey() const {
@@ -197,12 +376,7 @@ SequenceSortKey JobTaskSource::GetSortKey() const {
}
Optional<Task> JobTaskSource::Clear(TaskSource::Transaction* transaction) {
- // Invalidate |worker_count_| so that further calls to WillRunTask() never
- // succeed. std::memory_order_relaxed is sufficient because this task source
- // never needs to be re-enqueued after Clear().
- size_t worker_count_before_store =
- worker_count_.exchange(kInvalidWorkerCount, std::memory_order_relaxed);
- DCHECK_GT(worker_count_before_store, 0U);
+ Cancel();
// Nothing is cleared since other workers might still racily run tasks. For
// simplicity, the destructor will take care of it once all references are
// released.
diff --git a/chromium/base/task/thread_pool/job_task_source.h b/chromium/base/task/thread_pool/job_task_source.h
index ddaca19b343..57838679e7a 100644
--- a/chromium/base/task/thread_pool/job_task_source.h
+++ b/chromium/base/task/thread_pool/job_task_source.h
@@ -38,10 +38,33 @@ class BASE_EXPORT JobTaskSource : public TaskSource {
RepeatingCallback<size_t()> max_concurrency_callback,
PooledTaskRunnerDelegate* delegate);
+ static experimental::JobHandle CreateJobHandle(
+ scoped_refptr<internal::JobTaskSource> task_source) {
+ return experimental::JobHandle(std::move(task_source));
+ }
+
// Notifies this task source that max concurrency was increased, and the
// number of worker should be adjusted.
void NotifyConcurrencyIncrease();
+ // Informs this JobTaskSource that the current thread would like to join and
+ // contribute to running |worker_task|. Returns true if the joining thread can
+ // contribute (RunJoinTask() can be called), or false if joining was completed
+ // and all other workers returned because either there's no work remaining or
+ // Job was cancelled.
+ bool WillJoin();
+
+ // Contributes to running |worker_task| and returns true if the joining thread
+ // can contribute again (RunJoinTask() can be called again), or false if
+ // joining was completed and all other workers returned because either there's
+ // no work remaining or Job was cancelled. This should be called only after
+ // WillJoin() or RunJoinTask() previously returned true.
+ bool RunJoinTask();
+
+ // Cancels this JobTaskSource, causing all workers to yield and WillRunTask()
+ // to return RunStatus::kDisallowed.
+ void Cancel(TaskSource::Transaction* transaction = nullptr);
+
// TaskSource:
ExecutionEnvironment GetExecutionEnvironment() override;
size_t GetRemainingConcurrency() const override;
@@ -50,6 +73,12 @@ class BASE_EXPORT JobTaskSource : public TaskSource {
// concurrently.
size_t GetMaxConcurrency() const;
+ // Returns true if a worker should return from the worker task on the current
+ // thread ASAP.
+ bool ShouldYield();
+
+ PooledTaskRunnerDelegate* delegate() const { return delegate_; }
+
#if DCHECK_IS_ON()
size_t GetConcurrencyIncreaseVersion() const;
// Returns true if the concurrency version was updated above
@@ -58,11 +87,98 @@ class BASE_EXPORT JobTaskSource : public TaskSource {
#endif // DCHECK_IS_ON()
private:
- static constexpr size_t kInvalidWorkerCount =
- std::numeric_limits<size_t>::max();
+ // Atomic internal state to track the number of workers running a task from
+ // this JobTaskSource and whether this JobTaskSource is canceled.
+ class State {
+ public:
+ static constexpr size_t kCanceledMask = 1;
+ static constexpr size_t kWorkerCountBitOffset = 1;
+ static constexpr size_t kWorkerCountIncrement = 1 << kWorkerCountBitOffset;
+
+ struct Value {
+ size_t worker_count() const { return value >> kWorkerCountBitOffset; }
+ // Returns true if canceled.
+ bool is_canceled() const { return value & kCanceledMask; }
+
+ uint32_t value;
+ };
+
+ State();
+ ~State();
+
+ // Sets as canceled using std::memory_order_relaxed. Returns the state
+ // before the operation.
+ Value Cancel();
+
+ // Increments the worker count by 1 if smaller than |max_concurrency| and if
+ // |!is_canceled()|, using std::memory_order_release, and returns the state
+ // before the operation. Equivalent to Load() otherwise.
+ Value TryIncrementWorkerCountFromWorkerRelease(size_t max_concurrency);
+
+ // Decrements the worker count by 1 using std::memory_order_acquire. Returns
+ // the state before the operation.
+ Value DecrementWorkerCountFromWorkerAcquire();
+
+ // Increments the worker count by 1 using std::memory_order_relaxed. Returns
+ // the state before the operation.
+ Value IncrementWorkerCountFromJoiningThread();
+
+ // Decrements the worker count by 1 using std::memory_order_relaxed. Returns
+ // the state before the operation.
+ Value DecrementWorkerCountFromJoiningThread();
+
+ // Loads and returns the state, using std::memory_order_relaxed.
+ Value Load() const;
+
+ private:
+ std::atomic<uint32_t> value_{0};
+ };
+
+ // Atomic flag that indicates if the joining thread is currently waiting on
+ // another worker to yield or to signal.
+ class JoinFlag {
+ public:
+ static constexpr uint32_t kNotWaiting = 0;
+ static constexpr uint32_t kWaitingForWorkerToSignal = 1;
+ static constexpr uint32_t kWaitingForWorkerToYield = 3;
+ // kWaitingForWorkerToYield is 3 because the impl relies on the following
+ // property.
+ static_assert((kWaitingForWorkerToYield & kWaitingForWorkerToSignal) ==
+ kWaitingForWorkerToSignal,
+ "");
+
+ JoinFlag();
+ ~JoinFlag();
+
+ // Sets the status as kWaitingForWorkerToYield using
+ // std::memory_order_relaxed.
+ void SetWaiting();
+
+ // If the flag is kWaitingForWorkerToYield, returns true indicating that the
+ // worker should yield, and atomically updates to kWaitingForWorkerToSignal
+ // (using std::memory_order_relaxed) to ensure that a single worker yields
+ // in response to SetWaiting().
+ bool ShouldWorkerYield();
+
+ // If the flag is kWaiting*, returns true indicating that the worker should
+ // signal, and atomically updates to kNotWaiting (using
+ // std::memory_order_relaxed) to ensure that a single worker signals in
+ // response to SetWaiting().
+ bool ShouldWorkerSignal();
+
+ private:
+ std::atomic<uint32_t> value_{kNotWaiting};
+ };
~JobTaskSource() override;
+ // Called from the joining thread. Waits for the worker count to be below or
+ // equal to max concurrency (will happen when a worker calls
+ // DidProcessTask()). Returns true if the joining thread should run a task, or
+ // false if joining was completed and all other workers returned because
+ // either there's no work remaining or Job was cancelled.
+ bool WaitForParticipationOpportunity();
+
// TaskSource:
RunStatus WillRunTask() override;
Optional<Task> TakeTask(TaskSource::Transaction* transaction) override;
@@ -70,13 +186,23 @@ class BASE_EXPORT JobTaskSource : public TaskSource {
bool DidProcessTask(TaskSource::Transaction* transaction) override;
SequenceSortKey GetSortKey() const override;
- // The current number of workers concurrently running tasks from this
- // TaskSource.
- std::atomic_size_t worker_count_{0U};
+ // Current atomic state.
+ State state_;
+ // Normally, |join_flag_| is protected by |lock_|, except in ShouldYield()
+ // hence the use of atomics.
+ JoinFlag join_flag_ GUARDED_BY(lock_);
+ // Signaled when |join_flag_| is kWaiting* and a worker returns.
+ std::unique_ptr<ConditionVariable> worker_released_condition_
+ GUARDED_BY(lock_);
const Location from_here_;
- base::RepeatingCallback<size_t()> max_concurrency_callback_;
- base::RepeatingClosure worker_task_;
+ RepeatingCallback<size_t()> max_concurrency_callback_;
+
+ // Worker task set by the job owner.
+ RepeatingCallback<void(experimental::JobDelegate*)> worker_task_;
+ // Task returned from TakeTask(), that calls |worker_task_| internally.
+ RepeatingClosure primary_task_;
+
const TimeTicks queue_time_;
PooledTaskRunnerDelegate* delegate_;
diff --git a/chromium/base/task/thread_pool/job_task_source_unittest.cc b/chromium/base/task/thread_pool/job_task_source_unittest.cc
index 789cf5ab7a0..413771f2235 100644
--- a/chromium/base/task/thread_pool/job_task_source_unittest.cc
+++ b/chromium/base/task/thread_pool/job_task_source_unittest.cc
@@ -29,6 +29,8 @@ class MockPooledTaskRunnerDelegate : public PooledTaskRunnerDelegate {
MOCK_CONST_METHOD1(ShouldYield, bool(const TaskSource* task_source));
MOCK_METHOD1(EnqueueJobTaskSource,
bool(scoped_refptr<JobTaskSource> task_source));
+ MOCK_METHOD1(RemoveJobTaskSource,
+ void(scoped_refptr<JobTaskSource> task_source));
MOCK_CONST_METHOD1(IsRunningPoolWithTraits, bool(const TaskTraits& traits));
MOCK_METHOD2(UpdatePriority,
void(scoped_refptr<TaskSource> task_source,
@@ -108,13 +110,17 @@ TEST_F(ThreadPoolJobTaskSourceTest, Clear) {
EXPECT_EQ(registered_task_source_d.WillRunTask(),
TaskSource::RunStatus::kAllowedNotSaturated);
+ EXPECT_FALSE(task_source->ShouldYield());
+
{
EXPECT_EQ(1U, task_source->GetRemainingConcurrency());
auto task = registered_task_source_c.Clear();
std::move(task->task).Run();
+ registered_task_source_c.DidProcessTask();
EXPECT_EQ(0U, task_source->GetRemainingConcurrency());
}
// The task source shouldn't allow any further tasks after Clear.
+ EXPECT_TRUE(task_source->ShouldYield());
EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
TaskSource::RunStatus::kDisallowed);
@@ -122,6 +128,7 @@ TEST_F(ThreadPoolJobTaskSourceTest, Clear) {
{
auto task = registered_task_source_d.Clear();
std::move(task->task).Run();
+ registered_task_source_d.DidProcessTask();
EXPECT_EQ(0U, task_source->GetRemainingConcurrency());
}
@@ -129,15 +136,53 @@ TEST_F(ThreadPoolJobTaskSourceTest, Clear) {
std::move(task_a->task).Run();
registered_task_source_a.DidProcessTask();
- // A valid outstanding RunStatus can also take & run a task.
+ // A valid outstanding RunStatus can also take and run a task.
{
auto task = registered_task_source_b.TakeTask();
std::move(task->task).Run();
registered_task_source_b.DidProcessTask();
}
- // Sanity check.
+}
+
+// Verifies that a job task source doesn't return an "allowed" RunStatus after
+// Cancel() is called.
+TEST_F(ThreadPoolJobTaskSourceTest, Cancel) {
+ auto job_task = base::MakeRefCounted<test::MockJobTask>(
+ DoNothing(), /* num_tasks_to_run */ 3);
+ scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+ FROM_HERE, {ThreadPool(), TaskPriority::BEST_EFFORT},
+ &pooled_task_runner_delegate_);
+
+ auto registered_task_source_a =
+ RegisteredTaskSource::CreateForTesting(task_source);
+ EXPECT_EQ(registered_task_source_a.WillRunTask(),
+ TaskSource::RunStatus::kAllowedNotSaturated);
+ auto task_a = registered_task_source_a.TakeTask();
+
+ auto registered_task_source_b =
+ RegisteredTaskSource::CreateForTesting(task_source);
+ EXPECT_EQ(registered_task_source_b.WillRunTask(),
+ TaskSource::RunStatus::kAllowedNotSaturated);
+
+ EXPECT_FALSE(task_source->ShouldYield());
+
+ task_source->Cancel();
+ EXPECT_TRUE(task_source->ShouldYield());
+
+ // The task source shouldn't allow any further tasks after Cancel.
EXPECT_EQ(RegisteredTaskSource::CreateForTesting(task_source).WillRunTask(),
TaskSource::RunStatus::kDisallowed);
+
+ // A task that was already acquired can still run.
+ std::move(task_a->task).Run();
+ registered_task_source_a.DidProcessTask();
+
+ // A RegisteredTaskSource that's ready can also take and run a task.
+ {
+ auto task = registered_task_source_b.TakeTask();
+ std::move(task->task).Run();
+ registered_task_source_b.DidProcessTask();
+ }
}
// Verifies that multiple tasks can run in parallel up to |max_concurrency|.
@@ -183,6 +228,68 @@ TEST_F(ThreadPoolJobTaskSourceTest, RunTasksInParallel) {
EXPECT_FALSE(registered_task_source_c.DidProcessTask());
}
+// Verifies the normal flow of running the join task until completion.
+TEST_F(ThreadPoolJobTaskSourceTest, RunJoinTask) {
+ auto job_task = base::MakeRefCounted<test::MockJobTask>(
+ DoNothing(), /* num_tasks_to_run */ 2);
+ scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+ FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
+
+ EXPECT_TRUE(task_source->WillJoin());
+ // Intentionally run |worker_task| twice to make sure RunJoinTask() calls
+ // it again. This can happen in production if the joining thread spuriously
+ // return and needs to run again.
+ EXPECT_TRUE(task_source->RunJoinTask());
+ EXPECT_FALSE(task_source->RunJoinTask());
+}
+
+// Verifies that WillJoin() doesn't allow a joining thread to contribute
+// after Cancel() is called.
+TEST_F(ThreadPoolJobTaskSourceTest, CancelJoinTask) {
+ auto job_task = base::MakeRefCounted<test::MockJobTask>(
+ DoNothing(), /* num_tasks_to_run */ 2);
+ scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+ FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
+
+ task_source->Cancel();
+ EXPECT_FALSE(task_source->WillJoin());
+}
+
+// Verifies that RunJoinTask() doesn't allow a joining thread to contribute
+// after Cancel() is called.
+TEST_F(ThreadPoolJobTaskSourceTest, JoinCancelTask) {
+ auto job_task = base::MakeRefCounted<test::MockJobTask>(
+ DoNothing(), /* num_tasks_to_run */ 2);
+ scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+ FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
+
+ EXPECT_TRUE(task_source->WillJoin());
+ task_source->Cancel();
+ EXPECT_FALSE(task_source->RunJoinTask());
+}
+
+// Verifies that the join task can run in parallel with worker tasks up to
+// |max_concurrency|.
+TEST_F(ThreadPoolJobTaskSourceTest, RunJoinTaskInParallel) {
+ auto job_task = base::MakeRefCounted<test::MockJobTask>(
+ DoNothing(), /* num_tasks_to_run */ 2);
+ scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+ FROM_HERE, ThreadPool(), &pooled_task_runner_delegate_);
+
+ auto registered_task_source =
+ RegisteredTaskSource::CreateForTesting(task_source);
+ EXPECT_EQ(registered_task_source.WillRunTask(),
+ TaskSource::RunStatus::kAllowedNotSaturated);
+ auto worker_task = registered_task_source.TakeTask();
+
+ EXPECT_TRUE(task_source->WillJoin());
+
+ std::move(worker_task->task).Run();
+ EXPECT_FALSE(registered_task_source.DidProcessTask());
+
+ EXPECT_FALSE(task_source->RunJoinTask());
+}
+
// Verifies that a call to NotifyConcurrencyIncrease() calls the delegate
// and allows to run additional tasks.
TEST_F(ThreadPoolJobTaskSourceTest, NotifyConcurrencyIncrease) {
@@ -342,13 +449,9 @@ TEST_F(ThreadPoolJobTaskSourceTest, InvalidDidProcessTask) {
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(task_source);
- EXPECT_EQ(registered_task_source.WillRunTask(),
- TaskSource::RunStatus::kAllowedSaturated);
- // Can not be called before TakeTask().
- EXPECT_DCHECK_DEATH(registered_task_source.DidProcessTask());
- auto task = registered_task_source.TakeTask();
- registered_task_source.DidProcessTask();
+ // Can not be called before WillRunTask().
+ EXPECT_DCHECK_DEATH(registered_task_source.DidProcessTask());
}
} // namespace internal
diff --git a/chromium/base/task/thread_pool/pooled_task_runner_delegate.h b/chromium/base/task/thread_pool/pooled_task_runner_delegate.h
index e00f481c6a5..0ec1b87c10b 100644
--- a/chromium/base/task/thread_pool/pooled_task_runner_delegate.h
+++ b/chromium/base/task/thread_pool/pooled_task_runner_delegate.h
@@ -46,6 +46,10 @@ class BASE_EXPORT PooledTaskRunnerDelegate {
virtual bool EnqueueJobTaskSource(
scoped_refptr<JobTaskSource> task_source) = 0;
+ // Removes |task_source| from the priority queue.
+ virtual void RemoveJobTaskSource(
+ scoped_refptr<JobTaskSource> task_source) = 0;
+
// Invoked when RunsTasksInCurrentSequence() is called on a
// PooledParallelTaskRunner. Returns true if the current thread is part of the
// ThreadGroup associated with |traits|.
diff --git a/chromium/base/task/thread_pool/priority_queue.cc b/chromium/base/task/thread_pool/priority_queue.cc
index 28077b8740e..85eae1fe469 100644
--- a/chromium/base/task/thread_pool/priority_queue.cc
+++ b/chromium/base/task/thread_pool/priority_queue.cc
@@ -139,20 +139,18 @@ RegisteredTaskSource PriorityQueue::PopTaskSource() {
}
RegisteredTaskSource PriorityQueue::RemoveTaskSource(
- scoped_refptr<TaskSource> task_source) {
- DCHECK(task_source);
-
+ const TaskSource& task_source) {
if (IsEmpty())
return nullptr;
- const HeapHandle heap_handle = task_source->heap_handle();
+ const HeapHandle heap_handle = task_source.heap_handle();
if (!heap_handle.IsValid())
return nullptr;
TaskSourceAndSortKey& task_source_and_sort_key =
const_cast<PriorityQueue::TaskSourceAndSortKey&>(
container_.at(heap_handle));
- DCHECK_EQ(task_source_and_sort_key.task_source().get(), task_source);
+ DCHECK_EQ(task_source_and_sort_key.task_source().get(), &task_source);
RegisteredTaskSource registered_task_source =
task_source_and_sort_key.take_task_source();
diff --git a/chromium/base/task/thread_pool/priority_queue.h b/chromium/base/task/thread_pool/priority_queue.h
index fbe391e9384..d4d88842126 100644
--- a/chromium/base/task/thread_pool/priority_queue.h
+++ b/chromium/base/task/thread_pool/priority_queue.h
@@ -49,7 +49,7 @@ class BASE_EXPORT PriorityQueue {
// RegisteredTaskSource which evaluates to true if successful, or false if
// |task_source| is not currently in the PriorityQueue or the PriorityQueue is
// empty.
- RegisteredTaskSource RemoveTaskSource(scoped_refptr<TaskSource> task_source);
+ RegisteredTaskSource RemoveTaskSource(const TaskSource& task_source);
// Updates the sort key of the TaskSource in |transaction| to
// match its current traits. No-ops if the TaskSource is not in the
diff --git a/chromium/base/task/thread_pool/priority_queue_unittest.cc b/chromium/base/task/thread_pool/priority_queue_unittest.cc
index d5af9167457..30ad206ac98 100644
--- a/chromium/base/task/thread_pool/priority_queue_unittest.cc
+++ b/chromium/base/task/thread_pool/priority_queue_unittest.cc
@@ -55,7 +55,7 @@ class PriorityQueueWithSequencesTest : public testing::Test {
}
test::TaskEnvironment task_environment{
- test::ScopedTaskEnvironment::TimeSource::MOCK_TIME};
+ test::TaskEnvironment::TimeSource::MOCK_TIME};
scoped_refptr<TaskSource> sequence_a = MakeSequenceWithTraitsAndTask(
TaskTraits(ThreadPool(), TaskPriority::USER_VISIBLE));
@@ -144,34 +144,34 @@ TEST_F(PriorityQueueWithSequencesTest, RemoveSequence) {
// Remove |sequence_a| from the PriorityQueue. |sequence_b| is still the
// sequence with the highest priority.
- EXPECT_TRUE(pq.RemoveTaskSource(sequence_a).Unregister());
+ EXPECT_TRUE(pq.RemoveTaskSource(*sequence_a).Unregister());
EXPECT_EQ(sort_key_b, pq.PeekSortKey());
ExpectNumSequences(1U, 0U, 2U);
// RemoveTaskSource() should return false if called on a sequence not in the
// PriorityQueue.
- EXPECT_FALSE(pq.RemoveTaskSource(sequence_a).Unregister());
+ EXPECT_FALSE(pq.RemoveTaskSource(*sequence_a).Unregister());
ExpectNumSequences(1U, 0U, 2U);
// Remove |sequence_b| from the PriorityQueue. |sequence_c| becomes the
// sequence with the highest priority.
- EXPECT_TRUE(pq.RemoveTaskSource(sequence_b).Unregister());
+ EXPECT_TRUE(pq.RemoveTaskSource(*sequence_b).Unregister());
EXPECT_EQ(sort_key_c, pq.PeekSortKey());
ExpectNumSequences(1U, 0U, 1U);
// Remove |sequence_d| from the PriorityQueue. |sequence_c| is still the
// sequence with the highest priority.
- EXPECT_TRUE(pq.RemoveTaskSource(sequence_d).Unregister());
+ EXPECT_TRUE(pq.RemoveTaskSource(*sequence_d).Unregister());
EXPECT_EQ(sort_key_c, pq.PeekSortKey());
ExpectNumSequences(0U, 0U, 1U);
// Remove |sequence_c| from the PriorityQueue, making it empty.
- EXPECT_TRUE(pq.RemoveTaskSource(sequence_c).Unregister());
+ EXPECT_TRUE(pq.RemoveTaskSource(*sequence_c).Unregister());
EXPECT_TRUE(pq.IsEmpty());
ExpectNumSequences(0U, 0U, 0U);
// Return false if RemoveTaskSource() is called on an empty PriorityQueue.
- EXPECT_FALSE(pq.RemoveTaskSource(sequence_c).Unregister());
+ EXPECT_FALSE(pq.RemoveTaskSource(*sequence_c).Unregister());
ExpectNumSequences(0U, 0U, 0U);
}
diff --git a/chromium/base/task/thread_pool/sequence.cc b/chromium/base/task/thread_pool/sequence.cc
index 3a4139c551e..ea7ce3053de 100644
--- a/chromium/base/task/thread_pool/sequence.cc
+++ b/chromium/base/task/thread_pool/sequence.cc
@@ -91,6 +91,7 @@ bool Sequence::DidProcessTask(TaskSource::Transaction* transaction) {
// WillRunTask().
DCHECK(has_worker_);
has_worker_ = false;
+ // See comment on TaskSource::task_runner_ for lifetime management details.
if (queue_.empty()) {
ReleaseTaskRunner();
return false;
@@ -108,20 +109,17 @@ SequenceSortKey Sequence::GetSortKey() const {
Optional<Task> Sequence::Clear(TaskSource::Transaction* transaction) {
CheckedAutoLockMaybe auto_lock(transaction ? nullptr : &lock_);
- has_worker_ = false;
- return base::make_optional<Task>(
- FROM_HERE,
- base::BindOnce(
- [](scoped_refptr<Sequence> self, base::queue<Task> queue) {
- bool queue_was_empty = queue.empty();
- while (!queue.empty())
- queue.pop();
- if (!queue_was_empty) {
- self->ReleaseTaskRunner();
- }
- },
- scoped_refptr<Sequence>(this), std::move(queue_)),
- TimeDelta());
+ // See comment on TaskSource::task_runner_ for lifetime management details.
+ if (!queue_.empty() && !has_worker_)
+ ReleaseTaskRunner();
+ return base::make_optional<Task>(FROM_HERE,
+ base::BindOnce(
+ [](base::queue<Task> queue) {
+ while (!queue.empty())
+ queue.pop();
+ },
+ std::move(queue_)),
+ TimeDelta());
}
void Sequence::ReleaseTaskRunner() {
diff --git a/chromium/base/task/thread_pool/sequence.h b/chromium/base/task/thread_pool/sequence.h
index cbbbda42645..ca505ae84db 100644
--- a/chromium/base/task/thread_pool/sequence.h
+++ b/chromium/base/task/thread_pool/sequence.h
@@ -104,8 +104,7 @@ class BASE_EXPORT Sequence : public TaskSource {
bool DidProcessTask(TaskSource::Transaction* transaction) override;
SequenceSortKey GetSortKey() const override;
- // Releases reference to TaskRunner. This might cause this object to be
- // deleted; therefore, no member access should be made after this method.
+ // Releases reference to TaskRunner.
void ReleaseTaskRunner();
const SequenceToken token_ = SequenceToken::Create();
diff --git a/chromium/base/task/thread_pool/sequence_unittest.cc b/chromium/base/task/thread_pool/sequence_unittest.cc
index 7257a20d6f1..c5166239151 100644
--- a/chromium/base/task/thread_pool/sequence_unittest.cc
+++ b/chromium/base/task/thread_pool/sequence_unittest.cc
@@ -184,7 +184,7 @@ TEST(ThreadPoolSequenceTest, GetSortKeyForeground) {
// Verify that a DCHECK fires if DidProcessTask() is called on a sequence which
// didn't return a Task.
-TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutTakeTask) {
+TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutWillRunTask) {
scoped_refptr<Sequence> sequence = MakeRefCounted<Sequence>(
TaskTraits(ThreadPool()), nullptr, TaskSourceExecutionMode::kParallel);
Sequence::Transaction sequence_transaction(sequence->BeginTransaction());
@@ -193,7 +193,6 @@ TEST(ThreadPoolSequenceTest, DidProcessTaskWithoutTakeTask) {
auto registered_task_source =
RegisteredTaskSource::CreateForTesting(sequence);
EXPECT_DCHECK_DEATH({
- registered_task_source.WillRunTask();
registered_task_source.DidProcessTask(&sequence_transaction);
});
}
diff --git a/chromium/base/task/thread_pool/service_thread.cc b/chromium/base/task/thread_pool/service_thread.cc
index 400fbb0590e..a7e1d21061a 100644
--- a/chromium/base/task/thread_pool/service_thread.cc
+++ b/chromium/base/task/thread_pool/service_thread.cc
@@ -71,39 +71,27 @@ void ServiceThread::PerformHeartbeatLatencyReport() const {
if (!task_tracker_)
return;
- static constexpr TaskTraits kReportedTraits[] = {
- {ThreadPool(), TaskPriority::BEST_EFFORT},
- {ThreadPool(), TaskPriority::BEST_EFFORT, MayBlock()},
- {ThreadPool(), TaskPriority::USER_VISIBLE},
- {ThreadPool(), TaskPriority::USER_VISIBLE, MayBlock()},
- {ThreadPool(), TaskPriority::USER_BLOCKING},
- {ThreadPool(), TaskPriority::USER_BLOCKING, MayBlock()}};
-
- // Only record latency for one set of TaskTraits per report to avoid bias in
- // the order in which tasks are posted (should we record all at once) as well
- // as to avoid spinning up many worker threads to process this report if the
+ // Only record latency for one TaskPriority per report to avoid bias in the
+ // order in which tasks are posted (should we record all at once) as well as
+ // to avoid spinning up many worker threads to process this report if the
// thread pool is currently idle (each thread group keeps at least one idle
// thread so a single task isn't an issue).
// Invoke RandInt() out-of-line to ensure it's obtained before
// TimeTicks::Now().
- const TaskTraits& profiled_traits =
- kReportedTraits[RandInt(0, base::size(kReportedTraits) - 1)];
+ const TaskPriority profiled_priority = static_cast<TaskPriority>(
+ RandInt(static_cast<int>(TaskPriority::LOWEST),
+ static_cast<int>(TaskPriority::HIGHEST)));
// Post through the static API to time the full stack. Use a new Now() for
// every set of traits in case PostTask() itself is slow.
// Bonus: this approach also includes the overhead of BindOnce() in the
// reported latency.
- // TODO(jessemckenna): pass |profiled_traits| directly to
- // RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms() once compiler
- // error on NaCl is fixed
- TaskPriority task_priority = profiled_traits.priority();
- bool may_block = profiled_traits.may_block();
base::PostTask(
- FROM_HERE, profiled_traits,
+ FROM_HERE, {base::ThreadPool(), profiled_priority},
BindOnce(
&TaskTracker::RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms,
- Unretained(task_tracker_), task_priority, may_block, TimeTicks::Now(),
+ Unretained(task_tracker_), profiled_priority, TimeTicks::Now(),
task_tracker_->GetNumTasksRun()));
}
diff --git a/chromium/base/task/thread_pool/service_thread_unittest.cc b/chromium/base/task/thread_pool/service_thread_unittest.cc
index ce098456877..7b3d3c21031 100644
--- a/chromium/base/task/thread_pool/service_thread_unittest.cc
+++ b/chromium/base/task/thread_pool/service_thread_unittest.cc
@@ -65,15 +65,9 @@ TEST(ThreadPoolServiceThreadIntegrationTest, HeartbeatLatencyReport) {
"ThreadPool.HeartbeatLatencyMicroseconds.Test."
"UserBlockingTaskPriority",
"ThreadPool.HeartbeatLatencyMicroseconds.Test."
- "UserBlockingTaskPriority_MayBlock",
- "ThreadPool.HeartbeatLatencyMicroseconds.Test."
"UserVisibleTaskPriority",
"ThreadPool.HeartbeatLatencyMicroseconds.Test."
- "UserVisibleTaskPriority_MayBlock",
- "ThreadPool.HeartbeatLatencyMicroseconds.Test."
- "BackgroundTaskPriority",
- "ThreadPool.HeartbeatLatencyMicroseconds.Test."
- "BackgroundTaskPriority_MayBlock"};
+ "BackgroundTaskPriority"};
// Each report hits a single histogram above (randomly selected). But 1000
// reports should touch all histograms at least once the vast majority of the
diff --git a/chromium/base/task/thread_pool/task_source.cc b/chromium/base/task/thread_pool/task_source.cc
index 271988c2400..9674346b962 100644
--- a/chromium/base/task/thread_pool/task_source.cc
+++ b/chromium/base/task/thread_pool/task_source.cc
@@ -131,7 +131,6 @@ Optional<Task> RegisteredTaskSource::TakeTask(
DCHECK(!transaction || transaction->task_source() == get());
#if DCHECK_IS_ON()
DCHECK_EQ(State::kReady, run_step_);
- run_step_ = State::kTaskAcquired;
#endif // DCHECK_IS_ON()
return task_source_->TakeTask(transaction);
}
@@ -139,9 +138,6 @@ Optional<Task> RegisteredTaskSource::TakeTask(
Optional<Task> RegisteredTaskSource::Clear(
TaskSource::Transaction* transaction) {
DCHECK(!transaction || transaction->task_source() == get());
-#if DCHECK_IS_ON()
- run_step_ = State::kInitial;
-#endif // DCHECK_IS_ON()
return task_source_->Clear(transaction);
}
@@ -149,7 +145,7 @@ bool RegisteredTaskSource::DidProcessTask(
TaskSource::Transaction* transaction) {
DCHECK(!transaction || transaction->task_source() == get());
#if DCHECK_IS_ON()
- DCHECK_EQ(State::kTaskAcquired, run_step_);
+ DCHECK_EQ(State::kReady, run_step_);
run_step_ = State::kInitial;
#endif // DCHECK_IS_ON()
return task_source_->DidProcessTask(transaction);
diff --git a/chromium/base/task/thread_pool/task_source.h b/chromium/base/task/thread_pool/task_source.h
index e56b42eda69..4dfa0875885 100644
--- a/chromium/base/task/thread_pool/task_source.h
+++ b/chromium/base/task/thread_pool/task_source.h
@@ -55,9 +55,9 @@ struct BASE_EXPORT ExecutionEnvironment {
// RegisteredTaskSource after obtaining it from the queue:
// 1- Check whether a task can run with WillRunTask() (and register/enqueue the
// task source again if not saturated).
-// 2- Iff (1) determined that a task can run, access the next task with
-// TakeTask().
-// 3- Execute the task.
+// 2- (optional) Iff (1) determined that a task can run, access the next task
+// with TakeTask().
+// 3- (optional) Execute the task.
// 4- Inform the task source that a task was processed with DidProcessTask(),
// and re-enqueue the task source iff requested.
// When a task source is registered multiple times, many overlapping chains of
@@ -86,10 +86,10 @@ class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> {
enum class RunStatus {
// TakeTask() cannot be called.
kDisallowed,
- // TakeTask() must be called, and the TaskSource has not reached its maximum
+ // TakeTask() may called, and the TaskSource has not reached its maximum
// concurrency (i.e. the TaskSource still needs to be queued).
kAllowedNotSaturated,
- // TakeTask() must be called, and the TaskSource has reached its maximum
+ // TakeTask() may called, and the TaskSource has reached its maximum
// concurrency (i.e. the TaskSource no longer needs to be queued).
kAllowedSaturated,
};
@@ -218,7 +218,7 @@ class BASE_EXPORT TaskSource : public RefCountedThreadSafe<TaskSource> {
// A pointer to the TaskRunner that posts to this TaskSource, if any. The
// derived class is responsible for calling AddRef() when a TaskSource from
// which no Task is executing becomes non-empty and Release() when
- // DidProcessTask() returns false.
+ // it becomes empty again (e.g. when DidProcessTask() returns false).
TaskRunner* task_runner_;
TaskSourceExecutionMode execution_mode_;
@@ -270,11 +270,11 @@ class BASE_EXPORT RegisteredTaskSource {
Optional<Task> TakeTask(TaskSource::Transaction* transaction = nullptr)
WARN_UNUSED_RESULT;
- // Must be called once the task was run. This resets this RegisteredTaskSource
- // to its initial state so that WillRunTask() may be called again.
- // |transaction| is optional and should only be provided if this operation is
- // already part of a transaction. Returns true if the TaskSource should be
- // queued after this operation.
+ // Must be called after WillRunTask() or once the task was run if TakeTask()
+ // was called. This resets this RegisteredTaskSource to its initial state so
+ // that WillRunTask() may be called again. |transaction| is optional and
+ // should only be provided if this operation is already part of a transaction.
+ // Returns true if the TaskSource should be queued after this operation.
bool DidProcessTask(TaskSource::Transaction* transaction = nullptr);
// Returns a task that clears this TaskSource to make it empty. |transaction|
@@ -293,7 +293,6 @@ class BASE_EXPORT RegisteredTaskSource {
enum class State {
kInitial, // WillRunTask() may be called.
kReady, // After WillRunTask() returned a valid RunStatus.
- kTaskAcquired, // After TakeTask().
};
State run_step_ = State::kInitial;
diff --git a/chromium/base/task/thread_pool/task_tracker.cc b/chromium/base/task/thread_pool/task_tracker.cc
index d21422bc9fa..a74f14003b3 100644
--- a/chromium/base/task/thread_pool/task_tracker.cc
+++ b/chromium/base/task/thread_pool/task_tracker.cc
@@ -20,6 +20,7 @@
#include "base/sequence_token.h"
#include "base/synchronization/condition_variable.h"
#include "base/task/scoped_set_task_priority_for_current_thread.h"
+#include "base/task/task_executor.h"
#include "base/threading/sequence_local_storage_map.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
@@ -27,6 +28,7 @@
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
+#include "build/build_config.h"
namespace base {
namespace internal {
@@ -75,59 +77,34 @@ void TaskTracingInfo::AppendAsTraceFormat(std::string* out) const {
out->append(tmp);
}
-// Constructs a histogram to track latency which is logging to
-// "ThreadPool.{histogram_name}.{histogram_label}.{task_type_suffix}".
-HistogramBase* GetLatencyHistogram(StringPiece histogram_name,
- StringPiece histogram_label,
- StringPiece task_type_suffix) {
- DCHECK(!histogram_name.empty());
- DCHECK(!histogram_label.empty());
- DCHECK(!task_type_suffix.empty());
- // Mimics the UMA_HISTOGRAM_HIGH_RESOLUTION_CUSTOM_TIMES macro. The minimums
- // and maximums were chosen to place the 1ms mark at around the 70% range
- // coverage for buckets giving us good info for tasks that have a latency
- // below 1ms (most of them) and enough info to assess how bad the latency is
- // for tasks that exceed this threshold.
- const std::string histogram = JoinString(
- {"ThreadPool", histogram_name, histogram_label, task_type_suffix}, ".");
- return Histogram::FactoryMicrosecondsTimeGet(
- histogram, TimeDelta::FromMicroseconds(1),
- TimeDelta::FromMilliseconds(20), 50,
- HistogramBase::kUmaTargetedHistogramFlag);
-}
-
-// Constructs a histogram to track task count which is logging to
-// "ThreadPool.{histogram_name}.{histogram_label}.{task_type_suffix}".
-HistogramBase* GetCountHistogram(StringPiece histogram_name,
- StringPiece histogram_label,
- StringPiece task_type_suffix) {
- DCHECK(!histogram_name.empty());
- DCHECK(!histogram_label.empty());
- DCHECK(!task_type_suffix.empty());
- // Mimics the UMA_HISTOGRAM_CUSTOM_COUNTS macro.
- const std::string histogram = JoinString(
- {"ThreadPool", histogram_name, histogram_label, task_type_suffix}, ".");
- // 500 was chosen as the maximum number of tasks run while queuing because
- // values this high would likely indicate an error, beyond which knowing the
- // actual number of tasks is not informative.
- return Histogram::FactoryGet(histogram, 1, 500, 50,
- HistogramBase::kUmaTargetedHistogramFlag);
-}
-
-// Returns a histogram stored in a 2D array indexed by task priority and
-// whether it may block.
-// TODO(jessemckenna): use the STATIC_HISTOGRAM_POINTER_GROUP macro from
-// histogram_macros.h instead.
-HistogramBase* GetHistogramForTaskTraits(
- TaskTraits task_traits,
- HistogramBase* const (*histograms)[2]) {
- return histograms[static_cast<int>(task_traits.priority())]
- [task_traits.may_block() ||
- task_traits.with_base_sync_primitives()
- ? 1
- : 0];
+const char* GetTaskPrioritySuffix(TaskPriority priority) {
+ switch (priority) {
+ case TaskPriority::BEST_EFFORT:
+ return "BackgroundTaskPriority";
+ case TaskPriority::USER_VISIBLE:
+ return "UserVisibleTaskPriority";
+ case TaskPriority::USER_BLOCKING:
+ return "UserBlockingTaskPriority";
+ }
}
+// Records |time_sample| to the histogram |histogram_name|.|priority suffix|,
+// where |priority_suffix| is derived from |priority|.
+//
+// The minimums and maximums were chosen to place the 1ms mark at around the 70%
+// range coverage for buckets giving us good info for tasks that have a latency
+// below 1ms (most of them) and enough info to assess how bad the latency is for
+// tasks that exceed this threshold.
+#define STATIC_LATENCY_HISTOGRAM_POINTER_GROUP(histogram_name, priority, \
+ time_sample) \
+ STATIC_HISTOGRAM_POINTER_GROUP( \
+ histogram_name, static_cast<int>(priority), \
+ static_cast<int>(TaskPriority::HIGHEST) + 1, AddTime(time_sample), \
+ Histogram::FactoryMicrosecondsTimeGet( \
+ histogram_name, TimeDelta::FromMicroseconds(1), \
+ TimeDelta::FromMilliseconds(20), 50, \
+ HistogramBase::kUmaTargetedHistogramFlag));
+
bool HasLogBestEffortTasksSwitch() {
// The CommandLine might not be initialized if ThreadPool is initialized in a
// dynamic library which doesn't have access to argc/argv.
@@ -136,6 +113,84 @@ bool HasLogBestEffortTasksSwitch() {
switches::kLogBestEffortTasks);
}
+// Needed for PostTaskHere and CurrentThread. This executor lives for the
+// duration of a threadpool task invocation.
+class EphemeralTaskExecutor : public TaskExecutor {
+ public:
+ // |sequenced_task_runner| and |single_thread_task_runner| must outlive this
+ // EphemeralTaskExecutor.
+ EphemeralTaskExecutor(SequencedTaskRunner* sequenced_task_runner,
+ SingleThreadTaskRunner* single_thread_task_runner,
+ const TaskTraits* sequence_traits)
+ : sequenced_task_runner_(sequenced_task_runner),
+ single_thread_task_runner_(single_thread_task_runner),
+ sequence_traits_(sequence_traits) {
+ SetTaskExecutorForCurrentThread(this);
+ }
+
+ ~EphemeralTaskExecutor() override {
+ SetTaskExecutorForCurrentThread(nullptr);
+ }
+
+ // TaskExecutor:
+ bool PostDelayedTask(const Location& from_here,
+ const TaskTraits& traits,
+ OnceClosure task,
+ TimeDelta delay) override {
+ CheckTraitsCompatibleWithSequenceTraits(traits);
+ return sequenced_task_runner_->PostDelayedTask(from_here, std::move(task),
+ delay);
+ }
+
+ scoped_refptr<TaskRunner> CreateTaskRunner(
+ const TaskTraits& traits) override {
+ CheckTraitsCompatibleWithSequenceTraits(traits);
+ return sequenced_task_runner_;
+ }
+
+ scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunner(
+ const TaskTraits& traits) override {
+ CheckTraitsCompatibleWithSequenceTraits(traits);
+ return sequenced_task_runner_;
+ }
+
+ scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunner(
+ const TaskTraits& traits,
+ SingleThreadTaskRunnerThreadMode thread_mode) override {
+ CheckTraitsCompatibleWithSequenceTraits(traits);
+ return single_thread_task_runner_;
+ }
+
+#if defined(OS_WIN)
+ scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner(
+ const TaskTraits& traits,
+ SingleThreadTaskRunnerThreadMode thread_mode) override {
+ CheckTraitsCompatibleWithSequenceTraits(traits);
+ return single_thread_task_runner_;
+ }
+#endif // defined(OS_WIN)
+
+ private:
+ // Currently ignores |traits.priority()|.
+ void CheckTraitsCompatibleWithSequenceTraits(const TaskTraits& traits) {
+ if (traits.shutdown_behavior_set_explicitly()) {
+ DCHECK_EQ(traits.shutdown_behavior(),
+ sequence_traits_->shutdown_behavior());
+ }
+
+ DCHECK(!traits.may_block() ||
+ traits.may_block() == sequence_traits_->may_block());
+
+ DCHECK(!traits.with_base_sync_primitives() ||
+ traits.with_base_sync_primitives() ==
+ sequence_traits_->with_base_sync_primitives());
+ }
+
+ SequencedTaskRunner* const sequenced_task_runner_;
+ SingleThreadTaskRunner* const single_thread_task_runner_;
+ const TaskTraits* const sequence_traits_;
+};
+
} // namespace
// Atomic internal state used by TaskTracker to track items that are blocking
@@ -228,78 +283,14 @@ class TaskTracker::State {
DISALLOW_COPY_AND_ASSIGN(State);
};
-// TODO(jessemckenna): Write a helper function to avoid code duplication below.
TaskTracker::TaskTracker(StringPiece histogram_label)
- : has_log_best_effort_tasks_switch_(HasLogBestEffortTasksSwitch()),
+ : histogram_label_(histogram_label),
+ has_log_best_effort_tasks_switch_(HasLogBestEffortTasksSwitch()),
state_(new State),
can_run_policy_(CanRunPolicy::kAll),
flush_cv_(flush_lock_.CreateConditionVariable()),
shutdown_lock_(&flush_lock_),
- task_latency_histograms_{
- {GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "BackgroundTaskPriority"),
- GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "BackgroundTaskPriority_MayBlock")},
- {GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "UserVisibleTaskPriority"),
- GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "UserVisibleTaskPriority_MayBlock")},
- {GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "UserBlockingTaskPriority"),
- GetLatencyHistogram("TaskLatencyMicroseconds",
- histogram_label,
- "UserBlockingTaskPriority_MayBlock")}},
- heartbeat_latency_histograms_{
- {GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "BackgroundTaskPriority"),
- GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "BackgroundTaskPriority_MayBlock")},
- {GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "UserVisibleTaskPriority"),
- GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "UserVisibleTaskPriority_MayBlock")},
- {GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "UserBlockingTaskPriority"),
- GetLatencyHistogram("HeartbeatLatencyMicroseconds",
- histogram_label,
- "UserBlockingTaskPriority_MayBlock")}},
- num_tasks_run_while_queuing_histograms_{
- {GetCountHistogram("NumTasksRunWhileQueuing",
- histogram_label,
- "BackgroundTaskPriority"),
- GetCountHistogram("NumTasksRunWhileQueuing",
- histogram_label,
- "BackgroundTaskPriority_MayBlock")},
- {GetCountHistogram("NumTasksRunWhileQueuing",
- histogram_label,
- "UserVisibleTaskPriority"),
- GetCountHistogram("NumTasksRunWhileQueuing",
- histogram_label,
- "UserVisibleTaskPriority_MayBlock")},
- {GetCountHistogram("NumTasksRunWhileQueuing",
- histogram_label,
- "UserBlockingTaskPriority"),
- GetCountHistogram("NumTasksRunWhileQueuing",
- histogram_label,
- "UserBlockingTaskPriority_MayBlock")}},
- tracked_ref_factory_(this) {
- // Confirm that all |task_latency_histograms_| have been initialized above.
- for (TaskPriorityType i = 0; i < kNumTaskPriorities; ++i) {
- for (uint8_t j = 0; j < kNumBlockingModes; ++j) {
- DCHECK(task_latency_histograms_[i][j]);
- }
- }
-}
+ tracked_ref_factory_(this) {}
TaskTracker::~TaskTracker() = default;
@@ -441,7 +432,7 @@ RegisteredTaskSource TaskTracker::RunAndPopNextTask(
RegisteredTaskSource task_source) {
DCHECK(task_source);
- const bool can_run_worker_task =
+ const bool task_is_worker_task =
BeforeRunTask(task_source->shutdown_behavior());
// Run the next task in |task_source|.
@@ -449,7 +440,7 @@ RegisteredTaskSource TaskTracker::RunAndPopNextTask(
TaskTraits traits{ThreadPool()};
{
auto transaction = task_source->BeginTransaction();
- task = can_run_worker_task ? task_source.TakeTask(&transaction)
+ task = task_is_worker_task ? task_source.TakeTask(&transaction)
: task_source.Clear(&transaction);
traits = transaction.traits();
}
@@ -458,13 +449,12 @@ RegisteredTaskSource TaskTracker::RunAndPopNextTask(
// Run the |task| (whether it's a worker task or the Clear() closure).
RunTask(std::move(task.value()), task_source.get(), traits);
}
- if (can_run_worker_task) {
+ if (task_is_worker_task)
AfterRunTask(task_source->shutdown_behavior());
- const bool task_source_must_be_queued = task_source.DidProcessTask();
- // |task_source| should be reenqueued iff requested by DidProcessTask().
- if (task_source_must_be_queued)
- return task_source;
- }
+ const bool task_source_must_be_queued = task_source.DidProcessTask();
+ // |task_source| should be reenqueued iff requested by DidProcessTask().
+ if (task_source_must_be_queued)
+ return task_source;
return nullptr;
}
@@ -477,37 +467,50 @@ bool TaskTracker::IsShutdownComplete() const {
return shutdown_event_ && shutdown_event_->IsSignaled();
}
-void TaskTracker::RecordLatencyHistogram(
- LatencyHistogramType latency_histogram_type,
- TaskTraits task_traits,
- TimeTicks posted_time) const {
- const TimeDelta task_latency = TimeTicks::Now() - posted_time;
+void TaskTracker::RecordLatencyHistogram(TaskPriority priority,
+ TimeTicks posted_time) const {
+ if (histogram_label_.empty())
+ return;
- DCHECK(latency_histogram_type == LatencyHistogramType::TASK_LATENCY ||
- latency_histogram_type == LatencyHistogramType::HEARTBEAT_LATENCY);
- const auto& histograms =
- latency_histogram_type == LatencyHistogramType::TASK_LATENCY
- ? task_latency_histograms_
- : heartbeat_latency_histograms_;
- GetHistogramForTaskTraits(task_traits, histograms)
- ->AddTimeMicrosecondsGranularity(task_latency);
+ auto get_latency_histogram_name = [this, priority]() {
+ return JoinString({"ThreadPool.TaskLatencyMicroseconds", histogram_label_,
+ GetTaskPrioritySuffix(priority)},
+ ".");
+ };
+ STATIC_LATENCY_HISTOGRAM_POINTER_GROUP(get_latency_histogram_name(), priority,
+ TimeTicks::Now() - posted_time);
}
void TaskTracker::RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms(
- TaskPriority task_priority,
- bool may_block,
+ TaskPriority priority,
TimeTicks posted_time,
int num_tasks_run_when_posted) const {
- TaskTraits task_traits{ThreadPool()};
- if (may_block)
- task_traits = TaskTraits(ThreadPool(), task_priority, MayBlock());
- else
- task_traits = TaskTraits(ThreadPool(), task_priority);
- RecordLatencyHistogram(LatencyHistogramType::HEARTBEAT_LATENCY, task_traits,
- posted_time);
- GetHistogramForTaskTraits(task_traits,
- num_tasks_run_while_queuing_histograms_)
- ->Add(GetNumTasksRun() - num_tasks_run_when_posted);
+ if (histogram_label_.empty())
+ return;
+
+ auto get_heartbeat_latency_histogram_name = [this, priority]() {
+ return JoinString({"ThreadPool.HeartbeatLatencyMicroseconds",
+ histogram_label_, GetTaskPrioritySuffix(priority)},
+ ".");
+ };
+ STATIC_LATENCY_HISTOGRAM_POINTER_GROUP(get_heartbeat_latency_histogram_name(),
+ priority,
+ TimeTicks::Now() - posted_time);
+
+ auto get_num_tasks_run_while_queuing_histogram_name = [this, priority]() {
+ return JoinString({"ThreadPool.NumTasksRunWhileQueuing", histogram_label_,
+ GetTaskPrioritySuffix(priority)},
+ ".");
+ };
+ STATIC_HISTOGRAM_POINTER_GROUP(
+ get_num_tasks_run_while_queuing_histogram_name(),
+ static_cast<int>(priority), static_cast<int>(TaskPriority::HIGHEST) + 1,
+ Add(GetNumTasksRun() - num_tasks_run_when_posted),
+ // 500 was chosen as the maximum number of tasks run while queuing because
+ // values this high would likely indicate an error, beyond which knowing
+ // the actual number of tasks is not informative.
+ Histogram::FactoryGet(get_num_tasks_run_while_queuing_histogram_name(), 1,
+ 500, 50, HistogramBase::kUmaTargetedHistogramFlag));
}
int TaskTracker::GetNumTasksRun() const {
@@ -522,8 +525,7 @@ void TaskTracker::RunTask(Task task,
TaskSource* task_source,
const TaskTraits& traits) {
DCHECK(task_source);
- RecordLatencyHistogram(LatencyHistogramType::TASK_LATENCY, traits,
- task.queue_time);
+ RecordLatencyHistogram(traits.priority(), task.queue_time);
const auto environment = task_source->GetExecutionEnvironment();
@@ -557,6 +559,7 @@ void TaskTracker::RunTask(Task task,
// Set up TaskRunnerHandle as expected for the scope of the task.
Optional<SequencedTaskRunnerHandle> sequenced_task_runner_handle;
Optional<ThreadTaskRunnerHandle> single_thread_task_runner_handle;
+ Optional<EphemeralTaskExecutor> ephemiral_task_executor;
switch (task_source->execution_mode()) {
case TaskSourceExecutionMode::kJob:
case TaskSourceExecutionMode::kParallel:
@@ -565,11 +568,18 @@ void TaskTracker::RunTask(Task task,
DCHECK(task_source->task_runner());
sequenced_task_runner_handle.emplace(
static_cast<SequencedTaskRunner*>(task_source->task_runner()));
+ ephemiral_task_executor.emplace(
+ static_cast<SequencedTaskRunner*>(task_source->task_runner()),
+ nullptr, &traits);
break;
case TaskSourceExecutionMode::kSingleThread:
DCHECK(task_source->task_runner());
single_thread_task_runner_handle.emplace(
static_cast<SingleThreadTaskRunner*>(task_source->task_runner()));
+ ephemiral_task_executor.emplace(
+ static_cast<SequencedTaskRunner*>(task_source->task_runner()),
+ static_cast<SingleThreadTaskRunner*>(task_source->task_runner()),
+ &traits);
break;
}
diff --git a/chromium/base/task/thread_pool/task_tracker.h b/chromium/base/task/thread_pool/task_tracker.h
index bb5296b7da1..59f0726b8eb 100644
--- a/chromium/base/task/thread_pool/task_tracker.h
+++ b/chromium/base/task/thread_pool/task_tracker.h
@@ -31,7 +31,6 @@
namespace base {
class ConditionVariable;
-class HistogramBase;
namespace internal {
@@ -54,7 +53,8 @@ enum class CanRunPolicy {
// and records metrics and trace events. This class is thread-safe.
class BASE_EXPORT TaskTracker {
public:
- // |histogram_label| is used as a suffix for histograms, it must not be empty.
+ // |histogram_label| is used to label histograms. No histograms are recorded
+ // if it is empty.
TaskTracker(StringPiece histogram_label);
virtual ~TaskTracker();
@@ -131,26 +131,15 @@ class BASE_EXPORT TaskTracker {
// no tasks are blocking shutdown).
bool IsShutdownComplete() const;
- enum class LatencyHistogramType {
- // Records the latency of each individual task posted through TaskTracker.
- TASK_LATENCY,
- // Records the latency of heartbeat tasks which are independent of current
- // workload. These avoid a bias towards TASK_LATENCY reporting that high-
- // priority tasks are "slower" than regular tasks because high-priority
- // tasks tend to be correlated with heavy workloads.
- HEARTBEAT_LATENCY,
- };
-
// Records two histograms
// 1. ThreadPool.[label].HeartbeatLatencyMicroseconds.[suffix]:
// Now() - posted_time
// 2. ThreadPool.[label].NumTasksRunWhileQueuing.[suffix]:
// GetNumTasksRun() - num_tasks_run_when_posted.
// [label] is the histogram label provided to the constructor.
- // [suffix] is derived from |task_priority| and |may_block|.
+ // [suffix] is derived from |task_priority|.
void RecordHeartbeatLatencyAndTasksRunWhileQueuingHistograms(
TaskPriority task_priority,
- bool may_block,
TimeTicks posted_time,
int num_tasks_run_when_posted) const;
@@ -213,10 +202,9 @@ class BASE_EXPORT TaskTracker {
// manner.
void CallFlushCallbackForTesting();
- // Records |Now() - posted_time| to the appropriate |latency_histogram_type|
- // based on |task_traits|.
- void RecordLatencyHistogram(LatencyHistogramType latency_histogram_type,
- TaskTraits task_traits,
+ // Records |Now() - posted_time| to the
+ // ThreadPool.TaskLatencyMicroseconds.[label].[priority] histogram.
+ void RecordLatencyHistogram(TaskPriority priority,
TimeTicks posted_time) const;
void IncrementNumTasksRun();
@@ -230,6 +218,9 @@ class BASE_EXPORT TaskTracker {
TaskAnnotator task_annotator_;
+ // Suffix for histograms recorded by this TaskTracker.
+ const std::string histogram_label_;
+
// Indicates whether logging information about TaskPriority::BEST_EFFORT tasks
// was enabled with a command line switch.
const bool has_log_best_effort_tasks_switch_;
@@ -277,25 +268,6 @@ class BASE_EXPORT TaskTracker {
// a task queued to histogram.
std::atomic_int num_tasks_run_{0};
- // ThreadPool.TaskLatencyMicroseconds.*,
- // ThreadPool.HeartbeatLatencyMicroseconds.*, and
- // ThreadPool.NumTasksRunWhileQueuing.* histograms. The first index is
- // a TaskPriority. The second index is 0 for non-blocking tasks, 1 for
- // blocking tasks. Intentionally leaked.
- // TODO(scheduler-dev): Consider using STATIC_HISTOGRAM_POINTER_GROUP for
- // these.
- using TaskPriorityType = std::underlying_type<TaskPriority>::type;
- static constexpr TaskPriorityType kNumTaskPriorities =
- static_cast<TaskPriorityType>(TaskPriority::HIGHEST) + 1;
- static constexpr uint8_t kNumBlockingModes = 2;
- HistogramBase* const task_latency_histograms_[kNumTaskPriorities]
- [kNumBlockingModes];
- HistogramBase* const heartbeat_latency_histograms_[kNumTaskPriorities]
- [kNumBlockingModes];
- HistogramBase* const
- num_tasks_run_while_queuing_histograms_[kNumTaskPriorities]
- [kNumBlockingModes];
-
// Ensures all state (e.g. dangling cleaned up workers) is coalesced before
// destroying the TaskTracker (e.g. in test environments).
// Ref. https://crbug.com/827615.
diff --git a/chromium/base/task/thread_pool/task_tracker_unittest.cc b/chromium/base/task/thread_pool/task_tracker_unittest.cc
index fa231c9b7be..a3ba41cf559 100644
--- a/chromium/base/task/thread_pool/task_tracker_unittest.cc
+++ b/chromium/base/task/thread_pool/task_tracker_unittest.cc
@@ -19,7 +19,6 @@
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_samples.h"
-#include "base/metrics/statistics_recorder.h"
#include "base/sequence_token.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
@@ -37,6 +36,7 @@
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/simple_thread.h"
+#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -1244,8 +1244,6 @@ TEST(ThreadPoolTaskTrackerWaitAllowedTest, WaitAllowed) {
// Verify that ThreadPool.TaskLatency.* histograms are correctly recorded
// when a task runs.
TEST(ThreadPoolTaskTrackerHistogramTest, TaskLatency) {
- auto statistics_recorder = StatisticsRecorder::CreateTemporaryForTesting();
-
TaskTracker tracker("Test");
struct {
@@ -1257,28 +1255,28 @@ TEST(ThreadPoolTaskTrackerHistogramTest, TaskLatency) {
"BackgroundTaskPriority"},
{{ThreadPool(), MayBlock(), TaskPriority::BEST_EFFORT},
"ThreadPool.TaskLatencyMicroseconds.Test."
- "BackgroundTaskPriority_MayBlock"},
+ "BackgroundTaskPriority"},
{{ThreadPool(), WithBaseSyncPrimitives(), TaskPriority::BEST_EFFORT},
"ThreadPool.TaskLatencyMicroseconds.Test."
- "BackgroundTaskPriority_MayBlock"},
+ "BackgroundTaskPriority"},
{{ThreadPool(), TaskPriority::USER_VISIBLE},
"ThreadPool.TaskLatencyMicroseconds.Test."
"UserVisibleTaskPriority"},
{{ThreadPool(), MayBlock(), TaskPriority::USER_VISIBLE},
"ThreadPool.TaskLatencyMicroseconds.Test."
- "UserVisibleTaskPriority_MayBlock"},
+ "UserVisibleTaskPriority"},
{{ThreadPool(), WithBaseSyncPrimitives(), TaskPriority::USER_VISIBLE},
"ThreadPool.TaskLatencyMicroseconds.Test."
- "UserVisibleTaskPriority_MayBlock"},
+ "UserVisibleTaskPriority"},
{{ThreadPool(), TaskPriority::USER_BLOCKING},
"ThreadPool.TaskLatencyMicroseconds.Test."
"UserBlockingTaskPriority"},
{{ThreadPool(), MayBlock(), TaskPriority::USER_BLOCKING},
"ThreadPool.TaskLatencyMicroseconds.Test."
- "UserBlockingTaskPriority_MayBlock"},
+ "UserBlockingTaskPriority"},
{{ThreadPool(), WithBaseSyncPrimitives(), TaskPriority::USER_BLOCKING},
"ThreadPool.TaskLatencyMicroseconds.Test."
- "UserBlockingTaskPriority_MayBlock"}};
+ "UserBlockingTaskPriority"}};
for (const auto& test : kTests) {
Task task(FROM_HERE, DoNothing(), TimeDelta());
diff --git a/chromium/base/task/thread_pool/test_utils.cc b/chromium/base/task/thread_pool/test_utils.cc
index a02e24bf3a6..d6236373f84 100644
--- a/chromium/base/task/thread_pool/test_utils.cc
+++ b/chromium/base/task/thread_pool/test_utils.cc
@@ -239,6 +239,11 @@ bool MockPooledTaskRunnerDelegate::EnqueueJobTaskSource(
return true;
}
+void MockPooledTaskRunnerDelegate::RemoveJobTaskSource(
+ scoped_refptr<JobTaskSource> task_source) {
+ thread_group_->RemoveTaskSource(*task_source);
+}
+
bool MockPooledTaskRunnerDelegate::IsRunningPoolWithTraits(
const TaskTraits& traits) const {
// |thread_group_| must be initialized with SetThreadGroup() before
diff --git a/chromium/base/task/thread_pool/test_utils.h b/chromium/base/task/thread_pool/test_utils.h
index 1d85ee8cea2..ebdfbe28467 100644
--- a/chromium/base/task/thread_pool/test_utils.h
+++ b/chromium/base/task/thread_pool/test_utils.h
@@ -62,6 +62,7 @@ class MockPooledTaskRunnerDelegate : public PooledTaskRunnerDelegate {
bool PostTaskWithSequence(Task task,
scoped_refptr<Sequence> sequence) override;
bool EnqueueJobTaskSource(scoped_refptr<JobTaskSource> task_source) override;
+ void RemoveJobTaskSource(scoped_refptr<JobTaskSource> task_source) override;
bool ShouldYield(const TaskSource* task_source) const override;
bool IsRunningPoolWithTraits(const TaskTraits& traits) const override;
void UpdatePriority(scoped_refptr<TaskSource> task_source,
diff --git a/chromium/base/task/thread_pool/thread_group.cc b/chromium/base/task/thread_pool/thread_group.cc
index 7f6dcef2630..8de5b518fde 100644
--- a/chromium/base/task/thread_pool/thread_group.cc
+++ b/chromium/base/task/thread_pool/thread_group.cc
@@ -139,9 +139,9 @@ ThreadGroup::GetNumAdditionalWorkersForForegroundTaskSourcesLockRequired()
}
RegisteredTaskSource ThreadGroup::RemoveTaskSource(
- scoped_refptr<TaskSource> task_source) {
+ const TaskSource& task_source) {
CheckedAutoLock auto_lock(lock_);
- return priority_queue_.RemoveTaskSource(std::move(task_source));
+ return priority_queue_.RemoveTaskSource(task_source);
}
void ThreadGroup::ReEnqueueTaskSourceLockRequired(
diff --git a/chromium/base/task/thread_pool/thread_group.h b/chromium/base/task/thread_pool/thread_group.h
index c00755413f5..4db7bd5f66d 100644
--- a/chromium/base/task/thread_pool/thread_group.h
+++ b/chromium/base/task/thread_pool/thread_group.h
@@ -65,7 +65,7 @@ class BASE_EXPORT ThreadGroup {
// RegisteredTaskSource that evaluats to true if successful, or false if
// |task_source| is not currently in |priority_queue_|, such as when a worker
// is running a task from it.
- RegisteredTaskSource RemoveTaskSource(scoped_refptr<TaskSource> task_source);
+ RegisteredTaskSource RemoveTaskSource(const TaskSource& task_source);
// Updates the position of the TaskSource in |transaction| in this
// ThreadGroup's PriorityQueue based on the TaskSource's current traits.
diff --git a/chromium/base/task/thread_pool/thread_group_impl.cc b/chromium/base/task/thread_pool/thread_group_impl.cc
index 378493afc78..0c17b47d31b 100644
--- a/chromium/base/task/thread_pool/thread_group_impl.cc
+++ b/chromium/base/task/thread_pool/thread_group_impl.cc
@@ -41,25 +41,6 @@
#include "base/win/windows_version.h"
#endif // defined(OS_WIN)
-// Data from deprecated UMA histograms:
-//
-// ThreadPool.NumTasksBetweenWaits.(Browser/Renderer).Foreground, August 2019
-// Number of tasks between two waits by a foreground worker thread in a
-// browser/renderer process.
-//
-// Windows (browser/renderer)
-// 1 at 87th percentile / 92th percentile
-// 2 at 95th percentile / 98th percentile
-// 5 at 99th percentile / 100th percentile
-// Mac (browser/renderer)
-// 1 at 81th percentile / 90th percentile
-// 2 at 92th percentile / 97th percentile
-// 5 at 98th percentile / 100th percentile
-// Android (browser/renderer)
-// 1 at 92th percentile / 96th percentile
-// 2 at 97th percentile / 98th percentile
-// 5 at 99th percentile / 100th percentile
-
namespace base {
namespace internal {
@@ -342,40 +323,57 @@ ThreadGroupImpl::ThreadGroupImpl(StringPiece histogram_label,
priority_hint_(priority_hint),
idle_workers_stack_cv_for_testing_(lock_.CreateConditionVariable()),
// Mimics the UMA_HISTOGRAM_LONG_TIMES macro.
- detach_duration_histogram_(Histogram::FactoryTimeGet(
- JoinString({kDetachDurationHistogramPrefix, histogram_label}, ""),
- TimeDelta::FromMilliseconds(1),
- TimeDelta::FromHours(1),
- 50,
- HistogramBase::kUmaTargetedHistogramFlag)),
+ detach_duration_histogram_(
+ histogram_label.empty()
+ ? nullptr
+ : Histogram::FactoryTimeGet(
+ JoinString(
+ {kDetachDurationHistogramPrefix, histogram_label},
+ ""),
+ TimeDelta::FromMilliseconds(1),
+ TimeDelta::FromHours(1),
+ 50,
+ HistogramBase::kUmaTargetedHistogramFlag)),
// Mimics the UMA_HISTOGRAM_COUNTS_1000 macro. When a worker runs more
// than 1000 tasks before detaching, there is no need to know the exact
// number of tasks that ran.
- num_tasks_before_detach_histogram_(Histogram::FactoryGet(
- JoinString({kNumTasksBeforeDetachHistogramPrefix, histogram_label},
- ""),
- 1,
- 1000,
- 50,
- HistogramBase::kUmaTargetedHistogramFlag)),
+ num_tasks_before_detach_histogram_(
+ histogram_label.empty()
+ ? nullptr
+ : Histogram::FactoryGet(
+ JoinString(
+ {kNumTasksBeforeDetachHistogramPrefix, histogram_label},
+ ""),
+ 1,
+ 1000,
+ 50,
+ HistogramBase::kUmaTargetedHistogramFlag)),
// Mimics the UMA_HISTOGRAM_COUNTS_100 macro. A ThreadGroup is
// expected to run between zero and a few tens of workers.
// When it runs more than 100 worker, there is no need to know the exact
// number of workers that ran.
- num_workers_histogram_(Histogram::FactoryGet(
- JoinString({kNumWorkersHistogramPrefix, histogram_label}, ""),
- 1,
- 100,
- 50,
- HistogramBase::kUmaTargetedHistogramFlag)),
- num_active_workers_histogram_(Histogram::FactoryGet(
- JoinString({kNumActiveWorkersHistogramPrefix, histogram_label}, ""),
- 1,
- 100,
- 50,
- HistogramBase::kUmaTargetedHistogramFlag)),
+ num_workers_histogram_(
+ histogram_label.empty()
+ ? nullptr
+ : Histogram::FactoryGet(
+ JoinString({kNumWorkersHistogramPrefix, histogram_label},
+ ""),
+ 1,
+ 100,
+ 50,
+ HistogramBase::kUmaTargetedHistogramFlag)),
+ num_active_workers_histogram_(
+ histogram_label.empty()
+ ? nullptr
+ : Histogram::FactoryGet(
+ JoinString(
+ {kNumActiveWorkersHistogramPrefix, histogram_label},
+ ""),
+ 1,
+ 100,
+ 50,
+ HistogramBase::kUmaTargetedHistogramFlag)),
tracked_ref_factory_(this) {
- DCHECK(!histogram_label.empty());
DCHECK(!thread_group_label_.empty());
}
@@ -484,10 +482,6 @@ void ThreadGroupImpl::WaitForWorkersCleanedUpForTesting(size_t n) {
}
void ThreadGroupImpl::JoinForTesting() {
-#if DCHECK_IS_ON()
- join_for_testing_started_.Set();
-#endif
-
decltype(workers_) workers_copy;
{
CheckedAutoLock auto_lock(lock_);
@@ -496,6 +490,8 @@ void ThreadGroupImpl::JoinForTesting() {
DCHECK_GT(workers_.size(), size_t(0))
<< "Joined an unstarted thread group.";
+ join_for_testing_started_ = true;
+
// Ensure WorkerThreads in |workers_| do not attempt to cleanup while
// being joined.
worker_cleanup_disallowed_for_testing_ = true;
@@ -531,10 +527,13 @@ size_t ThreadGroupImpl::NumberOfIdleWorkersForTesting() const {
void ThreadGroupImpl::ReportHeartbeatMetrics() const {
CheckedAutoLock auto_lock(lock_);
- num_workers_histogram_->Add(workers_.size());
-
- num_active_workers_histogram_->Add(workers_.size() -
- idle_workers_stack_.Size());
+ if (num_workers_histogram_) {
+ num_workers_histogram_->Add(workers_.size());
+ }
+ if (num_active_workers_histogram_) {
+ num_active_workers_histogram_->Add(workers_.size() -
+ idle_workers_stack_.Size());
+ }
}
ThreadGroupImpl::WorkerThreadDelegateImpl::WorkerThreadDelegateImpl(
@@ -710,10 +709,13 @@ bool ThreadGroupImpl::WorkerThreadDelegateImpl::CanCleanupLockRequired(
void ThreadGroupImpl::WorkerThreadDelegateImpl::CleanupLockRequired(
WorkerThread* worker) {
+ DCHECK(!outer_->join_for_testing_started_);
DCHECK_CALLED_ON_VALID_THREAD(worker_thread_checker_);
- outer_->num_tasks_before_detach_histogram_->Add(
- worker_only().num_tasks_since_last_detach);
+ if (outer_->num_tasks_before_detach_histogram_) {
+ outer_->num_tasks_before_detach_histogram_->Add(
+ worker_only().num_tasks_since_last_detach);
+ }
outer_->cleanup_timestamps_.push(subtle::TimeTicksNowIgnoringOverride());
worker->Cleanup();
outer_->idle_workers_stack_.Remove(worker);
@@ -756,7 +758,7 @@ void ThreadGroupImpl::WorkerThreadDelegateImpl::OnMainExit(
// |workers_| by the time the thread is about to exit. (except in the cases
// where the thread group is no longer going to be used - in which case,
// it's fine for there to be invalid workers in the thread group.
- if (!shutdown_complete && !outer_->join_for_testing_started_.IsSet()) {
+ if (!shutdown_complete && !outer_->join_for_testing_started_) {
DCHECK(!outer_->idle_workers_stack_.Contains(worker));
DCHECK(!ContainsWorker(outer_->workers_, worker));
}
@@ -944,6 +946,7 @@ void ThreadGroupImpl::MaintainAtLeastOneIdleWorkerLockRequired(
scoped_refptr<WorkerThread>
ThreadGroupImpl::CreateAndRegisterWorkerLockRequired(
ScopedWorkersExecutor* executor) {
+ DCHECK(!join_for_testing_started_);
DCHECK_LT(workers_.size(), max_tasks_);
DCHECK_LT(workers_.size(), kMaxNumberOfWorkers);
DCHECK(idle_workers_stack_.IsEmpty());
@@ -962,8 +965,10 @@ ThreadGroupImpl::CreateAndRegisterWorkerLockRequired(
DCHECK_LE(workers_.size(), max_tasks_);
if (!cleanup_timestamps_.empty()) {
- detach_duration_histogram_->AddTime(subtle::TimeTicksNowIgnoringOverride() -
- cleanup_timestamps_.top());
+ if (detach_duration_histogram_) {
+ detach_duration_histogram_->AddTime(
+ subtle::TimeTicksNowIgnoringOverride() - cleanup_timestamps_.top());
+ }
cleanup_timestamps_.pop();
}
@@ -1011,7 +1016,7 @@ void ThreadGroupImpl::DidUpdateCanRunPolicy() {
void ThreadGroupImpl::EnsureEnoughWorkersLockRequired(
BaseScopedWorkersExecutor* base_executor) {
// Don't do anything if the thread group isn't started.
- if (max_tasks_ == 0)
+ if (max_tasks_ == 0 || UNLIKELY(join_for_testing_started_))
return;
ScopedWorkersExecutor* executor =
diff --git a/chromium/base/task/thread_pool/thread_group_impl.h b/chromium/base/task/thread_pool/thread_group_impl.h
index 3df96cfdef0..e0782bf2614 100644
--- a/chromium/base/task/thread_pool/thread_group_impl.h
+++ b/chromium/base/task/thread_pool/thread_group_impl.h
@@ -20,7 +20,6 @@
#include "base/memory/ref_counted.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
-#include "base/synchronization/atomic_flag.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/thread_pool/task.h"
@@ -329,10 +328,8 @@ class BASE_EXPORT ThreadGroupImpl : public ThreadGroup {
std::unique_ptr<ConditionVariable> num_workers_cleaned_up_for_testing_cv_
GUARDED_BY(lock_);
-#if DCHECK_IS_ON()
// Set at the start of JoinForTesting().
- AtomicFlag join_for_testing_started_;
-#endif
+ bool join_for_testing_started_ GUARDED_BY(lock_) = false;
// ThreadPool.DetachDuration.[thread group name] histogram. Intentionally
// leaked.
diff --git a/chromium/base/task/thread_pool/thread_group_unittest.cc b/chromium/base/task/thread_pool/thread_group_unittest.cc
index cb4481c3cf1..3ed430a60db 100644
--- a/chromium/base/task/thread_pool/thread_group_unittest.cc
+++ b/chromium/base/task/thread_pool/thread_group_unittest.cc
@@ -53,6 +53,7 @@ using ThreadGroupNativeType =
#endif
constexpr size_t kMaxTasks = 4;
+constexpr size_t kTooManyTasks = 1000;
// By default, tests allow half of the thread group to be used by best-effort
// tasks.
constexpr size_t kMaxBestEffortTasks = kMaxTasks / 2;
@@ -654,6 +655,50 @@ TEST_P(ThreadGroupTest, ScheduleJobTaskSourceMultipleTime) {
task_tracker_.FlushForTesting();
}
+// Verify that Cancel() on a job stops running the worker task and causes
+// current workers to yield.
+TEST_P(ThreadGroupTest, CancelJobTaskSource) {
+ StartThreadGroup();
+
+ CheckedLock tasks_running_lock;
+ std::unique_ptr<ConditionVariable> tasks_running_cv =
+ tasks_running_lock.CreateConditionVariable();
+ bool tasks_running = false;
+
+ // Schedule a big number of tasks.
+ auto job_task = base::MakeRefCounted<test::MockJobTask>(
+ BindLambdaForTesting([&](experimental::JobDelegate* delegate) {
+ {
+ CheckedAutoLock auto_lock(tasks_running_lock);
+ tasks_running = true;
+ }
+ tasks_running_cv->Signal();
+
+ while (!delegate->ShouldYield()) {
+ }
+ }),
+ /* num_tasks_to_run */ kTooManyTasks);
+ scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+ FROM_HERE, {ThreadPool()}, &mock_pooled_task_runner_delegate_);
+
+ mock_pooled_task_runner_delegate_.EnqueueJobTaskSource(task_source);
+ experimental::JobHandle job_handle =
+ internal::JobTaskSource::CreateJobHandle(task_source);
+
+ // Wait for at least 1 task to start running.
+ {
+ CheckedAutoLock auto_lock(tasks_running_lock);
+ while (!tasks_running)
+ tasks_running_cv->Wait();
+ }
+
+ // Cancels pending tasks and unblocks running ones.
+ job_handle.Cancel();
+
+ // This should not block since the job got cancelled.
+ task_tracker_.FlushForTesting();
+}
+
// Verify that calling JobTaskSource::NotifyConcurrencyIncrease() (re-)schedule
// tasks with the intended concurrency.
TEST_P(ThreadGroupTest, JobTaskSourceConcurrencyIncrease) {
@@ -733,6 +778,37 @@ TEST_P(ThreadGroupTest, ScheduleEmptyJobTaskSource) {
task_tracker_.FlushForTesting();
}
+// Verify that Join() on a job contributes to max concurrency and waits for all
+// workers to return.
+TEST_P(ThreadGroupTest, JoinJobTaskSource) {
+ StartThreadGroup();
+
+ WaitableEvent threads_continue;
+ RepeatingClosure threads_continue_barrier = BarrierClosure(
+ kMaxTasks + 1,
+ BindOnce(&WaitableEvent::Signal, Unretained(&threads_continue)));
+
+ auto job_task = base::MakeRefCounted<test::MockJobTask>(
+ BindLambdaForTesting([&](experimental::JobDelegate*) {
+ threads_continue_barrier.Run();
+ test::WaitWithoutBlockingObserver(&threads_continue);
+ }),
+ /* num_tasks_to_run */ kMaxTasks + 1);
+ scoped_refptr<JobTaskSource> task_source = job_task->GetJobTaskSource(
+ FROM_HERE, {ThreadPool()}, &mock_pooled_task_runner_delegate_);
+
+ mock_pooled_task_runner_delegate_.EnqueueJobTaskSource(task_source);
+ experimental::JobHandle job_handle =
+ internal::JobTaskSource::CreateJobHandle(task_source);
+ job_handle.Join();
+ // All worker tasks should complete before Join() returns.
+ EXPECT_EQ(0U, job_task->GetMaxConcurrency());
+ thread_group_->JoinForTesting();
+ EXPECT_EQ(1U, task_source->HasOneRef());
+ // Prevent TearDown() from calling JoinForTesting() again.
+ thread_group_ = nullptr;
+}
+
// Verify that the maximum number of BEST_EFFORT tasks that can run concurrently
// in a thread group does not affect JobTaskSource with a priority that was
// increased from BEST_EFFORT to USER_BLOCKING.
diff --git a/chromium/base/task/thread_pool/thread_pool_impl.cc b/chromium/base/task/thread_pool/thread_pool_impl.cc
index 64d580d08ad..ff1bfa78211 100644
--- a/chromium/base/task/thread_pool/thread_pool_impl.cc
+++ b/chromium/base/task/thread_pool/thread_pool_impl.cc
@@ -77,20 +77,23 @@ ThreadPoolImpl::ThreadPoolImpl(StringPiece histogram_label,
&delayed_task_manager_),
has_disable_best_effort_switch_(HasDisableBestEffortTasksSwitch()),
tracked_ref_factory_(this) {
- DCHECK(!histogram_label.empty());
-
foreground_thread_group_ = std::make_unique<ThreadGroupImpl>(
- JoinString(
- {histogram_label, kForegroundPoolEnvironmentParams.name_suffix}, "."),
+ histogram_label.empty()
+ ? std::string()
+ : JoinString(
+ {histogram_label, kForegroundPoolEnvironmentParams.name_suffix},
+ "."),
kForegroundPoolEnvironmentParams.name_suffix,
kForegroundPoolEnvironmentParams.priority_hint,
task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef());
if (CanUseBackgroundPriorityForWorkerThread()) {
background_thread_group_ = std::make_unique<ThreadGroupImpl>(
- JoinString(
- {histogram_label, kBackgroundPoolEnvironmentParams.name_suffix},
- "."),
+ histogram_label.empty()
+ ? std::string()
+ : JoinString({histogram_label,
+ kBackgroundPoolEnvironmentParams.name_suffix},
+ "."),
kBackgroundPoolEnvironmentParams.name_suffix,
kBackgroundPoolEnvironmentParams.priority_hint,
task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef());
@@ -423,6 +426,14 @@ bool ThreadPoolImpl::EnqueueJobTaskSource(
return true;
}
+void ThreadPoolImpl::RemoveJobTaskSource(
+ scoped_refptr<JobTaskSource> task_source) {
+ auto transaction = task_source->BeginTransaction();
+ ThreadGroup* const current_thread_group =
+ GetThreadGroupForTraits(transaction.traits());
+ current_thread_group->RemoveTaskSource(*task_source);
+}
+
bool ThreadPoolImpl::IsRunningPoolWithTraits(const TaskTraits& traits) const {
return GetThreadGroupForTraits(traits)->IsBoundToCurrentThread();
}
@@ -455,7 +466,7 @@ void ThreadPoolImpl::UpdatePriority(scoped_refptr<TaskSource> task_source,
// |task_source| is changing thread groups; remove it from its current
// thread group and reenqueue it.
auto registered_task_source =
- current_thread_group->RemoveTaskSource(task_source);
+ current_thread_group->RemoveTaskSource(*task_source);
if (registered_task_source) {
DCHECK(task_source);
new_thread_group->PushTaskSourceAndWakeUpWorkers(
diff --git a/chromium/base/task/thread_pool/thread_pool_impl.h b/chromium/base/task/thread_pool/thread_pool_impl.h
index 049ee6de692..8b3a6760ce9 100644
--- a/chromium/base/task/thread_pool/thread_pool_impl.h
+++ b/chromium/base/task/thread_pool/thread_pool_impl.h
@@ -60,8 +60,8 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance,
TaskTracker;
#endif
- // Creates a ThreadPoolImpl with a production TaskTracker.
- //|histogram_label| is used to label histograms, it must not be empty.
+ // Creates a ThreadPoolImpl with a production TaskTracker. |histogram_label|
+ // is used to label histograms. No histograms are recorded if it is empty.
explicit ThreadPoolImpl(StringPiece histogram_label);
// For testing only. Creates a ThreadPoolImpl with a custom TaskTracker.
@@ -103,6 +103,7 @@ class BASE_EXPORT ThreadPoolImpl : public ThreadPoolInstance,
// PooledTaskRunnerDelegate:
bool EnqueueJobTaskSource(scoped_refptr<JobTaskSource> task_source) override;
+ void RemoveJobTaskSource(scoped_refptr<JobTaskSource> task_source) override;
void UpdatePriority(scoped_refptr<TaskSource> task_source,
TaskPriority priority) override;
diff --git a/chromium/base/task_runner.cc b/chromium/base/task_runner.cc
index e2787f3d1ac..06aca8a68db 100644
--- a/chromium/base/task_runner.cc
+++ b/chromium/base/task_runner.cc
@@ -54,13 +54,14 @@ bool TaskRunner::PostTaskAndReply(const Location& from_here,
from_here, std::move(task), std::move(reply));
}
-bool TaskRunner::PostPromiseInternal(
- const scoped_refptr<internal::AbstractPromise>& promise,
- base::TimeDelta delay) {
- return PostDelayedTask(promise->from_here(),
- BindOnce(&internal::AbstractPromise::Execute,
- internal::PromiseHolder(promise)),
- delay);
+bool TaskRunner::PostPromiseInternal(WrappedPromise promise,
+ base::TimeDelta delay) {
+ Location from_here = promise.from_here();
+ return PostDelayedTask(
+ from_here,
+ BindOnce([](WrappedPromise promise) { promise.Execute(); },
+ std::move(promise)),
+ delay);
}
TaskRunner::TaskRunner() = default;
diff --git a/chromium/base/task_runner.h b/chromium/base/task_runner.h
index 471f23cd7bb..2bd5d4eb2bf 100644
--- a/chromium/base/task_runner.h
+++ b/chromium/base/task_runner.h
@@ -11,14 +11,11 @@
#include "base/callback.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
+#include "base/task/promise/abstract_promise.h"
#include "base/time/time.h"
namespace base {
-namespace internal {
-class AbstractPromise;
-} // namespace internal
-
struct TaskRunnerTraits;
// A TaskRunner is an object that runs posted tasks (in the form of
@@ -139,9 +136,7 @@ class BASE_EXPORT TaskRunner
// TODO(alexclarke): This should become pure virtual and replace
// PostDelayedTask. NB passing by reference to reduce binary size.
- bool PostPromiseInternal(
- const scoped_refptr<internal::AbstractPromise>& promise,
- base::TimeDelta delay);
+ bool PostPromiseInternal(WrappedPromise promise, base::TimeDelta delay);
protected:
friend struct TaskRunnerTraits;
diff --git a/chromium/base/test/BUILD.gn b/chromium/base/test/BUILD.gn
index 1b94239db7b..b71bb86a384 100644
--- a/chromium/base/test/BUILD.gn
+++ b/chromium/base/test/BUILD.gn
@@ -27,8 +27,6 @@ static_library("test_config") {
static_library("test_support") {
testonly = true
sources = [
- "../memory/fake_memory_pressure_monitor.cc",
- "../memory/fake_memory_pressure_monitor.h",
"../task/sequence_manager/test/fake_task.cc",
"../task/sequence_manager/test/fake_task.h",
"../task/sequence_manager/test/mock_time_domain.cc",
@@ -228,6 +226,7 @@ static_library("test_support") {
set_sources_assignment_filter([])
sources += [ "test_file_util_mac.cc" ]
set_sources_assignment_filter(sources_assignment_filter)
+ deps += [ ":google_test_runner_shared_headers" ]
}
if (is_mac) {
@@ -434,7 +433,7 @@ if (is_linux) {
]
args = []
outputs = [
- "$root_out_dir/fontconfig_caches/df1acc8c-39d5-4a8b-8507-b1a7396ac3ac-le64.cache-7",
+ "$root_out_dir/fontconfig_caches/fb5c91b2895aa445d23aebf7f9e2189c-le64.cache-7",
"$root_out_dir/test_fonts/.uuid",
]
}
@@ -455,6 +454,7 @@ if (is_fuchsia || is_linux) {
if (is_android) {
generate_jni("base_unittests_jni_headers") {
+ testonly = true
sources = [
"android/java/src/org/chromium/base/ContentUriTestUtils.java",
"android/java/src/org/chromium/base/JavaHandlerThreadHelpers.java",
@@ -462,6 +462,7 @@ if (is_android) {
}
generate_jni("test_support_jni_headers") {
+ testonly = true
sources = [
"android/java/src/org/chromium/base/MainReturnCodeResult.java",
"android/java/src/org/chromium/base/MultiprocessTestClientLauncher.java",
@@ -477,7 +478,7 @@ if (is_android) {
"//base:base_java",
"//base:base_java_test_support",
"//testing/android/native_test:native_main_runner_java",
- "//third_party/android_deps:com_android_support_support_annotations_java",
+ "//third_party/android_deps:androidx_annotation_annotation_java",
"//third_party/jsr-305:jsr_305_javalib",
]
srcjar_deps = [ ":test_support_java_aidl" ]
@@ -513,6 +514,29 @@ if (is_android) {
}
}
+if (is_ios) {
+ source_set("google_test_runner_shared_headers") {
+ sources = [
+ "ios/google_test_runner_delegate.h",
+ ]
+ }
+
+ source_set("google_test_runner") {
+ sources = [
+ "ios/google_test_runner.mm",
+ ]
+ deps = [
+ ":google_test_runner_shared_headers",
+ "//base",
+ ]
+ libs = [ "UIKit.framework" ]
+ configs += [
+ "//build/config/compiler:enable_arc",
+ "//build/config/ios:xctest_config",
+ ]
+ }
+}
+
# Trivial executable which outputs space-delimited argv to stdout,
# used for testing.
executable("test_child_process") {
diff --git a/chromium/base/threading/platform_thread_unittest.cc b/chromium/base/threading/platform_thread_unittest.cc
index 2ef69fe32f0..96ea6629fc7 100644
--- a/chromium/base/threading/platform_thread_unittest.cc
+++ b/chromium/base/threading/platform_thread_unittest.cc
@@ -289,9 +289,7 @@ TEST(PlatformThreadTest, MAYBE_SetCurrentThreadPriority) {
#if defined(OS_WIN)
// Test changing a created thread's priority, with the
// kWindowsThreadModeBackground feature enabled.
-// Flaky: https://crbug.com/931706
-TEST(PlatformThreadTest,
- DISABLED_SetCurrentThreadPriorityWithThreadModeBackground) {
+TEST(PlatformThreadTest, SetCurrentThreadPriorityWithThreadModeBackground) {
test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
features::kWindowsThreadModeBackground);
@@ -301,9 +299,8 @@ TEST(PlatformThreadTest,
// Test changing a created thread's priority, with the
// kWindowsThreadModeBackground feature enabled, in an IDLE_PRIORITY_CLASS
// process (regression test for https://crbug.com/901483).
-// Flaky: https://crbug.com/931706
TEST(PlatformThreadTest,
- DISABLED_SetCurrentThreadPriorityWithThreadModeBackgroundIdleProcess) {
+ SetCurrentThreadPriorityWithThreadModeBackgroundIdleProcess) {
::SetPriorityClass(Process::Current().Handle(), IDLE_PRIORITY_CLASS);
test::ScopedFeatureList scoped_feature_list;
diff --git a/chromium/base/threading/platform_thread_win.cc b/chromium/base/threading/platform_thread_win.cc
index 0fef2a8d69c..2889b253b75 100644
--- a/chromium/base/threading/platform_thread_win.cc
+++ b/chromium/base/threading/platform_thread_win.cc
@@ -29,9 +29,9 @@ namespace base {
namespace {
-// The value returned by ::GetThreadPriority() after background thread mode is
-// enabled on Windows 8+.
-constexpr int kWin8AboveBackgroundThreadModePriority = -4;
+// The most common value returned by ::GetThreadPriority() after background
+// thread mode is enabled on Windows 7.
+constexpr int kWin7BackgroundThreadModePriority = 4;
// The information on how to set the thread name comes from
// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
@@ -401,17 +401,45 @@ void PlatformThread::SetCurrentThreadPriorityImpl(ThreadPriority priority) {
// static
ThreadPriority PlatformThread::GetCurrentThreadPriority() {
+ static_assert(
+ THREAD_PRIORITY_IDLE < 0,
+ "THREAD_PRIORITY_IDLE is >= 0 and will incorrectly cause errors.");
+ static_assert(
+ THREAD_PRIORITY_LOWEST < 0,
+ "THREAD_PRIORITY_LOWEST is >= 0 and will incorrectly cause errors.");
+ static_assert(THREAD_PRIORITY_BELOW_NORMAL < 0,
+ "THREAD_PRIORITY_BELOW_NORMAL is >= 0 and will incorrectly "
+ "cause errors.");
+ static_assert(
+ THREAD_PRIORITY_NORMAL == 0,
+ "The logic below assumes that THREAD_PRIORITY_NORMAL is zero. If it is "
+ "not, ThreadPriority::BACKGROUND may be incorrectly detected.");
+ static_assert(THREAD_PRIORITY_ABOVE_NORMAL >= 0,
+ "THREAD_PRIORITY_ABOVE_NORMAL is < 0 and will incorrectly be "
+ "translated to ThreadPriority::BACKGROUND.");
+ static_assert(THREAD_PRIORITY_HIGHEST >= 0,
+ "THREAD_PRIORITY_HIGHEST is < 0 and will incorrectly be "
+ "translated to ThreadPriority::BACKGROUND.");
+ static_assert(THREAD_PRIORITY_TIME_CRITICAL >= 0,
+ "THREAD_PRIORITY_TIME_CRITICAL is < 0 and will incorrectly be "
+ "translated to ThreadPriority::BACKGROUND.");
+ static_assert(THREAD_PRIORITY_ERROR_RETURN >= 0,
+ "THREAD_PRIORITY_ERROR_RETURN is < 0 and will incorrectly be "
+ "translated to ThreadPriority::BACKGROUND.");
+
const int priority =
::GetThreadPriority(PlatformThread::CurrentHandle().platform_handle());
+ // Negative values represent a background priority. We have observed -3, -4,
+ // -6 when THREAD_MODE_BACKGROUND_* is used. THREAD_PRIORITY_IDLE,
+ // THREAD_PRIORITY_LOWEST and THREAD_PRIORITY_BELOW_NORMAL are other possible
+ // negative values.
+ if (priority < THREAD_PRIORITY_NORMAL)
+ return ThreadPriority::BACKGROUND;
+
switch (priority) {
- case THREAD_PRIORITY_IDLE:
- case internal::kWin7BackgroundThreadModePriority:
+ case kWin7BackgroundThreadModePriority:
DCHECK_EQ(win::GetVersion(), win::Version::WIN7);
- FALLTHROUGH;
- case kWin8AboveBackgroundThreadModePriority:
- case THREAD_PRIORITY_LOWEST:
- case THREAD_PRIORITY_BELOW_NORMAL:
return ThreadPriority::BACKGROUND;
case THREAD_PRIORITY_NORMAL:
return ThreadPriority::NORMAL;
@@ -421,10 +449,10 @@ ThreadPriority PlatformThread::GetCurrentThreadPriority() {
case THREAD_PRIORITY_TIME_CRITICAL:
return ThreadPriority::REALTIME_AUDIO;
case THREAD_PRIORITY_ERROR_RETURN:
- DPCHECK(false) << "GetThreadPriority error";
+ DPCHECK(false) << "::GetThreadPriority error";
}
- NOTREACHED() << "GetCurrentThreadPriority returned " << priority << ".";
+ NOTREACHED() << "::GetThreadPriority returned " << priority << ".";
return ThreadPriority::NORMAL;
}
diff --git a/chromium/base/threading/platform_thread_win.h b/chromium/base/threading/platform_thread_win.h
index d1bf4205de4..879d5062667 100644
--- a/chromium/base/threading/platform_thread_win.h
+++ b/chromium/base/threading/platform_thread_win.h
@@ -25,10 +25,6 @@ BASE_EXPORT extern const Feature kWindowsThreadModeBackground;
namespace internal {
-// The value returned by ::GetThreadPriority() after background thread mode is
-// enabled on Windows 7. Exposed for unit tests.
-constexpr int kWin7BackgroundThreadModePriority = 4;
-
// Assert that the memory priority of |thread| is |memory_priority|. No-op on
// Windows 7 because ::GetThreadInformation() is not available. Exposed for unit
// tests.
diff --git a/chromium/base/threading/platform_thread_win_unittest.cc b/chromium/base/threading/platform_thread_win_unittest.cc
index 15c9939da46..de1d2cab595 100644
--- a/chromium/base/threading/platform_thread_win_unittest.cc
+++ b/chromium/base/threading/platform_thread_win_unittest.cc
@@ -24,9 +24,7 @@ namespace base {
// behavior which we suspect is a Windows kernel bug. If this test starts
// failing, the mitigation for https://crbug.com/901483 in
// PlatformThread::SetCurrentThreadPriority() should be revisited.
-// Fails on various windows bots: https://crbug.com/931720.
-TEST(PlatformThreadWinTest,
- DISABLED_SetBackgroundThreadModeFailsInIdlePriorityProcess) {
+TEST(PlatformThreadWinTest, SetBackgroundThreadModeFailsInIdlePriorityProcess) {
PlatformThreadHandle::Handle thread_handle =
PlatformThread::CurrentHandle().platform_handle();
@@ -53,23 +51,19 @@ TEST(PlatformThreadWinTest,
EXPECT_TRUE(::SetThreadPriority(thread_handle, THREAD_MODE_BACKGROUND_BEGIN));
// On Win8, GetThreadPriority() stays NORMAL. On Win7, it can stay NORMAL or
- // switch to one of the 2 priorities that are usually observed after entering
+ // switch to one of the various priorities that are observed after entering
// thread mode background in a NORMAL_PRIORITY_CLASS process. On all Windows
- // verisons, memory priority becomes VERY_LOW.
+ // versions, memory priority becomes VERY_LOW.
//
// Note: this documents the aforementioned kernel bug. Ideally this would
// *not* be the case.
const float priority_after_thread_mode_background_begin =
::GetThreadPriority(thread_handle);
if (win::GetVersion() == win::Version::WIN7) {
- constexpr std::array<int, 3> kExpectedWin7Priorities(
- {// Priority if GetThreadPriority() is not affected.
- THREAD_PRIORITY_NORMAL,
- // Priorities if GetThreadPriority() behaves like in a
- // NORMAL_PRIORITY_CLASS process.
- THREAD_PRIORITY_IDLE, internal::kWin7BackgroundThreadModePriority});
- EXPECT_THAT(kExpectedWin7Priorities,
- testing::Contains(priority_after_thread_mode_background_begin));
+ if (priority_after_thread_mode_background_begin != THREAD_PRIORITY_NORMAL) {
+ EXPECT_EQ(ThreadPriority::BACKGROUND,
+ base::PlatformThread::GetCurrentThreadPriority());
+ }
} else {
EXPECT_EQ(priority_after_thread_mode_background_begin,
THREAD_PRIORITY_NORMAL);
diff --git a/chromium/base/threading/sequenced_task_runner_handle.cc b/chromium/base/threading/sequenced_task_runner_handle.cc
index 8a7e129cbe9..2bfc0cf42ee 100644
--- a/chromium/base/threading/sequenced_task_runner_handle.cc
+++ b/chromium/base/threading/sequenced_task_runner_handle.cc
@@ -23,8 +23,10 @@ LazyInstance<ThreadLocalPointer<SequencedTaskRunnerHandle>>::Leaky
const scoped_refptr<SequencedTaskRunner>& SequencedTaskRunnerHandle::Get() {
const SequencedTaskRunnerHandle* current =
sequenced_task_runner_tls.Pointer()->Get();
- CHECK(current) << "Error: This caller requires a sequenced context (i.e. the "
- "current task needs to run from a SequencedTaskRunner).";
+ CHECK(current)
+ << "Error: This caller requires a sequenced context (i.e. the current "
+ "task needs to run from a SequencedTaskRunner). If you're in a test "
+ "refer to //docs/threading_and_tasks_testing.md.";
return current->task_runner_;
}
diff --git a/chromium/base/threading/thread.cc b/chromium/base/threading/thread.cc
index a411bb8a0e2..eecd3491890 100644
--- a/chromium/base/threading/thread.cc
+++ b/chromium/base/threading/thread.cc
@@ -4,14 +4,22 @@
#include "base/threading/thread.h"
+#include <type_traits>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/message_loop/message_loop_current.h"
+#include "base/message_loop/message_pump.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
+#include "base/task/sequence_manager/sequence_manager_impl.h"
+#include "base/task/sequence_manager/task_queue.h"
+#include "base/task/simple_task_executor.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_local.h"
@@ -38,6 +46,58 @@ namespace {
base::LazyInstance<base::ThreadLocalBoolean>::Leaky lazy_tls_bool =
LAZY_INSTANCE_INITIALIZER;
+class SequenceManagerThreadDelegate : public Thread::Delegate {
+ public:
+ explicit SequenceManagerThreadDelegate(
+ MessagePumpType message_pump_type,
+ OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory)
+ : sequence_manager_(
+ sequence_manager::internal::SequenceManagerImpl::CreateUnbound(
+ sequence_manager::SequenceManager::Settings::Builder()
+ .SetMessagePumpType(message_pump_type)
+ .Build())),
+ default_task_queue_(sequence_manager_->CreateTaskQueue(
+ sequence_manager::TaskQueue::Spec("default_tq"))),
+ message_pump_factory_(std::move(message_pump_factory)) {
+ sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner());
+ }
+
+ ~SequenceManagerThreadDelegate() override = default;
+
+ scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override {
+ // Surprisingly this might not be default_task_queue_->task_runner() which
+ // we set in the constructor. The Thread::Init() method could create a
+ // SequenceManager on top of the current one and call
+ // SequenceManager::SetDefaultTaskRunner which would propagate the new
+ // TaskRunner down to our SequenceManager. Turns out, code actually relies
+ // on this and somehow relies on
+ // SequenceManagerThreadDelegate::GetDefaultTaskRunner returning this new
+ // TaskRunner. So instead of returning default_task_queue_->task_runner() we
+ // need to query the SequenceManager for it.
+ // The underlying problem here is that Subclasses of Thread can do crazy
+ // stuff in Init() but they are not really in control of what happens in the
+ // Thread::Delegate, as this is passed in on calling StartWithOptions which
+ // could happen far away from where the Thread is created. We should
+ // consider getting rid of StartWithOptions, and pass them as a constructor
+ // argument instead.
+ return sequence_manager_->GetTaskRunner();
+ }
+
+ void BindToCurrentThread(TimerSlack timer_slack) override {
+ sequence_manager_->BindToMessagePump(
+ std::move(message_pump_factory_).Run());
+ sequence_manager_->SetTimerSlack(timer_slack);
+ simple_task_executor_.emplace(GetDefaultTaskRunner());
+ }
+
+ private:
+ std::unique_ptr<sequence_manager::internal::SequenceManagerImpl>
+ sequence_manager_;
+ scoped_refptr<sequence_manager::TaskQueue> default_task_queue_;
+ OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory_;
+ base::Optional<SimpleTaskExecutor> simple_task_executor_;
+};
+
} // namespace
Thread::Options::Options() = default;
@@ -100,11 +160,13 @@ bool Thread::StartWithOptions(const Options& options) {
DCHECK(!options.message_pump_factory);
delegate_ = WrapUnique(options.delegate);
} else if (options.message_pump_factory) {
- delegate_ = std::make_unique<internal::MessageLoopThreadDelegate>(
- MessageLoop::CreateUnbound(options.message_pump_factory.Run()));
+ delegate_ = std::make_unique<SequenceManagerThreadDelegate>(
+ MessagePumpType::CUSTOM, options.message_pump_factory);
} else {
- delegate_ = std::make_unique<internal::MessageLoopThreadDelegate>(
- MessageLoop::CreateUnbound(options.message_pump_type));
+ delegate_ = std::make_unique<SequenceManagerThreadDelegate>(
+ options.message_pump_type,
+ BindOnce([](MessagePumpType type) { return MessagePump::Create(type); },
+ options.message_pump_type));
}
start_event_.Reset();
@@ -291,9 +353,10 @@ void Thread::ThreadMain() {
#if defined(OS_WIN)
std::unique_ptr<win::ScopedCOMInitializer> com_initializer;
if (com_status_ != NONE) {
- com_initializer.reset((com_status_ == STA) ?
- new win::ScopedCOMInitializer() :
- new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
+ com_initializer.reset(
+ (com_status_ == STA)
+ ? new win::ScopedCOMInitializer()
+ : new win::ScopedCOMInitializer(win::ScopedCOMInitializer::kMTA));
}
#endif
@@ -337,24 +400,4 @@ void Thread::ThreadQuitHelper() {
SetThreadWasQuitProperly(true);
}
-namespace internal {
-
-MessageLoopThreadDelegate::MessageLoopThreadDelegate(
- std::unique_ptr<MessageLoop> message_loop)
- : message_loop_(std::move(message_loop)) {}
-
-MessageLoopThreadDelegate::~MessageLoopThreadDelegate() {}
-
-scoped_refptr<SingleThreadTaskRunner>
-MessageLoopThreadDelegate::GetDefaultTaskRunner() {
- return message_loop_->task_runner();
-}
-
-void MessageLoopThreadDelegate::BindToCurrentThread(TimerSlack timer_slack) {
- message_loop_->BindToCurrentThread();
- message_loop_->SetTimerSlack(timer_slack);
-}
-
-} // namespace internal
-
} // namespace base
diff --git a/chromium/base/threading/thread.h b/chromium/base/threading/thread.h
index 73a444f178a..312caab25a2 100644
--- a/chromium/base/threading/thread.h
+++ b/chromium/base/threading/thread.h
@@ -13,8 +13,6 @@
#include "base/base_export.h"
#include "base/callback.h"
#include "base/macros.h"
-#include "base/message_loop/message_loop.h"
-#include "base/message_loop/message_loop_current.h"
#include "base/message_loop/message_pump_type.h"
#include "base/message_loop/timer_slack.h"
#include "base/sequence_checker.h"
@@ -331,24 +329,6 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
DISALLOW_COPY_AND_ASSIGN(Thread);
};
-namespace internal {
-
-class BASE_EXPORT MessageLoopThreadDelegate : public Thread::Delegate {
- public:
- explicit MessageLoopThreadDelegate(std::unique_ptr<MessageLoop> message_loop);
-
- ~MessageLoopThreadDelegate() override;
-
- // Thread::Delegate:
- scoped_refptr<SingleThreadTaskRunner> GetDefaultTaskRunner() override;
- void BindToCurrentThread(TimerSlack timer_slack) override;
-
- private:
- std::unique_ptr<MessageLoop> message_loop_;
-};
-
-} // namespace internal
-
} // namespace base
#endif // BASE_THREADING_THREAD_H_
diff --git a/chromium/base/threading/thread_perftest.cc b/chromium/base/threading/thread_perftest.cc
index e19b18bf523..5e1e65bc7c3 100644
--- a/chromium/base/threading/thread_perftest.cc
+++ b/chromium/base/threading/thread_perftest.cc
@@ -12,6 +12,7 @@
#include "base/command_line.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
+#include "base/message_loop/message_loop_current.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/condition_variable.h"
diff --git a/chromium/base/threading/thread_restrictions.h b/chromium/base/threading/thread_restrictions.h
index 052978192ab..c93f1fcaec0 100644
--- a/chromium/base/threading/thread_restrictions.h
+++ b/chromium/base/threading/thread_restrictions.h
@@ -179,9 +179,6 @@ class HistoryReportJniBridge;
namespace gpu {
class GpuChannelHost;
}
-namespace leveldb {
-class LevelDBMojoProxy;
-}
namespace leveldb_env {
class DBTracker;
}
@@ -189,6 +186,7 @@ namespace media {
class AudioInputDevice;
class AudioOutputDevice;
class BlockingUrlProtocol;
+class PaintCanvasVideoRenderer;
}
namespace memory_instrumentation {
class OSMetrics;
@@ -196,6 +194,9 @@ class OSMetrics;
namespace midi {
class TaskService; // https://crbug.com/796830
}
+namespace module_installer {
+class ScopedAllowModulePakLoad;
+}
namespace mojo {
class CoreLibraryInitializer;
class SyncCallRestrictions;
@@ -204,6 +205,7 @@ class ScopedIPCSupport;
}
}
namespace printing {
+class PrintJobWorker;
class PrinterQuery;
}
namespace rlz_lib {
@@ -262,6 +264,10 @@ class WebMainLoop;
class WebSubThread;
}
+namespace weblayer {
+class ProfileImpl;
+}
+
namespace webrtc {
class DesktopConfigurationMonitor;
}
@@ -349,11 +355,14 @@ class BASE_EXPORT ScopedAllowBlocking {
friend class cronet::CronetPrefsManager;
friend class cronet::CronetURLRequestContext;
friend class memory_instrumentation::OSMetrics;
+ friend class module_installer::ScopedAllowModulePakLoad;
friend class mojo::CoreLibraryInitializer;
+ friend class printing::PrintJobWorker;
friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703
friend class ui::MaterialDesignController;
friend class web::WebSubThread;
friend class StackSamplingProfiler;
+ friend class weblayer::ProfileImpl;
ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
~ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
@@ -407,7 +416,6 @@ class BASE_EXPORT ScopedAllowBaseSyncPrimitives {
friend class functions::ExecScriptScopedAllowBaseSyncPrimitives;
friend class history_report::HistoryReportJniBridge;
friend class internal::TaskTracker;
- friend class leveldb::LevelDBMojoProxy;
friend class leveldb_env::DBTracker;
friend class media::BlockingUrlProtocol;
friend class mojo::core::ScopedIPCSupport;
@@ -475,6 +483,7 @@ class BASE_EXPORT ScopedAllowBaseSyncPrimitivesOutsideBlockingScope {
friend class content::SynchronousCompositorSyncCallBridge;
friend class media::AudioInputDevice;
friend class media::AudioOutputDevice;
+ friend class media::PaintCanvasVideoRenderer;
friend class mojo::SyncCallRestrictions;
friend class net::NetworkConfigWatcherMacThread;
friend class viz::HostGpuMemoryBufferManager;
diff --git a/chromium/base/threading/thread_task_runner_handle.cc b/chromium/base/threading/thread_task_runner_handle.cc
index 27a3f84eee6..4a9ac88f61c 100644
--- a/chromium/base/threading/thread_task_runner_handle.cc
+++ b/chromium/base/threading/thread_task_runner_handle.cc
@@ -26,9 +26,10 @@ base::LazyInstance<base::ThreadLocalPointer<ThreadTaskRunnerHandle>>::Leaky
const scoped_refptr<SingleThreadTaskRunner>& ThreadTaskRunnerHandle::Get() {
const ThreadTaskRunnerHandle* current =
thread_task_runner_tls.Pointer()->Get();
- CHECK(current) << "Error: This caller requires a single-threaded context "
- "(i.e. the current task needs to run from a "
- "SingleThreadTaskRunner).";
+ CHECK(current)
+ << "Error: This caller requires a single-threaded context (i.e. the "
+ "current task needs to run from a SingleThreadTaskRunner). If you're "
+ "in a test refer to //docs/threading_and_tasks_testing.md.";
return current->task_runner_;
}
diff --git a/chromium/base/threading/thread_unittest.cc b/chromium/base/threading/thread_unittest.cc
index bdb57cc19fe..5f1722877ad 100644
--- a/chromium/base/threading/thread_unittest.cc
+++ b/chromium/base/threading/thread_unittest.cc
@@ -17,16 +17,21 @@
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
+#include "base/task/post_task.h"
#include "base/task/sequence_manager/sequence_manager_impl.h"
+#include "base/task/task_executor.h"
+#include "base/test/bind_test_util.h"
#include "base/test/gtest_util.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
using base::Thread;
+using ::testing::NotNull;
typedef PlatformTest ThreadTest;
@@ -522,6 +527,44 @@ TEST_F(ThreadTest, FlushForTesting) {
a.FlushForTesting();
}
+TEST_F(ThreadTest, GetTaskExecutorForCurrentThread) {
+ Thread a("GetTaskExecutorForCurrentThread");
+ ASSERT_TRUE(a.Start());
+
+ base::WaitableEvent event;
+
+ a.task_runner()->PostTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ EXPECT_THAT(base::GetTaskExecutorForCurrentThread(), NotNull());
+ event.Signal();
+ }));
+
+ event.Wait();
+ a.Stop();
+}
+
+TEST_F(ThreadTest, CurrentThread) {
+ Thread a("CurrentThread");
+ ASSERT_TRUE(a.Start());
+
+ base::WaitableEvent event;
+
+ a.task_runner()->PostTask(
+ FROM_HERE, base::BindLambdaForTesting([&]() {
+ EXPECT_EQ(a.task_runner(),
+ base::CreateSingleThreadTaskRunner({base::CurrentThread()}));
+
+ // There's only a single task runner so base::TaskPriority is ignored.
+ EXPECT_EQ(a.task_runner(), base::CreateSingleThreadTaskRunner(
+ {base::CurrentThread(),
+ base::TaskPriority::BEST_EFFORT}));
+ event.Signal();
+ }));
+
+ event.Wait();
+ a.Stop();
+}
+
namespace {
class SequenceManagerThreadDelegate : public Thread::Delegate {
diff --git a/chromium/base/time/time.cc b/chromium/base/time/time.cc
index 3e46f17575c..9ac9d4a21db 100644
--- a/chromium/base/time/time.cc
+++ b/chromium/base/time/time.cc
@@ -135,52 +135,6 @@ int64_t TimeDelta::InNanoseconds() const {
return delta_ * Time::kNanosecondsPerMicrosecond;
}
-namespace time_internal {
-
-int64_t SaturatedAdd(int64_t value, TimeDelta delta) {
- // Treat Min/Max() as +/- infinity (additions involving two infinities are
- // only valid if signs match).
- if (delta.is_max()) {
- CHECK_GT(value, std::numeric_limits<int64_t>::min());
- return std::numeric_limits<int64_t>::max();
- } else if (delta.is_min()) {
- CHECK_LT(value, std::numeric_limits<int64_t>::max());
- return std::numeric_limits<int64_t>::min();
- }
-
- CheckedNumeric<int64_t> rv(value);
- rv += delta.delta_;
- if (rv.IsValid())
- return rv.ValueOrDie();
- // Positive RHS overflows. Negative RHS underflows.
- if (delta.delta_ < 0)
- return std::numeric_limits<int64_t>::min();
- return std::numeric_limits<int64_t>::max();
-}
-
-int64_t SaturatedSub(int64_t value, TimeDelta delta) {
- // Treat Min/Max() as +/- infinity (subtractions involving two infinities are
- // only valid if signs are opposite).
- if (delta.is_max()) {
- CHECK_LT(value, std::numeric_limits<int64_t>::max());
- return std::numeric_limits<int64_t>::min();
- } else if (delta.is_min()) {
- CHECK_GT(value, std::numeric_limits<int64_t>::min());
- return std::numeric_limits<int64_t>::max();
- }
-
- CheckedNumeric<int64_t> rv(value);
- rv -= delta.delta_;
- if (rv.IsValid())
- return rv.ValueOrDie();
- // Negative RHS overflows. Positive RHS underflows.
- if (delta.delta_ < 0)
- return std::numeric_limits<int64_t>::max();
- return std::numeric_limits<int64_t>::min();
-}
-
-} // namespace time_internal
-
std::ostream& operator<<(std::ostream& os, TimeDelta time_delta) {
return os << time_delta.InSecondsF() << " s";
}
diff --git a/chromium/base/time/time.h b/chromium/base/time/time.h
index 6abe4112645..aed3de765b1 100644
--- a/chromium/base/time/time.h
+++ b/chromium/base/time/time.h
@@ -109,8 +109,8 @@ namespace time_internal {
// as infinity and will always saturate the return value (infinity math applies
// if |value| also is at either limit of its spectrum). The int64_t argument and
// return value are in terms of a microsecond timebase.
-BASE_EXPORT int64_t SaturatedAdd(int64_t value, TimeDelta delta);
-BASE_EXPORT int64_t SaturatedSub(int64_t value, TimeDelta delta);
+BASE_EXPORT constexpr int64_t SaturatedAdd(int64_t value, TimeDelta delta);
+BASE_EXPORT constexpr int64_t SaturatedSub(int64_t value, TimeDelta delta);
} // namespace time_internal
@@ -121,6 +121,9 @@ class BASE_EXPORT TimeDelta {
constexpr TimeDelta() : delta_(0) {}
// Converts units of time to TimeDeltas.
+ // WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may
+ // not precisely equal |t|. Hence, floating point values should not be used
+ // for storage.
static constexpr TimeDelta FromDays(int days);
static constexpr TimeDelta FromHours(int hours);
static constexpr TimeDelta FromMinutes(int minutes);
@@ -208,6 +211,9 @@ class BASE_EXPORT TimeDelta {
// towards zero, std::trunc() behavior). The InXYZFloored() versions round to
// lesser integers (std::floor() behavior). The XYZRoundedUp() versions round
// up to greater integers (std::ceil() behavior).
+ // WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may
+ // not precisely equal |t|. Hence, floating point values should not be used
+ // for storage.
int InDays() const;
int InDaysFloored() const;
int InHours() const;
@@ -221,30 +227,25 @@ class BASE_EXPORT TimeDelta {
double InMicrosecondsF() const;
int64_t InNanoseconds() const;
- // Computations with other deltas. Can easily be made constexpr with C++17 but
- // hard to do until then per limitations around
- // __builtin_(add|sub)_overflow in safe_math_clang_gcc_impl.h :
- // https://chromium-review.googlesource.com/c/chromium/src/+/873352#message-59594ab70827795a67e0780404adf37b4b6c2f14
- TimeDelta operator+(TimeDelta other) const {
+ // Computations with other deltas.
+ constexpr TimeDelta operator+(TimeDelta other) const {
return TimeDelta(time_internal::SaturatedAdd(delta_, other));
}
- TimeDelta operator-(TimeDelta other) const {
+ constexpr TimeDelta operator-(TimeDelta other) const {
return TimeDelta(time_internal::SaturatedSub(delta_, other));
}
- TimeDelta& operator+=(TimeDelta other) {
+ constexpr TimeDelta& operator+=(TimeDelta other) {
return *this = (*this + other);
}
- TimeDelta& operator-=(TimeDelta other) {
+ constexpr TimeDelta& operator-=(TimeDelta other) {
return *this = (*this - other);
}
constexpr TimeDelta operator-() const { return TimeDelta(-delta_); }
- // Computations with numeric types. operator*() isn't constexpr because of a
- // limitation around __builtin_mul_overflow (but operator/(1.0/a) works for
- // |a|'s of "reasonable" size -- i.e. that don't risk overflow).
+ // Computations with numeric types.
template <typename T>
- TimeDelta operator*(T a) const {
+ constexpr TimeDelta operator*(T a) const {
CheckedNumeric<int64_t> rv(delta_);
rv *= a;
if (rv.IsValid())
@@ -267,7 +268,7 @@ class BASE_EXPORT TimeDelta {
return TimeDelta(std::numeric_limits<int64_t>::max());
}
template <typename T>
- TimeDelta& operator*=(T a) {
+ constexpr TimeDelta& operator*=(T a) {
return *this = (*this * a);
}
template <typename T>
@@ -276,9 +277,11 @@ class BASE_EXPORT TimeDelta {
}
constexpr int64_t operator/(TimeDelta a) const { return delta_ / a.delta_; }
+
constexpr TimeDelta operator%(TimeDelta a) const {
return TimeDelta(delta_ % a.delta_);
}
+ TimeDelta& operator%=(TimeDelta other) { return *this = (*this % other); }
// Comparison operators.
constexpr bool operator==(TimeDelta other) const {
@@ -301,8 +304,10 @@ class BASE_EXPORT TimeDelta {
}
private:
- friend int64_t time_internal::SaturatedAdd(int64_t value, TimeDelta delta);
- friend int64_t time_internal::SaturatedSub(int64_t value, TimeDelta delta);
+ friend constexpr int64_t time_internal::SaturatedAdd(int64_t value,
+ TimeDelta delta);
+ friend constexpr int64_t time_internal::SaturatedSub(int64_t value,
+ TimeDelta delta);
// Constructs a delta given the duration in microseconds. This is private
// to avoid confusion by callers with an integer constructor. Use
@@ -321,7 +326,7 @@ class BASE_EXPORT TimeDelta {
};
template <typename T>
-TimeDelta operator*(T a, TimeDelta td) {
+constexpr TimeDelta operator*(T a, TimeDelta td) {
return td * a;
}
@@ -333,6 +338,34 @@ BASE_EXPORT std::ostream& operator<<(std::ostream& os, TimeDelta time_delta);
// TimeBase members via those classes.
namespace time_internal {
+constexpr int64_t SaturatedAdd(int64_t value, TimeDelta delta) {
+ // Treat Min/Max() as +/- infinity (additions involving two infinities are
+ // only valid if signs match).
+ if (delta.is_max()) {
+ CHECK_GT(value, std::numeric_limits<int64_t>::min());
+ return std::numeric_limits<int64_t>::max();
+ } else if (delta.is_min()) {
+ CHECK_LT(value, std::numeric_limits<int64_t>::max());
+ return std::numeric_limits<int64_t>::min();
+ }
+
+ return base::ClampAdd(value, delta.delta_);
+}
+
+constexpr int64_t SaturatedSub(int64_t value, TimeDelta delta) {
+ // Treat Min/Max() as +/- infinity (subtractions involving two infinities are
+ // only valid if signs are opposite).
+ if (delta.is_max()) {
+ CHECK_LT(value, std::numeric_limits<int64_t>::max());
+ return std::numeric_limits<int64_t>::min();
+ } else if (delta.is_min()) {
+ CHECK_GT(value, std::numeric_limits<int64_t>::min());
+ return std::numeric_limits<int64_t>::max();
+ }
+
+ return base::ClampSub(value, delta.delta_);
+}
+
// TimeBase--------------------------------------------------------------------
// Provides value storage and comparison/math operations common to all time
@@ -377,11 +410,11 @@ class TimeBase {
// Returns the maximum/minimum times, which should be greater/less than than
// any reasonable time with which we might compare it.
- static TimeClass Max() {
+ static constexpr TimeClass Max() {
return TimeClass(std::numeric_limits<int64_t>::max());
}
- static TimeClass Min() {
+ static constexpr TimeClass Min() {
return TimeClass(std::numeric_limits<int64_t>::min());
}
@@ -403,51 +436,39 @@ class TimeBase {
return TimeDelta::FromMicroseconds(us_);
}
- TimeClass& operator=(TimeClass other) {
+ constexpr TimeClass& operator=(TimeClass other) {
us_ = other.us_;
return *(static_cast<TimeClass*>(this));
}
// Compute the difference between two times.
- TimeDelta operator-(TimeClass other) const {
+ constexpr TimeDelta operator-(TimeClass other) const {
return TimeDelta::FromMicroseconds(us_ - other.us_);
}
// Return a new time modified by some delta.
- TimeClass operator+(TimeDelta delta) const {
+ constexpr TimeClass operator+(TimeDelta delta) const {
return TimeClass(time_internal::SaturatedAdd(us_, delta));
}
- TimeClass operator-(TimeDelta delta) const {
+ constexpr TimeClass operator-(TimeDelta delta) const {
return TimeClass(time_internal::SaturatedSub(us_, delta));
}
// Modify by some time delta.
- TimeClass& operator+=(TimeDelta delta) {
+ constexpr TimeClass& operator+=(TimeDelta delta) {
return static_cast<TimeClass&>(*this = (*this + delta));
}
- TimeClass& operator-=(TimeDelta delta) {
+ constexpr TimeClass& operator-=(TimeDelta delta) {
return static_cast<TimeClass&>(*this = (*this - delta));
}
// Comparison operators
- bool operator==(TimeClass other) const {
- return us_ == other.us_;
- }
- bool operator!=(TimeClass other) const {
- return us_ != other.us_;
- }
- bool operator<(TimeClass other) const {
- return us_ < other.us_;
- }
- bool operator<=(TimeClass other) const {
- return us_ <= other.us_;
- }
- bool operator>(TimeClass other) const {
- return us_ > other.us_;
- }
- bool operator>=(TimeClass other) const {
- return us_ >= other.us_;
- }
+ constexpr bool operator==(TimeClass other) const { return us_ == other.us_; }
+ constexpr bool operator!=(TimeClass other) const { return us_ != other.us_; }
+ constexpr bool operator<(TimeClass other) const { return us_ < other.us_; }
+ constexpr bool operator<=(TimeClass other) const { return us_ <= other.us_; }
+ constexpr bool operator>(TimeClass other) const { return us_ > other.us_; }
+ constexpr bool operator>=(TimeClass other) const { return us_ >= other.us_; }
protected:
constexpr explicit TimeBase(int64_t us) : us_(us) {}
@@ -458,8 +479,8 @@ class TimeBase {
} // namespace time_internal
-template<class TimeClass>
-inline TimeClass operator+(TimeDelta delta, TimeClass t) {
+template <class TimeClass>
+inline constexpr TimeClass operator+(TimeDelta delta, TimeClass t) {
return t + delta;
}
@@ -816,13 +837,7 @@ constexpr TimeDelta TimeDelta::Min() {
// static
constexpr TimeDelta TimeDelta::FromDouble(double value) {
- // TODO(crbug.com/612601): Use saturated_cast<int64_t>(value) once we sort out
- // the Min() behavior.
- return value > std::numeric_limits<int64_t>::max()
- ? Max()
- : value < std::numeric_limits<int64_t>::min()
- ? Min()
- : TimeDelta(static_cast<int64_t>(value));
+ return TimeDelta(saturated_cast<int64_t>(value));
}
// static
diff --git a/chromium/base/time/time_exploded_posix.cc b/chromium/base/time/time_exploded_posix.cc
index 0655703a1f4..7683d13d2a1 100644
--- a/chromium/base/time/time_exploded_posix.cc
+++ b/chromium/base/time/time_exploded_posix.cc
@@ -26,8 +26,9 @@
#if defined(OS_FUCHSIA)
#include <fuchsia/deprecatedtimezone/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+#include "base/fuchsia/default_context.h"
#include "base/fuchsia/fuchsia_logging.h"
-#include "base/fuchsia/service_directory_client.h"
#include "base/no_destructor.h"
#include "base/numerics/clamped_math.h"
#endif
@@ -72,11 +73,16 @@ void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
#elif defined(OS_FUCHSIA)
typedef time_t SysTime;
+fuchsia::deprecatedtimezone::TimezoneSyncPtr ConnectTimeZoneServiceSync() {
+ fuchsia::deprecatedtimezone::TimezoneSyncPtr timezone;
+ base::fuchsia::ComponentContextForCurrentProcess()->svc()->Connect(
+ timezone.NewRequest());
+ return timezone;
+}
+
SysTime GetTimezoneOffset(SysTime utc_time) {
static base::NoDestructor<fuchsia::deprecatedtimezone::TimezoneSyncPtr>
- timezone(
- base::fuchsia::ServiceDirectoryClient::ForCurrentProcess()
- ->ConnectToServiceSync<fuchsia::deprecatedtimezone::Timezone>());
+ timezone(ConnectTimeZoneServiceSync());
int64_t milliseconds_since_epoch =
base::ClampMul(utc_time, base::Time::kMillisecondsPerSecond);
diff --git a/chromium/base/time/time_unittest.cc b/chromium/base/time/time_unittest.cc
index 5e8bad99065..fb00f8bf866 100644
--- a/chromium/base/time/time_unittest.cc
+++ b/chromium/base/time/time_unittest.cc
@@ -1352,6 +1352,10 @@ TEST(TimeDelta, MaxConversions) {
.is_max(),
"");
+ static_assert(
+ TimeDelta::FromMicrosecondsD(max_d).is_max(),
+ "Make sure that 2^63 correctly gets clamped to `max` (crbug.com/612601)");
+
// Floating point arithmetic resulting in infinity isn't constexpr in C++14.
EXPECT_TRUE(
TimeDelta::FromMillisecondsD(std::numeric_limits<double>::infinity())
diff --git a/chromium/base/time/time_win_unittest.cc b/chromium/base/time/time_win_unittest.cc
index 7460bd17c34..a21b982da5e 100644
--- a/chromium/base/time/time_win_unittest.cc
+++ b/chromium/base/time/time_win_unittest.cc
@@ -259,13 +259,11 @@ TEST(TimeTicks, TSCTicksPerSecond) {
// Read the CPU frequency from the registry.
base::win::RegKey processor_key(
HKEY_LOCAL_MACHINE,
- STRING16_LITERAL("Hardware\\Description\\System\\CentralProcessor\\0"),
- KEY_QUERY_VALUE);
+ L"Hardware\\Description\\System\\CentralProcessor\\0", KEY_QUERY_VALUE);
ASSERT_TRUE(processor_key.Valid());
DWORD processor_mhz_from_registry;
ASSERT_EQ(ERROR_SUCCESS,
- processor_key.ReadValueDW(STRING16_LITERAL("~MHz"),
- &processor_mhz_from_registry));
+ processor_key.ReadValueDW(L"~MHz", &processor_mhz_from_registry));
// Expect the measured TSC frequency to be similar to the processor
// frequency from the registry (0.5% error).
diff --git a/chromium/base/trace_event/OWNERS b/chromium/base/trace_event/OWNERS
index aa3ab84aa9f..8d95238ff95 100644
--- a/chromium/base/trace_event/OWNERS
+++ b/chromium/base/trace_event/OWNERS
@@ -1,4 +1,3 @@
-chiniforooshan@chromium.org
eseckler@chromium.org
oysteine@chromium.org
primiano@chromium.org
diff --git a/chromium/base/trace_event/blame_context.cc b/chromium/base/trace_event/blame_context.cc
index 3c5f32ab464..e7599efa83f 100644
--- a/chromium/base/trace_event/blame_context.cc
+++ b/chromium/base/trace_event/blame_context.cc
@@ -40,6 +40,8 @@ BlameContext::~BlameContext() {
void BlameContext::Enter() {
DCHECK(WasInitialized());
+ if (LIKELY(!*category_group_enabled_))
+ return;
TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_ENTER_CONTEXT,
category_group_enabled_, name_, scope_, id_,
nullptr, TRACE_EVENT_FLAG_HAS_ID);
@@ -47,6 +49,8 @@ void BlameContext::Enter() {
void BlameContext::Leave() {
DCHECK(WasInitialized());
+ if (LIKELY(!*category_group_enabled_))
+ return;
TRACE_EVENT_API_ADD_TRACE_EVENT(TRACE_EVENT_PHASE_LEAVE_CONTEXT,
category_group_enabled_, name_, scope_, id_,
nullptr, TRACE_EVENT_FLAG_HAS_ID);
@@ -55,7 +59,7 @@ void BlameContext::Leave() {
void BlameContext::TakeSnapshot() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(WasInitialized());
- if (!*category_group_enabled_)
+ if (LIKELY(!*category_group_enabled_))
return;
std::unique_ptr<trace_event::TracedValue> snapshot(
new trace_event::TracedValue);
diff --git a/chromium/base/trace_event/builtin_categories.h b/chromium/base/trace_event/builtin_categories.h
index 6717a2bd93b..1ac3513eb4f 100644
--- a/chromium/base/trace_event/builtin_categories.h
+++ b/chromium/base/trace_event/builtin_categories.h
@@ -15,6 +15,9 @@
// your code and you get a static assert, this is the right place to register
// the name. If the name is going to be used only for testing, please add it to
// |kIgnoredCategoriesForTesting| instead.
+//
+// Prefer to use '_' to separate word of category name, like content_capture.
+//
// Parameter |X| must be a *macro* that takes a single |name| string argument,
// denoting a category name.
#define INTERNAL_TRACE_LIST_BUILTIN_CATEGORIES(X) \
@@ -52,6 +55,7 @@
X("cma") \
X("compositor") \
X("content") \
+ X("content_capture") \
X("devtools") \
X("devtools.timeline") \
X("devtools.timeline.async") \
@@ -139,6 +143,7 @@
X("vk") \
X("wayland") \
X("webaudio") \
+ X("weblayer") \
X("WebCore") \
X("webrtc") \
X("xr") \
@@ -177,6 +182,7 @@
X(TRACE_DISABLED_BY_DEFAULT("gpu.device")) \
X(TRACE_DISABLED_BY_DEFAULT("gpu.service")) \
X(TRACE_DISABLED_BY_DEFAULT("ipc.flow")) \
+ X(TRACE_DISABLED_BY_DEFAULT("java-heap-profiler")) \
X(TRACE_DISABLED_BY_DEFAULT("layer-element")) \
X(TRACE_DISABLED_BY_DEFAULT("lifecycles")) \
X(TRACE_DISABLED_BY_DEFAULT("loading")) \
diff --git a/chromium/base/trace_event/malloc_dump_provider.cc b/chromium/base/trace_event/malloc_dump_provider.cc
index e89597c1d6b..7e42cfc20bb 100644
--- a/chromium/base/trace_event/malloc_dump_provider.cc
+++ b/chromium/base/trace_event/malloc_dump_provider.cc
@@ -134,7 +134,10 @@ bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
// TODO(fuchsia): Port, see https://crbug.com/706592.
#else
struct mallinfo info = mallinfo();
- DCHECK_GE(info.arena + info.hblkhd, info.uordblks);
+#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
+ // Sanitizers override mallinfo.
+ DCHECK_GT(static_cast<int>(info.uordblks), 0);
+#endif
// In case of Android's jemalloc |arena| is 0 and the outer pages size is
// reported by |hblkhd|. In case of dlmalloc the total is given by
diff --git a/chromium/base/trace_event/memory_dump_manager.cc b/chromium/base/trace_event/memory_dump_manager.cc
index 8b1bd7a0a58..d79d405ac9f 100644
--- a/chromium/base/trace_event/memory_dump_manager.cc
+++ b/chromium/base/trace_event/memory_dump_manager.cc
@@ -529,7 +529,8 @@ MemoryDumpManager::ProcessMemoryDumpAsyncState::ProcessMemoryDumpAsyncState(
dump_thread_task_runner(std::move(dump_thread_task_runner)) {
pending_dump_providers.reserve(dump_providers.size());
pending_dump_providers.assign(dump_providers.rbegin(), dump_providers.rend());
- MemoryDumpArgs args = {req_args.level_of_detail, req_args.dump_guid};
+ MemoryDumpArgs args = {req_args.level_of_detail, req_args.determinism,
+ req_args.dump_guid};
process_memory_dump = std::make_unique<ProcessMemoryDump>(args);
}
diff --git a/chromium/base/trace_event/memory_dump_manager_unittest.cc b/chromium/base/trace_event/memory_dump_manager_unittest.cc
index b33b677beb9..c7b413d46a5 100644
--- a/chromium/base/trace_event/memory_dump_manager_unittest.cc
+++ b/chromium/base/trace_event/memory_dump_manager_unittest.cc
@@ -54,6 +54,14 @@ MATCHER(IsLightDump, "") {
return arg.level_of_detail == MemoryDumpLevelOfDetail::LIGHT;
}
+MATCHER(IsDeterministicDump, "") {
+ return arg.determinism == MemoryDumpDeterminism::FORCE_GC;
+}
+
+MATCHER(IsNotDeterministicDump, "") {
+ return arg.determinism == MemoryDumpDeterminism::NONE;
+}
+
namespace {
const char* kMDPName = "TestDumpProvider";
@@ -193,12 +201,14 @@ class MemoryDumpManagerTest : public testing::Test {
// memory dump is complete. Returns:
// - return value: the |success| from the CreateProcessDump() callback.
bool RequestProcessDumpAndWait(MemoryDumpType dump_type,
- MemoryDumpLevelOfDetail level_of_detail) {
+ MemoryDumpLevelOfDetail level_of_detail,
+ MemoryDumpDeterminism determinism) {
RunLoop run_loop;
bool success = false;
static uint64_t test_guid = 1;
test_guid++;
- MemoryDumpRequestArgs request_args{test_guid, dump_type, level_of_detail};
+ MemoryDumpRequestArgs request_args{test_guid, dump_type, level_of_detail,
+ determinism};
// The signature of the callback delivered by MemoryDumpManager is:
// void ProcessMemoryDumpCallback(
@@ -276,7 +286,8 @@ TEST_F(MemoryDumpManagerTest, SingleDumper) {
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3);
for (int i = 0; i < 3; ++i) {
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
}
DisableTracing();
@@ -288,7 +299,8 @@ TEST_F(MemoryDumpManagerTest, SingleDumper) {
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
for (int i = 0; i < 3; ++i) {
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
}
DisableTracing();
}
@@ -302,7 +314,8 @@ TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) {
EnableForTracing();
EXPECT_CALL(mdp, OnMemoryDump(IsDetailedDump(), _));
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp);
@@ -312,7 +325,34 @@ TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) {
EnableForTracing();
EXPECT_CALL(mdp, OnMemoryDump(IsLightDump(), _));
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::LIGHT));
+ MemoryDumpLevelOfDetail::LIGHT,
+ MemoryDumpDeterminism::NONE));
+ DisableTracing();
+ mdm_->UnregisterDumpProvider(&mdp);
+}
+
+// Checks that requesting deterministic dumps actually propagates
+// the deterministic option properly to OnMemoryDump() call on dump providers.
+TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgsDeterministic) {
+ MockMemoryDumpProvider mdp;
+
+ RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ EnableForTracing();
+ EXPECT_CALL(mdp, OnMemoryDump(IsDeterministicDump(), _));
+ EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::FORCE_GC));
+ DisableTracing();
+ mdm_->UnregisterDumpProvider(&mdp);
+
+ // Check that requesting dumps with deterministic option set to false
+ // actually propagates to OnMemoryDump() call on dump providers.
+ RegisterDumpProvider(&mdp, ThreadTaskRunnerHandle::Get());
+ EnableForTracing();
+ EXPECT_CALL(mdp, OnMemoryDump(IsNotDeterministicDump(), _));
+ EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
+ MemoryDumpLevelOfDetail::LIGHT,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp);
}
@@ -328,7 +368,8 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
EXPECT_CALL(mdp1, OnMemoryDump(_, _));
EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0);
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
// Invert: enable mdp2 and disable mdp1.
@@ -338,7 +379,8 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0);
EXPECT_CALL(mdp2, OnMemoryDump(_, _));
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
// Enable both mdp1 and mdp2.
@@ -347,7 +389,8 @@ TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
EXPECT_CALL(mdp1, OnMemoryDump(_, _));
EXPECT_CALL(mdp2, OnMemoryDump(_, _));
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
}
@@ -368,7 +411,8 @@ TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) {
EXPECT_CALL(mdp, OnMemoryDump(_, _));
EnableForTracing();
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
}
@@ -378,7 +422,8 @@ TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) {
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
EnableForTracing();
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
}
@@ -389,7 +434,8 @@ TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) {
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
EnableForTracing();
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
}
@@ -401,7 +447,8 @@ TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) {
EXPECT_CALL(mdp, OnMemoryDump(_, _));
EnableForTracing();
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
}
}
@@ -439,7 +486,8 @@ TEST_F(MemoryDumpManagerTest, RespectTaskRunnerAffinity) {
while (!threads.empty()) {
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
// Unregister a MDP and destroy one thread at each iteration to check the
// live unregistration logic. The unregistration needs to happen on the same
@@ -485,13 +533,15 @@ TEST_F(MemoryDumpManagerTest, PostTaskForSequencedTaskRunner) {
task_runner1->set_enabled(false);
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
EXPECT_EQ(1u, task_runner1->no_of_post_tasks());
EXPECT_EQ(1u, task_runner2->no_of_post_tasks());
task_runner1->set_enabled(true);
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
EXPECT_EQ(2u, task_runner1->no_of_post_tasks());
EXPECT_EQ(2u, task_runner2->no_of_post_tasks());
DisableTracing();
@@ -522,7 +572,8 @@ TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) {
const int kNumDumps = 2 * GetMaxConsecutiveFailuresCount();
for (int i = 0; i < kNumDumps; i++) {
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
}
DisableTracing();
@@ -553,7 +604,8 @@ TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) {
for (int i = 0; i < 4; i++) {
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
}
DisableTracing();
@@ -584,7 +636,8 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileDumping) {
for (int i = 0; i < 4; i++) {
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
}
DisableTracing();
@@ -632,7 +685,8 @@ TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
EnableForTracing();
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
ASSERT_EQ(1, on_memory_dump_call_count);
DisableTracing();
@@ -679,7 +733,8 @@ TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) {
EnableForTracing();
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
ASSERT_EQ(1, on_memory_dump_call_count);
DisableTracing();
@@ -692,7 +747,8 @@ TEST_F(MemoryDumpManagerTest, TriggerDumpWithoutTracing) {
RegisterDumpProvider(&mdp, nullptr);
EXPECT_CALL(mdp, OnMemoryDump(_, _));
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
}
TEST_F(MemoryDumpManagerTest, BackgroundWhitelisting) {
@@ -707,7 +763,8 @@ TEST_F(MemoryDumpManagerTest, BackgroundWhitelisting) {
EXPECT_CALL(backgroundMdp, OnMemoryDump(_, _)).Times(1);
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::SUMMARY_ONLY,
- MemoryDumpLevelOfDetail::BACKGROUND));
+ MemoryDumpLevelOfDetail::BACKGROUND,
+ MemoryDumpDeterminism::NONE));
DisableTracing();
}
@@ -769,7 +826,8 @@ TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoonDuringDump) {
EnableForTracing();
for (int i = 0; i < 2; ++i) {
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
}
DisableTracing();
}
@@ -822,11 +880,14 @@ TEST_F(MemoryDumpManagerTest, NoStackOverflowWithTooManyMDPs) {
stopped_thread->Stop();
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::DETAILED));
+ MemoryDumpLevelOfDetail::DETAILED,
+ MemoryDumpDeterminism::NONE));
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::EXPLICITLY_TRIGGERED,
- MemoryDumpLevelOfDetail::BACKGROUND));
+ MemoryDumpLevelOfDetail::BACKGROUND,
+ MemoryDumpDeterminism::NONE));
EXPECT_TRUE(RequestProcessDumpAndWait(MemoryDumpType::SUMMARY_ONLY,
- MemoryDumpLevelOfDetail::BACKGROUND));
+ MemoryDumpLevelOfDetail::BACKGROUND,
+ MemoryDumpDeterminism::NONE));
}
} // namespace trace_event
diff --git a/chromium/base/trace_event/memory_dump_request_args.h b/chromium/base/trace_event/memory_dump_request_args.h
index c50a1cb2cfc..d62a0dc84c8 100644
--- a/chromium/base/trace_event/memory_dump_request_args.h
+++ b/chromium/base/trace_event/memory_dump_request_args.h
@@ -58,6 +58,12 @@ enum class MemoryDumpLevelOfDetail : uint32_t {
LAST = DETAILED
};
+// Tells the MemoryDumpProvider(s) if they should try to make the result
+// more deterministic by forcing garbage collection.
+// Keep this consistent with memory_instrumentation.mojo and
+// memory_instrumentation_struct_traits.{h,cc}
+enum class MemoryDumpDeterminism : uint32_t { NONE, FORCE_GC };
+
// Keep this consistent with memory_instrumentation.mojo and
// memory_instrumentation_struct_traits.{h,cc}
struct BASE_EXPORT MemoryDumpRequestArgs {
@@ -68,6 +74,7 @@ struct BASE_EXPORT MemoryDumpRequestArgs {
MemoryDumpType dump_type;
MemoryDumpLevelOfDetail level_of_detail;
+ MemoryDumpDeterminism determinism;
};
// Args for ProcessMemoryDump and passed to OnMemoryDump calls for memory dump
@@ -75,6 +82,8 @@ struct BASE_EXPORT MemoryDumpRequestArgs {
struct MemoryDumpArgs {
// Specifies how detailed the dumps should be.
MemoryDumpLevelOfDetail level_of_detail;
+ // Specifies whether the dumps should be more deterministic.
+ MemoryDumpDeterminism determinism;
// Globally unique identifier. In multi-process dumps, all processes issue a
// local dump with the same guid. This allows the trace importers to
diff --git a/chromium/base/trace_event/memory_infra_background_whitelist.cc b/chromium/base/trace_event/memory_infra_background_whitelist.cc
index 3874f4521ea..4c88b5ca4a5 100644
--- a/chromium/base/trace_event/memory_infra_background_whitelist.cc
+++ b/chromium/base/trace_event/memory_infra_background_whitelist.cc
@@ -67,8 +67,8 @@ const char* const kDumpProviderWhitelist[] = {
// A list of string names that are allowed for the memory allocator dumps in
// background mode.
const char* const kAllocatorDumpNameWhitelist[] = {
- "blink_gc",
- "blink_gc/allocated_objects",
+ "blink_gc/main/heap",
+ "blink_gc/workers/heap/worker_0x?",
"blink_objects/AdSubframe",
"blink_objects/AudioHandler",
"blink_objects/ContextLifecycleStateObserver",
@@ -344,6 +344,8 @@ const char* const kAllocatorDumpNameWhitelist[] = {
"sync/0x?/model_type/MANAGED_USER_WHITELIST",
"sync/0x?/model_type/MOUNTAIN_SHARE",
"sync/0x?/model_type/NIGORI",
+ "sync/0x?/model_type/OS_PREFERENCE",
+ "sync/0x?/model_type/OS_PRIORITY_PREFERENCE",
"sync/0x?/model_type/PASSWORD",
"sync/0x?/model_type/PREFERENCE",
"sync/0x?/model_type/PRINTER",
diff --git a/chromium/base/trace_event/trace_config.cc b/chromium/base/trace_event/trace_config.cc
index 061100c23ad..5b4493f1bd6 100644
--- a/chromium/base/trace_event/trace_config.cc
+++ b/chromium/base/trace_event/trace_config.cc
@@ -28,6 +28,8 @@ const char kRecordContinuously[] = "record-continuously";
const char kRecordAsMuchAsPossible[] = "record-as-much-as-possible";
const char kTraceToConsole[] = "trace-to-console";
const char kEnableSystrace[] = "enable-systrace";
+constexpr int kEnableSystraceLength = sizeof(kEnableSystrace) - 1;
+
const char kEnableArgumentFilter[] = "enable-argument-filter";
// String parameters that can be used to parse the trace config string.
@@ -35,6 +37,7 @@ const char kRecordModeParam[] = "record_mode";
const char kTraceBufferSizeInEvents[] = "trace_buffer_size_in_events";
const char kTraceBufferSizeInKb[] = "trace_buffer_size_in_kb";
const char kEnableSystraceParam[] = "enable_systrace";
+const char kSystraceEventsParam[] = "enable_systrace_events";
const char kEnableArgumentFilterParam[] = "enable_argument_filter";
// String parameters that is used to parse memory dump config in trace config
@@ -163,7 +166,7 @@ void TraceConfig::ProcessFilterConfig::ToDict(Value* dict) const {
std::set<base::ProcessId> ordered_set(included_process_ids_.begin(),
included_process_ids_.end());
for (auto process_id : ordered_set)
- list->GetList().emplace_back(static_cast<int>(process_id));
+ list->Append(static_cast<int>(process_id));
}
bool TraceConfig::ProcessFilterConfig::IsEnabled(
@@ -298,6 +301,7 @@ TraceConfig& TraceConfig::operator=(const TraceConfig& rhs) {
memory_dump_config_ = rhs.memory_dump_config_;
event_filters_ = rhs.event_filters_;
histogram_names_ = rhs.histogram_names_;
+ systrace_events_ = rhs.systrace_events_;
return *this;
}
@@ -355,6 +359,7 @@ void TraceConfig::Clear() {
process_filter_config_.Clear();
event_filters_.clear();
histogram_names_.clear();
+ systrace_events_.clear();
}
void TraceConfig::InitializeDefault() {
@@ -406,6 +411,15 @@ void TraceConfig::InitializeFromConfigDict(const Value& dict) {
else
SetDefaultMemoryDumpConfig();
}
+
+ systrace_events_.clear();
+ if (enable_systrace_) {
+ const Value* systrace_events = dict.FindListKey(kSystraceEventsParam);
+ if (systrace_events) {
+ for (const Value& value : systrace_events->GetList())
+ systrace_events_.insert(value.GetString());
+ }
+ }
}
void TraceConfig::InitializeFromConfigString(StringPiece config_string) {
@@ -425,6 +439,7 @@ void TraceConfig::InitializeFromStrings(StringPiece category_filter_string,
trace_buffer_size_in_events_ = 0;
trace_buffer_size_in_kb_ = 0;
enable_systrace_ = false;
+ systrace_events_.clear();
enable_argument_filter_ = false;
if (!trace_options_string.empty()) {
std::vector<std::string> split =
@@ -438,8 +453,27 @@ void TraceConfig::InitializeFromStrings(StringPiece category_filter_string,
record_mode_ = ECHO_TO_CONSOLE;
} else if (token == kRecordAsMuchAsPossible) {
record_mode_ = RECORD_AS_MUCH_AS_POSSIBLE;
- } else if (token == kEnableSystrace) {
+ } else if (token.find(kEnableSystrace) == 0) {
+ // Find optional events list.
+ const size_t length = token.length();
+ if (length == kEnableSystraceLength) {
+ // Use all predefined categories.
+ enable_systrace_ = true;
+ continue;
+ }
+ const auto system_events_not_trimmed =
+ token.substr(kEnableSystraceLength);
+ const auto system_events =
+ TrimString(system_events_not_trimmed, kWhitespaceASCII, TRIM_ALL);
+ if (system_events[0] != '=') {
+ LOG(ERROR) << "Failed to parse " << token;
+ continue;
+ }
enable_systrace_ = true;
+ const std::vector<std::string> split_systrace_events = SplitString(
+ system_events.substr(1), " ", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+ for (const std::string& systrace_event : split_systrace_events)
+ systrace_events_.insert(systrace_event);
} else if (token == kEnableArgumentFilter) {
enable_argument_filter_ = true;
}
@@ -622,9 +656,22 @@ Value TraceConfig::ToValue() const {
dict.SetKey(kHistogramNamesParam, Value(std::move(histogram_names)));
}
+ if (enable_systrace_) {
+ if (!systrace_events_.empty()) {
+ std::vector<Value> systrace_events;
+ for (const std::string& systrace_event : systrace_events_)
+ systrace_events.emplace_back(systrace_event);
+ dict.SetKey(kSystraceEventsParam, Value(std::move(systrace_events)));
+ }
+ }
+
return dict;
}
+void TraceConfig::EnableSystraceEvent(const std::string& systrace_event) {
+ systrace_events_.insert(systrace_event);
+}
+
void TraceConfig::EnableHistogram(const std::string& histogram_name) {
histogram_names_.insert(histogram_name);
}
@@ -647,10 +694,24 @@ std::string TraceConfig::ToTraceOptionsString() const {
default:
NOTREACHED();
}
- if (enable_systrace_)
- ret = ret + "," + kEnableSystrace;
- if (enable_argument_filter_)
- ret = ret + "," + kEnableArgumentFilter;
+ if (enable_systrace_) {
+ ret += ",";
+ ret += kEnableSystrace;
+ bool first_param = true;
+ for (const std::string& systrace_event : systrace_events_) {
+ if (first_param) {
+ ret += "=";
+ first_param = false;
+ } else {
+ ret += " ";
+ }
+ ret = ret + systrace_event;
+ }
+ }
+ if (enable_argument_filter_) {
+ ret += ",";
+ ret += kEnableArgumentFilter;
+ }
return ret;
}
diff --git a/chromium/base/trace_event/trace_config.h b/chromium/base/trace_event/trace_config.h
index 9798c7f6ae9..3e3d0078a7f 100644
--- a/chromium/base/trace_event/trace_config.h
+++ b/chromium/base/trace_event/trace_config.h
@@ -242,6 +242,7 @@ class BASE_EXPORT TraceConfig {
}
void SetTraceBufferSizeInKb(size_t size) { trace_buffer_size_in_kb_ = size; }
void EnableSystrace() { enable_systrace_ = true; }
+ void EnableSystraceEvent(const std::string& systrace_event);
void EnableArgumentFilter() { enable_argument_filter_ = true; }
void EnableHistogram(const std::string& histogram_name);
@@ -255,6 +256,11 @@ class BASE_EXPORT TraceConfig {
// Write the string representation of the CategoryFilter part.
std::string ToCategoryFilterString() const;
+ // Write the string representation of the trace options part (record mode,
+ // systrace, argument filtering). Does not include category filters, event
+ // filters, or memory dump configs.
+ std::string ToTraceOptionsString() const;
+
// Returns true if at least one category in the list is enabled by this
// trace config. This is used to determine if the category filters are
// enabled in the TRACE_* macros.
@@ -286,6 +292,10 @@ class BASE_EXPORT TraceConfig {
event_filters_ = filter_configs;
}
+ const std::unordered_set<std::string>& systrace_events() const {
+ return systrace_events_;
+ }
+
const std::unordered_set<std::string>& histogram_names() const {
return histogram_names_;
}
@@ -294,6 +304,7 @@ class BASE_EXPORT TraceConfig {
FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, TraceConfigFromValidLegacyFormat);
FRIEND_TEST_ALL_PREFIXES(TraceConfigTest,
TraceConfigFromInvalidLegacyStrings);
+ FRIEND_TEST_ALL_PREFIXES(TraceConfigTest, SystraceEventsSerialization);
// The default trace config, used when none is provided.
// Allows all non-disabled-by-default categories through, except if they end
@@ -317,8 +328,6 @@ class BASE_EXPORT TraceConfig {
void SetEventFiltersFromConfigList(const Value& event_filters);
Value ToValue() const;
- std::string ToTraceOptionsString() const;
-
TraceRecordMode record_mode_;
size_t trace_buffer_size_in_events_ = 0; // 0 specifies default size
size_t trace_buffer_size_in_kb_ = 0; // 0 specifies default size
@@ -332,6 +341,7 @@ class BASE_EXPORT TraceConfig {
EventFilters event_filters_;
std::unordered_set<std::string> histogram_names_;
+ std::unordered_set<std::string> systrace_events_;
};
} // namespace trace_event
diff --git a/chromium/base/trace_event/trace_config_unittest.cc b/chromium/base/trace_event/trace_config_unittest.cc
index c7e98a4ce64..16cd67ecd9e 100644
--- a/chromium/base/trace_event/trace_config_unittest.cc
+++ b/chromium/base/trace_event/trace_config_unittest.cc
@@ -674,5 +674,25 @@ TEST(TraceConfigTest, LegacyStringToMemoryDumpConfig) {
tc.memory_dump_config().heap_profiler_options.breakdown_threshold_bytes);
}
+TEST(TraceConfigTest, SystraceEventsSerialization) {
+ TraceConfig tc(MemoryDumpManager::kTraceCategory, "");
+ tc.EnableSystrace();
+ EXPECT_EQ(0U, tc.systrace_events().size());
+ tc.EnableSystraceEvent("power"); // As a events category
+ tc.EnableSystraceEvent("timer:tick_stop"); // As an event
+ EXPECT_EQ(2U, tc.systrace_events().size());
+
+ const TraceConfig tc1(MemoryDumpManager::kTraceCategory,
+ tc.ToTraceOptionsString());
+ EXPECT_EQ(2U, tc1.systrace_events().size());
+ EXPECT_TRUE(tc1.systrace_events().count("power"));
+ EXPECT_TRUE(tc1.systrace_events().count("timer:tick_stop"));
+
+ const TraceConfig tc2(tc.ToString());
+ EXPECT_EQ(2U, tc2.systrace_events().size());
+ EXPECT_TRUE(tc2.systrace_events().count("power"));
+ EXPECT_TRUE(tc2.systrace_events().count("timer:tick_stop"));
+}
+
} // namespace trace_event
} // namespace base
diff --git a/chromium/base/trace_event/trace_log.cc b/chromium/base/trace_event/trace_log.cc
index 39c19c8e46b..1e0cd1413c4 100644
--- a/chromium/base/trace_event/trace_log.cc
+++ b/chromium/base/trace_event/trace_log.cc
@@ -378,7 +378,7 @@ TraceLog::TraceLog()
num_traces_recorded_(0),
process_sort_index_(0),
process_id_hash_(0),
- process_id_(0),
+ process_id_(base::kNullProcessId),
trace_options_(kInternalRecordUntilFull),
trace_config_(TraceConfig()),
thread_shared_chunk_index_(0),
diff --git a/chromium/base/trace_event/trace_log.h b/chromium/base/trace_event/trace_log.h
index 63be5aa4649..e0c850ca41e 100644
--- a/chromium/base/trace_event/trace_log.h
+++ b/chromium/base/trace_event/trace_log.h
@@ -301,6 +301,7 @@ class BASE_EXPORT TraceLog : public MemoryDumpProvider {
TraceEventHandle handle);
int process_id() const { return process_id_; }
+ const std::string& process_name() const { return process_name_; }
uint64_t MangleEventId(uint64_t id);
diff --git a/chromium/base/trace_event/traced_value.cc b/chromium/base/trace_event/traced_value.cc
index b1ea5aabeac..5b6467a4a15 100644
--- a/chromium/base/trace_event/traced_value.cc
+++ b/chromium/base/trace_event/traced_value.cc
@@ -341,7 +341,7 @@ class PickleWriter final : public TracedValue::Writer {
stack.push_back(cur_dict);
cur_dict = cur_dict->SetKey(ReadKeyName(it), std::move(new_dict));
} else {
- cur_list->GetList().push_back(std::move(new_dict));
+ cur_list->Append(std::move(new_dict));
// |new_dict| is invalidated at this point, so |cur_dict| needs to
// be reset.
cur_dict = &cur_list->GetList().back();
@@ -369,7 +369,7 @@ class PickleWriter final : public TracedValue::Writer {
cur_list = cur_dict->SetKey(ReadKeyName(it), std::move(new_list));
cur_dict = nullptr;
} else {
- cur_list->GetList().push_back(std::move(new_list));
+ cur_list->Append(std::move(new_list));
stack.push_back(cur_list);
// |cur_list| is invalidated at this point by the Append, so it
// needs to be reset.
@@ -383,7 +383,7 @@ class PickleWriter final : public TracedValue::Writer {
if (cur_dict) {
cur_dict->SetBoolKey(ReadKeyName(it), value);
} else {
- cur_list->GetList().emplace_back(value);
+ cur_list->Append(value);
}
} break;
@@ -393,7 +393,7 @@ class PickleWriter final : public TracedValue::Writer {
if (cur_dict) {
cur_dict->SetIntKey(ReadKeyName(it), value);
} else {
- cur_list->GetList().emplace_back(value);
+ cur_list->Append(value);
}
} break;
@@ -403,7 +403,7 @@ class PickleWriter final : public TracedValue::Writer {
if (cur_dict) {
cur_dict->SetDoubleKey(ReadKeyName(it), value);
} else {
- cur_list->GetList().emplace_back(value);
+ cur_list->Append(value);
}
} break;
@@ -413,7 +413,7 @@ class PickleWriter final : public TracedValue::Writer {
if (cur_dict) {
cur_dict->SetStringKey(ReadKeyName(it), std::move(value));
} else {
- cur_list->GetList().emplace_back(std::move(value));
+ cur_list->Append(std::move(value));
}
} break;
diff --git a/chromium/base/unguessable_token.h b/chromium/base/unguessable_token.h
index 222177a9a1e..895dbc46c4c 100644
--- a/chromium/base/unguessable_token.h
+++ b/chromium/base/unguessable_token.h
@@ -20,26 +20,30 @@ namespace base {
struct UnguessableTokenHash;
// UnguessableToken is, like Token, a randomly chosen 128-bit value. Unlike
-// Token however, a new UnguessableToken must always be generated at runtime
-// from a cryptographically strong random source (or copied or serialized and
+// Token, a new UnguessableToken is always generated at runtime from a
+// cryptographically strong random source (or copied or serialized and
// deserialized from another such UnguessableToken). It can be used as part of a
// larger aggregate type, or as an ID in and of itself.
//
-// UnguessableToken can be used to implement "Capability-Based Security".
-// In other words, UnguessableToken can be used when the resource associated
-// with the ID needs to be protected against manipulation by other untrusted
-// agents in the system, and there is no other convenient way to verify the
-// authority of the agent to do so (because the resource is part of a table
-// shared across processes, for instance). In such a scheme, knowledge of the
-// token value in and of itself is sufficient proof of authority to carry out
-// an operation against the associated resource.
+// An UnguessableToken is a strong *bearer token*. Bearer tokens are like HTTP
+// cookies: if a caller has the token, the callee thereby considers the caller
+// authorized to request the operation the callee performs.
+//
+// UnguessableToken can be used when the resource associated with the ID needs
+// to be protected against manipulation by other untrusted agents in the system,
+// and there is no other convenient way to verify the authority of the agent to
+// do so (because the resource is part of a table shared across processes, for
+// instance). In such a scheme, knowledge of the token value in and of itself is
+// sufficient proof of authority to carry out an operation on the associated
+// resource.
//
// Use Create() for creating new UnguessableTokens.
//
// NOTE: It is illegal to send empty UnguessableTokens across processes, and
-// sending/receiving empty tokens should be treated as a security issue.
-// If there is a valid scenario for sending "no token" across processes,
-// base::Optional should be used instead of an empty token.
+// sending/receiving empty tokens should be treated as a security issue. If
+// there is a valid scenario for sending "no token" across processes, use
+// base::Optional instead of an empty token.
+
class BASE_EXPORT UnguessableToken {
public:
// Create a unique UnguessableToken.
diff --git a/chromium/base/util/memory_pressure/BUILD.gn b/chromium/base/util/memory_pressure/BUILD.gn
index 6065c9a7a4f..16bb4784b54 100644
--- a/chromium/base/util/memory_pressure/BUILD.gn
+++ b/chromium/base/util/memory_pressure/BUILD.gn
@@ -41,3 +41,16 @@ source_set("unittests") {
"//testing/gtest",
]
}
+
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "fake_memory_pressure_monitor.cc",
+ "fake_memory_pressure_monitor.h",
+ ]
+
+ public_deps = [
+ ":memory_pressure",
+ "//base",
+ ]
+}
diff --git a/chromium/base/memory/fake_memory_pressure_monitor.cc b/chromium/base/util/memory_pressure/fake_memory_pressure_monitor.cc
index 59fd3ef66eb..b9273c5a7ac 100644
--- a/chromium/base/memory/fake_memory_pressure_monitor.cc
+++ b/chromium/base/util/memory_pressure/fake_memory_pressure_monitor.cc
@@ -2,13 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/memory/fake_memory_pressure_monitor.h"
+#include "base/util/memory_pressure/fake_memory_pressure_monitor.h"
-namespace base {
+namespace util {
namespace test {
FakeMemoryPressureMonitor::FakeMemoryPressureMonitor()
- : MemoryPressureMonitor(),
+ : MultiSourceMemoryPressureMonitor(),
memory_pressure_level_(MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_NONE) {}
FakeMemoryPressureMonitor::~FakeMemoryPressureMonitor() {}
@@ -30,4 +30,4 @@ void FakeMemoryPressureMonitor::SetDispatchCallback(
}
} // namespace test
-} // namespace base
+} // namespace util
diff --git a/chromium/base/memory/fake_memory_pressure_monitor.h b/chromium/base/util/memory_pressure/fake_memory_pressure_monitor.h
index d012876d54e..e972d08c001 100644
--- a/chromium/base/memory/fake_memory_pressure_monitor.h
+++ b/chromium/base/util/memory_pressure/fake_memory_pressure_monitor.h
@@ -2,17 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef BASE_MEMORY_FAKE_MEMORY_PRESSURE_MONITOR_H_
-#define BASE_MEMORY_FAKE_MEMORY_PRESSURE_MONITOR_H_
+#ifndef BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_
+#define BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_
#include "base/macros.h"
-#include "base/memory/memory_pressure_monitor.h"
+#include "base/util/memory_pressure/multi_source_memory_pressure_monitor.h"
-namespace base {
+namespace util {
namespace test {
-class FakeMemoryPressureMonitor : public base::MemoryPressureMonitor {
+class FakeMemoryPressureMonitor
+ : public ::util::MultiSourceMemoryPressureMonitor {
public:
+ using MemoryPressureLevel =
+ ::util::MultiSourceMemoryPressureMonitor::MemoryPressureLevel;
+ using DispatchCallback =
+ ::util::MultiSourceMemoryPressureMonitor::DispatchCallback;
+
FakeMemoryPressureMonitor();
~FakeMemoryPressureMonitor() override;
@@ -29,6 +35,6 @@ class FakeMemoryPressureMonitor : public base::MemoryPressureMonitor {
};
} // namespace test
-} // namespace base
+} // namespace util
-#endif // BASE_MEMORY_FAKE_MEMORY_PRESSURE_MONITOR_H_
+#endif // BASE_UTIL_MEMORY_PRESSURE_FAKE_MEMORY_PRESSURE_MONITOR_H_
diff --git a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
index e8c59d1ea70..2b9a2c43eb0 100644
--- a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
+++ b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.cc
@@ -17,16 +17,23 @@ MultiSourceMemoryPressureMonitor::MultiSourceMemoryPressureMonitor()
dispatch_callback_(base::BindRepeating(
&base::MemoryPressureListener::NotifyMemoryPressure)),
aggregator_(this) {
- // This can't be in the parameter list because |sequence_checker_| wouldn't be
- // available, which would be needed by the |system_evaluator_|'s constructor's
- // call to CreateVoter().
+}
+
+MultiSourceMemoryPressureMonitor::~MultiSourceMemoryPressureMonitor() {
+ // Destroy system evaluator early while the remaining members of this class
+ // still exist. MultiSourceMemoryPressureMonitor implements
+ // MemoryPressureVoteAggregator::Delegate, and
+ // delegate_->OnMemoryPressureLevelChanged() gets indirectly called during
+ // ~SystemMemoryPressureEvaluator().
+ system_evaluator_.reset();
+}
+
+void MultiSourceMemoryPressureMonitor::Start() {
system_evaluator_ =
SystemMemoryPressureEvaluator::CreateDefaultSystemEvaluator(this);
StartMetricsTimer();
}
-MultiSourceMemoryPressureMonitor::~MultiSourceMemoryPressureMonitor() = default;
-
void MultiSourceMemoryPressureMonitor::StartMetricsTimer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Unretained is safe here since this task is running on a timer owned by this
diff --git a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h
index 3787ed9d4d9..078d58a4320 100644
--- a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h
+++ b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor.h
@@ -30,6 +30,9 @@ class MultiSourceMemoryPressureMonitor
MultiSourceMemoryPressureMonitor();
~MultiSourceMemoryPressureMonitor() override;
+ // Start monitoring memory pressure using the platform-specific voter.
+ void Start();
+
// MemoryPressureMonitor implementation.
MemoryPressureLevel GetCurrentPressureLevel() const override;
void SetDispatchCallback(const DispatchCallback& callback) override;
diff --git a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc
index 8c119714736..d796e2168c3 100644
--- a/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc
+++ b/chromium/base/util/memory_pressure/multi_source_memory_pressure_monitor_unittest.cc
@@ -14,6 +14,7 @@ namespace util {
TEST(MultiSourceMemoryPressureMonitorTest, RunDispatchCallback) {
base::test::TaskEnvironment task_environment;
MultiSourceMemoryPressureMonitor monitor;
+ monitor.Start();
auto* aggregator = monitor.aggregator_for_testing();
bool callback_called = false;
diff --git a/chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc b/chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc
index 8ef9d6a09b5..3e0297366b6 100644
--- a/chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc
+++ b/chromium/base/util/memory_pressure/system_memory_pressure_evaluator_win_unittest.cc
@@ -146,7 +146,8 @@ class WinSystemMemoryPressureEvaluatorTest : public testing::Test {
evaluator->CalculateCurrentPressureLevel());
}
- base::MessageLoopForUI message_loop_;
+ base::test::SingleThreadTaskEnvironment task_environment_{
+ base::test::SingleThreadTaskEnvironment::MainThreadType::UI};
};
// Tests the fundamental direct calculation of memory pressure with automatic
diff --git a/chromium/base/values.cc b/chromium/base/values.cc
index 02e92b7e082..6886b846e8d 100644
--- a/chromium/base/values.cc
+++ b/chromium/base/values.cc
@@ -58,7 +58,7 @@ std::unique_ptr<Value> CopyListWithoutEmptyChildren(const Value& list) {
for (const auto& entry : list.GetList()) {
std::unique_ptr<Value> child_copy = CopyWithoutEmptyChildren(entry);
if (child_copy)
- copy.GetList().push_back(std::move(*child_copy));
+ copy.Append(std::move(*child_copy));
}
return copy.GetList().empty() ? nullptr
: std::make_unique<Value>(std::move(copy));
@@ -147,6 +147,18 @@ std::unique_ptr<Value> Value::ToUniquePtrValue(Value val) {
return std::make_unique<Value>(std::move(val));
}
+// static
+const DictionaryValue& Value::AsDictionaryValue(const Value& val) {
+ CHECK(val.is_dict());
+ return static_cast<const DictionaryValue&>(val);
+}
+
+// static
+const ListValue& Value::AsListValue(const Value& val) {
+ CHECK(val.is_list());
+ return static_cast<const ListValue&>(val);
+}
+
Value::Value(Value&& that) noexcept {
InternalMoveConstructFrom(std::move(that));
}
@@ -234,7 +246,7 @@ Value::Value(const DictStorage& in_dict) : type_(Type::DICTIONARY), dict_() {
Value::Value(DictStorage&& in_dict) noexcept
: type_(Type::DICTIONARY), dict_(std::move(in_dict)) {}
-Value::Value(const ListStorage& in_list) : type_(Type::LIST), list_() {
+Value::Value(span<const Value> in_list) : type_(Type::LIST), list_() {
list_.reserve(in_list.size());
for (const auto& val : in_list)
list_.emplace_back(val.Clone());
@@ -335,11 +347,79 @@ Value::ListStorage& Value::GetList() {
return list_;
}
-const Value::ListStorage& Value::GetList() const {
+span<const Value> Value::GetList() const {
CHECK(is_list());
return list_;
}
+Value::ListStorage Value::TakeList() {
+ CHECK(is_list());
+ return std::exchange(list_, ListStorage());
+}
+
+void Value::Append(bool value) {
+ CHECK(is_list());
+ list_.emplace_back(value);
+}
+
+void Value::Append(int value) {
+ CHECK(is_list());
+ list_.emplace_back(value);
+}
+
+void Value::Append(double value) {
+ CHECK(is_list());
+ list_.emplace_back(value);
+}
+
+void Value::Append(const char* value) {
+ CHECK(is_list());
+ list_.emplace_back(value);
+}
+
+void Value::Append(StringPiece value) {
+ CHECK(is_list());
+ list_.emplace_back(value);
+}
+
+void Value::Append(std::string&& value) {
+ CHECK(is_list());
+ list_.emplace_back(std::move(value));
+}
+
+void Value::Append(const char16* value) {
+ CHECK(is_list());
+ list_.emplace_back(value);
+}
+
+void Value::Append(StringPiece16 value) {
+ CHECK(is_list());
+ list_.emplace_back(value);
+}
+
+void Value::Append(Value&& value) {
+ CHECK(is_list());
+ list_.emplace_back(std::move(value));
+}
+
+bool Value::EraseListIter(ListStorage::const_iterator iter) {
+ CHECK(is_list());
+ if (iter == list_.end())
+ return false;
+
+ list_.erase(iter);
+ return true;
+}
+
+bool Value::EraseListIter(CheckedContiguousConstIterator<Value> iter) {
+ const auto offset = iter - static_cast<const Value*>(this)->GetList().begin();
+ return EraseListIter(list_.begin() + offset);
+}
+
+size_t Value::EraseListValue(const Value& val) {
+ return EraseListValueIf([&val](const Value& other) { return val == other; });
+}
+
Value* Value::FindKey(StringPiece key) {
return const_cast<Value*>(static_cast<const Value*>(this)->FindKey(key));
}
@@ -1535,7 +1615,7 @@ std::unique_ptr<ListValue> ListValue::From(std::unique_ptr<Value> value) {
}
ListValue::ListValue() : Value(Type::LIST) {}
-ListValue::ListValue(const ListStorage& in_list) : Value(in_list) {}
+ListValue::ListValue(span<const Value> in_list) : Value(in_list) {}
ListValue::ListValue(ListStorage&& in_list) noexcept
: Value(std::move(in_list)) {}
diff --git a/chromium/base/values.h b/chromium/base/values.h
index 38e4f37d68a..ccf87bfd88c 100644
--- a/chromium/base/values.h
+++ b/chromium/base/values.h
@@ -32,6 +32,7 @@
#include <vector>
#include "base/base_export.h"
+#include "base/containers/checked_iterators.h"
#include "base/containers/flat_map.h"
#include "base/containers/span.h"
#include "base/macros.h"
@@ -111,6 +112,8 @@ class BASE_EXPORT Value {
// Adaptors for converting from the old way to the new way and vice versa.
static Value FromUniquePtrValue(std::unique_ptr<Value> val);
static std::unique_ptr<Value> ToUniquePtrValue(Value val);
+ static const DictionaryValue& AsDictionaryValue(const Value& val);
+ static const ListValue& AsListValue(const Value& val);
Value(Value&& that) noexcept;
Value() noexcept {} // A null value
@@ -144,7 +147,7 @@ class BASE_EXPORT Value {
explicit Value(const DictStorage& in_dict);
explicit Value(DictStorage&& in_dict) noexcept;
- explicit Value(const ListStorage& in_list);
+ explicit Value(span<const Value> in_list);
explicit Value(ListStorage&& in_list) noexcept;
Value& operator=(Value&& that) noexcept;
@@ -176,7 +179,46 @@ class BASE_EXPORT Value {
const BlobStorage& GetBlob() const;
ListStorage& GetList();
- const ListStorage& GetList() const;
+ span<const Value> GetList() const;
+
+ // Transfers ownership of the the underlying list to the caller. Subsequent
+ // calls to GetList() will return an empty list.
+ // Note: This CHECKs that type() is Type::LIST.
+ ListStorage TakeList();
+
+ // Appends |value| to the end of the list.
+ // Note: These CHECK that type() is Type::LIST.
+ void Append(bool value);
+ void Append(int value);
+ void Append(double value);
+ void Append(const char* value);
+ void Append(StringPiece value);
+ void Append(std::string&& value);
+ void Append(const char16* value);
+ void Append(StringPiece16 value);
+ void Append(Value&& value);
+
+ // Erases the Value pointed to by |iter|. Returns false if |iter| is out of
+ // bounds.
+ // Note: This CHECKs that type() is Type::LIST.
+ bool EraseListIter(ListStorage::const_iterator iter);
+ bool EraseListIter(CheckedContiguousConstIterator<Value> iter);
+
+ // Erases all Values that compare equal to |val|. Returns the number of
+ // deleted Values.
+ // Note: This CHECKs that type() is Type::LIST.
+ size_t EraseListValue(const Value& val);
+
+ // Erases all Values for which |pred| returns true. Returns the number of
+ // deleted Values.
+ // Note: This CHECKs that type() is Type::LIST.
+ template <typename Predicate>
+ size_t EraseListValueIf(Predicate pred) {
+ CHECK(is_list());
+ const size_t old_size = list_.size();
+ base::EraseIf(list_, pred);
+ return old_size - list_.size();
+ }
// |FindKey| looks up |key| in the underlying dictionary. If found, it returns
// a pointer to the element. Otherwise it returns nullptr.
@@ -723,7 +765,7 @@ class BASE_EXPORT ListValue : public Value {
static std::unique_ptr<ListValue> From(std::unique_ptr<Value> value);
ListValue();
- explicit ListValue(const ListStorage& in_list);
+ explicit ListValue(span<const Value> in_list);
explicit ListValue(ListStorage&& in_list) noexcept;
// Clears the contents of this ListValue
@@ -803,24 +845,25 @@ class BASE_EXPORT ListValue : public Value {
// DEPRECATED, use GetList()::erase() instead.
iterator Erase(iterator iter, std::unique_ptr<Value>* out_value);
+ using Value::Append;
// Appends a Value to the end of the list.
- // DEPRECATED, use GetList()::push_back() instead.
+ // DEPRECATED, use Value::Append() instead.
void Append(std::unique_ptr<Value> in_value);
// Convenience forms of Append.
- // DEPRECATED, use GetList()::emplace_back() instead.
+ // DEPRECATED, use Value::Append() instead.
void AppendBoolean(bool in_value);
void AppendInteger(int in_value);
void AppendDouble(double in_value);
void AppendString(StringPiece in_value);
void AppendString(const string16& in_value);
- // DEPRECATED, use GetList()::emplace_back() in a loop instead.
+ // DEPRECATED, use Value::Append() in a loop instead.
void AppendStrings(const std::vector<std::string>& in_values);
void AppendStrings(const std::vector<string16>& in_values);
// Appends a Value if it's not already present. Returns true if successful,
// or false if the value was already
- // DEPRECATED, use std::find() with GetList()::push_back() instead.
+ // DEPRECATED, use std::find() with Value::Append() instead.
bool AppendIfNotPresent(std::unique_ptr<Value> in_value);
// Insert a Value at index.
diff --git a/chromium/base/values_unittest.cc b/chromium/base/values_unittest.cc
index c8be7e5fd35..2ae7ae833a7 100644
--- a/chromium/base/values_unittest.cc
+++ b/chromium/base/values_unittest.cc
@@ -458,6 +458,133 @@ TEST(ValuesTest, MoveList) {
EXPECT_EQ(123, blank.GetList().back().GetInt());
}
+TEST(ValuesTest, TakeList) {
+ // Prepare a list with a value of each type.
+ ListValue value;
+ value.Append(Value(Value::Type::NONE));
+ value.Append(Value(Value::Type::BOOLEAN));
+ value.Append(Value(Value::Type::INTEGER));
+ value.Append(Value(Value::Type::DOUBLE));
+ value.Append(Value(Value::Type::STRING));
+ value.Append(Value(Value::Type::BINARY));
+ value.Append(Value(Value::Type::LIST));
+ value.Append(Value(Value::Type::DICTIONARY));
+
+ // Take ownership of the list and make sure its contents are what we expect.
+ auto list = value.TakeList();
+ EXPECT_EQ(8u, list.size());
+ EXPECT_TRUE(list[0].is_none());
+ EXPECT_TRUE(list[1].is_bool());
+ EXPECT_TRUE(list[2].is_int());
+ EXPECT_TRUE(list[3].is_double());
+ EXPECT_TRUE(list[4].is_string());
+ EXPECT_TRUE(list[5].is_blob());
+ EXPECT_TRUE(list[6].is_list());
+ EXPECT_TRUE(list[7].is_dict());
+
+ // Validate that |value| no longer contains values.
+ EXPECT_TRUE(value.GetList().empty());
+}
+
+TEST(ValuesTest, Append) {
+ ListValue value;
+ value.Append(true);
+ EXPECT_TRUE(value.GetList().back().is_bool());
+
+ value.Append(123);
+ EXPECT_TRUE(value.GetList().back().is_int());
+
+ value.Append(3.14);
+ EXPECT_TRUE(value.GetList().back().is_double());
+
+ std::string str = "foo";
+ value.Append(str.c_str());
+ EXPECT_TRUE(value.GetList().back().is_string());
+
+ value.Append(StringPiece(str));
+ EXPECT_TRUE(value.GetList().back().is_string());
+
+ value.Append(std::move(str));
+ EXPECT_TRUE(value.GetList().back().is_string());
+
+ string16 str16 = ASCIIToUTF16("bar");
+ value.Append(str16.c_str());
+ EXPECT_TRUE(value.GetList().back().is_string());
+
+ value.Append(base::StringPiece16(str16));
+ EXPECT_TRUE(value.GetList().back().is_string());
+
+ value.Append(Value());
+ EXPECT_TRUE(value.GetList().back().is_none());
+
+ value.Append(Value(Value::Type::DICTIONARY));
+ EXPECT_TRUE(value.GetList().back().is_dict());
+
+ value.Append(Value(Value::Type::LIST));
+ EXPECT_TRUE(value.GetList().back().is_list());
+}
+
+TEST(ValuesTest, EraseListIter) {
+ ListValue value;
+ value.Append(1);
+ value.Append(2);
+ value.Append(3);
+
+ EXPECT_TRUE(value.EraseListIter(value.GetList().begin() + 1));
+ EXPECT_EQ(2u, value.GetList().size());
+ EXPECT_EQ(1, value.GetList()[0].GetInt());
+ EXPECT_EQ(3, value.GetList()[1].GetInt());
+
+ EXPECT_TRUE(value.EraseListIter(value.GetList().begin()));
+ EXPECT_EQ(1u, value.GetList().size());
+ EXPECT_EQ(3, value.GetList()[0].GetInt());
+
+ EXPECT_TRUE(value.EraseListIter(value.GetList().begin()));
+ EXPECT_TRUE(value.GetList().empty());
+
+ EXPECT_FALSE(value.EraseListIter(value.GetList().begin()));
+}
+
+TEST(ValuesTest, EraseListValue) {
+ ListValue value;
+ value.Append(1);
+ value.Append(2);
+ value.Append(2);
+ value.Append(3);
+
+ EXPECT_EQ(2u, value.EraseListValue(Value(2)));
+ EXPECT_EQ(2u, value.GetList().size());
+ EXPECT_EQ(1, value.GetList()[0].GetInt());
+ EXPECT_EQ(3, value.GetList()[1].GetInt());
+
+ EXPECT_EQ(1u, value.EraseListValue(Value(1)));
+ EXPECT_EQ(1u, value.GetList().size());
+ EXPECT_EQ(3, value.GetList()[0].GetInt());
+
+ EXPECT_EQ(1u, value.EraseListValue(Value(3)));
+ EXPECT_TRUE(value.GetList().empty());
+
+ EXPECT_EQ(0u, value.EraseListValue(Value(3)));
+}
+
+TEST(ValuesTest, EraseListValueIf) {
+ ListValue value;
+ value.Append(1);
+ value.Append(2);
+ value.Append(2);
+ value.Append(3);
+
+ EXPECT_EQ(3u, value.EraseListValueIf(
+ [](const auto& val) { return val >= Value(2); }));
+ EXPECT_EQ(1u, value.GetList().size());
+ EXPECT_EQ(1, value.GetList()[0].GetInt());
+
+ EXPECT_EQ(1u, value.EraseListValueIf([](const auto& val) { return true; }));
+ EXPECT_TRUE(value.GetList().empty());
+
+ EXPECT_EQ(0u, value.EraseListValueIf([](const auto& val) { return true; }));
+}
+
TEST(ValuesTest, FindKey) {
Value::DictStorage storage;
storage.emplace("foo", std::make_unique<Value>("bar"));
@@ -1183,7 +1310,7 @@ TEST(ValuesTest, Basic) {
DictionaryValue new_bookmark;
new_bookmark.SetKey("name", Value("Froogle"));
new_bookmark.SetKey("url", Value("http://froogle.com"));
- toolbar_bookmarks->GetList().push_back(std::move(new_bookmark));
+ toolbar_bookmarks->Append(std::move(new_bookmark));
Value* bookmark_list = settings.FindPath("global.toolbar.bookmarks");
ASSERT_TRUE(bookmark_list);
diff --git a/chromium/base/version.cc b/chromium/base/version.cc
index 3ba39d40caf..2ee8793c03a 100644
--- a/chromium/base/version.cc
+++ b/chromium/base/version.cc
@@ -151,7 +151,7 @@ int Version::CompareTo(const Version& other) const {
return CompareVersionComponents(components_, other.components_);
}
-const std::string Version::GetString() const {
+std::string Version::GetString() const {
DCHECK(IsValid());
std::string version_str;
size_t count = components_.size();
diff --git a/chromium/base/version.h b/chromium/base/version.h
index 272cbe82c79..9199449de69 100644
--- a/chromium/base/version.h
+++ b/chromium/base/version.h
@@ -56,7 +56,7 @@ class BASE_EXPORT Version {
int CompareToWildcardString(StringPiece wildcard_string) const;
// Return the string representation of this version.
- const std::string GetString() const;
+ std::string GetString() const;
const std::vector<uint32_t>& components() const { return components_; }
diff --git a/chromium/base/win/com_init_check_hook.h b/chromium/base/win/com_init_check_hook.h
index 304e62a42b5..f143adee869 100644
--- a/chromium/base/win/com_init_check_hook.h
+++ b/chromium/base/win/com_init_check_hook.h
@@ -21,9 +21,8 @@ namespace win {
// binaries contain a convenient 2 byte hotpatch noop. This doesn't exist in
// 64-bit binaries.
-#if DCHECK_IS_ON() && defined(ARCH_CPU_X86_FAMILY) && \
- defined(ARCH_CPU_32_BITS) && !defined(GOOGLE_CHROME_BUILD) && \
- !defined(OFFICIAL_BUILD) && \
+#if DCHECK_IS_ON() && defined(ARCH_CPU_X86_FAMILY) && \
+ defined(ARCH_CPU_32_BITS) && !defined(OFFICIAL_BUILD) && \
!defined(COM_INIT_CHECK_HOOK_DISABLED) // See crbug/737090 for details.
#define COM_INIT_CHECK_HOOK_ENABLED
#endif
diff --git a/chromium/base/win/embedded_i18n/create_string_rc.py b/chromium/base/win/embedded_i18n/create_string_rc.py
index e046b6fe39c..934131c2af2 100755
--- a/chromium/base/win/embedded_i18n/create_string_rc.py
+++ b/chromium/base/win/embedded_i18n/create_string_rc.py
@@ -575,7 +575,7 @@ def main():
parser.error('A brand was specified (' + brand + ') but no mode '
'specific strings were given.')
valid_brands = [b for b in
- mode_specific_strings.itervalues().next().iterkeys()]
+ next(iter(mode_specific_strings.values())).keys()]
if not brand in valid_brands:
parser.error('A brand was specified (' + brand + ') but it is not '
'a valid brand [' + ', '.join(valid_brands) + '].')
diff --git a/chromium/base/win/embedded_i18n/language_selector.cc b/chromium/base/win/embedded_i18n/language_selector.cc
index 1a3a8a79602..762b3713e04 100644
--- a/chromium/base/win/embedded_i18n/language_selector.cc
+++ b/chromium/base/win/embedded_i18n/language_selector.cc
@@ -44,13 +44,13 @@ struct AvailableLanguageAliases {
#if DCHECK_IS_ON()
// Returns true if the items in the given range are sorted and lower cased.
-bool IsArraySortedAndLowerCased(
- base::span<const LangToOffset> languages_to_offset) {
+bool IsArraySortedAndLowerCased(span<const LangToOffset> languages_to_offset) {
return std::is_sorted(languages_to_offset.begin(),
languages_to_offset.end()) &&
std::all_of(languages_to_offset.begin(), languages_to_offset.end(),
[](const auto& lang) {
- return base::ToLowerASCII(lang.first) == lang.first;
+ auto language = AsStringPiece16(lang.first);
+ return ToLowerASCII(language) == language;
});
}
#endif // DCHECK_IS_ON()
@@ -58,29 +58,29 @@ bool IsArraySortedAndLowerCased(
// Determines the availability of all languages that may be used as aliases in
// GetAliasedLanguageOffset or GetCompatibleNeutralLanguageOffset
AvailableLanguageAliases DetermineAvailableAliases(
- base::span<const LangToOffset> languages_to_offset) {
+ span<const LangToOffset> languages_to_offset) {
AvailableLanguageAliases available_aliases = {};
for (const LangToOffset& lang_to_offset : languages_to_offset) {
- if (lang_to_offset.first == STRING16_LITERAL("en-gb"))
+ if (lang_to_offset.first == L"en-gb")
available_aliases.en_gb_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("en-us"))
+ else if (lang_to_offset.first == L"en-us")
available_aliases.en_us_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("es"))
+ else if (lang_to_offset.first == L"es")
available_aliases.es_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("es-419"))
+ else if (lang_to_offset.first == L"es-419")
available_aliases.es_419_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("fil"))
+ else if (lang_to_offset.first == L"fil")
available_aliases.fil_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("iw"))
+ else if (lang_to_offset.first == L"iw")
available_aliases.iw_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("no"))
+ else if (lang_to_offset.first == L"no")
available_aliases.no_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("pt-br"))
+ else if (lang_to_offset.first == L"pt-br")
available_aliases.pt_br_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("zh-cn"))
+ else if (lang_to_offset.first == L"zh-cn")
available_aliases.zh_cn_language_offset = &lang_to_offset;
- else if (lang_to_offset.first == STRING16_LITERAL("zh-tw"))
+ else if (lang_to_offset.first == L"zh-tw")
available_aliases.zh_tw_language_offset = &lang_to_offset;
}
@@ -93,8 +93,8 @@ AvailableLanguageAliases DetermineAvailableAliases(
// that matches the |language| exactly. |offset| will store the offset of the
// language that matches if any. |languages_to_offset| must be sorted by
// language and all languages must lower case.
-bool GetExactLanguageOffset(base::span<const LangToOffset> languages_to_offset,
- const base::string16& language,
+bool GetExactLanguageOffset(span<const LangToOffset> languages_to_offset,
+ const std::wstring& language,
const LangToOffset** matched_language_to_offset) {
DCHECK(matched_language_to_offset);
@@ -102,7 +102,7 @@ bool GetExactLanguageOffset(base::span<const LangToOffset> languages_to_offset,
// to a given language |name|.
auto search_result = std::lower_bound(
languages_to_offset.begin(), languages_to_offset.end(), language,
- [](const LangToOffset& left, const base::string16& to_find) {
+ [](const LangToOffset& left, const std::wstring& to_find) {
return left.first < to_find;
});
if (languages_to_offset.end() != search_result &&
@@ -115,65 +115,58 @@ bool GetExactLanguageOffset(base::span<const LangToOffset> languages_to_offset,
// Returns true if the current language can be aliased to another language.
bool GetAliasedLanguageOffset(const AvailableLanguageAliases& available_aliases,
- const base::string16& language,
+ const std::wstring& language,
const LangToOffset** matched_language_to_offset) {
DCHECK(matched_language_to_offset);
// Alias some English variants to British English (all others wildcard to
// US).
if (available_aliases.en_gb_language_offset &&
- (language == STRING16_LITERAL("en-au") ||
- language == STRING16_LITERAL("en-ca") ||
- language == STRING16_LITERAL("en-nz") ||
- language == STRING16_LITERAL("en-za"))) {
+ (language == L"en-au" || language == L"en-ca" || language == L"en-nz" ||
+ language == L"en-za")) {
*matched_language_to_offset = available_aliases.en_gb_language_offset;
return true;
}
// Alias es-es to es (all others wildcard to es-419).
- if (available_aliases.es_language_offset &&
- language == STRING16_LITERAL("es-es")) {
+ if (available_aliases.es_language_offset && language == L"es-es") {
*matched_language_to_offset = available_aliases.es_language_offset;
return true;
}
// Google web properties use iw for he. Handle both just to be safe.
- if (available_aliases.iw_language_offset &&
- language == STRING16_LITERAL("he")) {
+ if (available_aliases.iw_language_offset && language == L"he") {
*matched_language_to_offset = available_aliases.iw_language_offset;
return true;
}
// Google web properties use no for nb. Handle both just to be safe.
- if (available_aliases.no_language_offset &&
- language == STRING16_LITERAL("nb")) {
+ if (available_aliases.no_language_offset && language == L"nb") {
*matched_language_to_offset = available_aliases.no_language_offset;
return true;
}
// Some Google web properties use tl for fil. Handle both just to be safe.
// They're not completely identical, but alias it here.
- if (available_aliases.fil_language_offset &&
- language == STRING16_LITERAL("tl")) {
+ if (available_aliases.fil_language_offset && language == L"tl") {
*matched_language_to_offset = available_aliases.fil_language_offset;
return true;
}
if (available_aliases.zh_cn_language_offset &&
// Pre-Vista alias for Chinese w/ script subtag.
- (language == STRING16_LITERAL("zh-chs") ||
+ (language == L"zh-chs" ||
// Vista+ alias for Chinese w/ script subtag.
- language == STRING16_LITERAL("zh-hans") ||
+ language == L"zh-hans" ||
// Although the wildcard entry for zh would result in this, alias zh-sg
// so that it will win if it precedes another valid tag in a list of
// candidates.
- language == STRING16_LITERAL("zh-sg"))) {
+ language == L"zh-sg")) {
*matched_language_to_offset = available_aliases.zh_cn_language_offset;
return true;
}
if (available_aliases.zh_tw_language_offset &&
// Pre-Vista alias for Chinese w/ script subtag.
- (language == STRING16_LITERAL("zh-cht") ||
+ (language == L"zh-cht" ||
// Vista+ alias for Chinese w/ script subtag.
- language == STRING16_LITERAL("zh-hant") ||
+ language == L"zh-hant" ||
// Alias Hong Kong and Macau to Taiwan.
- language == STRING16_LITERAL("zh-hk") ||
- language == STRING16_LITERAL("zh-mo"))) {
+ language == L"zh-hk" || language == L"zh-mo")) {
*matched_language_to_offset = available_aliases.zh_tw_language_offset;
return true;
}
@@ -185,30 +178,26 @@ bool GetAliasedLanguageOffset(const AvailableLanguageAliases& available_aliases,
// language.
bool GetCompatibleNeutralLanguageOffset(
const AvailableLanguageAliases& available_aliases,
- const base::string16& neutral_language,
+ const std::wstring& neutral_language,
const LangToOffset** matched_language_to_offset) {
DCHECK(matched_language_to_offset);
- if (available_aliases.en_us_language_offset &&
- neutral_language == STRING16_LITERAL("en")) {
+ if (available_aliases.en_us_language_offset && neutral_language == L"en") {
// Use the U.S. region for anything English.
*matched_language_to_offset = available_aliases.en_us_language_offset;
return true;
}
- if (available_aliases.es_419_language_offset &&
- neutral_language == STRING16_LITERAL("es")) {
+ if (available_aliases.es_419_language_offset && neutral_language == L"es") {
// Use the Latin American region for anything Spanish.
*matched_language_to_offset = available_aliases.es_419_language_offset;
return true;
}
- if (available_aliases.pt_br_language_offset &&
- neutral_language == STRING16_LITERAL("pt")) {
+ if (available_aliases.pt_br_language_offset && neutral_language == L"pt") {
// Use the Brazil region for anything Portugese.
*matched_language_to_offset = available_aliases.pt_br_language_offset;
return true;
}
- if (available_aliases.zh_cn_language_offset &&
- neutral_language == STRING16_LITERAL("zh")) {
+ if (available_aliases.zh_cn_language_offset && neutral_language == L"zh") {
// Use the P.R.C. region for anything Chinese.
*matched_language_to_offset = available_aliases.zh_cn_language_offset;
return true;
@@ -223,11 +212,11 @@ bool GetCompatibleNeutralLanguageOffset(
// candidate and |matched_offset| is assigned the language offset of the
// selected translation.
// static
-bool SelectIf(const std::vector<base::string16>& candidates,
- base::span<const LangToOffset> languages_to_offset,
+bool SelectIf(const std::vector<std::wstring>& candidates,
+ span<const LangToOffset> languages_to_offset,
const AvailableLanguageAliases& available_aliases,
const LangToOffset** matched_language_to_offset,
- base::string16* matched_name) {
+ std::wstring* matched_name) {
DCHECK(matched_language_to_offset);
DCHECK(matched_name);
@@ -236,8 +225,9 @@ bool SelectIf(const std::vector<base::string16>& candidates,
// An earlier candidate entry matching on an exact match or alias match takes
// precedence over a later candidate entry matching on an exact match.
- for (const base::string16& scan : candidates) {
- base::string16 lower_case_candidate = base::ToLowerASCII(scan);
+ for (const std::wstring& scan : candidates) {
+ std::wstring lower_case_candidate =
+ AsWString(ToLowerASCII(AsStringPiece16(scan)));
if (GetExactLanguageOffset(languages_to_offset, lower_case_candidate,
matched_language_to_offset) ||
GetAliasedLanguageOffset(available_aliases, lower_case_candidate,
@@ -249,12 +239,13 @@ bool SelectIf(const std::vector<base::string16>& candidates,
// If no candidate matches exactly or by alias, try to match by locale neutral
// language.
- for (const base::string16& scan : candidates) {
- base::string16 lower_case_candidate = base::ToLowerASCII(scan);
+ for (const std::wstring& scan : candidates) {
+ std::wstring lower_case_candidate =
+ AsWString(ToLowerASCII(AsStringPiece16(scan)));
// Extract the locale neutral language from the language to search and try
// to find an exact match for that language in the provided table.
- base::string16 neutral_language =
+ std::wstring neutral_language =
lower_case_candidate.substr(0, lower_case_candidate.find(L'-'));
if (GetCompatibleNeutralLanguageOffset(available_aliases, neutral_language,
@@ -268,11 +259,11 @@ bool SelectIf(const std::vector<base::string16>& candidates,
}
void SelectLanguageMatchingCandidate(
- const std::vector<base::string16>& candidates,
- base::span<const LangToOffset> languages_to_offset,
+ const std::vector<std::wstring>& candidates,
+ span<const LangToOffset> languages_to_offset,
int* selected_offset,
- base::string16* matched_candidate,
- base::string16* selected_language) {
+ std::wstring* matched_candidate,
+ std::wstring* selected_language) {
DCHECK(selected_offset);
DCHECK(matched_candidate);
DCHECK(selected_language);
@@ -303,18 +294,18 @@ void SelectLanguageMatchingCandidate(
&matched_language_to_offset, matched_candidate)) {
matched_language_to_offset = available_aliases.en_us_language_offset;
*matched_candidate =
- base::string16(available_aliases.en_us_language_offset->first);
+ std::wstring(available_aliases.en_us_language_offset->first);
}
DCHECK(matched_language_to_offset);
// Get the real language being used for the matched candidate.
- *selected_language = base::string16(matched_language_to_offset->first);
+ *selected_language = std::wstring(matched_language_to_offset->first);
*selected_offset = matched_language_to_offset->second;
}
-std::vector<base::string16> GetCandidatesFromSystem(
- base::StringPiece16 preferred_language) {
- std::vector<base::string16> candidates;
+std::vector<std::wstring> GetCandidatesFromSystem(
+ WStringPiece preferred_language) {
+ std::vector<std::wstring> candidates;
// Get the intitial candidate list for this particular implementation (if
// applicable).
@@ -323,21 +314,19 @@ std::vector<base::string16> GetCandidatesFromSystem(
// Now try the UI languages. Use the thread preferred ones since that will
// kindly return us a list of all kinds of fallbacks.
- base::win::i18n::GetThreadPreferredUILanguageList(&candidates);
+ win::i18n::GetThreadPreferredUILanguageList(&candidates);
return candidates;
}
} // namespace
-LanguageSelector::LanguageSelector(
- base::StringPiece16 preferred_language,
- base::span<const LangToOffset> languages_to_offset)
+LanguageSelector::LanguageSelector(WStringPiece preferred_language,
+ span<const LangToOffset> languages_to_offset)
: LanguageSelector(GetCandidatesFromSystem(preferred_language),
languages_to_offset) {}
-LanguageSelector::LanguageSelector(
- const std::vector<base::string16>& candidates,
- base::span<const LangToOffset> languages_to_offset)
+LanguageSelector::LanguageSelector(const std::vector<std::wstring>& candidates,
+ span<const LangToOffset> languages_to_offset)
: selected_offset_(languages_to_offset.size()) {
SelectLanguageMatchingCandidate(candidates, languages_to_offset,
&selected_offset_, &matched_candidate_,
diff --git a/chromium/base/win/embedded_i18n/language_selector.h b/chromium/base/win/embedded_i18n/language_selector.h
index 2772aee8f7a..84d1e9cd506 100644
--- a/chromium/base/win/embedded_i18n/language_selector.h
+++ b/chromium/base/win/embedded_i18n/language_selector.h
@@ -8,13 +8,13 @@
#ifndef BASE_WIN_EMBEDDED_I18N_LANGUAGE_SELECTOR_H_
#define BASE_WIN_EMBEDDED_I18N_LANGUAGE_SELECTOR_H_
+#include <string>
#include <utility>
#include <vector>
#include "base/base_export.h"
#include "base/containers/span.h"
#include "base/macros.h"
-#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
namespace base {
@@ -26,7 +26,7 @@ namespace i18n {
// override selection should a corresponding translation be available.
class BASE_EXPORT LanguageSelector {
public:
- using LangToOffset = std::pair<base::StringPiece16, int>;
+ using LangToOffset = std::pair<WStringPiece, int>;
// Constructor to be used for users of this class that will provide the actual
// language offsets that will be used.
@@ -36,8 +36,8 @@ class BASE_EXPORT LanguageSelector {
// |languages_to_offset_begin| and |languages_to_offset_end| point to a sorted
// array of language identifiers (and their offsets) for which translations
// are available.
- LanguageSelector(base::StringPiece16 preferred_language,
- base::span<const LangToOffset> languages_to_offset);
+ LanguageSelector(WStringPiece preferred_language,
+ span<const LangToOffset> languages_to_offset);
// Constructor for testing purposes.
// |candidates| is a list of all candiate languages that can be used to
@@ -45,8 +45,8 @@ class BASE_EXPORT LanguageSelector {
// |languages_to_offset_begin| and |languages_to_offset_end| point to a sorted
// array of language identifiers (and their offsets) for which translations
// are available.
- LanguageSelector(const std::vector<base::string16>& candidates,
- base::span<const LangToOffset> languages_to_offset);
+ LanguageSelector(const std::vector<std::wstring>& candidates,
+ span<const LangToOffset> languages_to_offset);
~LanguageSelector();
@@ -54,16 +54,16 @@ class BASE_EXPORT LanguageSelector {
int offset() const { return selected_offset_; }
// The full name of the candidate language for which a match was found.
- const base::string16& matched_candidate() const { return matched_candidate_; }
+ const std::wstring& matched_candidate() const { return matched_candidate_; }
// The name of the selected translation.
- const base::string16& selected_translation() const {
+ const std::wstring& selected_translation() const {
return selected_language_;
}
private:
- base::string16 matched_candidate_;
- base::string16 selected_language_;
+ std::wstring matched_candidate_;
+ std::wstring selected_language_;
int selected_offset_;
DISALLOW_COPY_AND_ASSIGN(LanguageSelector);
diff --git a/chromium/base/win/embedded_i18n/language_selector_unittest.cc b/chromium/base/win/embedded_i18n/language_selector_unittest.cc
index 64efc1da713..6903d0cb38c 100644
--- a/chromium/base/win/embedded_i18n/language_selector_unittest.cc
+++ b/chromium/base/win/embedded_i18n/language_selector_unittest.cc
@@ -17,49 +17,26 @@ namespace win {
namespace i18n {
namespace {
-constexpr const base::char16* kExactMatchCandidates[] = {
- STRING16_LITERAL("am"), STRING16_LITERAL("ar"),
- STRING16_LITERAL("bg"), STRING16_LITERAL("bn"),
- STRING16_LITERAL("ca"), STRING16_LITERAL("cs"),
- STRING16_LITERAL("da"), STRING16_LITERAL("de"),
- STRING16_LITERAL("el"), STRING16_LITERAL("en-gb"),
- STRING16_LITERAL("en-us"), STRING16_LITERAL("es"),
- STRING16_LITERAL("es-419"), STRING16_LITERAL("et"),
- STRING16_LITERAL("fa"), STRING16_LITERAL("fi"),
- STRING16_LITERAL("fil"), STRING16_LITERAL("fr"),
- STRING16_LITERAL("gu"), STRING16_LITERAL("hi"),
- STRING16_LITERAL("hr"), STRING16_LITERAL("hu"),
- STRING16_LITERAL("id"), STRING16_LITERAL("it"),
- STRING16_LITERAL("iw"), STRING16_LITERAL("ja"),
- STRING16_LITERAL("kn"), STRING16_LITERAL("ko"),
- STRING16_LITERAL("lt"), STRING16_LITERAL("lv"),
- STRING16_LITERAL("ml"), STRING16_LITERAL("mr"),
- STRING16_LITERAL("nl"), STRING16_LITERAL("no"),
- STRING16_LITERAL("pl"), STRING16_LITERAL("pt-br"),
- STRING16_LITERAL("pt-pt"), STRING16_LITERAL("ro"),
- STRING16_LITERAL("ru"), STRING16_LITERAL("sk"),
- STRING16_LITERAL("sl"), STRING16_LITERAL("sr"),
- STRING16_LITERAL("sv"), STRING16_LITERAL("sw"),
- STRING16_LITERAL("ta"), STRING16_LITERAL("te"),
- STRING16_LITERAL("th"), STRING16_LITERAL("tr"),
- STRING16_LITERAL("uk"), STRING16_LITERAL("vi"),
- STRING16_LITERAL("zh-cn"), STRING16_LITERAL("zh-tw")};
-
-constexpr const base::char16* kAliasMatchCandidates[] = {
- STRING16_LITERAL("he"), STRING16_LITERAL("nb"),
- STRING16_LITERAL("tl"), STRING16_LITERAL("zh-chs"),
- STRING16_LITERAL("zh-cht"), STRING16_LITERAL("zh-hans"),
- STRING16_LITERAL("zh-hant"), STRING16_LITERAL("zh-hk"),
- STRING16_LITERAL("zh-mo")};
-
-constexpr const base::char16* kWildcardMatchCandidates[] = {
- STRING16_LITERAL("en-AU"), STRING16_LITERAL("es-CO"),
- STRING16_LITERAL("pt-AB"), STRING16_LITERAL("zh-SG")};
+constexpr const wchar_t* kExactMatchCandidates[] = {
+ L"am", L"ar", L"bg", L"bn", L"ca", L"cs", L"da", L"de",
+ L"el", L"en-gb", L"en-us", L"es", L"es-419", L"et", L"fa", L"fi",
+ L"fil", L"fr", L"gu", L"hi", L"hr", L"hu", L"id", L"it",
+ L"iw", L"ja", L"kn", L"ko", L"lt", L"lv", L"ml", L"mr",
+ L"nl", L"no", L"pl", L"pt-br", L"pt-pt", L"ro", L"ru", L"sk",
+ L"sl", L"sr", L"sv", L"sw", L"ta", L"te", L"th", L"tr",
+ L"uk", L"vi", L"zh-cn", L"zh-tw"};
+
+constexpr const wchar_t* kAliasMatchCandidates[] = {
+ L"he", L"nb", L"tl", L"zh-chs", L"zh-cht",
+ L"zh-hans", L"zh-hant", L"zh-hk", L"zh-mo"};
+
+constexpr const wchar_t* kWildcardMatchCandidates[] = {L"en-AU", L"es-CO",
+ L"pt-AB", L"zh-SG"};
std::vector<LanguageSelector::LangToOffset> MakeLanguageOffsetPairs() {
std::vector<LanguageSelector::LangToOffset> language_offset_pairs;
int i = 0;
- for (const base::char16* lang : kExactMatchCandidates) {
+ for (const wchar_t* lang : kExactMatchCandidates) {
language_offset_pairs.push_back({lang, i++});
}
@@ -68,13 +45,12 @@ std::vector<LanguageSelector::LangToOffset> MakeLanguageOffsetPairs() {
class TestLanguageSelector : public LanguageSelector {
public:
- TestLanguageSelector()
- : TestLanguageSelector(std::vector<base::string16>()) {}
- explicit TestLanguageSelector(const std::vector<base::string16>& candidates)
+ TestLanguageSelector() : TestLanguageSelector(std::vector<std::wstring>()) {}
+ explicit TestLanguageSelector(const std::vector<std::wstring>& candidates)
: TestLanguageSelector(candidates, MakeLanguageOffsetPairs()) {}
TestLanguageSelector(
- const std::vector<base::string16>& candidates,
- base::span<const LanguageSelector::LangToOffset> languages_to_offset)
+ const std::vector<std::wstring>& candidates,
+ span<const LanguageSelector::LangToOffset> languages_to_offset)
: LanguageSelector(candidates, languages_to_offset) {}
};
@@ -89,32 +65,28 @@ TEST(LanguageSelectorTest, DefaultSelection) {
// Test some hypothetical candidate sets.
TEST(LanguageSelectorTest, AssortedSelections) {
{
- std::vector<base::string16> candidates = {STRING16_LITERAL("fr-BE"),
- STRING16_LITERAL("fr"),
- STRING16_LITERAL("en")};
+ std::vector<std::wstring> candidates = {L"fr-BE", L"fr", L"en"};
TestLanguageSelector instance(candidates);
// Expect the exact match to win.
- EXPECT_EQ(STRING16_LITERAL("fr"), instance.matched_candidate());
+ EXPECT_EQ(L"fr", instance.matched_candidate());
}
{
- std::vector<base::string16> candidates = {STRING16_LITERAL("xx-YY"),
- STRING16_LITERAL("cc-Ssss-RR")};
+ std::vector<std::wstring> candidates = {L"xx-YY", L"cc-Ssss-RR"};
TestLanguageSelector instance(candidates);
// Expect the fallback to win.
- EXPECT_EQ(STRING16_LITERAL("en-us"), instance.matched_candidate());
+ EXPECT_EQ(L"en-us", instance.matched_candidate());
}
{
- std::vector<base::string16> candidates = {STRING16_LITERAL("zh-SG"),
- STRING16_LITERAL("en-GB")};
+ std::vector<std::wstring> candidates = {L"zh-SG", L"en-GB"};
TestLanguageSelector instance(candidates);
// Expect the alias match to win.
- EXPECT_EQ(STRING16_LITERAL("zh-SG"), instance.matched_candidate());
+ EXPECT_EQ(L"zh-SG", instance.matched_candidate());
}
}
// A fixture for testing sets of single-candidate selections.
class LanguageSelectorMatchCandidateTest
- : public ::testing::TestWithParam<const base::char16*> {};
+ : public ::testing::TestWithParam<const wchar_t*> {};
TEST_P(LanguageSelectorMatchCandidateTest, TestMatchCandidate) {
TestLanguageSelector instance({GetParam()});
@@ -141,7 +113,7 @@ INSTANTIATE_TEST_SUITE_P(TestWildcardMatches,
// candidate that should be aliased to the expectation.
class LanguageSelectorAliasTest
: public ::testing::TestWithParam<
- std::tuple<const base::char16*, const base::char16*>> {};
+ std::tuple<const wchar_t*, const wchar_t*>> {};
// Test that the candidate language maps to the aliased translation.
TEST_P(LanguageSelectorAliasTest, AliasesMatch) {
@@ -149,49 +121,42 @@ TEST_P(LanguageSelectorAliasTest, AliasesMatch) {
EXPECT_EQ(std::get<0>(GetParam()), instance.selected_translation());
}
-INSTANTIATE_TEST_SUITE_P(
- EnGbAliases,
- LanguageSelectorAliasTest,
- ::testing::Combine(::testing::Values(STRING16_LITERAL("en-gb")),
- ::testing::Values(STRING16_LITERAL("en-au"),
- STRING16_LITERAL("en-ca"),
- STRING16_LITERAL("en-nz"),
- STRING16_LITERAL("en-za"))));
-
-INSTANTIATE_TEST_SUITE_P(
- IwAliases,
- LanguageSelectorAliasTest,
- ::testing::Combine(::testing::Values(STRING16_LITERAL("iw")),
- ::testing::Values(STRING16_LITERAL("he"))));
-
-INSTANTIATE_TEST_SUITE_P(
- NoAliases,
- LanguageSelectorAliasTest,
- ::testing::Combine(::testing::Values(STRING16_LITERAL("no")),
- ::testing::Values(STRING16_LITERAL("nb"))));
-
-INSTANTIATE_TEST_SUITE_P(
- FilAliases,
- LanguageSelectorAliasTest,
- ::testing::Combine(::testing::Values(STRING16_LITERAL("fil")),
- ::testing::Values(STRING16_LITERAL("tl"))));
+INSTANTIATE_TEST_SUITE_P(EnGbAliases,
+ LanguageSelectorAliasTest,
+ ::testing::Combine(::testing::Values(L"en-gb"),
+ ::testing::Values(L"en-au",
+ L"en-ca",
+ L"en-nz",
+ L"en-za")));
+
+INSTANTIATE_TEST_SUITE_P(IwAliases,
+ LanguageSelectorAliasTest,
+ ::testing::Combine(::testing::Values(L"iw"),
+ ::testing::Values(L"he")));
+
+INSTANTIATE_TEST_SUITE_P(NoAliases,
+ LanguageSelectorAliasTest,
+ ::testing::Combine(::testing::Values(L"no"),
+ ::testing::Values(L"nb")));
+
+INSTANTIATE_TEST_SUITE_P(FilAliases,
+ LanguageSelectorAliasTest,
+ ::testing::Combine(::testing::Values(L"fil"),
+ ::testing::Values(L"tl")));
INSTANTIATE_TEST_SUITE_P(
ZhCnAliases,
LanguageSelectorAliasTest,
- ::testing::Combine(::testing::Values(STRING16_LITERAL("zh-cn")),
- ::testing::Values(STRING16_LITERAL("zh-chs"),
- STRING16_LITERAL("zh-hans"),
- STRING16_LITERAL("zh-sg"))));
+ ::testing::Combine(::testing::Values(L"zh-cn"),
+ ::testing::Values(L"zh-chs", L"zh-hans", L"zh-sg")));
-INSTANTIATE_TEST_SUITE_P(
- ZhTwAliases,
- LanguageSelectorAliasTest,
- ::testing::Combine(::testing::Values(STRING16_LITERAL("zh-tw")),
- ::testing::Values(STRING16_LITERAL("zh-cht"),
- STRING16_LITERAL("zh-hant"),
- STRING16_LITERAL("zh-hk"),
- STRING16_LITERAL("zh-mo"))));
+INSTANTIATE_TEST_SUITE_P(ZhTwAliases,
+ LanguageSelectorAliasTest,
+ ::testing::Combine(::testing::Values(L"zh-tw"),
+ ::testing::Values(L"zh-cht",
+ L"zh-hant",
+ L"zh-hk",
+ L"zh-mo")));
// Test that we can get a match of the default language.
TEST(LanguageSelectorTest, DefaultLanguageName) {
@@ -202,34 +167,32 @@ TEST(LanguageSelectorTest, DefaultLanguageName) {
// All languages given to the selector must be lower cased (since generally
// the language names are generated by a python script).
TEST(LanguageSelectorTest, InvalidLanguageCasing) {
- constexpr LanguageSelector::LangToOffset kLangToOffset[] = {
- {STRING16_LITERAL("en-US"), 0}};
+ constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-US", 0}};
EXPECT_DCHECK_DEATH(LanguageSelector instance(
- std::vector<base::string16>({STRING16_LITERAL("en-us")}), kLangToOffset));
+ std::vector<std::wstring>({L"en-us"}), kLangToOffset));
}
// Language name and offset pairs must be ordered when generated by the
// python script.
TEST(LanguageSelectorTest, InvalidLanguageNameOrder) {
- constexpr LanguageSelector::LangToOffset kLangToOffset[] = {
- {STRING16_LITERAL("en-us"), 0}, {STRING16_LITERAL("en-gb"), 1}};
+ constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-us", 0},
+ {L"en-gb", 1}};
EXPECT_DCHECK_DEATH(LanguageSelector instance(
- std::vector<base::string16>({STRING16_LITERAL("en-us")}), kLangToOffset));
+ std::vector<std::wstring>({L"en-us"}), kLangToOffset));
}
// There needs to be a fallback language available in the generated
// languages if ever the selector is given a language that does not exist.
TEST(LanguageSelectorTest, NoFallbackLanguageAvailable) {
- constexpr LanguageSelector::LangToOffset kLangToOffset[] = {
- {STRING16_LITERAL("en-gb"), 0}};
+ constexpr LanguageSelector::LangToOffset kLangToOffset[] = {{L"en-gb", 0}};
EXPECT_DCHECK_DEATH(LanguageSelector instance(
- std::vector<base::string16>({STRING16_LITERAL("aa-bb")}), kLangToOffset));
+ std::vector<std::wstring>({L"aa-bb"}), kLangToOffset));
}
// No languages available.
TEST(LanguageSelectorTest, NoLanguagesAvailable) {
- EXPECT_DCHECK_DEATH(LanguageSelector instance(
- std::vector<base::string16>({STRING16_LITERAL("en-us")}), {}));
+ EXPECT_DCHECK_DEATH(
+ LanguageSelector instance(std::vector<std::wstring>({L"en-us"}), {}));
}
} // namespace i18n
diff --git a/chromium/base/win/enum_variant.cc b/chromium/base/win/enum_variant.cc
index 38861bfe70f..9ae35e2fdd3 100644
--- a/chromium/base/win/enum_variant.cc
+++ b/chromium/base/win/enum_variant.cc
@@ -4,6 +4,8 @@
#include "base/win/enum_variant.h"
+#include <wrl/client.h>
+
#include <algorithm>
#include "base/logging.h"
@@ -26,27 +28,9 @@ VARIANT* EnumVariant::ItemAt(ULONG index) {
return items_[index].AsInput();
}
-ULONG STDMETHODCALLTYPE EnumVariant::AddRef() {
- return IUnknownImpl::AddRef();
-}
-
-ULONG STDMETHODCALLTYPE EnumVariant::Release() {
- return IUnknownImpl::Release();
-}
-
-STDMETHODIMP EnumVariant::QueryInterface(REFIID riid, void** ppv) {
- if (riid == IID_IEnumVARIANT) {
- *ppv = static_cast<IEnumVARIANT*>(this);
- AddRef();
- return S_OK;
- }
-
- return IUnknownImpl::QueryInterface(riid, ppv);
-}
-
-STDMETHODIMP EnumVariant::Next(ULONG requested_count,
- VARIANT* out_elements,
- ULONG* out_elements_received) {
+HRESULT EnumVariant::Next(ULONG requested_count,
+ VARIANT* out_elements,
+ ULONG* out_elements_received) {
if (!out_elements)
return E_INVALIDARG;
@@ -65,7 +49,7 @@ STDMETHODIMP EnumVariant::Next(ULONG requested_count,
return (count == requested_count ? S_OK : S_FALSE);
}
-STDMETHODIMP EnumVariant::Skip(ULONG skip_count) {
+HRESULT EnumVariant::Skip(ULONG skip_count) {
ULONG count = skip_count;
if (current_index_ + count > ULONG{items_.size()})
count = ULONG{items_.size()} - current_index_;
@@ -74,24 +58,23 @@ STDMETHODIMP EnumVariant::Skip(ULONG skip_count) {
return (count == skip_count ? S_OK : S_FALSE);
}
-STDMETHODIMP EnumVariant::Reset() {
+HRESULT EnumVariant::Reset() {
current_index_ = 0;
return S_OK;
}
-STDMETHODIMP EnumVariant::Clone(IEnumVARIANT** out_cloned_object) {
+HRESULT EnumVariant::Clone(IEnumVARIANT** out_cloned_object) {
if (!out_cloned_object)
return E_INVALIDARG;
size_t count = items_.size();
- EnumVariant* other = new EnumVariant(ULONG{count});
+ Microsoft::WRL::ComPtr<EnumVariant> other =
+ Microsoft::WRL::Make<EnumVariant>(ULONG{count});
for (size_t i = 0; i < count; ++i)
other->items_[i] = static_cast<const VARIANT&>(items_[i]);
other->Skip(current_index_);
- other->AddRef();
- *out_cloned_object = other;
- return S_OK;
+ return other.CopyTo(IID_PPV_ARGS(out_cloned_object));
}
} // namespace win
diff --git a/chromium/base/win/enum_variant.h b/chromium/base/win/enum_variant.h
index 47ffd070de1..1adf4aac224 100644
--- a/chromium/base/win/enum_variant.h
+++ b/chromium/base/win/enum_variant.h
@@ -5,12 +5,11 @@
#ifndef BASE_WIN_ENUM_VARIANT_H_
#define BASE_WIN_ENUM_VARIANT_H_
-#include <unknwn.h>
+#include <wrl/implements.h>
#include <memory>
#include <vector>
-#include "base/win/iunknown_impl.h"
#include "base/win/scoped_variant.h"
namespace base {
@@ -18,29 +17,25 @@ namespace win {
// A simple implementation of IEnumVARIANT.
class BASE_EXPORT EnumVariant
- : public IEnumVARIANT,
- public IUnknownImpl {
+ : public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+ IEnumVARIANT> {
public:
// The constructor allocates a vector of empty ScopedVariants of size |count|.
// Use ItemAt to set the value of each item in the array.
explicit EnumVariant(ULONG count);
+ // IEnumVARIANT:
+ IFACEMETHODIMP Next(ULONG requested_count,
+ VARIANT* out_elements,
+ ULONG* out_elements_received) override;
+ IFACEMETHODIMP Skip(ULONG skip_count) override;
+ IFACEMETHODIMP Reset() override;
+ IFACEMETHODIMP Clone(IEnumVARIANT** out_cloned_object) override;
+
// Returns a mutable pointer to the item at position |index|.
VARIANT* ItemAt(ULONG index);
- // IUnknown.
- ULONG STDMETHODCALLTYPE AddRef() override;
- ULONG STDMETHODCALLTYPE Release() override;
- STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
-
- // IEnumVARIANT.
- STDMETHODIMP Next(ULONG requested_count,
- VARIANT* out_elements,
- ULONG* out_elements_received) override;
- STDMETHODIMP Skip(ULONG skip_count) override;
- STDMETHODIMP Reset() override;
- STDMETHODIMP Clone(IEnumVARIANT** out_cloned_object) override;
-
private:
~EnumVariant() override;
diff --git a/chromium/base/win/enum_variant_unittest.cc b/chromium/base/win/enum_variant_unittest.cc
index 77b2fdef73c..3165b5e2659 100644
--- a/chromium/base/win/enum_variant_unittest.cc
+++ b/chromium/base/win/enum_variant_unittest.cc
@@ -4,7 +4,11 @@
#include "base/win/enum_variant.h"
+#include <wrl/client.h>
+#include <wrl/implements.h>
+
#include "base/win/scoped_com_initializer.h"
+#include "base/win/scoped_variant.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -13,52 +17,34 @@ namespace win {
TEST(EnumVariantTest, EmptyEnumVariant) {
ScopedCOMInitializer com_initializer;
- EnumVariant* ev = new EnumVariant(0);
- ev->AddRef();
-
- IUnknown* iunknown;
- EXPECT_TRUE(SUCCEEDED(
- ev->QueryInterface(IID_IUnknown, reinterpret_cast<void**>(&iunknown))));
- iunknown->Release();
+ Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(0);
+ Microsoft::WRL::ComPtr<IEnumVARIANT> ienumvariant;
+ ASSERT_TRUE(SUCCEEDED(ev->QueryInterface(IID_PPV_ARGS(&ienumvariant))));
- IEnumVARIANT* ienumvariant;
- EXPECT_TRUE(SUCCEEDED(
- ev->QueryInterface(IID_IEnumVARIANT,
- reinterpret_cast<void**>(&ienumvariant))));
- EXPECT_EQ(ev, ienumvariant);
- ienumvariant->Release();
-
- VARIANT out_element;
- ::VariantInit(&out_element);
- ULONG out_received = 0;
- EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
- EXPECT_EQ(0u, out_received);
- ::VariantClear(&out_element);
+ {
+ base::win::ScopedVariant out_element;
+ ULONG out_received = 0;
+ EXPECT_EQ(S_FALSE, ev->Next(1, out_element.Receive(), &out_received));
+ EXPECT_EQ(0u, out_received);
+ }
EXPECT_EQ(S_FALSE, ev->Skip(1));
EXPECT_EQ(S_OK, ev->Reset());
- IEnumVARIANT* ev2 = NULL;
+ Microsoft::WRL::ComPtr<IEnumVARIANT> ev2;
EXPECT_EQ(S_OK, ev->Clone(&ev2));
- EXPECT_NE(static_cast<IEnumVARIANT*>(NULL), ev2);
+ EXPECT_NE(nullptr, ev2);
EXPECT_NE(ev, ev2);
EXPECT_EQ(S_FALSE, ev2->Skip(1));
EXPECT_EQ(S_OK, ev2->Reset());
-
- ULONG ev2_finalrefcount = ev2->Release();
- EXPECT_EQ(0u, ev2_finalrefcount);
-
- ULONG ev_finalrefcount = ev->Release();
- EXPECT_EQ(0u, ev_finalrefcount);
}
TEST(EnumVariantTest, SimpleEnumVariant) {
ScopedCOMInitializer com_initializer;
- EnumVariant* ev = new EnumVariant(3);
- ev->AddRef();
+ Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(3);
ev->ItemAt(0)->vt = VT_I4;
ev->ItemAt(0)->lVal = 10;
ev->ItemAt(1)->vt = VT_I4;
@@ -66,31 +52,36 @@ TEST(EnumVariantTest, SimpleEnumVariant) {
ev->ItemAt(2)->vt = VT_I4;
ev->ItemAt(2)->lVal = 30;
- // Get elements one at a time.
- VARIANT out_element;
- ::VariantInit(&out_element);
- ULONG out_received = 0;
- EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received));
- EXPECT_EQ(1u, out_received);
- EXPECT_EQ(VT_I4, out_element.vt);
- EXPECT_EQ(10, out_element.lVal);
- ::VariantClear(&out_element);
+ // Get elements one at a time from index 0 and 2.
+ base::win::ScopedVariant out_element_0;
+ ULONG out_received_0 = 0;
+ EXPECT_EQ(S_OK, ev->Next(1, out_element_0.Receive(), &out_received_0));
+ EXPECT_EQ(1u, out_received_0);
+ EXPECT_EQ(VT_I4, out_element_0.ptr()->vt);
+ EXPECT_EQ(10, out_element_0.ptr()->lVal);
+
EXPECT_EQ(S_OK, ev->Skip(1));
- EXPECT_EQ(S_OK, ev->Next(1, &out_element, &out_received));
- EXPECT_EQ(1u, out_received);
- EXPECT_EQ(VT_I4, out_element.vt);
- EXPECT_EQ(30, out_element.lVal);
- ::VariantClear(&out_element);
- EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
- ::VariantClear(&out_element);
-
- // Reset and get all elements at once.
+
+ base::win::ScopedVariant out_element_2;
+ ULONG out_received_2 = 0;
+ EXPECT_EQ(S_OK, ev->Next(1, out_element_2.Receive(), &out_received_2));
+ EXPECT_EQ(1u, out_received_2);
+ EXPECT_EQ(VT_I4, out_element_2.ptr()->vt);
+ EXPECT_EQ(30, out_element_2.ptr()->lVal);
+
+ base::win::ScopedVariant placeholder_variant;
+ EXPECT_EQ(S_FALSE, ev->Next(1, placeholder_variant.Receive(), nullptr));
+
+ // Verify the reset works for the next step.
+ ASSERT_EQ(S_OK, ev->Reset());
+
+ // Get all elements at once.
VARIANT out_elements[3];
+ ULONG out_received_multiple;
for (int i = 0; i < 3; ++i)
::VariantInit(&out_elements[i]);
- EXPECT_EQ(S_OK, ev->Reset());
- EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received));
- EXPECT_EQ(3u, out_received);
+ EXPECT_EQ(S_OK, ev->Next(3, out_elements, &out_received_multiple));
+ EXPECT_EQ(3u, out_received_multiple);
EXPECT_EQ(VT_I4, out_elements[0].vt);
EXPECT_EQ(10, out_elements[0].lVal);
EXPECT_EQ(VT_I4, out_elements[1].vt);
@@ -99,16 +90,31 @@ TEST(EnumVariantTest, SimpleEnumVariant) {
EXPECT_EQ(30, out_elements[2].lVal);
for (int i = 0; i < 3; ++i)
::VariantClear(&out_elements[i]);
- EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
- ::VariantClear(&out_element);
+
+ base::win::ScopedVariant placeholder_variant_multiple;
+ EXPECT_EQ(S_FALSE,
+ ev->Next(1, placeholder_variant_multiple.Receive(), nullptr));
+}
+
+TEST(EnumVariantTest, Clone) {
+ ScopedCOMInitializer com_initializer;
+
+ Microsoft::WRL::ComPtr<EnumVariant> ev = Microsoft::WRL::Make<EnumVariant>(3);
+ ev->ItemAt(0)->vt = VT_I4;
+ ev->ItemAt(0)->lVal = 10;
+ ev->ItemAt(1)->vt = VT_I4;
+ ev->ItemAt(1)->lVal = 20;
+ ev->ItemAt(2)->vt = VT_I4;
+ ev->ItemAt(2)->lVal = 30;
// Clone it.
- IEnumVARIANT* ev2 = NULL;
+ Microsoft::WRL::ComPtr<IEnumVARIANT> ev2;
EXPECT_EQ(S_OK, ev->Clone(&ev2));
EXPECT_TRUE(ev2 != NULL);
- EXPECT_EQ(S_FALSE, ev->Next(1, &out_element, &out_received));
- ::VariantClear(&out_element);
- EXPECT_EQ(S_OK, ev2->Reset());
+
+ VARIANT out_elements[3];
+ for (int i = 0; i < 3; ++i)
+ ::VariantInit(&out_elements[i]);
EXPECT_EQ(S_OK, ev2->Next(3, out_elements, nullptr));
EXPECT_EQ(VT_I4, out_elements[0].vt);
EXPECT_EQ(10, out_elements[0].lVal);
@@ -118,14 +124,6 @@ TEST(EnumVariantTest, SimpleEnumVariant) {
EXPECT_EQ(30, out_elements[2].lVal);
for (int i = 0; i < 3; ++i)
::VariantClear(&out_elements[i]);
- EXPECT_EQ(S_FALSE, ev2->Next(1, &out_element, nullptr));
- ::VariantClear(&out_element);
-
- ULONG ev2_finalrefcount = ev2->Release();
- EXPECT_EQ(0u, ev2_finalrefcount);
-
- ULONG ev_finalrefcount = ev->Release();
- EXPECT_EQ(0u, ev_finalrefcount);
}
} // namespace win
diff --git a/chromium/base/win/i18n.cc b/chromium/base/win/i18n.cc
index adba36d13e8..ca80f93884d 100644
--- a/chromium/base/win/i18n.cc
+++ b/chromium/base/win/i18n.cc
@@ -16,7 +16,7 @@ typedef decltype(::GetSystemPreferredUILanguages)* GetPreferredUILanguages_Fn;
bool GetPreferredUILanguageList(GetPreferredUILanguages_Fn function,
ULONG flags,
- std::vector<base::string16>* languages) {
+ std::vector<std::wstring>* languages) {
DCHECK_EQ((flags & (MUI_LANGUAGE_ID | MUI_LANGUAGE_NAME)), 0U);
const ULONG call_flags = flags | MUI_LANGUAGE_NAME;
ULONG language_count = 0;
@@ -27,18 +27,21 @@ bool GetPreferredUILanguageList(GetPreferredUILanguages_Fn function,
return false;
}
- base::string16 buffer(buffer_length, '\0');
- if (!function(call_flags, &language_count, base::as_writable_wcstr(buffer),
+ std::wstring buffer(buffer_length, '\0');
+ if (!function(call_flags, &language_count, base::data(buffer),
&buffer_length) ||
!language_count) {
DPCHECK(!language_count) << "Failed getting preferred UI languages.";
return false;
}
+ languages->clear();
// Split string on NUL characters.
- *languages =
- base::SplitString(buffer, base::string16(1, '\0'), base::KEEP_WHITESPACE,
- base::SPLIT_WANT_NONEMPTY);
+ for (const auto& token : base::SplitStringPiece(
+ base::AsStringPiece16(buffer), base::string16(1, '\0'),
+ base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
+ languages->push_back(base::AsWString(token));
+ }
DCHECK_EQ(languages->size(), language_count);
return true;
}
@@ -49,13 +52,13 @@ namespace base {
namespace win {
namespace i18n {
-bool GetUserPreferredUILanguageList(std::vector<base::string16>* languages) {
+bool GetUserPreferredUILanguageList(std::vector<std::wstring>* languages) {
DCHECK(languages);
return GetPreferredUILanguageList(::GetUserPreferredUILanguages, 0,
languages);
}
-bool GetThreadPreferredUILanguageList(std::vector<base::string16>* languages) {
+bool GetThreadPreferredUILanguageList(std::vector<std::wstring>* languages) {
DCHECK(languages);
return GetPreferredUILanguageList(
::GetThreadPreferredUILanguages,
diff --git a/chromium/base/win/i18n.h b/chromium/base/win/i18n.h
index d586646a286..9e74d3f3da3 100644
--- a/chromium/base/win/i18n.h
+++ b/chromium/base/win/i18n.h
@@ -5,10 +5,10 @@
#ifndef BASE_WIN_I18N_H_
#define BASE_WIN_I18N_H_
+#include <string>
#include <vector>
#include "base/base_export.h"
-#include "base/strings/string16.h"
namespace base {
namespace win {
@@ -18,13 +18,13 @@ namespace i18n {
// available, falling-back on the user default UI language otherwise. Returns
// true if at least one language is added.
BASE_EXPORT bool GetUserPreferredUILanguageList(
- std::vector<base::string16>* languages);
+ std::vector<std::wstring>* languages);
// Adds to |languages| the list of thread, process, user, and system preferred
// UI languages from MUI, if available, falling-back on the user default UI
// language otherwise. Returns true if at least one language is added.
BASE_EXPORT bool GetThreadPreferredUILanguageList(
- std::vector<base::string16>* languages);
+ std::vector<std::wstring>* languages);
} // namespace i18n
} // namespace win
diff --git a/chromium/base/win/i18n_unittest.cc b/chromium/base/win/i18n_unittest.cc
index 3b0b668a735..f38db247a4a 100644
--- a/chromium/base/win/i18n_unittest.cc
+++ b/chromium/base/win/i18n_unittest.cc
@@ -19,24 +19,24 @@ namespace i18n {
// Tests that at least one user preferred UI language can be obtained.
TEST(I18NTest, GetUserPreferredUILanguageList) {
- std::vector<base::string16> languages;
+ std::vector<std::wstring> languages;
EXPECT_TRUE(GetUserPreferredUILanguageList(&languages));
EXPECT_FALSE(languages.empty());
for (const auto& language : languages) {
EXPECT_FALSE(language.empty());
// Ensure there's no extra trailing 0 characters.
- EXPECT_EQ(language.size(), wcslen(base::as_wcstr(language)));
+ EXPECT_EQ(language.size(), wcslen(language.c_str()));
}
}
// Tests that at least one thread preferred UI language can be obtained.
TEST(I18NTest, GetThreadPreferredUILanguageList) {
- std::vector<base::string16> languages;
+ std::vector<std::wstring> languages;
EXPECT_TRUE(GetThreadPreferredUILanguageList(&languages));
EXPECT_FALSE(languages.empty());
for (const auto& language : languages) {
EXPECT_FALSE(language.empty());
- EXPECT_EQ(language.size(), wcslen(base::as_wcstr(language)));
+ EXPECT_EQ(language.size(), wcslen(language.c_str()));
}
}
diff --git a/chromium/base/win/iunknown_impl.cc b/chromium/base/win/iunknown_impl.cc
deleted file mode 100644
index 2a88439220d..00000000000
--- a/chromium/base/win/iunknown_impl.cc
+++ /dev/null
@@ -1,42 +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 "base/win/iunknown_impl.h"
-
-namespace base {
-namespace win {
-
-IUnknownImpl::IUnknownImpl()
- : ref_count_(0) {
-}
-
-IUnknownImpl::~IUnknownImpl() {
-}
-
-ULONG STDMETHODCALLTYPE IUnknownImpl::AddRef() {
- ref_count_.Increment();
- return 1;
-}
-
-ULONG STDMETHODCALLTYPE IUnknownImpl::Release() {
- if (!ref_count_.Decrement()) {
- delete this;
- return 0;
- }
- return 1;
-}
-
-STDMETHODIMP IUnknownImpl::QueryInterface(REFIID riid, void** ppv) {
- if (riid == IID_IUnknown) {
- *ppv = static_cast<IUnknown*>(this);
- AddRef();
- return S_OK;
- }
-
- *ppv = NULL;
- return E_NOINTERFACE;
-}
-
-} // namespace win
-} // namespace base
diff --git a/chromium/base/win/iunknown_impl.h b/chromium/base/win/iunknown_impl.h
deleted file mode 100644
index b7de205ac6c..00000000000
--- a/chromium/base/win/iunknown_impl.h
+++ /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.
-
-#ifndef BASE_WIN_IUNKNOWN_IMPL_H_
-#define BASE_WIN_IUNKNOWN_IMPL_H_
-
-#include <unknwn.h>
-
-#include "base/atomic_ref_count.h"
-#include "base/base_export.h"
-#include "base/compiler_specific.h"
-
-namespace base {
-namespace win {
-
-// IUnknown implementation for other classes to derive from.
-class BASE_EXPORT IUnknownImpl : public IUnknown {
- public:
- IUnknownImpl();
-
- ULONG STDMETHODCALLTYPE AddRef() override;
- ULONG STDMETHODCALLTYPE Release() override;
-
- // Subclasses should extend this to return any interfaces they provide.
- STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
-
- protected:
- virtual ~IUnknownImpl();
-
- private:
- AtomicRefCount ref_count_;
-};
-
-} // namespace win
-} // namespace base
-
-#endif // BASE_WIN_IUNKNOWN_IMPL_H_
diff --git a/chromium/base/win/iunknown_impl_unittest.cc b/chromium/base/win/iunknown_impl_unittest.cc
deleted file mode 100644
index c6c3539037e..00000000000
--- a/chromium/base/win/iunknown_impl_unittest.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 "base/win/iunknown_impl.h"
-
-#include "base/win/scoped_com_initializer.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace win {
-
-class TestIUnknownImplSubclass : public IUnknownImpl {
- public:
- TestIUnknownImplSubclass() {
- ++instance_count;
- }
- ~TestIUnknownImplSubclass() override { --instance_count; }
- static int instance_count;
-};
-
-// static
-int TestIUnknownImplSubclass::instance_count = 0;
-
-TEST(IUnknownImplTest, IUnknownImpl) {
- ScopedCOMInitializer com_initializer;
-
- EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count);
- IUnknown* u = new TestIUnknownImplSubclass();
-
- EXPECT_EQ(1, TestIUnknownImplSubclass::instance_count);
-
- EXPECT_EQ(1u, u->AddRef());
- EXPECT_EQ(1u, u->AddRef());
-
- IUnknown* other = NULL;
- EXPECT_EQ(E_NOINTERFACE, u->QueryInterface(
- IID_IDispatch, reinterpret_cast<void**>(&other)));
- EXPECT_EQ(S_OK, u->QueryInterface(
- IID_IUnknown, reinterpret_cast<void**>(&other)));
- other->Release();
-
- EXPECT_EQ(1u, u->Release());
- EXPECT_EQ(0u, u->Release());
- EXPECT_EQ(0, TestIUnknownImplSubclass::instance_count);
-}
-
-} // namespace win
-} // namespace base
diff --git a/chromium/base/win/object_watcher.cc b/chromium/base/win/object_watcher.cc
index 20f146655b0..bc4fd1a11e3 100644
--- a/chromium/base/win/object_watcher.cc
+++ b/chromium/base/win/object_watcher.cc
@@ -21,13 +21,16 @@ ObjectWatcher::~ObjectWatcher() {
StopWatching();
}
-bool ObjectWatcher::StartWatchingOnce(HANDLE object, Delegate* delegate) {
- return StartWatchingInternal(object, delegate, true);
+bool ObjectWatcher::StartWatchingOnce(HANDLE object,
+ Delegate* delegate,
+ const Location& from_here) {
+ return StartWatchingInternal(object, delegate, true, from_here);
}
bool ObjectWatcher::StartWatchingMultipleTimes(HANDLE object,
- Delegate* delegate) {
- return StartWatchingInternal(object, delegate, false);
+ Delegate* delegate,
+ const Location& from_here) {
+ return StartWatchingInternal(object, delegate, false, from_here);
}
bool ObjectWatcher::StopWatching() {
@@ -63,17 +66,20 @@ void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
// The destructor blocks on any callbacks that are in flight, so we know that
// that is always a pointer to a valid ObjectWater.
ObjectWatcher* that = static_cast<ObjectWatcher*>(param);
- that->task_runner_->PostTask(FROM_HERE, that->callback_);
+ that->task_runner_->PostTask(that->location_, that->callback_);
if (that->run_once_)
that->callback_.Reset();
}
-bool ObjectWatcher::StartWatchingInternal(HANDLE object, Delegate* delegate,
- bool execute_only_once) {
+bool ObjectWatcher::StartWatchingInternal(HANDLE object,
+ Delegate* delegate,
+ bool execute_only_once,
+ const Location& from_here) {
DCHECK(delegate);
DCHECK(!wait_object_) << "Already watching an object";
DCHECK(SequencedTaskRunnerHandle::IsSet());
+ location_ = from_here;
task_runner_ = SequencedTaskRunnerHandle::Get();
run_once_ = execute_only_once;
@@ -112,6 +118,7 @@ void ObjectWatcher::Signal(Delegate* delegate) {
void ObjectWatcher::Reset() {
callback_.Reset();
+ location_ = {};
object_ = nullptr;
wait_object_ = nullptr;
task_runner_ = nullptr;
diff --git a/chromium/base/win/object_watcher.h b/chromium/base/win/object_watcher.h
index 0779c271325..62b7277ad65 100644
--- a/chromium/base/win/object_watcher.h
+++ b/chromium/base/win/object_watcher.h
@@ -71,14 +71,19 @@ class BASE_EXPORT ObjectWatcher {
// where StartWatchingOnce is called. The ObjectWatcher is not responsible for
// deleting the delegate.
// Returns whether watching was successfully initiated.
- bool StartWatchingOnce(HANDLE object, Delegate* delegate);
+ bool StartWatchingOnce(HANDLE object,
+ Delegate* delegate,
+ const Location& from_here = Location::Current());
// Notifies the delegate, on the sequence where this method is called, each
// time the object is set. By definition, the handle must be an auto-reset
// object. The caller must ensure that it (or any Windows system code) doesn't
// reset the event or else the delegate won't be called.
// Returns whether watching was successfully initiated.
- bool StartWatchingMultipleTimes(HANDLE object, Delegate* delegate);
+ bool StartWatchingMultipleTimes(
+ HANDLE object,
+ Delegate* delegate,
+ const Location& from_here = Location::Current());
// Stops watching. Does nothing if the watch has already completed. If the
// watch is still active, then it is canceled, and the associated delegate is
@@ -98,13 +103,17 @@ class BASE_EXPORT ObjectWatcher {
static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out);
// Helper used by StartWatchingOnce and StartWatchingMultipleTimes.
- bool StartWatchingInternal(HANDLE object, Delegate* delegate,
- bool execute_only_once);
+ bool StartWatchingInternal(HANDLE object,
+ Delegate* delegate,
+ bool execute_only_once,
+ const Location& from_here);
void Signal(Delegate* delegate);
void Reset();
+ Location location_;
+
// A callback pre-bound to Signal() that is posted to the caller's task runner
// when the wait completes.
RepeatingClosure callback_;
diff --git a/chromium/base/win/pe_image_unittest.cc b/chromium/base/win/pe_image_unittest.cc
index caf241b8d22..45279c39815 100644
--- a/chromium/base/win/pe_image_unittest.cc
+++ b/chromium/base/win/pe_image_unittest.cc
@@ -182,29 +182,19 @@ TEST(PEImageTest, EnumeratesPEWithTargetModule) {
const char kTargetModuleStatic[] = "user32.dll";
const char kTargetModuleDelay[] = "cfgmgr32.dll";
-#if defined(ARCH_CPU_ARM64)
- const int kSections = 7;
- const int kImportsDlls = 2;
+ const int kImportsDlls = 1;
const int kDelayDlls = 1;
const int kExports = 3;
const int kImports = 2;
const int kDelayImports = 1;
+#if defined(ARCH_CPU_ARM64)
+ const int kSections = 7;
const int kRelocs = 740;
#elif defined(ARCH_CPU_64_BITS)
const int kSections = 6;
- const int kImportsDlls = 1;
- const int kDelayDlls = 1;
- const int kExports = 3;
- const int kImports = 2;
- const int kDelayImports = 1;
const int kRelocs = 976;
#else
const int kSections = 5;
- const int kImportsDlls = 1;
- const int kDelayDlls = 1;
- const int kExports = 3;
- const int kImports = 2;
- const int kDelayImports = 1;
const int kRelocs = 2114;
#endif
diff --git a/chromium/base/win/registry.cc b/chromium/base/win/registry.cc
index b95b4746858..cd6208eda5f 100644
--- a/chromium/base/win/registry.cc
+++ b/chromium/base/win/registry.cc
@@ -13,6 +13,7 @@
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
+#include "base/strings/string_util_win.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/shlwapi.h"
#include "base/win/windows_version.h"
@@ -95,7 +96,7 @@ RegKey::RegKey() : key_(NULL), wow64access_(0) {
RegKey::RegKey(HKEY key) : key_(key), wow64access_(0) {
}
-RegKey::RegKey(HKEY rootkey, const char16* subkey, REGSAM access)
+RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
: key_(NULL), wow64access_(0) {
if (rootkey) {
if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
@@ -112,20 +113,20 @@ RegKey::~RegKey() {
Close();
}
-LONG RegKey::Create(HKEY rootkey, const char16* subkey, REGSAM access) {
+LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
DWORD disposition_value;
return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
}
LONG RegKey::CreateWithDisposition(HKEY rootkey,
- const char16* subkey,
+ const wchar_t* subkey,
DWORD* disposition,
REGSAM access) {
DCHECK(rootkey && subkey && access && disposition);
HKEY subhkey = NULL;
- LONG result = RegCreateKeyEx(rootkey, as_wcstr(subkey), 0, NULL,
- REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
- disposition);
+ LONG result =
+ RegCreateKeyEx(rootkey, subkey, 0, NULL, REG_OPTION_NON_VOLATILE, access,
+ NULL, &subhkey, disposition);
if (result == ERROR_SUCCESS) {
Close();
key_ = subhkey;
@@ -135,7 +136,7 @@ LONG RegKey::CreateWithDisposition(HKEY rootkey,
return result;
}
-LONG RegKey::CreateKey(const char16* name, REGSAM access) {
+LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
DCHECK(name && access);
// After the application has accessed an alternate registry view using one of
// the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
@@ -147,9 +148,8 @@ LONG RegKey::CreateKey(const char16* name, REGSAM access) {
return ERROR_INVALID_PARAMETER;
}
HKEY subkey = NULL;
- LONG result =
- RegCreateKeyEx(key_, as_wcstr(name), 0, NULL, REG_OPTION_NON_VOLATILE,
- access, NULL, &subkey, NULL);
+ LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
+ access, NULL, &subkey, NULL);
if (result == ERROR_SUCCESS) {
Close();
key_ = subkey;
@@ -159,11 +159,11 @@ LONG RegKey::CreateKey(const char16* name, REGSAM access) {
return result;
}
-LONG RegKey::Open(HKEY rootkey, const char16* subkey, REGSAM access) {
+LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
DCHECK(rootkey && subkey && access);
HKEY subhkey = NULL;
- LONG result = RegOpenKeyEx(rootkey, as_wcstr(subkey), 0, access, &subhkey);
+ LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
if (result == ERROR_SUCCESS) {
Close();
key_ = subhkey;
@@ -173,7 +173,7 @@ LONG RegKey::Open(HKEY rootkey, const char16* subkey, REGSAM access) {
return result;
}
-LONG RegKey::OpenKey(const char16* relative_key_name, REGSAM access) {
+LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
DCHECK(relative_key_name && access);
// After the application has accessed an alternate registry view using one of
// the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
@@ -185,8 +185,7 @@ LONG RegKey::OpenKey(const char16* relative_key_name, REGSAM access) {
return ERROR_INVALID_PARAMETER;
}
HKEY subkey = NULL;
- LONG result =
- RegOpenKeyEx(key_, as_wcstr(relative_key_name), 0, access, &subkey);
+ LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
// We have to close the current opened key before replacing it with the new
// one.
@@ -221,9 +220,8 @@ HKEY RegKey::Take() {
return key;
}
-bool RegKey::HasValue(const char16* name) const {
- return RegQueryValueEx(key_, as_wcstr(name), 0, NULL, NULL, NULL) ==
- ERROR_SUCCESS;
+bool RegKey::HasValue(const wchar_t* name) const {
+ return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
}
DWORD RegKey::GetValueCount() const {
@@ -233,26 +231,25 @@ DWORD RegKey::GetValueCount() const {
return (result == ERROR_SUCCESS) ? count : 0;
}
-LONG RegKey::GetValueNameAt(int index, string16* name) const {
- char16 buf[256];
+LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
+ wchar_t buf[256];
DWORD bufsize = size(buf);
- LONG r = ::RegEnumValue(key_, index, as_writable_wcstr(buf), &bufsize, NULL,
- NULL, NULL, NULL);
+ LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
if (r == ERROR_SUCCESS)
name->assign(buf, bufsize);
return r;
}
-LONG RegKey::DeleteKey(const char16* name) {
+LONG RegKey::DeleteKey(const wchar_t* name) {
DCHECK(key_);
DCHECK(name);
HKEY subkey = NULL;
// Verify the key exists before attempting delete to replicate previous
// behavior.
- LONG result = RegOpenKeyEx(key_, as_wcstr(name), 0,
- READ_CONTROL | wow64access_, &subkey);
+ LONG result =
+ RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
if (result != ERROR_SUCCESS)
return result;
RegCloseKey(subkey);
@@ -260,13 +257,13 @@ LONG RegKey::DeleteKey(const char16* name) {
return RegDelRecurse(key_, name, wow64access_);
}
-LONG RegKey::DeleteEmptyKey(const char16* name) {
+LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
DCHECK(key_);
DCHECK(name);
HKEY target_key = NULL;
- LONG result = RegOpenKeyEx(key_, as_wcstr(name), 0, KEY_READ | wow64access_,
- &target_key);
+ LONG result =
+ RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_, &target_key);
if (result != ERROR_SUCCESS)
return result;
@@ -281,18 +278,18 @@ LONG RegKey::DeleteEmptyKey(const char16* name) {
return result;
if (count == 0)
- return RegDeleteKeyEx(key_, as_wcstr(name), wow64access_, 0);
+ return RegDeleteKeyEx(key_, name, wow64access_, 0);
return ERROR_DIR_NOT_EMPTY;
}
-LONG RegKey::DeleteValue(const char16* value_name) {
+LONG RegKey::DeleteValue(const wchar_t* value_name) {
DCHECK(key_);
- LONG result = RegDeleteValue(key_, as_wcstr(value_name));
+ LONG result = RegDeleteValue(key_, value_name);
return result;
}
-LONG RegKey::ReadValueDW(const char16* name, DWORD* out_value) const {
+LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
DCHECK(out_value);
DWORD type = REG_DWORD;
DWORD size = sizeof(DWORD);
@@ -308,7 +305,7 @@ LONG RegKey::ReadValueDW(const char16* name, DWORD* out_value) const {
return result;
}
-LONG RegKey::ReadInt64(const char16* name, int64_t* out_value) const {
+LONG RegKey::ReadInt64(const wchar_t* name, int64_t* out_value) const {
DCHECK(out_value);
DWORD type = REG_QWORD;
int64_t local_value = 0;
@@ -325,20 +322,19 @@ LONG RegKey::ReadInt64(const char16* name, int64_t* out_value) const {
return result;
}
-LONG RegKey::ReadValue(const char16* name, string16* out_value) const {
+LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
DCHECK(out_value);
const size_t kMaxStringLength = 1024; // This is after expansion.
// Use the one of the other forms of ReadValue if 1024 is too small for you.
- char16 raw_value[kMaxStringLength];
+ wchar_t raw_value[kMaxStringLength];
DWORD type = REG_SZ, size = sizeof(raw_value);
- LONG result = ReadValue(name, as_writable_wcstr(raw_value), &size, &type);
+ LONG result = ReadValue(name, raw_value, &size, &type);
if (result == ERROR_SUCCESS) {
if (type == REG_SZ) {
*out_value = raw_value;
} else if (type == REG_EXPAND_SZ) {
- char16 expanded[kMaxStringLength];
- size = ExpandEnvironmentStrings(
- as_wcstr(raw_value), as_writable_wcstr(expanded), kMaxStringLength);
+ wchar_t expanded[kMaxStringLength];
+ size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
// Success: returns the number of wchar_t's copied
// Fail: buffer too small, returns the size required
// Fail: other, returns 0
@@ -356,16 +352,17 @@ LONG RegKey::ReadValue(const char16* name, string16* out_value) const {
return result;
}
-LONG RegKey::ReadValue(const char16* name,
+LONG RegKey::ReadValue(const wchar_t* name,
void* data,
DWORD* dsize,
DWORD* dtype) const {
- LONG result = RegQueryValueEx(key_, as_wcstr(name), 0, dtype,
+ LONG result = RegQueryValueEx(key_, name, 0, dtype,
reinterpret_cast<LPBYTE>(data), dsize);
return result;
}
-LONG RegKey::ReadValues(const char16* name, std::vector<string16>* values) {
+LONG RegKey::ReadValues(const wchar_t* name,
+ std::vector<std::wstring>* values) {
values->clear();
DWORD type = REG_MULTI_SZ;
@@ -377,8 +374,8 @@ LONG RegKey::ReadValues(const char16* name, std::vector<string16>* values) {
if (type != REG_MULTI_SZ)
return ERROR_CANTREAD;
- std::vector<char16> buffer(size / sizeof(char16));
- result = ReadValue(name, as_writable_wcstr(buffer.data()), &size, NULL);
+ std::vector<wchar_t> buffer(size / sizeof(wchar_t));
+ result = ReadValue(name, buffer.data(), &size, NULL);
if (result != ERROR_SUCCESS || size == 0)
return result;
@@ -395,27 +392,27 @@ LONG RegKey::ReadValues(const char16* name, std::vector<string16>* values) {
return 0;
}
-LONG RegKey::WriteValue(const char16* name, DWORD in_value) {
+LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
return WriteValue(
name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD);
}
-LONG RegKey::WriteValue(const char16* name, const char16* in_value) {
+LONG RegKey::WriteValue(const wchar_t* name, const wchar_t* in_value) {
return WriteValue(
name, in_value,
static_cast<DWORD>(sizeof(*in_value) *
- (std::char_traits<char16>::length(in_value) + 1)),
+ (std::char_traits<wchar_t>::length(in_value) + 1)),
REG_SZ);
}
-LONG RegKey::WriteValue(const char16* name,
+LONG RegKey::WriteValue(const wchar_t* name,
const void* data,
DWORD dsize,
DWORD dtype) {
DCHECK(data || !dsize);
LONG result =
- RegSetValueEx(key_, as_wcstr(name), 0, dtype,
+ RegSetValueEx(key_, name, 0, dtype,
reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
return result;
}
@@ -431,22 +428,22 @@ bool RegKey::StartWatching(ChangeCallback callback) {
}
// static
-LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) {
+LONG RegKey::RegDelRecurse(HKEY root_key, const wchar_t* name, REGSAM access) {
// First, see if the key can be deleted without having to recurse.
- LONG result = RegDeleteKeyEx(root_key, as_wcstr(name), access, 0);
+ LONG result = RegDeleteKeyEx(root_key, name, access, 0);
if (result == ERROR_SUCCESS)
return result;
HKEY target_key = NULL;
- result = RegOpenKeyEx(root_key, as_wcstr(name), 0,
- KEY_ENUMERATE_SUB_KEYS | access, &target_key);
+ result = RegOpenKeyEx(root_key, name, 0, KEY_ENUMERATE_SUB_KEYS | access,
+ &target_key);
if (result == ERROR_FILE_NOT_FOUND)
return ERROR_SUCCESS;
if (result != ERROR_SUCCESS)
return result;
- string16 subkey_name(name);
+ std::wstring subkey_name(name);
// Check for an ending slash and add one if it is missing.
if (!subkey_name.empty() && subkey_name.back() != '\\')
@@ -456,12 +453,11 @@ LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) {
result = ERROR_SUCCESS;
const DWORD kMaxKeyNameLength = MAX_PATH;
const size_t base_key_length = subkey_name.length();
- string16 key_name;
+ std::wstring key_name;
while (result == ERROR_SUCCESS) {
DWORD key_size = kMaxKeyNameLength;
result =
- RegEnumKeyEx(target_key, 0,
- as_writable_wcstr(WriteInto(&key_name, kMaxKeyNameLength)),
+ RegEnumKeyEx(target_key, 0, WriteIntoW(&key_name, kMaxKeyNameLength),
&key_size, NULL, NULL, NULL, NULL);
if (result != ERROR_SUCCESS)
@@ -478,7 +474,7 @@ LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) {
RegCloseKey(target_key);
// Try again to delete the key.
- result = RegDeleteKeyEx(root_key, as_wcstr(name), access, 0);
+ result = RegDeleteKeyEx(root_key, name, access, 0);
return result;
}
@@ -486,24 +482,24 @@ LONG RegKey::RegDelRecurse(HKEY root_key, const char16* name, REGSAM access) {
// RegistryValueIterator ------------------------------------------------------
RegistryValueIterator::RegistryValueIterator(HKEY root_key,
- const char16* folder_key,
+ const wchar_t* folder_key,
REGSAM wow64access)
: name_(MAX_PATH, '\0'), value_(MAX_PATH, '\0') {
Initialize(root_key, folder_key, wow64access);
}
RegistryValueIterator::RegistryValueIterator(HKEY root_key,
- const char16* folder_key)
+ const wchar_t* folder_key)
: name_(MAX_PATH, '\0'), value_(MAX_PATH, '\0') {
Initialize(root_key, folder_key, 0);
}
void RegistryValueIterator::Initialize(HKEY root_key,
- const char16* folder_key,
+ const wchar_t* folder_key,
REGSAM wow64access) {
DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
- LONG result = RegOpenKeyEx(root_key, as_wcstr(folder_key), 0,
- KEY_READ | wow64access, &key_);
+ LONG result =
+ RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_);
if (result != ERROR_SUCCESS) {
key_ = NULL;
} else {
@@ -551,13 +547,10 @@ bool RegistryValueIterator::Read() {
DWORD capacity = static_cast<DWORD>(name_.capacity());
DWORD name_size = capacity;
// |value_size_| is in bytes. Reserve the last character for a NUL.
- static_assert(sizeof(wchar_t) == sizeof(char16),
- "wchar_t is not the same size as base::char16");
value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
LONG result = ::RegEnumValue(
- key_, index_, as_writable_wcstr(WriteInto(&name_, name_size)),
- &name_size, NULL, &type_, reinterpret_cast<BYTE*>(value_.data()),
- &value_size_);
+ key_, index_, WriteIntoW(&name_, name_size), &name_size, NULL, &type_,
+ reinterpret_cast<BYTE*>(value_.data()), &value_size_);
if (result == ERROR_MORE_DATA) {
// Registry key names are limited to 255 characters and fit within
@@ -572,9 +565,8 @@ bool RegistryValueIterator::Read() {
value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
result = ::RegEnumValue(
- key_, index_, as_writable_wcstr(WriteInto(&name_, name_size)),
- &name_size, NULL, &type_, reinterpret_cast<BYTE*>(value_.data()),
- &value_size_);
+ key_, index_, WriteIntoW(&name_, name_size), &name_size, NULL, &type_,
+ reinterpret_cast<BYTE*>(value_.data()), &value_size_);
}
if (result == ERROR_SUCCESS) {
@@ -593,12 +585,12 @@ bool RegistryValueIterator::Read() {
// RegistryKeyIterator --------------------------------------------------------
RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
- const char16* folder_key) {
+ const wchar_t* folder_key) {
Initialize(root_key, folder_key, 0);
}
RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
- const char16* folder_key,
+ const wchar_t* folder_key,
REGSAM wow64access) {
Initialize(root_key, folder_key, wow64access);
}
@@ -631,8 +623,8 @@ bool RegistryKeyIterator::Read() {
if (Valid()) {
DWORD ncount = static_cast<DWORD>(size(name_));
FILETIME written;
- LONG r = ::RegEnumKeyEx(key_, index_, as_writable_wcstr(name_), &ncount,
- NULL, NULL, NULL, &written);
+ LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL, NULL,
+ &written);
if (ERROR_SUCCESS == r)
return true;
}
@@ -642,11 +634,11 @@ bool RegistryKeyIterator::Read() {
}
void RegistryKeyIterator::Initialize(HKEY root_key,
- const char16* folder_key,
+ const wchar_t* folder_key,
REGSAM wow64access) {
DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
- LONG result = RegOpenKeyEx(root_key, as_wcstr(folder_key), 0,
- KEY_READ | wow64access, &key_);
+ LONG result =
+ RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_);
if (result != ERROR_SUCCESS) {
key_ = NULL;
} else {
diff --git a/chromium/base/win/registry.h b/chromium/base/win/registry.h
index 1c57fc9f537..f7728219355 100644
--- a/chromium/base/win/registry.h
+++ b/chromium/base/win/registry.h
@@ -8,12 +8,12 @@
#include <stdint.h>
#include <memory>
+#include <string>
#include <vector>
#include "base/base_export.h"
#include "base/callback.h"
#include "base/macros.h"
-#include "base/strings/string16.h"
#include "base/win/object_watcher.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_types.h"
@@ -37,24 +37,24 @@ class BASE_EXPORT RegKey {
RegKey();
explicit RegKey(HKEY key);
- RegKey(HKEY rootkey, const char16* subkey, REGSAM access);
+ RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access);
~RegKey();
- LONG Create(HKEY rootkey, const char16* subkey, REGSAM access);
+ LONG Create(HKEY rootkey, const wchar_t* subkey, REGSAM access);
LONG CreateWithDisposition(HKEY rootkey,
- const char16* subkey,
+ const wchar_t* subkey,
DWORD* disposition,
REGSAM access);
// Creates a subkey or open it if it already exists.
- LONG CreateKey(const char16* name, REGSAM access);
+ LONG CreateKey(const wchar_t* name, REGSAM access);
// Opens an existing reg key.
- LONG Open(HKEY rootkey, const char16* subkey, REGSAM access);
+ LONG Open(HKEY rootkey, const wchar_t* subkey, REGSAM access);
// Opens an existing reg key, given the relative key name.
- LONG OpenKey(const char16* relative_key_name, REGSAM access);
+ LONG OpenKey(const wchar_t* relative_key_name, REGSAM access);
// Closes this reg key.
void Close();
@@ -67,51 +67,51 @@ class BASE_EXPORT RegKey {
// Returns false if this key does not have the specified value, or if an error
// occurrs while attempting to access it.
- bool HasValue(const char16* value_name) const;
+ bool HasValue(const wchar_t* value_name) const;
// Returns the number of values for this key, or 0 if the number cannot be
// determined.
DWORD GetValueCount() const;
// Determines the nth value's name.
- LONG GetValueNameAt(int index, string16* name) const;
+ LONG GetValueNameAt(int index, std::wstring* name) const;
// True while the key is valid.
bool Valid() const { return key_ != NULL; }
// Kills a key and everything that lives below it; please be careful when
// using it.
- LONG DeleteKey(const char16* name);
+ LONG DeleteKey(const wchar_t* name);
// Deletes an empty subkey. If the subkey has subkeys or values then this
// will fail.
- LONG DeleteEmptyKey(const char16* name);
+ LONG DeleteEmptyKey(const wchar_t* name);
// Deletes a single value within the key.
- LONG DeleteValue(const char16* name);
+ LONG DeleteValue(const wchar_t* name);
// Getters:
// Reads a REG_DWORD (uint32_t) into |out_value|. If |name| is null or empty,
// reads the key's default value, if any.
- LONG ReadValueDW(const char16* name, DWORD* out_value) const;
+ LONG ReadValueDW(const wchar_t* name, DWORD* out_value) const;
// Reads a REG_QWORD (int64_t) into |out_value|. If |name| is null or empty,
// reads the key's default value, if any.
- LONG ReadInt64(const char16* name, int64_t* out_value) const;
+ LONG ReadInt64(const wchar_t* name, int64_t* out_value) const;
// Reads a string into |out_value|. If |name| is null or empty, reads
// the key's default value, if any.
- LONG ReadValue(const char16* name, string16* out_value) const;
+ LONG ReadValue(const wchar_t* name, std::wstring* out_value) const;
// Reads a REG_MULTI_SZ registry field into a vector of strings. Clears
// |values| initially and adds further strings to the list. Returns
// ERROR_CANTREAD if type is not REG_MULTI_SZ.
- LONG ReadValues(const char16* name, std::vector<string16>* values);
+ LONG ReadValues(const wchar_t* name, std::vector<std::wstring>* values);
// Reads raw data into |data|. If |name| is null or empty, reads the key's
// default value, if any.
- LONG ReadValue(const char16* name,
+ LONG ReadValue(const wchar_t* name,
void* data,
DWORD* dsize,
DWORD* dtype) const;
@@ -119,13 +119,13 @@ class BASE_EXPORT RegKey {
// Setters:
// Sets an int32_t value.
- LONG WriteValue(const char16* name, DWORD in_value);
+ LONG WriteValue(const wchar_t* name, DWORD in_value);
// Sets a string value.
- LONG WriteValue(const char16* name, const char16* in_value);
+ LONG WriteValue(const wchar_t* name, const wchar_t* in_value);
// Sets raw data, including type.
- LONG WriteValue(const char16* name,
+ LONG WriteValue(const wchar_t* name,
const void* data,
DWORD dsize,
DWORD dtype);
@@ -143,7 +143,7 @@ class BASE_EXPORT RegKey {
class Watcher;
// Recursively deletes a key and all of its subkeys.
- static LONG RegDelRecurse(HKEY root_key, const char16* name, REGSAM access);
+ static LONG RegDelRecurse(HKEY root_key, const wchar_t* name, REGSAM access);
HKEY key_; // The registry key being iterated.
REGSAM wow64access_;
@@ -156,7 +156,7 @@ class BASE_EXPORT RegKey {
class BASE_EXPORT RegistryValueIterator {
public:
// Constructs a Registry Value Iterator with default WOW64 access.
- RegistryValueIterator(HKEY root_key, const char16* folder_key);
+ RegistryValueIterator(HKEY root_key, const wchar_t* folder_key);
// Constructs a Registry Key Iterator with specific WOW64 access, one of
// KEY_WOW64_32KEY or KEY_WOW64_64KEY, or 0.
@@ -164,7 +164,7 @@ class BASE_EXPORT RegistryValueIterator {
// previously, or a predefined key (e.g. HKEY_LOCAL_MACHINE).
// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
RegistryValueIterator(HKEY root_key,
- const char16* folder_key,
+ const wchar_t* folder_key,
REGSAM wow64access);
~RegistryValueIterator();
@@ -177,8 +177,8 @@ class BASE_EXPORT RegistryValueIterator {
// Advances to the next registry entry.
void operator++();
- const char16* Name() const { return name_.c_str(); }
- const char16* Value() const { return value_.data(); }
+ const wchar_t* Name() const { return name_.c_str(); }
+ const wchar_t* Value() const { return value_.data(); }
// ValueSize() is in bytes.
DWORD ValueSize() const { return value_size_; }
DWORD Type() const { return type_; }
@@ -189,7 +189,7 @@ class BASE_EXPORT RegistryValueIterator {
// Reads in the current values.
bool Read();
- void Initialize(HKEY root_key, const char16* folder_key, REGSAM wow64access);
+ void Initialize(HKEY root_key, const wchar_t* folder_key, REGSAM wow64access);
// The registry key being iterated.
HKEY key_;
@@ -198,8 +198,8 @@ class BASE_EXPORT RegistryValueIterator {
int index_;
// Current values.
- string16 name_;
- std::vector<char16> value_;
+ std::wstring name_;
+ std::vector<wchar_t> value_;
DWORD value_size_;
DWORD type_;
@@ -209,7 +209,7 @@ class BASE_EXPORT RegistryValueIterator {
class BASE_EXPORT RegistryKeyIterator {
public:
// Constructs a Registry Key Iterator with default WOW64 access.
- RegistryKeyIterator(HKEY root_key, const char16* folder_key);
+ RegistryKeyIterator(HKEY root_key, const wchar_t* folder_key);
// Constructs a Registry Value Iterator with specific WOW64 access, one of
// KEY_WOW64_32KEY or KEY_WOW64_64KEY, or 0.
@@ -217,7 +217,7 @@ class BASE_EXPORT RegistryKeyIterator {
// previously, or a predefined key (e.g. HKEY_LOCAL_MACHINE).
// See http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
RegistryKeyIterator(HKEY root_key,
- const char16* folder_key,
+ const wchar_t* folder_key,
REGSAM wow64access);
~RegistryKeyIterator();
@@ -230,7 +230,7 @@ class BASE_EXPORT RegistryKeyIterator {
// Advances to the next entry in the folder.
void operator++();
- const char16* Name() const { return name_; }
+ const wchar_t* Name() const { return name_; }
int Index() const { return index_; }
@@ -238,7 +238,7 @@ class BASE_EXPORT RegistryKeyIterator {
// Reads in the current values.
bool Read();
- void Initialize(HKEY root_key, const char16* folder_key, REGSAM wow64access);
+ void Initialize(HKEY root_key, const wchar_t* folder_key, REGSAM wow64access);
// The registry key being iterated.
HKEY key_;
@@ -246,7 +246,7 @@ class BASE_EXPORT RegistryKeyIterator {
// Current index of the iteration.
int index_;
- char16 name_[MAX_PATH];
+ wchar_t name_[MAX_PATH];
DISALLOW_COPY_AND_ASSIGN(RegistryKeyIterator);
};
diff --git a/chromium/base/win/registry_unittest.cc b/chromium/base/win/registry_unittest.cc
index 12ebc00a1d0..50be1d3230f 100644
--- a/chromium/base/win/registry_unittest.cc
+++ b/chromium/base/win/registry_unittest.cc
@@ -23,7 +23,7 @@ namespace win {
namespace {
-const char16 kRootKey[] = STRING16_LITERAL("Base_Registry_Unittest");
+const wchar_t kRootKey[] = L"Base_Registry_Unittest";
class RegistryTest : public testing::Test {
protected:
@@ -38,18 +38,18 @@ class RegistryTest : public testing::Test {
RegistryTest() {}
void SetUp() override {
// Create a temporary key.
- RegKey key(HKEY_CURRENT_USER, STRING16_LITERAL(""), KEY_ALL_ACCESS);
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
key.DeleteKey(kRootKey);
ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ));
- foo_software_key_ = STRING16_LITERAL("Software\\");
+ foo_software_key_ = L"Software\\";
foo_software_key_ += kRootKey;
- foo_software_key_ += STRING16_LITERAL("\\Foo");
+ foo_software_key_ += L"\\Foo";
}
void TearDown() override {
// Clean up the temporary key.
- RegKey key(HKEY_CURRENT_USER, STRING16_LITERAL(""), KEY_SET_VALUE);
+ RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE);
ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
}
@@ -62,7 +62,7 @@ class RegistryTest : public testing::Test {
#endif
}
- string16 foo_software_key_;
+ std::wstring foo_software_key_;
private:
DISALLOW_COPY_AND_ASSIGN(RegistryTest);
@@ -75,8 +75,8 @@ const REGSAM RegistryTest::kRedirectedViewMask;
TEST_F(RegistryTest, ValueTest) {
RegKey key;
- string16 foo_key(kRootKey);
- foo_key += STRING16_LITERAL("\\Foo");
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
KEY_READ));
@@ -85,10 +85,10 @@ TEST_F(RegistryTest, ValueTest) {
KEY_READ | KEY_SET_VALUE));
ASSERT_TRUE(key.Valid());
- const char16 kStringValueName[] = STRING16_LITERAL("StringValue");
- const char16 kDWORDValueName[] = STRING16_LITERAL("DWORDValue");
- const char16 kInt64ValueName[] = STRING16_LITERAL("Int64Value");
- const char16 kStringData[] = STRING16_LITERAL("string data");
+ const wchar_t kStringValueName[] = L"StringValue";
+ const wchar_t kDWORDValueName[] = L"DWORDValue";
+ const wchar_t kInt64ValueName[] = L"Int64Value";
+ const wchar_t kStringData[] = L"string data";
const DWORD kDWORDData = 0xdeadbabe;
const int64_t kInt64Data = 0xdeadbabedeadbabeLL;
@@ -103,7 +103,7 @@ TEST_F(RegistryTest, ValueTest) {
EXPECT_TRUE(key.HasValue(kInt64ValueName));
// Test Read
- string16 string_value;
+ std::wstring string_value;
DWORD dword_value = 0;
int64_t int64_value = 0;
ASSERT_EQ(ERROR_SUCCESS, key.ReadValue(kStringValueName, &string_value));
@@ -114,7 +114,7 @@ TEST_F(RegistryTest, ValueTest) {
EXPECT_EQ(kInt64Data, int64_value);
// Make sure out args are not touched if ReadValue fails
- const char16* kNonExistent = STRING16_LITERAL("NonExistent");
+ const wchar_t* kNonExistent = L"NonExistent";
ASSERT_NE(ERROR_SUCCESS, key.ReadValue(kNonExistent, &string_value));
ASSERT_NE(ERROR_SUCCESS, key.ReadValueDW(kNonExistent, &dword_value));
ASSERT_NE(ERROR_SUCCESS, key.ReadInt64(kNonExistent, &int64_value));
@@ -135,8 +135,8 @@ TEST_F(RegistryTest, ValueTest) {
TEST_F(RegistryTest, BigValueIteratorTest) {
RegKey key;
- string16 foo_key(kRootKey);
- foo_key += STRING16_LITERAL("\\Foo");
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
KEY_READ));
ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(),
@@ -144,7 +144,7 @@ TEST_F(RegistryTest, BigValueIteratorTest) {
ASSERT_TRUE(key.Valid());
// Create a test value that is larger than MAX_PATH.
- string16 data(MAX_PATH * 2, 'a');
+ std::wstring data(MAX_PATH * 2, 'a');
ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(data.c_str(), data.c_str()));
@@ -153,23 +153,23 @@ TEST_F(RegistryTest, BigValueIteratorTest) {
EXPECT_EQ(data, iterator.Name());
EXPECT_EQ(data, iterator.Value());
// ValueSize() is in bytes, including NUL.
- EXPECT_EQ((MAX_PATH * 2 + 1) * sizeof(char16), iterator.ValueSize());
+ EXPECT_EQ((MAX_PATH * 2 + 1) * sizeof(wchar_t), iterator.ValueSize());
++iterator;
EXPECT_FALSE(iterator.Valid());
}
TEST_F(RegistryTest, TruncatedCharTest) {
RegKey key;
- string16 foo_key(kRootKey);
- foo_key += STRING16_LITERAL("\\Foo");
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
KEY_READ));
ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, foo_key.c_str(),
KEY_READ | KEY_SET_VALUE));
ASSERT_TRUE(key.Valid());
- const char16 kName[] = STRING16_LITERAL("name");
- // kData size is not a multiple of sizeof(char16).
+ const wchar_t kName[] = L"name";
+ // kData size is not a multiple of sizeof(wchar_t).
const uint8_t kData[] = {1, 2, 3, 4, 5};
EXPECT_EQ(5u, size(kData));
ASSERT_EQ(ERROR_SUCCESS,
@@ -179,11 +179,11 @@ TEST_F(RegistryTest, TruncatedCharTest) {
ASSERT_TRUE(iterator.Valid());
// Avoid having to use EXPECT_STREQ here by leveraging StringPiece's
// operator== to perform a deep comparison.
- EXPECT_EQ(StringPiece16(kName), StringPiece16(iterator.Name()));
+ EXPECT_EQ(WStringPiece(kName), WStringPiece(iterator.Name()));
// ValueSize() is in bytes.
ASSERT_EQ(size(kData), iterator.ValueSize());
// Value() is NUL terminated.
- int end = (iterator.ValueSize() + sizeof(char16) - 1) / sizeof(char16);
+ int end = (iterator.ValueSize() + sizeof(wchar_t) - 1) / sizeof(wchar_t);
EXPECT_NE('\0', iterator.Value()[end - 1]);
EXPECT_EQ('\0', iterator.Value()[end]);
EXPECT_EQ(0, std::memcmp(kData, iterator.Value(), size(kData)));
@@ -201,50 +201,47 @@ TEST_F(RegistryTest, RecursiveDelete) {
// \->Moo
// \->Foo
// and delete kRootKey->Foo
- string16 foo_key(kRootKey);
- foo_key += STRING16_LITERAL("\\Foo");
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
ASSERT_EQ(ERROR_SUCCESS,
key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Bar"), KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(STRING16_LITERAL("TestValue"),
- STRING16_LITERAL("TestData")));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
ASSERT_EQ(ERROR_SUCCESS,
key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Moo"), KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Moo", KEY_WRITE));
ASSERT_EQ(ERROR_SUCCESS,
key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Foo"), KEY_WRITE));
- foo_key += STRING16_LITERAL("\\Bar");
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
+ foo_key += L"\\Bar";
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
- foo_key += STRING16_LITERAL("\\Foo");
- ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Foo"), KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(STRING16_LITERAL("TestValue"),
- STRING16_LITERAL("TestData")));
+ foo_key += L"\\Foo";
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
- ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("Bar")));
- ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(STRING16_LITERAL("Foo")));
- ASSERT_NE(ERROR_SUCCESS,
- key.DeleteEmptyKey(STRING16_LITERAL("Foo\\Bar\\Foo")));
- ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(STRING16_LITERAL("Foo\\Bar")));
- ASSERT_EQ(ERROR_SUCCESS, key.DeleteEmptyKey(STRING16_LITERAL("Foo\\Foo")));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Bar"));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo"));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar\\Foo"));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar"));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Foo"));
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Bar"), KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("Foo"), KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("")));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L""));
ASSERT_NE(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("Foo")));
- ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("Foo")));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"Foo"));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Foo"));
ASSERT_NE(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
}
@@ -273,18 +270,15 @@ TEST_F(RegistryTest, DISABLED_Wow64RedirectedFromNative) {
// Open the non-redirected view of the parent and try to delete the test key.
ASSERT_EQ(ERROR_SUCCESS,
- key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"),
- KEY_SET_VALUE));
+ key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE));
ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
- ASSERT_EQ(ERROR_SUCCESS,
- key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"),
- KEY_SET_VALUE | kNativeViewMask));
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
+ KEY_SET_VALUE | kNativeViewMask));
ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
// Open the redirected view and delete the key created above.
- ASSERT_EQ(ERROR_SUCCESS,
- key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"),
- KEY_SET_VALUE | kRedirectedViewMask));
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
+ KEY_SET_VALUE | kRedirectedViewMask));
ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
}
@@ -294,13 +288,11 @@ TEST_F(RegistryTest, DISABLED_Wow64RedirectedFromNative) {
TEST_F(RegistryTest, SameWowFlags) {
RegKey key;
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
+ KEY_READ | KEY_WOW64_64KEY));
ASSERT_EQ(ERROR_SUCCESS,
- key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"),
- KEY_READ | KEY_WOW64_64KEY));
- ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(STRING16_LITERAL("Microsoft"),
- KEY_READ | KEY_WOW64_64KEY));
- ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(STRING16_LITERAL("Windows"),
- KEY_READ | KEY_WOW64_64KEY));
+ key.OpenKey(L"Microsoft", KEY_READ | KEY_WOW64_64KEY));
+ ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"Windows", KEY_READ | KEY_WOW64_64KEY));
}
// TODO(wfh): flaky test on Vista. See http://crbug.com/377917
@@ -323,14 +315,12 @@ TEST_F(RegistryTest, DISABLED_Wow64NativeFromRedirected) {
// Open the redirected view of the parent and try to delete the test key
// from the non-redirected view.
- ASSERT_EQ(ERROR_SUCCESS,
- key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"),
- KEY_SET_VALUE | kRedirectedViewMask));
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
+ KEY_SET_VALUE | kRedirectedViewMask));
ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
- ASSERT_EQ(ERROR_SUCCESS,
- key.Open(HKEY_LOCAL_MACHINE, STRING16_LITERAL("Software"),
- KEY_SET_VALUE | kNativeViewMask));
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
+ KEY_SET_VALUE | kNativeViewMask));
ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
}
@@ -341,18 +331,18 @@ TEST_F(RegistryTest, OpenSubKey) {
kRootKey,
KEY_READ | KEY_CREATE_SUB_KEY));
- ASSERT_NE(ERROR_SUCCESS, key.OpenKey(STRING16_LITERAL("foo"), KEY_READ));
- ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(STRING16_LITERAL("foo"), KEY_READ));
+ ASSERT_NE(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"foo", KEY_READ));
ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
- ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(STRING16_LITERAL("foo"), KEY_READ));
+ ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
- string16 foo_key(kRootKey);
- foo_key += STRING16_LITERAL("\\Foo");
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
- ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(STRING16_LITERAL("foo")));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo"));
}
class TestChangeDelegate {
@@ -380,8 +370,8 @@ TEST_F(RegistryTest, ChangeCallback) {
TestChangeDelegate delegate;
test::TaskEnvironment task_environment;
- string16 foo_key(kRootKey);
- foo_key += STRING16_LITERAL("\\Foo");
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(),
KEY_READ));
@@ -394,8 +384,7 @@ TEST_F(RegistryTest, ChangeCallback) {
ASSERT_EQ(ERROR_SUCCESS, key2.Open(HKEY_CURRENT_USER, foo_key.c_str(),
KEY_READ | KEY_SET_VALUE));
ASSERT_TRUE(key2.Valid());
- EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(STRING16_LITERAL("name"),
- STRING16_LITERAL("data")));
+ EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name", L"data"));
// Allow delivery of the notification.
EXPECT_FALSE(delegate.WasCalled());
@@ -408,8 +397,7 @@ TEST_F(RegistryTest, ChangeCallback) {
BindRepeating(&TestChangeDelegate::OnKeyChanged, Unretained(&delegate))));
// Change something else.
- EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(STRING16_LITERAL("name2"),
- STRING16_LITERAL("data2")));
+ EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name2", L"data2"));
RunLoop().Run();
ASSERT_TRUE(delegate.WasCalled());
diff --git a/chromium/base/win/scoped_bstr.cc b/chromium/base/win/scoped_bstr.cc
index dc68cbac013..94d7d084c26 100644
--- a/chromium/base/win/scoped_bstr.cc
+++ b/chromium/base/win/scoped_bstr.cc
@@ -16,8 +16,8 @@ namespace win {
namespace {
-BSTR AllocBstrOrDie(StringPiece16 non_bstr) {
- BSTR result = ::SysAllocStringLen(as_wcstr(non_bstr),
+BSTR AllocBstrOrDie(WStringPiece non_bstr) {
+ BSTR result = ::SysAllocStringLen(non_bstr.data(),
checked_cast<UINT>(non_bstr.length()));
if (!result) {
base::TerminateBecauseOutOfMemory((non_bstr.length() + 1) *
@@ -35,7 +35,7 @@ BSTR AllocBstrBytesOrDie(size_t bytes) {
} // namespace
-ScopedBstr::ScopedBstr(StringPiece16 non_bstr)
+ScopedBstr::ScopedBstr(WStringPiece non_bstr)
: bstr_(AllocBstrOrDie(non_bstr)) {}
ScopedBstr::~ScopedBstr() {
@@ -68,7 +68,7 @@ BSTR* ScopedBstr::Receive() {
return &bstr_;
}
-BSTR ScopedBstr::Allocate(StringPiece16 str) {
+BSTR ScopedBstr::Allocate(WStringPiece str) {
Reset(AllocBstrOrDie(str));
return bstr_;
}
diff --git a/chromium/base/win/scoped_bstr.h b/chromium/base/win/scoped_bstr.h
index 658e815dfa9..a3d3684d986 100644
--- a/chromium/base/win/scoped_bstr.h
+++ b/chromium/base/win/scoped_bstr.h
@@ -12,7 +12,6 @@
#include "base/base_export.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
namespace base {
@@ -28,7 +27,7 @@ class BASE_EXPORT ScopedBstr {
//
// NOTE: Do not pass a BSTR to this constructor expecting ownership to
// be transferred - even though it compiles! ;-)
- explicit ScopedBstr(StringPiece16 non_bstr);
+ explicit ScopedBstr(WStringPiece non_bstr);
~ScopedBstr();
// Give ScopedBstr ownership over an already allocated BSTR or null.
@@ -44,7 +43,7 @@ class BASE_EXPORT ScopedBstr {
// ScopedBstr instance, call |reset| instead.
//
// Returns a pointer to the new BSTR.
- BSTR Allocate(StringPiece16 str);
+ BSTR Allocate(WStringPiece str);
// Allocates a new BSTR with the specified number of bytes.
// Returns a pointer to the new BSTR.
diff --git a/chromium/base/win/scoped_bstr_unittest.cc b/chromium/base/win/scoped_bstr_unittest.cc
index 1be543e1408..66cb0bd5845 100644
--- a/chromium/base/win/scoped_bstr_unittest.cc
+++ b/chromium/base/win/scoped_bstr_unittest.cc
@@ -7,8 +7,6 @@
#include <stddef.h>
#include "base/stl_util.h"
-#include "base/strings/string16.h"
-#include "base/strings/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -16,8 +14,8 @@ namespace win {
namespace {
-static const char16 kTestString1[] = STRING16_LITERAL("123");
-static const char16 kTestString2[] = STRING16_LITERAL("456789");
+static const wchar_t kTestString1[] = L"123";
+static const wchar_t kTestString2[] = L"456789";
size_t test1_len = size(kTestString1) - 1;
size_t test2_len = size(kTestString2) - 1;
@@ -35,7 +33,7 @@ void DumbBstrTests() {
}
void GiveMeABstr(BSTR* ret) {
- *ret = SysAllocString(as_wcstr(kTestString1));
+ *ret = SysAllocString(kTestString1);
}
void BasicBstrTests() {
@@ -47,10 +45,10 @@ void BasicBstrTests() {
b1.Swap(b2);
EXPECT_EQ(test1_len, b2.Length());
EXPECT_EQ(0u, b1.Length());
- EXPECT_STREQ(b2, as_wcstr(kTestString1));
+ EXPECT_STREQ(b2, kTestString1);
BSTR tmp = b2.Release();
EXPECT_TRUE(tmp != NULL);
- EXPECT_STREQ(tmp, as_wcstr(kTestString1));
+ EXPECT_STREQ(tmp, kTestString1);
EXPECT_TRUE(b2 == NULL);
SysFreeString(tmp);
@@ -60,7 +58,7 @@ void BasicBstrTests() {
EXPECT_TRUE(b2.AllocateBytes(100) != NULL);
EXPECT_EQ(100u, b2.ByteLength());
EXPECT_EQ(100 / sizeof(kTestString1[0]), b2.Length());
- lstrcpy(static_cast<BSTR>(b2), as_wcstr(kTestString1));
+ lstrcpy(static_cast<BSTR>(b2), kTestString1);
EXPECT_EQ(test1_len, static_cast<size_t>(lstrlen(b2)));
EXPECT_EQ(100 / sizeof(kTestString1[0]), b2.Length());
b2.SetByteLen(lstrlen(b2) * sizeof(kTestString2[0]));
diff --git a/chromium/base/win/shortcut.cc b/chromium/base/win/shortcut.cc
index 289f5e6ec14..5d9f102778b 100644
--- a/chromium/base/win/shortcut.cc
+++ b/chromium/base/win/shortcut.cc
@@ -115,7 +115,7 @@ bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path,
}
if (properties.options & ShortcutProperties::PROPERTIES_ARGUMENTS) {
- if (FAILED(i_shell_link->SetArguments(as_wcstr(properties.arguments))))
+ if (FAILED(i_shell_link->SetArguments(properties.arguments.c_str())))
return false;
} else if (old_i_persist_file.Get()) {
wchar_t current_arguments[MAX_PATH] = {0};
@@ -126,7 +126,7 @@ bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path,
}
if ((properties.options & ShortcutProperties::PROPERTIES_DESCRIPTION) &&
- FAILED(i_shell_link->SetDescription(as_wcstr(properties.description)))) {
+ FAILED(i_shell_link->SetDescription(properties.description.c_str()))) {
return false;
}
@@ -225,42 +225,39 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path,
// Reset |properties|.
properties->options = 0;
- char16 temp[MAX_PATH];
+ wchar_t temp[MAX_PATH];
if (options & ShortcutProperties::PROPERTIES_TARGET) {
- if (FAILED(i_shell_link->GetPath(as_writable_wcstr(temp), MAX_PATH, NULL,
- SLGP_UNCPRIORITY))) {
+ if (FAILED(i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY))) {
return false;
}
- properties->set_target(FilePath(temp));
+ properties->set_target(FilePath(AsStringPiece16(temp)));
}
if (options & ShortcutProperties::PROPERTIES_WORKING_DIR) {
- if (FAILED(i_shell_link->GetWorkingDirectory(as_writable_wcstr(temp),
- MAX_PATH)))
+ if (FAILED(i_shell_link->GetWorkingDirectory(temp, MAX_PATH)))
return false;
- properties->set_working_dir(FilePath(temp));
+ properties->set_working_dir(FilePath(AsStringPiece16(temp)));
}
if (options & ShortcutProperties::PROPERTIES_ARGUMENTS) {
- if (FAILED(i_shell_link->GetArguments(as_writable_wcstr(temp), MAX_PATH)))
+ if (FAILED(i_shell_link->GetArguments(temp, MAX_PATH)))
return false;
properties->set_arguments(temp);
}
if (options & ShortcutProperties::PROPERTIES_DESCRIPTION) {
// Note: description length constrained by MAX_PATH.
- if (FAILED(i_shell_link->GetDescription(as_writable_wcstr(temp), MAX_PATH)))
+ if (FAILED(i_shell_link->GetDescription(temp, MAX_PATH)))
return false;
properties->set_description(temp);
}
if (options & ShortcutProperties::PROPERTIES_ICON) {
int temp_index;
- if (FAILED(i_shell_link->GetIconLocation(as_writable_wcstr(temp), MAX_PATH,
- &temp_index))) {
+ if (FAILED(i_shell_link->GetIconLocation(temp, MAX_PATH, &temp_index))) {
return false;
}
- properties->set_icon(FilePath(temp), temp_index);
+ properties->set_icon(FilePath(AsStringPiece16(temp)), temp_index);
}
if (options & (ShortcutProperties::PROPERTIES_APP_ID |
@@ -278,10 +275,10 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path,
}
switch (pv_app_id.get().vt) {
case VT_EMPTY:
- properties->set_app_id(string16());
+ properties->set_app_id(std::wstring());
break;
case VT_LPWSTR:
- properties->set_app_id(WideToUTF16(pv_app_id.get().pwszVal));
+ properties->set_app_id(pv_app_id.get().pwszVal);
break;
default:
NOTREACHED() << "Unexpected variant type: " << pv_app_id.get().vt;
@@ -336,7 +333,7 @@ bool ResolveShortcutProperties(const FilePath& shortcut_path,
bool ResolveShortcut(const FilePath& shortcut_path,
FilePath* target_path,
- string16* args) {
+ std::wstring* args) {
uint32_t options = 0;
if (target_path)
options |= ShortcutProperties::PROPERTIES_TARGET;
diff --git a/chromium/base/win/shortcut.h b/chromium/base/win/shortcut.h
index 38c12b77f4d..9f656ad54a2 100644
--- a/chromium/base/win/shortcut.h
+++ b/chromium/base/win/shortcut.h
@@ -11,7 +11,6 @@
#include "base/base_export.h"
#include "base/files/file_path.h"
#include "base/logging.h"
-#include "base/strings/string16.h"
namespace base {
namespace win {
@@ -62,14 +61,14 @@ struct BASE_EXPORT ShortcutProperties {
options |= PROPERTIES_WORKING_DIR;
}
- void set_arguments(const string16& arguments_in) {
+ void set_arguments(const std::wstring& arguments_in) {
// Size restriction as per MSDN at http://goo.gl/TJ7q5.
DCHECK(arguments_in.size() < MAX_PATH);
arguments = arguments_in;
options |= PROPERTIES_ARGUMENTS;
}
- void set_description(const string16& description_in) {
+ void set_description(const std::wstring& description_in) {
// Size restriction as per MSDN at http://goo.gl/OdNQq.
DCHECK(description_in.size() < MAX_PATH);
description = description_in;
@@ -82,7 +81,7 @@ struct BASE_EXPORT ShortcutProperties {
options |= PROPERTIES_ICON;
}
- void set_app_id(const string16& app_id_in) {
+ void set_app_id(const std::wstring& app_id_in) {
app_id = app_id_in;
options |= PROPERTIES_APP_ID;
}
@@ -104,16 +103,16 @@ struct BASE_EXPORT ShortcutProperties {
FilePath working_dir;
// The arguments to be applied to |target| when launching from this shortcut.
// The length of this string must be less than MAX_PATH.
- string16 arguments;
+ std::wstring arguments;
// The localized description of the shortcut.
// The length of this string must be less than MAX_PATH.
- string16 description;
+ std::wstring description;
// The path to the icon (can be a dll or exe, in which case |icon_index| is
// the resource id).
FilePath icon;
int icon_index;
// The app model id for the shortcut.
- string16 app_id;
+ std::wstring app_id;
// Whether this is a dual mode shortcut (Win8+).
bool dual_mode;
// The CLSID of the COM object registered with the OS via the shortcut. This
@@ -159,7 +158,7 @@ BASE_EXPORT bool ResolveShortcutProperties(const FilePath& shortcut_path,
// |shortcut_path| and |target_path|.
BASE_EXPORT bool ResolveShortcut(const FilePath& shortcut_path,
FilePath* target_path,
- string16* args);
+ std::wstring* args);
// Pin to taskbar is only supported on Windows 7 and Windows 8. Returns true on
// those platforms.
diff --git a/chromium/base/win/shortcut_unittest.cc b/chromium/base/win/shortcut_unittest.cc
index 992c1b118e6..76c80dcff55 100644
--- a/chromium/base/win/shortcut_unittest.cc
+++ b/chromium/base/win/shortcut_unittest.cc
@@ -12,7 +12,6 @@
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/stl_util.h"
-#include "base/strings/string16.h"
#include "base/test/test_file_util.h"
#include "base/test/test_shortcut_win.h"
#include "base/win/scoped_com_initializer.h"
@@ -42,10 +41,10 @@ class ShortcutTest : public testing::Test {
link_properties_.set_target(target_file);
link_properties_.set_working_dir(temp_dir_.GetPath());
- link_properties_.set_arguments(STRING16_LITERAL("--magic --awesome"));
- link_properties_.set_description(STRING16_LITERAL("Chrome is awesome."));
+ link_properties_.set_arguments(L"--magic --awesome");
+ link_properties_.set_description(L"Chrome is awesome.");
link_properties_.set_icon(link_properties_.target, 4);
- link_properties_.set_app_id(STRING16_LITERAL("Chrome"));
+ link_properties_.set_app_id(L"Chrome");
link_properties_.set_dual_mode(false);
// The CLSID below was randomly selected.
@@ -68,12 +67,10 @@ class ShortcutTest : public testing::Test {
link_properties_2_.set_target(target_file_2);
link_properties_2_.set_working_dir(temp_dir_2_.GetPath());
- link_properties_2_.set_arguments(STRING16_LITERAL("--super --crazy"));
- link_properties_2_.set_description(
- STRING16_LITERAL("The best in the west."));
+ link_properties_2_.set_arguments(L"--super --crazy");
+ link_properties_2_.set_description(L"The best in the west.");
link_properties_2_.set_icon(icon_path_2, 0);
- link_properties_2_.set_app_id(
- STRING16_LITERAL("Chrome.UserLevelCrazySuffix"));
+ link_properties_2_.set_app_id(L"Chrome.UserLevelCrazySuffix");
link_properties_2_.set_dual_mode(true);
link_properties_2_.set_toast_activator_clsid(CLSID_NULL);
}
@@ -133,11 +130,11 @@ TEST_F(ShortcutTest, CreateAndResolveShortcutProperties) {
ValidatePathsAreEqual(only_target_properties.target,
properties_read_2.target);
ValidatePathsAreEqual(FilePath(), properties_read_2.working_dir);
- EXPECT_EQ(STRING16_LITERAL(""), properties_read_2.arguments);
- EXPECT_EQ(STRING16_LITERAL(""), properties_read_2.description);
+ EXPECT_EQ(L"", properties_read_2.arguments);
+ EXPECT_EQ(L"", properties_read_2.description);
ValidatePathsAreEqual(FilePath(), properties_read_2.icon);
EXPECT_EQ(0, properties_read_2.icon_index);
- EXPECT_EQ(STRING16_LITERAL(""), properties_read_2.app_id);
+ EXPECT_EQ(L"", properties_read_2.app_id);
EXPECT_FALSE(properties_read_2.dual_mode);
EXPECT_EQ(CLSID_NULL, properties_read_2.toast_activator_clsid);
}
@@ -162,7 +159,7 @@ TEST_F(ShortcutTest, ResolveShortcutWithArgs) {
link_file_, link_properties_, SHORTCUT_CREATE_ALWAYS));
FilePath resolved_name;
- string16 args;
+ std::wstring args;
EXPECT_TRUE(ResolveShortcut(link_file_, &resolved_name, &args));
char read_contents[base::size(kFileContents)];
@@ -260,14 +257,14 @@ TEST_F(ShortcutTest, UpdateShortcutClearArguments) {
link_file_, link_properties_, SHORTCUT_CREATE_ALWAYS));
ShortcutProperties clear_arguments_properties;
- clear_arguments_properties.set_arguments(string16());
+ clear_arguments_properties.set_arguments(std::wstring());
ASSERT_TRUE(CreateOrUpdateShortcutLink(
link_file_, clear_arguments_properties,
SHORTCUT_UPDATE_EXISTING));
ShortcutProperties expected_properties = link_properties_;
- expected_properties.set_arguments(string16());
+ expected_properties.set_arguments(std::wstring());
ValidateShortcut(link_file_, expected_properties);
}
@@ -303,7 +300,7 @@ TEST_F(ShortcutTest, ReplaceShortcutSomeProperties) {
ShortcutProperties expected_properties(new_properties);
expected_properties.set_working_dir(FilePath());
expected_properties.set_icon(FilePath(), 0);
- expected_properties.set_app_id(string16());
+ expected_properties.set_app_id(std::wstring());
expected_properties.set_dual_mode(false);
ValidateShortcut(link_file_, expected_properties);
}
diff --git a/chromium/base/win/static_constants.cc b/chromium/base/win/static_constants.cc
new file mode 100644
index 00000000000..f9894a22bd0
--- /dev/null
+++ b/chromium/base/win/static_constants.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/static_constants.h"
+
+namespace base {
+namespace win {
+
+const char kApplicationVerifierDllName[] = "verifier.dll";
+
+} // namespace win
+} // namespace base
diff --git a/chromium/base/win/static_constants.h b/chromium/base/win/static_constants.h
new file mode 100644
index 00000000000..98a631f7cf2
--- /dev/null
+++ b/chromium/base/win/static_constants.h
@@ -0,0 +1,21 @@
+// Copyright 2019 The Chromium 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 constants needed before imports (like base.dll) are fully resolved.
+// For example, constants defined here can be used by interceptions (i.e. hooks)
+// in the sandbox, which run before imports are resolved, and can therefore only
+// reference static variables.
+
+#ifndef BASE_WIN_STATIC_CONSTANTS_H_
+#define BASE_WIN_STATIC_CONSTANTS_H_
+
+namespace base {
+namespace win {
+
+extern const char kApplicationVerifierDllName[];
+
+} // namespace win
+} // namespace base
+
+#endif // BASE_WIN_STATIC_CONSTANTS_H_
diff --git a/chromium/base/win/win_client_metrics.h b/chromium/base/win/win_client_metrics.h
deleted file mode 100644
index 102148f06a9..00000000000
--- a/chromium/base/win/win_client_metrics.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// This file is separate from base/win/win_util.h to avoid pulling windows.h
-// into too many translation units.
-
-#ifndef BASE_WIN_WIN_CLIENT_METRICS_H_
-#define BASE_WIN_WIN_CLIENT_METRICS_H_
-
-#include <windows.h>
-
-// This is the same as NONCLIENTMETRICS except that the
-// unused member |iPaddedBorderWidth| has been removed.
-struct NONCLIENTMETRICS_XP {
- UINT cbSize;
- int iBorderWidth;
- int iScrollWidth;
- int iScrollHeight;
- int iCaptionWidth;
- int iCaptionHeight;
- LOGFONTW lfCaptionFont;
- int iSmCaptionWidth;
- int iSmCaptionHeight;
- LOGFONTW lfSmCaptionFont;
- int iMenuWidth;
- int iMenuHeight;
- LOGFONTW lfMenuFont;
- LOGFONTW lfStatusFont;
- LOGFONTW lfMessageFont;
-};
-
-namespace base {
-namespace win {
-
-BASE_EXPORT void GetNonClientMetrics(NONCLIENTMETRICS_XP* metrics);
-
-} // namespace win
-} // namespace base
-
-#endif // BASE_WIN_WIN_CLIENT_METRICS_H_
diff --git a/chromium/base/win/win_util.cc b/chromium/base/win/win_util.cc
index 7c87ace4dba..848e166f0da 100644
--- a/chromium/base/win/win_util.cc
+++ b/chromium/base/win/win_util.cc
@@ -40,6 +40,7 @@
#include "base/macros.h"
#include "base/scoped_native_library.h"
#include "base/strings/string_util.h"
+#include "base/strings/string_util_win.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/scoped_thread_priority.h"
@@ -52,7 +53,6 @@
#include "base/win/scoped_hstring.h"
#include "base/win/scoped_propvariant.h"
#include "base/win/shlwapi.h"
-#include "base/win/win_client_metrics.h"
#include "base/win/windows_version.h"
namespace base {
@@ -233,7 +233,7 @@ bool IsWindows10TabletMode(HWND hwnd) {
// if the keyboard count is 1 or more.. While this will work in most cases
// it won't work if there are devices which expose keyboard interfaces which
// are attached to the machine.
-bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) {
+bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason) {
bool result = false;
if (GetVersion() < Version::WIN8) {
@@ -266,15 +266,14 @@ bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) {
if (reason)
*reason += "Tablet device.\n";
return false;
- } else {
- if (reason) {
- *reason += "Not a tablet device";
- result = true;
- } else {
- return true;
- }
}
+ if (!reason)
+ return true;
+
+ *reason += "Not a tablet device";
+ result = true;
+
// To determine whether a keyboard is present on the device, we do the
// following:-
// 1. Check whether the device supports auto rotation. If it does then
@@ -301,13 +300,12 @@ bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) {
// If there is no auto rotation sensor or rotation is not supported in
// the current configuration, then we can assume that this is a desktop
// or a traditional laptop.
- if (reason) {
- *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
- : "AR_NOT_SUPPORTED\n";
- result = true;
- } else {
+ if (!reason)
return true;
- }
+
+ *reason += (auto_rotation_state & AR_NOSENSOR) ? "AR_NOSENSOR\n"
+ : "AR_NOT_SUPPORTED\n";
+ result = true;
}
}
@@ -334,20 +332,19 @@ bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) {
break;
// Get the device ID.
- char16 device_id[MAX_DEVICE_ID_LEN];
- CONFIGRET status =
- CM_Get_Device_ID(device_info_data.DevInst, as_writable_wcstr(device_id),
- MAX_DEVICE_ID_LEN, 0);
+ wchar_t device_id[MAX_DEVICE_ID_LEN];
+ CONFIGRET status = CM_Get_Device_ID(device_info_data.DevInst, device_id,
+ MAX_DEVICE_ID_LEN, 0);
if (status == CR_SUCCESS) {
// To reduce the scope of the hack we only look for ACPI and HID\\VID
// prefixes in the keyboard device ids.
- if (StartsWith(device_id, STRING16_LITERAL("ACPI"),
+ if (StartsWith(AsStringPiece16(device_id), STRING16_LITERAL("ACPI"),
CompareCase::INSENSITIVE_ASCII) ||
- StartsWith(device_id, STRING16_LITERAL("HID\\VID"),
+ StartsWith(AsStringPiece16(device_id), STRING16_LITERAL("HID\\VID"),
CompareCase::INSENSITIVE_ASCII)) {
if (reason) {
*reason += "device: ";
- *reason += UTF16ToUTF8(device_id);
+ *reason += WideToUTF8(device_id);
*reason += '\n';
}
// The heuristic we are using is to check the count of keyboards and
@@ -363,18 +360,7 @@ bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd) {
static bool g_crash_on_process_detach = false;
-void GetNonClientMetrics(NONCLIENTMETRICS_XP* metrics) {
- DCHECK(metrics);
- metrics->cbSize = sizeof(*metrics);
- const bool success = !!SystemParametersInfo(
- SPI_GETNONCLIENTMETRICS,
- metrics->cbSize,
- reinterpret_cast<NONCLIENTMETRICS*>(metrics),
- 0);
- DCHECK(success);
-}
-
-bool GetUserSidString(string16* user_sid) {
+bool GetUserSidString(std::wstring* user_sid) {
// Get the current token.
HANDLE token = NULL;
if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
@@ -396,7 +382,7 @@ bool GetUserSidString(string16* user_sid) {
if (!::ConvertSidToStringSid(user->User.Sid, &sid_string))
return false;
- *user_sid = as_u16cstr(sid_string);
+ *user_sid = sid_string;
::LocalFree(sid_string);
@@ -409,14 +395,11 @@ bool UserAccountControlIsEnabled() {
// http://code.google.com/p/chromium/issues/detail?id=61644
ThreadRestrictions::ScopedAllowIO allow_io;
- RegKey key(
- HKEY_LOCAL_MACHINE,
- STRING16_LITERAL(
- "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"),
- KEY_READ);
+ RegKey key(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
+ KEY_READ);
DWORD uac_enabled;
- if (key.ReadValueDW(STRING16_LITERAL("EnableLUA"), &uac_enabled) !=
- ERROR_SUCCESS) {
+ if (key.ReadValueDW(L"EnableLUA", &uac_enabled) != ERROR_SUCCESS) {
return true;
}
// Users can set the EnableLUA value to something arbitrary, like 2, which
@@ -440,9 +423,9 @@ bool SetBooleanValueForPropertyStore(IPropertyStore* property_store,
bool SetStringValueForPropertyStore(IPropertyStore* property_store,
const PROPERTYKEY& property_key,
- const char16* property_string_value) {
+ const wchar_t* property_string_value) {
ScopedPropVariant property_value;
- if (FAILED(InitPropVariantFromString(as_wcstr(property_string_value),
+ if (FAILED(InitPropVariantFromString(property_string_value,
property_value.Receive()))) {
return false;
}
@@ -466,36 +449,37 @@ bool SetClsidForPropertyStore(IPropertyStore* property_store,
}
bool SetAppIdForPropertyStore(IPropertyStore* property_store,
- const char16* app_id) {
+ const wchar_t* app_id) {
// App id should be less than 64 chars and contain no space. And recommended
// format is CompanyName.ProductName[.SubProduct.ProductNumber].
// See http://msdn.microsoft.com/en-us/library/dd378459%28VS.85%29.aspx
- DCHECK_LT(lstrlen(as_wcstr(app_id)), 64);
- DCHECK_EQ(wcschr(as_wcstr(app_id), L' '), nullptr);
+ DCHECK_LT(lstrlen(app_id), 64);
+ DCHECK_EQ(wcschr(app_id, L' '), nullptr);
return SetStringValueForPropertyStore(property_store,
PKEY_AppUserModel_ID,
app_id);
}
-static const char16 kAutoRunKeyPath[] =
- STRING16_LITERAL("Software\\Microsoft\\Windows\\CurrentVersion\\Run");
+static const wchar_t kAutoRunKeyPath[] =
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Run";
-bool AddCommandToAutoRun(HKEY root_key, const string16& name,
- const string16& command) {
+bool AddCommandToAutoRun(HKEY root_key,
+ const std::wstring& name,
+ const std::wstring& command) {
RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
return (autorun_key.WriteValue(name.c_str(), command.c_str()) ==
ERROR_SUCCESS);
}
-bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name) {
+bool RemoveCommandFromAutoRun(HKEY root_key, const std::wstring& name) {
RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_SET_VALUE);
return (autorun_key.DeleteValue(name.c_str()) == ERROR_SUCCESS);
}
bool ReadCommandFromAutoRun(HKEY root_key,
- const string16& name,
- string16* command) {
+ const std::wstring& name,
+ std::wstring* command) {
RegKey autorun_key(root_key, kAutoRunKeyPath, KEY_QUERY_VALUE);
return (autorun_key.ReadValue(name.c_str(), command) == ERROR_SUCCESS);
}
@@ -766,9 +750,9 @@ void* GetUser32FunctionPointer(const char* function_name,
return nullptr;
}
-string16 GetWindowObjectName(HANDLE handle) {
+std::wstring GetWindowObjectName(HANDLE handle) {
// Get the size of the name.
- string16 object_name;
+ std::wstring object_name;
DWORD size = 0;
::GetUserObjectInformation(handle, UOI_NAME, nullptr, 0, &size);
@@ -781,7 +765,7 @@ string16 GetWindowObjectName(HANDLE handle) {
// Query the name of the object.
if (!::GetUserObjectInformation(
- handle, UOI_NAME, WriteInto(&object_name, size / sizeof(wchar_t)),
+ handle, UOI_NAME, WriteIntoW(&object_name, size / sizeof(wchar_t)),
size, &size)) {
DPCHECK(false);
}
@@ -789,13 +773,14 @@ string16 GetWindowObjectName(HANDLE handle) {
return object_name;
}
-bool IsRunningUnderDesktopName(StringPiece16 desktop_name) {
+bool IsRunningUnderDesktopName(WStringPiece desktop_name) {
HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
if (!thread_desktop)
return false;
- string16 current_desktop_name = GetWindowObjectName(thread_desktop);
- return EqualsCaseInsensitiveASCII(current_desktop_name, desktop_name);
+ std::wstring current_desktop_name = GetWindowObjectName(thread_desktop);
+ return EqualsCaseInsensitiveASCII(AsStringPiece16(current_desktop_name),
+ AsStringPiece16(desktop_name));
}
// This method is used to detect whether current session is a remote session.
@@ -810,14 +795,13 @@ bool IsCurrentSessionRemote() {
if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &current_session_id))
return false;
- static constexpr char16 kRdpSettingsKeyName[] =
- STRING16_LITERAL("SYSTEM\\CurrentControlSet\\Control\\Terminal Server");
+ static constexpr wchar_t kRdpSettingsKeyName[] =
+ L"SYSTEM\\CurrentControlSet\\Control\\Terminal Server";
RegKey key(HKEY_LOCAL_MACHINE, kRdpSettingsKeyName, KEY_READ);
if (!key.Valid())
return false;
- static constexpr char16 kGlassSessionIdValueName[] =
- STRING16_LITERAL("GlassSessionId");
+ static constexpr wchar_t kGlassSessionIdValueName[] = L"GlassSessionId";
DWORD glass_session_id = 0;
if (key.ReadValueDW(kGlassSessionIdValueName, &glass_session_id) !=
ERROR_SUCCESS)
diff --git a/chromium/base/win/win_util.h b/chromium/base/win/win_util.h
index 921b5c102ea..c0848b62f9e 100644
--- a/chromium/base/win/win_util.h
+++ b/chromium/base/win/win_util.h
@@ -58,7 +58,7 @@ inline HANDLE Uint32ToHandle(uint32_t h) {
// Returns the string representing the current user sid. Does not modify
// |user_sid| on failure.
-BASE_EXPORT bool GetUserSidString(string16* user_sid);
+BASE_EXPORT bool GetUserSidString(std::wstring* user_sid);
// Returns false if user account control (UAC) has been disabled with the
// EnableLUA registry flag. Returns true if user account control is enabled.
@@ -78,7 +78,7 @@ BASE_EXPORT bool SetBooleanValueForPropertyStore(
BASE_EXPORT bool SetStringValueForPropertyStore(
IPropertyStore* property_store,
const PROPERTYKEY& property_key,
- const char16* property_string_value);
+ const wchar_t* property_string_value);
// Sets the CLSID value for a given key in a given IPropertyStore.
BASE_EXPORT bool SetClsidForPropertyStore(IPropertyStore* property_store,
@@ -89,22 +89,23 @@ BASE_EXPORT bool SetClsidForPropertyStore(IPropertyStore* property_store,
// for tagging application/chromium shortcut, browser window and jump list for
// Win7.
BASE_EXPORT bool SetAppIdForPropertyStore(IPropertyStore* property_store,
- const char16* app_id);
+ const wchar_t* app_id);
// Adds the specified |command| using the specified |name| to the AutoRun key.
// |root_key| could be HKCU or HKLM or the root of any user hive.
BASE_EXPORT bool AddCommandToAutoRun(HKEY root_key,
- const string16& name,
- const string16& command);
+ const std::wstring& name,
+ const std::wstring& command);
// Removes the command specified by |name| from the AutoRun key. |root_key|
// could be HKCU or HKLM or the root of any user hive.
-BASE_EXPORT bool RemoveCommandFromAutoRun(HKEY root_key, const string16& name);
+BASE_EXPORT bool RemoveCommandFromAutoRun(HKEY root_key,
+ const std::wstring& name);
// Reads the command specified by |name| from the AutoRun key. |root_key|
// could be HKCU or HKLM or the root of any user hive. Used for unit-tests.
BASE_EXPORT bool ReadCommandFromAutoRun(HKEY root_key,
- const string16& name,
- string16* command);
+ const std::wstring& name,
+ std::wstring* command);
// Sets whether to crash the process during exit. This is inspected by DLLMain
// and used to intercept unexpected terminations of the process (via calls to
@@ -148,7 +149,7 @@ BASE_EXPORT bool IsDeviceUsedAsATablet(std::string* reason);
// A slate is a touch device that may have a keyboard attached. This function
// returns true if a keyboard is attached and optionally will set the |reason|
// parameter to the detection method that was used to detect the keyboard.
-BASE_EXPORT bool IsKeyboardPresentOnSlate(std::string* reason, HWND hwnd);
+BASE_EXPORT bool IsKeyboardPresentOnSlate(HWND hwnd, std::string* reason);
// Get the size of a struct up to and including the specified member.
// This is necessary to set compatible struct sizes for different versions
@@ -216,12 +217,12 @@ BASE_EXPORT void* GetUser32FunctionPointer(
NativeLibraryLoadError* error = nullptr);
// Returns the name of a desktop or a window station.
-BASE_EXPORT string16 GetWindowObjectName(HANDLE handle);
+BASE_EXPORT std::wstring GetWindowObjectName(HANDLE handle);
// Checks if the calling thread is running under a desktop with the name
// given by |desktop_name|. |desktop_name| is ASCII case insensitive (non-ASCII
// characters will be compared with exact matches).
-BASE_EXPORT bool IsRunningUnderDesktopName(StringPiece16 desktop_name);
+BASE_EXPORT bool IsRunningUnderDesktopName(WStringPiece desktop_name);
// Returns true if current session is a remote session.
BASE_EXPORT bool IsCurrentSessionRemote();
diff --git a/chromium/base/win/win_util_unittest.cc b/chromium/base/win/win_util_unittest.cc
index 1b161aa2bc6..db741f5e2ed 100644
--- a/chromium/base/win/win_util_unittest.cc
+++ b/chromium/base/win/win_util_unittest.cc
@@ -13,7 +13,6 @@
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/win/scoped_co_mem.h"
-#include "base/win/win_client_metrics.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -43,19 +42,11 @@ TEST(BaseWinUtilTest, TestIsUACEnabled) {
}
TEST(BaseWinUtilTest, TestGetUserSidString) {
- string16 user_sid;
+ std::wstring user_sid;
EXPECT_TRUE(GetUserSidString(&user_sid));
EXPECT_TRUE(!user_sid.empty());
}
-TEST(BaseWinUtilTest, TestGetNonClientMetrics) {
- NONCLIENTMETRICS_XP metrics = {0};
- GetNonClientMetrics(&metrics);
- EXPECT_GT(metrics.cbSize, 0u);
- EXPECT_GT(metrics.iScrollWidth, 0);
- EXPECT_GT(metrics.iScrollHeight, 0);
-}
-
TEST(BaseWinUtilTest, TestGetLoadedModulesSnapshot) {
std::vector<HMODULE> snapshot;
@@ -101,9 +92,9 @@ TEST(BaseWinUtilTest, String16FromGUID) {
}
TEST(BaseWinUtilTest, GetWindowObjectName) {
- base::string16 created_desktop_name(STRING16_LITERAL("test_desktop"));
+ std::wstring created_desktop_name(L"test_desktop");
HDESK desktop_handle =
- ::CreateDesktop(as_wcstr(created_desktop_name), nullptr, nullptr, 0,
+ ::CreateDesktop(created_desktop_name.c_str(), nullptr, nullptr, 0,
DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS |
READ_CONTROL | WRITE_DAC | WRITE_OWNER,
nullptr);
@@ -117,13 +108,15 @@ TEST(BaseWinUtilTest, IsRunningUnderDesktopName) {
HDESK thread_desktop = ::GetThreadDesktop(::GetCurrentThreadId());
ASSERT_NE(thread_desktop, nullptr);
- base::string16 desktop_name = GetWindowObjectName(thread_desktop);
+ std::wstring desktop_name = GetWindowObjectName(thread_desktop);
EXPECT_TRUE(IsRunningUnderDesktopName(desktop_name));
- EXPECT_TRUE(IsRunningUnderDesktopName(base::ToLowerASCII(desktop_name)));
- EXPECT_TRUE(IsRunningUnderDesktopName(base::ToUpperASCII(desktop_name)));
- EXPECT_FALSE(IsRunningUnderDesktopName(
- desktop_name + STRING16_LITERAL("_non_existent_desktop_name")));
+ EXPECT_TRUE(IsRunningUnderDesktopName(
+ AsWString(ToLowerASCII(AsStringPiece16(desktop_name)))));
+ EXPECT_TRUE(IsRunningUnderDesktopName(
+ AsWString(ToUpperASCII(AsStringPiece16(desktop_name)))));
+ EXPECT_FALSE(
+ IsRunningUnderDesktopName(desktop_name + L"_non_existent_desktop_name"));
}
} // namespace win
diff --git a/chromium/base/win/windows_types.h b/chromium/base/win/windows_types.h
index a57277a7c6a..60c4ffe8c9a 100644
--- a/chromium/base/win/windows_types.h
+++ b/chromium/base/win/windows_types.h
@@ -244,6 +244,8 @@ WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode);
#define DeleteFile DeleteFileW
#define DispatchMessage DispatchMessageW
#define DrawText DrawTextW
+#define FindFirstFile FindFirstFileW
+#define FindNextFile FindNextFileW
#define GetComputerName GetComputerNameW
#define GetCurrentDirectory GetCurrentDirectoryW
#define GetCurrentTime() GetTickCount()
@@ -253,6 +255,7 @@ WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode);
#define LoadIcon LoadIconW
#define LoadImage LoadImageW
#define PostMessage PostMessageW
+#define RemoveDirectory RemoveDirectoryW
#define ReplaceFile ReplaceFileW
#define ReportEvent ReportEventW
#define SendMessage SendMessageW
diff --git a/chromium/base/win/windows_version.cc b/chromium/base/win/windows_version.cc
index 1d3721944f4..476744b41fe 100644
--- a/chromium/base/win/windows_version.cc
+++ b/chromium/base/win/windows_version.cc
@@ -14,7 +14,6 @@
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/no_destructor.h"
-#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
@@ -34,8 +33,8 @@ namespace {
// The values under the CurrentVersion registry hive are mirrored under
// the corresponding Wow6432 hive.
-constexpr char16 kRegKeyWindowsNTCurrentVersion[] =
- STRING16_LITERAL("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
+constexpr wchar_t kRegKeyWindowsNTCurrentVersion[] =
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
// Returns the "UBR" (Windows 10 patch number) and "ReleaseId" (Windows 10
// release number) from the registry. "UBR" is an undocumented value and will be
@@ -43,16 +42,16 @@ constexpr char16 kRegKeyWindowsNTCurrentVersion[] =
// value is not found.
std::pair<int, std::string> GetVersionData() {
DWORD ubr = 0;
- string16 release_id;
+ std::wstring release_id;
RegKey key;
if (key.Open(HKEY_LOCAL_MACHINE, kRegKeyWindowsNTCurrentVersion,
KEY_QUERY_VALUE) == ERROR_SUCCESS) {
- key.ReadValueDW(STRING16_LITERAL("UBR"), &ubr);
- key.ReadValue(STRING16_LITERAL("ReleaseId"), &release_id);
+ key.ReadValueDW(L"UBR", &ubr);
+ key.ReadValue(L"ReleaseId", &release_id);
}
- return std::make_pair(static_cast<int>(ubr), UTF16ToUTF8(release_id));
+ return std::make_pair(static_cast<int>(ubr), WideToUTF8(release_id));
}
const _SYSTEM_INFO& GetSystemInfoStorage() {
@@ -215,27 +214,19 @@ base::Version OSInfo::Kernel32BaseVersion() const {
FilePath(FILE_PATH_LITERAL("kernelbase.dll")));
}
CHECK(file_version_info);
- const int major =
- HIWORD(file_version_info->fixed_file_info()->dwFileVersionMS);
- const int minor =
- LOWORD(file_version_info->fixed_file_info()->dwFileVersionMS);
- const int build =
- HIWORD(file_version_info->fixed_file_info()->dwFileVersionLS);
- const int patch =
- LOWORD(file_version_info->fixed_file_info()->dwFileVersionLS);
- return base::Version(std::vector<uint32_t>{major, minor, build, patch});
+ return file_version_info->GetFileVersion();
}());
return *version;
}
std::string OSInfo::processor_model_name() {
if (processor_model_name_.empty()) {
- const char16 kProcessorNameString[] =
- STRING16_LITERAL("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
+ const wchar_t kProcessorNameString[] =
+ L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
RegKey key(HKEY_LOCAL_MACHINE, kProcessorNameString, KEY_READ);
- string16 value;
- key.ReadValue(STRING16_LITERAL("ProcessorNameString"), &value);
- processor_model_name_ = UTF16ToUTF8(value);
+ std::wstring value;
+ key.ReadValue(L"ProcessorNameString", &value);
+ processor_model_name_ = WideToUTF8(value);
}
return processor_model_name_;
}
diff --git a/chromium/base/win/wmi.cc b/chromium/base/win/wmi.cc
index 4d5e094c3c4..c7da8a3c869 100644
--- a/chromium/base/win/wmi.cc
+++ b/chromium/base/win/wmi.cc
@@ -36,9 +36,9 @@ bool CreateLocalWmiConnection(bool set_blanket,
return false;
ComPtr<IWbemServices> wmi_services_r;
- hr = wmi_locator->ConnectServer(ScopedBstr(STRING16_LITERAL("ROOT\\CIMV2")),
- nullptr, nullptr, nullptr, 0, nullptr,
- nullptr, &wmi_services_r);
+ hr =
+ wmi_locator->ConnectServer(ScopedBstr(L"ROOT\\CIMV2"), nullptr, nullptr,
+ nullptr, 0, nullptr, nullptr, &wmi_services_r);
if (FAILED(hr))
return false;
@@ -55,8 +55,8 @@ bool CreateLocalWmiConnection(bool set_blanket,
}
bool CreateWmiClassMethodObject(IWbemServices* wmi_services,
- StringPiece16 class_name,
- StringPiece16 method_name,
+ WStringPiece class_name,
+ WStringPiece method_name,
ComPtr<IWbemClassObject>* class_instance) {
// We attempt to instantiate a COM object that represents a WMI object plus
// a method rolled into one entity.
@@ -90,20 +90,20 @@ bool CreateWmiClassMethodObject(IWbemServices* wmi_services,
// NOTE: The documentation for the Create method suggests that the ProcessId
// parameter and return value are of type uint32_t, but when we call the method
// the values in the returned out_params, are VT_I4, which is int32_t.
-bool WmiLaunchProcess(const string16& command_line, int* process_id) {
+bool WmiLaunchProcess(const std::wstring& command_line, int* process_id) {
ComPtr<IWbemServices> wmi_local;
if (!CreateLocalWmiConnection(true, &wmi_local))
return false;
- static constexpr char16 class_name[] = STRING16_LITERAL("Win32_Process");
- static constexpr char16 method_name[] = STRING16_LITERAL("Create");
+ static constexpr wchar_t class_name[] = L"Win32_Process";
+ static constexpr wchar_t method_name[] = L"Create";
ComPtr<IWbemClassObject> process_create;
if (!CreateWmiClassMethodObject(wmi_local.Get(), class_name, method_name,
&process_create)) {
return false;
}
- ScopedVariant b_command_line(as_wcstr(command_line));
+ ScopedVariant b_command_line(command_line.c_str());
if (FAILED(process_create->Put(L"CommandLine", 0, b_command_line.AsInput(),
0))) {
@@ -151,14 +151,14 @@ WmiComputerSystemInfo WmiComputerSystemInfo::Get() {
void WmiComputerSystemInfo::PopulateModelAndManufacturer(
const ComPtr<IWbemServices>& services) {
- static constexpr StringPiece16 query_computer_system =
- STRING16_LITERAL("SELECT Manufacturer,Model FROM Win32_ComputerSystem");
+ static constexpr WStringPiece query_computer_system =
+ L"SELECT Manufacturer,Model FROM Win32_ComputerSystem";
ComPtr<IEnumWbemClassObject> enumerator_computer_system;
- HRESULT hr = services->ExecQuery(
- ScopedBstr(STRING16_LITERAL("WQL")), ScopedBstr(query_computer_system),
- WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
- &enumerator_computer_system);
+ HRESULT hr =
+ services->ExecQuery(ScopedBstr(L"WQL"), ScopedBstr(query_computer_system),
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ nullptr, &enumerator_computer_system);
if (FAILED(hr) || !enumerator_computer_system.Get())
return;
@@ -172,27 +172,26 @@ void WmiComputerSystemInfo::PopulateModelAndManufacturer(
ScopedVariant manufacturer;
hr = class_object->Get(L"Manufacturer", 0, manufacturer.Receive(), 0, 0);
if (SUCCEEDED(hr) && manufacturer.type() == VT_BSTR) {
- WideToUTF16(V_BSTR(manufacturer.ptr()),
- ::SysStringLen(V_BSTR(manufacturer.ptr())), &manufacturer_);
+ manufacturer_.assign(V_BSTR(manufacturer.ptr()),
+ ::SysStringLen(V_BSTR(manufacturer.ptr())));
}
ScopedVariant model;
hr = class_object->Get(L"Model", 0, model.Receive(), 0, 0);
if (SUCCEEDED(hr) && model.type() == VT_BSTR) {
- WideToUTF16(V_BSTR(model.ptr()), ::SysStringLen(V_BSTR(model.ptr())),
- &model_);
+ model_.assign(V_BSTR(model.ptr()), ::SysStringLen(V_BSTR(model.ptr())));
}
}
void WmiComputerSystemInfo::PopulateSerialNumber(
const ComPtr<IWbemServices>& services) {
- static constexpr StringPiece16 query_bios =
- STRING16_LITERAL("SELECT SerialNumber FROM Win32_Bios");
+ static constexpr WStringPiece query_bios =
+ L"SELECT SerialNumber FROM Win32_Bios";
ComPtr<IEnumWbemClassObject> enumerator_bios;
- HRESULT hr = services->ExecQuery(
- ScopedBstr(STRING16_LITERAL("WQL")), ScopedBstr(query_bios),
- WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, nullptr,
- &enumerator_bios);
+ HRESULT hr =
+ services->ExecQuery(ScopedBstr(L"WQL"), ScopedBstr(query_bios),
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ nullptr, &enumerator_bios);
if (FAILED(hr) || !enumerator_bios.Get())
return;
@@ -205,8 +204,8 @@ void WmiComputerSystemInfo::PopulateSerialNumber(
ScopedVariant serial_number;
hr = class_obj->Get(L"SerialNumber", 0, serial_number.Receive(), 0, 0);
if (SUCCEEDED(hr) && serial_number.type() == VT_BSTR) {
- WideToUTF16(V_BSTR(serial_number.ptr()),
- ::SysStringLen(V_BSTR(serial_number.ptr())), &serial_number_);
+ serial_number_.assign(V_BSTR(serial_number.ptr()),
+ ::SysStringLen(V_BSTR(serial_number.ptr())));
}
}
diff --git a/chromium/base/win/wmi.h b/chromium/base/win/wmi.h
index e1523152ce7..7949888a229 100644
--- a/chromium/base/win/wmi.h
+++ b/chromium/base/win/wmi.h
@@ -24,7 +24,6 @@
#include <wrl/client.h>
#include "base/base_export.h"
-#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
namespace base {
@@ -47,8 +46,8 @@ BASE_EXPORT bool CreateLocalWmiConnection(
// WMI method that you can fill with parameter values using SetParameter.
BASE_EXPORT bool CreateWmiClassMethodObject(
IWbemServices* wmi_services,
- StringPiece16 class_name,
- StringPiece16 method_name,
+ WStringPiece class_name,
+ WStringPiece method_name,
Microsoft::WRL::ComPtr<IWbemClassObject>* class_instance);
// Creates a new process from |command_line|. The advantage over CreateProcess
@@ -61,7 +60,7 @@ BASE_EXPORT bool CreateWmiClassMethodObject(
// Processes created this way are children of wmiprvse.exe and run with the
// caller credentials.
// More info: http://msdn2.microsoft.com/en-us/library/aa394372(VS.85).aspx
-BASE_EXPORT bool WmiLaunchProcess(const string16& command_line,
+BASE_EXPORT bool WmiLaunchProcess(const std::wstring& command_line,
int* process_id);
// An encapsulation of information retrieved from the 'Win32_ComputerSystem' and
@@ -72,9 +71,9 @@ class BASE_EXPORT WmiComputerSystemInfo {
public:
static WmiComputerSystemInfo Get();
- const string16& manufacturer() const { return manufacturer_; }
- const string16& model() const { return model_; }
- const string16& serial_number() const { return serial_number_; }
+ const std::wstring& manufacturer() const { return manufacturer_; }
+ const std::wstring& model() const { return model_; }
+ const std::wstring& serial_number() const { return serial_number_; }
private:
void PopulateModelAndManufacturer(
@@ -82,9 +81,9 @@ class BASE_EXPORT WmiComputerSystemInfo {
void PopulateSerialNumber(
const Microsoft::WRL::ComPtr<IWbemServices>& services);
- string16 manufacturer_;
- string16 model_;
- string16 serial_number_;
+ std::wstring manufacturer_;
+ std::wstring model_;
+ std::wstring serial_number_;
};
} // namespace win
diff --git a/chromium/base/win/wmi_unittest.cc b/chromium/base/win/wmi_unittest.cc
index 6c465256196..438fff75d9c 100644
--- a/chromium/base/win/wmi_unittest.cc
+++ b/chromium/base/win/wmi_unittest.cc
@@ -42,8 +42,7 @@ TEST_F(WMITest, TestCreateClassMethod) {
ASSERT_NE(wmi_services.Get(), nullptr);
ComPtr<IWbemClassObject> class_method = nullptr;
EXPECT_TRUE(CreateWmiClassMethodObject(
- wmi_services.Get(), STRING16_LITERAL("Win32_ShortcutFile"),
- STRING16_LITERAL("Rename"), &class_method));
+ wmi_services.Get(), L"Win32_ShortcutFile", L"Rename", &class_method));
ASSERT_NE(class_method.Get(), nullptr);
ULONG refs = class_method.Reset();
EXPECT_EQ(0u, refs);
@@ -54,8 +53,7 @@ TEST_F(WMITest, TestCreateClassMethod) {
// Creates an instance of cmd which executes 'echo' and exits immediately.
TEST_F(WMITest, TestLaunchProcess) {
int pid = 0;
- bool result =
- WmiLaunchProcess(STRING16_LITERAL("cmd.exe /c echo excelent!"), &pid);
+ bool result = WmiLaunchProcess(L"cmd.exe /c echo excelent!", &pid);
EXPECT_TRUE(result);
EXPECT_GT(pid, 0);
}